Add ApplyPacketOptions()

When libjingle is compied with ENABLE_EXTERNAL_AUTH the sending socket
needs to update RTP header in order for the outgoing packet to be
valid. The corresponding code was in chromium in
content/browser/renderer_host/p2p/socket_host.cc and it was impossible
to reuse it anywhere else. This CL moves this code to
talk/media/base/rtputils.h/cc, so it can be used outside of chrome.

BUG=crbug.com/547158
R=pthatcher@webrtc.org

Review URL: https://codereview.webrtc.org/1578323002 .

Cr-Commit-Position: refs/heads/master@{#11261}
This commit is contained in:
Sergey Ulanov 2016-01-14 17:14:54 -08:00
parent 20ac434010
commit dc305db059
8 changed files with 838 additions and 10 deletions

View File

@ -513,6 +513,8 @@
'media/base/screencastid.h',
'media/base/streamparams.cc',
'media/base/streamparams.h',
'media/base/turnutils.cc',
'media/base/turnutils.h',
'media/base/videoadapter.cc',
'media/base/videoadapter.h',
'media/base/videocapturer.cc',

View File

@ -87,6 +87,7 @@
'media/base/streamparams_unittest.cc',
'media/base/testutils.cc',
'media/base/testutils.h',
'media/base/turnutils_unittest.cc',
'media/base/videoadapter_unittest.cc',
'media/base/videocapturer_unittest.cc',
'media/base/videocommon_unittest.cc',

View File

@ -27,6 +27,13 @@
#include "talk/media/base/rtputils.h"
#include "talk/media/base/turnutils.h"
// PacketTimeUpdateParams is defined in asyncpacketsocket.h.
// TODO(sergeyu): Find more appropriate place for PacketTimeUpdateParams.
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/messagedigest.h"
namespace cricket {
static const uint8_t kRtpVersion = 2;
@ -36,6 +43,95 @@ static const size_t kRtpSeqNumOffset = 2;
static const size_t kRtpTimestampOffset = 4;
static const size_t kRtpSsrcOffset = 8;
static const size_t kRtcpPayloadTypeOffset = 1;
static const size_t kRtpExtensionHeaderLen = 4;
static const size_t kAbsSendTimeExtensionLen = 3;
static const size_t kOneByteExtensionHeaderLen = 1;
namespace {
// Fake auth tag written by the sender when external authentication is enabled.
// HMAC in packet will be compared against this value before updating packet
// with actual HMAC value.
static const uint8_t kFakeAuthTag[10] = {
0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd
};
void UpdateAbsSendTimeExtensionValue(uint8_t* extension_data,
size_t length,
uint64_t time_us) {
// Absolute send time in RTP streams.
//
// The absolute send time is signaled to the receiver in-band using the
// general mechanism for RTP header extensions [RFC5285]. The payload
// of this extension (the transmitted value) is a 24-bit unsigned integer
// containing the sender's current time in seconds as a fixed point number
// with 18 bits fractional part.
//
// The form of the absolute send time extension block:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=2 | absolute send time |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
if (length != kAbsSendTimeExtensionLen) {
RTC_NOTREACHED();
return;
}
// Convert microseconds to a 6.18 fixed point value in seconds.
uint32_t send_time = ((time_us << 18) / 1000000) & 0x00FFFFFF;
extension_data[0] = static_cast<uint8_t>(send_time >> 16);
extension_data[1] = static_cast<uint8_t>(send_time >> 8);
extension_data[2] = static_cast<uint8_t>(send_time);
}
// Assumes |length| is actual packet length + tag length. Updates HMAC at end of
// the RTP packet.
void UpdateRtpAuthTag(uint8_t* rtp,
size_t length,
const rtc::PacketTimeUpdateParams& packet_time_params) {
// If there is no key, return.
if (packet_time_params.srtp_auth_key.empty()) {
return;
}
size_t tag_length = packet_time_params.srtp_auth_tag_len;
// ROC (rollover counter) is at the beginning of the auth tag.
const size_t kRocLength = 4;
if (tag_length < kRocLength || tag_length > length) {
RTC_NOTREACHED();
return;
}
uint8_t* auth_tag = rtp + (length - tag_length);
// We should have a fake HMAC value @ auth_tag.
RTC_DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length));
// Copy ROC after end of rtp packet.
memcpy(auth_tag, &packet_time_params.srtp_packet_index, kRocLength);
// Authentication of a RTP packet will have RTP packet + ROC size.
size_t auth_required_length = length - tag_length + kRocLength;
uint8_t output[64];
size_t result = rtc::ComputeHmac(
rtc::DIGEST_SHA_1, &packet_time_params.srtp_auth_key[0],
packet_time_params.srtp_auth_key.size(), rtp,
auth_required_length, output, sizeof(output));
if (result < tag_length) {
RTC_NOTREACHED();
return;
}
// Copy HMAC from output to packet. This is required as auth tag length
// may not be equal to the actual HMAC length.
memcpy(auth_tag, output, tag_length);
}
}
bool GetUint8(const void* data, size_t offset, int* value) {
if (!data || !value) {
@ -200,4 +296,186 @@ bool IsValidRtpPayloadType(int payload_type) {
return payload_type >= 0 && payload_type <= 127;
}
bool ValidateRtpHeader(const uint8_t* rtp,
size_t length,
size_t* header_length) {
if (header_length) {
*header_length = 0;
}
if (length < kMinRtpPacketLen) {
return false;
}
size_t cc_count = rtp[0] & 0x0F;
size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count;
if (header_length_without_extension > length) {
return false;
}
// If extension bit is not set, we are done with header processing, as input
// length is verified above.
if (!(rtp[0] & 0x10)) {
if (header_length)
*header_length = header_length_without_extension;
return true;
}
rtp += header_length_without_extension;
if (header_length_without_extension + kRtpExtensionHeaderLen > length) {
return false;
}
// Getting extension profile length.
// Length is in 32 bit words.
uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2);
size_t extension_length = extension_length_in_32bits * 4;
size_t rtp_header_length = extension_length +
header_length_without_extension +
kRtpExtensionHeaderLen;
// Verify input length against total header size.
if (rtp_header_length > length) {
return false;
}
if (header_length) {
*header_length = rtp_header_length;
}
return true;
}
// ValidateRtpHeader() must be called before this method to make sure, we have
// a sane rtp packet.
bool UpdateRtpAbsSendTimeExtension(uint8_t* rtp,
size_t length,
int extension_id,
uint64_t time_us) {
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|X| CC |M| PT | sequence number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | timestamp |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | synchronization source (SSRC) identifier |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | contributing source (CSRC) identifiers |
// | .... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Return if extension bit is not set.
if (!(rtp[0] & 0x10)) {
return true;
}
size_t cc_count = rtp[0] & 0x0F;
size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count;
rtp += header_length_without_extension;
// Getting extension profile ID and length.
uint16_t profile_id = rtc::GetBE16(rtp);
// Length is in 32 bit words.
uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2);
size_t extension_length = extension_length_in_32bits * 4;
rtp += kRtpExtensionHeaderLen; // Moving past extension header.
bool found = false;
// WebRTC is using one byte header extension.
// TODO(mallinath) - Handle two byte header extension.
if (profile_id == 0xBEDE) { // OneByte extension header
// 0
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// | ID |length |
// +-+-+-+-+-+-+-+-+
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0xBE | 0xDE | length=3 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | L=0 | data | ID | L=1 | data...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// ...data | 0 (pad) | 0 (pad) | ID | L=3 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | data |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
const uint8_t* extension_start = rtp;
const uint8_t* extension_end = extension_start + extension_length;
while (rtp < extension_end) {
const int id = (*rtp & 0xF0) >> 4;
const size_t length = (*rtp & 0x0F) + 1;
if (rtp + kOneByteExtensionHeaderLen + length > extension_end) {
return false;
}
// The 4-bit length is the number minus one of data bytes of this header
// extension element following the one-byte header.
if (id == extension_id) {
UpdateAbsSendTimeExtensionValue(rtp + kOneByteExtensionHeaderLen,
length, time_us);
found = true;
break;
}
rtp += kOneByteExtensionHeaderLen + length;
// Counting padding bytes.
while ((rtp < extension_end) && (*rtp == 0)) {
++rtp;
}
}
}
return found;
}
bool ApplyPacketOptions(uint8_t* data,
size_t length,
const rtc::PacketTimeUpdateParams& packet_time_params,
uint64_t time_us) {
RTC_DCHECK(data);
RTC_DCHECK(length);
// if there is no valid |rtp_sendtime_extension_id| and |srtp_auth_key| in
// PacketOptions, nothing to be updated in this packet.
if (packet_time_params.rtp_sendtime_extension_id == -1 &&
packet_time_params.srtp_auth_key.empty()) {
return true;
}
// If there is a srtp auth key present then the packet must be an RTP packet.
// RTP packet may have been wrapped in a TURN Channel Data or TURN send
// indication.
size_t rtp_start_pos;
size_t rtp_length;
if (!UnwrapTurnPacket(data, length, &rtp_start_pos, &rtp_length)) {
RTC_NOTREACHED();
return false;
}
// Making sure we have a valid RTP packet at the end.
if (!IsRtpPacket(data + rtp_start_pos, rtp_length) ||
!ValidateRtpHeader(data + rtp_start_pos, rtp_length, nullptr)) {
RTC_NOTREACHED();
return false;
}
uint8_t* start = data + rtp_start_pos;
// If packet option has non default value (-1) for sendtime extension id,
// then we should parse the rtp packet to update the timestamp. Otherwise
// just calculate HMAC and update packet with it.
if (packet_time_params.rtp_sendtime_extension_id != -1) {
UpdateRtpAbsSendTimeExtension(start, rtp_length,
packet_time_params.rtp_sendtime_extension_id,
time_us);
}
UpdateRtpAuthTag(start, rtp_length, packet_time_params);
return true;
}
} // namespace cricket

View File

@ -30,6 +30,10 @@
#include "webrtc/base/byteorder.h"
namespace rtc {
struct PacketTimeUpdateParams;
} // namespace rtc
namespace cricket {
const size_t kMinRtpPacketLen = 12;
@ -71,6 +75,25 @@ bool IsRtpPacket(const void* data, size_t len);
// True if |payload type| is 0-127.
bool IsValidRtpPayloadType(int payload_type);
// Verifies that a packet has a valid RTP header.
bool ValidateRtpHeader(const uint8_t* rtp,
size_t length,
size_t* header_length);
// Helper method which updates the absolute send time extension if present.
bool UpdateRtpAbsSendTimeExtension(uint8_t* rtp,
size_t length,
int extension_id,
uint64_t time_us);
// Applies specified |options| to the packet. It updates the absolute send time
// extension header if it is present present then updates HMAC.
bool ApplyPacketOptions(uint8_t* data,
size_t length,
const rtc::PacketTimeUpdateParams& packet_time_params,
uint64_t time_us);
} // namespace cricket
#endif // TALK_MEDIA_BASE_RTPUTILS_H_

View File

@ -27,31 +27,32 @@
#include "talk/media/base/fakertp.h"
#include "talk/media/base/rtputils.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/gunit.h"
namespace cricket {
static const unsigned char kRtpPacketWithMarker[] = {
static const uint8_t kRtpPacketWithMarker[] = {
0x80, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
};
// 3 CSRCs (0x01020304, 0x12345678, 0xAABBCCDD)
// Extension (0xBEDE, 0x1122334455667788)
static const unsigned char kRtpPacketWithMarkerAndCsrcAndExtension[] = {
static const uint8_t kRtpPacketWithMarkerAndCsrcAndExtension[] = {
0x93, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x02, 0x03, 0x04, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD,
0xBE, 0xDE, 0x00, 0x02, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
};
static const unsigned char kInvalidPacket[] = { 0x80, 0x00 };
static const unsigned char kInvalidPacketWithCsrc[] = {
static const uint8_t kInvalidPacket[] = { 0x80, 0x00 };
static const uint8_t kInvalidPacketWithCsrc[] = {
0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x02, 0x03, 0x04, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC
};
static const unsigned char kInvalidPacketWithCsrcAndExtension1[] = {
static const uint8_t kInvalidPacketWithCsrcAndExtension1[] = {
0x93, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x02, 0x03, 0x04, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD,
0xBE, 0xDE, 0x00
};
static const unsigned char kInvalidPacketWithCsrcAndExtension2[] = {
static const uint8_t kInvalidPacketWithCsrcAndExtension2[] = {
0x93, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x02, 0x03, 0x04, 0x12, 0x34, 0x56, 0x78, 0xAA, 0xBB, 0xCC, 0xDD,
0xBE, 0xDE, 0x00, 0x02, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
@ -59,21 +60,48 @@ static const unsigned char kInvalidPacketWithCsrcAndExtension2[] = {
// PT = 206, FMT = 1, Sender SSRC = 0x1111, Media SSRC = 0x1111
// No FCI information is needed for PLI.
static const unsigned char kNonCompoundRtcpPliFeedbackPacket[] = {
static const uint8_t kNonCompoundRtcpPliFeedbackPacket[] = {
0x81, 0xCE, 0x00, 0x0C, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x11, 0x11
};
// Packet has only mandatory fixed RTCP header
// PT = 204, SSRC = 0x1111
static const unsigned char kNonCompoundRtcpAppPacket[] = {
static const uint8_t kNonCompoundRtcpAppPacket[] = {
0x81, 0xCC, 0x00, 0x0C, 0x00, 0x00, 0x11, 0x11
};
// PT = 202, Source count = 0
static const unsigned char kNonCompoundRtcpSDESPacket[] = {
static const uint8_t kNonCompoundRtcpSDESPacket[] = {
0x80, 0xCA, 0x00, 0x00
};
static uint8_t kFakeTag[4] = { 0xba, 0xdd, 0xba, 0xdd };
static uint8_t kTestKey[] = "12345678901234567890";
static uint8_t kTestAstValue[3] = { 0xaa, 0xbb, 0xcc };
// Valid rtp Message with 2 byte header extension.
static uint8_t kRtpMsgWith2ByteExtnHeader[] = {
0x90, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xAA, 0xBB, 0xCC, 0XDD, // SSRC
0x10, 0x00, 0x00, 0x01, // 2 Byte header extension
0x01, 0x00, 0x00, 0x00
};
// RTP packet with single byte extension header of length 4 bytes.
// Extension id = 3 and length = 3
static uint8_t kRtpMsgWithAbsSendTimeExtension[] = {
0x90, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xBE, 0xDE, 0x00, 0x02,
0x22, 0x00, 0x02, 0x1c,
0x32, 0xaa, 0xbb, 0xcc,
};
// Index of AbsSendTimeExtn data in message |kRtpMsgWithAbsSendTimeExtension|.
static const int kAstIndexInRtpMsg = 21;
TEST(RtpUtilsTest, GetRtp) {
EXPECT_TRUE(IsRtpPacket(kPcmuFrame, sizeof(kPcmuFrame)));
@ -110,7 +138,7 @@ TEST(RtpUtilsTest, GetRtp) {
}
TEST(RtpUtilsTest, SetRtpHeader) {
unsigned char packet[] = {
uint8_t packet[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
@ -169,4 +197,172 @@ TEST(RtpUtilsTest, GetRtcp) {
&ssrc));
}
// Invalid RTP packets.
TEST(RtpUtilsTest, InvalidRtpHeader) {
// Rtp message with invalid length.
const uint8_t kRtpMsgWithInvalidLength[] = {
0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xAA, 0xBB, 0xCC, 0XDD, // SSRC
0xDD, 0xCC, 0xBB, 0xAA, // Only 1 CSRC, but CC count is 4.
};
EXPECT_FALSE(ValidateRtpHeader(kRtpMsgWithInvalidLength,
sizeof(kRtpMsgWithInvalidLength), nullptr));
// Rtp message with single byte header extension, invalid extension length.
const uint8_t kRtpMsgWithInvalidExtnLength[] = {
0x90, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xBE, 0xDE, 0x0A, 0x00, // Extn length - 0x0A00
};
EXPECT_FALSE(ValidateRtpHeader(kRtpMsgWithInvalidExtnLength,
sizeof(kRtpMsgWithInvalidExtnLength),
nullptr));
}
// Valid RTP packet with a 2byte header extension.
TEST(RtpUtilsTest, Valid2ByteExtnHdrRtpMessage) {
EXPECT_TRUE(ValidateRtpHeader(kRtpMsgWith2ByteExtnHeader,
sizeof(kRtpMsgWith2ByteExtnHeader), nullptr));
}
// Valid RTP packet which has 1 byte header AbsSendTime extension in it.
TEST(RtpUtilsTest, ValidRtpPacketWithAbsSendTimeExtension) {
EXPECT_TRUE(ValidateRtpHeader(kRtpMsgWithAbsSendTimeExtension,
sizeof(kRtpMsgWithAbsSendTimeExtension),
nullptr));
}
// Verify handling of a 2 byte extension header RTP messsage. Currently these
// messages are not supported.
TEST(RtpUtilsTest, UpdateAbsSendTimeExtensionIn2ByteHeaderExtn) {
std::vector<uint8_t> data(
kRtpMsgWith2ByteExtnHeader,
kRtpMsgWith2ByteExtnHeader + sizeof(kRtpMsgWith2ByteExtnHeader));
EXPECT_FALSE(UpdateRtpAbsSendTimeExtension(&data[0], data.size(), 3, 0));
}
// Verify finding an extension ID in the TURN send indication message.
TEST(RtpUtilsTest, UpdateAbsSendTimeExtensionInTurnSendIndication) {
// A valid STUN indication message with a valid RTP header in data attribute
// payload field and no extension bit set.
uint8_t message_without_extension[] = {
0x00, 0x16, 0x00, 0x18, // length of
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7',
'8', '9', 'a', 'b',
0x00, 0x20, 0x00, 0x04, // Mapped address.
0x00, 0x00, 0x00, 0x00,
0x00, 0x13, 0x00, 0x0C, // Data attribute.
0x80, 0x00, 0x00, 0x00, // RTP packet.
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(
message_without_extension, sizeof(message_without_extension), 3, 0));
// A valid STUN indication message with a valid RTP header and a extension
// header.
uint8_t message[] = {
0x00, 0x16, 0x00, 0x24, // length of
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7',
'8', '9', 'a', 'b',
0x00, 0x20, 0x00, 0x04, // Mapped address.
0x00, 0x00, 0x00, 0x00,
0x00, 0x13, 0x00, 0x18, // Data attribute.
0x90, 0x00, 0x00, 0x00, // RTP packet.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xDE,
0x00, 0x02, 0x22, 0xaa, 0xbb, 0xcc, 0x32, 0xaa, 0xbb, 0xcc,
};
EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(message, sizeof(message), 3, 0));
}
// Test without any packet options variables set. This method should return
// without HMAC value in the packet.
TEST(RtpUtilsTest, ApplyPacketOptionsWithDefaultValues) {
rtc::PacketTimeUpdateParams packet_time_params;
std::vector<uint8_t> rtp_packet(kRtpMsgWithAbsSendTimeExtension,
kRtpMsgWithAbsSendTimeExtension +
sizeof(kRtpMsgWithAbsSendTimeExtension));
rtp_packet.insert(rtp_packet.end(), kFakeTag, kFakeTag + sizeof(kFakeTag));
EXPECT_TRUE(ApplyPacketOptions(&rtp_packet[0], rtp_packet.size(),
packet_time_params, 0));
// Making sure HMAC wasn't updated..
EXPECT_EQ(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)],
kFakeTag, 4));
// Verify AbsouluteSendTime extension field wasn't modified.
EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], kTestAstValue,
sizeof(kTestAstValue)));
}
// Veirfy HMAC is updated when packet option parameters are set.
TEST(RtpUtilsTest, ApplyPacketOptionsWithAuthParams) {
rtc::PacketTimeUpdateParams packet_time_params;
packet_time_params.srtp_auth_key.assign(kTestKey,
kTestKey + sizeof(kTestKey));
packet_time_params.srtp_auth_tag_len = 4;
std::vector<uint8_t> rtp_packet(kRtpMsgWithAbsSendTimeExtension,
kRtpMsgWithAbsSendTimeExtension +
sizeof(kRtpMsgWithAbsSendTimeExtension));
rtp_packet.insert(rtp_packet.end(), kFakeTag, kFakeTag + sizeof(kFakeTag));
EXPECT_TRUE(ApplyPacketOptions(&rtp_packet[0], rtp_packet.size(),
packet_time_params, 0));
uint8_t kExpectedTag[] = {0xc1, 0x7a, 0x8c, 0xa0};
EXPECT_EQ(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)],
kExpectedTag, sizeof(kExpectedTag)));
// Verify AbsouluteSendTime extension field is not modified.
EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], kTestAstValue,
sizeof(kTestAstValue)));
}
// Verify finding an extension ID in a raw rtp message.
TEST(RtpUtilsTest, UpdateAbsSendTimeExtensionInRtpPacket) {
std::vector<uint8_t> rtp_packet(kRtpMsgWithAbsSendTimeExtension,
kRtpMsgWithAbsSendTimeExtension +
sizeof(kRtpMsgWithAbsSendTimeExtension));
EXPECT_TRUE(UpdateRtpAbsSendTimeExtension(&rtp_packet[0], rtp_packet.size(),
3, 51183266));
// Verify that the timestamp was updated.
const uint8_t kExpectedTimestamp[3] = {0xcc, 0xbb, 0xaa};
EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], kExpectedTimestamp,
sizeof(kExpectedTimestamp)));
}
// Verify we update both AbsSendTime extension header and HMAC.
TEST(RtpUtilsTest, ApplyPacketOptionsWithAuthParamsAndAbsSendTime) {
rtc::PacketTimeUpdateParams packet_time_params;
packet_time_params.srtp_auth_key.assign(kTestKey,
kTestKey + sizeof(kTestKey));
packet_time_params.srtp_auth_tag_len = 4;
packet_time_params.rtp_sendtime_extension_id = 3;
// 3 is also present in the test message.
std::vector<uint8_t> rtp_packet(kRtpMsgWithAbsSendTimeExtension,
kRtpMsgWithAbsSendTimeExtension +
sizeof(kRtpMsgWithAbsSendTimeExtension));
rtp_packet.insert(rtp_packet.end(), kFakeTag, kFakeTag + sizeof(kFakeTag));
EXPECT_TRUE(ApplyPacketOptions(&rtp_packet[0], rtp_packet.size(),
packet_time_params, 51183266));
const uint8_t kExpectedTag[] = {0x81, 0xd1, 0x2c, 0x0e};
EXPECT_EQ(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)],
kExpectedTag, sizeof(kExpectedTag)));
// Verify that the timestamp was updated.
const uint8_t kExpectedTimestamp[3] = {0xcc, 0xbb, 0xaa};
EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], kExpectedTimestamp,
sizeof(kExpectedTimestamp)));
}
} // namespace cricket

View File

@ -0,0 +1,144 @@
/*
* libjingle
* Copyright 2016 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/media/base/turnutils.h"
#include "webrtc/base/byteorder.h"
#include "webrtc/base/checks.h"
#include "webrtc/p2p/base/stun.h"
namespace cricket {
namespace {
const size_t kTurnChannelHeaderLength = 4;
bool IsTurnChannelData(const uint8_t* data, size_t length) {
return length >= kTurnChannelHeaderLength && ((*data & 0xC0) == 0x40);
}
bool IsTurnSendIndicationPacket(const uint8_t* data, size_t length) {
if (length < kStunHeaderSize) {
return false;
}
uint16_t type = rtc::GetBE16(data);
return (type == TURN_SEND_INDICATION);
}
} // namespace
bool UnwrapTurnPacket(const uint8_t* packet,
size_t packet_size,
size_t* content_position,
size_t* content_size) {
if (IsTurnChannelData(packet, packet_size)) {
// Turn Channel Message header format.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Channel Number | Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// / Application Data /
// / /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
size_t length = rtc::GetBE16(&packet[2]);
if (length + kTurnChannelHeaderLength > packet_size) {
return false;
}
*content_position = kTurnChannelHeaderLength;
*content_size = length;
return true;
}
if (IsTurnSendIndicationPacket(packet, packet_size)) {
// Validate STUN message length.
const size_t stun_message_length = rtc::GetBE16(&packet[2]);
if (stun_message_length + kStunHeaderSize != packet_size) {
return false;
}
// First skip mandatory stun header which is of 20 bytes.
size_t pos = kStunHeaderSize;
// Loop through STUN attributes until we find STUN DATA attribute.
while (pos < packet_size) {
// Keep reading STUN attributes until we hit DATA attribute.
// Attribute will be a TLV structure.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Value (variable) ....
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// The value in the length field MUST contain the length of the Value
// part of the attribute, prior to padding, measured in bytes. Since
// STUN aligns attributes on 32-bit boundaries, attributes whose content
// is not a multiple of 4 bytes are padded with 1, 2, or 3 bytes of
// padding so that its value contains a multiple of 4 bytes. The
// padding bits are ignored, and may be any value.
uint16_t attr_type, attr_length;
const int kAttrHeaderLength = sizeof(attr_type) + sizeof(attr_length);
if (packet_size < pos + kAttrHeaderLength) {
return false;
}
// Getting attribute type and length.
attr_type = rtc::GetBE16(&packet[pos]);
attr_length = rtc::GetBE16(&packet[pos + sizeof(attr_type)]);
pos += kAttrHeaderLength; // Skip STUN_DATA_ATTR header.
// Checking for bogus attribute length.
if (pos + attr_length > packet_size) {
return false;
}
if (attr_type == STUN_ATTR_DATA) {
*content_position = pos;
*content_size = attr_length;
return true;
}
pos += attr_length;
if ((attr_length % 4) != 0) {
pos += (4 - (attr_length % 4));
}
}
// There is no data attribute present in the message.
return false;
}
// This is not a TURN packet.
*content_position = 0;
*content_size = packet_size;
return true;
}
} // namespace cricket

View File

@ -0,0 +1,47 @@
/*
* libjingle
* Copyright 2016 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TALK_MEDIA_BASE_TURNUTILS_H_
#define TALK_MEDIA_BASE_TURNUTILS_H_
#include <cstddef>
#include <cstdint>
namespace cricket {
struct PacketOptions;
// Finds data location within a TURN Channel Message or TURN Send Indication
// message.
bool UnwrapTurnPacket(const uint8_t* packet,
size_t packet_size,
size_t* content_position,
size_t* content_size);
} // namespace cricket
#endif // TALK_MEDIA_BASE_TURNUTILS_H_

View File

@ -0,0 +1,137 @@
/*
* libjingle
* Copyright 2016 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/media/base/turnutils.h"
#include <stddef.h>
#include "webrtc/base/gunit.h"
namespace cricket {
// Invalid TURN send indication messages. Messages are proper STUN
// messages with incorrect values in attributes.
TEST(TurnUtilsTest, InvalidTurnSendIndicationMessages) {
size_t content_pos = SIZE_MAX;
size_t content_size = SIZE_MAX;
// Stun Indication message with Zero length
uint8_t kTurnSendIndicationMsgWithNoAttributes[] = {
0x00, 0x16, 0x00, 0x00, // Zero length
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7', '8', '9', 'a', 'b',
};
EXPECT_FALSE(UnwrapTurnPacket(kTurnSendIndicationMsgWithNoAttributes,
sizeof(kTurnSendIndicationMsgWithNoAttributes),
&content_pos, &content_size));
EXPECT_EQ(SIZE_MAX, content_pos);
EXPECT_EQ(SIZE_MAX, content_size);
// Stun Send Indication message with invalid length in stun header.
const uint8_t kTurnSendIndicationMsgWithInvalidLength[] = {
0x00, 0x16, 0xFF, 0x00, // length of 0xFF00
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7', '8', '9', 'a', 'b',
};
EXPECT_FALSE(UnwrapTurnPacket(kTurnSendIndicationMsgWithInvalidLength,
sizeof(kTurnSendIndicationMsgWithInvalidLength),
&content_pos, &content_size));
EXPECT_EQ(SIZE_MAX, content_pos);
EXPECT_EQ(SIZE_MAX, content_size);
// Stun Send Indication message with no DATA attribute in message.
const uint8_t kTurnSendIndicatinMsgWithNoDataAttribute[] = {
0x00, 0x16, 0x00, 0x08, // length of
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7', '8', '9', 'a', 'b',
0x00, 0x20, 0x00, 0x04, // Mapped address.
0x00, 0x00, 0x00, 0x00,
};
EXPECT_FALSE(
UnwrapTurnPacket(kTurnSendIndicatinMsgWithNoDataAttribute,
sizeof(kTurnSendIndicatinMsgWithNoDataAttribute),
&content_pos, &content_size));
EXPECT_EQ(SIZE_MAX, content_pos);
EXPECT_EQ(SIZE_MAX, content_size);
}
// Valid TURN Send Indication messages.
TEST(TurnUtilsTest, ValidTurnSendIndicationMessage) {
size_t content_pos = SIZE_MAX;
size_t content_size = SIZE_MAX;
// A valid STUN indication message with a valid RTP header in data attribute
// payload field and no extension bit set.
const uint8_t kTurnSendIndicationMsgWithoutRtpExtension[] = {
0x00, 0x16, 0x00, 0x18, // length of
0x21, 0x12, 0xA4, 0x42, // magic cookie
'0', '1', '2', '3', // transaction id
'4', '5', '6', '7', '8', '9', 'a', 'b',
0x00, 0x20, 0x00, 0x04, // Mapped address.
0x00, 0x00, 0x00, 0x00,
0x00, 0x13, 0x00, 0x0C, // Data attribute.
0x80, 0x00, 0x00, 0x00, // RTP packet.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
EXPECT_TRUE(UnwrapTurnPacket(
kTurnSendIndicationMsgWithoutRtpExtension,
sizeof(kTurnSendIndicationMsgWithoutRtpExtension), &content_pos,
&content_size));
EXPECT_EQ(12U, content_size);
EXPECT_EQ(32U, content_pos);
}
// Verify that parsing of valid TURN Channel Messages.
TEST(TurnUtilsTest, ValidTurnChannelMessages) {
const uint8_t kTurnChannelMsgWithRtpPacket[] = {
0x40, 0x00, 0x00, 0x0C,
0x80, 0x00, 0x00, 0x00, // RTP packet.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
size_t content_pos = 0, content_size = 0;
EXPECT_TRUE(UnwrapTurnPacket(
kTurnChannelMsgWithRtpPacket,
sizeof(kTurnChannelMsgWithRtpPacket), &content_pos, &content_size));
EXPECT_EQ(12U, content_size);
EXPECT_EQ(4U, content_pos);
}
TEST(TurnUtilsTest, ChannelMessageZeroLength) {
const uint8_t kTurnChannelMsgWithZeroLength[] = {0x40, 0x00, 0x00, 0x00};
size_t content_pos = SIZE_MAX;
size_t content_size = SIZE_MAX;
EXPECT_TRUE(UnwrapTurnPacket(kTurnChannelMsgWithZeroLength,
sizeof(kTurnChannelMsgWithZeroLength),
&content_pos, &content_size));
EXPECT_EQ(4, content_pos);
EXPECT_EQ(0, content_size);
}
} // namespace cricket