Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Unified Diff: content/browser/renderer_host/p2p_sockets_host.cc

Issue 6598053: P2P Sockets host implementation. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: - Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/renderer_host/p2p_sockets_host.h ('k') | content/content_browser.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/renderer_host/p2p_sockets_host.cc
diff --git a/content/browser/renderer_host/p2p_sockets_host.cc b/content/browser/renderer_host/p2p_sockets_host.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9d32c98e977d58071bbc8a46358c814c9b778a1a
--- /dev/null
+++ b/content/browser/renderer_host/p2p_sockets_host.cc
@@ -0,0 +1,373 @@
+// Copyright (c) 2011 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_sockets_host.h"
+
+#include "chrome/common/render_messages.h"
+
+// Currently P2P Sockets are not implemented for Windows yet.
awong 2011/02/28 23:37:46 Should this be in a p2p_sockets_host_posix.cc file
Sergey Ulanov 2011/03/01 10:49:33 Actually it needs to be split into several files.
awong 2011/03/01 19:57:53 okay. Add TODO then.
Sergey Ulanov 2011/03/01 22:34:17 Split it into multiple files.
+// TODO(sergeyu) implement P2PSocket on Windows.
+#if !defined(OS_WIN)
+
+#include <errno.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+namespace {
+
+// This method returns address of the first IPv4 enabled network
+// interface it finds ignoring the loopback interface. This address is
+// used for all sockets.
+//
+// TODO(sergeyu): This approach works only in the simplest case when
+// host has only one network connection. Instead of binding all
+// connections to this interface we must provide list of interfaces to
+// the renderer, and let the PortAllocater in the renderer process
+// choose local address.
+bool GetLocalAddress(sockaddr_in* addr) {
+ int fd;
+ if ((fd = ::socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ LOG(ERROR) << "socket() failed: " << fd;
+ return false;
+ }
+
+ struct ifconf ifc;
+ ifc.ifc_len = 64 * sizeof(struct ifreq);
+ ifc.ifc_buf = new char[ifc.ifc_len];
+
+ int result = ::ioctl(fd, SIOCGIFCONF, &ifc);
+ if (result < 0) {
+ LOG(ERROR) << "GetLocalAddress: ioctl returned error:" << result;
+ return false;
awong 2011/02/28 23:37:46 This will leak ifc.ifc_buf won't it? Does it make
Sergey Ulanov 2011/03/01 10:49:33 Done.
+ }
+ CHECK(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
awong 2011/02/28 23:37:46 maybe use CHECK_LT?
Sergey Ulanov 2011/03/01 10:49:33 Done.
+
+ struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf);
+ struct ifreq* end =
+ reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len);
+
+ bool found = false;
+ while (ptr < end) {
+ struct sockaddr_in* inaddr =
+ reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr);
+ if (inaddr->sin_family == AF_INET &&
+ strncmp(ptr->ifr_name, "lo", 2) != 0) {
+ memcpy(addr, inaddr, sizeof(sockaddr_in));
+ found = true;
+ break;
+ }
+
+ ptr++;
+ }
+
+ delete [] ifc.ifc_buf;
+ close(fd);
+
+ return found;
+}
+
+bool SocketAddressToSockAddr(P2PSocketAddress address, sockaddr_in* addr) {
+ if (address.address.size() != 4) {
+ return true;
awong 2011/02/28 23:37:46 return false?
Sergey Ulanov 2011/03/01 10:49:33 Done.
+ }
+
+ // TODO(sergeyu): Add IPv6 support.
+ addr->sin_family = AF_INET;
+ memcpy(&addr->sin_addr, &address.address[0], 4);
+ addr->sin_port = htons(address.port);
+ return true;
+}
+
+bool SockAddrToSocketAddress(sockaddr_in* addr,
+ P2PSocketAddress* address) {
+ if (addr->sin_family != AF_INET) {
+ LOG(ERROR) << "SockAddrToSocketAddress: only IPv4 addresses are supported";
+ // TODO(sergeyu): Add IPv6 support.
+ return false;
+ }
+
+ address->address.resize(4);
+ memcpy(&address->address[0], &addr->sin_addr, 4);
+ address->port = ntohs(addr->sin_port);
+
+ return true;
+}
+
+}
awong 2011/02/28 23:37:46 // namespace
Sergey Ulanov 2011/03/01 10:49:33 Done.
+
+class P2PSocketsHost::P2PSocket {
+ public:
+ P2PSocket(P2PSocketsHost* host, int routing_id, int id);
+ ~P2PSocket();
+
+ bool Init();
+
+ void Send(P2PSocketAddress socket_address, const std::vector<char>& data);
+ void DestroyConnection(P2PSocketAddress socket_address);
+
+ private:
+ enum State {
+ STATE_UNINITIALIZED,
+ STATE_OPEN,
+ STATE_ERROR,
+ };
+
+ class ReadWatcher : public MessageLoopForIO::Watcher {
+ public:
+ explicit ReadWatcher(P2PSocketsHost::P2PSocket* socket) : socket_(socket) {}
+
+ // MessageLoopForIO::Watcher methods
+ virtual void OnFileCanReadWithoutBlocking(int /* fd */) {
+ socket_->DidCompleteRead();
+ }
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) {}
+
+ private:
+ P2PSocketsHost::P2PSocket* socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadWatcher);
+ };
+
+ void DidCompleteRead();
+ void OnError();
+
+ P2PSocketsHost* host_;
+ int routing_id_;
+ int id_;
+ State state_;
+ int socket_;
+ MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
+ ReadWatcher read_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(P2PSocket);
+};
+
+P2PSocketsHost::P2PSocket::P2PSocket(
+ P2PSocketsHost* host, int routing_id, int id)
+ : host_(host), routing_id_(routing_id), id_(id),
+ state_(STATE_UNINITIALIZED), socket_(0), read_watcher_(this) {
+}
+
+P2PSocketsHost::P2PSocket::~P2PSocket() {
+ if (state_ == STATE_OPEN) {
+ DCHECK_NE(socket_, 0);
+ ::close(socket_);
+ }
+}
+
+bool P2PSocketsHost::P2PSocket::Init() {
+ socket_ = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ if (socket_ < 0) {
+ LOG(ERROR) << "Failed to create socket: " << socket_;
+ OnError();
+ return false;
+ }
+
+ sockaddr_in addr;
+ if (!GetLocalAddress(&addr)) {
+ LOG(ERROR) << "Failed to get local network address.";
+ OnError();
+ return false;
+ }
+
+ int res = ::bind(socket_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (res < 0) {
+ LOG(ERROR) << "bind() failed: " << res;
+ OnError();
awong 2011/02/28 23:37:46 I don't think socket_ is cleaned up in this situat
Sergey Ulanov 2011/03/01 10:49:33 Changed OnError() to close socket_ if it is open.
+ return false;
+ }
+
+ if (!MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, MessageLoopForIO::WATCH_READ,
+ &read_socket_watcher_, &read_watcher_)) {
+ LOG(ERROR) << "WatchFileDescriptor failed on read, errno: " << errno;
+ OnError();
+ return false;
+ }
+
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getsockname(socket_, reinterpret_cast<sockaddr*>(&addr),
+ &addrlen);
+ if (result < 0) {
+ LOG(ERROR) << "P2PSocket::Init(): unable to get local addr: "
+ << result;
+ OnError();
+ return false;
+ }
+
+ P2PSocketAddress address;
+ if (!SockAddrToSocketAddress(&addr, &address)) {
+ OnError();
+ return false;
+ }
+
+ VLOG(1) << "getsockname() returned "
+ << net::NetAddressToString(
+ reinterpret_cast<sockaddr*>(&addr), sizeof(addr))
+ << ":" << address.port;
+
+ state_ = STATE_OPEN;
+
+ host_->Send(new ViewMsg_P2P_OnSocketCreated(routing_id_, id_, address));
+
+ return true;
+}
+
+void P2PSocketsHost::P2PSocket::OnError() {
+ if (state_ == STATE_OPEN) {
+ DCHECK_NE(socket_, 0);
+ ::close(socket_);
+ socket_ = 0;
+ }
+
+ if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN) {
+ host_->Send(new ViewMsg_P2P_OnError(routing_id_, id_));
+ }
+
+ state_ = STATE_ERROR;
+}
+
+void P2PSocketsHost::P2PSocket::DidCompleteRead() {
+ if (state_ != STATE_OPEN) {
+ return;
+ }
+
+ std::vector<char> data;
+ data.resize(4096);
+ sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int result = ::recvfrom(socket_, &data[0], data.size(), 0,
+ reinterpret_cast<sockaddr*>(&addr), &addr_len);
+ if (result > 0) {
+ data.resize(result);
+ VLOG(2) << "received " << result << " bytes from "
+ << net::NetAddressToString(
+ reinterpret_cast<sockaddr*>(&addr), sizeof(addr))
+ << ":" << ntohs(addr.sin_port);
+ P2PSocketAddress address;
+ if (!SockAddrToSocketAddress(&addr, &address)) {
+ // Drop the data if we faile to convert the address. This should
awong 2011/02/28 23:37:46 faile -> fail
Sergey Ulanov 2011/03/01 10:49:33 Done.
+ // not normally happen.
awong 2011/02/28 23:37:46 Hmm...does this warrant STATE_ERROR?
Sergey Ulanov 2011/03/01 10:49:33 I've added NOTREACHED() here.
+ return;
+ }
+
+ host_->Send(new ViewMsg_P2P_OnDataReceived(routing_id_, id_,
+ address, data));
+ } else if (result < 0) {
+ LOG(ERROR) << "recvfrom() returned error: " << result;
+ OnError();
+ }
+}
+
+void P2PSocketsHost::P2PSocket::Send(P2PSocketAddress socket_address,
+ const std::vector<char>& data) {
awong 2011/02/28 23:37:46 indentation.
Sergey Ulanov 2011/03/01 10:49:33 Done.
+ sockaddr_in addr;
+ SocketAddressToSockAddr(socket_address, &addr);
+ int result = sendto(socket_, &data[0], data.size(), 0,
+ reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (result < 0) {
+ LOG(ERROR) << "Send failed.";
+ } else {
+ VLOG(2) << "Sent " << result << " bytes to "
+ << net::NetAddressToString(
+ reinterpret_cast<sockaddr*>(&addr), sizeof(addr))
+ << ":" << ntohs(addr.sin_port);
+ }
+}
+
+#else // defined(OS_WIN)
+
+class P2PSocketsHost::P2PSocket {
+ public:
+ P2PSocket(P2PSocketsHost* host, int routing_id, int id)
+ : host_(host), routing_id_(routing_id), id_(id) {
+ }
+ ~P2PSocket() { }
+
+ bool Init() {
+ host_->Send(new ViewMsg_P2P_OnError(routing_id_, id_));
+ return false;
+ }
+
+ void Send(P2PSocketAddress socket_address, const std::vector<char>& data) { }
+ void DestroyConnection(P2PSocketAddress socket_address) { }
+
+ private:
+ P2PSocketsHost* host_;
+ int routing_id_;
+ int id_;
+
+ DISALLOW_COPY_AND_ASSIGN(P2PSocket);
+};
+
+#endif // defined(OS_WIN)
+
+P2PSocketsHost::P2PSocketsHost() {
+}
+
+void P2PSocketsHost::OnChannelClosing() {
+ BrowserMessageFilter::OnChannelClosing();
+
+ // Since the IPC channel is gone, close pending connections.
+ for (IDMap<P2PSocket>::iterator i(&sockets_); !i.IsAtEnd(); i.Advance()) {
+ sockets_.Remove(i.GetCurrentKey());
+ }
+}
+
+void P2PSocketsHost::OnDestruct() const {
+ BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
+bool P2PSocketsHost::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(P2PSocketsHost, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_P2P_CreateSocket, OnCreateSocket)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_P2P_Send, OnSend)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_P2P_DestroySocket, OnDestroySocket)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void P2PSocketsHost::OnCreateSocket(
+ const IPC::Message& msg, P2PSocketType type, int socket_id,
+ P2PSocketAddress remote_address) {
+ if (sockets_.Lookup(socket_id)) {
+ LOG(ERROR) << "Received ViewHostMsg_P2P_CreateSocket for socket "
+ "that already exists.";
+ return;
+ }
+
+ if (type != P2P_SOCKET_UDP) {
+ Send(new ViewMsg_P2P_OnError(msg.routing_id(), socket_id));
+ return;
+ }
+
+ scoped_ptr<P2PSocket> socket(
+ new P2PSocket(this, msg.routing_id(), socket_id));
+ if (socket->Init()) {
+ sockets_.AddWithID(socket.release(), socket_id);
+ }
+}
+
+void P2PSocketsHost::OnSend(const IPC::Message& msg, int socket_id,
+ P2PSocketAddress socket_address,
+ const std::vector<char>& data) {
+ P2PSocket* socket = sockets_.Lookup(socket_id);
+ if (!socket) {
+ LOG(ERROR) << "Received ViewHostMsg_P2P_Send for invalid socket_id.";
+ return;
+ }
+ socket->Send(socket_address, data);
+}
+
+void P2PSocketsHost::OnDestroySocket(const IPC::Message& msg, int socket_id) {
+ sockets_.Remove(socket_id);
+}
« no previous file with comments | « content/browser/renderer_host/p2p_sockets_host.h ('k') | content/content_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698