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 "nsISocketFilter.h"
113 #include "nsDebug.h"
114 #include "nsNetUtil.h"
115
116 #ifdef XP_WIN
117 # include "mozilla/WindowsVersion.h"
118 #endif
119
120 #if defined(MOZILLA_INTERNAL_API)
121 // csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
122 # ifdef LOG_INFO
123 # define LOG_TEMP_INFO LOG_INFO
124 # undef LOG_INFO
125 # endif
126 # ifdef LOG_WARNING
127 # define LOG_TEMP_WARNING LOG_WARNING
128 # undef LOG_WARNING
129 # endif
130 # if defined(LOG_DEBUG)
131 # define LOG_TEMP_DEBUG LOG_DEBUG
132 # undef LOG_DEBUG
133 # endif
134 # undef strlcpy
135
136 # include "mozilla/dom/network/UDPSocketChild.h"
137
138 # ifdef LOG_TEMP_INFO
139 # define LOG_INFO LOG_TEMP_INFO
140 # endif
141 # ifdef LOG_TEMP_WARNING
142 # define LOG_WARNING LOG_TEMP_WARNING
143 # endif
144
145 # ifdef LOG_TEMP_DEBUG
146 # define LOG_DEBUG LOG_TEMP_DEBUG
147 # endif
148 # ifdef XP_WIN
149 # ifdef LOG_DEBUG
150 # undef LOG_DEBUG
151 # endif
152 // cloned from csi_platform.h. Win32 doesn't like how we hide symbols
153 # define LOG_DEBUG 7
154 # endif
155 #endif
156
157 extern "C" {
158 #include "nr_api.h"
159 #include "async_wait.h"
160 #include "nr_socket.h"
161 #include "nr_socket_local.h"
162 #include "stun_hint.h"
163 }
164 #include "nr_socket_prsock.h"
165 #include "simpletokenbucket.h"
166 #include "test_nr_socket.h"
167 #include "nr_socket_tcp.h"
168 #include "nr_socket_proxy_config.h"
169
170 // Implement the nsISupports ref counting
171 namespace mozilla {
172
173 #if defined(MOZILLA_INTERNAL_API)
174 class SingletonThreadHolder final {
175 private:
~SingletonThreadHolder()176 ~SingletonThreadHolder() {
177 r_log(LOG_GENERIC, LOG_DEBUG, "Deleting SingletonThreadHolder");
178 if (mThread) {
179 // Likely a connection is somehow being held in CC or GC
180 NS_WARNING(
181 "SingletonThreads should be Released and shut down before exit!");
182 mThread->Shutdown();
183 mThread = nullptr;
184 }
185 }
186
187 DISALLOW_COPY_ASSIGN(SingletonThreadHolder);
188
189 public:
190 // Must be threadsafe for StaticRefPtr/ClearOnShutdown
191 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder)
192
SingletonThreadHolder(const nsACString & aName)193 explicit SingletonThreadHolder(const nsACString& aName) : mName(aName) {
194 mParentThread = NS_GetCurrentThread();
195 }
196
GetThread()197 nsIThread* GetThread() { return mThread; }
198
199 /*
200 * Keep track of how many instances are using a SingletonThreadHolder.
201 * When no one is using it, shut it down
202 */
AddUse()203 void AddUse() {
204 MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
205 MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
206 nsrefcnt count = ++mUseCount;
207 if (count == 1) {
208 // idle -> in-use
209 nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
210 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread,
211 "Should successfully create mtransport I/O thread");
212 r_log(LOG_GENERIC, LOG_DEBUG, "Created wrapped SingletonThread %p",
213 mThread.get());
214 }
215 r_log(LOG_GENERIC, LOG_DEBUG, "AddUse_i: %lu", (unsigned long)count);
216 }
217
ReleaseUse()218 void ReleaseUse() {
219 MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
220 nsrefcnt count = --mUseCount;
221 MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
222 if (mThread && count == 0) {
223 // in-use -> idle -- no one forcing it to remain instantiated
224 r_log(LOG_GENERIC, LOG_DEBUG, "Shutting down wrapped SingletonThread %p",
225 mThread.get());
226 mThread->AsyncShutdown();
227 mThread = nullptr;
228 // It'd be nice to use a timer instead... But be careful of
229 // xpcom-shutdown-threads in that case
230 }
231 r_log(LOG_GENERIC, LOG_DEBUG, "ReleaseUse_i: %lu", (unsigned long)count);
232 }
233
234 private:
235 nsCString mName;
236 nsAutoRefCnt mUseCount;
237 nsCOMPtr<nsIThread> mParentThread;
238 nsCOMPtr<nsIThread> mThread;
239 };
240
241 static StaticRefPtr<SingletonThreadHolder> sThread;
242
ClearSingletonOnShutdown()243 static void ClearSingletonOnShutdown() {
244 ClearOnShutdown(&sThread, ShutdownPhase::XPCOMShutdownLoaders);
245 }
246 #endif
247
GetIOThreadAndAddUse_s()248 static nsIThread* GetIOThreadAndAddUse_s() {
249 // Always runs on STS thread!
250 #if defined(MOZILLA_INTERNAL_API)
251 // We need to safely release this on shutdown to avoid leaks
252 if (!sThread) {
253 sThread = new SingletonThreadHolder("mtransport"_ns);
254 NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
255 }
256 // Mark that we're using the shared thread and need it to stick around
257 sThread->AddUse();
258 return sThread->GetThread();
259 #else
260 static nsCOMPtr<nsIThread> sThread;
261 if (!sThread) {
262 (void)NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
263 }
264 return sThread;
265 #endif
266 }
267
NrSocketIpc(nsIEventTarget * aThread)268 NrSocketIpc::NrSocketIpc(nsIEventTarget* aThread) : io_thread_(aThread) {}
269
270 static TimeStamp nr_socket_short_term_violation_time;
271 static TimeStamp nr_socket_long_term_violation_time;
272
short_term_violation_time()273 TimeStamp NrSocketBase::short_term_violation_time() {
274 return nr_socket_short_term_violation_time;
275 }
276
long_term_violation_time()277 TimeStamp NrSocketBase::long_term_violation_time() {
278 return nr_socket_long_term_violation_time;
279 }
280
281 // NrSocketBase implementation
282 // async_event APIs
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)283 int NrSocketBase::async_wait(int how, NR_async_cb cb, void* cb_arg,
284 char* function, int line) {
285 uint16_t flag;
286
287 switch (how) {
288 case NR_ASYNC_WAIT_READ:
289 flag = PR_POLL_READ;
290 break;
291 case NR_ASYNC_WAIT_WRITE:
292 flag = PR_POLL_WRITE;
293 break;
294 default:
295 return R_BAD_ARGS;
296 }
297
298 cbs_[how] = cb;
299 cb_args_[how] = cb_arg;
300 poll_flags_ |= flag;
301
302 return 0;
303 }
304
cancel(int how)305 int NrSocketBase::cancel(int how) {
306 uint16_t flag;
307
308 switch (how) {
309 case NR_ASYNC_WAIT_READ:
310 flag = PR_POLL_READ;
311 break;
312 case NR_ASYNC_WAIT_WRITE:
313 flag = PR_POLL_WRITE;
314 break;
315 default:
316 return R_BAD_ARGS;
317 }
318
319 poll_flags_ &= ~flag;
320
321 return 0;
322 }
323
fire_callback(int how)324 void NrSocketBase::fire_callback(int how) {
325 // This can't happen unless we are armed because we only set
326 // the flags if we are armed
327 MOZ_ASSERT(cbs_[how]);
328
329 // Now cancel so that we need to be re-armed. Note that
330 // the re-arming probably happens in the callback we are
331 // about to fire.
332 cancel(how);
333
334 cbs_[how](this, how, cb_args_[how]);
335 }
336
337 // NrSocket implementation
NS_IMPL_QUERY_INTERFACE0(NrSocket)338 NS_IMPL_QUERY_INTERFACE0(NrSocket)
339
340 // The nsASocket callbacks
341 void NrSocket::OnSocketReady(PRFileDesc* fd, int16_t outflags) {
342 if (outflags & PR_POLL_READ & poll_flags()) fire_callback(NR_ASYNC_WAIT_READ);
343 if (outflags & PR_POLL_WRITE & poll_flags())
344 fire_callback(NR_ASYNC_WAIT_WRITE);
345 if (outflags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP))
346 // TODO: Bug 946423: how do we notify the upper layers about this?
347 close();
348 }
349
OnSocketDetached(PRFileDesc * fd)350 void NrSocket::OnSocketDetached(PRFileDesc* fd) {
351 r_log(LOG_GENERIC, LOG_DEBUG, "Socket %p detached", fd);
352 }
353
IsLocal(bool * aIsLocal)354 void NrSocket::IsLocal(bool* aIsLocal) {
355 // TODO(jesup): better check? Does it matter? (likely no)
356 *aIsLocal = false;
357 }
358
359 // async_event APIs
async_wait(int how,NR_async_cb cb,void * cb_arg,char * function,int line)360 int NrSocket::async_wait(int how, NR_async_cb cb, void* cb_arg, char* function,
361 int line) {
362 int r = NrSocketBase::async_wait(how, cb, cb_arg, function, line);
363
364 if (!r) {
365 mPollFlags = poll_flags();
366 }
367
368 return r;
369 }
370
cancel(int how)371 int NrSocket::cancel(int how) {
372 int r = NrSocketBase::cancel(how);
373
374 if (!r) {
375 mPollFlags = poll_flags();
376 }
377
378 return r;
379 }
380
381 // Helper functions for addresses
nr_transport_addr_to_praddr(const nr_transport_addr * addr,PRNetAddr * naddr)382 static int nr_transport_addr_to_praddr(const nr_transport_addr* addr,
383 PRNetAddr* naddr) {
384 int _status;
385
386 memset(naddr, 0, sizeof(*naddr));
387
388 switch (addr->protocol) {
389 case IPPROTO_TCP:
390 break;
391 case IPPROTO_UDP:
392 break;
393 default:
394 ABORT(R_BAD_ARGS);
395 }
396
397 switch (addr->ip_version) {
398 case NR_IPV4:
399 naddr->inet.family = PR_AF_INET;
400 naddr->inet.port = addr->u.addr4.sin_port;
401 naddr->inet.ip = addr->u.addr4.sin_addr.s_addr;
402 break;
403 case NR_IPV6:
404 naddr->ipv6.family = PR_AF_INET6;
405 naddr->ipv6.port = addr->u.addr6.sin6_port;
406 naddr->ipv6.flowinfo = addr->u.addr6.sin6_flowinfo;
407 memcpy(&naddr->ipv6.ip, &addr->u.addr6.sin6_addr, sizeof(in6_addr));
408 naddr->ipv6.scope_id = addr->u.addr6.sin6_scope_id;
409 break;
410 default:
411 ABORT(R_BAD_ARGS);
412 }
413
414 _status = 0;
415 abort:
416 return (_status);
417 }
418
419 // XXX schien@mozilla.com: copy from PRNetAddrToNetAddr,
420 // should be removed after fix the link error in signaling_unittests
praddr_to_netaddr(const PRNetAddr * prAddr,net::NetAddr * addr)421 static int praddr_to_netaddr(const PRNetAddr* prAddr, net::NetAddr* addr) {
422 int _status;
423
424 switch (prAddr->raw.family) {
425 case PR_AF_INET:
426 addr->inet.family = AF_INET;
427 addr->inet.port = prAddr->inet.port;
428 addr->inet.ip = prAddr->inet.ip;
429 break;
430 case PR_AF_INET6:
431 addr->inet6.family = AF_INET6;
432 addr->inet6.port = prAddr->ipv6.port;
433 addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
434 memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
435 addr->inet6.scope_id = prAddr->ipv6.scope_id;
436 break;
437 default:
438 MOZ_ASSERT(false);
439 ABORT(R_BAD_ARGS);
440 }
441
442 _status = 0;
443 abort:
444 return (_status);
445 }
446
nr_transport_addr_to_netaddr(const nr_transport_addr * addr,net::NetAddr * naddr)447 static int nr_transport_addr_to_netaddr(const nr_transport_addr* addr,
448 net::NetAddr* naddr) {
449 int r, _status;
450 PRNetAddr praddr;
451
452 if ((r = nr_transport_addr_to_praddr(addr, &praddr))) {
453 ABORT(r);
454 }
455
456 if ((r = praddr_to_netaddr(&praddr, naddr))) {
457 ABORT(r);
458 }
459
460 _status = 0;
461 abort:
462 return (_status);
463 }
464
nr_netaddr_to_transport_addr(const net::NetAddr * netaddr,nr_transport_addr * addr,int protocol)465 int nr_netaddr_to_transport_addr(const net::NetAddr* netaddr,
466 nr_transport_addr* addr, int protocol) {
467 int _status;
468 int r;
469
470 switch (netaddr->raw.family) {
471 case AF_INET:
472 if ((r = nr_ip4_port_to_transport_addr(ntohl(netaddr->inet.ip),
473 ntohs(netaddr->inet.port),
474 protocol, addr)))
475 ABORT(r);
476 break;
477 case AF_INET6:
478 if ((r = nr_ip6_port_to_transport_addr((in6_addr*)&netaddr->inet6.ip.u8,
479 ntohs(netaddr->inet6.port),
480 protocol, addr)))
481 ABORT(r);
482 break;
483 default:
484 MOZ_ASSERT(false);
485 ABORT(R_BAD_ARGS);
486 }
487 _status = 0;
488 abort:
489 return (_status);
490 }
491
nr_praddr_to_transport_addr(const PRNetAddr * praddr,nr_transport_addr * addr,int protocol,int keep)492 int nr_praddr_to_transport_addr(const PRNetAddr* praddr,
493 nr_transport_addr* addr, int protocol,
494 int keep) {
495 int _status;
496 int r;
497 struct sockaddr_in ip4;
498 struct sockaddr_in6 ip6;
499
500 switch (praddr->raw.family) {
501 case PR_AF_INET:
502 ip4.sin_family = PF_INET;
503 ip4.sin_addr.s_addr = praddr->inet.ip;
504 ip4.sin_port = praddr->inet.port;
505 if ((r = nr_sockaddr_to_transport_addr((sockaddr*)&ip4, protocol, keep,
506 addr)))
507 ABORT(r);
508 break;
509 case PR_AF_INET6:
510 ip6.sin6_family = PF_INET6;
511 ip6.sin6_port = praddr->ipv6.port;
512 ip6.sin6_flowinfo = praddr->ipv6.flowinfo;
513 memcpy(&ip6.sin6_addr, &praddr->ipv6.ip, sizeof(in6_addr));
514 ip6.sin6_scope_id = praddr->ipv6.scope_id;
515 if ((r = nr_sockaddr_to_transport_addr((sockaddr*)&ip6, protocol, keep,
516 addr)))
517 ABORT(r);
518 break;
519 default:
520 MOZ_ASSERT(false);
521 ABORT(R_BAD_ARGS);
522 }
523
524 _status = 0;
525 abort:
526 return (_status);
527 }
528
529 /*
530 * nr_transport_addr_get_addrstring_and_port
531 * convert nr_transport_addr to IP address string and port number
532 */
nr_transport_addr_get_addrstring_and_port(const nr_transport_addr * addr,nsACString * host,int32_t * port)533 int nr_transport_addr_get_addrstring_and_port(const nr_transport_addr* addr,
534 nsACString* host, int32_t* port) {
535 int r, _status;
536 char addr_string[64];
537
538 // We cannot directly use |nr_transport_addr.as_string| because it contains
539 // more than ip address, therefore, we need to explicity convert it
540 // from |nr_transport_addr_get_addrstring|.
541 if ((r = nr_transport_addr_get_addrstring(addr, addr_string,
542 sizeof(addr_string)))) {
543 ABORT(r);
544 }
545
546 if ((r = nr_transport_addr_get_port(addr, port))) {
547 ABORT(r);
548 }
549
550 *host = addr_string;
551
552 _status = 0;
553 abort:
554 return (_status);
555 }
556
557 // nr_socket APIs (as member functions)
create(nr_transport_addr * addr)558 int NrSocket::create(nr_transport_addr* addr) {
559 int r, _status;
560
561 PRStatus status;
562 PRNetAddr naddr;
563
564 nsresult rv;
565 nsCOMPtr<nsISocketTransportService> stservice =
566 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
567
568 if (!NS_SUCCEEDED(rv)) {
569 ABORT(R_INTERNAL);
570 }
571
572 if ((r = nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r);
573
574 switch (addr->protocol) {
575 case IPPROTO_UDP:
576 if (!(fd_ = PR_OpenUDPSocket(naddr.raw.family))) {
577 r_log(LOG_GENERIC, LOG_CRIT,
578 "Couldn't create UDP socket, "
579 "family=%d, err=%d",
580 naddr.raw.family, PR_GetError());
581 ABORT(R_INTERNAL);
582 }
583 #ifdef XP_WIN
584 if (!mozilla::IsWin8OrLater()) {
585 // Increase default send and receive buffer sizes on <= Win7 to be able
586 // to receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K
587 // size) stream without losing packets. Manual testing showed that 100K
588 // buffer size was not enough and the packet loss dis-appeared with 256K
589 // buffer size. See bug 1252769 for future improvements of this.
590 PRSize min_buffer_size = 256 * 1024;
591 PRSocketOptionData opt_rcvbuf;
592 opt_rcvbuf.option = PR_SockOpt_RecvBufferSize;
593 if ((status = PR_GetSocketOption(fd_, &opt_rcvbuf)) == PR_SUCCESS) {
594 if (opt_rcvbuf.value.recv_buffer_size < min_buffer_size) {
595 opt_rcvbuf.value.recv_buffer_size = min_buffer_size;
596 if ((status = PR_SetSocketOption(fd_, &opt_rcvbuf)) != PR_SUCCESS) {
597 r_log(LOG_GENERIC, LOG_CRIT,
598 "Couldn't set socket receive buffer size: %d", status);
599 }
600 } else {
601 r_log(LOG_GENERIC, LOG_INFO,
602 "Socket receive buffer size is already: %d",
603 opt_rcvbuf.value.recv_buffer_size);
604 }
605 } else {
606 r_log(LOG_GENERIC, LOG_CRIT,
607 "Couldn't get socket receive buffer size: %d", status);
608 }
609 PRSocketOptionData opt_sndbuf;
610 opt_sndbuf.option = PR_SockOpt_SendBufferSize;
611 if ((status = PR_GetSocketOption(fd_, &opt_sndbuf)) == PR_SUCCESS) {
612 if (opt_sndbuf.value.recv_buffer_size < min_buffer_size) {
613 opt_sndbuf.value.recv_buffer_size = min_buffer_size;
614 if ((status = PR_SetSocketOption(fd_, &opt_sndbuf)) != PR_SUCCESS) {
615 r_log(LOG_GENERIC, LOG_CRIT,
616 "Couldn't set socket send buffer size: %d", status);
617 }
618 } else {
619 r_log(LOG_GENERIC, LOG_INFO,
620 "Socket send buffer size is already: %d",
621 opt_sndbuf.value.recv_buffer_size);
622 }
623 } else {
624 r_log(LOG_GENERIC, LOG_CRIT,
625 "Couldn't get socket send buffer size: %d", status);
626 }
627 }
628 #endif
629 break;
630 case IPPROTO_TCP:
631 // TODO: Rewrite this to use WebrtcTcpSocket.
632 // Also use the same logic for TLS.
633 if (my_addr_.fqdn[0] != '\0') ABORT(R_INTERNAL);
634
635 if (!(fd_ = PR_OpenTCPSocket(naddr.raw.family))) {
636 r_log(LOG_GENERIC, LOG_CRIT,
637 "Couldn't create TCP socket, "
638 "family=%d, err=%d",
639 naddr.raw.family, PR_GetError());
640 ABORT(R_INTERNAL);
641 }
642 // Set ReuseAddr for TCP sockets to enable having several
643 // sockets bound to same local IP and port
644 PRSocketOptionData opt_reuseaddr;
645 opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
646 opt_reuseaddr.value.reuse_addr = PR_TRUE;
647 status = PR_SetSocketOption(fd_, &opt_reuseaddr);
648 if (status != PR_SUCCESS) {
649 r_log(LOG_GENERIC, LOG_CRIT,
650 "Couldn't set reuse addr socket option: %d", status);
651 ABORT(R_INTERNAL);
652 }
653 // And also set ReusePort for platforms supporting this socket option
654 PRSocketOptionData opt_reuseport;
655 opt_reuseport.option = PR_SockOpt_Reuseport;
656 opt_reuseport.value.reuse_port = PR_TRUE;
657 status = PR_SetSocketOption(fd_, &opt_reuseport);
658 if (status != PR_SUCCESS) {
659 if (PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
660 r_log(LOG_GENERIC, LOG_CRIT,
661 "Couldn't set reuse port socket option: %d", status);
662 ABORT(R_INTERNAL);
663 }
664 }
665 // Try to speedup packet delivery by disabling TCP Nagle
666 PRSocketOptionData opt_nodelay;
667 opt_nodelay.option = PR_SockOpt_NoDelay;
668 opt_nodelay.value.no_delay = PR_TRUE;
669 status = PR_SetSocketOption(fd_, &opt_nodelay);
670 if (status != PR_SUCCESS) {
671 r_log(LOG_GENERIC, LOG_WARNING,
672 "Couldn't set Nodelay socket option: %d", status);
673 }
674 break;
675 default:
676 ABORT(R_INTERNAL);
677 }
678
679 status = PR_Bind(fd_, &naddr);
680 if (status != PR_SUCCESS) {
681 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't bind socket to address %s",
682 addr->as_string);
683 ABORT(R_INTERNAL);
684 }
685
686 r_log(LOG_GENERIC, LOG_DEBUG, "Creating socket %p with addr %s", fd_,
687 addr->as_string);
688 nr_transport_addr_copy(&my_addr_, addr);
689
690 /* If we have a wildcard port, patch up the addr */
691 if (nr_transport_addr_is_wildcard(addr)) {
692 status = PR_GetSockName(fd_, &naddr);
693 if (status != PR_SUCCESS) {
694 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
695 ABORT(R_INTERNAL);
696 }
697
698 if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
699 ABORT(r);
700 }
701
702 // Set nonblocking
703 PRSocketOptionData opt_nonblock;
704 opt_nonblock.option = PR_SockOpt_Nonblocking;
705 opt_nonblock.value.non_blocking = PR_TRUE;
706 status = PR_SetSocketOption(fd_, &opt_nonblock);
707 if (status != PR_SUCCESS) {
708 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking");
709 ABORT(R_INTERNAL);
710 }
711
712 // Remember our thread.
713 ststhread_ = do_QueryInterface(stservice, &rv);
714 if (!NS_SUCCEEDED(rv)) ABORT(R_INTERNAL);
715
716 // Finally, register with the STS
717 rv = stservice->AttachSocket(fd_, this);
718 if (!NS_SUCCEEDED(rv)) {
719 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't attach socket to STS, rv=%u",
720 static_cast<unsigned>(rv));
721 ABORT(R_INTERNAL);
722 }
723
724 _status = 0;
725
726 abort:
727 return (_status);
728 }
729
ShouldDrop(size_t len)730 static int ShouldDrop(size_t len) {
731 // Global rate limiting for stun requests, to mitigate the ice hammer DoS
732 // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc)
733
734 // Tolerate rate of 8k/sec, for one second.
735 static SimpleTokenBucket burst(16384 * 1, 16384);
736 // Tolerate rate of 7.2k/sec over twenty seconds.
737 static SimpleTokenBucket sustained(7372 * 20, 7372);
738
739 // Check number of tokens in each bucket.
740 if (burst.getTokens(UINT32_MAX) < len) {
741 r_log(LOG_GENERIC, LOG_ERR,
742 "Short term global rate limit for STUN requests exceeded.");
743 #ifdef MOZILLA_INTERNAL_API
744 nr_socket_short_term_violation_time = TimeStamp::Now();
745 #endif
746
747 // Bug 1013007
748 #if !EARLY_BETA_OR_EARLIER
749 return R_WOULDBLOCK;
750 #else
751 MOZ_ASSERT(false,
752 "Short term global rate limit for STUN requests exceeded. Go "
753 "bug bcampen@mozilla.com if you weren't intentionally "
754 "spamming ICE candidates, or don't know what that means.");
755 #endif
756 }
757
758 if (sustained.getTokens(UINT32_MAX) < len) {
759 r_log(LOG_GENERIC, LOG_ERR,
760 "Long term global rate limit for STUN requests exceeded.");
761 #ifdef MOZILLA_INTERNAL_API
762 nr_socket_long_term_violation_time = TimeStamp::Now();
763 #endif
764 // Bug 1013007
765 #if !EARLY_BETA_OR_EARLIER
766 return R_WOULDBLOCK;
767 #else
768 MOZ_ASSERT(false,
769 "Long term global rate limit for STUN requests exceeded. Go "
770 "bug bcampen@mozilla.com if you weren't intentionally "
771 "spamming ICE candidates, or don't know what that means.");
772 #endif
773 }
774
775 // Take len tokens from both buckets.
776 // (not threadsafe, but no problem since this is only called from STS)
777 burst.getTokens(len);
778 sustained.getTokens(len);
779 return 0;
780 }
781
782 // This should be called on the STS thread.
sendto(const void * msg,size_t len,int flags,const nr_transport_addr * to)783 int NrSocket::sendto(const void* msg, size_t len, int flags,
784 const nr_transport_addr* to) {
785 ASSERT_ON_THREAD(ststhread_);
786 int r, _status;
787 PRNetAddr naddr;
788 int32_t status;
789
790 if ((r = nr_transport_addr_to_praddr(to, &naddr))) ABORT(r);
791
792 if (fd_ == nullptr) ABORT(R_EOD);
793
794 if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
795 ABORT(R_WOULDBLOCK);
796 }
797
798 // TODO: Convert flags?
799 status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT);
800 if (status < 0 || (size_t)status != len) {
801 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
802
803 r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s: %d", to->as_string,
804 PR_GetError());
805 ABORT(R_IO_ERROR);
806 }
807
808 _status = 0;
809 abort:
810 return (_status);
811 }
812
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)813 int NrSocket::recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
814 nr_transport_addr* from) {
815 ASSERT_ON_THREAD(ststhread_);
816 int r, _status;
817 PRNetAddr nfrom;
818 int32_t status;
819
820 status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
821 if (status <= 0) {
822 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
823 r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError());
824 ABORT(R_IO_ERROR);
825 }
826 *len = status;
827
828 if ((r = nr_praddr_to_transport_addr(&nfrom, from, my_addr_.protocol, 0)))
829 ABORT(r);
830
831 // r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
832
833 _status = 0;
834 abort:
835 return (_status);
836 }
837
getaddr(nr_transport_addr * addrp)838 int NrSocket::getaddr(nr_transport_addr* addrp) {
839 ASSERT_ON_THREAD(ststhread_);
840 return nr_transport_addr_copy(addrp, &my_addr_);
841 }
842
843 // Close the socket so that the STS will detach and then kill it
close()844 void NrSocket::close() {
845 ASSERT_ON_THREAD(ststhread_);
846 mCondition = NS_BASE_STREAM_CLOSED;
847 cancel(NR_ASYNC_WAIT_READ);
848 cancel(NR_ASYNC_WAIT_WRITE);
849 }
850
connect(const nr_transport_addr * addr)851 int NrSocket::connect(const nr_transport_addr* addr) {
852 ASSERT_ON_THREAD(ststhread_);
853 int r, _status;
854 PRNetAddr naddr;
855 int32_t connect_status, getsockname_status;
856
857 if ((r = nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r);
858
859 if (!fd_) ABORT(R_EOD);
860
861 // Note: this just means we tried to connect, not that we
862 // are actually live.
863 connect_invoked_ = true;
864 connect_status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
865 if (connect_status != PR_SUCCESS) {
866 if (PR_GetError() != PR_IN_PROGRESS_ERROR) {
867 r_log(LOG_GENERIC, LOG_CRIT, "PR_Connect failed: %d", PR_GetError());
868 ABORT(R_IO_ERROR);
869 }
870 }
871
872 // If our local address is wildcard, then fill in the
873 // address now.
874 if (nr_transport_addr_is_wildcard(&my_addr_)) {
875 getsockname_status = PR_GetSockName(fd_, &naddr);
876 if (getsockname_status != PR_SUCCESS) {
877 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
878 ABORT(R_INTERNAL);
879 }
880
881 if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
882 ABORT(r);
883 }
884
885 // Now return the WOULDBLOCK if needed.
886 if (connect_status != PR_SUCCESS) {
887 ABORT(R_WOULDBLOCK);
888 }
889
890 _status = 0;
891 abort:
892 return (_status);
893 }
894
write(const void * msg,size_t len,size_t * written)895 int NrSocket::write(const void* msg, size_t len, size_t* written) {
896 ASSERT_ON_THREAD(ststhread_);
897 int _status;
898 int32_t status;
899
900 if (!connect_invoked_) ABORT(R_FAILED);
901
902 status = PR_Write(fd_, msg, len);
903 if (status < 0) {
904 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
905 r_log(LOG_GENERIC, LOG_INFO, "Error in write");
906 ABORT(R_IO_ERROR);
907 }
908
909 *written = status;
910
911 _status = 0;
912 abort:
913 return _status;
914 }
915
read(void * buf,size_t maxlen,size_t * len)916 int NrSocket::read(void* buf, size_t maxlen, size_t* len) {
917 ASSERT_ON_THREAD(ststhread_);
918 int _status;
919 int32_t status;
920
921 if (!connect_invoked_) ABORT(R_FAILED);
922
923 status = PR_Read(fd_, buf, maxlen);
924 if (status < 0) {
925 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
926 r_log(LOG_GENERIC, LOG_INFO, "Error in read");
927 ABORT(R_IO_ERROR);
928 }
929 if (status == 0) ABORT(R_EOD);
930
931 *len = (size_t)status; // Guaranteed to be > 0
932 _status = 0;
933 abort:
934 return (_status);
935 }
936
listen(int backlog)937 int NrSocket::listen(int backlog) {
938 ASSERT_ON_THREAD(ststhread_);
939 int32_t status;
940 int _status;
941
942 assert(fd_);
943 status = PR_Listen(fd_, backlog);
944 if (status != PR_SUCCESS) {
945 r_log(LOG_GENERIC, LOG_CRIT, "%s: PR_GetError() == %d", __FUNCTION__,
946 PR_GetError());
947 ABORT(R_IO_ERROR);
948 }
949
950 _status = 0;
951 abort:
952 return (_status);
953 }
954
accept(nr_transport_addr * addrp,nr_socket ** sockp)955 int NrSocket::accept(nr_transport_addr* addrp, nr_socket** sockp) {
956 ASSERT_ON_THREAD(ststhread_);
957 int _status, r;
958 PRStatus status;
959 PRFileDesc* prfd;
960 PRNetAddr nfrom;
961 NrSocket* sock = nullptr;
962 nsresult rv;
963 PRSocketOptionData opt_nonblock, opt_nodelay;
964 nsCOMPtr<nsISocketTransportService> stservice =
965 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
966
967 if (NS_FAILED(rv)) {
968 ABORT(R_INTERNAL);
969 }
970
971 if (!fd_) ABORT(R_EOD);
972
973 prfd = PR_Accept(fd_, &nfrom, PR_INTERVAL_NO_WAIT);
974
975 if (!prfd) {
976 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
977
978 ABORT(R_IO_ERROR);
979 }
980
981 sock = new NrSocket();
982
983 sock->fd_ = prfd;
984 nr_transport_addr_copy(&sock->my_addr_, &my_addr_);
985
986 if ((r = nr_praddr_to_transport_addr(&nfrom, addrp, my_addr_.protocol, 0)))
987 ABORT(r);
988
989 // Set nonblocking
990 opt_nonblock.option = PR_SockOpt_Nonblocking;
991 opt_nonblock.value.non_blocking = PR_TRUE;
992 status = PR_SetSocketOption(prfd, &opt_nonblock);
993 if (status != PR_SUCCESS) {
994 r_log(LOG_GENERIC, LOG_CRIT,
995 "Failed to make accepted socket nonblocking: %d", status);
996 ABORT(R_INTERNAL);
997 }
998 // Disable TCP Nagle
999 opt_nodelay.option = PR_SockOpt_NoDelay;
1000 opt_nodelay.value.no_delay = PR_TRUE;
1001 status = PR_SetSocketOption(prfd, &opt_nodelay);
1002 if (status != PR_SUCCESS) {
1003 r_log(LOG_GENERIC, LOG_WARNING,
1004 "Failed to set Nodelay on accepted socket: %d", status);
1005 }
1006
1007 // Should fail only with OOM
1008 if ((r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp)))
1009 ABORT(r);
1010
1011 // Remember our thread.
1012 sock->ststhread_ = do_QueryInterface(stservice, &rv);
1013 if (NS_FAILED(rv)) ABORT(R_INTERNAL);
1014
1015 // Finally, register with the STS
1016 rv = stservice->AttachSocket(prfd, sock);
1017 if (NS_FAILED(rv)) {
1018 ABORT(R_INTERNAL);
1019 }
1020
1021 sock->connect_invoked_ = true;
1022
1023 // Add a reference so that we can delete it in destroy()
1024 sock->AddRef();
1025 _status = 0;
1026 abort:
1027 if (_status) {
1028 delete sock;
1029 }
1030
1031 return (_status);
1032 }
1033
NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy,nsIUDPSocketInternal)1034 NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal)
1035
1036 nsresult NrUdpSocketIpcProxy::Init(const RefPtr<NrUdpSocketIpc>& socket) {
1037 nsresult rv;
1038 sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1039 if (NS_FAILED(rv)) {
1040 MOZ_ASSERT(false, "Failed to get STS thread");
1041 return rv;
1042 }
1043
1044 socket_ = socket;
1045 return NS_OK;
1046 }
1047
~NrUdpSocketIpcProxy()1048 NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy() {
1049 // Send our ref to STS to be released
1050 RUN_ON_THREAD(sts_thread_, mozilla::WrapRelease(socket_.forget()),
1051 NS_DISPATCH_NORMAL);
1052 }
1053
1054 // IUDPSocketInternal interfaces
1055 // callback while error happened in UDP socket operation
CallListenerError(const nsACString & message,const nsACString & filename,uint32_t line_number)1056 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString& message,
1057 const nsACString& filename,
1058 uint32_t line_number) {
1059 return socket_->CallListenerError(message, filename, line_number);
1060 }
1061
1062 // callback while receiving UDP packet
CallListenerReceivedData(const nsACString & host,uint16_t port,const nsTArray<uint8_t> & data)1063 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData(
1064 const nsACString& host, uint16_t port, const nsTArray<uint8_t>& data) {
1065 return socket_->CallListenerReceivedData(host, port, data);
1066 }
1067
1068 // callback while UDP socket is opened
CallListenerOpened()1069 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
1070 return socket_->CallListenerOpened();
1071 }
1072
1073 // callback while UDP socket is connected
CallListenerConnected()1074 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerConnected() {
1075 return socket_->CallListenerConnected();
1076 }
1077
1078 // callback while UDP socket is closed
CallListenerClosed()1079 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
1080 return socket_->CallListenerClosed();
1081 }
1082
1083 // NrUdpSocketIpc Implementation
NrUdpSocketIpc()1084 NrUdpSocketIpc::NrUdpSocketIpc()
1085 : NrSocketIpc(GetIOThreadAndAddUse_s()),
1086 monitor_("NrUdpSocketIpc"),
1087 err_(false),
1088 state_(NR_INIT) {}
1089
1090 NrUdpSocketIpc::~NrUdpSocketIpc() = default;
1091
Destroy()1092 void NrUdpSocketIpc::Destroy() {
1093 #if defined(MOZILLA_INTERNAL_API)
1094 // destroy_i also dispatches back to STS to call ReleaseUse, to avoid shutting
1095 // down the IO thread before close() runs.
1096 // We use a NonOwning runnable because our refcount has already gone to 0.
1097 io_thread_->Dispatch(
1098 NewNonOwningRunnableMethod(__func__, this, &NrUdpSocketIpc::destroy_i));
1099 #endif
1100 }
1101
1102 // IUDPSocketInternal interfaces
1103 // callback while error happened in UDP socket operation
CallListenerError(const nsACString & message,const nsACString & filename,uint32_t line_number)1104 NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString& message,
1105 const nsACString& filename,
1106 uint32_t line_number) {
1107 ASSERT_ON_THREAD(io_thread_);
1108
1109 r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d this=%p",
1110 message.BeginReading(), filename.BeginReading(), line_number,
1111 (void*)this);
1112
1113 ReentrantMonitorAutoEnter mon(monitor_);
1114 err_ = true;
1115 monitor_.NotifyAll();
1116
1117 return NS_OK;
1118 }
1119
1120 // callback while receiving UDP packet
CallListenerReceivedData(const nsACString & host,uint16_t port,const nsTArray<uint8_t> & data)1121 NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData(
1122 const nsACString& host, uint16_t port, const nsTArray<uint8_t>& data) {
1123 ASSERT_ON_THREAD(io_thread_);
1124
1125 PRNetAddr addr;
1126 memset(&addr, 0, sizeof(addr));
1127
1128 {
1129 ReentrantMonitorAutoEnter mon(monitor_);
1130
1131 if (PR_SUCCESS != PR_StringToNetAddr(host.BeginReading(), &addr)) {
1132 err_ = true;
1133 MOZ_ASSERT(false, "Failed to convert remote host to PRNetAddr");
1134 return NS_OK;
1135 }
1136
1137 // Use PR_IpAddrNull to avoid address being reset to 0.
1138 if (PR_SUCCESS !=
1139 PR_SetNetAddr(PR_IpAddrNull, addr.raw.family, port, &addr)) {
1140 err_ = true;
1141 MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
1142 return NS_OK;
1143 }
1144 }
1145
1146 auto buf = MakeUnique<MediaPacket>();
1147 buf->Copy(data.Elements(), data.Length());
1148 RefPtr<nr_udp_message> msg(new nr_udp_message(addr, std::move(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 = socket_child_->LocalPort();
1159
1160 nsAutoCString address(socket_child_->LocalAddress());
1161
1162 PRNetAddr praddr;
1163 if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, port, &praddr)) {
1164 err_ = true;
1165 MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
1166 return NS_OK;
1167 }
1168
1169 if (PR_SUCCESS != PR_StringToNetAddr(address.BeginReading(), &praddr)) {
1170 err_ = true;
1171 MOZ_ASSERT(false, "Failed to convert local host to PRNetAddr");
1172 return NS_OK;
1173 }
1174
1175 nr_transport_addr expected_addr;
1176 if (nr_transport_addr_copy(&expected_addr, &my_addr_)) {
1177 err_ = true;
1178 MOZ_ASSERT(false, "Failed to copy my_addr_");
1179 }
1180
1181 if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) {
1182 err_ = true;
1183 MOZ_ASSERT(false, "Failed to copy local host to my_addr_");
1184 }
1185
1186 if (!nr_transport_addr_is_wildcard(&expected_addr) &&
1187 nr_transport_addr_cmp(&expected_addr, &my_addr_,
1188 NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
1189 err_ = true;
1190 MOZ_ASSERT(false, "Address of opened socket is not expected");
1191 }
1192
1193 return NS_OK;
1194 }
1195
1196 // callback while UDP socket is opened
CallListenerOpened()1197 NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() {
1198 ASSERT_ON_THREAD(io_thread_);
1199 ReentrantMonitorAutoEnter mon(monitor_);
1200
1201 r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket opened this=%p", (void*)this);
1202 nsresult rv = SetAddress();
1203 if (NS_FAILED(rv)) {
1204 return rv;
1205 }
1206
1207 mon.NotifyAll();
1208
1209 return NS_OK;
1210 }
1211
1212 // callback while UDP socket is connected
CallListenerConnected()1213 NS_IMETHODIMP NrUdpSocketIpc::CallListenerConnected() {
1214 ASSERT_ON_THREAD(io_thread_);
1215
1216 ReentrantMonitorAutoEnter mon(monitor_);
1217
1218 r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket connected this=%p", (void*)this);
1219 MOZ_ASSERT(state_ == NR_CONNECTED);
1220
1221 nsresult rv = SetAddress();
1222 if (NS_FAILED(rv)) {
1223 mon.NotifyAll();
1224 return rv;
1225 }
1226
1227 r_log(LOG_GENERIC, LOG_INFO, "Exit UDP socket connected");
1228 mon.NotifyAll();
1229
1230 return NS_OK;
1231 }
1232
1233 // callback while UDP socket is closed
CallListenerClosed()1234 NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() {
1235 ASSERT_ON_THREAD(io_thread_);
1236
1237 ReentrantMonitorAutoEnter mon(monitor_);
1238
1239 r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket closed this=%p", (void*)this);
1240 MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
1241 state_ = NR_CLOSED;
1242
1243 return NS_OK;
1244 }
1245
1246 //
1247 // NrSocketBase methods.
1248 //
create(nr_transport_addr * addr)1249 int NrUdpSocketIpc::create(nr_transport_addr* addr) {
1250 ASSERT_ON_THREAD(sts_thread_);
1251
1252 int r, _status;
1253 nsresult rv;
1254 int32_t port;
1255 nsCString host;
1256
1257 ReentrantMonitorAutoEnter mon(monitor_);
1258
1259 if (state_ != NR_INIT) {
1260 ABORT(R_INTERNAL);
1261 }
1262
1263 sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1264 if (NS_FAILED(rv)) {
1265 MOZ_ASSERT(false, "Failed to get STS thread");
1266 ABORT(R_INTERNAL);
1267 }
1268
1269 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1270 ABORT(r);
1271 }
1272
1273 // wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid
1274 if ((r = nr_transport_addr_copy(&my_addr_, addr))) {
1275 ABORT(r);
1276 }
1277
1278 state_ = NR_CONNECTING;
1279
1280 MOZ_ASSERT(io_thread_);
1281 RUN_ON_THREAD(io_thread_,
1282 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1283 &NrUdpSocketIpc::create_i, host,
1284 static_cast<uint16_t>(port)),
1285 NS_DISPATCH_NORMAL);
1286
1287 // Wait until socket creation complete.
1288 mon.Wait();
1289
1290 if (err_) {
1291 close();
1292 ABORT(R_INTERNAL);
1293 }
1294
1295 state_ = NR_CONNECTED;
1296
1297 _status = 0;
1298 abort:
1299 return (_status);
1300 }
1301
sendto(const void * msg,size_t len,int flags,const nr_transport_addr * to)1302 int NrUdpSocketIpc::sendto(const void* msg, size_t len, int flags,
1303 const nr_transport_addr* to) {
1304 ASSERT_ON_THREAD(sts_thread_);
1305
1306 ReentrantMonitorAutoEnter mon(monitor_);
1307
1308 // If send err happened before, simply return the error.
1309 if (err_) {
1310 return R_IO_ERROR;
1311 }
1312
1313 if (state_ != NR_CONNECTED) {
1314 return R_INTERNAL;
1315 }
1316
1317 int r;
1318 net::NetAddr addr;
1319 if ((r = nr_transport_addr_to_netaddr(to, &addr))) {
1320 return r;
1321 }
1322
1323 if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
1324 return R_WOULDBLOCK;
1325 }
1326
1327 UniquePtr<MediaPacket> buf(new MediaPacket);
1328 buf->Copy(static_cast<const uint8_t*>(msg), len);
1329
1330 RUN_ON_THREAD(
1331 io_thread_,
1332 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1333 &NrUdpSocketIpc::sendto_i, addr, std::move(buf)),
1334 NS_DISPATCH_NORMAL);
1335 return 0;
1336 }
1337
close()1338 void NrUdpSocketIpc::close() {
1339 r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::close()");
1340
1341 ASSERT_ON_THREAD(sts_thread_);
1342
1343 ReentrantMonitorAutoEnter mon(monitor_);
1344 state_ = NR_CLOSING;
1345
1346 RUN_ON_THREAD(io_thread_,
1347 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1348 &NrUdpSocketIpc::close_i),
1349 NS_DISPATCH_NORMAL);
1350
1351 // remove all enqueued messages
1352 std::queue<RefPtr<nr_udp_message>> empty;
1353 std::swap(received_msgs_, empty);
1354 }
1355
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)1356 int NrUdpSocketIpc::recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
1357 nr_transport_addr* from) {
1358 ASSERT_ON_THREAD(sts_thread_);
1359
1360 ReentrantMonitorAutoEnter mon(monitor_);
1361
1362 int r, _status;
1363 uint32_t consumed_len;
1364
1365 *len = 0;
1366
1367 if (state_ != NR_CONNECTED) {
1368 ABORT(R_INTERNAL);
1369 }
1370
1371 if (received_msgs_.empty()) {
1372 ABORT(R_WOULDBLOCK);
1373 }
1374
1375 {
1376 RefPtr<nr_udp_message> msg(received_msgs_.front());
1377
1378 received_msgs_.pop();
1379
1380 if ((r = nr_praddr_to_transport_addr(&msg->from, from, IPPROTO_UDP, 0))) {
1381 err_ = true;
1382 MOZ_ASSERT(false, "Get bogus address for received UDP packet");
1383 ABORT(r);
1384 }
1385
1386 consumed_len = std::min(maxlen, msg->data->len());
1387 if (consumed_len < msg->data->len()) {
1388 r_log(LOG_GENERIC, LOG_DEBUG,
1389 "Partial received UDP packet will be discard");
1390 }
1391
1392 memcpy(buf, msg->data->data(), consumed_len);
1393 *len = consumed_len;
1394 }
1395
1396 _status = 0;
1397 abort:
1398 return (_status);
1399 }
1400
getaddr(nr_transport_addr * addrp)1401 int NrUdpSocketIpc::getaddr(nr_transport_addr* addrp) {
1402 ASSERT_ON_THREAD(sts_thread_);
1403
1404 ReentrantMonitorAutoEnter mon(monitor_);
1405
1406 return nr_transport_addr_copy(addrp, &my_addr_);
1407 }
1408
connect(const nr_transport_addr * addr)1409 int NrUdpSocketIpc::connect(const nr_transport_addr* addr) {
1410 int r, _status;
1411 int32_t port;
1412 nsCString host;
1413
1414 ReentrantMonitorAutoEnter mon(monitor_);
1415 r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::connect(%s) this=%p",
1416 addr->as_string, (void*)this);
1417
1418 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1419 ABORT(r);
1420 }
1421
1422 RUN_ON_THREAD(io_thread_,
1423 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1424 &NrUdpSocketIpc::connect_i, host,
1425 static_cast<uint16_t>(port)),
1426 NS_DISPATCH_NORMAL);
1427
1428 // Wait until connect() completes.
1429 mon.Wait();
1430
1431 r_log(LOG_GENERIC, LOG_DEBUG,
1432 "NrUdpSocketIpc::connect this=%p completed err_ = %s", (void*)this,
1433 err_ ? "true" : "false");
1434
1435 if (err_) {
1436 ABORT(R_INTERNAL);
1437 }
1438
1439 _status = 0;
1440 abort:
1441 return _status;
1442 }
1443
write(const void * msg,size_t len,size_t * written)1444 int NrUdpSocketIpc::write(const void* msg, size_t len, size_t* written) {
1445 MOZ_ASSERT(false);
1446 return R_INTERNAL;
1447 }
1448
read(void * buf,size_t maxlen,size_t * len)1449 int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t* len) {
1450 MOZ_ASSERT(false);
1451 return R_INTERNAL;
1452 }
1453
listen(int backlog)1454 int NrUdpSocketIpc::listen(int backlog) {
1455 MOZ_ASSERT(false);
1456 return R_INTERNAL;
1457 }
1458
accept(nr_transport_addr * addrp,nr_socket ** sockp)1459 int NrUdpSocketIpc::accept(nr_transport_addr* addrp, nr_socket** sockp) {
1460 MOZ_ASSERT(false);
1461 return R_INTERNAL;
1462 }
1463
1464 // IO thread executors
create_i(const nsACString & host,const uint16_t port)1465 void NrUdpSocketIpc::create_i(const nsACString& host, const uint16_t port) {
1466 ASSERT_ON_THREAD(io_thread_);
1467
1468 uint32_t minBuffSize = 0;
1469 RefPtr<dom::UDPSocketChild> socketChild = new dom::UDPSocketChild();
1470
1471 // This can spin the event loop; don't do that with the monitor held
1472 socketChild->SetBackgroundSpinsEvents();
1473
1474 ReentrantMonitorAutoEnter mon(monitor_);
1475 if (!socket_child_) {
1476 socket_child_ = socketChild;
1477 socket_child_->SetFilterName(
1478 nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX));
1479 } else {
1480 socketChild = nullptr;
1481 }
1482
1483 RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
1484 nsresult rv = proxy->Init(this);
1485 if (NS_FAILED(rv)) {
1486 err_ = true;
1487 mon.NotifyAll();
1488 return;
1489 }
1490
1491 #ifdef XP_WIN
1492 if (!mozilla::IsWin8OrLater()) {
1493 // Increase default receive and send buffer size on <= Win7 to be able to
1494 // receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K size)
1495 // stream without losing packets.
1496 // Manual testing showed that 100K buffer size was not enough and the
1497 // packet loss dis-appeared with 256K buffer size.
1498 // See bug 1252769 for future improvements of this.
1499 minBuffSize = 256 * 1024;
1500 }
1501 #endif
1502 // XXX bug 1126232 - don't use null Principal!
1503 if (NS_FAILED(socket_child_->Bind(proxy, nullptr, host, port,
1504 /* addressReuse = */ false,
1505 /* loopback = */ false,
1506 /* recv buffer size */ minBuffSize,
1507 /* send buffer size */ minBuffSize,
1508 /* mainThreadEventTarget */ nullptr))) {
1509 err_ = true;
1510 MOZ_ASSERT(false, "Failed to create UDP socket");
1511 mon.NotifyAll();
1512 return;
1513 }
1514 }
1515
connect_i(const nsACString & host,const uint16_t port)1516 void NrUdpSocketIpc::connect_i(const nsACString& host, const uint16_t port) {
1517 ASSERT_ON_THREAD(io_thread_);
1518 nsresult rv;
1519 ReentrantMonitorAutoEnter mon(monitor_);
1520
1521 RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
1522 rv = proxy->Init(this);
1523 if (NS_FAILED(rv)) {
1524 err_ = true;
1525 mon.NotifyAll();
1526 return;
1527 }
1528
1529 socket_child_->Connect(proxy, host, port);
1530 }
1531
sendto_i(const net::NetAddr & addr,UniquePtr<MediaPacket> buf)1532 void NrUdpSocketIpc::sendto_i(const net::NetAddr& addr,
1533 UniquePtr<MediaPacket> buf) {
1534 ASSERT_ON_THREAD(io_thread_);
1535
1536 ReentrantMonitorAutoEnter mon(monitor_);
1537
1538 if (!socket_child_) {
1539 MOZ_ASSERT(false);
1540 err_ = true;
1541 return;
1542 }
1543 if (NS_FAILED(
1544 socket_child_->SendWithAddress(&addr, buf->data(), buf->len()))) {
1545 err_ = true;
1546 }
1547 }
1548
close_i()1549 void NrUdpSocketIpc::close_i() {
1550 ASSERT_ON_THREAD(io_thread_);
1551
1552 if (socket_child_) {
1553 socket_child_->Close();
1554 socket_child_ = nullptr;
1555 }
1556 }
1557
1558 #if defined(MOZILLA_INTERNAL_API)
1559
ReleaseIOThread_s()1560 static void ReleaseIOThread_s() { sThread->ReleaseUse(); }
1561
destroy_i()1562 void NrUdpSocketIpc::destroy_i() {
1563 close_i();
1564
1565 RUN_ON_THREAD(sts_thread_, WrapRunnableNM(&ReleaseIOThread_s),
1566 NS_DISPATCH_NORMAL);
1567 }
1568 #endif
1569
recv_callback_s(RefPtr<nr_udp_message> msg)1570 void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
1571 ASSERT_ON_THREAD(sts_thread_);
1572
1573 {
1574 ReentrantMonitorAutoEnter mon(monitor_);
1575 if (state_ != NR_CONNECTED) {
1576 return;
1577 }
1578 }
1579
1580 // enqueue received message
1581 received_msgs_.push(msg);
1582
1583 if ((poll_flags() & PR_POLL_READ)) {
1584 fire_callback(NR_ASYNC_WAIT_READ);
1585 }
1586 }
1587
1588 } // namespace mozilla
1589
1590 using namespace mozilla;
1591
1592 // Bridge to the nr_socket interface
1593 static int nr_socket_local_destroy(void** objp);
1594 static int nr_socket_local_sendto(void* obj, const void* msg, size_t len,
1595 int flags, const nr_transport_addr* to);
1596 static int nr_socket_local_recvfrom(void* obj, void* restrict buf,
1597 size_t maxlen, size_t* len, int flags,
1598 nr_transport_addr* from);
1599 static int nr_socket_local_getfd(void* obj, NR_SOCKET* fd);
1600 static int nr_socket_local_getaddr(void* obj, nr_transport_addr* addrp);
1601 static int nr_socket_local_close(void* obj);
1602 static int nr_socket_local_connect(void* obj, const nr_transport_addr* addr);
1603 static int nr_socket_local_write(void* obj, const void* msg, size_t len,
1604 size_t* written);
1605 static int nr_socket_local_read(void* obj, void* restrict buf, size_t maxlen,
1606 size_t* len);
1607 static int nr_socket_local_listen(void* obj, int backlog);
1608 static int nr_socket_local_accept(void* obj, nr_transport_addr* addrp,
1609 nr_socket** sockp);
1610
1611 static nr_socket_vtbl nr_socket_local_vtbl = {2,
1612 nr_socket_local_destroy,
1613 nr_socket_local_sendto,
1614 nr_socket_local_recvfrom,
1615 nr_socket_local_getfd,
1616 nr_socket_local_getaddr,
1617 nr_socket_local_connect,
1618 nr_socket_local_write,
1619 nr_socket_local_read,
1620 nr_socket_local_close,
1621 nr_socket_local_listen,
1622 nr_socket_local_accept};
1623
1624 /* static */
CreateSocket(nr_transport_addr * addr,RefPtr<NrSocketBase> * sock,const std::shared_ptr<NrSocketProxyConfig> & config)1625 int NrSocketBase::CreateSocket(
1626 nr_transport_addr* addr, RefPtr<NrSocketBase>* sock,
1627 const std::shared_ptr<NrSocketProxyConfig>& config) {
1628 int r, _status;
1629
1630 if (IsForbiddenAddress(addr)) {
1631 ABORT(R_REJECTED);
1632 }
1633
1634 if (config && config->GetForceProxy() && addr->protocol == IPPROTO_UDP) {
1635 ABORT(R_REJECTED);
1636 }
1637
1638 // create IPC bridge for content process
1639 if (XRE_IsParentProcess()) {
1640 // TODO: Make NrTcpSocket work on the parent process
1641 *sock = new NrSocket();
1642 } else if (XRE_IsSocketProcess()) {
1643 if (addr->protocol == IPPROTO_TCP) {
1644 *sock = new NrTcpSocket(config);
1645 } else {
1646 *sock = new NrSocket();
1647 }
1648 } else {
1649 if (addr->protocol == IPPROTO_TCP) {
1650 *sock = new NrTcpSocket(config);
1651 } else {
1652 *sock = new NrUdpSocketIpc();
1653 }
1654 }
1655
1656 r = (*sock)->create(addr);
1657 if (r) ABORT(r);
1658
1659 _status = 0;
1660 abort:
1661 if (_status) {
1662 *sock = nullptr;
1663 }
1664 return _status;
1665 }
1666
1667 // static
IsForbiddenAddress(nr_transport_addr * addr)1668 bool NrSocketBase::IsForbiddenAddress(nr_transport_addr* addr) {
1669 int r, port;
1670
1671 r = nr_transport_addr_get_port(addr, &port);
1672 if (r) {
1673 return true;
1674 }
1675
1676 // allow auto assigned ports
1677 if (port != 0) {
1678 // Don't need to check an override scheme
1679 nsresult rv = NS_CheckPortSafety(port, nullptr);
1680 if (NS_FAILED(rv)) {
1681 return true;
1682 }
1683 }
1684
1685 return false;
1686 }
1687
nr_socket_local_destroy(void ** objp)1688 static int nr_socket_local_destroy(void** objp) {
1689 if (!objp || !*objp) return 0;
1690
1691 NrSocketBase* sock = static_cast<NrSocketBase*>(*objp);
1692 *objp = nullptr;
1693
1694 sock->close(); // Signal STS that we want not to listen
1695 sock->Release(); // Decrement the ref count
1696
1697 return 0;
1698 }
1699
nr_socket_local_sendto(void * obj,const void * msg,size_t len,int flags,const nr_transport_addr * addr)1700 static int nr_socket_local_sendto(void* obj, const void* msg, size_t len,
1701 int flags, const nr_transport_addr* addr) {
1702 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1703
1704 return sock->sendto(msg, len, flags, addr);
1705 }
1706
nr_socket_local_recvfrom(void * obj,void * restrict buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * addr)1707 static int nr_socket_local_recvfrom(void* obj, void* restrict buf,
1708 size_t maxlen, size_t* len, int flags,
1709 nr_transport_addr* addr) {
1710 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1711
1712 return sock->recvfrom(buf, maxlen, len, flags, addr);
1713 }
1714
nr_socket_local_getfd(void * obj,NR_SOCKET * fd)1715 static int nr_socket_local_getfd(void* obj, NR_SOCKET* fd) {
1716 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1717
1718 *fd = sock;
1719
1720 return 0;
1721 }
1722
nr_socket_local_getaddr(void * obj,nr_transport_addr * addrp)1723 static int nr_socket_local_getaddr(void* obj, nr_transport_addr* addrp) {
1724 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1725
1726 return sock->getaddr(addrp);
1727 }
1728
nr_socket_local_close(void * obj)1729 static int nr_socket_local_close(void* obj) {
1730 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1731
1732 sock->close();
1733
1734 return 0;
1735 }
1736
nr_socket_local_write(void * obj,const void * msg,size_t len,size_t * written)1737 static int nr_socket_local_write(void* obj, const void* msg, size_t len,
1738 size_t* written) {
1739 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1740
1741 return sock->write(msg, len, written);
1742 }
1743
nr_socket_local_read(void * obj,void * restrict buf,size_t maxlen,size_t * len)1744 static int nr_socket_local_read(void* obj, void* restrict buf, size_t maxlen,
1745 size_t* len) {
1746 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1747
1748 return sock->read(buf, maxlen, len);
1749 }
1750
nr_socket_local_connect(void * obj,const nr_transport_addr * addr)1751 static int nr_socket_local_connect(void* obj, const nr_transport_addr* addr) {
1752 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1753
1754 return sock->connect(addr);
1755 }
1756
nr_socket_local_listen(void * obj,int backlog)1757 static int nr_socket_local_listen(void* obj, int backlog) {
1758 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1759
1760 return sock->listen(backlog);
1761 }
1762
nr_socket_local_accept(void * obj,nr_transport_addr * addrp,nr_socket ** sockp)1763 static int nr_socket_local_accept(void* obj, nr_transport_addr* addrp,
1764 nr_socket** sockp) {
1765 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1766
1767 return sock->accept(addrp, sockp);
1768 }
1769
1770 // Implement async api
NR_async_wait(NR_SOCKET sock,int how,NR_async_cb cb,void * cb_arg,char * function,int line)1771 int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb, void* cb_arg,
1772 char* function, int line) {
1773 NrSocketBase* s = static_cast<NrSocketBase*>(sock);
1774
1775 return s->async_wait(how, cb, cb_arg, function, line);
1776 }
1777
NR_async_cancel(NR_SOCKET sock,int how)1778 int NR_async_cancel(NR_SOCKET sock, int how) {
1779 NrSocketBase* s = static_cast<NrSocketBase*>(sock);
1780
1781 return s->cancel(how);
1782 }
1783
vtbl()1784 nr_socket_vtbl* NrSocketBase::vtbl() { return &nr_socket_local_vtbl; }
1785