db17c5f6f3fa3b4d8b565015cead895f122e2755
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / p2p / socket_host_udp.cc
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/libjingle/source/talk/base/asyncpacketsocket.h"
19
20 namespace {
21
22 // UDP packets cannot be bigger than 64k.
23 const int kReadBufferSize = 65536;
24 // Socket receive buffer size.
25 const 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.
39 bool 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 }
46
47 }  // namespace
48
49 namespace content {
50
51 P2PSocketHostUdp::PendingPacket::PendingPacket(
52     const net::IPEndPoint& to,
53     const std::vector<char>& content,
54     const talk_base::PacketOptions& options,
55     uint64 id)
56     : to(to),
57       data(new net::IOBuffer(content.size())),
58       size(content.size()),
59       packet_options(options),
60       id(id) {
61   memcpy(data->data(), &content[0], size);
62 }
63
64 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
65 }
66
67 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
68                                    int id,
69                                    P2PMessageThrottler* throttler)
70     : P2PSocketHost(message_sender, id),
71       socket_(new net::UDPServerSocket(
72           GetContentClient()->browser()->GetNetLog(),
73           net::NetLog::Source())),
74       send_pending_(false),
75       last_dscp_(net::DSCP_CS0),
76       throttler_(throttler) {
77 }
78
79 P2PSocketHostUdp::~P2PSocketHostUdp() {
80   if (state_ == STATE_OPEN) {
81     DCHECK(socket_.get());
82     socket_.reset();
83   }
84 }
85
86 bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
87                             const P2PHostAndIPEndPoint& remote_address) {
88   DCHECK_EQ(state_, STATE_UNINITIALIZED);
89
90   int result = socket_->Listen(local_address);
91   if (result < 0) {
92     LOG(ERROR) << "bind() failed: " << result;
93     OnError();
94     return false;
95   }
96
97   // Setting recv socket buffer size.
98   if (!socket_->SetReceiveBufferSize(kRecvSocketBufferSize)) {
99     LOG(WARNING) << "Failed to set socket receive buffer size to "
100                  << kRecvSocketBufferSize;
101   }
102
103   net::IPEndPoint address;
104   result = socket_->GetLocalAddress(&address);
105   if (result < 0) {
106     LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
107                << result;
108     OnError();
109     return false;
110   }
111   VLOG(1) << "Local address: " << address.ToString();
112
113   state_ = STATE_OPEN;
114
115   message_sender_->Send(new P2PMsg_OnSocketCreated(id_, address));
116
117   recv_buffer_ = new net::IOBuffer(kReadBufferSize);
118   DoRead();
119
120   return true;
121 }
122
123 void P2PSocketHostUdp::OnError() {
124   socket_.reset();
125   send_queue_.clear();
126
127   if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
128     message_sender_->Send(new P2PMsg_OnError(id_));
129
130   state_ = STATE_ERROR;
131 }
132
133 void P2PSocketHostUdp::DoRead() {
134   int result;
135   do {
136     result = socket_->RecvFrom(
137         recv_buffer_.get(),
138         kReadBufferSize,
139         &recv_address_,
140         base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
141     if (result == net::ERR_IO_PENDING)
142       return;
143     HandleReadResult(result);
144   } while (state_ == STATE_OPEN);
145 }
146
147 void P2PSocketHostUdp::OnRecv(int result) {
148   HandleReadResult(result);
149   if (state_ == STATE_OPEN) {
150     DoRead();
151   }
152 }
153
154 void P2PSocketHostUdp::HandleReadResult(int result) {
155   DCHECK_EQ(STATE_OPEN, state_);
156
157   if (result > 0) {
158     std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
159
160     if (!ContainsKey(connected_peers_, recv_address_)) {
161       P2PSocketHost::StunMessageType type;
162       bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
163       if ((stun && IsRequestOrResponse(type))) {
164         connected_peers_.insert(recv_address_);
165       } else if (!stun || type == STUN_DATA_INDICATION) {
166         LOG(ERROR) << "Received unexpected data packet from "
167                    << recv_address_.ToString()
168                    << " before STUN binding is finished.";
169         return;
170       }
171     }
172
173     message_sender_->Send(new P2PMsg_OnDataReceived(
174         id_, recv_address_, data, base::TimeTicks::Now()));
175   } else if (result < 0 && !IsTransientError(result)) {
176     LOG(ERROR) << "Error when reading from UDP socket: " << result;
177     OnError();
178   }
179 }
180
181 void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
182                             const std::vector<char>& data,
183                             const talk_base::PacketOptions& options,
184                             uint64 packet_id) {
185   if (!socket_) {
186     // The Send message may be sent after the an OnError message was
187     // sent by hasn't been processed the renderer.
188     return;
189   }
190
191   if (!ContainsKey(connected_peers_, to)) {
192     P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
193     bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
194     if (!stun || type == STUN_DATA_INDICATION) {
195       LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
196                  << " before STUN binding is finished.";
197       OnError();
198       return;
199     }
200
201     if (throttler_->DropNextPacket(data.size())) {
202       VLOG(0) << "STUN message is dropped due to high volume.";
203       // Do not reset socket.
204       return;
205     }
206   }
207
208   if (send_pending_) {
209     send_queue_.push_back(PendingPacket(to, data, options, packet_id));
210   } else {
211     // TODO(mallinath: Remove unnecessary memcpy in this case.
212     PendingPacket packet(to, data, options, packet_id);
213     DoSend(packet);
214   }
215 }
216
217 void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
218   TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
219                                "size", packet.size);
220   // Don't try to set DSCP in following conditions,
221   // 1. If the outgoing packet is set to DSCP_NO_CHANGE
222   // 2. If no change in DSCP value from last packet
223   // 3. If there is any error in setting DSCP on socket.
224   net::DiffServCodePoint dscp =
225       static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
226   if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
227       last_dscp_ != net::DSCP_NO_CHANGE) {
228     int result = socket_->SetDiffServCodePoint(dscp);
229     if (result == net::OK) {
230       last_dscp_ = dscp;
231     } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
232       // We receieved a non-transient error, and it seems we have
233       // not changed the DSCP in the past, disable DSCP as it unlikely
234       // to work in the future.
235       last_dscp_ = net::DSCP_NO_CHANGE;
236     }
237   }
238   packet_processing_helpers::ApplyPacketOptions(
239       packet.data->data(), packet.size, packet.packet_options, 0);
240   int result = socket_->SendTo(
241       packet.data.get(),
242       packet.size,
243       packet.to,
244       base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
245
246   // sendto() may return an error, e.g. if we've received an ICMP Destination
247   // Unreachable message. When this happens try sending the same packet again,
248   // and just drop it if it fails again.
249   if (IsTransientError(result)) {
250     result = socket_->SendTo(
251         packet.data.get(),
252         packet.size,
253         packet.to,
254         base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
255                    packet.id));
256   }
257
258   if (result == net::ERR_IO_PENDING) {
259     send_pending_ = true;
260   } else {
261     HandleSendResult(packet.id, result);
262   }
263 }
264
265 void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
266   DCHECK(send_pending_);
267   DCHECK_NE(result, net::ERR_IO_PENDING);
268
269   send_pending_ = false;
270
271   HandleSendResult(packet_id, result);
272
273   // Send next packets if we have them waiting in the buffer.
274   while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
275     DoSend(send_queue_.front());
276     send_queue_.pop_front();
277   }
278 }
279
280 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
281   TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
282                          "result", result);
283   if (result > 0) {
284     message_sender_->Send(new P2PMsg_OnSendComplete(id_));
285   } else if (IsTransientError(result)) {
286     VLOG(0) << "sendto() has failed twice returning a "
287         " transient error. Dropping the packet.";
288   } else if (result < 0) {
289     LOG(ERROR) << "Error when sending data in UDP socket: " << result;
290     OnError();
291   }
292 }
293
294 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
295     const net::IPEndPoint& remote_address, int id) {
296   NOTREACHED();
297   OnError();
298   return NULL;
299 }
300
301 bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
302   DCHECK_EQ(STATE_OPEN, state_);
303   switch (option) {
304     case P2P_SOCKET_OPT_RCVBUF:
305       return socket_->SetReceiveBufferSize(value);
306     case P2P_SOCKET_OPT_SNDBUF:
307       return socket_->SetSendBufferSize(value);
308     case P2P_SOCKET_OPT_DSCP:
309       return (net::OK == socket_->SetDiffServCodePoint(
310           static_cast<net::DiffServCodePoint>(value))) ? true : false;
311     default:
312       NOTREACHED();
313       return false;
314   }
315 }
316
317 }  // namespace content