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