| 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;
|
| +}
|
|
|