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

Unified Diff: content/browser/renderer_host/p2p_socket_host_posix.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
Index: content/browser/renderer_host/p2p_socket_host_posix.cc
diff --git a/content/browser/renderer_host/p2p_socket_host_posix.cc b/content/browser/renderer_host/p2p_socket_host_posix.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f9ae31f793c64279fd20041bd0e9ae69327f2e7c
--- /dev/null
+++ b/content/browser/renderer_host/p2p_socket_host_posix.cc
@@ -0,0 +1,260 @@
+// 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_socket_host_posix.h"
+
+#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>
+
+#include "content/browser/renderer_host/p2p_sockets_host.h"
+#include "content/common/p2p_messages.h"
+#include "net/base/net_util.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);
+ scoped_array<char> ifc_buffer(new char[ifc.ifc_len]);
+ ifc.ifc_buf = ifc_buffer.get();
+
+ int result = ioctl(fd, SIOCGIFCONF, &ifc);
+ if (result < 0) {
+ LOG(ERROR) << "GetLocalAddress: ioctl returned error:" << result;
+ close(fd);
+ return false;
+ }
+ CHECK_LT(ifc.ifc_len, static_cast<int>(64 * sizeof(struct ifreq)));
+
+ 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++;
+ }
+
+ close(fd);
+
+ return found;
+}
+
+bool SocketAddressToSockAddr(P2PSocketAddress address, sockaddr_in* addr) {
+ // TODO(sergeyu): Add IPv6 support.
+ if (address.address.size() != 4) {
+ return false;
+ }
+
+ 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;
+}
+
+} // namespace
+
+P2PSocketHostPosix::P2PSocketHostPosix(
+ P2PSocketsHost* host, int routing_id, int id)
+ : P2PSocketHost(host, routing_id, id),
+ state_(STATE_UNINITIALIZED), socket_(0), read_watcher_(this) {
+}
+
+P2PSocketHostPosix::~P2PSocketHostPosix() {
+ if (state_ == STATE_OPEN) {
+ DCHECK_NE(socket_, 0);
+ close(socket_);
+ }
+}
+
+bool P2PSocketHostPosix::Init() {
+ socket_ = socket(AF_INET, SOCK_DGRAM, 0);
+ if (socket_ < 0) {
+ LOG(ERROR) << "Failed to create socket: " << socket_;
+ OnError();
+ return false;
+ }
+
+ int result = net::SetNonBlocking(socket_);
+ if (result < 0) {
+ LOG(ERROR) << "Failed to set O_NONBLOCK flag: " << result;
+ OnError();
+ return false;
+ }
+
+ sockaddr_in addr;
+ if (!GetLocalAddress(&addr)) {
+ LOG(ERROR) << "Failed to get local network address.";
+ OnError();
+ return false;
+ }
+
+ result = bind(socket_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (result < 0) {
+ LOG(ERROR) << "bind() failed: " << result;
+ OnError();
+ 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);
+ 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 P2PMsg_OnSocketCreated(routing_id_, id_, address));
+
+ return true;
+}
+
+void P2PSocketHostPosix::OnError() {
+ if (socket_ != 0) {
+ close(socket_);
+ socket_ = 0;
+ }
+
+ if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN) {
+ host_->Send(new P2PMsg_OnError(routing_id_, id_));
+ }
+
+ state_ = STATE_ERROR;
+}
+
+void P2PSocketHostPosix::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)) {
+ // Address conversion fails only if we receive a non-IPv4
+ // packet, which should never happen because the socket is IPv4.
+ NOTREACHED();
+ return;
+ }
+
+ host_->Send(new P2PMsg_OnDataReceived(routing_id_, id_,
+ address, data));
+ } else if (result < 0) {
+ LOG(ERROR) << "recvfrom() returned error: " << result;
+ OnError();
+ }
+}
+
+void P2PSocketHostPosix::Send(P2PSocketAddress socket_address,
+ const std::vector<char>& data) {
+ 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);
+ }
+}
+
+// static
+P2PSocketHost* P2PSocketHost::Create(
+ P2PSocketsHost* host, int routing_id, int id, P2PSocketType type) {
+ switch (type) {
+ case P2P_SOCKET_UDP:
+ return new P2PSocketHostPosix(host, routing_id, id);
+
+ case P2P_SOCKET_TCP_SERVER:
+ // TODO(sergeyu): Implement TCP sockets support.
+ return NULL;
+
+ case P2P_SOCKET_TCP_CLIENT:
+ return NULL;
+ }
+
+ NOTREACHED();
+ return NULL;
+}
« no previous file with comments | « content/browser/renderer_host/p2p_socket_host_posix.h ('k') | content/browser/renderer_host/p2p_socket_host_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698