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