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