OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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_posix.h" |
| 6 |
| 7 #include <errno.h> |
| 8 #include <net/if.h> |
| 9 #include <netinet/in.h> |
| 10 #include <sys/ioctl.h> |
| 11 #include <sys/socket.h> |
| 12 #include <sys/types.h> |
| 13 #include <sys/utsname.h> |
| 14 #include <unistd.h> |
| 15 |
| 16 #include "content/browser/renderer_host/p2p_sockets_host.h" |
| 17 #include "content/common/p2p_messages.h" |
| 18 #include "net/base/net_util.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 // This method returns address of the first IPv4 enabled network |
| 23 // interface it finds ignoring the loopback interface. This address is |
| 24 // used for all sockets. |
| 25 // |
| 26 // TODO(sergeyu): This approach works only in the simplest case when |
| 27 // host has only one network connection. Instead of binding all |
| 28 // connections to this interface we must provide list of interfaces to |
| 29 // the renderer, and let the PortAllocater in the renderer process |
| 30 // choose local address. |
| 31 bool GetLocalAddress(sockaddr_in* addr) { |
| 32 int fd; |
| 33 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
| 34 LOG(ERROR) << "socket() failed: " << fd; |
| 35 return false; |
| 36 } |
| 37 |
| 38 struct ifconf ifc; |
| 39 ifc.ifc_len = 64 * sizeof(struct ifreq); |
| 40 scoped_array<char> ifc_buffer(new char[ifc.ifc_len]); |
| 41 ifc.ifc_buf = ifc_buffer.get(); |
| 42 |
| 43 int result = ioctl(fd, SIOCGIFCONF, &ifc); |
| 44 if (result < 0) { |
| 45 LOG(ERROR) << "GetLocalAddress: ioctl returned error:" << result; |
| 46 close(fd); |
| 47 return false; |
| 48 } |
| 49 CHECK_LT(ifc.ifc_len, static_cast<int>(64 * sizeof(struct ifreq))); |
| 50 |
| 51 struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf); |
| 52 struct ifreq* end = |
| 53 reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len); |
| 54 |
| 55 bool found = false; |
| 56 while (ptr < end) { |
| 57 struct sockaddr_in* inaddr = |
| 58 reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr); |
| 59 if (inaddr->sin_family == AF_INET && |
| 60 strncmp(ptr->ifr_name, "lo", 2) != 0) { |
| 61 memcpy(addr, inaddr, sizeof(sockaddr_in)); |
| 62 found = true; |
| 63 break; |
| 64 } |
| 65 |
| 66 ptr++; |
| 67 } |
| 68 |
| 69 close(fd); |
| 70 |
| 71 return found; |
| 72 } |
| 73 |
| 74 bool SocketAddressToSockAddr(P2PSocketAddress address, sockaddr_in* addr) { |
| 75 // TODO(sergeyu): Add IPv6 support. |
| 76 if (address.address.size() != 4) { |
| 77 return false; |
| 78 } |
| 79 |
| 80 addr->sin_family = AF_INET; |
| 81 memcpy(&addr->sin_addr, &address.address[0], 4); |
| 82 addr->sin_port = htons(address.port); |
| 83 return true; |
| 84 } |
| 85 |
| 86 bool SockAddrToSocketAddress(sockaddr_in* addr, |
| 87 P2PSocketAddress* address) { |
| 88 if (addr->sin_family != AF_INET) { |
| 89 LOG(ERROR) << "SockAddrToSocketAddress: only IPv4 addresses are supported"; |
| 90 // TODO(sergeyu): Add IPv6 support. |
| 91 return false; |
| 92 } |
| 93 |
| 94 address->address.resize(4); |
| 95 memcpy(&address->address[0], &addr->sin_addr, 4); |
| 96 address->port = ntohs(addr->sin_port); |
| 97 |
| 98 return true; |
| 99 } |
| 100 |
| 101 } // namespace |
| 102 |
| 103 P2PSocketHostPosix::P2PSocketHostPosix( |
| 104 P2PSocketsHost* host, int routing_id, int id) |
| 105 : P2PSocketHost(host, routing_id, id), |
| 106 state_(STATE_UNINITIALIZED), socket_(0), read_watcher_(this) { |
| 107 } |
| 108 |
| 109 P2PSocketHostPosix::~P2PSocketHostPosix() { |
| 110 if (state_ == STATE_OPEN) { |
| 111 DCHECK_NE(socket_, 0); |
| 112 close(socket_); |
| 113 } |
| 114 } |
| 115 |
| 116 bool P2PSocketHostPosix::Init() { |
| 117 socket_ = socket(AF_INET, SOCK_DGRAM, 0); |
| 118 if (socket_ < 0) { |
| 119 LOG(ERROR) << "Failed to create socket: " << socket_; |
| 120 OnError(); |
| 121 return false; |
| 122 } |
| 123 |
| 124 int result = net::SetNonBlocking(socket_); |
| 125 if (result < 0) { |
| 126 LOG(ERROR) << "Failed to set O_NONBLOCK flag: " << result; |
| 127 OnError(); |
| 128 return false; |
| 129 } |
| 130 |
| 131 sockaddr_in addr; |
| 132 if (!GetLocalAddress(&addr)) { |
| 133 LOG(ERROR) << "Failed to get local network address."; |
| 134 OnError(); |
| 135 return false; |
| 136 } |
| 137 |
| 138 result = bind(socket_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)); |
| 139 if (result < 0) { |
| 140 LOG(ERROR) << "bind() failed: " << result; |
| 141 OnError(); |
| 142 return false; |
| 143 } |
| 144 |
| 145 if (!MessageLoopForIO::current()->WatchFileDescriptor( |
| 146 socket_, true, MessageLoopForIO::WATCH_READ, |
| 147 &read_socket_watcher_, &read_watcher_)) { |
| 148 LOG(ERROR) << "WatchFileDescriptor failed on read, errno: " << errno; |
| 149 OnError(); |
| 150 return false; |
| 151 } |
| 152 |
| 153 socklen_t addrlen = sizeof(addr); |
| 154 result = getsockname(socket_, reinterpret_cast<sockaddr*>(&addr), |
| 155 &addrlen); |
| 156 if (result < 0) { |
| 157 LOG(ERROR) << "P2PSocket::Init(): unable to get local addr: " |
| 158 << result; |
| 159 OnError(); |
| 160 return false; |
| 161 } |
| 162 |
| 163 P2PSocketAddress address; |
| 164 if (!SockAddrToSocketAddress(&addr, &address)) { |
| 165 OnError(); |
| 166 return false; |
| 167 } |
| 168 |
| 169 VLOG(1) << "getsockname() returned " |
| 170 << net::NetAddressToString( |
| 171 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) |
| 172 << ":" << address.port; |
| 173 |
| 174 state_ = STATE_OPEN; |
| 175 |
| 176 host_->Send(new P2PMsg_OnSocketCreated(routing_id_, id_, address)); |
| 177 |
| 178 return true; |
| 179 } |
| 180 |
| 181 void P2PSocketHostPosix::OnError() { |
| 182 if (socket_ != 0) { |
| 183 close(socket_); |
| 184 socket_ = 0; |
| 185 } |
| 186 |
| 187 if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN) { |
| 188 host_->Send(new P2PMsg_OnError(routing_id_, id_)); |
| 189 } |
| 190 |
| 191 state_ = STATE_ERROR; |
| 192 } |
| 193 |
| 194 void P2PSocketHostPosix::DidCompleteRead() { |
| 195 if (state_ != STATE_OPEN) { |
| 196 return; |
| 197 } |
| 198 |
| 199 std::vector<char> data; |
| 200 data.resize(4096); |
| 201 sockaddr_in addr; |
| 202 socklen_t addr_len = sizeof(addr); |
| 203 int result = recvfrom(socket_, &data[0], data.size(), 0, |
| 204 reinterpret_cast<sockaddr*>(&addr), &addr_len); |
| 205 if (result > 0) { |
| 206 data.resize(result); |
| 207 VLOG(2) << "received " << result << " bytes from " |
| 208 << net::NetAddressToString( |
| 209 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) |
| 210 << ":" << ntohs(addr.sin_port); |
| 211 P2PSocketAddress address; |
| 212 if (!SockAddrToSocketAddress(&addr, &address)) { |
| 213 // Address conversion fails only if we receive a non-IPv4 |
| 214 // packet, which should never happen because the socket is IPv4. |
| 215 NOTREACHED(); |
| 216 return; |
| 217 } |
| 218 |
| 219 host_->Send(new P2PMsg_OnDataReceived(routing_id_, id_, |
| 220 address, data)); |
| 221 } else if (result < 0) { |
| 222 LOG(ERROR) << "recvfrom() returned error: " << result; |
| 223 OnError(); |
| 224 } |
| 225 } |
| 226 |
| 227 void P2PSocketHostPosix::Send(P2PSocketAddress socket_address, |
| 228 const std::vector<char>& data) { |
| 229 sockaddr_in addr; |
| 230 SocketAddressToSockAddr(socket_address, &addr); |
| 231 int result = sendto(socket_, &data[0], data.size(), 0, |
| 232 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)); |
| 233 if (result < 0) { |
| 234 LOG(ERROR) << "Send failed."; |
| 235 } else { |
| 236 VLOG(2) << "Sent " << result << " bytes to " |
| 237 << net::NetAddressToString( |
| 238 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) |
| 239 << ":" << ntohs(addr.sin_port); |
| 240 } |
| 241 } |
| 242 |
| 243 // static |
| 244 P2PSocketHost* P2PSocketHost::Create( |
| 245 P2PSocketsHost* host, int routing_id, int id, P2PSocketType type) { |
| 246 switch (type) { |
| 247 case P2P_SOCKET_UDP: |
| 248 return new P2PSocketHostPosix(host, routing_id, id); |
| 249 |
| 250 case P2P_SOCKET_TCP_SERVER: |
| 251 // TODO(sergeyu): Implement TCP sockets support. |
| 252 return NULL; |
| 253 |
| 254 case P2P_SOCKET_TCP_CLIENT: |
| 255 return NULL; |
| 256 } |
| 257 |
| 258 NOTREACHED(); |
| 259 return NULL; |
| 260 } |
OLD | NEW |