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"
8 #include "base/command_line.h"
9 #include "base/debug/trace_event.h"
10 #include "base/stl_util.h"
11 #include "content/browser/renderer_host/p2p/socket_host_throttler.h"
12 #include "content/common/p2p_messages.h"
13 #include "content/public/common/content_switches.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"
21 // UDP packets cannot be bigger than 64k.
22 const int kReadBufferSize
= 65536;
23 // Socket receive buffer size.
24 const int kRecvSocketBufferSize
= 65536; // 64K
26 // Defines set of transient errors. These errors are ignored when we get them
27 // from sendto() or recvfrom() calls.
29 // net::ERR_OUT_OF_MEMORY
31 // This is caused by ENOBUFS which means the buffer of the network interface
34 // net::ERR_CONNECTION_RESET
36 // This is caused by WSAENETRESET or WSAECONNRESET which means the
37 // last send resulted in an "ICMP Port Unreachable" message.
38 bool IsTransientError(int error
) {
39 return error
== net::ERR_ADDRESS_UNREACHABLE
||
40 error
== net::ERR_ADDRESS_INVALID
||
41 error
== net::ERR_ACCESS_DENIED
||
42 error
== net::ERR_CONNECTION_RESET
||
43 error
== net::ERR_OUT_OF_MEMORY
;
46 bool AllowUDPWithoutSTUN() {
47 return CommandLine::ForCurrentProcess()->HasSwitch(
48 switches::kDisableP2PSocketSTUNFilter
);
55 P2PSocketHostUdp::PendingPacket::PendingPacket(
56 const net::IPEndPoint
& to
,
57 const std::vector
<char>& content
,
58 net::DiffServCodePoint dscp_
,
61 data(new net::IOBuffer(content
.size())),
65 memcpy(data
->data(), &content
[0], size
);
68 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
71 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender
* message_sender
,
73 P2PMessageThrottler
* throttler
)
74 : P2PSocketHost(message_sender
, id
),
75 socket_(new net::UDPServerSocket(NULL
, net::NetLog::Source())),
77 last_dscp_(net::DSCP_CS0
),
78 throttler_(throttler
) {
81 P2PSocketHostUdp::~P2PSocketHostUdp() {
82 if (state_
== STATE_OPEN
) {
83 DCHECK(socket_
.get());
88 bool P2PSocketHostUdp::Init(const net::IPEndPoint
& local_address
,
89 const net::IPEndPoint
& remote_address
) {
90 DCHECK_EQ(state_
, STATE_UNINITIALIZED
);
92 int result
= socket_
->Listen(local_address
);
94 LOG(ERROR
) << "bind() failed: " << result
;
99 // Setting recv socket buffer size.
100 if (!socket_
->SetReceiveBufferSize(kRecvSocketBufferSize
)) {
101 LOG(WARNING
) << "Failed to set socket receive buffer size to "
102 << kRecvSocketBufferSize
;
105 net::IPEndPoint address
;
106 result
= socket_
->GetLocalAddress(&address
);
108 LOG(ERROR
) << "P2PSocketHostUdp::Init(): unable to get local address: "
113 VLOG(1) << "Local address: " << address
.ToString();
117 message_sender_
->Send(new P2PMsg_OnSocketCreated(id_
, address
));
119 recv_buffer_
= new net::IOBuffer(kReadBufferSize
);
125 void P2PSocketHostUdp::OnError() {
129 if (state_
== STATE_UNINITIALIZED
|| state_
== STATE_OPEN
)
130 message_sender_
->Send(new P2PMsg_OnError(id_
));
132 state_
= STATE_ERROR
;
135 void P2PSocketHostUdp::DoRead() {
138 result
= socket_
->RecvFrom(
142 base::Bind(&P2PSocketHostUdp::OnRecv
, base::Unretained(this)));
143 if (result
== net::ERR_IO_PENDING
)
145 HandleReadResult(result
);
146 } while (state_
== STATE_OPEN
);
149 void P2PSocketHostUdp::OnRecv(int result
) {
150 HandleReadResult(result
);
151 if (state_
== STATE_OPEN
) {
156 void P2PSocketHostUdp::HandleReadResult(int result
) {
157 DCHECK_EQ(STATE_OPEN
, state_
);
160 std::vector
<char> data(recv_buffer_
->data(), recv_buffer_
->data() + result
);
162 if (!ContainsKey(connected_peers_
, recv_address_
)) {
163 P2PSocketHost::StunMessageType type
;
164 bool stun
= GetStunPacketType(&*data
.begin(), data
.size(), &type
);
165 if ((stun
&& IsRequestOrResponse(type
)) || AllowUDPWithoutSTUN()) {
166 connected_peers_
.insert(recv_address_
);
167 } else if (!stun
|| type
== STUN_DATA_INDICATION
) {
168 LOG(ERROR
) << "Received unexpected data packet from "
169 << recv_address_
.ToString()
170 << " before STUN binding is finished.";
175 message_sender_
->Send(new P2PMsg_OnDataReceived(
176 id_
, recv_address_
, data
, base::TimeTicks::Now()));
177 } else if (result
< 0 && !IsTransientError(result
)) {
178 LOG(ERROR
) << "Error when reading from UDP socket: " << result
;
183 void P2PSocketHostUdp::Send(const net::IPEndPoint
& to
,
184 const std::vector
<char>& data
,
185 net::DiffServCodePoint dscp
,
188 // The Send message may be sent after the an OnError message was
189 // sent by hasn't been processed the renderer.
193 if (!ContainsKey(connected_peers_
, to
) && !AllowUDPWithoutSTUN()) {
194 P2PSocketHost::StunMessageType type
= P2PSocketHost::StunMessageType();
195 bool stun
= GetStunPacketType(&*data
.begin(), data
.size(), &type
);
196 if (!stun
|| type
== STUN_DATA_INDICATION
) {
197 LOG(ERROR
) << "Page tried to send a data packet to " << to
.ToString()
198 << " before STUN binding is finished.";
203 if (throttler_
->DropNextPacket(data
.size())) {
204 VLOG(0) << "STUN message is dropped due to high volume.";
205 // Do not reset socket.
211 send_queue_
.push_back(PendingPacket(to
, data
, dscp
, packet_id
));
213 PendingPacket
packet(to
, data
, dscp
, packet_id
);
218 void P2PSocketHostUdp::DoSend(const PendingPacket
& packet
) {
219 TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet
.id
, "UdpAsyncSendTo",
220 "size", packet
.size
);
221 // Don't try to set DSCP in following conditions,
222 // 1. If the outgoing packet is set to DSCP_NO_CHANGE
223 // 2. If no change in DSCP value from last packet
224 // 3. If there is any error in setting DSCP on socket.
225 if (packet
.dscp
!= net::DSCP_NO_CHANGE
&&
226 last_dscp_
!= packet
.dscp
&& last_dscp_
!= net::DSCP_NO_CHANGE
) {
227 int result
= socket_
->SetDiffServCodePoint(packet
.dscp
);
228 if (result
== net::OK
) {
229 last_dscp_
= packet
.dscp
;
230 } else if (!IsTransientError(result
) && last_dscp_
!= net::DSCP_CS0
) {
231 // We receieved a non-transient error, and it seems we have
232 // not changed the DSCP in the past, disable DSCP as it unlikely
233 // to work in the future.
234 last_dscp_
= net::DSCP_NO_CHANGE
;
237 int result
= socket_
->SendTo(
241 base::Bind(&P2PSocketHostUdp::OnSend
, base::Unretained(this), packet
.id
));
243 // sendto() may return an error, e.g. if we've received an ICMP Destination
244 // Unreachable message. When this happens try sending the same packet again,
245 // and just drop it if it fails again.
246 if (IsTransientError(result
)) {
247 result
= socket_
->SendTo(
251 base::Bind(&P2PSocketHostUdp::OnSend
, base::Unretained(this),
255 if (result
== net::ERR_IO_PENDING
) {
256 send_pending_
= true;
258 HandleSendResult(packet
.id
, result
);
262 void P2PSocketHostUdp::OnSend(uint64 packet_id
, int result
) {
263 DCHECK(send_pending_
);
264 DCHECK_NE(result
, net::ERR_IO_PENDING
);
266 send_pending_
= false;
268 HandleSendResult(packet_id
, result
);
270 // Send next packets if we have them waiting in the buffer.
271 while (state_
== STATE_OPEN
&& !send_queue_
.empty() && !send_pending_
) {
272 DoSend(send_queue_
.front());
273 send_queue_
.pop_front();
277 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id
, int result
) {
278 TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id
,
281 message_sender_
->Send(new P2PMsg_OnSendComplete(id_
));
282 } else if (IsTransientError(result
)) {
283 VLOG(0) << "sendto() has failed twice returning a "
284 " transient error. Dropping the packet.";
285 } else if (result
< 0) {
286 LOG(ERROR
) << "Error when sending data in UDP socket: " << result
;
291 P2PSocketHost
* P2PSocketHostUdp::AcceptIncomingTcpConnection(
292 const net::IPEndPoint
& remote_address
, int id
) {
298 } // namespace content