1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/renderer_host/p2p/socket_host_udp.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/stl_util.h"
10#include "content/browser/renderer_host/p2p/socket_host_throttler.h"
11#include "content/common/p2p_messages.h"
12#include "content/public/browser/content_browser_client.h"
13#include "content/public/common/content_client.h"
14#include "ipc/ipc_sender.h"
15#include "net/base/io_buffer.h"
16#include "net/base/net_errors.h"
17#include "net/base/net_util.h"
18#include "third_party/webrtc/base/asyncpacketsocket.h"
19
20namespace {
21
22// UDP packets cannot be bigger than 64k.
23const int kReadBufferSize = 65536;
24// Socket receive buffer size.
25const int kRecvSocketBufferSize = 65536;  // 64K
26
27// Defines set of transient errors. These errors are ignored when we get them
28// from sendto() or recvfrom() calls.
29//
30// net::ERR_OUT_OF_MEMORY
31//
32// This is caused by ENOBUFS which means the buffer of the network interface
33// is full.
34//
35// net::ERR_CONNECTION_RESET
36//
37// This is caused by WSAENETRESET or WSAECONNRESET which means the
38// last send resulted in an "ICMP Port Unreachable" message.
39bool IsTransientError(int error) {
40  return error == net::ERR_ADDRESS_UNREACHABLE ||
41         error == net::ERR_ADDRESS_INVALID ||
42         error == net::ERR_ACCESS_DENIED ||
43         error == net::ERR_CONNECTION_RESET ||
44         error == net::ERR_OUT_OF_MEMORY ||
45         error == net::ERR_INTERNET_DISCONNECTED;
46}
47
48}  // namespace
49
50namespace content {
51
52P2PSocketHostUdp::PendingPacket::PendingPacket(
53    const net::IPEndPoint& to,
54    const std::vector<char>& content,
55    const rtc::PacketOptions& options,
56    uint64 id)
57    : to(to),
58      data(new net::IOBuffer(content.size())),
59      size(content.size()),
60      packet_options(options),
61      id(id) {
62  memcpy(data->data(), &content[0], size);
63}
64
65P2PSocketHostUdp::PendingPacket::~PendingPacket() {
66}
67
68P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
69                                   int socket_id,
70                                   P2PMessageThrottler* throttler)
71    : P2PSocketHost(message_sender, socket_id, P2PSocketHost::UDP),
72      socket_(
73          new net::UDPServerSocket(GetContentClient()->browser()->GetNetLog(),
74                                   net::NetLog::Source())),
75      send_pending_(false),
76      last_dscp_(net::DSCP_CS0),
77      throttler_(throttler) {
78}
79
80P2PSocketHostUdp::~P2PSocketHostUdp() {
81  if (state_ == STATE_OPEN) {
82    DCHECK(socket_.get());
83    socket_.reset();
84  }
85}
86
87bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
88                            const P2PHostAndIPEndPoint& remote_address) {
89  DCHECK_EQ(state_, STATE_UNINITIALIZED);
90
91  int result = socket_->Listen(local_address);
92  if (result < 0) {
93    LOG(ERROR) << "bind() failed: " << result;
94    OnError();
95    return false;
96  }
97
98  // Setting recv socket buffer size.
99  if (socket_->SetReceiveBufferSize(kRecvSocketBufferSize) != net::OK) {
100    LOG(WARNING) << "Failed to set socket receive buffer size to "
101                 << kRecvSocketBufferSize;
102  }
103
104  net::IPEndPoint address;
105  result = socket_->GetLocalAddress(&address);
106  if (result < 0) {
107    LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
108               << result;
109    OnError();
110    return false;
111  }
112  VLOG(1) << "Local address: " << address.ToString();
113
114  state_ = STATE_OPEN;
115
116  // NOTE: Remote address will be same as what renderer provided.
117  message_sender_->Send(new P2PMsg_OnSocketCreated(
118      id_, address, remote_address.ip_address));
119
120  recv_buffer_ = new net::IOBuffer(kReadBufferSize);
121  DoRead();
122
123  return true;
124}
125
126void P2PSocketHostUdp::OnError() {
127  socket_.reset();
128  send_queue_.clear();
129
130  if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
131    message_sender_->Send(new P2PMsg_OnError(id_));
132
133  state_ = STATE_ERROR;
134}
135
136void P2PSocketHostUdp::DoRead() {
137  int result;
138  do {
139    result = socket_->RecvFrom(
140        recv_buffer_.get(),
141        kReadBufferSize,
142        &recv_address_,
143        base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
144    if (result == net::ERR_IO_PENDING)
145      return;
146    HandleReadResult(result);
147  } while (state_ == STATE_OPEN);
148}
149
150void P2PSocketHostUdp::OnRecv(int result) {
151  HandleReadResult(result);
152  if (state_ == STATE_OPEN) {
153    DoRead();
154  }
155}
156
157void P2PSocketHostUdp::HandleReadResult(int result) {
158  DCHECK_EQ(STATE_OPEN, state_);
159
160  if (result > 0) {
161    std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
162
163    if (!ContainsKey(connected_peers_, recv_address_)) {
164      P2PSocketHost::StunMessageType type;
165      bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
166      if ((stun && IsRequestOrResponse(type))) {
167        connected_peers_.insert(recv_address_);
168      } else if (!stun || type == STUN_DATA_INDICATION) {
169        LOG(ERROR) << "Received unexpected data packet from "
170                   << recv_address_.ToString()
171                   << " before STUN binding is finished.";
172        return;
173      }
174    }
175
176    message_sender_->Send(new P2PMsg_OnDataReceived(
177        id_, recv_address_, data, base::TimeTicks::Now()));
178
179    if (dump_incoming_rtp_packet_)
180      DumpRtpPacket(&data[0], data.size(), true);
181  } else if (result < 0 && !IsTransientError(result)) {
182    LOG(ERROR) << "Error when reading from UDP socket: " << result;
183    OnError();
184  }
185}
186
187void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
188                            const std::vector<char>& data,
189                            const rtc::PacketOptions& options,
190                            uint64 packet_id) {
191  if (!socket_) {
192    // The Send message may be sent after the an OnError message was
193    // sent by hasn't been processed the renderer.
194    return;
195  }
196
197  if (!ContainsKey(connected_peers_, to)) {
198    P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
199    bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
200    if (!stun || type == STUN_DATA_INDICATION) {
201      LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
202                 << " before STUN binding is finished.";
203      OnError();
204      return;
205    }
206
207    if (throttler_->DropNextPacket(data.size())) {
208      VLOG(0) << "STUN message is dropped due to high volume.";
209      // Do not reset socket.
210      return;
211    }
212  }
213
214  IncrementTotalSentPackets();
215
216  if (send_pending_) {
217    send_queue_.push_back(PendingPacket(to, data, options, packet_id));
218    IncrementDelayedBytes(data.size());
219    IncrementDelayedPackets();
220  } else {
221    // TODO(mallinath: Remove unnecessary memcpy in this case.
222    PendingPacket packet(to, data, options, packet_id);
223    DoSend(packet);
224  }
225}
226
227void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
228  TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
229                               "size", packet.size);
230  // Don't try to set DSCP in following conditions,
231  // 1. If the outgoing packet is set to DSCP_NO_CHANGE
232  // 2. If no change in DSCP value from last packet
233  // 3. If there is any error in setting DSCP on socket.
234  net::DiffServCodePoint dscp =
235      static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
236  if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
237      last_dscp_ != net::DSCP_NO_CHANGE) {
238    int result = socket_->SetDiffServCodePoint(dscp);
239    if (result == net::OK) {
240      last_dscp_ = dscp;
241    } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
242      // We receieved a non-transient error, and it seems we have
243      // not changed the DSCP in the past, disable DSCP as it unlikely
244      // to work in the future.
245      last_dscp_ = net::DSCP_NO_CHANGE;
246    }
247  }
248  packet_processing_helpers::ApplyPacketOptions(
249      packet.data->data(), packet.size, packet.packet_options, 0);
250  int result = socket_->SendTo(
251      packet.data.get(),
252      packet.size,
253      packet.to,
254      base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
255
256  // sendto() may return an error, e.g. if we've received an ICMP Destination
257  // Unreachable message. When this happens try sending the same packet again,
258  // and just drop it if it fails again.
259  if (IsTransientError(result)) {
260    result = socket_->SendTo(
261        packet.data.get(),
262        packet.size,
263        packet.to,
264        base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
265                   packet.id));
266  }
267
268  if (result == net::ERR_IO_PENDING) {
269    send_pending_ = true;
270  } else {
271    HandleSendResult(packet.id, result);
272  }
273
274  if (dump_outgoing_rtp_packet_)
275    DumpRtpPacket(packet.data->data(), packet.size, false);
276}
277
278void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
279  DCHECK(send_pending_);
280  DCHECK_NE(result, net::ERR_IO_PENDING);
281
282  send_pending_ = false;
283
284  HandleSendResult(packet_id, result);
285
286  // Send next packets if we have them waiting in the buffer.
287  while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
288    PendingPacket packet = send_queue_.front();
289    DoSend(packet);
290    send_queue_.pop_front();
291    DecrementDelayedBytes(packet.size);
292  }
293}
294
295void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
296  TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
297                         "result", result);
298  if (result < 0) {
299    if (!IsTransientError(result)) {
300      LOG(ERROR) << "Error when sending data in UDP socket: " << result;
301      OnError();
302      return;
303    }
304    VLOG(0) << "sendto() has failed twice returning a "
305               " transient error. Dropping the packet.";
306  }
307  message_sender_->Send(new P2PMsg_OnSendComplete(id_));
308}
309
310P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
311    const net::IPEndPoint& remote_address, int id) {
312  NOTREACHED();
313  OnError();
314  return NULL;
315}
316
317bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
318  DCHECK_EQ(STATE_OPEN, state_);
319  switch (option) {
320    case P2P_SOCKET_OPT_RCVBUF:
321      return socket_->SetReceiveBufferSize(value) == net::OK;
322    case P2P_SOCKET_OPT_SNDBUF:
323      return socket_->SetSendBufferSize(value) == net::OK;
324    case P2P_SOCKET_OPT_DSCP:
325      return (net::OK == socket_->SetDiffServCodePoint(
326          static_cast<net::DiffServCodePoint>(value))) ? true : false;
327    default:
328      NOTREACHED();
329      return false;
330  }
331}
332
333}  // namespace content
334