diff options
author | mallinath@chromium.org <mallinath@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-12 17:59:44 +0000 |
---|---|---|
committer | mallinath@chromium.org <mallinath@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-12 17:59:44 +0000 |
commit | ecdb463c565f27ea68b7e9dd71f6ddbdb65ced7a (patch) | |
tree | cb01b5971d7ed5527002f5b1dffd0b5f146723cd | |
parent | a96f4c5795e7cf0d2778a3d77316614de6fcba69 (diff) | |
download | chromium_src-ecdb463c565f27ea68b7e9dd71f6ddbdb65ced7a.zip chromium_src-ecdb463c565f27ea68b7e9dd71f6ddbdb65ced7a.tar.gz chromium_src-ecdb463c565f27ea68b7e9dd71f6ddbdb65ced7a.tar.bz2 |
This CL adds methods to manipulate RTP header extension, particularly
AbsoulteSendTime extension. If there is matching extension ID present
in RTP packet, we will update with the current time.
R=solenberg@chromium.org, juberti@chromium.org, sergeyu@chromium.org
BUG=
Review URL: https://codereview.chromium.org/159353002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@256580 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host.cc | 373 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host.h | 22 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_tcp.cc | 15 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_tcp.h | 9 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_udp.cc | 3 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_unittest.cc | 376 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | third_party/libjingle/libjingle.gyp | 2 |
8 files changed, 795 insertions, 6 deletions
diff --git a/content/browser/renderer_host/p2p/socket_host.cc b/content/browser/renderer_host/p2p/socket_host.cc index 4b45e99..fd1a903 100644 --- a/content/browser/renderer_host/p2p/socket_host.cc +++ b/content/browser/renderer_host/p2p/socket_host.cc @@ -8,13 +8,386 @@ #include "content/browser/renderer_host/p2p/socket_host_tcp.h" #include "content/browser/renderer_host/p2p/socket_host_tcp_server.h" #include "content/browser/renderer_host/p2p/socket_host_udp.h" +#include "crypto/hmac.h" +#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h" +#include "third_party/libjingle/source/talk/base/byteorder.h" +#include "third_party/libjingle/source/talk/base/messagedigest.h" +#include "third_party/libjingle/source/talk/p2p/base/stun.h" namespace { + const uint32 kStunMagicCookie = 0x2112A442; +const int kMinRtpHdrLen = 12; +const int kRtpExtnHdrLen = 4; +const int kDtlsRecordHeaderLen = 13; +const int kTurnChannelHdrLen = 4; +const int kAbsSendTimeExtnLen = 3; +const int kOneByteHdrLen = 1; + +// Fake auth tag written by the render process if external authentication is +// enabled. HMAC in packet will be compared against this value before updating +// packet with actual HMAC value. +static const unsigned char kFakeAuthTag[10] = { + 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd +}; + +bool IsTurnChannelData(const char* data) { + return ((*data & 0xC0) == 0x40); +} + +bool IsDtlsPacket(const char* data, int len) { + const uint8* u = reinterpret_cast<const uint8*>(data); + return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64)); +} + +bool IsRtcpPacket(const char* data) { + int type = (static_cast<uint8>(data[1]) & 0x7F); + return (type >= 64 && type < 96); +} + +bool IsTurnSendIndicationPacket(const char* data) { + uint16 type = talk_base::GetBE16(data); + return (type == cricket::TURN_SEND_INDICATION); +} + +bool IsRtpPacket(const char* data, int len) { + return ((*data & 0xC0) == 0x80); +} + +// Verifies rtp header and message length. +bool ValidateRtpHeader(char* rtp, int length) { + int cc_count = rtp[0] & 0x0F; + int rtp_hdr_len_without_extn = kMinRtpHdrLen + 4 * cc_count; + if (rtp_hdr_len_without_extn > 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)) { + return true; + } + + rtp += rtp_hdr_len_without_extn; + + // Getting extension profile length. + // Length is in 32 bit words. + uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4; + + // Verify input length against total header size. + if (rtp_hdr_len_without_extn + kRtpExtnHdrLen + extn_length > length) { + return false; + } + return true; +} + +void UpdateAbsSendTimeExtnValue(char* extn_data, int len, + uint32 abs_send_time) { + // 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 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + DCHECK_EQ(len, kAbsSendTimeExtnLen); + // Now() has resolution ~1-15ms, using HighResNow(). But it is warned not to + // use it unless necessary, as it is expensive than Now(). + uint32 now_second = abs_send_time; + if (!now_second) { + uint64 now_us = + (base::TimeTicks::HighResNow() - base::TimeTicks()).InMicroseconds(); + // Convert second to 24-bit unsigned with 18 bit fractional part + now_second = + ((now_us << 18) / base::Time::kMicrosecondsPerSecond) & 0x00FFFFFF; + } + // TODO(mallinath) - Add SetBE24 to byteorder.h in libjingle. + extn_data[0] = static_cast<uint8>(now_second >> 16); + extn_data[1] = static_cast<uint8>(now_second >> 8); + extn_data[2] = static_cast<uint8>(now_second); +} + +// Assumes |len| is actual packet length + tag length. Updates HMAC at end of +// the RTP packet. +void UpdateRtpAuthTag(char* rtp, int len, + const talk_base::PacketOptions& options) { + // If there is no key, return. + if (options.packet_time_params.srtp_auth_key.empty()) + return; + + size_t tag_length = options.packet_time_params.srtp_auth_tag_len; + char* auth_tag = rtp + (len - tag_length); + + // We should have a fake HMAC value @ auth_tag. + DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length)); + + crypto::HMAC hmac(crypto::HMAC::SHA1); + if (!hmac.Init(reinterpret_cast<const unsigned char*>( + &options.packet_time_params.srtp_auth_key[0]), + options.packet_time_params.srtp_auth_key.size())) { + NOTREACHED(); + return; + } + + if (hmac.DigestLength() < tag_length) { + NOTREACHED(); + return; + } + + // Copy ROC after end of rtp packet. + memcpy(auth_tag, &options.packet_time_params.srtp_packet_index, 4); + // Authentication of a RTP packet will have RTP packet + ROC size. + int auth_required_length = len - tag_length + 4; + + unsigned char output[64]; + if (!hmac.Sign(base::StringPiece(rtp, auth_required_length), + output, sizeof(output))) { + 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); +} + } // namespace namespace content { +namespace packet_processing_helpers { + +bool ApplyPacketOptions(char* data, int length, + const talk_base::PacketOptions& options, + uint32 abs_send_time) { + DCHECK(data != NULL); + DCHECK(length > 0); + // if there is no valid |rtp_sendtime_extension_id| and |srtp_auth_key| in + // PacketOptions, nothing to be updated in this packet. + if (options.packet_time_params.rtp_sendtime_extension_id == -1 && + options.packet_time_params.srtp_auth_key.empty()) { + return true; + } + + DCHECK(!IsDtlsPacket(data, length)); + DCHECK(!IsRtcpPacket(data)); + + // If there is a srtp auth key present then packet must be a RTP packet. + // RTP packet may have been wrapped in a TURN Channel Data or + // TURN send indication. + int rtp_start_pos; + int rtp_length; + if (!GetRtpPacketStartPositionAndLength( + data, length, &rtp_start_pos, &rtp_length)) { + // This method should never return false. + NOTREACHED(); + return false; + } + + // Skip to rtp packet. + char* 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 (options.packet_time_params.rtp_sendtime_extension_id != -1) { + UpdateRtpAbsSendTimeExtn( + start, rtp_length, + options.packet_time_params.rtp_sendtime_extension_id, abs_send_time); + } + + UpdateRtpAuthTag(start, rtp_length, options); + return true; +} + +bool GetRtpPacketStartPositionAndLength( + char* packet, int length, int* rtp_start_pos, int* rtp_packet_length) { + int rtp_begin, rtp_length; + if (IsTurnChannelData(packet)) { + // 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 / + // / / + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + if (length < kTurnChannelHdrLen) { + return false; + } + + rtp_begin = kTurnChannelHdrLen; + rtp_length = talk_base::GetBE16(&packet[2]); + if (length < rtp_length + kTurnChannelHdrLen) { + return false; + } + } else if (IsTurnSendIndicationPacket(packet)) { + if (length <= P2PSocketHost::kStunHeaderSize) { + // Message must be greater than 20 bytes, if it's carrying any payload. + return false; + } + // Validate STUN message length. + int stun_msg_len = talk_base::GetBE16(&packet[2]); + if (stun_msg_len + P2PSocketHost::kStunHeaderSize != length) { + return false; + } + + // First skip mandatory stun header which is of 20 bytes. + rtp_begin = P2PSocketHost::kStunHeaderSize; + // Loop through STUN attributes until we find STUN DATA attribute. + char* start = packet + rtp_begin; + bool data_attr_present = false; + while ((packet + rtp_begin) - start < length) { + // 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 attr_type, attr_length; + // Getting attribute type and length. + attr_type = talk_base::GetBE16(&packet[rtp_begin]); + attr_length = talk_base::GetBE16( + &packet[rtp_begin + sizeof(attr_type)]); + // Checking for bogus attribute length. + if (length < attr_length + rtp_begin) { + return false; + } + + if (attr_type != cricket::STUN_ATTR_DATA) { + rtp_begin += sizeof(attr_type) + sizeof(attr_length) + attr_length; + if ((attr_length % 4) != 0) { + rtp_begin += (4 - (attr_length % 4)); + } + continue; + } + + data_attr_present = true; + rtp_begin += 4; // Skip STUN_DATA_ATTR header. + rtp_length = attr_length; + // One final check of length before exiting. + if (length < rtp_length + rtp_begin) { + return false; + } + // We found STUN_DATA_ATTR. We can skip parsing rest of the packet. + break; + } + + if (!data_attr_present) { + // There is no data attribute present in the message. We can't do anything + // with the message. + return false; + } + + } else { + // This is a raw RTP packet. + rtp_begin = 0; + rtp_length = length; + } + + // Making sure we have a valid RTP packet at the end. + if (IsRtpPacket(packet + rtp_begin, rtp_length) && + ValidateRtpHeader(packet + rtp_begin, rtp_length)) { + *rtp_start_pos = rtp_begin; + *rtp_packet_length = rtp_length; + return true; + } + return false; +} + +// ValidateRtpHeader must be called before this method to make sure, we have +// a sane rtp packet. +bool UpdateRtpAbsSendTimeExtn(char* rtp, int length, + int extension_id, uint32 abs_send_time) { + // 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; + } + + int cc_count = rtp[0] & 0x0F; + int rtp_hdr_len_without_extn = kMinRtpHdrLen + 4 * cc_count; + + rtp += rtp_hdr_len_without_extn; + + // Getting extension profile ID and length. + uint16 profile_id = talk_base::GetBE16(rtp); + // Length is in 32 bit words. + uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4; + + rtp += kRtpExtnHdrLen; // Moving past extn 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 | len | + // +-+-+-+-+-+-+-+-+ + + // 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 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + char* extn_start = rtp; + while (rtp - extn_start < extn_length) { + const int id = (*rtp & 0xF0) >> 4; + const int len = (*rtp & 0x0F) + 1; + // 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) { + UpdateAbsSendTimeExtnValue(rtp + kOneByteHdrLen, len, abs_send_time); + found = true; + break; + } + rtp += kOneByteHdrLen + len; + // Counting padding bytes. + while ((*rtp == 0) && (rtp - extn_start < extn_length)) { + ++rtp; + } + } + } + return found; +} + +} // packet_processing_helpers + P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, int id) : message_sender_(message_sender), diff --git a/content/browser/renderer_host/p2p/socket_host.h b/content/browser/renderer_host/p2p/socket_host.h index d4ff1da..a4c82f3 100644 --- a/content/browser/renderer_host/p2p/socket_host.h +++ b/content/browser/renderer_host/p2p/socket_host.h @@ -25,6 +25,28 @@ struct PacketOptions; namespace content { class P2PMessageThrottler; +namespace packet_processing_helpers { + +// This method can handle only RTP packet, otherwise this method must not be +// called. It will try to do, 1. update absolute send time extension header +// if present with current time and 2. update HMAC in RTP packet. +// If abs_send_time is 0, ApplyPacketOption will get current time from system. +CONTENT_EXPORT bool ApplyPacketOptions(char* data, int length, + const talk_base::PacketOptions& options, + uint32 abs_send_time); + +// Helper method which finds RTP ofset and length if the packet is encapsulated +// in a TURN Channel Message or TURN Send Indication message. +CONTENT_EXPORT bool GetRtpPacketStartPositionAndLength(char* data, int length, + int* rtp_start_pos, + int* rtp_packet_length); +// Helper method which updates absoulute send time extension if present. +CONTENT_EXPORT bool UpdateRtpAbsSendTimeExtn(char* rtp, int length, + int extension_id, + uint32 abs_send_time); + +} // packet_processing_helpers + // Base class for P2P sockets. class CONTENT_EXPORT P2PSocketHost { public: diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.cc b/content/browser/renderer_host/p2p/socket_host_tcp.cc index 00f2e87..9b4acb7 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -18,6 +18,7 @@ #include "net/socket/tcp_client_socket.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h" namespace { @@ -318,7 +319,7 @@ void P2PSocketHostTcpBase::Send(const net::IPEndPoint& to, } } - DoSend(to, data); + DoSend(to, data, options); } void P2PSocketHostTcpBase::WriteOrQueue( @@ -450,13 +451,17 @@ int P2PSocketHostTcp::ProcessInput(char* input, int input_len) { } void P2PSocketHostTcp::DoSend(const net::IPEndPoint& to, - const std::vector<char>& data) { + const std::vector<char>& data, + const talk_base::PacketOptions& options) { int size = kPacketHeaderSize + data.size(); scoped_refptr<net::DrainableIOBuffer> buffer = new net::DrainableIOBuffer(new net::IOBuffer(size), size); *reinterpret_cast<uint16*>(buffer->data()) = base::HostToNet16(data.size()); memcpy(buffer->data() + kPacketHeaderSize, &data[0], data.size()); + packet_processing_helpers::ApplyPacketOptions( + buffer->data(), buffer->BytesRemaining(), options, 0); + WriteOrQueue(buffer); } @@ -495,7 +500,8 @@ int P2PSocketHostStunTcp::ProcessInput(char* input, int input_len) { } void P2PSocketHostStunTcp::DoSend(const net::IPEndPoint& to, - const std::vector<char>& data) { + const std::vector<char>& data, + const talk_base::PacketOptions& options) { // Each packet is expected to have header (STUN/TURN ChannelData), where // header contains message type and and length of message. if (data.size() < kPacketHeaderSize + kPacketLengthOffset) { @@ -522,6 +528,9 @@ void P2PSocketHostStunTcp::DoSend(const net::IPEndPoint& to, new net::DrainableIOBuffer(new net::IOBuffer(size), size); memcpy(buffer->data(), &data[0], data.size()); + packet_processing_helpers::ApplyPacketOptions( + buffer->data(), data.size(), options, 0); + if (pad_bytes) { char padding[4] = {0}; DCHECK_LE(pad_bytes, 4); diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.h b/content/browser/renderer_host/p2p/socket_host_tcp.h index b7ed2bc1..fcaac2a 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.h +++ b/content/browser/renderer_host/p2p/socket_host_tcp.h @@ -52,7 +52,8 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost { // Derived classes will provide the implementation. virtual int ProcessInput(char* input, int input_len) = 0; virtual void DoSend(const net::IPEndPoint& to, - const std::vector<char>& data) = 0; + const std::vector<char>& data, + const talk_base::PacketOptions& options) = 0; void WriteOrQueue(scoped_refptr<net::DrainableIOBuffer>& buffer); void OnPacket(const std::vector<char>& data); @@ -108,7 +109,8 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHostTcpBase { protected: virtual int ProcessInput(char* input, int input_len) OVERRIDE; virtual void DoSend(const net::IPEndPoint& to, - const std::vector<char>& data) OVERRIDE; + const std::vector<char>& data, + const talk_base::PacketOptions& options) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(P2PSocketHostTcp); }; @@ -129,7 +131,8 @@ class CONTENT_EXPORT P2PSocketHostStunTcp : public P2PSocketHostTcpBase { protected: virtual int ProcessInput(char* input, int input_len) OVERRIDE; virtual void DoSend(const net::IPEndPoint& to, - const std::vector<char>& data) OVERRIDE; + const std::vector<char>& data, + const talk_base::PacketOptions& options) OVERRIDE; private: int GetExpectedPacketSize(const char* data, int len, int* pad_bytes); diff --git a/content/browser/renderer_host/p2p/socket_host_udp.cc b/content/browser/renderer_host/p2p/socket_host_udp.cc index 7569bb3..db17c5f 100644 --- a/content/browser/renderer_host/p2p/socket_host_udp.cc +++ b/content/browser/renderer_host/p2p/socket_host_udp.cc @@ -15,6 +15,7 @@ #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h" namespace { @@ -234,6 +235,8 @@ void P2PSocketHostUdp::DoSend(const PendingPacket& packet) { last_dscp_ = net::DSCP_NO_CHANGE; } } + packet_processing_helpers::ApplyPacketOptions( + packet.data->data(), packet.size, packet.packet_options, 0); int result = socket_->SendTo( packet.data.get(), packet.size, diff --git a/content/browser/renderer_host/p2p/socket_host_unittest.cc b/content/browser/renderer_host/p2p/socket_host_unittest.cc new file mode 100644 index 0000000..73dd900 --- /dev/null +++ b/content/browser/renderer_host/p2p/socket_host_unittest.cc @@ -0,0 +1,376 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/renderer_host/p2p/socket_host.h" + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/p2p/socket_host_test_utils.h" +#include "net/base/ip_endpoint.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +static unsigned char kFakeTag[4] = { 0xba, 0xdd, 0xba, 0xdd }; +static unsigned char kTestKey[] = "12345678901234567890"; +static unsigned char kTestAstValue[3] = { 0xaa, 0xbb, 0xcc }; + +// Rtp message with invalid length. +static unsigned char 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. +}; + +// Rtp message with single byte header extension, invalid extension length. +static unsigned char kRtpMsgWithInvalidExtnLength[] = { + 0x90, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xBE, 0xDE, 0x0A, 0x00 // Extn length - 0x0A00 +}; + +// Valid rtp Message with 2 byte header extension. +static unsigned char 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 +}; + +// Stun Indication message with Zero length +static unsigned char 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', +}; + +// Stun Send Indication message with invalid length in stun header. +static unsigned char 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', +}; + +// Stun Send Indication message with no DATA attribute in message. +static unsigned char 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 +}; + +// A valid STUN indication message with a valid RTP header in data attribute +// payload field and no extension bit set. +static unsigned char 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, +}; + +// A valid STUN indication message with a valid RTP header and a extension +// header. +static unsigned char kTurnSendIndicationMsgWithAbsSendTimeExtension[] = { + 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, +}; + +// A valid TURN channel header, but not RTP packet. +static unsigned char kTurnChannelMsgNoRtpPacket[] = { + 0x40, 0x00, 0x00, 0x04, + 0xaa, 0xbb, 0xcc, 0xdd, +}; + +// Turn ChannelMessage with zero length of payload. +static unsigned char kTurnChannelMsgWithZeroLength[] = { + 0x40, 0x00, 0x00, 0x00 +}; + +// Turn ChannelMessage, wrapping a RTP packet without extension. +static unsigned char kTurnChannelMsgWithRtpPacket[] = { + 0x40, 0x00, 0x00, 0x0C, + 0x80, 0x00, 0x00, 0x00, // RTP packet. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +// Turn ChannelMessage, wrapping a RTP packet with AbsSendTime Extension. +static unsigned char kTurnChannelMsgWithAbsSendTimeExtension[] = { + 0x40, 0x00, 0x00, 0x14, + 0x90, 0x00, 0x00, 0x00, // RTP packet. + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xBE, 0xDE, 0x00, 0x01, + 0x32, 0xaa, 0xbb, 0xcc, +}; + +// RTP packet with single byte extension header of length 4 bytes. +// Extension id = 3 and length = 3 +static unsigned char 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; + +namespace content { + +// This test verifies parsing of all invalid raw packets. +TEST(P2PSocketHostTest, TestInvalidRawRtpMessages) { + int start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kRtpMsgWithInvalidLength), + sizeof(kRtpMsgWithInvalidLength), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); + + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kRtpMsgWithInvalidExtnLength), + sizeof(kRtpMsgWithInvalidExtnLength), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); +} + +// Verify invalid TURN send indication messages. Messages are proper STUN +// messages with incorrect values in attributes. +TEST(P2PSocketHostTest, TestInvalidTurnSendIndicationMessages) { + // Initializing out params to very large value. + int start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnSendIndicationMsgWithNoAttributes), + sizeof(kTurnSendIndicationMsgWithNoAttributes), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); + + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnSendIndicationMsgWithInvalidLength), + sizeof(kTurnSendIndicationMsgWithInvalidLength), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); + + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnSendIndicatinMsgWithNoDataAttribute), + sizeof(kTurnSendIndicatinMsgWithNoDataAttribute), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); +} + +// This test verifies incorrectly formed TURN channel messages. +TEST(P2PSocketHostTest, TestInvalidTurnChannelMessages) { + int start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnChannelMsgNoRtpPacket), + sizeof(kTurnChannelMsgNoRtpPacket), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); + + EXPECT_FALSE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnChannelMsgWithZeroLength), + sizeof(kTurnChannelMsgWithZeroLength), + &start_pos, &rtp_length)); + EXPECT_EQ(INT_MAX, start_pos); + EXPECT_EQ(INT_MAX, rtp_length); +} + +// This test verifies parsing of a valid RTP packet which has 2byte header +// extension instead of a 1 byte header extension. +TEST(P2PSocketHostTest, TestValid2ByteExtnHdrRtpMessage) { + int start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kRtpMsgWith2ByteExtnHeader), + sizeof(kRtpMsgWith2ByteExtnHeader), + &start_pos, &rtp_length)); + EXPECT_EQ(20, rtp_length); + EXPECT_EQ(0, start_pos); +} + +// This test verifies parsing of a valid RTP packet which has 1 byte header +// AbsSendTime extension in it. +TEST(P2PSocketHostTest, TestValidRtpPacketWithAbsSendTimeExtension) { + int start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kRtpMsgWithAbsSendTimeExtension), + sizeof(kRtpMsgWithAbsSendTimeExtension), + &start_pos, &rtp_length)); + EXPECT_EQ(24, rtp_length); + EXPECT_EQ(0, start_pos); +} + +// This test verifies parsing of a valid TURN Send Indication messages. +TEST(P2PSocketHostTest, TestValidTurnSendIndicationMessages) { + int start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnSendIndicationMsgWithoutRtpExtension), + sizeof(kTurnSendIndicationMsgWithoutRtpExtension), + &start_pos, &rtp_length)); + EXPECT_EQ(12, rtp_length); + EXPECT_EQ(32, start_pos); + + start_pos = INT_MAX, rtp_length = INT_MAX; + EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnSendIndicationMsgWithAbsSendTimeExtension), + sizeof(kTurnSendIndicationMsgWithAbsSendTimeExtension), + &start_pos, &rtp_length)); + EXPECT_EQ(24, rtp_length); + EXPECT_EQ(32, start_pos); +} + +// This test verifies parsing of valid TURN Channel Messages. +TEST(P2PSocketHostTest, TestValidTurnChannelMessages) { + int start_pos = -1, rtp_length = -1; + EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnChannelMsgWithRtpPacket), + sizeof(kTurnChannelMsgWithRtpPacket), &start_pos, &rtp_length)); + EXPECT_EQ(12, rtp_length); + EXPECT_EQ(4, start_pos); + + start_pos = -1, rtp_length = -1; + EXPECT_TRUE(packet_processing_helpers::GetRtpPacketStartPositionAndLength( + reinterpret_cast<char*>(kTurnChannelMsgWithAbsSendTimeExtension), + sizeof(kTurnChannelMsgWithAbsSendTimeExtension), + &start_pos, &rtp_length)); + EXPECT_EQ(20, rtp_length); + EXPECT_EQ(4, start_pos); +} + +// Verify handling of a 2 byte extension header RTP messsage. Currently we don't +// handle this kind of message. +TEST(P2PSocketHostTest, TestUpdateAbsSendTimeExtensionIn2ByteHeaderExtn) { + EXPECT_FALSE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn( + reinterpret_cast<char*>(kRtpMsgWith2ByteExtnHeader), + sizeof(kRtpMsgWith2ByteExtnHeader), 3, 0)); +} + +// Verify finding an extension ID in the TURN send indication message. +TEST(P2PSocketHostTest, TestUpdateAbsSendTimeExtensionInTurnSendIndication) { + EXPECT_TRUE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn( + reinterpret_cast<char*>(kTurnSendIndicationMsgWithoutRtpExtension), + sizeof(kTurnSendIndicationMsgWithoutRtpExtension), 3, 0)); + + EXPECT_TRUE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn( + reinterpret_cast<char*>(kTurnSendIndicationMsgWithAbsSendTimeExtension), + sizeof(kTurnSendIndicationMsgWithAbsSendTimeExtension), 3, 0)); +} + +// Verify finding an extension ID in a raw rtp message. +TEST(P2PSocketHostTest, TestUpdateAbsSendTimeExtensionInRtpPacket) { + EXPECT_TRUE(packet_processing_helpers::UpdateRtpAbsSendTimeExtn( + reinterpret_cast<char*>(kRtpMsgWithAbsSendTimeExtension), + sizeof(kRtpMsgWithAbsSendTimeExtension), 3, 0)); +} + +// Test without any packet options variables set. This method should return +// without HMAC value in the packet. +TEST(P2PSocketHostTest, TestApplyPacketOptionsWithDefaultValues) { + unsigned char fake_tag[4] = { 0xba, 0xdd, 0xba, 0xdd }; + talk_base::PacketOptions options; + std::vector<char> rtp_packet; + rtp_packet.resize(sizeof(kRtpMsgWithAbsSendTimeExtension) + 4); // tag length + memcpy(&rtp_packet[0], kRtpMsgWithAbsSendTimeExtension, + sizeof(kRtpMsgWithAbsSendTimeExtension)); + memcpy(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], fake_tag, 4); + EXPECT_TRUE( + packet_processing_helpers::ApplyPacketOptions( + &rtp_packet[0], rtp_packet.size(), options, 0)); + // Making sure we have't updated the HMAC. + EXPECT_EQ(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], + fake_tag, 4)); + + // Verify AbsouluteSendTime extension field is not modified. + EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], + kTestAstValue, sizeof(kTestAstValue))); +} + +// Veirfy HMAC is updated when packet option parameters are set. +TEST(P2PSocketHostTest, TestApplyPacketOptionsWithAuthParams) { + talk_base::PacketOptions options; + options.packet_time_params.srtp_auth_key.resize(20); + options.packet_time_params.srtp_auth_key.assign( + kTestKey, kTestKey + sizeof(kTestKey)); + options.packet_time_params.srtp_auth_tag_len = 4; + + std::vector<char> rtp_packet; + rtp_packet.resize(sizeof(kRtpMsgWithAbsSendTimeExtension) + 4); // tag length + memcpy(&rtp_packet[0], kRtpMsgWithAbsSendTimeExtension, + sizeof(kRtpMsgWithAbsSendTimeExtension)); + memcpy(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], kFakeTag, 4); + EXPECT_TRUE(packet_processing_helpers::ApplyPacketOptions( + &rtp_packet[0], rtp_packet.size(), options, 0)); + // HMAC should be different from fake_tag. + EXPECT_NE(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], + kFakeTag, sizeof(kFakeTag))); + + // Verify AbsouluteSendTime extension field is not modified. + EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], + kTestAstValue, sizeof(kTestAstValue))); +} + +// Verify we update both AbsSendTime extension header and HMAC. +TEST(P2PSocketHostTest, TestApplyPacketOptionsWithAuthParamsAndAbsSendTime) { + talk_base::PacketOptions options; + options.packet_time_params.srtp_auth_key.resize(20); + options.packet_time_params.srtp_auth_key.assign( + kTestKey, kTestKey + sizeof(kTestKey)); + options.packet_time_params.srtp_auth_tag_len = 4; + options.packet_time_params.rtp_sendtime_extension_id = 3; + // 3 is also present in the test message. + + std::vector<char> rtp_packet; + rtp_packet.resize(sizeof(kRtpMsgWithAbsSendTimeExtension) + 4); // tag length + memcpy(&rtp_packet[0], kRtpMsgWithAbsSendTimeExtension, + sizeof(kRtpMsgWithAbsSendTimeExtension)); + memcpy(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], kFakeTag, 4); + EXPECT_TRUE(packet_processing_helpers::ApplyPacketOptions( + &rtp_packet[0], rtp_packet.size(), options, 0xccbbaa)); + // HMAC should be different from fake_tag. + EXPECT_NE(0, memcmp(&rtp_packet[sizeof(kRtpMsgWithAbsSendTimeExtension)], + kFakeTag, sizeof(kFakeTag))); + + // ApplyPackets should have the new timestamp passed as input. + unsigned char timestamp_array[3] = { 0xcc, 0xbb, 0xaa }; + EXPECT_EQ(0, memcmp(&rtp_packet[kAstIndexInRtpMsg], + timestamp_array, sizeof(timestamp_array))); +} + +} // namespace content diff --git a/content/content_tests.gypi b/content/content_tests.gypi index eb4b07b..ebd5f91 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -710,6 +710,7 @@ 'browser/renderer_host/p2p/socket_host_tcp_unittest.cc', 'browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc', 'browser/renderer_host/p2p/socket_host_udp_unittest.cc', + 'browser/renderer_host/p2p/socket_host_unittest.cc', 'renderer/media/media_stream_audio_processor_unittest.cc', 'renderer/media/media_stream_dependency_factory_unittest.cc', 'renderer/media/media_stream_dispatcher_unittest.cc', diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp index 6e8319f..c7dc2f8 100644 --- a/third_party/libjingle/libjingle.gyp +++ b/third_party/libjingle/libjingle.gyp @@ -435,6 +435,8 @@ '<(libjingle_source)/talk/session/media/channelmanager.h', '<(libjingle_source)/talk/session/media/currentspeakermonitor.cc', '<(libjingle_source)/talk/session/media/currentspeakermonitor.h', + '<(libjingle_source)/talk/session/media/externalhmac.cc', + '<(libjingle_source)/talk/session/media/externalhmac.h', '<(libjingle_source)/talk/session/media/mediamessages.cc', '<(libjingle_source)/talk/session/media/mediamessages.h', '<(libjingle_source)/talk/session/media/mediamonitor.cc', |