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 |