1/*
2 *  Copyright 2013 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/p2p/base/asyncstuntcpsocket.h"
12
13#include <string.h>
14
15#include "webrtc/p2p/base/stun.h"
16#include "webrtc/base/common.h"
17#include "webrtc/base/logging.h"
18
19namespace cricket {
20
21static const size_t kMaxPacketSize = 64 * 1024;
22
23typedef uint16_t PacketLength;
24static const size_t kPacketLenSize = sizeof(PacketLength);
25static const size_t kPacketLenOffset = 2;
26static const size_t kBufSize = kMaxPacketSize + kStunHeaderSize;
27static const size_t kTurnChannelDataHdrSize = 4;
28
29inline bool IsStunMessage(uint16_t msg_type) {
30  // The first two bits of a channel data message are 0b01.
31  return (msg_type & 0xC000) ? false : true;
32}
33
34// AsyncStunTCPSocket
35// Binds and connects |socket| and creates AsyncTCPSocket for
36// it. Takes ownership of |socket|. Returns NULL if bind() or
37// connect() fail (|socket| is destroyed in that case).
38AsyncStunTCPSocket* AsyncStunTCPSocket::Create(
39    rtc::AsyncSocket* socket,
40    const rtc::SocketAddress& bind_address,
41    const rtc::SocketAddress& remote_address) {
42  return new AsyncStunTCPSocket(AsyncTCPSocketBase::ConnectSocket(
43      socket, bind_address, remote_address), false);
44}
45
46AsyncStunTCPSocket::AsyncStunTCPSocket(
47    rtc::AsyncSocket* socket, bool listen)
48    : rtc::AsyncTCPSocketBase(socket, listen, kBufSize) {
49}
50
51int AsyncStunTCPSocket::Send(const void *pv, size_t cb,
52                             const rtc::PacketOptions& options) {
53  if (cb > kBufSize || cb < kPacketLenSize + kPacketLenOffset) {
54    SetError(EMSGSIZE);
55    return -1;
56  }
57
58  // If we are blocking on send, then silently drop this packet
59  if (!IsOutBufferEmpty())
60    return static_cast<int>(cb);
61
62  int pad_bytes;
63  size_t expected_pkt_len = GetExpectedLength(pv, cb, &pad_bytes);
64
65  // Accepts only complete STUN/ChannelData packets.
66  if (cb != expected_pkt_len)
67    return -1;
68
69  AppendToOutBuffer(pv, cb);
70
71  ASSERT(pad_bytes < 4);
72  char padding[4] = {0};
73  AppendToOutBuffer(padding, pad_bytes);
74
75  int res = FlushOutBuffer();
76  if (res <= 0) {
77    // drop packet if we made no progress
78    ClearOutBuffer();
79    return res;
80  }
81
82  // We claim to have sent the whole thing, even if we only sent partial
83  return static_cast<int>(cb);
84}
85
86void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) {
87  rtc::SocketAddress remote_addr(GetRemoteAddress());
88  // STUN packet - First 4 bytes. Total header size is 20 bytes.
89  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90  // |0 0|     STUN Message Type     |         Message Length        |
91  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92
93  // TURN ChannelData
94  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95  // |         Channel Number        |            Length             |
96  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97
98  while (true) {
99    // We need at least 4 bytes to read the STUN or ChannelData packet length.
100    if (*len < kPacketLenOffset + kPacketLenSize)
101      return;
102
103    int pad_bytes;
104    size_t expected_pkt_len = GetExpectedLength(data, *len, &pad_bytes);
105    size_t actual_length = expected_pkt_len + pad_bytes;
106
107    if (*len < actual_length) {
108      return;
109    }
110
111    SignalReadPacket(this, data, expected_pkt_len, remote_addr,
112                     rtc::CreatePacketTime(0));
113
114    *len -= actual_length;
115    if (*len > 0) {
116      memmove(data, data + actual_length, *len);
117    }
118  }
119}
120
121void AsyncStunTCPSocket::HandleIncomingConnection(
122    rtc::AsyncSocket* socket) {
123  SignalNewConnection(this, new AsyncStunTCPSocket(socket, false));
124}
125
126size_t AsyncStunTCPSocket::GetExpectedLength(const void* data, size_t len,
127                                             int* pad_bytes) {
128  *pad_bytes = 0;
129  PacketLength pkt_len =
130      rtc::GetBE16(static_cast<const char*>(data) + kPacketLenOffset);
131  size_t expected_pkt_len;
132  uint16_t msg_type = rtc::GetBE16(data);
133  if (IsStunMessage(msg_type)) {
134    // STUN message.
135    expected_pkt_len = kStunHeaderSize + pkt_len;
136  } else {
137    // TURN ChannelData message.
138    expected_pkt_len = kTurnChannelDataHdrSize + pkt_len;
139    // From RFC 5766 section 11.5
140    // Over TCP and TLS-over-TCP, the ChannelData message MUST be padded to
141    // a multiple of four bytes in order to ensure the alignment of
142    // subsequent messages.  The padding is not reflected in the length
143    // field of the ChannelData message, so the actual size of a ChannelData
144    // message (including padding) is (4 + Length) rounded up to the nearest
145    // multiple of 4.  Over UDP, the padding is not required but MAY be
146    // included.
147    if (expected_pkt_len % 4)
148      *pad_bytes = 4 - (expected_pkt_len % 4);
149  }
150  return expected_pkt_len;
151}
152
153}  // namespace cricket
154