Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
* You can obtain one at http://mozilla.org/MPL/2.0/. */
6
/*
7
Modified version of nr_socket_local, adapted for NSPR
8
*/
9
10
/* This Source Code Form is subject to the terms of the Mozilla Public
11
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
12
* You can obtain one at http://mozilla.org/MPL/2.0/. */
13
14
/*
15
Original code from nICEr and nrappkit.
16
17
nICEr copyright:
18
19
Copyright (c) 2007, Adobe Systems, Incorporated
20
All rights reserved.
21
22
Redistribution and use in source and binary forms, with or without
23
modification, are permitted provided that the following conditions are
24
met:
25
26
* Redistributions of source code must retain the above copyright
27
notice, this list of conditions and the following disclaimer.
28
29
* Redistributions in binary form must reproduce the above copyright
30
notice, this list of conditions and the following disclaimer in the
31
documentation and/or other materials provided with the distribution.
32
33
* Neither the name of Adobe Systems, Network Resonance nor the names of its
34
contributors may be used to endorse or promote products derived from
35
this software without specific prior written permission.
36
37
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
38
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
40
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
41
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
49
50
nrappkit copyright:
51
52
Copyright (C) 2001-2003, Network Resonance, Inc.
53
Copyright (C) 2006, Network Resonance, Inc.
54
All Rights Reserved
55
56
Redistribution and use in source and binary forms, with or without
57
modification, are permitted provided that the following conditions
58
are met:
59
60
1. Redistributions of source code must retain the above copyright
61
notice, this list of conditions and the following disclaimer.
62
2. Redistributions in binary form must reproduce the above copyright
63
notice, this list of conditions and the following disclaimer in the
64
documentation and/or other materials provided with the distribution.
65
3. Neither the name of Network Resonance, Inc. nor the name of any
66
contributors to this software may be used to endorse or promote
67
products derived from this software without specific prior written
68
permission.
69
70
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
71
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
74
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
75
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
76
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
77
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
78
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
79
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
80
POSSIBILITY OF SUCH DAMAGE.
81
82
83
ekr@rtfm.com Thu Dec 20 20:14:49 2001
84
*/
85
86
#include <csi_platform.h>
87
#include <stdio.h>
88
#include <string.h>
89
#include <sys/types.h>
90
#include <assert.h>
91
#include <errno.h>
92
#include <string>
93
94
#include "nspr.h"
95
#include "prerror.h"
96
#include "prio.h"
97
#include "prnetdb.h"
98
99
#include "mozilla/net/DNS.h"
100
#include "nsCOMPtr.h"
101
#include "nsASocketHandler.h"
102
#include "nsISocketTransportService.h"
103
#include "nsNetCID.h"
104
#include "nsISupportsImpl.h"
105
#include "nsServiceManagerUtils.h"
106
#include "nsComponentManagerUtils.h"
107
#include "nsXPCOM.h"
108
#include "nsXULAppAPI.h"
109
#include "runnable_utils.h"
110
#include "mozilla/SyncRunnable.h"
111
#include "nsTArray.h"
112
#include "mozilla/SystemGroup.h"
113
#include "nsIPrefService.h"
114
#include "nsIPrefBranch.h"
115
#include "nsISocketFilter.h"
116
#include "nsDebug.h"
117
#include "nsNetUtil.h"
118
119
#ifdef XP_WIN
120
# include "mozilla/WindowsVersion.h"
121
#endif
122
123
#if defined(MOZILLA_INTERNAL_API)
124
// csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
125
# ifdef LOG_INFO
126
# define LOG_TEMP_INFO LOG_INFO
127
# undef LOG_INFO
128
# endif
129
# ifdef LOG_WARNING
130
# define LOG_TEMP_WARNING LOG_WARNING
131
# undef LOG_WARNING
132
# endif
133
# if defined(LOG_DEBUG)
134
# define LOG_TEMP_DEBUG LOG_DEBUG
135
# undef LOG_DEBUG
136
# endif
137
# undef strlcpy
138
139
# include "mozilla/dom/network/UDPSocketChild.h"
140
141
# ifdef LOG_TEMP_INFO
142
# define LOG_INFO LOG_TEMP_INFO
143
# endif
144
# ifdef LOG_TEMP_WARNING
145
# define LOG_WARNING LOG_TEMP_WARNING
146
# endif
147
148
# ifdef LOG_TEMP_DEBUG
149
# define LOG_DEBUG LOG_TEMP_DEBUG
150
# endif
151
# ifdef XP_WIN
152
# ifdef LOG_DEBUG
153
# undef LOG_DEBUG
154
# endif
155
// cloned from csi_platform.h. Win32 doesn't like how we hide symbols
156
# define LOG_DEBUG 7
157
# endif
158
#endif
159
160
extern "C" {
161
#include "nr_api.h"
162
#include "async_wait.h"
163
#include "nr_socket.h"
164
#include "nr_socket_local.h"
165
#include "stun_hint.h"
166
}
167
#include "nr_socket_prsock.h"
168
#include "simpletokenbucket.h"
169
#include "test_nr_socket.h"
170
#include "nr_socket_tcp.h"
171
#include "nr_socket_proxy_config.h"
172
173
// Implement the nsISupports ref counting
174
namespace mozilla {
175
176
#if defined(MOZILLA_INTERNAL_API)
177
class SingletonThreadHolder final {
178
private:
179
~SingletonThreadHolder() {
180
r_log(LOG_GENERIC, LOG_DEBUG, "Deleting SingletonThreadHolder");
181
if (mThread) {
182
// Likely a connection is somehow being held in CC or GC
183
NS_WARNING(
184
"SingletonThreads should be Released and shut down before exit!");
185
mThread->Shutdown();
186
mThread = nullptr;
187
}
188
}
189
190
DISALLOW_COPY_ASSIGN(SingletonThreadHolder);
191
192
public:
193
// Must be threadsafe for StaticRefPtr/ClearOnShutdown
194
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder)
195
196
explicit SingletonThreadHolder(const nsACString& aName) : mName(aName) {
197
mParentThread = NS_GetCurrentThread();
198
}
199
200
nsIThread* GetThread() { return mThread; }
201
202
/*
203
* Keep track of how many instances are using a SingletonThreadHolder.
204
* When no one is using it, shut it down
205
*/
206
void AddUse() {
207
MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
208
MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
209
nsrefcnt count = ++mUseCount;
210
if (count == 1) {
211
// idle -> in-use
212
nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
213
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread,
214
"Should successfully create mtransport I/O thread");
215
r_log(LOG_GENERIC, LOG_DEBUG, "Created wrapped SingletonThread %p",
216
mThread.get());
217
}
218
r_log(LOG_GENERIC, LOG_DEBUG, "AddUse_i: %lu", (unsigned long)count);
219
}
220
221
void ReleaseUse() {
222
MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
223
nsrefcnt count = --mUseCount;
224
MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
225
if (mThread && count == 0) {
226
// in-use -> idle -- no one forcing it to remain instantiated
227
r_log(LOG_GENERIC, LOG_DEBUG, "Shutting down wrapped SingletonThread %p",
228
mThread.get());
229
mThread->AsyncShutdown();
230
mThread = nullptr;
231
// It'd be nice to use a timer instead... But be careful of
232
// xpcom-shutdown-threads in that case
233
}
234
r_log(LOG_GENERIC, LOG_DEBUG, "ReleaseUse_i: %lu", (unsigned long)count);
235
}
236
237
private:
238
nsCString mName;
239
nsAutoRefCnt mUseCount;
240
nsCOMPtr<nsIThread> mParentThread;
241
nsCOMPtr<nsIThread> mThread;
242
};
243
244
static StaticRefPtr<SingletonThreadHolder> sThread;
245
246
static void ClearSingletonOnShutdown() {
247
ClearOnShutdown(&sThread, ShutdownPhase::ShutdownLoaders);
248
}
249
#endif
250
251
static nsIThread* GetIOThreadAndAddUse_s() {
252
// Always runs on STS thread!
253
#if defined(MOZILLA_INTERNAL_API)
254
// We need to safely release this on shutdown to avoid leaks
255
if (!sThread) {
256
sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
257
NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
258
}
259
// Mark that we're using the shared thread and need it to stick around
260
sThread->AddUse();
261
return sThread->GetThread();
262
#else
263
static nsCOMPtr<nsIThread> sThread;
264
if (!sThread) {
265
(void)NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
266
}
267
return sThread;
268
#endif
269
}
270
271
NrSocketIpc::NrSocketIpc(nsIEventTarget* aThread) : io_thread_(aThread) {}
272
273
static TimeStamp nr_socket_short_term_violation_time;
274
static TimeStamp nr_socket_long_term_violation_time;
275
276
TimeStamp NrSocketBase::short_term_violation_time() {
277
return nr_socket_short_term_violation_time;
278
}
279
280
TimeStamp NrSocketBase::long_term_violation_time() {
281
return nr_socket_long_term_violation_time;
282
}
283
284
// NrSocketBase implementation
285
// async_event APIs
286
int NrSocketBase::async_wait(int how, NR_async_cb cb, void* cb_arg,
287
char* function, int line) {
288
uint16_t flag;
289
290
switch (how) {
291
case NR_ASYNC_WAIT_READ:
292
flag = PR_POLL_READ;
293
break;
294
case NR_ASYNC_WAIT_WRITE:
295
flag = PR_POLL_WRITE;
296
break;
297
default:
298
return R_BAD_ARGS;
299
}
300
301
cbs_[how] = cb;
302
cb_args_[how] = cb_arg;
303
poll_flags_ |= flag;
304
305
return 0;
306
}
307
308
int NrSocketBase::cancel(int how) {
309
uint16_t flag;
310
311
switch (how) {
312
case NR_ASYNC_WAIT_READ:
313
flag = PR_POLL_READ;
314
break;
315
case NR_ASYNC_WAIT_WRITE:
316
flag = PR_POLL_WRITE;
317
break;
318
default:
319
return R_BAD_ARGS;
320
}
321
322
poll_flags_ &= ~flag;
323
324
return 0;
325
}
326
327
void NrSocketBase::fire_callback(int how) {
328
// This can't happen unless we are armed because we only set
329
// the flags if we are armed
330
MOZ_ASSERT(cbs_[how]);
331
332
// Now cancel so that we need to be re-armed. Note that
333
// the re-arming probably happens in the callback we are
334
// about to fire.
335
cancel(how);
336
337
cbs_[how](this, how, cb_args_[how]);
338
}
339
340
// NrSocket implementation
341
NS_IMPL_ISUPPORTS0(NrSocket)
342
343
// The nsASocket callbacks
344
void NrSocket::OnSocketReady(PRFileDesc* fd, int16_t outflags) {
345
if (outflags & PR_POLL_READ & poll_flags()) fire_callback(NR_ASYNC_WAIT_READ);
346
if (outflags & PR_POLL_WRITE & poll_flags())
347
fire_callback(NR_ASYNC_WAIT_WRITE);
348
if (outflags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP))
349
// TODO: Bug 946423: how do we notify the upper layers about this?
350
close();
351
}
352
353
void NrSocket::OnSocketDetached(PRFileDesc* fd) {
354
r_log(LOG_GENERIC, LOG_DEBUG, "Socket %p detached", fd);
355
}
356
357
void NrSocket::IsLocal(bool* aIsLocal) {
358
// TODO(jesup): better check? Does it matter? (likely no)
359
*aIsLocal = false;
360
}
361
362
// async_event APIs
363
int NrSocket::async_wait(int how, NR_async_cb cb, void* cb_arg, char* function,
364
int line) {
365
int r = NrSocketBase::async_wait(how, cb, cb_arg, function, line);
366
367
if (!r) {
368
mPollFlags = poll_flags();
369
}
370
371
return r;
372
}
373
374
int NrSocket::cancel(int how) {
375
int r = NrSocketBase::cancel(how);
376
377
if (!r) {
378
mPollFlags = poll_flags();
379
}
380
381
return r;
382
}
383
384
// Helper functions for addresses
385
static int nr_transport_addr_to_praddr(nr_transport_addr* addr,
386
PRNetAddr* naddr) {
387
int _status;
388
389
memset(naddr, 0, sizeof(*naddr));
390
391
switch (addr->protocol) {
392
case IPPROTO_TCP:
393
break;
394
case IPPROTO_UDP:
395
break;
396
default:
397
ABORT(R_BAD_ARGS);
398
}
399
400
switch (addr->ip_version) {
401
case NR_IPV4:
402
naddr->inet.family = PR_AF_INET;
403
naddr->inet.port = addr->u.addr4.sin_port;
404
naddr->inet.ip = addr->u.addr4.sin_addr.s_addr;
405
break;
406
case NR_IPV6:
407
naddr->ipv6.family = PR_AF_INET6;
408
naddr->ipv6.port = addr->u.addr6.sin6_port;
409
naddr->ipv6.flowinfo = addr->u.addr6.sin6_flowinfo;
410
memcpy(&naddr->ipv6.ip, &addr->u.addr6.sin6_addr, sizeof(in6_addr));
411
naddr->ipv6.scope_id = addr->u.addr6.sin6_scope_id;
412
break;
413
default:
414
ABORT(R_BAD_ARGS);
415
}
416
417
_status = 0;
418
abort:
419
return (_status);
420
}
421
422
// XXX schien@mozilla.com: copy from PRNetAddrToNetAddr,
423
// should be removed after fix the link error in signaling_unittests
424
static int praddr_to_netaddr(const PRNetAddr* prAddr, net::NetAddr* addr) {
425
int _status;
426
427
switch (prAddr->raw.family) {
428
case PR_AF_INET:
429
addr->inet.family = AF_INET;
430
addr->inet.port = prAddr->inet.port;
431
addr->inet.ip = prAddr->inet.ip;
432
break;
433
case PR_AF_INET6:
434
addr->inet6.family = AF_INET6;
435
addr->inet6.port = prAddr->ipv6.port;
436
addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
437
memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
438
addr->inet6.scope_id = prAddr->ipv6.scope_id;
439
break;
440
default:
441
MOZ_ASSERT(false);
442
ABORT(R_BAD_ARGS);
443
}
444
445
_status = 0;
446
abort:
447
return (_status);
448
}
449
450
static int nr_transport_addr_to_netaddr(nr_transport_addr* addr,
451
net::NetAddr* naddr) {
452
int r, _status;
453
PRNetAddr praddr;
454
455
if ((r = nr_transport_addr_to_praddr(addr, &praddr))) {
456
ABORT(r);
457
}
458
459
if ((r = praddr_to_netaddr(&praddr, naddr))) {
460
ABORT(r);
461
}
462
463
_status = 0;
464
abort:
465
return (_status);
466
}
467
468
int nr_netaddr_to_transport_addr(const net::NetAddr* netaddr,
469
nr_transport_addr* addr, int protocol) {
470
int _status;
471
int r;
472
473
switch (netaddr->raw.family) {
474
case AF_INET:
475
if ((r = nr_ip4_port_to_transport_addr(ntohl(netaddr->inet.ip),
476
ntohs(netaddr->inet.port),
477
protocol, addr)))
478
ABORT(r);
479
break;
480
case AF_INET6:
481
if ((r = nr_ip6_port_to_transport_addr((in6_addr*)&netaddr->inet6.ip.u8,
482
ntohs(netaddr->inet6.port),
483
protocol, addr)))
484
ABORT(r);
485
break;
486
default:
487
MOZ_ASSERT(false);
488
ABORT(R_BAD_ARGS);
489
}
490
_status = 0;
491
abort:
492
return (_status);
493
}
494
495
int nr_praddr_to_transport_addr(const PRNetAddr* praddr,
496
nr_transport_addr* addr, int protocol,
497
int keep) {
498
int _status;
499
int r;
500
struct sockaddr_in ip4;
501
struct sockaddr_in6 ip6;
502
503
switch (praddr->raw.family) {
504
case PR_AF_INET:
505
ip4.sin_family = PF_INET;
506
ip4.sin_addr.s_addr = praddr->inet.ip;
507
ip4.sin_port = praddr->inet.port;
508
if ((r = nr_sockaddr_to_transport_addr((sockaddr*)&ip4, protocol, keep,
509
addr)))
510
ABORT(r);
511
break;
512
case PR_AF_INET6:
513
ip6.sin6_family = PF_INET6;
514
ip6.sin6_port = praddr->ipv6.port;
515
ip6.sin6_flowinfo = praddr->ipv6.flowinfo;
516
memcpy(&ip6.sin6_addr, &praddr->ipv6.ip, sizeof(in6_addr));
517
ip6.sin6_scope_id = praddr->ipv6.scope_id;
518
if ((r = nr_sockaddr_to_transport_addr((sockaddr*)&ip6, protocol, keep,
519
addr)))
520
ABORT(r);
521
break;
522
default:
523
MOZ_ASSERT(false);
524
ABORT(R_BAD_ARGS);
525
}
526
527
_status = 0;
528
abort:
529
return (_status);
530
}
531
532
/*
533
* nr_transport_addr_get_addrstring_and_port
534
* convert nr_transport_addr to IP address string and port number
535
*/
536
int nr_transport_addr_get_addrstring_and_port(nr_transport_addr* addr,
537
nsACString* host, int32_t* port) {
538
int r, _status;
539
char addr_string[64];
540
541
// We cannot directly use |nr_transport_addr.as_string| because it contains
542
// more than ip address, therefore, we need to explicity convert it
543
// from |nr_transport_addr_get_addrstring|.
544
if ((r = nr_transport_addr_get_addrstring(addr, addr_string,
545
sizeof(addr_string)))) {
546
ABORT(r);
547
}
548
549
if ((r = nr_transport_addr_get_port(addr, port))) {
550
ABORT(r);
551
}
552
553
*host = addr_string;
554
555
_status = 0;
556
abort:
557
return (_status);
558
}
559
560
// nr_socket APIs (as member functions)
561
int NrSocket::create(nr_transport_addr* addr) {
562
int r, _status;
563
564
PRStatus status;
565
PRNetAddr naddr;
566
567
nsresult rv;
568
nsCOMPtr<nsISocketTransportService> stservice =
569
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
570
571
if (!NS_SUCCEEDED(rv)) {
572
ABORT(R_INTERNAL);
573
}
574
575
if ((r = nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r);
576
577
switch (addr->protocol) {
578
case IPPROTO_UDP:
579
if (!(fd_ = PR_OpenUDPSocket(naddr.raw.family))) {
580
r_log(LOG_GENERIC, LOG_CRIT,
581
"Couldn't create UDP socket, "
582
"family=%d, err=%d",
583
naddr.raw.family, PR_GetError());
584
ABORT(R_INTERNAL);
585
}
586
#ifdef XP_WIN
587
if (!mozilla::IsWin8OrLater()) {
588
// Increase default send and receive buffer sizes on <= Win7 to be able
589
// to receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K
590
// size) stream without losing packets. Manual testing showed that 100K
591
// buffer size was not enough and the packet loss dis-appeared with 256K
592
// buffer size. See bug 1252769 for future improvements of this.
593
PRSize min_buffer_size = 256 * 1024;
594
PRSocketOptionData opt_rcvbuf;
595
opt_rcvbuf.option = PR_SockOpt_RecvBufferSize;
596
if ((status = PR_GetSocketOption(fd_, &opt_rcvbuf)) == PR_SUCCESS) {
597
if (opt_rcvbuf.value.recv_buffer_size < min_buffer_size) {
598
opt_rcvbuf.value.recv_buffer_size = min_buffer_size;
599
if ((status = PR_SetSocketOption(fd_, &opt_rcvbuf)) != PR_SUCCESS) {
600
r_log(LOG_GENERIC, LOG_CRIT,
601
"Couldn't set socket receive buffer size: %d", status);
602
}
603
} else {
604
r_log(LOG_GENERIC, LOG_INFO,
605
"Socket receive buffer size is already: %d",
606
opt_rcvbuf.value.recv_buffer_size);
607
}
608
} else {
609
r_log(LOG_GENERIC, LOG_CRIT,
610
"Couldn't get socket receive buffer size: %d", status);
611
}
612
PRSocketOptionData opt_sndbuf;
613
opt_sndbuf.option = PR_SockOpt_SendBufferSize;
614
if ((status = PR_GetSocketOption(fd_, &opt_sndbuf)) == PR_SUCCESS) {
615
if (opt_sndbuf.value.recv_buffer_size < min_buffer_size) {
616
opt_sndbuf.value.recv_buffer_size = min_buffer_size;
617
if ((status = PR_SetSocketOption(fd_, &opt_sndbuf)) != PR_SUCCESS) {
618
r_log(LOG_GENERIC, LOG_CRIT,
619
"Couldn't set socket send buffer size: %d", status);
620
}
621
} else {
622
r_log(LOG_GENERIC, LOG_INFO,
623
"Socket send buffer size is already: %d",
624
opt_sndbuf.value.recv_buffer_size);
625
}
626
} else {
627
r_log(LOG_GENERIC, LOG_CRIT,
628
"Couldn't get socket send buffer size: %d", status);
629
}
630
}
631
#endif
632
break;
633
case IPPROTO_TCP:
634
// TODO: Add TLS layer with nsISocketProviderService?
635
if (my_addr_.tls_host[0] != '\0') ABORT(R_INTERNAL);
636
637
if (!(fd_ = PR_OpenTCPSocket(naddr.raw.family))) {
638
r_log(LOG_GENERIC, LOG_CRIT,
639
"Couldn't create TCP socket, "
640
"family=%d, err=%d",
641
naddr.raw.family, PR_GetError());
642
ABORT(R_INTERNAL);
643
}
644
// Set ReuseAddr for TCP sockets to enable having several
645
// sockets bound to same local IP and port
646
PRSocketOptionData opt_reuseaddr;
647
opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
648
opt_reuseaddr.value.reuse_addr = PR_TRUE;
649
status = PR_SetSocketOption(fd_, &opt_reuseaddr);
650
if (status != PR_SUCCESS) {
651
r_log(LOG_GENERIC, LOG_CRIT,
652
"Couldn't set reuse addr socket option: %d", status);
653
ABORT(R_INTERNAL);
654
}
655
// And also set ReusePort for platforms supporting this socket option
656
PRSocketOptionData opt_reuseport;
657
opt_reuseport.option = PR_SockOpt_Reuseport;
658
opt_reuseport.value.reuse_port = PR_TRUE;
659
status = PR_SetSocketOption(fd_, &opt_reuseport);
660
if (status != PR_SUCCESS) {
661
if (PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
662
r_log(LOG_GENERIC, LOG_CRIT,
663
"Couldn't set reuse port socket option: %d", status);
664
ABORT(R_INTERNAL);
665
}
666
}
667
// Try to speedup packet delivery by disabling TCP Nagle
668
PRSocketOptionData opt_nodelay;
669
opt_nodelay.option = PR_SockOpt_NoDelay;
670
opt_nodelay.value.no_delay = PR_TRUE;
671
status = PR_SetSocketOption(fd_, &opt_nodelay);
672
if (status != PR_SUCCESS) {
673
r_log(LOG_GENERIC, LOG_WARNING,
674
"Couldn't set Nodelay socket option: %d", status);
675
}
676
break;
677
default:
678
ABORT(R_INTERNAL);
679
}
680
681
status = PR_Bind(fd_, &naddr);
682
if (status != PR_SUCCESS) {
683
r_log(LOG_GENERIC, LOG_CRIT, "Couldn't bind socket to address %s",
684
addr->as_string);
685
ABORT(R_INTERNAL);
686
}
687
688
r_log(LOG_GENERIC, LOG_DEBUG, "Creating socket %p with addr %s", fd_,
689
addr->as_string);
690
nr_transport_addr_copy(&my_addr_, addr);
691
692
/* If we have a wildcard port, patch up the addr */
693
if (nr_transport_addr_is_wildcard(addr)) {
694
status = PR_GetSockName(fd_, &naddr);
695
if (status != PR_SUCCESS) {
696
r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
697
ABORT(R_INTERNAL);
698
}
699
700
if ((r = nr_praddr_to_transport_addr(&naddr, &my_addr_, addr->protocol, 1)))
701
ABORT(r);
702
}
703
704
// Set nonblocking
705
PRSocketOptionData opt_nonblock;
706
opt_nonblock.option = PR_SockOpt_Nonblocking;
707
opt_nonblock.value.non_blocking = PR_TRUE;
708
status = PR_SetSocketOption(fd_, &opt_nonblock);
709
if (status != PR_SUCCESS) {
710
r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking");
711
ABORT(R_INTERNAL);
712
}
713
714
// Remember our thread.
715
ststhread_ = do_QueryInterface(stservice, &rv);
716
if (!NS_SUCCEEDED(rv)) ABORT(R_INTERNAL);
717
718
// Finally, register with the STS
719
rv = stservice->AttachSocket(fd_, this);
720
if (!NS_SUCCEEDED(rv)) {
721
r_log(LOG_GENERIC, LOG_CRIT, "Couldn't attach socket to STS, rv=%u",
722
static_cast<unsigned>(rv));
723
ABORT(R_INTERNAL);
724
}
725
726
_status = 0;
727
728
abort:
729
return (_status);
730
}
731
732
static int ShouldDrop(size_t len) {
733
// Global rate limiting for stun requests, to mitigate the ice hammer DoS
735
736
// Tolerate rate of 8k/sec, for one second.
737
static SimpleTokenBucket burst(16384 * 1, 16384);
738
// Tolerate rate of 7.2k/sec over twenty seconds.
739
static SimpleTokenBucket sustained(7372 * 20, 7372);
740
741
// Check number of tokens in each bucket.
742
if (burst.getTokens(UINT32_MAX) < len) {
743
r_log(LOG_GENERIC, LOG_ERR,
744
"Short term global rate limit for STUN requests exceeded.");
745
#ifdef MOZILLA_INTERNAL_API
746
nr_socket_short_term_violation_time = TimeStamp::Now();
747
#endif
748
749
// Bug 1013007
750
#if !EARLY_BETA_OR_EARLIER
751
return R_WOULDBLOCK;
752
#else
753
MOZ_ASSERT(false,
754
"Short term global rate limit for STUN requests exceeded. Go "
755
"bug bcampen@mozilla.com if you weren't intentionally "
756
"spamming ICE candidates, or don't know what that means.");
757
#endif
758
}
759
760
if (sustained.getTokens(UINT32_MAX) < len) {
761
r_log(LOG_GENERIC, LOG_ERR,
762
"Long term global rate limit for STUN requests exceeded.");
763
#ifdef MOZILLA_INTERNAL_API
764
nr_socket_long_term_violation_time = TimeStamp::Now();
765
#endif
766
// Bug 1013007
767
#if !EARLY_BETA_OR_EARLIER
768
return R_WOULDBLOCK;
769
#else
770
MOZ_ASSERT(false,
771
"Long term global rate limit for STUN requests exceeded. Go "
772
"bug bcampen@mozilla.com if you weren't intentionally "
773
"spamming ICE candidates, or don't know what that means.");
774
#endif
775
}
776
777
// Take len tokens from both buckets.
778
// (not threadsafe, but no problem since this is only called from STS)
779
burst.getTokens(len);
780
sustained.getTokens(len);
781
return 0;
782
}
783
784
// This should be called on the STS thread.
785
int NrSocket::sendto(const void* msg, size_t len, int flags,
786
nr_transport_addr* to) {
787
ASSERT_ON_THREAD(ststhread_);
788
int r, _status;
789
PRNetAddr naddr;
790
int32_t status;
791
792
if ((r = nr_transport_addr_to_praddr(to, &naddr))) ABORT(r);
793
794
if (fd_ == nullptr) ABORT(R_EOD);
795
796
if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
797
ABORT(R_WOULDBLOCK);
798
}
799
800
// TODO: Convert flags?
801
status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT);
802
if (status < 0 || (size_t)status != len) {
803
if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
804
805
r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s: %d", to->as_string,
806
PR_GetError());
807
ABORT(R_IO_ERROR);
808
}
809
810
_status = 0;
811
abort:
812
return (_status);
813
}
814
815
int NrSocket::recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
816
nr_transport_addr* from) {
817
ASSERT_ON_THREAD(ststhread_);
818
int r, _status;
819
PRNetAddr nfrom;
820
int32_t status;
821
822
status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
823
if (status <= 0) {
824
if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK);
825
r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError());
826
ABORT(R_IO_ERROR);
827
}
828
*len = status;
829
830
if ((r = nr_praddr_to_transport_addr(&nfrom, from, my_addr_.protocol, 0)))
831
ABORT(r);
832
833
// r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
834
835
_status = 0;
836
abort:
837
return (_status);
838
}
839
840
int NrSocket::getaddr(nr_transport_addr* addrp) {
841
ASSERT_ON_THREAD(ststhread_);
842
return nr_transport_addr_copy(addrp, &my_addr_);
843
}
844
845
// Close the socket so that the STS will detach and then kill it
846
void NrSocket::close() {
847
ASSERT_ON_THREAD(ststhread_);
848
mCondition = NS_BASE_STREAM_CLOSED;
849
}
850
851
int NrSocket::connect(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
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
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
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
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
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
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
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
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
1069
NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
1070
return socket_->CallListenerOpened();
1071
}
1072
1073
// callback while UDP socket is connected
1074
NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerConnected() {
1075
return socket_->CallListenerConnected();
1076
}
1077
1078
// callback while UDP socket is closed
1079
NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
1080
return socket_->CallListenerClosed();
1081
}
1082
1083
// NrUdpSocketIpc Implementation
1084
NrUdpSocketIpc::NrUdpSocketIpc()
1085
: NrSocketIpc(GetIOThreadAndAddUse_s()),
1086
monitor_("NrUdpSocketIpc"),
1087
err_(false),
1088
state_(NR_INIT) {}
1089
1090
NrUdpSocketIpc::~NrUdpSocketIpc() {
1091
#if defined(MOZILLA_INTERNAL_API)
1092
// close(), but transfer the socket_child_ reference to die as well
1093
// destroy_i also dispatches back to STS to call ReleaseUse, to avoid shutting
1094
// down the IO thread before close() runs.
1095
RUN_ON_THREAD(
1096
io_thread_,
1097
mozilla::WrapRunnableNM(&NrUdpSocketIpc::destroy_i,
1098
socket_child_.forget().take(), sts_thread_),
1099
NS_DISPATCH_NORMAL);
1100
#endif
1101
}
1102
1103
// IUDPSocketInternal interfaces
1104
// callback while error happened in UDP socket operation
1105
NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString& message,
1106
const nsACString& filename,
1107
uint32_t line_number) {
1108
ASSERT_ON_THREAD(io_thread_);
1109
1110
r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d this=%p",
1111
message.BeginReading(), filename.BeginReading(), line_number,
1112
(void*)this);
1113
1114
ReentrantMonitorAutoEnter mon(monitor_);
1115
err_ = true;
1116
monitor_.NotifyAll();
1117
1118
return NS_OK;
1119
}
1120
1121
// callback while receiving UDP packet
1122
NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData(
1123
const nsACString& host, uint16_t port, const nsTArray<uint8_t>& data) {
1124
ASSERT_ON_THREAD(io_thread_);
1125
1126
PRNetAddr addr;
1127
memset(&addr, 0, sizeof(addr));
1128
1129
{
1130
ReentrantMonitorAutoEnter mon(monitor_);
1131
1132
if (PR_SUCCESS != PR_StringToNetAddr(host.BeginReading(), &addr)) {
1133
err_ = true;
1134
MOZ_ASSERT(false, "Failed to convert remote host to PRNetAddr");
1135
return NS_OK;
1136
}
1137
1138
// Use PR_IpAddrNull to avoid address being reset to 0.
1139
if (PR_SUCCESS !=
1140
PR_SetNetAddr(PR_IpAddrNull, addr.raw.family, port, &addr)) {
1141
err_ = true;
1142
MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
1143
return NS_OK;
1144
}
1145
}
1146
1147
nsAutoPtr<MediaPacket> buf(new MediaPacket);
1148
buf->Copy(data.Elements(), data.Length());
1149
RefPtr<nr_udp_message> msg(new nr_udp_message(addr, buf));
1150
1151
RUN_ON_THREAD(sts_thread_,
1152
mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1153
&NrUdpSocketIpc::recv_callback_s, msg),
1154
NS_DISPATCH_NORMAL);
1155
return NS_OK;
1156
}
1157
1158
nsresult NrUdpSocketIpc::SetAddress() {
1159
uint16_t port = socket_child_->LocalPort();
1160
1161
nsAutoCString address(socket_child_->LocalAddress());
1162
1163
PRNetAddr praddr;
1164
if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, port, &praddr)) {
1165
err_ = true;
1166
MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
1167
return NS_OK;
1168
}
1169
1170
if (PR_SUCCESS != PR_StringToNetAddr(address.BeginReading(), &praddr)) {
1171
err_ = true;
1172
MOZ_ASSERT(false, "Failed to convert local host to PRNetAddr");
1173
return NS_OK;
1174
}
1175
1176
nr_transport_addr expected_addr;
1177
if (nr_transport_addr_copy(&expected_addr, &my_addr_)) {
1178
err_ = true;
1179
MOZ_ASSERT(false, "Failed to copy my_addr_");
1180
}
1181
1182
if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) {
1183
err_ = true;
1184
MOZ_ASSERT(false, "Failed to copy local host to my_addr_");
1185
}
1186
1187
if (!nr_transport_addr_is_wildcard(&expected_addr) &&
1188
nr_transport_addr_cmp(&expected_addr, &my_addr_,
1189
NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
1190
err_ = true;
1191
MOZ_ASSERT(false, "Address of opened socket is not expected");
1192
}
1193
1194
return NS_OK;
1195
}
1196
1197
// callback while UDP socket is opened
1198
NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() {
1199
ASSERT_ON_THREAD(io_thread_);
1200
ReentrantMonitorAutoEnter mon(monitor_);
1201
1202
r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket opened this=%p", (void*)this);
1203
nsresult rv = SetAddress();
1204
if (NS_FAILED(rv)) {
1205
return rv;
1206
}
1207
1208
mon.NotifyAll();
1209
1210
return NS_OK;
1211
}
1212
1213
// callback while UDP socket is connected
1214
NS_IMETHODIMP NrUdpSocketIpc::CallListenerConnected() {
1215
ASSERT_ON_THREAD(io_thread_);
1216
1217
ReentrantMonitorAutoEnter mon(monitor_);
1218
1219
r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket connected this=%p", (void*)this);
1220
MOZ_ASSERT(state_ == NR_CONNECTED);
1221
1222
nsresult rv = SetAddress();
1223
if (NS_FAILED(rv)) {
1224
mon.NotifyAll();
1225
return rv;
1226
}
1227
1228
r_log(LOG_GENERIC, LOG_INFO, "Exit UDP socket connected");
1229
mon.NotifyAll();
1230
1231
return NS_OK;
1232
}
1233
1234
// callback while UDP socket is closed
1235
NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() {
1236
ASSERT_ON_THREAD(io_thread_);
1237
1238
ReentrantMonitorAutoEnter mon(monitor_);
1239
1240
r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket closed this=%p", (void*)this);
1241
MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
1242
state_ = NR_CLOSED;
1243
1244
return NS_OK;
1245
}
1246
1247
//
1248
// NrSocketBase methods.
1249
//
1250
int NrUdpSocketIpc::create(nr_transport_addr* addr) {
1251
ASSERT_ON_THREAD(sts_thread_);
1252
1253
int r, _status;
1254
nsresult rv;
1255
int32_t port;
1256
nsCString host;
1257
1258
ReentrantMonitorAutoEnter mon(monitor_);
1259
1260
if (state_ != NR_INIT) {
1261
ABORT(R_INTERNAL);
1262
}
1263
1264
sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1265
if (NS_FAILED(rv)) {
1266
MOZ_ASSERT(false, "Failed to get STS thread");
1267
ABORT(R_INTERNAL);
1268
}
1269
1270
if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
1271
ABORT(r);
1272
}
1273
1274
// wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid
1275
if ((r = nr_transport_addr_copy(&my_addr_, addr))) {
1276
ABORT(r);
1277
}
1278
1279
state_ = NR_CONNECTING;
1280
1281
MOZ_ASSERT(io_thread_);
1282
RUN_ON_THREAD(io_thread_,
1283
mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1284
&NrUdpSocketIpc::create_i, host,
1285
static_cast<uint16_t>(port)),
1286
NS_DISPATCH_NORMAL);
1287
1288
// Wait until socket creation complete.
1289
mon.Wait();
1290
1291
if (err_) {
1292
close();
1293
ABORT(R_INTERNAL);
1294
}
1295
1296
state_ = NR_CONNECTED;
1297
1298
_status = 0;
1299
abort:
1300
return (_status);
1301
}
1302
1303
int NrUdpSocketIpc::sendto(const void* msg, size_t len, int flags,
1304
nr_transport_addr* to) {
1305
ASSERT_ON_THREAD(sts_thread_);
1306
1307
ReentrantMonitorAutoEnter mon(monitor_);
1308
1309
// If send err happened before, simply return the error.
1310
if (err_) {
1311
return R_IO_ERROR;
1312
}
1313
1314
if (state_ != NR_CONNECTED) {
1315
return R_INTERNAL;
1316
}
1317
1318
int r;
1319
net::NetAddr addr;
1320
if ((r = nr_transport_addr_to_netaddr(to, &addr))) {
1321
return r;
1322
}
1323
1324
if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
1325
return R_WOULDBLOCK;
1326
}
1327
1328
nsAutoPtr<MediaPacket> buf(new MediaPacket);
1329
buf->Copy(static_cast<const uint8_t*>(msg), len);
1330
1331
RUN_ON_THREAD(io_thread_,
1332
mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
1333
&NrUdpSocketIpc::sendto_i, addr, buf),
1334
NS_DISPATCH_NORMAL);
1335
return 0;
1336
}
1337
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
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
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
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
1448
int NrUdpSocketIpc::write(const void* msg, size_t len, size_t* written) {
1449
MOZ_ASSERT(false);
1450
return R_INTERNAL;
1451
}
1452
1453
int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t* len) {
1454
MOZ_ASSERT(false);
1455
return R_INTERNAL;
1456
}
1457
1458
int NrUdpSocketIpc::listen(int backlog) {
1459
MOZ_ASSERT(false);
1460
return R_INTERNAL;
1461
}
1462
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
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
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
1536
void NrUdpSocketIpc::sendto_i(const net::NetAddr& addr,
1537
nsAutoPtr<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
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
1564
static void ReleaseIOThread_s() { sThread->ReleaseUse(); }
1565
1566
// close(), but transfer the socket_child_ reference to die as well
1567
// static
1568
void NrUdpSocketIpc::destroy_i(dom::UDPSocketChild* aChild,
1569
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
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 */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1795
nr_socket_vtbl* NrSocketBase::vtbl() { return &nr_socket_local_vtbl; }