This source file includes following definitions.
- IsTurnChannelData
- IsDtlsPacket
- IsRtcpPacket
- IsTurnSendIndicationPacket
- IsRtpPacket
- ValidateRtpHeader
- UpdateAbsSendTimeExtnValue
- UpdateRtpAuthTag
- ApplyPacketOptions
- GetRtpPacketStartPositionAndLength
- UpdateRtpAbsSendTimeExtn
- state_
- GetStunPacketType
- IsRequestOrResponse
- Create
#include "content/browser/renderer_host/p2p/socket_host.h"
#include "base/sys_byteorder.h"
#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;
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);
}
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 (!(rtp[0] & 0x10)) {
return true;
}
rtp += rtp_hdr_len_without_extn;
uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4;
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) {
DCHECK_EQ(len, kAbsSendTimeExtnLen);
uint32 now_second = abs_send_time;
if (!now_second) {
uint64 now_us =
(base::TimeTicks::HighResNow() - base::TimeTicks()).InMicroseconds();
now_second =
((now_us << 18) / base::Time::kMicrosecondsPerSecond) & 0x00FFFFFF;
}
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);
}
void UpdateRtpAuthTag(char* rtp, int len,
const talk_base::PacketOptions& options) {
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);
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;
}
memcpy(auth_tag, &options.packet_time_params.srtp_packet_index, 4);
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;
}
memcpy(auth_tag, output, tag_length);
}
}
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 (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));
int rtp_start_pos;
int rtp_length;
if (!GetRtpPacketStartPositionAndLength(
data, length, &rtp_start_pos, &rtp_length)) {
NOTREACHED();
return false;
}
char* start = data + rtp_start_pos;
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)) {
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) {
return false;
}
int stun_msg_len = talk_base::GetBE16(&packet[2]);
if (stun_msg_len + P2PSocketHost::kStunHeaderSize != length) {
return false;
}
rtp_begin = P2PSocketHost::kStunHeaderSize;
char* start = packet + rtp_begin;
bool data_attr_present = false;
while ((packet + rtp_begin) - start < stun_msg_len) {
uint16 attr_type, attr_length;
attr_type = talk_base::GetBE16(&packet[rtp_begin]);
attr_length = talk_base::GetBE16(
&packet[rtp_begin + sizeof(attr_type)]);
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;
rtp_length = attr_length;
if (length < rtp_length + rtp_begin) {
return false;
}
break;
}
if (!data_attr_present) {
return false;
}
} else {
rtp_begin = 0;
rtp_length = length;
}
if (!(rtp_length < kMinRtpHdrLen) &&
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;
}
bool UpdateRtpAbsSendTimeExtn(char* rtp, int length,
int extension_id, uint32 abs_send_time) {
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;
uint16 profile_id = talk_base::GetBE16(rtp);
uint16 extn_length = talk_base::GetBE16(rtp + 2) * 4;
rtp += kRtpExtnHdrLen;
bool found = false;
if (profile_id == 0xBEDE) {
char* extn_start = rtp;
while (rtp - extn_start < extn_length) {
const int id = (*rtp & 0xF0) >> 4;
const int len = (*rtp & 0x0F) + 1;
if (id == extension_id) {
UpdateAbsSendTimeExtnValue(rtp + kOneByteHdrLen, len, abs_send_time);
found = true;
break;
}
rtp += kOneByteHdrLen + len;
while ((*rtp == 0) && (rtp - extn_start < extn_length)) {
++rtp;
}
}
}
return found;
}
}
P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender,
int id)
: message_sender_(message_sender),
id_(id),
state_(STATE_UNINITIALIZED) {
}
P2PSocketHost::~P2PSocketHost() { }
bool P2PSocketHost::GetStunPacketType(
const char* data, int data_size, StunMessageType* type) {
if (data_size < kStunHeaderSize)
return false;
uint32 cookie = base::NetToHost32(*reinterpret_cast<const uint32*>(data + 4));
if (cookie != kStunMagicCookie)
return false;
uint16 length = base::NetToHost16(*reinterpret_cast<const uint16*>(data + 2));
if (length != data_size - kStunHeaderSize)
return false;
int message_type = base::NetToHost16(*reinterpret_cast<const uint16*>(data));
switch (message_type) {
case STUN_BINDING_REQUEST:
case STUN_BINDING_RESPONSE:
case STUN_BINDING_ERROR_RESPONSE:
case STUN_SHARED_SECRET_REQUEST:
case STUN_SHARED_SECRET_RESPONSE:
case STUN_SHARED_SECRET_ERROR_RESPONSE:
case STUN_ALLOCATE_REQUEST:
case STUN_ALLOCATE_RESPONSE:
case STUN_ALLOCATE_ERROR_RESPONSE:
case STUN_SEND_REQUEST:
case STUN_SEND_RESPONSE:
case STUN_SEND_ERROR_RESPONSE:
case STUN_DATA_INDICATION:
*type = static_cast<StunMessageType>(message_type);
return true;
default:
return false;
}
}
bool P2PSocketHost::IsRequestOrResponse(StunMessageType type) {
return type == STUN_BINDING_REQUEST || type == STUN_BINDING_RESPONSE ||
type == STUN_ALLOCATE_REQUEST || type == STUN_ALLOCATE_RESPONSE;
}
P2PSocketHost* P2PSocketHost::Create(
IPC::Sender* message_sender, int id, P2PSocketType type,
net::URLRequestContextGetter* url_context,
P2PMessageThrottler* throttler) {
switch (type) {
case P2P_SOCKET_UDP:
return new P2PSocketHostUdp(message_sender, id, throttler);
case P2P_SOCKET_TCP_SERVER:
return new P2PSocketHostTcpServer(
message_sender, id, P2P_SOCKET_TCP_CLIENT);
case P2P_SOCKET_STUN_TCP_SERVER:
return new P2PSocketHostTcpServer(
message_sender, id, P2P_SOCKET_STUN_TCP_CLIENT);
case P2P_SOCKET_TCP_CLIENT:
case P2P_SOCKET_SSLTCP_CLIENT:
case P2P_SOCKET_TLS_CLIENT:
return new P2PSocketHostTcp(message_sender, id, type, url_context);
case P2P_SOCKET_STUN_TCP_CLIENT:
case P2P_SOCKET_STUN_SSLTCP_CLIENT:
case P2P_SOCKET_STUN_TLS_CLIENT:
return new P2PSocketHostStunTcp(message_sender, id, type, url_context);
}
NOTREACHED();
return NULL;
}
}