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