/* * Copyright 2013 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/p2p/base/asyncstuntcpsocket.h" #include #include "webrtc/p2p/base/stun.h" #include "webrtc/base/common.h" #include "webrtc/base/logging.h" namespace cricket { static const size_t kMaxPacketSize = 64 * 1024; typedef uint16_t PacketLength; static const size_t kPacketLenSize = sizeof(PacketLength); static const size_t kPacketLenOffset = 2; static const size_t kBufSize = kMaxPacketSize + kStunHeaderSize; static const size_t kTurnChannelDataHdrSize = 4; inline bool IsStunMessage(uint16_t msg_type) { // The first two bits of a channel data message are 0b01. return (msg_type & 0xC000) ? false : true; } // AsyncStunTCPSocket // Binds and connects |socket| and creates AsyncTCPSocket for // it. Takes ownership of |socket|. Returns NULL if bind() or // connect() fail (|socket| is destroyed in that case). AsyncStunTCPSocket* AsyncStunTCPSocket::Create( rtc::AsyncSocket* socket, const rtc::SocketAddress& bind_address, const rtc::SocketAddress& remote_address) { return new AsyncStunTCPSocket(AsyncTCPSocketBase::ConnectSocket( socket, bind_address, remote_address), false); } AsyncStunTCPSocket::AsyncStunTCPSocket( rtc::AsyncSocket* socket, bool listen) : rtc::AsyncTCPSocketBase(socket, listen, kBufSize) { } int AsyncStunTCPSocket::Send(const void *pv, size_t cb, const rtc::PacketOptions& options) { if (cb > kBufSize || cb < kPacketLenSize + kPacketLenOffset) { SetError(EMSGSIZE); return -1; } // If we are blocking on send, then silently drop this packet if (!IsOutBufferEmpty()) return static_cast(cb); int pad_bytes; size_t expected_pkt_len = GetExpectedLength(pv, cb, &pad_bytes); // Accepts only complete STUN/ChannelData packets. if (cb != expected_pkt_len) return -1; AppendToOutBuffer(pv, cb); ASSERT(pad_bytes < 4); char padding[4] = {0}; AppendToOutBuffer(padding, pad_bytes); int res = FlushOutBuffer(); if (res <= 0) { // drop packet if we made no progress ClearOutBuffer(); return res; } // We claim to have sent the whole thing, even if we only sent partial return static_cast(cb); } void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) { rtc::SocketAddress remote_addr(GetRemoteAddress()); // STUN packet - First 4 bytes. Total header size is 20 bytes. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |0 0| STUN Message Type | Message Length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // TURN ChannelData // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Channel Number | Length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ while (true) { // We need at least 4 bytes to read the STUN or ChannelData packet length. if (*len < kPacketLenOffset + kPacketLenSize) return; int pad_bytes; size_t expected_pkt_len = GetExpectedLength(data, *len, &pad_bytes); size_t actual_length = expected_pkt_len + pad_bytes; if (*len < actual_length) { return; } SignalReadPacket(this, data, expected_pkt_len, remote_addr, rtc::CreatePacketTime(0)); *len -= actual_length; if (*len > 0) { memmove(data, data + actual_length, *len); } } } void AsyncStunTCPSocket::HandleIncomingConnection( rtc::AsyncSocket* socket) { SignalNewConnection(this, new AsyncStunTCPSocket(socket, false)); } size_t AsyncStunTCPSocket::GetExpectedLength(const void* data, size_t len, int* pad_bytes) { *pad_bytes = 0; PacketLength pkt_len = rtc::GetBE16(static_cast(data) + kPacketLenOffset); size_t expected_pkt_len; uint16_t msg_type = rtc::GetBE16(data); if (IsStunMessage(msg_type)) { // STUN message. expected_pkt_len = kStunHeaderSize + pkt_len; } else { // TURN ChannelData message. expected_pkt_len = kTurnChannelDataHdrSize + pkt_len; // From RFC 5766 section 11.5 // Over TCP and TLS-over-TCP, the ChannelData message MUST be padded to // a multiple of four bytes in order to ensure the alignment of // subsequent messages. The padding is not reflected in the length // field of the ChannelData message, so the actual size of a ChannelData // message (including padding) is (4 + Length) rounded up to the nearest // multiple of 4. Over UDP, the padding is not required but MAY be // included. if (expected_pkt_len % 4) *pad_bytes = 4 - (expected_pkt_len % 4); } return expected_pkt_len; } } // namespace cricket