summaryrefslogtreecommitdiff
path: root/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/renderer_host/p2p/socket_host_udp.cc')
-rw-r--r--chromium/content/browser/renderer_host/p2p/socket_host_udp.cc260
1 files changed, 260 insertions, 0 deletions
diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc
new file mode 100644
index 00000000000..bcfb282a2c4
--- /dev/null
+++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc
@@ -0,0 +1,260 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/p2p/socket_host_udp.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "content/common/p2p_messages.h"
+#include "ipc/ipc_sender.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+
+namespace {
+
+// UDP packets cannot be bigger than 64k.
+const int kReadBufferSize = 65536;
+
+// Defines set of transient errors. These errors are ignored when we get them
+// from sendto() or recvfrom() calls.
+//
+// net::ERR_OUT_OF_MEMORY
+//
+// This is caused by ENOBUFS which means the buffer of the network interface
+// is full.
+//
+// net::ERR_CONNECTION_RESET
+//
+// This is caused by WSAENETRESET or WSAECONNRESET which means the
+// last send resulted in an "ICMP Port Unreachable" message.
+bool IsTransientError(int error) {
+ return error == net::ERR_ADDRESS_UNREACHABLE ||
+ error == net::ERR_ADDRESS_INVALID ||
+ error == net::ERR_ACCESS_DENIED ||
+ error == net::ERR_CONNECTION_RESET ||
+ error == net::ERR_OUT_OF_MEMORY;
+}
+
+uint64 GetUniqueEventId(const content::P2PSocketHostUdp* obj,
+ uint64 packet_id) {
+ uint64 uid = reinterpret_cast<uintptr_t>(obj);
+ uid <<= 32;
+ uid |= packet_id;
+ return uid;
+}
+
+} // namespace
+
+namespace content {
+
+P2PSocketHostUdp::PendingPacket::PendingPacket(
+ const net::IPEndPoint& to, const std::vector<char>& content, uint64 id)
+ : to(to),
+ data(new net::IOBuffer(content.size())),
+ size(content.size()),
+ id(id) {
+ memcpy(data->data(), &content[0], size);
+}
+
+P2PSocketHostUdp::PendingPacket::~PendingPacket() {
+}
+
+P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender, int id)
+ : P2PSocketHost(message_sender, id),
+ socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())),
+ send_pending_(false),
+ send_packet_count_(0) {
+}
+
+P2PSocketHostUdp::~P2PSocketHostUdp() {
+ if (state_ == STATE_OPEN) {
+ DCHECK(socket_.get());
+ socket_.reset();
+ }
+}
+
+bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
+ const net::IPEndPoint& remote_address) {
+ DCHECK_EQ(state_, STATE_UNINITIALIZED);
+
+ int result = socket_->Listen(local_address);
+ if (result < 0) {
+ LOG(ERROR) << "bind() failed: " << result;
+ OnError();
+ return false;
+ }
+
+ net::IPEndPoint address;
+ result = socket_->GetLocalAddress(&address);
+ if (result < 0) {
+ LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
+ << result;
+ OnError();
+ return false;
+ }
+ VLOG(1) << "Local address: " << address.ToString();
+
+ state_ = STATE_OPEN;
+
+ message_sender_->Send(new P2PMsg_OnSocketCreated(id_, address));
+
+ recv_buffer_ = new net::IOBuffer(kReadBufferSize);
+ DoRead();
+
+ return true;
+}
+
+void P2PSocketHostUdp::OnError() {
+ socket_.reset();
+ send_queue_.clear();
+
+ if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
+ message_sender_->Send(new P2PMsg_OnError(id_));
+
+ state_ = STATE_ERROR;
+}
+
+void P2PSocketHostUdp::DoRead() {
+ int result;
+ do {
+ result = socket_->RecvFrom(
+ recv_buffer_.get(),
+ kReadBufferSize,
+ &recv_address_,
+ base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
+ if (result == net::ERR_IO_PENDING)
+ return;
+ HandleReadResult(result);
+ } while (state_ == STATE_OPEN);
+}
+
+void P2PSocketHostUdp::OnRecv(int result) {
+ HandleReadResult(result);
+ if (state_ == STATE_OPEN) {
+ DoRead();
+ }
+}
+
+void P2PSocketHostUdp::HandleReadResult(int result) {
+ DCHECK_EQ(state_, STATE_OPEN);
+
+ if (result > 0) {
+ std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
+
+ if (connected_peers_.find(recv_address_) == connected_peers_.end()) {
+ P2PSocketHost::StunMessageType type;
+ bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
+ if (stun && IsRequestOrResponse(type)) {
+ connected_peers_.insert(recv_address_);
+ } else if (!stun || type == STUN_DATA_INDICATION) {
+ LOG(ERROR) << "Received unexpected data packet from "
+ << recv_address_.ToString()
+ << " before STUN binding is finished.";
+ return;
+ }
+ }
+
+ message_sender_->Send(new P2PMsg_OnDataReceived(id_, recv_address_, data));
+ } else if (result < 0 && !IsTransientError(result)) {
+ LOG(ERROR) << "Error when reading from UDP socket: " << result;
+ OnError();
+ }
+}
+
+void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
+ const std::vector<char>& data) {
+ if (!socket_) {
+ // The Send message may be sent after the an OnError message was
+ // sent by hasn't been processed the renderer.
+ return;
+ }
+
+ if (connected_peers_.find(to) == connected_peers_.end()) {
+ P2PSocketHost::StunMessageType type;
+ bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
+ if (!stun || type == STUN_DATA_INDICATION) {
+ LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
+ << " before STUN binding is finished.";
+ OnError();
+ return;
+ }
+ }
+
+ if (send_pending_) {
+ send_queue_.push_back(PendingPacket(to, data, send_packet_count_));
+ } else {
+ PendingPacket packet(to, data, send_packet_count_);
+ DoSend(packet);
+ }
+ ++send_packet_count_;
+}
+
+void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
+ TRACE_EVENT_ASYNC_BEGIN1("p2p", "Udp::DoSend",
+ GetUniqueEventId(this, packet.id),
+ "size", packet.size);
+ int result = socket_->SendTo(
+ packet.data.get(),
+ packet.size,
+ packet.to,
+ base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
+
+ // sendto() may return an error, e.g. if we've received an ICMP Destination
+ // Unreachable message. When this happens try sending the same packet again,
+ // and just drop it if it fails again.
+ if (IsTransientError(result)) {
+ result = socket_->SendTo(
+ packet.data.get(),
+ packet.size,
+ packet.to,
+ base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
+ packet.id));
+ }
+
+ if (result == net::ERR_IO_PENDING) {
+ send_pending_ = true;
+ } else {
+ HandleSendResult(packet.id, result);
+ }
+}
+
+void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
+ DCHECK(send_pending_);
+ DCHECK_NE(result, net::ERR_IO_PENDING);
+
+ send_pending_ = false;
+
+ HandleSendResult(packet_id, result);
+
+ // Send next packets if we have them waiting in the buffer.
+ while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
+ DoSend(send_queue_.front());
+ send_queue_.pop_front();
+ }
+}
+
+void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
+ TRACE_EVENT_ASYNC_END1("p2p", "Udp::DoSend",
+ GetUniqueEventId(this, packet_id),
+ "result", result);
+ if (result > 0) {
+ message_sender_->Send(new P2PMsg_OnSendComplete(id_));
+ } else if (IsTransientError(result)) {
+ LOG(INFO) << "sendto() has failed twice returning a "
+ " transient error. Dropping the packet.";
+ } else if (result < 0) {
+ LOG(ERROR) << "Error when sending data in UDP socket: " << result;
+ OnError();
+ }
+}
+
+P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
+ const net::IPEndPoint& remote_address, int id) {
+ NOTREACHED();
+ OnError();
+ return NULL;
+}
+
+} // namespace content