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::ShutdownLoaders);
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(NS_LITERAL_CSTRING("mtransport"));
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_ISUPPORTS0(NrSocket)338 NS_IMPL_ISUPPORTS0(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(nr_transport_addr * addr,PRNetAddr * naddr)382 static int nr_transport_addr_to_praddr(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(nr_transport_addr * addr,net::NetAddr * naddr)447 static int nr_transport_addr_to_netaddr(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(nr_transport_addr * addr,nsACString * host,int32_t * port)533 int nr_transport_addr_get_addrstring_and_port(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: Add TLS layer with nsISocketProviderService?
632 if (my_addr_.tls_host[0] != '\0') ABORT(R_INTERNAL);
633
634 if (!(fd_ = PR_OpenTCPSocket(naddr.raw.family))) {
635 r_log(LOG_GENERIC, LOG_CRIT,
636 "Couldn't create TCP socket, "
637 "family=%d, err=%d",
638 naddr.raw.family, PR_GetError());
639 ABORT(R_INTERNAL);
640 }
641 // Set ReuseAddr for TCP sockets to enable having several
642 // sockets bound to same local IP and port
643 PRSocketOptionData opt_reuseaddr;
644 opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
645 opt_reuseaddr.value.reuse_addr = PR_TRUE;
646 status = PR_SetSocketOption(fd_, &opt_reuseaddr);
647 if (status != PR_SUCCESS) {
648 r_log(LOG_GENERIC, LOG_CRIT,
649 "Couldn't set reuse addr socket option: %d", status);
650 ABORT(R_INTERNAL);
651 }
652 // And also set ReusePort for platforms supporting this socket option
653 PRSocketOptionData opt_reuseport;
654 opt_reuseport.option = PR_SockOpt_Reuseport;
655 opt_reuseport.value.reuse_port = PR_TRUE;
656 status = PR_SetSocketOption(fd_, &opt_reuseport);
657 if (status != PR_SUCCESS) {
658 if (PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
659 r_log(LOG_GENERIC, LOG_CRIT,
660 "Couldn't set reuse port socket option: %d", status);
661 ABORT(R_INTERNAL);
662 }
663 }
664 // Try to speedup packet delivery by disabling TCP Nagle
665 PRSocketOptionData opt_nodelay;
666 opt_nodelay.option = PR_SockOpt_NoDelay;
667 opt_nodelay.value.no_delay = PR_TRUE;
668 status = PR_SetSocketOption(fd_, &opt_nodelay);
669 if (status != PR_SUCCESS) {
670 r_log(LOG_GENERIC, LOG_WARNING,
671 "Couldn't set Nodelay socket option: %d", status);
672 }
673 break;
674 default:
675 ABORT(R_INTERNAL);
676 }
677
678 status = PR_Bind(fd_, &naddr);
679 if (status != PR_SUCCESS) {
680 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't bind socket to address %s",
681 addr->as_string);
682 ABORT(R_INTERNAL);
683 }
684
685 r_log(LOG_GENERIC, LOG_DEBUG, "Creating socket %p with addr %s", fd_,
686 addr->as_string);
687 nr_transport_addr_copy(&my_addr_, addr);
688
689 /* If we have a wildcard port, patch up the addr */
690 if (nr_transport_addr_is_wildcard(addr)) {
691 status = PR_GetSockName(fd_, &naddr);
692 if (status != PR_SUCCESS) {
693 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
694 ABORT(R_INTERNAL);
695 }
696
697 if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
698 ABORT(r);
699 }
700
701 // Set nonblocking
702 PRSocketOptionData opt_nonblock;
703 opt_nonblock.option = PR_SockOpt_Nonblocking;
704 opt_nonblock.value.non_blocking = PR_TRUE;
705 status = PR_SetSocketOption(fd_, &opt_nonblock);
706 if (status != PR_SUCCESS) {
707 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking");
708 ABORT(R_INTERNAL);
709 }
710
711 // Remember our thread.
712 ststhread_ = do_QueryInterface(stservice, &rv);
713 if (!NS_SUCCEEDED(rv)) ABORT(R_INTERNAL);
714
715 // Finally, register with the STS
716 rv = stservice->AttachSocket(fd_, this);
717 if (!NS_SUCCEEDED(rv)) {
718 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't attach socket to STS, rv=%u",
719 static_cast<unsigned>(rv));
720 ABORT(R_INTERNAL);
721 }
722
723 _status = 0;
724
725 abort:
726 return (_status);
727 }
728
ShouldDrop(size_t len)729 static int ShouldDrop(size_t len) {
730 // Global rate limiting for stun requests, to mitigate the ice hammer DoS
731 // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc)
732
733 // Tolerate rate of 8k/sec, for one second.
734 static SimpleTokenBucket burst(16384 * 1, 16384);
735 // Tolerate rate of 7.2k/sec over twenty seconds.
736 static SimpleTokenBucket sustained(7372 * 20, 7372);
737
738 // Check number of tokens in each bucket.
739 if (burst.getTokens(UINT32_MAX) < len) {
740 r_log(LOG_GENERIC, LOG_ERR,
741 "Short term global rate limit for STUN requests exceeded.");
742 #ifdef MOZILLA_INTERNAL_API
743 nr_socket_short_term_violation_time = TimeStamp::Now();
744 #endif
745
746 // Bug 1013007
747 #if !EARLY_BETA_OR_EARLIER
748 return R_WOULDBLOCK;
749 #else
750 MOZ_ASSERT(false,
751 "Short term global rate limit for STUN requests exceeded. Go "
752 "bug bcampen@mozilla.com if you weren't intentionally "
753 "spamming ICE candidates, or don't know what that means.");
754 #endif
755 }
756
757 if (sustained.getTokens(UINT32_MAX) < len) {
758 r_log(LOG_GENERIC, LOG_ERR,
759 "Long term global rate limit for STUN requests exceeded.");
760 #ifdef MOZILLA_INTERNAL_API
761 nr_socket_long_term_violation_time = TimeStamp::Now();
762 #endif
763 // Bug 1013007
764 #if !EARLY_BETA_OR_EARLIER
765 return R_WOULDBLOCK;
766 #else
767 MOZ_ASSERT(false,
768 "Long term global rate limit for STUN requests exceeded. Go "
769 "bug bcampen@mozilla.com if you weren't intentionally "
770 "spamming ICE candidates, or don't know what that means.");
771 #endif
772 }
773
774 // Take len tokens from both buckets.
775 // (not threadsafe, but no problem since this is only called from STS)
776 burst.getTokens(len);
777 sustained.getTokens(len);
778 return 0;
779 }
780
781 // This should be called on the STS thread.
sendto(const void * msg,size_t len,int flags,nr_transport_addr * to)782 int NrSocket::sendto(const void* msg, size_t len, int flags,
783 nr_transport_addr* to) {
784 ASSERT_ON_THREAD(ststhread_);
785 int r, _status;
786 PRNetAddr naddr;
787 int32_t status;
788
789 if ((r = nr_transport_addr_to_praddr(to, &naddr))) ABORT(r);
790
791 if (fd_ == nullptr) ABORT(R_EOD);
792
793 if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
794 ABORT(R_WOULDBLOCK);
795 }
796
797 // TODO: Convert flags?
798 status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT);
799 if (status < 0 || (size_t)status != len) {
800 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
801
802 r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s: %d", to->as_string,
803 PR_GetError());
804 ABORT(R_IO_ERROR);
805 }
806
807 _status = 0;
808 abort:
809 return (_status);
810 }
811
recvfrom(void * buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * from)812 int NrSocket::recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
813 nr_transport_addr* from) {
814 ASSERT_ON_THREAD(ststhread_);
815 int r, _status;
816 PRNetAddr nfrom;
817 int32_t status;
818
819 status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
820 if (status <= 0) {
821 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
822 r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError());
823 ABORT(R_IO_ERROR);
824 }
825 *len = status;
826
827 if ((r = nr_praddr_to_transport_addr(&nfrom, from, my_addr_.protocol, 0)))
828 ABORT(r);
829
830 // r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
831
832 _status = 0;
833 abort:
834 return (_status);
835 }
836
getaddr(nr_transport_addr * addrp)837 int NrSocket::getaddr(nr_transport_addr* addrp) {
838 ASSERT_ON_THREAD(ststhread_);
839 return nr_transport_addr_copy(addrp, &my_addr_);
840 }
841
842 // Close the socket so that the STS will detach and then kill it
close()843 void NrSocket::close() {
844 ASSERT_ON_THREAD(ststhread_);
845 mCondition = NS_BASE_STREAM_CLOSED;
846 cancel(NR_ASYNC_WAIT_READ);
847 cancel(NR_ASYNC_WAIT_WRITE);
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) {
866 r_log(LOG_GENERIC, LOG_CRIT, "PR_Connect failed: %d", PR_GetError());
867 ABORT(R_IO_ERROR);
868 }
869 }
870
871 // If our local address is wildcard, then fill in the
872 // address now.
873 if (nr_transport_addr_is_wildcard(&my_addr_)) {
874 getsockname_status = PR_GetSockName(fd_, &naddr);
875 if (getsockname_status != PR_SUCCESS) {
876 r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
877 ABORT(R_INTERNAL);
878 }
879
880 if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
881 ABORT(r);
882 }
883
884 // Now return the WOULDBLOCK if needed.
885 if (connect_status != PR_SUCCESS) {
886 ABORT(R_WOULDBLOCK);
887 }
888
889 _status = 0;
890 abort:
891 return (_status);
892 }
893
write(const void * msg,size_t len,size_t * written)894 int NrSocket::write(const void* msg, size_t len, size_t* written) {
895 ASSERT_ON_THREAD(ststhread_);
896 int _status;
897 int32_t status;
898
899 if (!connect_invoked_) ABORT(R_FAILED);
900
901 status = PR_Write(fd_, msg, len);
902 if (status < 0) {
903 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
904 r_log(LOG_GENERIC, LOG_INFO, "Error in write");
905 ABORT(R_IO_ERROR);
906 }
907
908 *written = status;
909
910 _status = 0;
911 abort:
912 return _status;
913 }
914
read(void * buf,size_t maxlen,size_t * len)915 int NrSocket::read(void* buf, size_t maxlen, size_t* len) {
916 ASSERT_ON_THREAD(ststhread_);
917 int _status;
918 int32_t status;
919
920 if (!connect_invoked_) ABORT(R_FAILED);
921
922 status = PR_Read(fd_, buf, maxlen);
923 if (status < 0) {
924 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
925 r_log(LOG_GENERIC, LOG_INFO, "Error in read");
926 ABORT(R_IO_ERROR);
927 }
928 if (status == 0) ABORT(R_EOD);
929
930 *len = (size_t)status; // Guaranteed to be > 0
931 _status = 0;
932 abort:
933 return (_status);
934 }
935
listen(int backlog)936 int NrSocket::listen(int backlog) {
937 ASSERT_ON_THREAD(ststhread_);
938 int32_t status;
939 int _status;
940
941 assert(fd_);
942 status = PR_Listen(fd_, backlog);
943 if (status != PR_SUCCESS) {
944 r_log(LOG_GENERIC, LOG_CRIT, "%s: PR_GetError() == %d", __FUNCTION__,
945 PR_GetError());
946 ABORT(R_IO_ERROR);
947 }
948
949 _status = 0;
950 abort:
951 return (_status);
952 }
953
accept(nr_transport_addr * addrp,nr_socket ** sockp)954 int NrSocket::accept(nr_transport_addr* addrp, nr_socket** sockp) {
955 ASSERT_ON_THREAD(ststhread_);
956 int _status, r;
957 PRStatus status;
958 PRFileDesc* prfd;
959 PRNetAddr nfrom;
960 NrSocket* sock = nullptr;
961 nsresult rv;
962 PRSocketOptionData opt_nonblock, opt_nodelay;
963 nsCOMPtr<nsISocketTransportService> stservice =
964 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
965
966 if (NS_FAILED(rv)) {
967 ABORT(R_INTERNAL);
968 }
969
970 if (!fd_) ABORT(R_EOD);
971
972 prfd = PR_Accept(fd_, &nfrom, PR_INTERVAL_NO_WAIT);
973
974 if (!prfd) {
975 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
976
977 ABORT(R_IO_ERROR);
978 }
979
980 sock = new NrSocket();
981
982 sock->fd_ = prfd;
983 nr_transport_addr_copy(&sock->my_addr_, &my_addr_);
984
985 if ((r = nr_praddr_to_transport_addr(&nfrom, addrp, my_addr_.protocol, 0)))
986 ABORT(r);
987
988 // Set nonblocking
989 opt_nonblock.option = PR_SockOpt_Nonblocking;
990 opt_nonblock.value.non_blocking = PR_TRUE;
991 status = PR_SetSocketOption(prfd, &opt_nonblock);
992 if (status != PR_SUCCESS) {
993 r_log(LOG_GENERIC, LOG_CRIT,
994 "Failed to make accepted socket nonblocking: %d", status);
995 ABORT(R_INTERNAL);
996 }
997 // Disable TCP Nagle
998 opt_nodelay.option = PR_SockOpt_NoDelay;
999 opt_nodelay.value.no_delay = PR_TRUE;
1000 status = PR_SetSocketOption(prfd, &opt_nodelay);
1001 if (status != PR_SUCCESS) {
1002 r_log(LOG_GENERIC, LOG_WARNING,
1003 "Failed to set Nodelay on accepted socket: %d", status);
1004 }
1005
1006 // Should fail only with OOM
1007 if ((r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp)))
1008 ABORT(r);
1009
1010 // Remember our thread.
1011 sock->ststhread_ = do_QueryInterface(stservice, &rv);
1012 if (NS_FAILED(rv)) ABORT(R_INTERNAL);
1013
1014 // Finally, register with the STS
1015 rv = stservice->AttachSocket(prfd, sock);
1016 if (NS_FAILED(rv)) {
1017 ABORT(R_INTERNAL);
1018 }
1019
1020 sock->connect_invoked_ = true;
1021
1022 // Add a reference so that we can delete it in destroy()
1023 sock->AddRef();
1024 _status = 0;
1025 abort:
1026 if (_status) {
1027 delete sock;
1028 }
1029
1030 return (_status);
1031 }
1032
NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy,nsIUDPSocketInternal)1033 NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal)
1034
1035 nsresult NrUdpSocketIpcProxy::Init(const RefPtr<NrUdpSocketIpc>& socket) {
1036 nsresult rv;
1037 sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1038 if (NS_FAILED(rv)) {
1039 MOZ_ASSERT(false, "Failed to get STS thread");
1040 return rv;
1041 }
1042
1043 socket_ = socket;
1044 return NS_OK;
1045 }
1046
~NrUdpSocketIpcProxy()1047 NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy() {
1048 // Send our ref to STS to be released
1049 RUN_ON_THREAD(sts_thread_, mozilla::WrapRelease(socket_.forget()),
1050 NS_DISPATCH_NORMAL);
1051 }
1052
1053 // IUDPSocketInternal interfaces
1054 // callback while error happened in UDP socket operation
CallListenerError(const nsACString & message,const nsACString & filename,uint32_t line_number)1055 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString& message,
1056 const nsACString& filename,
1057 uint32_t line_number) {
1058 return socket_->CallListenerError(message, filename, line_number);
1059 }
1060
1061 // callback while receiving UDP packet
CallListenerReceivedData(const nsACString & host,uint16_t port,const nsTArray<uint8_t> & data)1062 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData(
1063 const nsACString& host, uint16_t port, const nsTArray<uint8_t>& data) {
1064 return socket_->CallListenerReceivedData(host, port, data);
1065 }
1066
1067 // callback while UDP socket is opened
CallListenerOpened()1068 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
1069 return socket_->CallListenerOpened();
1070 }
1071
1072 // callback while UDP socket is connected
CallListenerConnected()1073 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerConnected() {
1074 return socket_->CallListenerConnected();
1075 }
1076
1077 // callback while UDP socket is closed
CallListenerClosed()1078 NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
1079 return socket_->CallListenerClosed();
1080 }
1081
1082 // NrUdpSocketIpc Implementation
NrUdpSocketIpc()1083 NrUdpSocketIpc::NrUdpSocketIpc()
1084 : NrSocketIpc(GetIOThreadAndAddUse_s()),
1085 monitor_("NrUdpSocketIpc"),
1086 err_(false),
1087 state_(NR_INIT) {}
1088
~NrUdpSocketIpc()1089 NrUdpSocketIpc::~NrUdpSocketIpc() {
1090 #if defined(MOZILLA_INTERNAL_API)
1091 // close(), but transfer the socket_child_ reference to die as well
1092 // destroy_i also dispatches back to STS to call ReleaseUse, to avoid shutting
1093 // down the IO thread before close() runs.
1094 RUN_ON_THREAD(
1095 io_thread_,
1096 mozilla::WrapRunnableNM(&NrUdpSocketIpc::destroy_i,
1097 socket_child_.forget().take(), sts_thread_),
1098 NS_DISPATCH_NORMAL);
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,nr_transport_addr * to)1302 int NrUdpSocketIpc::sendto(const void* msg, size_t len, int flags,
1303 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 if (state_ != NR_CONNECTED) {
1407 return R_INTERNAL;
1408 }
1409
1410 return nr_transport_addr_copy(addrp, &my_addr_);
1411 }
1412
connect(nr_transport_addr * addr)1413 int NrUdpSocketIpc::connect(nr_transport_addr* addr) {
1414 int r, _status;
1415 int32_t port;
1416 nsCString host;
1417
1418 ReentrantMonitorAutoEnter mon(monitor_);
1419 r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::connect(%s) this=%p",
1420 addr->as_string, (void*)this);
1421
1422 if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1423 ABORT(r);
1424 }
1425
1426 RUN_ON_THREAD(io_thread_,
1427 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1428 &NrUdpSocketIpc::connect_i, host,
1429 static_cast<uint16_t>(port)),
1430 NS_DISPATCH_NORMAL);
1431
1432 // Wait until connect() completes.
1433 mon.Wait();
1434
1435 r_log(LOG_GENERIC, LOG_DEBUG,
1436 "NrUdpSocketIpc::connect this=%p completed err_ = %s", (void*)this,
1437 err_ ? "true" : "false");
1438
1439 if (err_) {
1440 ABORT(R_INTERNAL);
1441 }
1442
1443 _status = 0;
1444 abort:
1445 return _status;
1446 }
1447
write(const void * msg,size_t len,size_t * written)1448 int NrUdpSocketIpc::write(const void* msg, size_t len, size_t* written) {
1449 MOZ_ASSERT(false);
1450 return R_INTERNAL;
1451 }
1452
read(void * buf,size_t maxlen,size_t * len)1453 int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t* len) {
1454 MOZ_ASSERT(false);
1455 return R_INTERNAL;
1456 }
1457
listen(int backlog)1458 int NrUdpSocketIpc::listen(int backlog) {
1459 MOZ_ASSERT(false);
1460 return R_INTERNAL;
1461 }
1462
accept(nr_transport_addr * addrp,nr_socket ** sockp)1463 int NrUdpSocketIpc::accept(nr_transport_addr* addrp, nr_socket** sockp) {
1464 MOZ_ASSERT(false);
1465 return R_INTERNAL;
1466 }
1467
1468 // IO thread executors
create_i(const nsACString & host,const uint16_t port)1469 void NrUdpSocketIpc::create_i(const nsACString& host, const uint16_t port) {
1470 ASSERT_ON_THREAD(io_thread_);
1471
1472 uint32_t minBuffSize = 0;
1473 RefPtr<dom::UDPSocketChild> socketChild = new dom::UDPSocketChild();
1474
1475 // This can spin the event loop; don't do that with the monitor held
1476 socketChild->SetBackgroundSpinsEvents();
1477
1478 ReentrantMonitorAutoEnter mon(monitor_);
1479 if (!socket_child_) {
1480 socket_child_ = socketChild;
1481 socket_child_->SetFilterName(
1482 nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX));
1483 } else {
1484 socketChild = nullptr;
1485 }
1486
1487 RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
1488 nsresult rv = proxy->Init(this);
1489 if (NS_FAILED(rv)) {
1490 err_ = true;
1491 mon.NotifyAll();
1492 return;
1493 }
1494
1495 #ifdef XP_WIN
1496 if (!mozilla::IsWin8OrLater()) {
1497 // Increase default receive and send buffer size on <= Win7 to be able to
1498 // receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K size)
1499 // stream without losing packets.
1500 // Manual testing showed that 100K buffer size was not enough and the
1501 // packet loss dis-appeared with 256K buffer size.
1502 // See bug 1252769 for future improvements of this.
1503 minBuffSize = 256 * 1024;
1504 }
1505 #endif
1506 // XXX bug 1126232 - don't use null Principal!
1507 if (NS_FAILED(socket_child_->Bind(proxy, nullptr, host, port,
1508 /* addressReuse = */ false,
1509 /* loopback = */ false,
1510 /* recv buffer size */ minBuffSize,
1511 /* send buffer size */ minBuffSize,
1512 /* mainThreadEventTarget */ nullptr))) {
1513 err_ = true;
1514 MOZ_ASSERT(false, "Failed to create UDP socket");
1515 mon.NotifyAll();
1516 return;
1517 }
1518 }
1519
connect_i(const nsACString & host,const uint16_t port)1520 void NrUdpSocketIpc::connect_i(const nsACString& host, const uint16_t port) {
1521 ASSERT_ON_THREAD(io_thread_);
1522 nsresult rv;
1523 ReentrantMonitorAutoEnter mon(monitor_);
1524
1525 RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
1526 rv = proxy->Init(this);
1527 if (NS_FAILED(rv)) {
1528 err_ = true;
1529 mon.NotifyAll();
1530 return;
1531 }
1532
1533 socket_child_->Connect(proxy, host, port);
1534 }
1535
sendto_i(const net::NetAddr & addr,UniquePtr<MediaPacket> buf)1536 void NrUdpSocketIpc::sendto_i(const net::NetAddr& addr,
1537 UniquePtr<MediaPacket> buf) {
1538 ASSERT_ON_THREAD(io_thread_);
1539
1540 ReentrantMonitorAutoEnter mon(monitor_);
1541
1542 if (!socket_child_) {
1543 MOZ_ASSERT(false);
1544 err_ = true;
1545 return;
1546 }
1547 if (NS_FAILED(
1548 socket_child_->SendWithAddress(&addr, buf->data(), buf->len()))) {
1549 err_ = true;
1550 }
1551 }
1552
close_i()1553 void NrUdpSocketIpc::close_i() {
1554 ASSERT_ON_THREAD(io_thread_);
1555
1556 if (socket_child_) {
1557 socket_child_->Close();
1558 socket_child_ = nullptr;
1559 }
1560 }
1561
1562 #if defined(MOZILLA_INTERNAL_API)
1563
ReleaseIOThread_s()1564 static void ReleaseIOThread_s() { sThread->ReleaseUse(); }
1565
1566 // close(), but transfer the socket_child_ reference to die as well
1567 // static
destroy_i(dom::UDPSocketChild * aChild,const nsCOMPtr<nsIEventTarget> & aStsThread)1568 void NrUdpSocketIpc::destroy_i(dom::UDPSocketChild* aChild,
1569 const nsCOMPtr<nsIEventTarget>& aStsThread) {
1570 RefPtr<dom::UDPSocketChild> socket_child_ref =
1571 already_AddRefed<dom::UDPSocketChild>(aChild);
1572 if (socket_child_ref) {
1573 socket_child_ref->Close();
1574 }
1575
1576 RUN_ON_THREAD(aStsThread, WrapRunnableNM(&ReleaseIOThread_s),
1577 NS_DISPATCH_NORMAL);
1578 }
1579 #endif
1580
recv_callback_s(RefPtr<nr_udp_message> msg)1581 void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
1582 ASSERT_ON_THREAD(sts_thread_);
1583
1584 {
1585 ReentrantMonitorAutoEnter mon(monitor_);
1586 if (state_ != NR_CONNECTED) {
1587 return;
1588 }
1589 }
1590
1591 // enqueue received message
1592 received_msgs_.push(msg);
1593
1594 if ((poll_flags() & PR_POLL_READ)) {
1595 fire_callback(NR_ASYNC_WAIT_READ);
1596 }
1597 }
1598
1599 } // namespace mozilla
1600
1601 using namespace mozilla;
1602
1603 // Bridge to the nr_socket interface
1604 static int nr_socket_local_destroy(void** objp);
1605 static int nr_socket_local_sendto(void* obj, const void* msg, size_t len,
1606 int flags, nr_transport_addr* to);
1607 static int nr_socket_local_recvfrom(void* obj, void* restrict buf,
1608 size_t maxlen, size_t* len, int flags,
1609 nr_transport_addr* from);
1610 static int nr_socket_local_getfd(void* obj, NR_SOCKET* fd);
1611 static int nr_socket_local_getaddr(void* obj, nr_transport_addr* addrp);
1612 static int nr_socket_local_close(void* obj);
1613 static int nr_socket_local_connect(void* sock, nr_transport_addr* addr);
1614 static int nr_socket_local_write(void* obj, const void* msg, size_t len,
1615 size_t* written);
1616 static int nr_socket_local_read(void* obj, void* restrict buf, size_t maxlen,
1617 size_t* len);
1618 static int nr_socket_local_listen(void* obj, int backlog);
1619 static int nr_socket_local_accept(void* obj, nr_transport_addr* addrp,
1620 nr_socket** sockp);
1621
1622 static nr_socket_vtbl nr_socket_local_vtbl = {2,
1623 nr_socket_local_destroy,
1624 nr_socket_local_sendto,
1625 nr_socket_local_recvfrom,
1626 nr_socket_local_getfd,
1627 nr_socket_local_getaddr,
1628 nr_socket_local_connect,
1629 nr_socket_local_write,
1630 nr_socket_local_read,
1631 nr_socket_local_close,
1632 nr_socket_local_listen,
1633 nr_socket_local_accept};
1634
1635 /* static */
CreateSocket(nr_transport_addr * addr,RefPtr<NrSocketBase> * sock,const std::shared_ptr<NrSocketProxyConfig> & config)1636 int NrSocketBase::CreateSocket(
1637 nr_transport_addr* addr, RefPtr<NrSocketBase>* sock,
1638 const std::shared_ptr<NrSocketProxyConfig>& config) {
1639 int r, _status;
1640
1641 if (IsForbiddenAddress(addr)) {
1642 ABORT(R_REJECTED);
1643 }
1644
1645 if (config && config->GetForceProxy() && addr->protocol == IPPROTO_UDP) {
1646 ABORT(R_REJECTED);
1647 }
1648
1649 // create IPC bridge for content process
1650 if (XRE_IsParentProcess()) {
1651 // TODO: Make NrTcpSocket work on the parent process
1652 *sock = new NrSocket();
1653 } else if (XRE_IsSocketProcess()) {
1654 if (addr->protocol == IPPROTO_TCP) {
1655 *sock = new NrTcpSocket(config);
1656 } else {
1657 *sock = new NrSocket();
1658 }
1659 } else {
1660 if (addr->protocol == IPPROTO_TCP) {
1661 *sock = new NrTcpSocket(config);
1662 } else {
1663 *sock = new NrUdpSocketIpc();
1664 }
1665 }
1666
1667 r = (*sock)->create(addr);
1668 if (r) ABORT(r);
1669
1670 _status = 0;
1671 abort:
1672 if (_status) {
1673 *sock = nullptr;
1674 }
1675 return _status;
1676 }
1677
1678 // static
IsForbiddenAddress(nr_transport_addr * addr)1679 bool NrSocketBase::IsForbiddenAddress(nr_transport_addr* addr) {
1680 int r, port;
1681
1682 r = nr_transport_addr_get_port(addr, &port);
1683 if (r) {
1684 return true;
1685 }
1686
1687 // allow auto assigned ports
1688 if (port != 0) {
1689 // Don't need to check an override scheme
1690 nsresult rv = NS_CheckPortSafety(port, nullptr);
1691 if (NS_FAILED(rv)) {
1692 return true;
1693 }
1694 }
1695
1696 return false;
1697 }
1698
nr_socket_local_destroy(void ** objp)1699 static int nr_socket_local_destroy(void** objp) {
1700 if (!objp || !*objp) return 0;
1701
1702 NrSocketBase* sock = static_cast<NrSocketBase*>(*objp);
1703 *objp = nullptr;
1704
1705 sock->close(); // Signal STS that we want not to listen
1706 sock->Release(); // Decrement the ref count
1707
1708 return 0;
1709 }
1710
nr_socket_local_sendto(void * obj,const void * msg,size_t len,int flags,nr_transport_addr * addr)1711 static int nr_socket_local_sendto(void* obj, const void* msg, size_t len,
1712 int flags, nr_transport_addr* addr) {
1713 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1714
1715 return sock->sendto(msg, len, flags, addr);
1716 }
1717
nr_socket_local_recvfrom(void * obj,void * restrict buf,size_t maxlen,size_t * len,int flags,nr_transport_addr * addr)1718 static int nr_socket_local_recvfrom(void* obj, void* restrict buf,
1719 size_t maxlen, size_t* len, int flags,
1720 nr_transport_addr* addr) {
1721 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1722
1723 return sock->recvfrom(buf, maxlen, len, flags, addr);
1724 }
1725
nr_socket_local_getfd(void * obj,NR_SOCKET * fd)1726 static int nr_socket_local_getfd(void* obj, NR_SOCKET* fd) {
1727 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1728
1729 *fd = sock;
1730
1731 return 0;
1732 }
1733
nr_socket_local_getaddr(void * obj,nr_transport_addr * addrp)1734 static int nr_socket_local_getaddr(void* obj, nr_transport_addr* addrp) {
1735 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1736
1737 return sock->getaddr(addrp);
1738 }
1739
nr_socket_local_close(void * obj)1740 static int nr_socket_local_close(void* obj) {
1741 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1742
1743 sock->close();
1744
1745 return 0;
1746 }
1747
nr_socket_local_write(void * obj,const void * msg,size_t len,size_t * written)1748 static int nr_socket_local_write(void* obj, const void* msg, size_t len,
1749 size_t* written) {
1750 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1751
1752 return sock->write(msg, len, written);
1753 }
1754
nr_socket_local_read(void * obj,void * restrict buf,size_t maxlen,size_t * len)1755 static int nr_socket_local_read(void* obj, void* restrict buf, size_t maxlen,
1756 size_t* len) {
1757 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1758
1759 return sock->read(buf, maxlen, len);
1760 }
1761
nr_socket_local_connect(void * obj,nr_transport_addr * addr)1762 static int nr_socket_local_connect(void* obj, nr_transport_addr* addr) {
1763 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1764
1765 return sock->connect(addr);
1766 }
1767
nr_socket_local_listen(void * obj,int backlog)1768 static int nr_socket_local_listen(void* obj, int backlog) {
1769 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1770
1771 return sock->listen(backlog);
1772 }
1773
nr_socket_local_accept(void * obj,nr_transport_addr * addrp,nr_socket ** sockp)1774 static int nr_socket_local_accept(void* obj, nr_transport_addr* addrp,
1775 nr_socket** sockp) {
1776 NrSocketBase* sock = static_cast<NrSocketBase*>(obj);
1777
1778 return sock->accept(addrp, sockp);
1779 }
1780
1781 // Implement async api
NR_async_wait(NR_SOCKET sock,int how,NR_async_cb cb,void * cb_arg,char * function,int line)1782 int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb, void* cb_arg,
1783 char* function, int line) {
1784 NrSocketBase* s = static_cast<NrSocketBase*>(sock);
1785
1786 return s->async_wait(how, cb, cb_arg, function, line);
1787 }
1788
NR_async_cancel(NR_SOCKET sock,int how)1789 int NR_async_cancel(NR_SOCKET sock, int how) {
1790 NrSocketBase* s = static_cast<NrSocketBase*>(sock);
1791
1792 return s->cancel(how);
1793 }
1794
vtbl()1795 nr_socket_vtbl* NrSocketBase::vtbl() { return &nr_socket_local_vtbl; }
1796