Home | History | Annotate | Download | only in p2p
      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          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 talk_base::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   message_sender_->Send(new P2PMsg_OnSocketCreated(id_, address));
    117 
    118   recv_buffer_ = new net::IOBuffer(kReadBufferSize);
    119   DoRead();
    120 
    121   return true;
    122 }
    123 
    124 void P2PSocketHostUdp::OnError() {
    125   socket_.reset();
    126   send_queue_.clear();
    127 
    128   if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
    129     message_sender_->Send(new P2PMsg_OnError(id_));
    130 
    131   state_ = STATE_ERROR;
    132 }
    133 
    134 void P2PSocketHostUdp::DoRead() {
    135   int result;
    136   do {
    137     result = socket_->RecvFrom(
    138         recv_buffer_.get(),
    139         kReadBufferSize,
    140         &recv_address_,
    141         base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
    142     if (result == net::ERR_IO_PENDING)
    143       return;
    144     HandleReadResult(result);
    145   } while (state_ == STATE_OPEN);
    146 }
    147 
    148 void P2PSocketHostUdp::OnRecv(int result) {
    149   HandleReadResult(result);
    150   if (state_ == STATE_OPEN) {
    151     DoRead();
    152   }
    153 }
    154 
    155 void P2PSocketHostUdp::HandleReadResult(int result) {
    156   DCHECK_EQ(STATE_OPEN, state_);
    157 
    158   if (result > 0) {
    159     std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
    160 
    161     if (!ContainsKey(connected_peers_, recv_address_)) {
    162       P2PSocketHost::StunMessageType type;
    163       bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
    164       if ((stun && IsRequestOrResponse(type))) {
    165         connected_peers_.insert(recv_address_);
    166       } else if (!stun || type == STUN_DATA_INDICATION) {
    167         LOG(ERROR) << "Received unexpected data packet from "
    168                    << recv_address_.ToString()
    169                    << " before STUN binding is finished.";
    170         return;
    171       }
    172     }
    173 
    174     message_sender_->Send(new P2PMsg_OnDataReceived(
    175         id_, recv_address_, data, base::TimeTicks::Now()));
    176 
    177     if (dump_incoming_rtp_packet_)
    178       DumpRtpPacket(&data[0], data.size(), true);
    179   } else if (result < 0 && !IsTransientError(result)) {
    180     LOG(ERROR) << "Error when reading from UDP socket: " << result;
    181     OnError();
    182   }
    183 }
    184 
    185 void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
    186                             const std::vector<char>& data,
    187                             const talk_base::PacketOptions& options,
    188                             uint64 packet_id) {
    189   if (!socket_) {
    190     // The Send message may be sent after the an OnError message was
    191     // sent by hasn't been processed the renderer.
    192     return;
    193   }
    194 
    195   if (!ContainsKey(connected_peers_, to)) {
    196     P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
    197     bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
    198     if (!stun || type == STUN_DATA_INDICATION) {
    199       LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
    200                  << " before STUN binding is finished.";
    201       OnError();
    202       return;
    203     }
    204 
    205     if (throttler_->DropNextPacket(data.size())) {
    206       VLOG(0) << "STUN message is dropped due to high volume.";
    207       // Do not reset socket.
    208       return;
    209     }
    210   }
    211 
    212   if (send_pending_) {
    213     send_queue_.push_back(PendingPacket(to, data, options, packet_id));
    214   } else {
    215     // TODO(mallinath: Remove unnecessary memcpy in this case.
    216     PendingPacket packet(to, data, options, packet_id);
    217     DoSend(packet);
    218   }
    219 }
    220 
    221 void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
    222   TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
    223                                "size", packet.size);
    224   // Don't try to set DSCP in following conditions,
    225   // 1. If the outgoing packet is set to DSCP_NO_CHANGE
    226   // 2. If no change in DSCP value from last packet
    227   // 3. If there is any error in setting DSCP on socket.
    228   net::DiffServCodePoint dscp =
    229       static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
    230   if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
    231       last_dscp_ != net::DSCP_NO_CHANGE) {
    232     int result = socket_->SetDiffServCodePoint(dscp);
    233     if (result == net::OK) {
    234       last_dscp_ = dscp;
    235     } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
    236       // We receieved a non-transient error, and it seems we have
    237       // not changed the DSCP in the past, disable DSCP as it unlikely
    238       // to work in the future.
    239       last_dscp_ = net::DSCP_NO_CHANGE;
    240     }
    241   }
    242   packet_processing_helpers::ApplyPacketOptions(
    243       packet.data->data(), packet.size, packet.packet_options, 0);
    244   int result = socket_->SendTo(
    245       packet.data.get(),
    246       packet.size,
    247       packet.to,
    248       base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
    249 
    250   // sendto() may return an error, e.g. if we've received an ICMP Destination
    251   // Unreachable message. When this happens try sending the same packet again,
    252   // and just drop it if it fails again.
    253   if (IsTransientError(result)) {
    254     result = socket_->SendTo(
    255         packet.data.get(),
    256         packet.size,
    257         packet.to,
    258         base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
    259                    packet.id));
    260   }
    261 
    262   if (result == net::ERR_IO_PENDING) {
    263     send_pending_ = true;
    264   } else {
    265     HandleSendResult(packet.id, result);
    266   }
    267 
    268   if (dump_outgoing_rtp_packet_)
    269     DumpRtpPacket(packet.data->data(), packet.size, false);
    270 }
    271 
    272 void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
    273   DCHECK(send_pending_);
    274   DCHECK_NE(result, net::ERR_IO_PENDING);
    275 
    276   send_pending_ = false;
    277 
    278   HandleSendResult(packet_id, result);
    279 
    280   // Send next packets if we have them waiting in the buffer.
    281   while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
    282     DoSend(send_queue_.front());
    283     send_queue_.pop_front();
    284   }
    285 }
    286 
    287 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
    288   TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
    289                          "result", result);
    290   if (result < 0) {
    291     if (!IsTransientError(result)) {
    292       LOG(ERROR) << "Error when sending data in UDP socket: " << result;
    293       OnError();
    294       return;
    295     }
    296     VLOG(0) << "sendto() has failed twice returning a "
    297                " transient error. Dropping the packet.";
    298   }
    299   message_sender_->Send(new P2PMsg_OnSendComplete(id_));
    300 }
    301 
    302 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
    303     const net::IPEndPoint& remote_address, int id) {
    304   NOTREACHED();
    305   OnError();
    306   return NULL;
    307 }
    308 
    309 bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
    310   DCHECK_EQ(STATE_OPEN, state_);
    311   switch (option) {
    312     case P2P_SOCKET_OPT_RCVBUF:
    313       return socket_->SetReceiveBufferSize(value) == net::OK;
    314     case P2P_SOCKET_OPT_SNDBUF:
    315       return socket_->SetSendBufferSize(value) == net::OK;
    316     case P2P_SOCKET_OPT_DSCP:
    317       return (net::OK == socket_->SetDiffServCodePoint(
    318           static_cast<net::DiffServCodePoint>(value))) ? true : false;
    319     default:
    320       NOTREACHED();
    321       return false;
    322   }
    323 }
    324 
    325 }  // namespace content
    326