Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / browser / renderer_host / p2p / socket_host_udp.cc
blobf54dc787b7fbf33101c4aded928a84ca13837df0
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.
5 #include "content/browser/renderer_host/p2p/socket_host_udp.h"
7 #include "base/bind.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/browser/renderer_host/p2p/socket_host_throttler.h"
14 #include "content/common/p2p_messages.h"
15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/common/content_client.h"
17 #include "ipc/ipc_sender.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_util.h"
21 #include "third_party/webrtc/base/asyncpacketsocket.h"
23 namespace {
25 // UDP packets cannot be bigger than 64k.
26 const int kReadBufferSize = 65536;
27 // Socket receive buffer size.
28 const int kRecvSocketBufferSize = 65536; // 64K
30 // Defines set of transient errors. These errors are ignored when we get them
31 // from sendto() or recvfrom() calls.
33 // net::ERR_OUT_OF_MEMORY
35 // This is caused by ENOBUFS which means the buffer of the network interface
36 // is full.
38 // net::ERR_CONNECTION_RESET
40 // This is caused by WSAENETRESET or WSAECONNRESET which means the
41 // last send resulted in an "ICMP Port Unreachable" message.
42 struct {
43 int code;
44 const char* name;
45 } static const kTransientErrors[] {
46 {net::ERR_ADDRESS_UNREACHABLE, "net::ERR_ADDRESS_UNREACHABLE"},
47 {net::ERR_ADDRESS_INVALID, "net::ERR_ADDRESS_INVALID"},
48 {net::ERR_ACCESS_DENIED, "net::ERR_ACCESS_DENIED"},
49 {net::ERR_CONNECTION_RESET, "net::ERR_CONNECTION_RESET"},
50 {net::ERR_OUT_OF_MEMORY, "net::ERR_OUT_OF_MEMORY"},
51 {net::ERR_INTERNET_DISCONNECTED, "net::ERR_INTERNET_DISCONNECTED"}
54 bool IsTransientError(int error) {
55 for (const auto& transient_error : kTransientErrors) {
56 if (transient_error.code == error)
57 return true;
59 return false;
62 const char* GetTransientErrorName(int error) {
63 for (const auto& transient_error : kTransientErrors) {
64 if (transient_error.code == error)
65 return transient_error.name;
67 return "";
70 } // namespace
72 namespace content {
74 P2PSocketHostUdp::PendingPacket::PendingPacket(
75 const net::IPEndPoint& to,
76 const std::vector<char>& content,
77 const rtc::PacketOptions& options,
78 uint64 id)
79 : to(to),
80 data(new net::IOBuffer(content.size())),
81 size(content.size()),
82 packet_options(options),
83 id(id) {
84 memcpy(data->data(), &content[0], size);
87 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
90 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
91 int socket_id,
92 P2PMessageThrottler* throttler)
93 : P2PSocketHost(message_sender, socket_id, P2PSocketHost::UDP),
94 send_pending_(false),
95 last_dscp_(net::DSCP_CS0),
96 throttler_(throttler),
97 send_buffer_size_(0) {
98 net::UDPServerSocket* socket = new net::UDPServerSocket(
99 GetContentClient()->browser()->GetNetLog(), net::NetLog::Source());
100 #if defined(OS_WIN)
101 // If configured for finch experiment, use nonblocking IO.
102 if (base::FieldTrialList::FindFullName("WebRTC-UDPSocketNonBlockingIO") ==
103 "Enabled") {
104 socket->UseNonBlockingIO();
106 #endif
107 socket_.reset(socket);
110 P2PSocketHostUdp::~P2PSocketHostUdp() {
111 if (state_ == STATE_OPEN) {
112 DCHECK(socket_.get());
113 socket_.reset();
117 void P2PSocketHostUdp::SetSendBufferSize() {
118 unsigned int send_buffer_size = 0;
120 base::StringToUint(
121 base::FieldTrialList::FindFullName("WebRTC-SystemUDPSendSocketSize"),
122 &send_buffer_size);
124 if (send_buffer_size > 0) {
125 if (!SetOption(P2P_SOCKET_OPT_SNDBUF, send_buffer_size)) {
126 LOG(WARNING) << "Failed to set socket send buffer size to "
127 << send_buffer_size;
128 } else {
129 send_buffer_size_ = send_buffer_size;
134 bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
135 const P2PHostAndIPEndPoint& remote_address) {
136 DCHECK_EQ(state_, STATE_UNINITIALIZED);
138 int result = socket_->Listen(local_address);
139 if (result < 0) {
140 LOG(ERROR) << "bind() failed: " << result;
141 OnError();
142 return false;
145 // Setting recv socket buffer size.
146 if (socket_->SetReceiveBufferSize(kRecvSocketBufferSize) != net::OK) {
147 LOG(WARNING) << "Failed to set socket receive buffer size to "
148 << kRecvSocketBufferSize;
151 net::IPEndPoint address;
152 result = socket_->GetLocalAddress(&address);
153 if (result < 0) {
154 LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
155 << result;
156 OnError();
157 return false;
159 VLOG(1) << "Local address: " << address.ToString();
161 state_ = STATE_OPEN;
163 SetSendBufferSize();
165 // NOTE: Remote address will be same as what renderer provided.
166 message_sender_->Send(new P2PMsg_OnSocketCreated(
167 id_, address, remote_address.ip_address));
169 recv_buffer_ = new net::IOBuffer(kReadBufferSize);
170 DoRead();
172 return true;
175 void P2PSocketHostUdp::OnError() {
176 socket_.reset();
177 send_queue_.clear();
179 if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
180 message_sender_->Send(new P2PMsg_OnError(id_));
182 state_ = STATE_ERROR;
185 void P2PSocketHostUdp::DoRead() {
186 int result;
187 do {
188 result = socket_->RecvFrom(
189 recv_buffer_.get(),
190 kReadBufferSize,
191 &recv_address_,
192 base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
193 if (result == net::ERR_IO_PENDING)
194 return;
195 HandleReadResult(result);
196 } while (state_ == STATE_OPEN);
199 void P2PSocketHostUdp::OnRecv(int result) {
200 HandleReadResult(result);
201 if (state_ == STATE_OPEN) {
202 DoRead();
206 void P2PSocketHostUdp::HandleReadResult(int result) {
207 DCHECK_EQ(STATE_OPEN, state_);
209 if (result > 0) {
210 std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
212 if (!ContainsKey(connected_peers_, recv_address_)) {
213 P2PSocketHost::StunMessageType type;
214 bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
215 if ((stun && IsRequestOrResponse(type))) {
216 connected_peers_.insert(recv_address_);
217 } else if (!stun || type == STUN_DATA_INDICATION) {
218 LOG(ERROR) << "Received unexpected data packet from "
219 << recv_address_.ToString()
220 << " before STUN binding is finished.";
221 return;
225 message_sender_->Send(new P2PMsg_OnDataReceived(
226 id_, recv_address_, data, base::TimeTicks::Now()));
228 if (dump_incoming_rtp_packet_)
229 DumpRtpPacket(&data[0], data.size(), true);
230 } else if (result < 0 && !IsTransientError(result)) {
231 LOG(ERROR) << "Error when reading from UDP socket: " << result;
232 OnError();
236 void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
237 const std::vector<char>& data,
238 const rtc::PacketOptions& options,
239 uint64 packet_id) {
240 if (!socket_) {
241 // The Send message may be sent after the an OnError message was
242 // sent by hasn't been processed the renderer.
243 return;
246 if (!ContainsKey(connected_peers_, to)) {
247 P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
248 bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
249 if (!stun || type == STUN_DATA_INDICATION) {
250 LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
251 << " before STUN binding is finished.";
252 OnError();
253 return;
256 if (throttler_->DropNextPacket(data.size())) {
257 VLOG(0) << "STUN message is dropped due to high volume.";
258 // Do not reset socket.
259 return;
263 IncrementTotalSentPackets();
265 if (send_pending_) {
266 send_queue_.push_back(PendingPacket(to, data, options, packet_id));
267 IncrementDelayedBytes(data.size());
268 IncrementDelayedPackets();
269 } else {
270 // TODO(mallinath: Remove unnecessary memcpy in this case.
271 PendingPacket packet(to, data, options, packet_id);
272 DoSend(packet);
276 void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
277 TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
278 "size", packet.size);
279 // Don't try to set DSCP in following conditions,
280 // 1. If the outgoing packet is set to DSCP_NO_CHANGE
281 // 2. If no change in DSCP value from last packet
282 // 3. If there is any error in setting DSCP on socket.
283 net::DiffServCodePoint dscp =
284 static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
285 if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
286 last_dscp_ != net::DSCP_NO_CHANGE) {
287 int result = socket_->SetDiffServCodePoint(dscp);
288 if (result == net::OK) {
289 last_dscp_ = dscp;
290 } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
291 // We receieved a non-transient error, and it seems we have
292 // not changed the DSCP in the past, disable DSCP as it unlikely
293 // to work in the future.
294 last_dscp_ = net::DSCP_NO_CHANGE;
298 uint64 tick_received = base::TimeTicks::Now().ToInternalValue();
300 packet_processing_helpers::ApplyPacketOptions(
301 packet.data->data(), packet.size, packet.packet_options, 0);
302 int result = socket_->SendTo(packet.data.get(),
303 packet.size,
304 packet.to,
305 base::Bind(&P2PSocketHostUdp::OnSend,
306 base::Unretained(this),
307 packet.id,
308 tick_received));
310 // sendto() may return an error, e.g. if we've received an ICMP Destination
311 // Unreachable message. When this happens try sending the same packet again,
312 // and just drop it if it fails again.
313 if (IsTransientError(result)) {
314 result = socket_->SendTo(packet.data.get(),
315 packet.size,
316 packet.to,
317 base::Bind(&P2PSocketHostUdp::OnSend,
318 base::Unretained(this),
319 packet.id,
320 tick_received));
323 if (result == net::ERR_IO_PENDING) {
324 send_pending_ = true;
325 } else {
326 HandleSendResult(packet.id, tick_received, result);
329 if (dump_outgoing_rtp_packet_)
330 DumpRtpPacket(packet.data->data(), packet.size, false);
333 void P2PSocketHostUdp::OnSend(uint64 packet_id,
334 uint64 tick_received,
335 int result) {
336 DCHECK(send_pending_);
337 DCHECK_NE(result, net::ERR_IO_PENDING);
339 send_pending_ = false;
341 HandleSendResult(packet_id, tick_received, result);
343 // Send next packets if we have them waiting in the buffer.
344 while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
345 PendingPacket packet = send_queue_.front();
346 DoSend(packet);
347 send_queue_.pop_front();
348 DecrementDelayedBytes(packet.size);
352 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id,
353 uint64 tick_received,
354 int result) {
355 TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
356 "result", result);
357 if (result < 0) {
358 if (!IsTransientError(result)) {
359 LOG(ERROR) << "Error when sending data in UDP socket: " << result;
360 OnError();
361 return;
363 VLOG(0) << "sendto() has failed twice returning a "
364 " transient error " << GetTransientErrorName(result)
365 << ". Dropping the packet.";
368 // UMA to track the histograms from 1ms to 1 sec for how long a packet spends
369 // in the browser process.
370 UMA_HISTOGRAM_TIMES(
371 "WebRTC.SystemSendPacketDuration_UDP" /* name */,
372 base::TimeTicks::Now() -
373 base::TimeTicks::FromInternalValue(tick_received) /* sample */);
375 message_sender_->Send(
376 new P2PMsg_OnSendComplete(id_, P2PSendPacketMetrics(packet_id)));
379 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
380 const net::IPEndPoint& remote_address, int id) {
381 NOTREACHED();
382 OnError();
383 return NULL;
386 bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
387 DCHECK_EQ(STATE_OPEN, state_);
388 switch (option) {
389 case P2P_SOCKET_OPT_RCVBUF:
390 return socket_->SetReceiveBufferSize(value) == net::OK;
391 case P2P_SOCKET_OPT_SNDBUF:
392 // Ignore any following call to set the send buffer size if we're under
393 // experiment.
394 if (send_buffer_size_ > 0) {
395 return true;
397 return socket_->SetSendBufferSize(value) == net::OK;
398 case P2P_SOCKET_OPT_DSCP:
399 return (net::OK == socket_->SetDiffServCodePoint(
400 static_cast<net::DiffServCodePoint>(value))) ? true : false;
401 default:
402 NOTREACHED();
403 return false;
407 } // namespace content