Chromium Code Reviews| 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_sockets_host.h" | |
| 6 | |
| 7 #include "chrome/common/render_messages.h" | |
| 8 | |
| 9 // 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.
| |
| 10 // TODO(sergeyu) implement P2PSocket on Windows. | |
| 11 #if !defined(OS_WIN) | |
| 12 | |
| 13 #include <errno.h> | |
| 14 #include <net/if.h> | |
| 15 #include <netinet/in.h> | |
| 16 #include <sys/ioctl.h> | |
| 17 #include <sys/socket.h> | |
| 18 #include <sys/types.h> | |
| 19 #include <sys/utsname.h> | |
| 20 #include <unistd.h> | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // This method returns address of the first IPv4 enabled network | |
| 25 // interface it finds ignoring the loopback interface. This address is | |
| 26 // used for all sockets. | |
| 27 // | |
| 28 // TODO(sergeyu): This approach works only in the simplest case when | |
| 29 // host has only one network connection. Instead of binding all | |
| 30 // connections to this interface we must provide list of interfaces to | |
| 31 // the renderer, and let the PortAllocater in the renderer process | |
| 32 // choose local address. | |
| 33 bool GetLocalAddress(sockaddr_in* addr) { | |
| 34 int fd; | |
| 35 if ((fd = ::socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
| 36 LOG(ERROR) << "socket() failed: " << fd; | |
| 37 return false; | |
| 38 } | |
| 39 | |
| 40 struct ifconf ifc; | |
| 41 ifc.ifc_len = 64 * sizeof(struct ifreq); | |
| 42 ifc.ifc_buf = new char[ifc.ifc_len]; | |
| 43 | |
| 44 int result = ::ioctl(fd, SIOCGIFCONF, &ifc); | |
| 45 if (result < 0) { | |
| 46 LOG(ERROR) << "GetLocalAddress: ioctl returned error:" << result; | |
| 47 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.
| |
| 48 } | |
| 49 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.
| |
| 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 delete [] ifc.ifc_buf; | |
| 70 close(fd); | |
| 71 | |
| 72 return found; | |
| 73 } | |
| 74 | |
| 75 bool SocketAddressToSockAddr(P2PSocketAddress address, sockaddr_in* addr) { | |
| 76 if (address.address.size() != 4) { | |
| 77 return true; | |
|
awong
2011/02/28 23:37:46
return false?
Sergey Ulanov
2011/03/01 10:49:33
Done.
| |
| 78 } | |
| 79 | |
| 80 // TODO(sergeyu): Add IPv6 support. | |
| 81 addr->sin_family = AF_INET; | |
| 82 memcpy(&addr->sin_addr, &address.address[0], 4); | |
| 83 addr->sin_port = htons(address.port); | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 bool SockAddrToSocketAddress(sockaddr_in* addr, | |
| 88 P2PSocketAddress* address) { | |
| 89 if (addr->sin_family != AF_INET) { | |
| 90 LOG(ERROR) << "SockAddrToSocketAddress: only IPv4 addresses are supported"; | |
| 91 // TODO(sergeyu): Add IPv6 support. | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 address->address.resize(4); | |
| 96 memcpy(&address->address[0], &addr->sin_addr, 4); | |
| 97 address->port = ntohs(addr->sin_port); | |
| 98 | |
| 99 return true; | |
| 100 } | |
| 101 | |
| 102 } | |
|
awong
2011/02/28 23:37:46
// namespace
Sergey Ulanov
2011/03/01 10:49:33
Done.
| |
| 103 | |
| 104 class P2PSocketsHost::P2PSocket { | |
| 105 public: | |
| 106 P2PSocket(P2PSocketsHost* host, int routing_id, int id); | |
| 107 ~P2PSocket(); | |
| 108 | |
| 109 bool Init(); | |
| 110 | |
| 111 void Send(P2PSocketAddress socket_address, const std::vector<char>& data); | |
| 112 void DestroyConnection(P2PSocketAddress socket_address); | |
| 113 | |
| 114 private: | |
| 115 enum State { | |
| 116 STATE_UNINITIALIZED, | |
| 117 STATE_OPEN, | |
| 118 STATE_ERROR, | |
| 119 }; | |
| 120 | |
| 121 class ReadWatcher : public MessageLoopForIO::Watcher { | |
| 122 public: | |
| 123 explicit ReadWatcher(P2PSocketsHost::P2PSocket* socket) : socket_(socket) {} | |
| 124 | |
| 125 // MessageLoopForIO::Watcher methods | |
| 126 virtual void OnFileCanReadWithoutBlocking(int /* fd */) { | |
| 127 socket_->DidCompleteRead(); | |
| 128 } | |
| 129 virtual void OnFileCanWriteWithoutBlocking(int /* fd */) {} | |
| 130 | |
| 131 private: | |
| 132 P2PSocketsHost::P2PSocket* socket_; | |
| 133 | |
| 134 DISALLOW_COPY_AND_ASSIGN(ReadWatcher); | |
| 135 }; | |
| 136 | |
| 137 void DidCompleteRead(); | |
| 138 void OnError(); | |
| 139 | |
| 140 P2PSocketsHost* host_; | |
| 141 int routing_id_; | |
| 142 int id_; | |
| 143 State state_; | |
| 144 int socket_; | |
| 145 MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_; | |
| 146 ReadWatcher read_watcher_; | |
| 147 | |
| 148 DISALLOW_COPY_AND_ASSIGN(P2PSocket); | |
| 149 }; | |
| 150 | |
| 151 P2PSocketsHost::P2PSocket::P2PSocket( | |
| 152 P2PSocketsHost* host, int routing_id, int id) | |
| 153 : host_(host), routing_id_(routing_id), id_(id), | |
| 154 state_(STATE_UNINITIALIZED), socket_(0), read_watcher_(this) { | |
| 155 } | |
| 156 | |
| 157 P2PSocketsHost::P2PSocket::~P2PSocket() { | |
| 158 if (state_ == STATE_OPEN) { | |
| 159 DCHECK_NE(socket_, 0); | |
| 160 ::close(socket_); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 bool P2PSocketsHost::P2PSocket::Init() { | |
| 165 socket_ = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); | |
| 166 if (socket_ < 0) { | |
| 167 LOG(ERROR) << "Failed to create socket: " << socket_; | |
| 168 OnError(); | |
| 169 return false; | |
| 170 } | |
| 171 | |
| 172 sockaddr_in addr; | |
| 173 if (!GetLocalAddress(&addr)) { | |
| 174 LOG(ERROR) << "Failed to get local network address."; | |
| 175 OnError(); | |
| 176 return false; | |
| 177 } | |
| 178 | |
| 179 int res = ::bind(socket_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)); | |
| 180 if (res < 0) { | |
| 181 LOG(ERROR) << "bind() failed: " << res; | |
| 182 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.
| |
| 183 return false; | |
| 184 } | |
| 185 | |
| 186 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
| 187 socket_, true, MessageLoopForIO::WATCH_READ, | |
| 188 &read_socket_watcher_, &read_watcher_)) { | |
| 189 LOG(ERROR) << "WatchFileDescriptor failed on read, errno: " << errno; | |
| 190 OnError(); | |
| 191 return false; | |
| 192 } | |
| 193 | |
| 194 socklen_t addrlen = sizeof(addr); | |
| 195 int result = ::getsockname(socket_, reinterpret_cast<sockaddr*>(&addr), | |
| 196 &addrlen); | |
| 197 if (result < 0) { | |
| 198 LOG(ERROR) << "P2PSocket::Init(): unable to get local addr: " | |
| 199 << result; | |
| 200 OnError(); | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 P2PSocketAddress address; | |
| 205 if (!SockAddrToSocketAddress(&addr, &address)) { | |
| 206 OnError(); | |
| 207 return false; | |
| 208 } | |
| 209 | |
| 210 VLOG(1) << "getsockname() returned " | |
| 211 << net::NetAddressToString( | |
| 212 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) | |
| 213 << ":" << address.port; | |
| 214 | |
| 215 state_ = STATE_OPEN; | |
| 216 | |
| 217 host_->Send(new ViewMsg_P2P_OnSocketCreated(routing_id_, id_, address)); | |
| 218 | |
| 219 return true; | |
| 220 } | |
| 221 | |
| 222 void P2PSocketsHost::P2PSocket::OnError() { | |
| 223 if (state_ == STATE_OPEN) { | |
| 224 DCHECK_NE(socket_, 0); | |
| 225 ::close(socket_); | |
| 226 socket_ = 0; | |
| 227 } | |
| 228 | |
| 229 if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN) { | |
| 230 host_->Send(new ViewMsg_P2P_OnError(routing_id_, id_)); | |
| 231 } | |
| 232 | |
| 233 state_ = STATE_ERROR; | |
| 234 } | |
| 235 | |
| 236 void P2PSocketsHost::P2PSocket::DidCompleteRead() { | |
| 237 if (state_ != STATE_OPEN) { | |
| 238 return; | |
| 239 } | |
| 240 | |
| 241 std::vector<char> data; | |
| 242 data.resize(4096); | |
| 243 sockaddr_in addr; | |
| 244 socklen_t addr_len = sizeof(addr); | |
| 245 int result = ::recvfrom(socket_, &data[0], data.size(), 0, | |
| 246 reinterpret_cast<sockaddr*>(&addr), &addr_len); | |
| 247 if (result > 0) { | |
| 248 data.resize(result); | |
| 249 VLOG(2) << "received " << result << " bytes from " | |
| 250 << net::NetAddressToString( | |
| 251 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) | |
| 252 << ":" << ntohs(addr.sin_port); | |
| 253 P2PSocketAddress address; | |
| 254 if (!SockAddrToSocketAddress(&addr, &address)) { | |
| 255 // 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.
| |
| 256 // 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.
| |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 host_->Send(new ViewMsg_P2P_OnDataReceived(routing_id_, id_, | |
| 261 address, data)); | |
| 262 } else if (result < 0) { | |
| 263 LOG(ERROR) << "recvfrom() returned error: " << result; | |
| 264 OnError(); | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 void P2PSocketsHost::P2PSocket::Send(P2PSocketAddress socket_address, | |
| 269 const std::vector<char>& data) { | |
|
awong
2011/02/28 23:37:46
indentation.
Sergey Ulanov
2011/03/01 10:49:33
Done.
| |
| 270 sockaddr_in addr; | |
| 271 SocketAddressToSockAddr(socket_address, &addr); | |
| 272 int result = sendto(socket_, &data[0], data.size(), 0, | |
| 273 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)); | |
| 274 if (result < 0) { | |
| 275 LOG(ERROR) << "Send failed."; | |
| 276 } else { | |
| 277 VLOG(2) << "Sent " << result << " bytes to " | |
| 278 << net::NetAddressToString( | |
| 279 reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) | |
| 280 << ":" << ntohs(addr.sin_port); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 #else // defined(OS_WIN) | |
| 285 | |
| 286 class P2PSocketsHost::P2PSocket { | |
| 287 public: | |
| 288 P2PSocket(P2PSocketsHost* host, int routing_id, int id) | |
| 289 : host_(host), routing_id_(routing_id), id_(id) { | |
| 290 } | |
| 291 ~P2PSocket() { } | |
| 292 | |
| 293 bool Init() { | |
| 294 host_->Send(new ViewMsg_P2P_OnError(routing_id_, id_)); | |
| 295 return false; | |
| 296 } | |
| 297 | |
| 298 void Send(P2PSocketAddress socket_address, const std::vector<char>& data) { } | |
| 299 void DestroyConnection(P2PSocketAddress socket_address) { } | |
| 300 | |
| 301 private: | |
| 302 P2PSocketsHost* host_; | |
| 303 int routing_id_; | |
| 304 int id_; | |
| 305 | |
| 306 DISALLOW_COPY_AND_ASSIGN(P2PSocket); | |
| 307 }; | |
| 308 | |
| 309 #endif // defined(OS_WIN) | |
| 310 | |
| 311 P2PSocketsHost::P2PSocketsHost() { | |
| 312 } | |
| 313 | |
| 314 void P2PSocketsHost::OnChannelClosing() { | |
| 315 BrowserMessageFilter::OnChannelClosing(); | |
| 316 | |
| 317 // Since the IPC channel is gone, close pending connections. | |
| 318 for (IDMap<P2PSocket>::iterator i(&sockets_); !i.IsAtEnd(); i.Advance()) { | |
| 319 sockets_.Remove(i.GetCurrentKey()); | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 void P2PSocketsHost::OnDestruct() const { | |
| 324 BrowserThread::DeleteOnIOThread::Destruct(this); | |
| 325 } | |
| 326 | |
| 327 bool P2PSocketsHost::OnMessageReceived(const IPC::Message& message, | |
| 328 bool* message_was_ok) { | |
| 329 bool handled = true; | |
| 330 IPC_BEGIN_MESSAGE_MAP_EX(P2PSocketsHost, message, *message_was_ok) | |
| 331 IPC_MESSAGE_HANDLER(ViewHostMsg_P2P_CreateSocket, OnCreateSocket) | |
| 332 IPC_MESSAGE_HANDLER(ViewHostMsg_P2P_Send, OnSend) | |
| 333 IPC_MESSAGE_HANDLER(ViewHostMsg_P2P_DestroySocket, OnDestroySocket) | |
| 334 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 335 IPC_END_MESSAGE_MAP_EX() | |
| 336 return handled; | |
| 337 } | |
| 338 | |
| 339 void P2PSocketsHost::OnCreateSocket( | |
| 340 const IPC::Message& msg, P2PSocketType type, int socket_id, | |
| 341 P2PSocketAddress remote_address) { | |
| 342 if (sockets_.Lookup(socket_id)) { | |
| 343 LOG(ERROR) << "Received ViewHostMsg_P2P_CreateSocket for socket " | |
| 344 "that already exists."; | |
| 345 return; | |
| 346 } | |
| 347 | |
| 348 if (type != P2P_SOCKET_UDP) { | |
| 349 Send(new ViewMsg_P2P_OnError(msg.routing_id(), socket_id)); | |
| 350 return; | |
| 351 } | |
| 352 | |
| 353 scoped_ptr<P2PSocket> socket( | |
| 354 new P2PSocket(this, msg.routing_id(), socket_id)); | |
| 355 if (socket->Init()) { | |
| 356 sockets_.AddWithID(socket.release(), socket_id); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 void P2PSocketsHost::OnSend(const IPC::Message& msg, int socket_id, | |
| 361 P2PSocketAddress socket_address, | |
| 362 const std::vector<char>& data) { | |
| 363 P2PSocket* socket = sockets_.Lookup(socket_id); | |
| 364 if (!socket) { | |
| 365 LOG(ERROR) << "Received ViewHostMsg_P2P_Send for invalid socket_id."; | |
| 366 return; | |
| 367 } | |
| 368 socket->Send(socket_address, data); | |
| 369 } | |
| 370 | |
| 371 void P2PSocketsHost::OnDestroySocket(const IPC::Message& msg, int socket_id) { | |
| 372 sockets_.Remove(socket_id); | |
| 373 } | |
| OLD | NEW |