1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 Modified version of nr_socket_local, adapted for NSPR
8 */
9
10 /* This Source Code Form is subject to the terms of the Mozilla Public
11 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
12 * You can obtain one at http://mozilla.org/MPL/2.0/. */
13
14 /*
15 Original code from nICEr and nrappkit.
16
17 nICEr copyright:
18
19 Copyright (c) 2007, Adobe Systems, Incorporated
20 All rights reserved.
21
22 Redistribution and use in source and binary forms, with or without
23 modification, are permitted provided that the following conditions are
24 met:
25
26 * Redistributions of source code must retain the above copyright
27 notice, this list of conditions and the following disclaimer.
28
29 * Redistributions in binary form must reproduce the above copyright
30 notice, this list of conditions and the following disclaimer in the
31 documentation and/or other materials provided with the distribution.
32
33 * Neither the name of Adobe Systems, Network Resonance nor the names of its
34 contributors may be used to endorse or promote products derived from
35 this software without specific prior written permission.
36
37 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
38 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
40 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
41 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
49
50 nrappkit copyright:
51
52 Copyright (C) 2001-2003, Network Resonance, Inc.
53 Copyright (C) 2006, Network Resonance, Inc.
54 All Rights Reserved
55
56 Redistribution and use in source and binary forms, with or without
57 modification, are permitted provided that the following conditions
58 are met:
59
60 1. Redistributions of source code must retain the above copyright
61 notice, this list of conditions and the following disclaimer.
62 2. Redistributions in binary form must reproduce the above copyright
63 notice, this list of conditions and the following disclaimer in the
64 documentation and/or other materials provided with the distribution.
65 3. Neither the name of Network Resonance, Inc. nor the name of any
66 contributors to this software may be used to endorse or promote
67 products derived from this software without specific prior written
68 permission.
69
70 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
71 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
74 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
75 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
76 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
77 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
78 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
79 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
80 POSSIBILITY OF SUCH DAMAGE.
81
82
83 ekr@rtfm.com Thu Dec 20 20:14:49 2001
84 */
85
86 #include <csi_platform.h>
87 #include <stdio.h>
88 #include <string.h>
89 #include <sys/types.h>
90 #include <assert.h>
91 #include <errno.h>
92 #include <string>
93
94 #include "nspr.h"
95 #include "prerror.h"
96 #include "prio.h"
97 #include "prnetdb.h"
98
99 #include "mozilla/net/DNS.h"
100 #include "nsCOMPtr.h"
101 #include "nsASocketHandler.h"
102 #include "nsISocketTransportService.h"
103 #include "nsNetCID.h"
104 #include "nsISupportsImpl.h"
105 #include "nsServiceManagerUtils.h"
106 #include "nsComponentManagerUtils.h"
107 #include "nsXPCOM.h"
108 #include "nsXULAppAPI.h"
109 #include "runnable_utils.h"
110 #include "mozilla/SyncRunnable.h"
111 #include "nsTArray.h"
112 #include "mozilla/dom/TCPSocketBinding.h"
113 #include "mozilla/SystemGroup.h"
114 #include "nsITCPSocketCallback.h"
115 #include "nsIPrefService.h"
116 #include "nsIPrefBranch.h"
117 #include "nsISocketFilter.h"
118 #include "nsDebug.h"
119
120 #ifdef XP_WIN
121 #include "mozilla/WindowsVersion.h"
122 #endif
123
124 #if defined(MOZILLA_INTERNAL_API)
125 // csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
126 #ifdef LOG_INFO
127 #define LOG_TEMP_INFO LOG_INFO
128 #undef LOG_INFO
129 #endif
130 #ifdef LOG_WARNING
131 #define LOG_TEMP_WARNING LOG_WARNING
132 #undef LOG_WARNING
133 #endif
134 #if defined(LOG_DEBUG)
135 #define LOG_TEMP_DEBUG LOG_DEBUG
136 #undef LOG_DEBUG
137 #endif
138 #undef strlcpy
139
140 #include "mozilla/dom/network/TCPSocketChild.h"
141
142 #ifdef LOG_TEMP_INFO
143 #define LOG_INFO LOG_TEMP_INFO
144 #endif
145 #ifdef LOG_TEMP_WARNING
146 #define LOG_WARNING LOG_TEMP_WARNING
147 #endif
148
149 #ifdef LOG_TEMP_DEBUG
150 #define LOG_DEBUG LOG_TEMP_DEBUG
151 #endif
152 #ifdef XP_WIN
153 #ifdef LOG_DEBUG
154 #undef LOG_DEBUG
155 #endif
156 // cloned from csi_platform.h. Win32 doesn't like how we hide symbols
157 #define LOG_DEBUG 7
158 #endif
159 #endif
160
161 extern "C" {
162 #include "nr_api.h"
163 #include "async_wait.h"
164 #include "nr_socket.h"
165 #include "nr_socket_local.h"
166 #include "stun_hint.h"
167 }
168 #include "nr_socket_prsock.h"
169 #include "simpletokenbucket.h"
170 #include "test_nr_socket.h"
171
172 // Implement the nsISupports ref counting
173 namespace mozilla {
174
175 #if defined(MOZILLA_INTERNAL_API)
176 class SingletonThreadHolder final {
177 private:
~SingletonThreadHolder()178 ~SingletonThreadHolder() {
179 r_log(LOG_GENERIC, LOG_DEBUG, "Deleting SingletonThreadHolder");
180 if (mThread) {
181 // Likely a connection is somehow being held in CC or GC
182 NS_WARNING(
183 "SingletonThreads should be Released and shut down before exit!");
184 mThread->Shutdown();
185 mThread = nullptr;
186 }
187 }
188
189 DISALLOW_COPY_ASSIGN(SingletonThreadHolder);
190
191 public:
192 // Must be threadsafe for StaticRefPtr/ClearOnShutdown
193 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder)
194
SingletonThreadHolder(const nsACString & aName)195 explicit SingletonThreadHolder(const nsACString &aName) : mName(aName) {
196 mParentThread = NS_GetCurrentThread();
197 }
198
GetThread()199 nsIThread *GetThread() { return mThread; }
200
201 /*
202 * Keep track of how many instances are using a SingletonThreadHolder.
203 * When no one is using it, shut it down
204 */
AddUse()205 void AddUse() {
206 MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
207 MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
208 nsrefcnt count = ++mUseCount;
209 if (count == 1) {
210 // idle -> in-use
211 nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
212 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread,
213 "Should successfully create mtransport I/O thread");
214 r_log(LOG_GENERIC, LOG_DEBUG, "Created wrapped SingletonThread %p",
215 mThread.get());
216 }
217 r_log(LOG_GENERIC, LOG_DEBUG, "AddUse_i: %lu", (unsigned long)count);
218 }
219
ReleaseUse()220 void ReleaseUse() {
221 MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
222 nsrefcnt count = --mUseCount;
223 MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
224 if (mThread && count == 0) {
225 // in-use -> idle -- no one forcing it to remain instantiated
226 r_log(LOG_GENERIC, LOG_DEBUG, "Shutting down wrapped SingletonThread %p",
227 mThread.get());
228 mThread->AsyncShutdown();
229 mThread = nullptr;
230 // It'd be nice to use a timer instead... But be careful of
231 // xpcom-shutdown-threads in that case
232 }
233 r_log(LOG_GENERIC, LOG_DEBUG, "ReleaseUse_i: %lu", (unsigned long)count);
234 }
235
236 private:
237 nsCString mName;
238 nsAutoRefCnt mUseCount;
239 nsCOMPtr<nsIThread> mParentThread;
240 nsCOMPtr<nsIThread> mThread;
241 };
242
243 static StaticRefPtr<SingletonThreadHolder> sThread;
244
ClearSingletonOnShutdown()245 static void ClearSingletonOnShutdown() {
246 ClearOnShutdown(&sThread, ShutdownPhase::ShutdownLoaders);
247 }
248 #endif
249
GetIOThreadAndAddUse_s()250 static nsIThread *GetIOThreadAndAddUse_s() {
251 // Always runs on STS thread!
252 #if defined(MOZILLA_INTERNAL_API)
253 // We need to safely release this on shutdown to avoid leaks
254 if (!sThread) {
255 sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
256 NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
257 }
258 // Mark that we're using the shared thread and need it to stick around
259 sThread->AddUse();
260 return sThread->GetThread();
261 #else
262 static nsCOMPtr<nsIThread> sThread;
263 if (!sThread) {
264 (void)NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
265 }
266 return sThread;
267 #endif
268 }
269
NrSocketIpc(nsIEventTarget * aThread)270 NrSocketIpc::NrSocketIpc(nsIEventTarget *aThread) : io_thread_(aThread) {}
271
272 static TimeStamp nr_socket_short_term_violation_time;
273 static TimeStamp nr_socket_long_term_violation_time;
274
short_term_violation_time()275 TimeStamp NrSocketBase::short_term_violation_time() {
276 return nr_socket_short_term_violation_time;
277 }
278
long_term_violation_time()279 TimeStamp NrSocketBase::long_term_violation_time() {
280 return nr_socket_long_term_violation_time;
281 }
282
283 // NrSocketBase implementation
284 // async_event APIs
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)285 int NrSocketBase::async_wait(int how, NR_async_cb cb, void *cb_arg,
286 char *function, int line) {
287 uint16_t flag;
288
289 switch (how) {
290 case NR_ASYNC_WAIT_READ:
291 flag = PR_POLL_READ;
292 break;
293 case NR_ASYNC_WAIT_WRITE:
294 flag = PR_POLL_WRITE;
295 break;
296 default:
297 return R_BAD_ARGS;
298 }
299
300 cbs_[how] = cb;
301 cb_args_[how] = cb_arg;
302 poll_flags_ |= flag;
303
304 return 0;
305 }
306
cancel(int how)307 int NrSocketBase::cancel(int how) {
308 uint16_t flag;
309
310 switch (how) {
311 case NR_ASYNC_WAIT_READ:
312 flag = PR_POLL_READ;
313 break;
314 case NR_ASYNC_WAIT_WRITE:
315 flag = PR_POLL_WRITE;
316 break;
317 default:
318 return R_BAD_ARGS;
319 }
320
321 poll_flags_ &= ~flag;
322
323 return 0;
324 }
325
fire_callback(int how)326 void NrSocketBase::fire_callback(int how) {
327 // This can't happen unless we are armed because we only set
328 // the flags if we are armed
329 MOZ_ASSERT(cbs_[how]);
330
331 // Now cancel so that we need to be re-armed. Note that
332 // the re-arming probably happens in the callback we are
333 // about to fire.
334 cancel(how);
335
336 cbs_[how](this, how, cb_args_[how]);
337 }
338
339 // NrSocket implementation
NS_IMPL_ISUPPORTS0(NrSocket)340 NS_IMPL_ISUPPORTS0(NrSocket)
341
342 // The nsASocket callbacks
343 void NrSocket::OnSocketReady(PRFileDesc *fd, int16_t outflags) {
344 if (outflags & PR_POLL_READ & poll_flags()) fire_callback(NR_ASYNC_WAIT_READ);
345 if (outflags & PR_POLL_WRITE & poll_flags())
346 fire_callback(NR_ASYNC_WAIT_WRITE);
347 if (outflags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP))
348 // TODO: Bug 946423: how do we notify the upper layers about this?
349 close();
350 }
351
OnSocketDetached(PRFileDesc * fd)352 void NrSocket::OnSocketDetached(PRFileDesc *fd) {
353 r_log(LOG_GENERIC, LOG_DEBUG, "Socket %p detached", fd);
354 }
355
IsLocal(bool * aIsLocal)356 void NrSocket::IsLocal(bool *aIsLocal) {
357 // TODO(jesup): better check? Does it matter? (likely no)
358 *aIsLocal = false;
359 }
360
361 // async_event APIs
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)362 int NrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg, char *function,
363 int line) {
364 int r = NrSocketBase::async_wait(how, cb, cb_arg, function, line);
365
366 if (!r) {
367 mPollFlags = poll_flags();
368 }
369
370 return r;
371 }
372
cancel(int how)373 int NrSocket::cancel(int how) {
374 int r = NrSocketBase::cancel(how);
375
376 if (!r) {
377 mPollFlags = poll_flags();
378 }
379
380 return r;
381 }
382
383 // Helper functions for addresses
nr_transport_addr_to_praddr(nr_transport_addr * addr,PRNetAddr * naddr)384 static int nr_transport_addr_to_praddr(nr_transport_addr *addr,
385 PRNetAddr *naddr) {
386 int _status;
387
388 memset(naddr, 0, sizeof(*naddr));
389
390 switch (addr->protocol) {
391 case IPPROTO_TCP:
392 break;
393 case IPPROTO_UDP:
394 break;
395 default:
396 ABORT(R_BAD_ARGS);
397 }
398
399 switch (addr->ip_version) {
400 case NR_IPV4:
401 naddr->inet.family = PR_AF_INET;
402 naddr->inet.port = addr->u.addr4.sin_port;
403 naddr->inet.ip = addr->u.addr4.sin_addr.s_addr;
404 break;
405 case NR_IPV6:
406 naddr->ipv6.family = PR_AF_INET6;
407 naddr->ipv6.port = addr->u.addr6.sin6_port;
408 naddr->ipv6.flowinfo = addr->u.addr6.sin6_flowinfo;
409 memcpy(&naddr->ipv6.ip, &addr->u.addr6.sin6_addr, sizeof(in6_addr));
410 naddr->ipv6.scope_id = addr->u.addr6.sin6_scope_id;
411 break;
412 default:
413 ABORT(R_BAD_ARGS);
414 }
415
416 _status = 0;
417 abort:
418 return (_status);
419 }
420
421 // XXX schien@mozilla.com: copy from PRNetAddrToNetAddr,
422 // should be removed after fix the link error in signaling_unittests
praddr_to_netaddr(const PRNetAddr * prAddr,net::NetAddr * addr)423 static int praddr_to_netaddr(const PRNetAddr *prAddr, net::NetAddr *addr) {
424 int _status;
425
426 switch (prAddr->raw.family) {
427 case PR_AF_INET:
428 addr->inet.family = AF_INET;
429 addr->inet.port = prAddr->inet.port;
430 addr->inet.ip = prAddr->inet.ip;
431 break;
432 case PR_AF_INET6:
433 addr->inet6.family = AF_INET6;
434 addr->inet6.port = prAddr->ipv6.port;
435 addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
436 memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
437 addr->inet6.scope_id = prAddr->ipv6.scope_id;
438 break;
439 default:
440 MOZ_ASSERT(false);
441 ABORT(R_BAD_ARGS);
442 }
443
444 _status = 0;
445 abort:
446 return (_status);
447 }
448
nr_transport_addr_to_netaddr(nr_transport_addr * addr,net::NetAddr * naddr)449 static int nr_transport_addr_to_netaddr(nr_transport_addr *addr,
450 net::NetAddr *naddr) {
451 int r, _status;
452 PRNetAddr praddr;
453
454 if ((r = nr_transport_addr_to_praddr(addr, &praddr))) {
455 ABORT(r);
456 }
457
458 if ((r = praddr_to_netaddr(&praddr, naddr))) {
459 ABORT(r);
460 }
461
462 _status = 0;
463 abort:
464 return (_status);
465 }
466
nr_netaddr_to_transport_addr(const net::NetAddr * netaddr,nr_transport_addr * addr,int protocol)467 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
468 nr_transport_addr *addr, int protocol) {
469 int _status;
470 int r;
471
472 switch (netaddr->raw.family) {
473 case AF_INET:
474 if ((r = nr_ip4_port_to_transport_addr(ntohl(netaddr->inet.ip),
475 ntohs(netaddr->inet.port),
476 protocol, addr)))
477 ABORT(r);
478 break;
479 case AF_INET6:
480 if ((r = nr_ip6_port_to_transport_addr((in6_addr *)&netaddr->inet6.ip.u8,
481 ntohs(netaddr->inet6.port),
482 protocol, addr)))
483 ABORT(r);
484 break;
485 default:
486 MOZ_ASSERT(false);
487 ABORT(R_BAD_ARGS);
488 }
489 _status = 0;
490 abort:
491 return (_status);
492 }
493
nr_praddr_to_transport_addr(const PRNetAddr * praddr,nr_transport_addr * addr,int protocol,int keep)494 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
495 nr_transport_addr *addr, int protocol,
496 int keep) {
497 int _status;
498 int r;
499 struct sockaddr_in ip4;
500 struct sockaddr_in6 ip6;
501
502 switch (praddr->raw.family) {
503 case PR_AF_INET:
504 ip4.sin_family = PF_INET;
505 ip4.sin_addr.s_addr = praddr->inet.ip;
506 ip4.sin_port = praddr->inet.port;
507 if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip4, protocol, keep,
508 addr)))
509 ABORT(r);
510 break;
511 case PR_AF_INET6:
512 ip6.sin6_family = PF_INET6;
513 ip6.sin6_port = praddr->ipv6.port;
514 ip6.sin6_flowinfo = praddr->ipv6.flowinfo;
515 memcpy(&ip6.sin6_addr, &praddr->ipv6.ip, sizeof(in6_addr));
516 ip6.sin6_scope_id = praddr->ipv6.scope_id;
517 if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip6, protocol, keep,
518 addr)))
519 ABORT(r);
520 break;
521 default:
522 MOZ_ASSERT(false);
523 ABORT(R_BAD_ARGS);
524 }
525
526 _status = 0;
527 abort:
528 return (_status);
529 }
530
531 /*
532 * nr_transport_addr_get_addrstring_and_port
533 * convert nr_transport_addr to IP address string and port number
534 */
nr_transport_addr_get_addrstring_and_port(nr_transport_addr * addr,nsACString * host,int32_t * port)535 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
536 nsACString *host, int32_t *port) {
537 int r, _status;
538 char addr_string[64];
539
540 // We cannot directly use |nr_transport_addr.as_string| because it contains
541 // more than ip address, therefore, we need to explicity convert it
542 // from |nr_transport_addr_get_addrstring|.
543 if ((r = nr_transport_addr_get_addrstring(addr, addr_string,
544 sizeof(addr_string)))) {
545 ABORT(r);
546 }
547
548 if ((r = nr_transport_addr_get_port(addr, port))) {
549 ABORT(r);
550 }
551
552 *host = addr_string;
553
554 _status = 0;
555 abort:
556 return (_status);
557 }
558
559 // nr_socket APIs (as member functions)
create(nr_transport_addr * addr)560 int NrSocket::create(nr_transport_addr *addr) {
561 int r, _status;
562
563 PRStatus status;
564 PRNetAddr naddr;
565
566 nsresult rv;
567 nsCOMPtr<nsISocketTransportService> stservice =
568 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
569
570 if (!NS_SUCCEEDED(rv)) {
571 ABORT(R_INTERNAL);
572 }
573
574 if ((r = nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r);
575
576 switch (addr->protocol) {
577 case IPPROTO_UDP:
578 if (!(fd_ = PR_OpenUDPSocket(naddr.raw.family))) {
579 r_log(LOG_GENERIC, LOG_CRIT,
580 "Couldn't create UDP socket, "
581 "family=%d, err=%d",
582 naddr.raw.family, PR_GetError());
583 ABORT(R_INTERNAL);
584 }
585 #ifdef XP_WIN
586 if (!mozilla::IsWin8OrLater()) {
587 // Increase default send and receive buffer sizes on <= Win7 to be able
588 // to receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K
589 // size) stream without losing packets. Manual testing showed that 100K
590 // buffer size was not enough and the packet loss dis-appeared with 256K
591 // buffer size. See bug 1252769 for future improvements of this.
592 PRSize min_buffer_size = 256 * 1024;
593 PRSocketOptionData opt_rcvbuf;
594 opt_rcvbuf.option = PR_SockOpt_RecvBufferSize;
595 if ((status = PR_GetSocketOption(fd_, &opt_rcvbuf)) == PR_SUCCESS) {
596 if (opt_rcvbuf.value.recv_buffer_size < min_buffer_size) {
597 opt_rcvbuf.value.recv_buffer_size = min_buffer_size;
598 if ((status = PR_SetSocketOption(fd_, &opt_rcvbuf)) != PR_SUCCESS) {
599 r_log(LOG_GENERIC, LOG_CRIT,
600 "Couldn't set socket receive buffer size: %d", status);
601 }
602 } else {
603 r_log(LOG_GENERIC, LOG_INFO,
604 "Socket receive buffer size is already: %d",
605 opt_rcvbuf.value.recv_buffer_size);
606 }
607 } else {
608 r_log(LOG_GENERIC, LOG_CRIT,
609 "Couldn't get socket receive buffer size: %d", status);
610 }
611 PRSocketOptionData opt_sndbuf;
612 opt_sndbuf.option = PR_SockOpt_SendBufferSize;
613 if ((status = PR_GetSocketOption(fd_, &opt_sndbuf)) == PR_SUCCESS) {
614 if (opt_sndbuf.value.recv_buffer_size < min_buffer_size) {
615 opt_sndbuf.value.recv_buffer_size = min_buffer_size;
616 if ((status = PR_SetSocketOption(fd_, &opt_sndbuf)) != PR_SUCCESS) {
617 r_log(LOG_GENERIC, LOG_CRIT,
618 "Couldn't set socket send buffer size: %d", status);
619 }
620 } else {
621 r_log(LOG_GENERIC, LOG_INFO,
622 "Socket send buffer size is already: %d",
623 opt_sndbuf.value.recv_buffer_size);
624 }
625 } else {
626 r_log(LOG_GENERIC, LOG_CRIT,
627 "Couldn't get socket send buffer size: %d", status);
628 }
629 }
630 #endif
631 break;
632 case IPPROTO_TCP:
633 // TODO: Add TLS layer with nsISocketProviderService?
634 if (my_addr_.tls_host[0] != '\0') ABORT(R_INTERNAL);
635
636 if (!(fd_ = PR_OpenTCPSocket(naddr.raw.family))) {
637 r_log(LOG_GENERIC, LOG_CRIT,
638 "Couldn't create TCP socket, "
639 "family=%d, err=%d",
640 naddr.raw.family, PR_GetError());
641 ABORT(R_INTERNAL);
642 }
643 // Set ReuseAddr for TCP sockets to enable having several
644 // sockets bound to same local IP and port
645 PRSocketOptionData opt_reuseaddr;
646 opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
647 opt_reuseaddr.value.reuse_addr = PR_TRUE;
648 status = PR_SetSocketOption(fd_, &opt_reuseaddr);
649 if (status != PR_SUCCESS) {
650 r_log(LOG_GENERIC, LOG_CRIT,
651 "Couldn't set reuse addr socket option: %d", status);
652 ABORT(R_INTERNAL);
653 }
654 // And also set ReusePort for platforms supporting this socket option
655 PRSocketOptionData opt_reuseport;
656 opt_reuseport.option = PR_SockOpt_Reuseport;
657 opt_reuseport.value.reuse_port = PR_TRUE;
658 status = PR_SetSocketOption(fd_, &opt_reuseport);
659 if (status != PR_SUCCESS) {
660 if (PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
661 r_log(LOG_GENERIC, LOG_CRIT,
662 "Couldn't set reuse port socket option: %d", status);
663 ABORT(R_INTERNAL);
664 }
665 }
666 // Try to speedup packet delivery by disabling TCP Nagle
667 PRSocketOptionData opt_nodelay;
668 opt_nodelay.option = PR_SockOpt_NoDelay;
669 opt_nodelay.value.no_delay = PR_TRUE;
670 status = PR_SetSocketOption(fd_, &opt_nodelay);
671 if (status != PR_SUCCESS) {
672 r_log(LOG_GENERIC, LOG_WARNING,
673 "Couldn't set Nodelay socket option: %d", status);
674 }
675 break;
676 default:
677 ABORT(R_INTERNAL);
678 }
679
680 status = PR_Bind(fd_, &naddr);
681 if (status != PR_SUCCESS) {
682 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't bind socket to address %s",
683 addr->as_string);
684 ABORT(R_INTERNAL);
685 }
686
687 r_log(LOG_GENERIC, LOG_DEBUG, "Creating socket %p with addr %s", fd_,
688 addr->as_string);
689 nr_transport_addr_copy(&my_addr_, addr);
690
691 /* If we have a wildcard port, patch up the addr */
692 if (nr_transport_addr_is_wildcard(addr)) {
693 status = PR_GetSockName(fd_, &naddr);
694 if (status != PR_SUCCESS) {
695 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
696 ABORT(R_INTERNAL);
697 }
698
699 if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
700 ABORT(r);
701 }
702
703 // Set nonblocking
704 PRSocketOptionData opt_nonblock;
705 opt_nonblock.option = PR_SockOpt_Nonblocking;
706 opt_nonblock.value.non_blocking = PR_TRUE;
707 status = PR_SetSocketOption(fd_, &opt_nonblock);
708 if (status != PR_SUCCESS) {
709 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking");
710 ABORT(R_INTERNAL);
711 }
712
713 // Remember our thread.
714 ststhread_ = do_QueryInterface(stservice, &rv);
715 if (!NS_SUCCEEDED(rv)) ABORT(R_INTERNAL);
716
717 // Finally, register with the STS
718 rv = stservice->AttachSocket(fd_, this);
719 if (!NS_SUCCEEDED(rv)) {
720 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't attach socket to STS, rv=%u",
721 static_cast<unsigned>(rv));
722 ABORT(R_INTERNAL);
723 }
724
725 _status = 0;
726
727 abort:
728 return (_status);
729 }
730
ShouldDrop(size_t len)731 static int ShouldDrop(size_t len) {
732 // Global rate limiting for stun requests, to mitigate the ice hammer DoS
733 // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc)
734
735 // Tolerate rate of 8k/sec, for one second.
736 static SimpleTokenBucket burst(16384 * 1, 16384);
737 // Tolerate rate of 7.2k/sec over twenty seconds.
738 static SimpleTokenBucket sustained(7372 * 20, 7372);
739
740 // Check number of tokens in each bucket.
741 if (burst.getTokens(UINT32_MAX) < len) {
742 r_log(LOG_GENERIC, LOG_ERR,
743 "Short term global rate limit for STUN requests exceeded.");
744 #ifdef MOZILLA_INTERNAL_API
745 nr_socket_short_term_violation_time = TimeStamp::Now();
746 #endif
747
748 // Bug 1013007
749 #if !EARLY_BETA_OR_EARLIER
750 return R_WOULDBLOCK;
751 #else
752 MOZ_ASSERT(false,
753 "Short term global rate limit for STUN requests exceeded. Go "
754 "bug bcampen@mozilla.com if you weren't intentionally "
755 "spamming ICE candidates, or don't know what that means.");
756 #endif
757 }
758
759 if (sustained.getTokens(UINT32_MAX) < len) {
760 r_log(LOG_GENERIC, LOG_ERR,
761 "Long term global rate limit for STUN requests exceeded.");
762 #ifdef MOZILLA_INTERNAL_API
763 nr_socket_long_term_violation_time = TimeStamp::Now();
764 #endif
765 // Bug 1013007
766 #if !EARLY_BETA_OR_EARLIER
767 return R_WOULDBLOCK;
768 #else
769 MOZ_ASSERT(false,
770 "Long term global rate limit for STUN requests exceeded. Go "
771 "bug bcampen@mozilla.com if you weren't intentionally "
772 "spamming ICE candidates, or don't know what that means.");
773 #endif
774 }
775
776 // Take len tokens from both buckets.
777 // (not threadsafe, but no problem since this is only called from STS)
778 burst.getTokens(len);
779 sustained.getTokens(len);
780 return 0;
781 }
782
783 // This should be called on the STS thread.
sendto(const void * msg,size_t len,int flags,nr_transport_addr * to)784 int NrSocket::sendto(const void *msg, size_t len, int flags,
785 nr_transport_addr *to) {
786 ASSERT_ON_THREAD(ststhread_);
787 int r, _status;
788 PRNetAddr naddr;
789 int32_t status;
790
791 if ((r = nr_transport_addr_to_praddr(to, &naddr))) ABORT(r);
792
793 if (fd_ == nullptr) ABORT(R_EOD);
794
795 if (nr_is_stun_request_message((UCHAR *)msg, len) && ShouldDrop(len)) {
796 ABORT(R_WOULDBLOCK);
797 }
798
799 // TODO: Convert flags?
800 status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT);
801 if (status < 0 || (size_t)status != len) {
802 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
803
804 r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s: %d", to->as_string,
805 PR_GetError());
806 ABORT(R_IO_ERROR);
807 }
808
809 _status = 0;
810 abort:
811 return (_status);
812 }
813
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)814 int NrSocket::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
815 nr_transport_addr *from) {
816 ASSERT_ON_THREAD(ststhread_);
817 int r, _status;
818 PRNetAddr nfrom;
819 int32_t status;
820
821 status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
822 if (status <= 0) {
823 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
824 r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError());
825 ABORT(R_IO_ERROR);
826 }
827 *len = status;
828
829 if ((r = nr_praddr_to_transport_addr(&nfrom, from, my_addr_.protocol, 0)))
830 ABORT(r);
831
832 // r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
833
834 _status = 0;
835 abort:
836 return (_status);
837 }
838
getaddr(nr_transport_addr * addrp)839 int NrSocket::getaddr(nr_transport_addr *addrp) {
840 ASSERT_ON_THREAD(ststhread_);
841 return nr_transport_addr_copy(addrp, &my_addr_);
842 }
843
844 // Close the socket so that the STS will detach and then kill it
close()845 void NrSocket::close() {
846 ASSERT_ON_THREAD(ststhread_);
847 mCondition = NS_BASE_STREAM_CLOSED;
848 }
849
connect(nr_transport_addr * addr)850 int NrSocket::connect(nr_transport_addr *addr) {
851 ASSERT_ON_THREAD(ststhread_);
852 int r, _status;
853 PRNetAddr naddr;
854 int32_t connect_status, getsockname_status;
855
856 if ((r = nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r);
857
858 if (!fd_) ABORT(R_EOD);
859
860 // Note: this just means we tried to connect, not that we
861 // are actually live.
862 connect_invoked_ = true;
863 connect_status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
864 if (connect_status != PR_SUCCESS) {
865 if (PR_GetError() != PR_IN_PROGRESS_ERROR) ABORT(R_IO_ERROR);
866 }
867
868 // If our local address is wildcard, then fill in the
869 // address now.
870 if (nr_transport_addr_is_wildcard(&my_addr_)) {
871 getsockname_status = PR_GetSockName(fd_, &naddr);
872 if (getsockname_status != PR_SUCCESS) {
873 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
874 ABORT(R_INTERNAL);
875 }
876
877 if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
878 ABORT(r);
879 }
880
881 // Now return the WOULDBLOCK if needed.
882 if (connect_status != PR_SUCCESS) {
883 ABORT(R_WOULDBLOCK);
884 }
885
886 _status = 0;
887 abort:
888 return (_status);
889 }
890
write(const void * msg,size_t len,size_t * written)891 int NrSocket::write(const void *msg, size_t len, size_t *written) {
892 ASSERT_ON_THREAD(ststhread_);
893 int _status;
894 int32_t status;
895
896 if (!connect_invoked_) ABORT(R_FAILED);
897
898 status = PR_Write(fd_, msg, len);
899 if (status < 0) {
900 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
901 r_log(LOG_GENERIC, LOG_INFO, "Error in write");
902 ABORT(R_IO_ERROR);
903 }
904
905 *written = status;
906
907 _status = 0;
908 abort:
909 return _status;
910 }
911
read(void * buf,size_t maxlen,size_t * len)912 int NrSocket::read(void *buf, size_t maxlen, size_t *len) {
913 ASSERT_ON_THREAD(ststhread_);
914 int _status;
915 int32_t status;
916
917 if (!connect_invoked_) ABORT(R_FAILED);
918
919 status = PR_Read(fd_, buf, maxlen);
920 if (status < 0) {
921 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
922 r_log(LOG_GENERIC, LOG_INFO, "Error in read");
923 ABORT(R_IO_ERROR);
924 }
925 if (status == 0) ABORT(R_EOD);
926
927 *len = (size_t)status; // Guaranteed to be > 0
928 _status = 0;
929 abort:
930 return (_status);
931 }
932
listen(int backlog)933 int NrSocket::listen(int backlog) {
934 ASSERT_ON_THREAD(ststhread_);
935 int32_t status;
936 int _status;
937
938 assert(fd_);
939 status = PR_Listen(fd_, backlog);
940 if (status != PR_SUCCESS) {
941 r_log(LOG_GENERIC, LOG_CRIT, "%s: PR_GetError() == %d", __FUNCTION__,
942 PR_GetError());
943 ABORT(R_IO_ERROR);
944 }
945
946 _status = 0;
947 abort:
948 return (_status);
949 }
950
accept(nr_transport_addr * addrp,nr_socket ** sockp)951 int NrSocket::accept(nr_transport_addr *addrp, nr_socket **sockp) {
952 ASSERT_ON_THREAD(ststhread_);
953 int _status, r;
954 PRStatus status;
955 PRFileDesc *prfd;
956 PRNetAddr nfrom;
957 NrSocket *sock = nullptr;
958 nsresult rv;
959 PRSocketOptionData opt_nonblock, opt_nodelay;
960 nsCOMPtr<nsISocketTransportService> stservice =
961 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
962
963 if (NS_FAILED(rv)) {
964 ABORT(R_INTERNAL);
965 }
966
967 if (!fd_) ABORT(R_EOD);
968
969 prfd = PR_Accept(fd_, &nfrom, PR_INTERVAL_NO_WAIT);
970
971 if (!prfd) {
972 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
973
974 ABORT(R_IO_ERROR);
975 }
976
977 sock = new NrSocket();
978
979 sock->fd_ = prfd;
980 nr_transport_addr_copy(&sock->my_addr_, &my_addr_);
981
982 if ((r = nr_praddr_to_transport_addr(&nfrom, addrp, my_addr_.protocol, 0)))
983 ABORT(r);
984
985 // Set nonblocking
986 opt_nonblock.option = PR_SockOpt_Nonblocking;
987 opt_nonblock.value.non_blocking = PR_TRUE;
988 status = PR_SetSocketOption(prfd, &opt_nonblock);
989 if (status != PR_SUCCESS) {
990 r_log(LOG_GENERIC, LOG_CRIT,
991 "Failed to make accepted socket nonblocking: %d", status);
992 ABORT(R_INTERNAL);
993 }
994 // Disable TCP Nagle
995 opt_nodelay.option = PR_SockOpt_NoDelay;
996 opt_nodelay.value.no_delay = PR_TRUE;
997 status = PR_SetSocketOption(prfd, &opt_nodelay);
998 if (status != PR_SUCCESS) {
999 r_log(LOG_GENERIC, LOG_WARNING,
1000 "Failed to set Nodelay on accepted socket: %d", status);
1001 }
1002
1003 // Should fail only with OOM
1004 if ((r = nr_socket_create_int(static_cast<void *>(sock), sock->vtbl(),
1005 sockp)))
1006 ABORT(r);
1007
1008 // Remember our thread.
1009 sock->ststhread_ = do_QueryInterface(stservice, &rv);
1010 if (NS_FAILED(rv)) ABORT(R_INTERNAL);
1011
1012 // Finally, register with the STS
1013 rv = stservice->AttachSocket(prfd, sock);
1014 if (NS_FAILED(rv)) {
1015 ABORT(R_INTERNAL);
1016 }
1017
1018 sock->connect_invoked_ = true;
1019
1020 // Add a reference so that we can delete it in destroy()
1021 sock->AddRef();
1022 _status = 0;
1023 abort:
1024 if (_status) {
1025 delete sock;
1026 }
1027
1028 return (_status);
1029 }
1030
NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy,nsIUDPSocketInternal)1031 NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal)
1032
1033 nsresult NrUdpSocketIpcProxy::Init(const RefPtr<NrUdpSocketIpc> &socket) {
1034 nsresult rv;
1035 sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1036 if (NS_FAILED(rv)) {
1037 MOZ_ASSERT(false, "Failed to get STS thread");
1038 return rv;
1039 }
1040
1041 socket_ = socket;
1042 return NS_OK;
1043 }
1044
~NrUdpSocketIpcProxy()1045 NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy() {
1046 // Send our ref to STS to be released
1047 RUN_ON_THREAD(sts_thread_, mozilla::WrapRelease(socket_.forget()),
1048 NS_DISPATCH_NORMAL);
1049 }
1050
1051 // IUDPSocketInternal interfaces
1052 // callback while error happened in UDP socket operation
CallListenerError(const nsACString & message,const nsACString & filename,uint32_t line_number)1053 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString &message,
1054 const nsACString &filename,
1055 uint32_t line_number) {
1056 return socket_->CallListenerError(message, filename, line_number);
1057 }
1058
1059 // callback while receiving UDP packet
CallListenerReceivedData(const nsACString & host,uint16_t port,const uint8_t * data,uint32_t data_length)1060 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData(
1061 const nsACString &host, uint16_t port, const uint8_t *data,
1062 uint32_t data_length) {
1063 return socket_->CallListenerReceivedData(host, port, data, data_length);
1064 }
1065
1066 // callback while UDP socket is opened
CallListenerOpened()1067 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
1068 return socket_->CallListenerOpened();
1069 }
1070
1071 // callback while UDP socket is connected
CallListenerConnected()1072 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerConnected() {
1073 return socket_->CallListenerConnected();
1074 }
1075
1076 // callback while UDP socket is closed
CallListenerClosed()1077 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
1078 return socket_->CallListenerClosed();
1079 }
1080
1081 // NrUdpSocketIpc Implementation
NrUdpSocketIpc()1082 NrUdpSocketIpc::NrUdpSocketIpc()
1083 : NrSocketIpc(GetIOThreadAndAddUse_s()),
1084 monitor_("NrUdpSocketIpc"),
1085 err_(false),
1086 state_(NR_INIT) {}
1087
~NrUdpSocketIpc()1088 NrUdpSocketIpc::~NrUdpSocketIpc() {
1089 #if defined(MOZILLA_INTERNAL_API)
1090 // close(), but transfer the socket_child_ reference to die as well
1091 // destroy_i also dispatches back to STS to call ReleaseUse, to avoid shutting
1092 // down the IO thread before close() runs.
1093 RUN_ON_THREAD(
1094 io_thread_,
1095 mozilla::WrapRunnableNM(&NrUdpSocketIpc::destroy_i,
1096 socket_child_.forget().take(), sts_thread_),
1097 NS_DISPATCH_NORMAL);
1098 #endif
1099 }
1100
1101 // IUDPSocketInternal interfaces
1102 // callback while error happened in UDP socket operation
CallListenerError(const nsACString & message,const nsACString & filename,uint32_t line_number)1103 NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString &message,
1104 const nsACString &filename,
1105 uint32_t line_number) {
1106 ASSERT_ON_THREAD(io_thread_);
1107
1108 r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d this=%p",
1109 message.BeginReading(), filename.BeginReading(), line_number,
1110 (void *)this);
1111
1112 ReentrantMonitorAutoEnter mon(monitor_);
1113 err_ = true;
1114 monitor_.NotifyAll();
1115
1116 return NS_OK;
1117 }
1118
1119 // callback while receiving UDP packet
CallListenerReceivedData(const nsACString & host,uint16_t port,const uint8_t * data,uint32_t data_length)1120 NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData(const nsACString &host,
1121 uint16_t port,
1122 const uint8_t *data,
1123 uint32_t data_length) {
1124 ASSERT_ON_THREAD(io_thread_);
1125
1126 PRNetAddr addr;
1127 memset(&addr, 0, sizeof(addr));
1128
1129 {
1130 ReentrantMonitorAutoEnter mon(monitor_);
1131
1132 if (PR_SUCCESS != PR_StringToNetAddr(host.BeginReading(), &addr)) {
1133 err_ = true;
1134 MOZ_ASSERT(false, "Failed to convert remote host to PRNetAddr");
1135 return NS_OK;
1136 }
1137
1138 // Use PR_IpAddrNull to avoid address being reset to 0.
1139 if (PR_SUCCESS !=
1140 PR_SetNetAddr(PR_IpAddrNull, addr.raw.family, port, &addr)) {
1141 err_ = true;
1142 MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
1143 return NS_OK;
1144 }
1145 }
1146
1147 nsAutoPtr<DataBuffer> buf(new DataBuffer(data, data_length));
1148 RefPtr<nr_udp_message> msg(new nr_udp_message(addr, buf));
1149
1150 RUN_ON_THREAD(sts_thread_,
1151 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1152 &NrUdpSocketIpc::recv_callback_s, msg),
1153 NS_DISPATCH_NORMAL);
1154 return NS_OK;
1155 }
1156
SetAddress()1157 nsresult NrUdpSocketIpc::SetAddress() {
1158 uint16_t port;
1159 if (NS_FAILED(socket_child_->GetLocalPort(&port))) {
1160 err_ = true;
1161 MOZ_ASSERT(false, "Failed to get local port");
1162 return NS_OK;
1163 }
1164
1165 nsAutoCString address;
1166 if (NS_FAILED(socket_child_->GetLocalAddress(address))) {
1167 err_ = true;
1168 MOZ_ASSERT(false, "Failed to get local address");
1169 return NS_OK;
1170 }
1171
1172 PRNetAddr praddr;
1173 if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, port, &praddr)) {
1174 err_ = true;
1175 MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
1176 return NS_OK;
1177 }
1178
1179 if (PR_SUCCESS != PR_StringToNetAddr(address.BeginReading(), &praddr)) {
1180 err_ = true;
1181 MOZ_ASSERT(false, "Failed to convert local host to PRNetAddr");
1182 return NS_OK;
1183 }
1184
1185 nr_transport_addr expected_addr;
1186 if (nr_transport_addr_copy(&expected_addr, &my_addr_)) {
1187 err_ = true;
1188 MOZ_ASSERT(false, "Failed to copy my_addr_");
1189 }
1190
1191 if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) {
1192 err_ = true;
1193 MOZ_ASSERT(false, "Failed to copy local host to my_addr_");
1194 }
1195
1196 if (!nr_transport_addr_is_wildcard(&expected_addr) &&
1197 nr_transport_addr_cmp(&expected_addr, &my_addr_,
1198 NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
1199 err_ = true;
1200 MOZ_ASSERT(false, "Address of opened socket is not expected");
1201 }
1202
1203 return NS_OK;
1204 }
1205
1206 // callback while UDP socket is opened
CallListenerOpened()1207 NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() {
1208 ASSERT_ON_THREAD(io_thread_);
1209 ReentrantMonitorAutoEnter mon(monitor_);
1210
1211 r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket opened this=%p", (void *)this);
1212 nsresult rv = SetAddress();
1213 if (NS_FAILED(rv)) {
1214 return rv;
1215 }
1216
1217 mon.NotifyAll();
1218
1219 return NS_OK;
1220 }
1221
1222 // callback while UDP socket is connected
CallListenerConnected()1223 NS_IMETHODIMP NrUdpSocketIpc::CallListenerConnected() {
1224 ASSERT_ON_THREAD(io_thread_);
1225
1226 ReentrantMonitorAutoEnter mon(monitor_);
1227
1228 r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket connected this=%p", (void *)this);
1229 MOZ_ASSERT(state_ == NR_CONNECTED);
1230
1231 nsresult rv = SetAddress();
1232 if (NS_FAILED(rv)) {
1233 mon.NotifyAll();
1234 return rv;
1235 }
1236
1237 r_log(LOG_GENERIC, LOG_INFO, "Exit UDP socket connected");
1238 mon.NotifyAll();
1239
1240 return NS_OK;
1241 }
1242
1243 // callback while UDP socket is closed
CallListenerClosed()1244 NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() {
1245 ASSERT_ON_THREAD(io_thread_);
1246
1247 ReentrantMonitorAutoEnter mon(monitor_);
1248
1249 r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket closed this=%p", (void *)this);
1250 MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
1251 state_ = NR_CLOSED;
1252
1253 return NS_OK;
1254 }
1255
1256 //
1257 // NrSocketBase methods.
1258 //
create(nr_transport_addr * addr)1259 int NrUdpSocketIpc::create(nr_transport_addr *addr) {
1260 ASSERT_ON_THREAD(sts_thread_);
1261
1262 int r, _status;
1263 nsresult rv;
1264 int32_t port;
1265 nsCString host;
1266
1267 ReentrantMonitorAutoEnter mon(monitor_);
1268
1269 if (state_ != NR_INIT) {
1270 ABORT(R_INTERNAL);
1271 }
1272
1273 sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1274 if (NS_FAILED(rv)) {
1275 MOZ_ASSERT(false, "Failed to get STS thread");
1276 ABORT(R_INTERNAL);
1277 }
1278
1279 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1280 ABORT(r);
1281 }
1282
1283 // wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid
1284 if ((r = nr_transport_addr_copy(&my_addr_, addr))) {
1285 ABORT(r);
1286 }
1287
1288 state_ = NR_CONNECTING;
1289
1290 MOZ_ASSERT(io_thread_);
1291 RUN_ON_THREAD(io_thread_,
1292 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1293 &NrUdpSocketIpc::create_i, host,
1294 static_cast<uint16_t>(port)),
1295 NS_DISPATCH_NORMAL);
1296
1297 // Wait until socket creation complete.
1298 mon.Wait();
1299
1300 if (err_) {
1301 close();
1302 ABORT(R_INTERNAL);
1303 }
1304
1305 state_ = NR_CONNECTED;
1306
1307 _status = 0;
1308 abort:
1309 return (_status);
1310 }
1311
sendto(const void * msg,size_t len,int flags,nr_transport_addr * to)1312 int NrUdpSocketIpc::sendto(const void *msg, size_t len, int flags,
1313 nr_transport_addr *to) {
1314 ASSERT_ON_THREAD(sts_thread_);
1315
1316 ReentrantMonitorAutoEnter mon(monitor_);
1317
1318 // If send err happened before, simply return the error.
1319 if (err_) {
1320 return R_IO_ERROR;
1321 }
1322
1323 if (state_ != NR_CONNECTED) {
1324 return R_INTERNAL;
1325 }
1326
1327 int r;
1328 net::NetAddr addr;
1329 if ((r = nr_transport_addr_to_netaddr(to, &addr))) {
1330 return r;
1331 }
1332
1333 if (nr_is_stun_request_message((UCHAR *)msg, len) && ShouldDrop(len)) {
1334 return R_WOULDBLOCK;
1335 }
1336
1337 nsAutoPtr<DataBuffer> buf(
1338 new DataBuffer(static_cast<const uint8_t *>(msg), len));
1339
1340 RUN_ON_THREAD(io_thread_,
1341 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1342 &NrUdpSocketIpc::sendto_i, addr, buf),
1343 NS_DISPATCH_NORMAL);
1344 return 0;
1345 }
1346
close()1347 void NrUdpSocketIpc::close() {
1348 r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::close()");
1349
1350 ASSERT_ON_THREAD(sts_thread_);
1351
1352 ReentrantMonitorAutoEnter mon(monitor_);
1353 state_ = NR_CLOSING;
1354
1355 RUN_ON_THREAD(io_thread_,
1356 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1357 &NrUdpSocketIpc::close_i),
1358 NS_DISPATCH_NORMAL);
1359
1360 // remove all enqueued messages
1361 std::queue<RefPtr<nr_udp_message>> empty;
1362 std::swap(received_msgs_, empty);
1363 }
1364
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)1365 int NrUdpSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
1366 nr_transport_addr *from) {
1367 ASSERT_ON_THREAD(sts_thread_);
1368
1369 ReentrantMonitorAutoEnter mon(monitor_);
1370
1371 int r, _status;
1372 uint32_t consumed_len;
1373
1374 *len = 0;
1375
1376 if (state_ != NR_CONNECTED) {
1377 ABORT(R_INTERNAL);
1378 }
1379
1380 if (received_msgs_.empty()) {
1381 ABORT(R_WOULDBLOCK);
1382 }
1383
1384 {
1385 RefPtr<nr_udp_message> msg(received_msgs_.front());
1386
1387 received_msgs_.pop();
1388
1389 if ((r = nr_praddr_to_transport_addr(&msg->from, from, IPPROTO_UDP, 0))) {
1390 err_ = true;
1391 MOZ_ASSERT(false, "Get bogus address for received UDP packet");
1392 ABORT(r);
1393 }
1394
1395 consumed_len = std::min(maxlen, msg->data->len());
1396 if (consumed_len < msg->data->len()) {
1397 r_log(LOG_GENERIC, LOG_DEBUG,
1398 "Partial received UDP packet will be discard");
1399 }
1400
1401 memcpy(buf, msg->data->data(), consumed_len);
1402 *len = consumed_len;
1403 }
1404
1405 _status = 0;
1406 abort:
1407 return (_status);
1408 }
1409
getaddr(nr_transport_addr * addrp)1410 int NrUdpSocketIpc::getaddr(nr_transport_addr *addrp) {
1411 ASSERT_ON_THREAD(sts_thread_);
1412
1413 ReentrantMonitorAutoEnter mon(monitor_);
1414
1415 if (state_ != NR_CONNECTED) {
1416 return R_INTERNAL;
1417 }
1418
1419 return nr_transport_addr_copy(addrp, &my_addr_);
1420 }
1421
connect(nr_transport_addr * addr)1422 int NrUdpSocketIpc::connect(nr_transport_addr *addr) {
1423 int r, _status;
1424 int32_t port;
1425 nsCString host;
1426
1427 ReentrantMonitorAutoEnter mon(monitor_);
1428 r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::connect(%s) this=%p",
1429 addr->as_string, (void *)this);
1430
1431 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1432 ABORT(r);
1433 }
1434
1435 RUN_ON_THREAD(io_thread_,
1436 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1437 &NrUdpSocketIpc::connect_i, host,
1438 static_cast<uint16_t>(port)),
1439 NS_DISPATCH_NORMAL);
1440
1441 // Wait until connect() completes.
1442 mon.Wait();
1443
1444 r_log(LOG_GENERIC, LOG_DEBUG,
1445 "NrUdpSocketIpc::connect this=%p completed err_ = %s", (void *)this,
1446 err_ ? "true" : "false");
1447
1448 if (err_) {
1449 ABORT(R_INTERNAL);
1450 }
1451
1452 _status = 0;
1453 abort:
1454 return _status;
1455 }
1456
write(const void * msg,size_t len,size_t * written)1457 int NrUdpSocketIpc::write(const void *msg, size_t len, size_t *written) {
1458 MOZ_ASSERT(false);
1459 return R_INTERNAL;
1460 }
1461
read(void * buf,size_t maxlen,size_t * len)1462 int NrUdpSocketIpc::read(void *buf, size_t maxlen, size_t *len) {
1463 MOZ_ASSERT(false);
1464 return R_INTERNAL;
1465 }
1466
listen(int backlog)1467 int NrUdpSocketIpc::listen(int backlog) {
1468 MOZ_ASSERT(false);
1469 return R_INTERNAL;
1470 }
1471
accept(nr_transport_addr * addrp,nr_socket ** sockp)1472 int NrUdpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
1473 MOZ_ASSERT(false);
1474 return R_INTERNAL;
1475 }
1476
1477 // IO thread executors
create_i(const nsACString & host,const uint16_t port)1478 void NrUdpSocketIpc::create_i(const nsACString &host, const uint16_t port) {
1479 ASSERT_ON_THREAD(io_thread_);
1480
1481 uint32_t minBuffSize = 0;
1482 nsresult rv;
1483 nsCOMPtr<nsIUDPSocketChild> socketChild =
1484 do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
1485 if (NS_FAILED(rv)) {
1486 ReentrantMonitorAutoEnter mon(monitor_);
1487 err_ = true;
1488 MOZ_ASSERT(false, "Failed to create UDPSocketChild");
1489 return;
1490 }
1491
1492 // This can spin the event loop; don't do that with the monitor held
1493 socketChild->SetBackgroundSpinsEvents();
1494
1495 ReentrantMonitorAutoEnter mon(monitor_);
1496 if (!socket_child_) {
1497 socket_child_ = socketChild;
1498 socket_child_->SetFilterName(
1499 nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX));
1500 } else {
1501 socketChild = nullptr;
1502 }
1503
1504 RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
1505 rv = proxy->Init(this);
1506 if (NS_FAILED(rv)) {
1507 err_ = true;
1508 mon.NotifyAll();
1509 return;
1510 }
1511
1512 #ifdef XP_WIN
1513 if (!mozilla::IsWin8OrLater()) {
1514 // Increase default receive and send buffer size on <= Win7 to be able to
1515 // receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K size)
1516 // stream without losing packets.
1517 // Manual testing showed that 100K buffer size was not enough and the
1518 // packet loss dis-appeared with 256K buffer size.
1519 // See bug 1252769 for future improvements of this.
1520 minBuffSize = 256 * 1024;
1521 }
1522 #endif
1523 // XXX bug 1126232 - don't use null Principal!
1524 if (NS_FAILED(socket_child_->Bind(proxy, nullptr, host, port,
1525 /* addressReuse = */ false,
1526 /* loopback = */ false,
1527 /* recv buffer size */ minBuffSize,
1528 /* send buffer size */ minBuffSize,
1529 /* mainThreadEventTarget */ nullptr))) {
1530 err_ = true;
1531 MOZ_ASSERT(false, "Failed to create UDP socket");
1532 mon.NotifyAll();
1533 return;
1534 }
1535 }
1536
connect_i(const nsACString & host,const uint16_t port)1537 void NrUdpSocketIpc::connect_i(const nsACString &host, const uint16_t port) {
1538 ASSERT_ON_THREAD(io_thread_);
1539 nsresult rv;
1540 ReentrantMonitorAutoEnter mon(monitor_);
1541
1542 RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
1543 rv = proxy->Init(this);
1544 if (NS_FAILED(rv)) {
1545 err_ = true;
1546 mon.NotifyAll();
1547 return;
1548 }
1549
1550 if (NS_FAILED(socket_child_->Connect(proxy, host, port))) {
1551 err_ = true;
1552 MOZ_ASSERT(false, "Failed to connect UDP socket");
1553 mon.NotifyAll();
1554 return;
1555 }
1556 }
1557
sendto_i(const net::NetAddr & addr,nsAutoPtr<DataBuffer> buf)1558 void NrUdpSocketIpc::sendto_i(const net::NetAddr &addr,
1559 nsAutoPtr<DataBuffer> buf) {
1560 ASSERT_ON_THREAD(io_thread_);
1561
1562 ReentrantMonitorAutoEnter mon(monitor_);
1563
1564 if (!socket_child_) {
1565 MOZ_ASSERT(false);
1566 err_ = true;
1567 return;
1568 }
1569 if (NS_FAILED(
1570 socket_child_->SendWithAddress(&addr, buf->data(), buf->len()))) {
1571 err_ = true;
1572 }
1573 }
1574
close_i()1575 void NrUdpSocketIpc::close_i() {
1576 ASSERT_ON_THREAD(io_thread_);
1577
1578 if (socket_child_) {
1579 socket_child_->Close();
1580 socket_child_ = nullptr;
1581 }
1582 }
1583
1584 #if defined(MOZILLA_INTERNAL_API)
1585
ReleaseIOThread_s()1586 static void ReleaseIOThread_s() { sThread->ReleaseUse(); }
1587
1588 // close(), but transfer the socket_child_ reference to die as well
1589 // static
destroy_i(nsIUDPSocketChild * aChild,nsCOMPtr<nsIEventTarget> & aStsThread)1590 void NrUdpSocketIpc::destroy_i(nsIUDPSocketChild *aChild,
1591 nsCOMPtr<nsIEventTarget> &aStsThread) {
1592 RefPtr<nsIUDPSocketChild> socket_child_ref =
1593 already_AddRefed<nsIUDPSocketChild>(aChild);
1594 if (socket_child_ref) {
1595 socket_child_ref->Close();
1596 }
1597
1598 RUN_ON_THREAD(aStsThread, WrapRunnableNM(&ReleaseIOThread_s),
1599 NS_DISPATCH_NORMAL);
1600 }
1601 #endif
1602
recv_callback_s(RefPtr<nr_udp_message> msg)1603 void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
1604 ASSERT_ON_THREAD(sts_thread_);
1605
1606 {
1607 ReentrantMonitorAutoEnter mon(monitor_);
1608 if (state_ != NR_CONNECTED) {
1609 return;
1610 }
1611 }
1612
1613 // enqueue received message
1614 received_msgs_.push(msg);
1615
1616 if ((poll_flags() & PR_POLL_READ)) {
1617 fire_callback(NR_ASYNC_WAIT_READ);
1618 }
1619 }
1620
1621 #if defined(MOZILLA_INTERNAL_API)
1622 // TCPSocket.
1623 class NrTcpSocketIpc::TcpSocketReadyRunner : public Runnable {
1624 public:
TcpSocketReadyRunner(NrTcpSocketIpc * sck)1625 explicit TcpSocketReadyRunner(NrTcpSocketIpc *sck)
1626 : Runnable("NrTcpSocketIpc::TcpSocketReadyRunner"), socket_(sck) {}
1627
Run()1628 NS_IMETHOD Run() override {
1629 socket_->maybe_post_socket_ready();
1630 return NS_OK;
1631 }
1632
1633 private:
1634 RefPtr<NrTcpSocketIpc> socket_;
1635 };
1636
NS_IMPL_ISUPPORTS(NrTcpSocketIpc,nsITCPSocketCallback)1637 NS_IMPL_ISUPPORTS(NrTcpSocketIpc, nsITCPSocketCallback)
1638
1639 NrTcpSocketIpc::NrTcpSocketIpc(nsIThread *aThread)
1640 : NrSocketIpc(static_cast<nsIEventTarget *>(aThread)),
1641 mirror_state_(NR_INIT),
1642 state_(NR_INIT),
1643 buffered_bytes_(0),
1644 tracking_number_(0) {}
1645
~NrTcpSocketIpc()1646 NrTcpSocketIpc::~NrTcpSocketIpc() {
1647 // also guarantees socket_child_ is released from the io_thread
1648
1649 // close(), but transfer the socket_child_ reference to die as well
1650 RUN_ON_THREAD(io_thread_,
1651 mozilla::WrapRunnableNM(&NrTcpSocketIpc::release_child_i,
1652 socket_child_.forget().take()),
1653 NS_DISPATCH_NORMAL);
1654 }
1655
1656 //
1657 // nsITCPSocketCallback methods
1658 //
UpdateReadyState(uint32_t aReadyState)1659 NS_IMETHODIMP NrTcpSocketIpc::UpdateReadyState(uint32_t aReadyState) {
1660 NrSocketIpcState temp = NR_INIT;
1661 switch (static_cast<dom::TCPReadyState>(aReadyState)) {
1662 case dom::TCPReadyState::Connecting:
1663 temp = NR_CONNECTING;
1664 break;
1665 case dom::TCPReadyState::Open:
1666 temp = NR_CONNECTED;
1667 break;
1668 case dom::TCPReadyState::Closing:
1669 temp = NR_CLOSING;
1670 break;
1671 case dom::TCPReadyState::Closed:
1672 temp = NR_CLOSED;
1673 break;
1674 default:
1675 MOZ_ASSERT(false, "Invalid ReadyState");
1676 return NS_OK;
1677 }
1678 if (mirror_state_ != temp) {
1679 mirror_state_ = temp;
1680 RUN_ON_THREAD(sts_thread_,
1681 mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
1682 &NrTcpSocketIpc::update_state_s, temp),
1683 NS_DISPATCH_NORMAL);
1684 }
1685 return NS_OK;
1686 }
1687
UpdateBufferedAmount(uint32_t buffered_amount,uint32_t tracking_number)1688 NS_IMETHODIMP NrTcpSocketIpc::UpdateBufferedAmount(uint32_t buffered_amount,
1689 uint32_t tracking_number) {
1690 RUN_ON_THREAD(sts_thread_,
1691 mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
1692 &NrTcpSocketIpc::message_sent_s,
1693 buffered_amount, tracking_number),
1694 NS_DISPATCH_NORMAL);
1695
1696 return NS_OK;
1697 }
1698
FireDataArrayEvent(const nsAString & aType,const InfallibleTArray<uint8_t> & buffer)1699 NS_IMETHODIMP NrTcpSocketIpc::FireDataArrayEvent(
1700 const nsAString &aType, const InfallibleTArray<uint8_t> &buffer) {
1701 // Called when we received data.
1702 uint8_t *buf = const_cast<uint8_t *>(buffer.Elements());
1703
1704 nsAutoPtr<DataBuffer> data_buf(new DataBuffer(buf, buffer.Length()));
1705 RefPtr<nr_tcp_message> msg = new nr_tcp_message(data_buf);
1706
1707 RUN_ON_THREAD(sts_thread_,
1708 mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
1709 &NrTcpSocketIpc::recv_message_s, msg),
1710 NS_DISPATCH_NORMAL);
1711 return NS_OK;
1712 }
1713
FireErrorEvent(const nsAString & type,const nsAString & name)1714 NS_IMETHODIMP NrTcpSocketIpc::FireErrorEvent(const nsAString &type,
1715 const nsAString &name) {
1716 r_log(LOG_GENERIC, LOG_ERR, "Error from TCPSocketChild: type: %s, name: %s",
1717 NS_LossyConvertUTF16toASCII(type).get(),
1718 NS_LossyConvertUTF16toASCII(name).get());
1719 socket_child_ = nullptr;
1720
1721 mirror_state_ = NR_CLOSED;
1722 RUN_ON_THREAD(
1723 sts_thread_,
1724 mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
1725 &NrTcpSocketIpc::update_state_s, NR_CLOSED),
1726 NS_DISPATCH_NORMAL);
1727
1728 return NS_OK;
1729 }
1730
1731 // methods of nsITCPSocketCallback that we are not going to implement.
1732
FireDataStringEvent(const nsAString & type,const nsACString & data)1733 NS_IMETHODIMP NrTcpSocketIpc::FireDataStringEvent(const nsAString &type,
1734 const nsACString &data) {
1735 return NS_ERROR_NOT_IMPLEMENTED;
1736 }
1737
FireEvent(const nsAString & type)1738 NS_IMETHODIMP NrTcpSocketIpc::FireEvent(const nsAString &type) {
1739 // XXX support type.mData == 'close' at least
1740 return NS_ERROR_NOT_IMPLEMENTED;
1741 }
1742
1743 //
1744 // NrSocketBase methods.
1745 //
create(nr_transport_addr * addr)1746 int NrTcpSocketIpc::create(nr_transport_addr *addr) {
1747 int r, _status;
1748 nsresult rv;
1749 int32_t port;
1750 nsCString host;
1751
1752 if (state_ != NR_INIT) {
1753 ABORT(R_INTERNAL);
1754 }
1755
1756 sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1757 if (NS_FAILED(rv)) {
1758 MOZ_ASSERT(false, "Failed to get STS thread");
1759 ABORT(R_INTERNAL);
1760 }
1761
1762 // Sanity check
1763 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1764 ABORT(r);
1765 }
1766
1767 if ((r = nr_transport_addr_copy(&my_addr_, addr))) {
1768 ABORT(r);
1769 }
1770
1771 _status = 0;
1772 abort:
1773 return (_status);
1774 }
1775
sendto(const void * msg,size_t len,int flags,nr_transport_addr * to)1776 int NrTcpSocketIpc::sendto(const void *msg, size_t len, int flags,
1777 nr_transport_addr *to) {
1778 MOZ_ASSERT(false);
1779 return R_INTERNAL;
1780 }
1781
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)1782 int NrTcpSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
1783 nr_transport_addr *from) {
1784 MOZ_ASSERT(false);
1785 return R_INTERNAL;
1786 }
1787
getaddr(nr_transport_addr * addrp)1788 int NrTcpSocketIpc::getaddr(nr_transport_addr *addrp) {
1789 ASSERT_ON_THREAD(sts_thread_);
1790 return nr_transport_addr_copy(addrp, &my_addr_);
1791 }
1792
close()1793 void NrTcpSocketIpc::close() {
1794 ASSERT_ON_THREAD(sts_thread_);
1795
1796 if (state_ == NR_CLOSED || state_ == NR_CLOSING) {
1797 return;
1798 }
1799
1800 state_ = NR_CLOSING;
1801
1802 RUN_ON_THREAD(io_thread_,
1803 mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
1804 &NrTcpSocketIpc::close_i),
1805 NS_DISPATCH_NORMAL);
1806
1807 // remove all enqueued messages
1808 std::queue<RefPtr<nr_tcp_message>> empty;
1809 std::swap(msg_queue_, empty);
1810 }
1811
connect(nr_transport_addr * addr)1812 int NrTcpSocketIpc::connect(nr_transport_addr *addr) {
1813 nsCString remote_addr, local_addr;
1814 int32_t remote_port, local_port;
1815 int r, _status;
1816 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &remote_addr,
1817 &remote_port))) {
1818 ABORT(r);
1819 }
1820
1821 if ((r = nr_transport_addr_get_addrstring_and_port(&my_addr_, &local_addr,
1822 &local_port))) {
1823 MOZ_ASSERT(false); // shouldn't fail as it was sanity-checked in ::create()
1824 ABORT(r);
1825 }
1826
1827 state_ = mirror_state_ = NR_CONNECTING;
1828 RUN_ON_THREAD(
1829 io_thread_,
1830 mozilla::WrapRunnable(
1831 RefPtr<NrTcpSocketIpc>(this), &NrTcpSocketIpc::connect_i, remote_addr,
1832 static_cast<uint16_t>(remote_port), local_addr,
1833 static_cast<uint16_t>(local_port), nsCString(my_addr_.tls_host)),
1834 NS_DISPATCH_NORMAL);
1835
1836 // Make caller wait for ready to write.
1837 _status = R_WOULDBLOCK;
1838 abort:
1839 return _status;
1840 }
1841
write(const void * msg,size_t len,size_t * written)1842 int NrTcpSocketIpc::write(const void *msg, size_t len, size_t *written) {
1843 ASSERT_ON_THREAD(sts_thread_);
1844 int _status = 0;
1845 if (state_ != NR_CONNECTED) {
1846 ABORT(R_FAILED);
1847 }
1848
1849 if (buffered_bytes_ + len >= nsITCPSocketCallback::BUFFER_SIZE) {
1850 ABORT(R_WOULDBLOCK);
1851 }
1852
1853 buffered_bytes_ += len;
1854 {
1855 InfallibleTArray<uint8_t> *arr = new InfallibleTArray<uint8_t>();
1856 arr->AppendElements(static_cast<const uint8_t *>(msg), len);
1857 // keep track of un-acknowleged writes by tracking number.
1858 writes_in_flight_.push_back(len);
1859 RUN_ON_THREAD(
1860 io_thread_,
1861 mozilla::WrapRunnable(
1862 RefPtr<NrTcpSocketIpc>(this), &NrTcpSocketIpc::write_i,
1863 nsAutoPtr<InfallibleTArray<uint8_t>>(arr), ++tracking_number_),
1864 NS_DISPATCH_NORMAL);
1865 }
1866 *written = len;
1867 abort:
1868 return _status;
1869 }
1870
read(void * buf,size_t maxlen,size_t * len)1871 int NrTcpSocketIpc::read(void *buf, size_t maxlen, size_t *len) {
1872 int _status = 0;
1873 if (state_ != NR_CONNECTED) {
1874 ABORT(R_FAILED);
1875 }
1876
1877 if (msg_queue_.empty()) {
1878 ABORT(R_WOULDBLOCK);
1879 }
1880
1881 {
1882 RefPtr<nr_tcp_message> msg(msg_queue_.front());
1883 size_t consumed_len = std::min(maxlen, msg->unread_bytes());
1884 memcpy(buf, msg->reading_pointer(), consumed_len);
1885 if (consumed_len < msg->unread_bytes()) {
1886 // There is still something left in buffer.
1887 msg->read_bytes += consumed_len;
1888 } else {
1889 msg_queue_.pop();
1890 }
1891 *len = consumed_len;
1892 }
1893
1894 abort:
1895 return _status;
1896 }
1897
listen(int backlog)1898 int NrTcpSocketIpc::listen(int backlog) { return R_INTERNAL; }
1899
accept(nr_transport_addr * addrp,nr_socket ** sockp)1900 int NrTcpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
1901 return R_INTERNAL;
1902 }
1903
connect_i(const nsACString & remote_addr,uint16_t remote_port,const nsACString & local_addr,uint16_t local_port,const nsACString & tls_host)1904 void NrTcpSocketIpc::connect_i(const nsACString &remote_addr,
1905 uint16_t remote_port,
1906 const nsACString &local_addr,
1907 uint16_t local_port,
1908 const nsACString &tls_host) {
1909 ASSERT_ON_THREAD(io_thread_);
1910 // io_thread_ was initialized as main thread at constructor,
1911 // so the following assertion should be true.
1912 MOZ_ASSERT(NS_IsMainThread());
1913
1914 mirror_state_ = NR_CONNECTING;
1915
1916 dom::TCPSocketChild *child =
1917 new dom::TCPSocketChild(NS_ConvertUTF8toUTF16(remote_addr), remote_port,
1918 SystemGroup::EventTargetFor(TaskCategory::Other));
1919 socket_child_ = child;
1920
1921 // Bug 1285330: put filtering back in here
1922
1923 if (tls_host.IsEmpty()) {
1924 // XXX remove remote!
1925 socket_child_->SendWindowlessOpenBind(this, remote_addr, remote_port,
1926 local_addr, local_port,
1927 /* use ssl */ false,
1928 /* reuse addr port */ true);
1929 } else {
1930 // XXX remove remote!
1931 socket_child_->SendWindowlessOpenBind(this, tls_host, remote_port,
1932 local_addr, local_port,
1933 /* use ssl */ true,
1934 /* reuse addr port */ true);
1935 }
1936 }
1937
write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,uint32_t tracking_number)1938 void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
1939 uint32_t tracking_number) {
1940 ASSERT_ON_THREAD(io_thread_);
1941 if (!socket_child_) {
1942 return;
1943 }
1944 socket_child_->SendSendArray(*arr, tracking_number);
1945 }
1946
close_i()1947 void NrTcpSocketIpc::close_i() {
1948 ASSERT_ON_THREAD(io_thread_);
1949 mirror_state_ = NR_CLOSING;
1950 if (!socket_child_) {
1951 return;
1952 }
1953 socket_child_->SendClose();
1954 }
1955
1956 // close(), but transfer the socket_child_ reference to die as well
1957 // static
release_child_i(dom::TCPSocketChild * aChild)1958 void NrTcpSocketIpc::release_child_i(dom::TCPSocketChild *aChild) {
1959 RefPtr<dom::TCPSocketChild> socket_child_ref =
1960 already_AddRefed<dom::TCPSocketChild>(aChild);
1961 if (socket_child_ref) {
1962 socket_child_ref->SendClose();
1963 }
1964 // io_thread_ is MainThread, so no use to release
1965 }
1966
message_sent_s(uint32_t buffered_amount,uint32_t tracking_number)1967 void NrTcpSocketIpc::message_sent_s(uint32_t buffered_amount,
1968 uint32_t tracking_number) {
1969 ASSERT_ON_THREAD(sts_thread_);
1970
1971 size_t num_unacked_writes = tracking_number_ - tracking_number;
1972 while (writes_in_flight_.size() > num_unacked_writes) {
1973 writes_in_flight_.pop_front();
1974 }
1975
1976 for (size_t unacked_write_len : writes_in_flight_) {
1977 buffered_amount += unacked_write_len;
1978 }
1979
1980 r_log(LOG_GENERIC, LOG_ERR,
1981 "UpdateBufferedAmount: (tracking %u): %u, waiting: %s", tracking_number,
1982 buffered_amount, (poll_flags() & PR_POLL_WRITE) ? "yes" : "no");
1983
1984 buffered_bytes_ = buffered_amount;
1985 maybe_post_socket_ready();
1986 }
1987
recv_message_s(nr_tcp_message * msg)1988 void NrTcpSocketIpc::recv_message_s(nr_tcp_message *msg) {
1989 ASSERT_ON_THREAD(sts_thread_);
1990 msg_queue_.push(msg);
1991 maybe_post_socket_ready();
1992 }
1993
update_state_s(NrSocketIpcState next_state)1994 void NrTcpSocketIpc::update_state_s(NrSocketIpcState next_state) {
1995 ASSERT_ON_THREAD(sts_thread_);
1996 // only allow valid transitions
1997 switch (state_) {
1998 case NR_CONNECTING:
1999 if (next_state == NR_CONNECTED) {
2000 state_ = NR_CONNECTED;
2001 maybe_post_socket_ready();
2002 } else {
2003 state_ = next_state; // all states are valid from CONNECTING
2004 }
2005 break;
2006 case NR_CONNECTED:
2007 if (next_state != NR_CONNECTING) {
2008 state_ = next_state;
2009 }
2010 break;
2011 case NR_CLOSING:
2012 if (next_state == NR_CLOSED) {
2013 state_ = next_state;
2014 }
2015 break;
2016 case NR_CLOSED:
2017 break;
2018 default:
2019 MOZ_CRASH("update_state_s while in illegal state");
2020 }
2021 }
2022
maybe_post_socket_ready()2023 void NrTcpSocketIpc::maybe_post_socket_ready() {
2024 bool has_event = false;
2025 if (state_ == NR_CONNECTED) {
2026 if (poll_flags() & PR_POLL_WRITE) {
2027 // This effectively polls via the event loop until the
2028 // NR_ASYNC_WAIT_WRITE is no longer armed.
2029 if (buffered_bytes_ < nsITCPSocketCallback::BUFFER_SIZE) {
2030 r_log(LOG_GENERIC, LOG_INFO, "Firing write callback (%u)",
2031 (uint32_t)buffered_bytes_);
2032 fire_callback(NR_ASYNC_WAIT_WRITE);
2033 has_event = true;
2034 }
2035 }
2036 if (poll_flags() & PR_POLL_READ) {
2037 if (!msg_queue_.empty()) {
2038 if (msg_queue_.size() > 5) {
2039 r_log(LOG_GENERIC, LOG_INFO, "Firing read callback (%u)",
2040 (uint32_t)msg_queue_.size());
2041 }
2042 fire_callback(NR_ASYNC_WAIT_READ);
2043 has_event = true;
2044 }
2045 }
2046 }
2047
2048 // If any event has been posted, we post a runnable to see
2049 // if the events have to be posted again.
2050 if (has_event) {
2051 RefPtr<TcpSocketReadyRunner> runnable = new TcpSocketReadyRunner(this);
2052 NS_DispatchToCurrentThread(runnable);
2053 }
2054 }
2055 #endif
2056
2057 } // close namespace
2058
2059 using namespace mozilla;
2060
2061 // Bridge to the nr_socket interface
2062 static int nr_socket_local_destroy(void **objp);
2063 static int nr_socket_local_sendto(void *obj, const void *msg, size_t len,
2064 int flags, nr_transport_addr *to);
2065 static int nr_socket_local_recvfrom(void *obj, void *restrict buf,
2066 size_t maxlen, size_t *len, int flags,
2067 nr_transport_addr *from);
2068 static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd);
2069 static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp);
2070 static int nr_socket_local_close(void *obj);
2071 static int nr_socket_local_connect(void *sock, nr_transport_addr *addr);
2072 static int nr_socket_local_write(void *obj, const void *msg, size_t len,
2073 size_t *written);
2074 static int nr_socket_local_read(void *obj, void *restrict buf, size_t maxlen,
2075 size_t *len);
2076 static int nr_socket_local_listen(void *obj, int backlog);
2077 static int nr_socket_local_accept(void *obj, nr_transport_addr *addrp,
2078 nr_socket **sockp);
2079
2080 static nr_socket_vtbl nr_socket_local_vtbl = {2,
2081 nr_socket_local_destroy,
2082 nr_socket_local_sendto,
2083 nr_socket_local_recvfrom,
2084 nr_socket_local_getfd,
2085 nr_socket_local_getaddr,
2086 nr_socket_local_connect,
2087 nr_socket_local_write,
2088 nr_socket_local_read,
2089 nr_socket_local_close,
2090 nr_socket_local_listen,
2091 nr_socket_local_accept};
2092
2093 /* static */
CreateSocket(nr_transport_addr * addr,RefPtr<NrSocketBase> * sock)2094 int NrSocketBase::CreateSocket(nr_transport_addr *addr,
2095 RefPtr<NrSocketBase> *sock) {
2096 int r, _status;
2097
2098 // create IPC bridge for content process
2099 if (XRE_IsParentProcess()) {
2100 *sock = new NrSocket();
2101 } else {
2102 switch (addr->protocol) {
2103 case IPPROTO_UDP:
2104 *sock = new NrUdpSocketIpc();
2105 break;
2106 case IPPROTO_TCP:
2107 #if defined(MOZILLA_INTERNAL_API)
2108 {
2109 nsCOMPtr<nsIThread> main_thread;
2110 NS_GetMainThread(getter_AddRefs(main_thread));
2111 *sock = new NrTcpSocketIpc(main_thread.get());
2112 }
2113 #else
2114 ABORT(R_REJECTED);
2115 #endif
2116 break;
2117 }
2118 }
2119
2120 r = (*sock)->create(addr);
2121 if (r) ABORT(r);
2122
2123 _status = 0;
2124 abort:
2125 if (_status) {
2126 *sock = nullptr;
2127 }
2128 return _status;
2129 }
2130
nr_socket_local_create(void * obj,nr_transport_addr * addr,nr_socket ** sockp)2131 int nr_socket_local_create(void *obj, nr_transport_addr *addr,
2132 nr_socket **sockp) {
2133 RefPtr<NrSocketBase> sock;
2134 int r, _status;
2135
2136 r = NrSocketBase::CreateSocket(addr, &sock);
2137 if (r) {
2138 ABORT(r);
2139 }
2140
2141 r = nr_socket_create_int(static_cast<void *>(sock), sock->vtbl(), sockp);
2142 if (r) ABORT(r);
2143
2144 _status = 0;
2145
2146 {
2147 // We will release this reference in destroy(), not exactly the normal
2148 // ownership model, but it is what it is.
2149 NrSocketBase *dummy = sock.forget().take();
2150 (void)dummy;
2151 }
2152
2153 abort:
2154 return _status;
2155 }
2156
nr_socket_local_destroy(void ** objp)2157 static int nr_socket_local_destroy(void **objp) {
2158 if (!objp || !*objp) return 0;
2159
2160 NrSocketBase *sock = static_cast<NrSocketBase *>(*objp);
2161 *objp = nullptr;
2162
2163 sock->close(); // Signal STS that we want not to listen
2164 sock->Release(); // Decrement the ref count
2165
2166 return 0;
2167 }
2168
nr_socket_local_sendto(void * obj,const void * msg,size_t len,int flags,nr_transport_addr * addr)2169 static int nr_socket_local_sendto(void *obj, const void *msg, size_t len,
2170 int flags, nr_transport_addr *addr) {
2171 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2172
2173 return sock->sendto(msg, len, flags, addr);
2174 }
2175
nr_socket_local_recvfrom(void * obj,void * restrict buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * addr)2176 static int nr_socket_local_recvfrom(void *obj, void *restrict buf,
2177 size_t maxlen, size_t *len, int flags,
2178 nr_transport_addr *addr) {
2179 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2180
2181 return sock->recvfrom(buf, maxlen, len, flags, addr);
2182 }
2183
nr_socket_local_getfd(void * obj,NR_SOCKET * fd)2184 static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd) {
2185 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2186
2187 *fd = sock;
2188
2189 return 0;
2190 }
2191
nr_socket_local_getaddr(void * obj,nr_transport_addr * addrp)2192 static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp) {
2193 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2194
2195 return sock->getaddr(addrp);
2196 }
2197
nr_socket_local_close(void * obj)2198 static int nr_socket_local_close(void *obj) {
2199 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2200
2201 sock->close();
2202
2203 return 0;
2204 }
2205
nr_socket_local_write(void * obj,const void * msg,size_t len,size_t * written)2206 static int nr_socket_local_write(void *obj, const void *msg, size_t len,
2207 size_t *written) {
2208 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2209
2210 return sock->write(msg, len, written);
2211 }
2212
nr_socket_local_read(void * obj,void * restrict buf,size_t maxlen,size_t * len)2213 static int nr_socket_local_read(void *obj, void *restrict buf, size_t maxlen,
2214 size_t *len) {
2215 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2216
2217 return sock->read(buf, maxlen, len);
2218 }
2219
nr_socket_local_connect(void * obj,nr_transport_addr * addr)2220 static int nr_socket_local_connect(void *obj, nr_transport_addr *addr) {
2221 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2222
2223 return sock->connect(addr);
2224 }
2225
nr_socket_local_listen(void * obj,int backlog)2226 static int nr_socket_local_listen(void *obj, int backlog) {
2227 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2228
2229 return sock->listen(backlog);
2230 }
2231
nr_socket_local_accept(void * obj,nr_transport_addr * addrp,nr_socket ** sockp)2232 static int nr_socket_local_accept(void *obj, nr_transport_addr *addrp,
2233 nr_socket **sockp) {
2234 NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
2235
2236 return sock->accept(addrp, sockp);
2237 }
2238
2239 // Implement async api
NR_async_wait(NR_SOCKET sock,int how,NR_async_cb cb,void * cb_arg,char * function,int line)2240 int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb, void *cb_arg,
2241 char *function, int line) {
2242 NrSocketBase *s = static_cast<NrSocketBase *>(sock);
2243
2244 return s->async_wait(how, cb, cb_arg, function, line);
2245 }
2246
NR_async_cancel(NR_SOCKET sock,int how)2247 int NR_async_cancel(NR_SOCKET sock, int how) {
2248 NrSocketBase *s = static_cast<NrSocketBase *>(sock);
2249
2250 return s->cancel(how);
2251 }
2252
vtbl()2253 nr_socket_vtbl *NrSocketBase::vtbl() { return &nr_socket_local_vtbl; }
2254