3833b62d03b1b19536896498f75e7ff90ba60f40
[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/webrtc/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          error == net::ERR_INTERNET_DISCONNECTED;
46 }
47
48 }  // namespace
49
50 namespace content {
51
52 P2PSocketHostUdp::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
65 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
66 }
67
68 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
69                                    int socket_id,
70                                    P2PMessageThrottler* throttler)
71     : P2PSocketHost(message_sender, socket_id),
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
80 P2PSocketHostUdp::~P2PSocketHostUdp() {
81   if (state_ == STATE_OPEN) {
82     DCHECK(socket_.get());
83     socket_.reset();
84   }
85 }
86
87 bool 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
126 void 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
136 void 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
150 void P2PSocketHostUdp::OnRecv(int result) {
151   HandleReadResult(result);
152   if (state_ == STATE_OPEN) {
153     DoRead();
154   }
155 }
156
157 void 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
187 void 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   if (send_pending_) {
215     send_queue_.push_back(PendingPacket(to, data, options, packet_id));
216   } else {
217     // TODO(mallinath: Remove unnecessary memcpy in this case.
218     PendingPacket packet(to, data, options, packet_id);
219     DoSend(packet);
220   }
221 }
222
223 void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
224   TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
225                                "size", packet.size);
226   // Don't try to set DSCP in following conditions,
227   // 1. If the outgoing packet is set to DSCP_NO_CHANGE
228   // 2. If no change in DSCP value from last packet
229   // 3. If there is any error in setting DSCP on socket.
230   net::DiffServCodePoint dscp =
231       static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
232   if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
233       last_dscp_ != net::DSCP_NO_CHANGE) {
234     int result = socket_->SetDiffServCodePoint(dscp);
235     if (result == net::OK) {
236       last_dscp_ = dscp;
237     } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
238       // We receieved a non-transient error, and it seems we have
239       // not changed the DSCP in the past, disable DSCP as it unlikely
240       // to work in the future.
241       last_dscp_ = net::DSCP_NO_CHANGE;
242     }
243   }
244   packet_processing_helpers::ApplyPacketOptions(
245       packet.data->data(), packet.size, packet.packet_options, 0);
246   int result = socket_->SendTo(
247       packet.data.get(),
248       packet.size,
249       packet.to,
250       base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
251
252   // sendto() may return an error, e.g. if we've received an ICMP Destination
253   // Unreachable message. When this happens try sending the same packet again,
254   // and just drop it if it fails again.
255   if (IsTransientError(result)) {
256     result = socket_->SendTo(
257         packet.data.get(),
258         packet.size,
259         packet.to,
260         base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
261                    packet.id));
262   }
263
264   if (result == net::ERR_IO_PENDING) {
265     send_pending_ = true;
266   } else {
267     HandleSendResult(packet.id, result);
268   }
269
270   if (dump_outgoing_rtp_packet_)
271     DumpRtpPacket(packet.data->data(), packet.size, false);
272 }
273
274 void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
275   DCHECK(send_pending_);
276   DCHECK_NE(result, net::ERR_IO_PENDING);
277
278   send_pending_ = false;
279
280   HandleSendResult(packet_id, result);
281
282   // Send next packets if we have them waiting in the buffer.
283   while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
284     DoSend(send_queue_.front());
285     send_queue_.pop_front();
286   }
287 }
288
289 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
290   TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
291                          "result", result);
292   if (result < 0) {
293     if (!IsTransientError(result)) {
294       LOG(ERROR) << "Error when sending data in UDP socket: " << result;
295       OnError();
296       return;
297     }
298     VLOG(0) << "sendto() has failed twice returning a "
299                " transient error. Dropping the packet.";
300   }
301   message_sender_->Send(new P2PMsg_OnSendComplete(id_));
302 }
303
304 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
305     const net::IPEndPoint& remote_address, int id) {
306   NOTREACHED();
307   OnError();
308   return NULL;
309 }
310
311 bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
312   DCHECK_EQ(STATE_OPEN, state_);
313   switch (option) {
314     case P2P_SOCKET_OPT_RCVBUF:
315       return socket_->SetReceiveBufferSize(value) == net::OK;
316     case P2P_SOCKET_OPT_SNDBUF:
317       return socket_->SetSendBufferSize(value) == net::OK;
318     case P2P_SOCKET_OPT_DSCP:
319       return (net::OK == socket_->SetDiffServCodePoint(
320           static_cast<net::DiffServCodePoint>(value))) ? true : false;
321     default:
322       NOTREACHED();
323       return false;
324   }
325 }
326
327 }  // namespace content