IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / renderer_host / p2p / socket_host_udp.cc
blob8a14669a7ff4a5d57cf13647bee8f9f2af087bb2
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/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"
19 namespace {
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
32 // is full.
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);
51 } // namespace
53 namespace content {
55 P2PSocketHostUdp::PendingPacket::PendingPacket(
56 const net::IPEndPoint& to,
57 const std::vector<char>& content,
58 net::DiffServCodePoint dscp_,
59 uint64 id)
60 : to(to),
61 data(new net::IOBuffer(content.size())),
62 size(content.size()),
63 dscp(dscp_),
64 id(id) {
65 memcpy(data->data(), &content[0], size);
68 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
71 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
72 int id,
73 P2PMessageThrottler* throttler)
74 : P2PSocketHost(message_sender, id),
75 socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())),
76 send_pending_(false),
77 last_dscp_(net::DSCP_CS0),
78 throttler_(throttler) {
81 P2PSocketHostUdp::~P2PSocketHostUdp() {
82 if (state_ == STATE_OPEN) {
83 DCHECK(socket_.get());
84 socket_.reset();
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);
93 if (result < 0) {
94 LOG(ERROR) << "bind() failed: " << result;
95 OnError();
96 return false;
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);
107 if (result < 0) {
108 LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
109 << result;
110 OnError();
111 return false;
113 VLOG(1) << "Local address: " << address.ToString();
115 state_ = STATE_OPEN;
117 message_sender_->Send(new P2PMsg_OnSocketCreated(id_, address));
119 recv_buffer_ = new net::IOBuffer(kReadBufferSize);
120 DoRead();
122 return true;
125 void P2PSocketHostUdp::OnError() {
126 socket_.reset();
127 send_queue_.clear();
129 if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
130 message_sender_->Send(new P2PMsg_OnError(id_));
132 state_ = STATE_ERROR;
135 void P2PSocketHostUdp::DoRead() {
136 int result;
137 do {
138 result = socket_->RecvFrom(
139 recv_buffer_.get(),
140 kReadBufferSize,
141 &recv_address_,
142 base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
143 if (result == net::ERR_IO_PENDING)
144 return;
145 HandleReadResult(result);
146 } while (state_ == STATE_OPEN);
149 void P2PSocketHostUdp::OnRecv(int result) {
150 HandleReadResult(result);
151 if (state_ == STATE_OPEN) {
152 DoRead();
156 void P2PSocketHostUdp::HandleReadResult(int result) {
157 DCHECK_EQ(STATE_OPEN, state_);
159 if (result > 0) {
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.";
171 return;
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;
179 OnError();
183 void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
184 const std::vector<char>& data,
185 net::DiffServCodePoint dscp,
186 uint64 packet_id) {
187 if (!socket_) {
188 // The Send message may be sent after the an OnError message was
189 // sent by hasn't been processed the renderer.
190 return;
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.";
199 OnError();
200 return;
203 if (throttler_->DropNextPacket(data.size())) {
204 VLOG(0) << "STUN message is dropped due to high volume.";
205 // Do not reset socket.
206 return;
210 if (send_pending_) {
211 send_queue_.push_back(PendingPacket(to, data, dscp, packet_id));
212 } else {
213 PendingPacket packet(to, data, dscp, packet_id);
214 DoSend(packet);
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(
238 packet.data.get(),
239 packet.size,
240 packet.to,
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(
248 packet.data.get(),
249 packet.size,
250 packet.to,
251 base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
252 packet.id));
255 if (result == net::ERR_IO_PENDING) {
256 send_pending_ = true;
257 } else {
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,
279 "result", result);
280 if (result > 0) {
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;
287 OnError();
291 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
292 const net::IPEndPoint& remote_address, int id) {
293 NOTREACHED();
294 OnError();
295 return NULL;
298 } // namespace content