| 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 "chrome/renderer/p2p/ipc_socket_factory.h" | |
| 6 | |
| 7 #include "base/message_loop.h" | |
| 8 #include "base/message_loop_proxy.h" | |
| 9 #include "chrome/renderer/p2p/socket_client.h" | |
| 10 #include "chrome/renderer/p2p/socket_dispatcher.h" | |
| 11 #include "third_party/libjingle/source/talk/base/asyncpacketsocket.h" | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 const size_t kIPv4AddressSize = 4; | |
| 16 | |
| 17 // Chromium and libjingle represent socket addresses differently. The | |
| 18 // following two functions are used to convert addresses from one | |
| 19 // representation to another. | |
| 20 bool ChromeToLibjingleSocketAddress(const net::IPEndPoint& address_chrome, | |
| 21 talk_base::SocketAddress* address_lj) { | |
| 22 if (address_chrome.GetFamily() != AF_INET) { | |
| 23 LOG(ERROR) << "Only IPv4 addresses are supported."; | |
| 24 return false; | |
| 25 } | |
| 26 uint32 ip_as_int = ntohl(*reinterpret_cast<const uint32*>( | |
| 27 &address_chrome.address()[0])); | |
| 28 *address_lj = talk_base::SocketAddress(ip_as_int, address_chrome.port()); | |
| 29 return true; | |
| 30 } | |
| 31 | |
| 32 bool LibjingleToIPEndPoint(const talk_base::SocketAddress& address_lj, | |
| 33 net::IPEndPoint* address_chrome) { | |
| 34 uint32 ip = htonl(address_lj.ip()); | |
| 35 net::IPAddressNumber address; | |
| 36 address.resize(kIPv4AddressSize); | |
| 37 memcpy(&address[0], &ip, kIPv4AddressSize); | |
| 38 *address_chrome = net::IPEndPoint(address, address_lj.port()); | |
| 39 return true; | |
| 40 } | |
| 41 | |
| 42 // IpcPacketSocket implements talk_base::AsyncPacketSocket interface | |
| 43 // using P2PSocketClient that works over IPC-channel. It must be used | |
| 44 // on the thread it was created. | |
| 45 class IpcPacketSocket : public talk_base::AsyncPacketSocket, | |
| 46 public P2PSocketClient::Delegate { | |
| 47 public: | |
| 48 IpcPacketSocket(); | |
| 49 virtual ~IpcPacketSocket(); | |
| 50 | |
| 51 bool Init(P2PSocketType type, P2PSocketClient* client, | |
| 52 const talk_base::SocketAddress& address); | |
| 53 | |
| 54 // talk_base::AsyncPacketSocket interface. | |
| 55 virtual talk_base::SocketAddress GetLocalAddress(bool* allocated) const; | |
| 56 virtual talk_base::SocketAddress GetRemoteAddress() const; | |
| 57 virtual int Send(const void *pv, size_t cb); | |
| 58 virtual int SendTo(const void *pv, size_t cb, | |
| 59 const talk_base::SocketAddress& addr); | |
| 60 virtual int Close(); | |
| 61 virtual talk_base::Socket::ConnState GetState() const; | |
| 62 virtual int GetOption(talk_base::Socket::Option opt, int* value); | |
| 63 virtual int SetOption(talk_base::Socket::Option opt, int value); | |
| 64 virtual int GetError() const; | |
| 65 virtual void SetError(int error); | |
| 66 | |
| 67 // P2PSocketClient::Delegate | |
| 68 virtual void OnOpen(const net::IPEndPoint& address); | |
| 69 virtual void OnError(); | |
| 70 virtual void OnDataReceived(const net::IPEndPoint& address, | |
| 71 const std::vector<char>& data); | |
| 72 | |
| 73 private: | |
| 74 enum State { | |
| 75 STATE_UNINITIALIZED, | |
| 76 STATE_OPENING, | |
| 77 STATE_OPEN, | |
| 78 STATE_CLOSED, | |
| 79 STATE_ERROR, | |
| 80 }; | |
| 81 | |
| 82 // Message loop on which this socket was created and being used. | |
| 83 MessageLoop* message_loop_; | |
| 84 | |
| 85 // Corresponding P2P socket client. | |
| 86 scoped_refptr<P2PSocketClient> client_; | |
| 87 | |
| 88 // Local address is allocated by the browser process, and the | |
| 89 // renderer side doesn't know the address until it receives OnOpen() | |
| 90 // event from the browser. | |
| 91 talk_base::SocketAddress local_address_; | |
| 92 bool address_initialized_; | |
| 93 | |
| 94 // Remote address for client TCP connections. | |
| 95 talk_base::SocketAddress remote_address_; | |
| 96 | |
| 97 // Current state of the object. | |
| 98 State state_; | |
| 99 | |
| 100 // Current error code. Valid when state_ == STATE_ERROR. | |
| 101 int error_; | |
| 102 | |
| 103 DISALLOW_COPY_AND_ASSIGN(IpcPacketSocket); | |
| 104 }; | |
| 105 | |
| 106 IpcPacketSocket::IpcPacketSocket() | |
| 107 : message_loop_(MessageLoop::current()), | |
| 108 address_initialized_(false), | |
| 109 state_(STATE_UNINITIALIZED), error_(0) { | |
| 110 } | |
| 111 | |
| 112 IpcPacketSocket::~IpcPacketSocket() { | |
| 113 if (state_ == STATE_OPENING || state_ == STATE_OPEN || | |
| 114 state_ == STATE_ERROR) { | |
| 115 Close(); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 bool IpcPacketSocket::Init(P2PSocketType type, P2PSocketClient* client, | |
| 120 const talk_base::SocketAddress& address) { | |
| 121 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 122 DCHECK_EQ(state_, STATE_UNINITIALIZED); | |
| 123 | |
| 124 client_ = client; | |
| 125 remote_address_ = address; | |
| 126 state_ = STATE_OPENING; | |
| 127 | |
| 128 net::IPEndPoint address_chrome; | |
| 129 if (!LibjingleToIPEndPoint(address, &address_chrome)) { | |
| 130 return false; | |
| 131 } | |
| 132 | |
| 133 client_->Init(type, address_chrome, this, | |
| 134 base::MessageLoopProxy::CreateForCurrentThread()); | |
| 135 | |
| 136 return true; | |
| 137 } | |
| 138 | |
| 139 // talk_base::AsyncPacketSocket interface. | |
| 140 talk_base::SocketAddress IpcPacketSocket::GetLocalAddress( | |
| 141 bool* allocated) const { | |
| 142 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 143 | |
| 144 *allocated = address_initialized_; | |
| 145 return local_address_; | |
| 146 } | |
| 147 | |
| 148 talk_base::SocketAddress IpcPacketSocket::GetRemoteAddress() const { | |
| 149 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 150 | |
| 151 return remote_address_; | |
| 152 } | |
| 153 | |
| 154 int IpcPacketSocket::Send(const void *data, size_t data_size) { | |
| 155 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 156 return SendTo(data, data_size, remote_address_); | |
| 157 } | |
| 158 | |
| 159 int IpcPacketSocket::SendTo(const void *data, size_t data_size, | |
| 160 const talk_base::SocketAddress& address) { | |
| 161 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 162 | |
| 163 switch (state_) { | |
| 164 case STATE_UNINITIALIZED: | |
| 165 NOTREACHED(); | |
| 166 return EWOULDBLOCK; | |
| 167 case STATE_OPENING: | |
| 168 return EWOULDBLOCK; | |
| 169 case STATE_CLOSED: | |
| 170 return ENOTCONN; | |
| 171 case STATE_ERROR: | |
| 172 return error_; | |
| 173 case STATE_OPEN: | |
| 174 // Continue sending the packet. | |
| 175 break; | |
| 176 } | |
| 177 | |
| 178 const char* data_char = reinterpret_cast<const char*>(data); | |
| 179 std::vector<char> data_vector(data_char, data_char + data_size); | |
| 180 | |
| 181 net::IPEndPoint address_chrome; | |
| 182 if (!LibjingleToIPEndPoint(address, &address_chrome)) { | |
| 183 // Just drop the packet if we failed to convert the address. | |
| 184 return 0; | |
| 185 } | |
| 186 | |
| 187 client_->Send(address_chrome, data_vector); | |
| 188 | |
| 189 // Fake successful send. The caller ignores result anyway. | |
| 190 return data_size; | |
| 191 } | |
| 192 | |
| 193 int IpcPacketSocket::Close() { | |
| 194 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 195 | |
| 196 client_->Close(); | |
| 197 state_ = STATE_CLOSED; | |
| 198 | |
| 199 return 0; | |
| 200 } | |
| 201 | |
| 202 talk_base::Socket::ConnState IpcPacketSocket::GetState() const { | |
| 203 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 204 | |
| 205 switch (state_) { | |
| 206 case STATE_UNINITIALIZED: | |
| 207 NOTREACHED(); | |
| 208 return talk_base::Socket::CS_CONNECTING; | |
| 209 | |
| 210 case STATE_OPENING: | |
| 211 return talk_base::Socket::CS_CONNECTING; | |
| 212 | |
| 213 case STATE_OPEN: | |
| 214 return talk_base::Socket::CS_CONNECTED; | |
| 215 | |
| 216 case STATE_CLOSED: | |
| 217 case STATE_ERROR: | |
| 218 return talk_base::Socket::CS_CLOSED; | |
| 219 } | |
| 220 | |
| 221 NOTREACHED(); | |
| 222 return talk_base::Socket::CS_CLOSED; | |
| 223 } | |
| 224 | |
| 225 int IpcPacketSocket::GetOption(talk_base::Socket::Option opt, int* value) { | |
| 226 // We don't support socket options for IPC sockets. | |
| 227 return -1; | |
| 228 } | |
| 229 | |
| 230 int IpcPacketSocket::SetOption(talk_base::Socket::Option opt, int value) { | |
| 231 // We don't support socket options for IPC sockets. | |
| 232 // | |
| 233 // TODO(sergeyu): Make sure we set proper socket options on the | |
| 234 // browser side. | |
| 235 return -1; | |
| 236 } | |
| 237 | |
| 238 int IpcPacketSocket::GetError() const { | |
| 239 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 240 return error_; | |
| 241 } | |
| 242 | |
| 243 void IpcPacketSocket::SetError(int error) { | |
| 244 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 245 error_ = error; | |
| 246 } | |
| 247 | |
| 248 void IpcPacketSocket::OnOpen(const net::IPEndPoint& address) { | |
| 249 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 250 | |
| 251 if (!ChromeToLibjingleSocketAddress(address, &local_address_)) { | |
| 252 // Always expect correct IPv4 address to be allocated. | |
| 253 NOTREACHED(); | |
| 254 } | |
| 255 SignalAddressReady(this, local_address_); | |
| 256 address_initialized_ = true; | |
| 257 state_ = STATE_OPEN; | |
| 258 } | |
| 259 | |
| 260 void IpcPacketSocket::OnError() { | |
| 261 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 262 state_ = STATE_ERROR; | |
| 263 error_ = ECONNABORTED; | |
| 264 } | |
| 265 | |
| 266 void IpcPacketSocket::OnDataReceived(const net::IPEndPoint& address, | |
| 267 const std::vector<char>& data) { | |
| 268 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 269 | |
| 270 talk_base::SocketAddress address_lj; | |
| 271 if (!ChromeToLibjingleSocketAddress(address, &address_lj)) { | |
| 272 // We should always be able to convert address here because we | |
| 273 // don't expect IPv6 address on IPv4 connections. | |
| 274 NOTREACHED(); | |
| 275 return; | |
| 276 } | |
| 277 | |
| 278 SignalReadPacket(this, &data[0], data.size(), address_lj); | |
| 279 } | |
| 280 | |
| 281 } // namespace | |
| 282 | |
| 283 IpcPacketSocketFactory::IpcPacketSocketFactory( | |
| 284 P2PSocketDispatcher* socket_dispatcher) | |
| 285 : socket_dispatcher_(socket_dispatcher) { | |
| 286 } | |
| 287 | |
| 288 IpcPacketSocketFactory::~IpcPacketSocketFactory() { | |
| 289 } | |
| 290 | |
| 291 talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateUdpSocket( | |
| 292 const talk_base::SocketAddress& local_address, int min_port, int max_port) { | |
| 293 talk_base::SocketAddress crome_address; | |
| 294 P2PSocketClient* socket_client = new P2PSocketClient(socket_dispatcher_); | |
| 295 scoped_ptr<IpcPacketSocket> socket(new IpcPacketSocket()); | |
| 296 // TODO(sergeyu): Respect local_address and port limits here (need | |
| 297 // to pass them over IPC channel to the browser). | |
| 298 if (!socket->Init(P2P_SOCKET_UDP, socket_client, | |
| 299 talk_base::SocketAddress())) { | |
| 300 return NULL; | |
| 301 } | |
| 302 | |
| 303 // Socket increments reference count if Init() was successful. | |
| 304 return socket.release(); | |
| 305 } | |
| 306 | |
| 307 talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateServerTcpSocket( | |
| 308 const talk_base::SocketAddress& local_address, int min_port, int max_port, | |
| 309 bool listen, bool ssl) { | |
| 310 // TODO(sergeyu): Implement this; | |
| 311 NOTIMPLEMENTED(); | |
| 312 return NULL; | |
| 313 } | |
| 314 | |
| 315 talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateClientTcpSocket( | |
| 316 const talk_base::SocketAddress& local_address, | |
| 317 const talk_base::SocketAddress& remote_address, | |
| 318 const talk_base::ProxyInfo& proxy_info, | |
| 319 const std::string& user_agent, bool ssl) { | |
| 320 // TODO(sergeyu): Implement this; | |
| 321 NOTIMPLEMENTED(); | |
| 322 return NULL; | |
| 323 } | |
| OLD | NEW |