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 // Original author: ekr@rtfm.com
8
9 // Some of this code is cut-and-pasted from nICEr. Copyright is:
10
11 /*
12 Copyright (c) 2007, Adobe Systems, Incorporated
13 All rights reserved.
14
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
17 met:
18
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
21
22 * Redistributions in binary form must reproduce the above copyright
23 notice, this list of conditions and the following disclaimer in the
24 documentation and/or other materials provided with the distribution.
25
26 * Neither the name of Adobe Systems, Network Resonance nor the names of its
27 contributors may be used to endorse or promote products derived from
28 this software without specific prior written permission.
29
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 */
42
43 #include <string>
44 #include <vector>
45
46 #include "nr_socket_proxy_config.h"
47 #include "nsXULAppAPI.h"
48 #include "mozilla/UniquePtr.h"
49 #include "mozilla/Unused.h"
50
51 #include "logging.h"
52 #include "nspr.h"
53 #include "nss.h"
54 #include "pk11pub.h"
55 #include "plbase64.h"
56
57 #include "nsCOMPtr.h"
58 #include "nsComponentManagerUtils.h"
59 #include "nsError.h"
60 #include "nsNetCID.h"
61 #include "nsComponentManagerUtils.h"
62 #include "nsServiceManagerUtils.h"
63 #include "ScopedNSSTypes.h"
64 #include "runnable_utils.h"
65 #include "nsIPrefService.h"
66 #include "nsIPrefBranch.h"
67 #include "nsIUUIDGenerator.h"
68
69 // nICEr includes
70 extern "C" {
71 #include "nr_api.h"
72 #include "registry.h"
73 #include "async_timer.h"
74 #include "r_crc32.h"
75 #include "r_memory.h"
76 #include "ice_reg.h"
77 #include "ice_util.h"
78 #include "transport_addr.h"
79 #include "nr_crypto.h"
80 #include "nr_socket.h"
81 #include "nr_socket_local.h"
82 #include "stun_client_ctx.h"
83 #include "stun_reg.h"
84 #include "stun_server_ctx.h"
85 #include "stun_util.h"
86 #include "ice_codeword.h"
87 #include "ice_ctx.h"
88 #include "ice_candidate.h"
89 #include "ice_handler.h"
90 }
91
92 // Local includes
93 #include "nricectx.h"
94 #include "nricemediastream.h"
95 #include "nr_socket_prsock.h"
96 #include "nrinterfaceprioritizer.h"
97 #include "rlogconnector.h"
98 #include "test_nr_socket.h"
99
100 extern "C" {
101 #include "mdns_service/mdns_service.h"
102 }
103
104 namespace mozilla {
105
106 using std::shared_ptr;
107
nr_socket_short_term_violation_time()108 TimeStamp nr_socket_short_term_violation_time() {
109 return NrSocketBase::short_term_violation_time();
110 }
111
nr_socket_long_term_violation_time()112 TimeStamp nr_socket_long_term_violation_time() {
113 return NrSocketBase::long_term_violation_time();
114 }
115
116 MOZ_MTLOG_MODULE("mtransport")
117
118 const char kNrIceTransportUdp[] = "udp";
119 const char kNrIceTransportTcp[] = "tcp";
120 const char kNrIceTransportTls[] = "tls";
121
122 static bool initialized = false;
123
noop(void ** obj)124 static int noop(void** obj) { return 0; }
125
126 static nr_socket_factory_vtbl ctx_socket_factory_vtbl = {nr_socket_local_create,
127 noop};
128
129 // Implement NSPR-based crypto algorithms
nr_crypto_nss_random_bytes(UCHAR * buf,size_t len)130 static int nr_crypto_nss_random_bytes(UCHAR* buf, size_t len) {
131 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
132 if (!slot) return R_INTERNAL;
133
134 SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), buf, len);
135 if (rv != SECSuccess) return R_INTERNAL;
136
137 return 0;
138 }
139
nr_crypto_nss_hmac(UCHAR * key,size_t keyl,UCHAR * buf,size_t bufl,UCHAR * result)140 static int nr_crypto_nss_hmac(UCHAR* key, size_t keyl, UCHAR* buf, size_t bufl,
141 UCHAR* result) {
142 CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
143 PK11SlotInfo* slot = nullptr;
144 MOZ_ASSERT(keyl > 0);
145 SECItem keyi = {siBuffer, key, static_cast<unsigned int>(keyl)};
146 PK11SymKey* skey = nullptr;
147 PK11Context* hmac_ctx = nullptr;
148 SECStatus status;
149 unsigned int hmac_len;
150 SECItem param = {siBuffer, nullptr, 0};
151 int err = R_INTERNAL;
152
153 slot = PK11_GetInternalKeySlot();
154 if (!slot) goto abort;
155
156 skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_SIGN, &keyi,
157 nullptr);
158 if (!skey) goto abort;
159
160 hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, skey, ¶m);
161 if (!hmac_ctx) goto abort;
162
163 status = PK11_DigestBegin(hmac_ctx);
164 if (status != SECSuccess) goto abort;
165
166 status = PK11_DigestOp(hmac_ctx, buf, bufl);
167 if (status != SECSuccess) goto abort;
168
169 status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20);
170 if (status != SECSuccess) goto abort;
171
172 MOZ_ASSERT(hmac_len == 20);
173
174 err = 0;
175
176 abort:
177 if (hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE);
178 if (skey) PK11_FreeSymKey(skey);
179 if (slot) PK11_FreeSlot(slot);
180
181 return err;
182 }
183
nr_crypto_nss_md5(UCHAR * buf,size_t bufl,UCHAR * result)184 static int nr_crypto_nss_md5(UCHAR* buf, size_t bufl, UCHAR* result) {
185 int err = R_INTERNAL;
186 SECStatus rv;
187
188 const SECHashObject* ho = HASH_GetHashObject(HASH_AlgMD5);
189 MOZ_ASSERT(ho);
190 if (!ho) goto abort;
191
192 MOZ_ASSERT(ho->length == 16);
193
194 rv = HASH_HashBuf(ho->type, result, buf, bufl);
195 if (rv != SECSuccess) goto abort;
196
197 err = 0;
198 abort:
199 return err;
200 }
201
202 static nr_ice_crypto_vtbl nr_ice_crypto_nss_vtbl = {
203 nr_crypto_nss_random_bytes, nr_crypto_nss_hmac, nr_crypto_nss_md5};
204
ToNicerStunStruct(nr_ice_stun_server * server) const205 nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server* server) const {
206 int r;
207
208 memset(server, 0, sizeof(nr_ice_stun_server));
209 uint8_t protocol;
210 if (transport_ == kNrIceTransportUdp) {
211 protocol = IPPROTO_UDP;
212 } else if (transport_ == kNrIceTransportTcp) {
213 protocol = IPPROTO_TCP;
214 } else if (transport_ == kNrIceTransportTls) {
215 protocol = IPPROTO_TCP;
216 } else {
217 MOZ_MTLOG(ML_ERROR, "Unsupported STUN server transport: " << transport_);
218 return NS_ERROR_FAILURE;
219 }
220
221 if (has_addr_) {
222 if (transport_ == kNrIceTransportTls) {
223 // Refuse to try TLS without an FQDN
224 return NS_ERROR_INVALID_ARG;
225 }
226 r = nr_praddr_to_transport_addr(&addr_, &server->addr, protocol, 0);
227 if (r) {
228 return NS_ERROR_FAILURE;
229 }
230 } else {
231 MOZ_ASSERT(sizeof(server->addr.fqdn) > host_.size());
232 // Dummy information to keep nICEr happy
233 if (use_ipv6_if_fqdn_) {
234 nr_str_port_to_transport_addr("::", port_, protocol, &server->addr);
235 } else {
236 nr_str_port_to_transport_addr("0.0.0.0", port_, protocol, &server->addr);
237 }
238 PL_strncpyz(server->addr.fqdn, host_.c_str(), sizeof(server->addr.fqdn));
239 if (transport_ == kNrIceTransportTls) {
240 server->addr.tls = 1;
241 }
242 }
243
244 nr_transport_addr_fmt_addr_string(&server->addr);
245
246 return NS_OK;
247 }
248
ToNicerTurnStruct(nr_ice_turn_server * server) const249 nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server* server) const {
250 memset(server, 0, sizeof(nr_ice_turn_server));
251
252 nsresult rv = ToNicerStunStruct(&server->turn_server);
253 if (NS_FAILED(rv)) return rv;
254
255 if (!(server->username = r_strdup(username_.c_str())))
256 return NS_ERROR_OUT_OF_MEMORY;
257
258 // TODO(ekr@rtfm.com): handle non-ASCII passwords somehow?
259 // STUN requires they be SASLpreped, but we don't know if
260 // they are at this point.
261
262 // C++03 23.2.4, Paragraph 1 stipulates that the elements
263 // in std::vector must be contiguous, and can therefore be
264 // used as input to functions expecting C arrays.
265 const UCHAR* data = password_.empty() ? nullptr : &password_[0];
266 int r = r_data_create(&server->password, data, password_.size());
267 if (r) {
268 RFREE(server->username);
269 return NS_ERROR_OUT_OF_MEMORY;
270 }
271
272 return NS_OK;
273 }
274
NrIceCtx(const std::string & name,const Config & aConfig)275 NrIceCtx::NrIceCtx(const std::string& name, const Config& aConfig)
276 : connection_state_(ICE_CTX_INIT),
277 gathering_state_(ICE_CTX_GATHER_INIT),
278 name_(name),
279 ice_controlling_set_(false),
280 streams_(),
281 ctx_(nullptr),
282 peer_(nullptr),
283 ice_handler_vtbl_(nullptr),
284 ice_handler_(nullptr),
285 trickle_(true),
286 config_(aConfig),
287 nat_(nullptr),
288 proxy_config_(nullptr),
289 obfuscate_host_addresses_(false) {}
290
291 /* static */
Create(const std::string & aName,const Config & aConfig)292 RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& aName,
293 const Config& aConfig) {
294 RefPtr<NrIceCtx> ctx = new NrIceCtx(aName, aConfig);
295
296 if (!ctx->Initialize()) {
297 return nullptr;
298 }
299
300 return ctx;
301 }
302
CreateStream(const std::string & id,const std::string & name,int components)303 RefPtr<NrIceMediaStream> NrIceCtx::CreateStream(const std::string& id,
304 const std::string& name,
305 int components) {
306 if (streams_.count(id)) {
307 MOZ_ASSERT(false);
308 return nullptr;
309 }
310
311 RefPtr<NrIceMediaStream> stream =
312 new NrIceMediaStream(this, id, name, components);
313 streams_[id] = stream;
314 return stream;
315 }
316
DestroyStream(const std::string & id)317 void NrIceCtx::DestroyStream(const std::string& id) {
318 auto it = streams_.find(id);
319 if (it != streams_.end()) {
320 auto preexisting_stream = it->second;
321 streams_.erase(it);
322 preexisting_stream->Close();
323 }
324
325 if (streams_.empty()) {
326 SetGatheringState(ICE_CTX_GATHER_INIT);
327 }
328 }
329
330 // Handler callbacks
select_pair(void * obj,nr_ice_media_stream * stream,int component_id,nr_ice_cand_pair ** potentials,int potential_ct)331 int NrIceCtx::select_pair(void* obj, nr_ice_media_stream* stream,
332 int component_id, nr_ice_cand_pair** potentials,
333 int potential_ct) {
334 MOZ_MTLOG(ML_DEBUG, "select pair called: potential_ct = " << potential_ct);
335 MOZ_ASSERT(stream->local_stream);
336 MOZ_ASSERT(!stream->local_stream->obsolete);
337
338 return 0;
339 }
340
stream_ready(void * obj,nr_ice_media_stream * stream)341 int NrIceCtx::stream_ready(void* obj, nr_ice_media_stream* stream) {
342 MOZ_MTLOG(ML_DEBUG, "stream_ready called");
343 MOZ_ASSERT(!stream->local_stream);
344 MOZ_ASSERT(!stream->obsolete);
345
346 // Get the ICE ctx.
347 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
348
349 RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
350
351 // Streams which do not exist should never be ready.
352 MOZ_ASSERT(s);
353
354 s->Ready();
355
356 return 0;
357 }
358
stream_failed(void * obj,nr_ice_media_stream * stream)359 int NrIceCtx::stream_failed(void* obj, nr_ice_media_stream* stream) {
360 MOZ_MTLOG(ML_DEBUG, "stream_failed called");
361 MOZ_ASSERT(!stream->local_stream);
362 MOZ_ASSERT(!stream->obsolete);
363
364 // Get the ICE ctx
365 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
366 RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
367
368 // Streams which do not exist should never fail.
369 MOZ_ASSERT(s);
370
371 ctx->SetConnectionState(ICE_CTX_FAILED);
372 s->Failed();
373 return 0;
374 }
375
ice_checking(void * obj,nr_ice_peer_ctx * pctx)376 int NrIceCtx::ice_checking(void* obj, nr_ice_peer_ctx* pctx) {
377 MOZ_MTLOG(ML_DEBUG, "ice_checking called");
378
379 // Get the ICE ctx
380 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
381
382 ctx->SetConnectionState(ICE_CTX_CHECKING);
383
384 return 0;
385 }
386
ice_connected(void * obj,nr_ice_peer_ctx * pctx)387 int NrIceCtx::ice_connected(void* obj, nr_ice_peer_ctx* pctx) {
388 MOZ_MTLOG(ML_DEBUG, "ice_connected called");
389
390 // Get the ICE ctx
391 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
392
393 // This is called even on failed contexts.
394 if (ctx->connection_state() != ICE_CTX_FAILED) {
395 ctx->SetConnectionState(ICE_CTX_CONNECTED);
396 }
397
398 return 0;
399 }
400
ice_disconnected(void * obj,nr_ice_peer_ctx * pctx)401 int NrIceCtx::ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) {
402 MOZ_MTLOG(ML_DEBUG, "ice_disconnected called");
403
404 // Get the ICE ctx
405 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
406
407 ctx->SetConnectionState(ICE_CTX_DISCONNECTED);
408
409 return 0;
410 }
411
msg_recvd(void * obj,nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,int component_id,UCHAR * msg,int len)412 int NrIceCtx::msg_recvd(void* obj, nr_ice_peer_ctx* pctx,
413 nr_ice_media_stream* stream, int component_id,
414 UCHAR* msg, int len) {
415 // Get the ICE ctx
416 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
417 RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
418
419 // Streams which do not exist should never have packets.
420 MOZ_ASSERT(s);
421
422 s->SignalPacketReceived(s, component_id, msg, len);
423
424 return 0;
425 }
426
trickle_cb(void * arg,nr_ice_ctx * ice_ctx,nr_ice_media_stream * stream,int component_id,nr_ice_candidate * candidate)427 void NrIceCtx::trickle_cb(void* arg, nr_ice_ctx* ice_ctx,
428 nr_ice_media_stream* stream, int component_id,
429 nr_ice_candidate* candidate) {
430 if (stream->obsolete) {
431 // Stream was probably just marked obsolete, resulting in this callback
432 return;
433 }
434 // Get the ICE ctx
435 NrIceCtx* ctx = static_cast<NrIceCtx*>(arg);
436 RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
437
438 if (!s) {
439 // This stream has been removed because it is inactive
440 return;
441 }
442
443 if (!candidate) {
444 s->SignalCandidate(s, "", stream->ufrag, "", "");
445 return;
446 }
447
448 std::string actual_addr;
449 std::string mdns_addr;
450 ctx->GenerateObfuscatedAddress(candidate, &mdns_addr, &actual_addr);
451
452 // Format the candidate.
453 char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE];
454 int r = nr_ice_format_candidate_attribute(candidate, candidate_str,
455 sizeof(candidate_str),
456 ctx->obfuscate_host_addresses_);
457 MOZ_ASSERT(!r);
458 if (r) return;
459
460 MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate "
461 << candidate_str);
462
463 s->SignalCandidate(s, candidate_str, stream->ufrag, mdns_addr, actual_addr);
464 }
465
InitializeGlobals(const GlobalConfig & aConfig)466 void NrIceCtx::InitializeGlobals(const GlobalConfig& aConfig) {
467 RLogConnector::CreateInstance();
468 // Initialize the crypto callbacks and logging stuff
469 if (!initialized) {
470 NR_reg_init(NR_REG_MODE_LOCAL);
471 nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
472 initialized = true;
473
474 // Set the priorites for candidate type preferences.
475 // These numbers come from RFC 5245 S. 4.1.2.2
476 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_SRV_RFLX, 100);
477 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_PEER_RFLX, 110);
478 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_HOST, 126);
479 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_RELAYED, 5);
480 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP, 99);
481 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP, 109);
482 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_HOST_TCP, 125);
483 NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_RELAYED_TCP, 0);
484 NR_reg_set_uint4((char*)"stun.client.maximum_transmits",
485 aConfig.mStunClientMaxTransmits);
486 NR_reg_set_uint4((char*)NR_ICE_REG_TRICKLE_GRACE_PERIOD,
487 aConfig.mTrickleIceGracePeriod);
488 NR_reg_set_int4((char*)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT,
489 aConfig.mIceTcpSoSockCount);
490 NR_reg_set_int4((char*)NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG,
491 aConfig.mIceTcpListenBacklog);
492
493 NR_reg_set_char((char*)NR_ICE_REG_ICE_TCP_DISABLE, !aConfig.mTcpEnabled);
494
495 if (aConfig.mAllowLoopback) {
496 NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1);
497 }
498
499 if (aConfig.mAllowLinkLocal) {
500 NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, 1);
501 }
502 if (!aConfig.mForceNetInterface.Length()) {
503 NR_reg_set_string((char*)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME,
504 const_cast<char*>(aConfig.mForceNetInterface.get()));
505 }
506
507 // For now, always use nr_resolver for UDP.
508 NR_reg_set_char((char*)NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP, 1);
509
510 // Use nr_resolver for TCP only when not in e10s mode (for unit-tests)
511 if (XRE_IsParentProcess()) {
512 NR_reg_set_char((char*)NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP, 1);
513 }
514 }
515 }
516
SetTargetForDefaultLocalAddressLookup(const std::string & target_ip,uint16_t target_port)517 void NrIceCtx::SetTargetForDefaultLocalAddressLookup(
518 const std::string& target_ip, uint16_t target_port) {
519 nr_ice_set_target_for_default_local_address_lookup(ctx_, target_ip.c_str(),
520 target_port);
521 }
522
523 #define MAXADDRS 100 // mirrors setting in ice_ctx.c
524
525 /* static */
GetStunAddrs()526 nsTArray<NrIceStunAddr> NrIceCtx::GetStunAddrs() {
527 nsTArray<NrIceStunAddr> addrs;
528
529 nr_local_addr local_addrs[MAXADDRS];
530 int addr_ct = 0;
531
532 // most likely running on parent process and need crypto vtbl
533 // initialized on Windows (Linux and OSX don't seem to care)
534 if (!initialized) {
535 nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
536 }
537
538 MOZ_MTLOG(ML_INFO, "NrIceCtx static call to find local stun addresses");
539 if (nr_stun_find_local_addresses(local_addrs, MAXADDRS, &addr_ct)) {
540 MOZ_MTLOG(ML_INFO, "Error finding local stun addresses");
541 } else {
542 for (int i = 0; i < addr_ct; ++i) {
543 NrIceStunAddr addr(&local_addrs[i]);
544 addrs.AppendElement(addr);
545 }
546 }
547
548 return addrs;
549 }
550
SetStunAddrs(const nsTArray<NrIceStunAddr> & addrs)551 void NrIceCtx::SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs) {
552 nr_local_addr* local_addrs;
553 local_addrs = new nr_local_addr[addrs.Length()];
554
555 for (size_t i = 0; i < addrs.Length(); ++i) {
556 nr_local_addr_copy(&local_addrs[i],
557 const_cast<nr_local_addr*>(&addrs[i].localAddr()));
558 }
559 nr_ice_set_local_addresses(ctx_, local_addrs, addrs.Length());
560
561 delete[] local_addrs;
562 }
563
Initialize()564 bool NrIceCtx::Initialize() {
565 // Create the ICE context
566 int r;
567
568 UINT4 flags = NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
569 switch (config_.mPolicy) {
570 case ICE_POLICY_RELAY:
571 flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY;
572 break;
573 case ICE_POLICY_NO_HOST:
574 flags |= NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES;
575 break;
576 case ICE_POLICY_ALL:
577 break;
578 }
579
580 r = nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ctx_);
581
582 if (r) {
583 MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'");
584 return false;
585 }
586
587 // override default factory to capture optional proxy config when creating
588 // sockets.
589 nr_socket_factory* factory;
590 r = nr_socket_factory_create_int(this, &ctx_socket_factory_vtbl, &factory);
591
592 if (r) {
593 MOZ_MTLOG(LogLevel::Error, "Couldn't create ctx socket factory.");
594 return false;
595 }
596 nr_ice_ctx_set_socket_factory(ctx_, factory);
597
598 nr_interface_prioritizer* prioritizer = CreateInterfacePrioritizer();
599 if (!prioritizer) {
600 MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer.");
601 return false;
602 }
603
604 r = nr_ice_ctx_set_interface_prioritizer(ctx_, prioritizer);
605 if (r) {
606 MOZ_MTLOG(LogLevel::Error, "Couldn't set interface prioritizer.");
607 return false;
608 }
609
610 if (generating_trickle()) {
611 r = nr_ice_ctx_set_trickle_cb(ctx_, &NrIceCtx::trickle_cb, this);
612 if (r) {
613 MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name_ << "'");
614 return false;
615 }
616 }
617
618 if (config_.mNatSimulatorConfig.isSome()) {
619 TestNat* test_nat = new TestNat;
620 test_nat->filtering_type_ = TestNat::ToNatBehavior(
621 config_.mNatSimulatorConfig->mFilteringType.get());
622 test_nat->mapping_type_ =
623 TestNat::ToNatBehavior(config_.mNatSimulatorConfig->mMappingType.get());
624 test_nat->block_udp_ = config_.mNatSimulatorConfig->mBlockUdp;
625 test_nat->block_tcp_ = config_.mNatSimulatorConfig->mBlockTcp;
626 test_nat->error_code_for_drop_ =
627 config_.mNatSimulatorConfig->mErrorCodeForDrop;
628 if (config_.mNatSimulatorConfig->mRedirectAddress.Length()) {
629 test_nat
630 ->stun_redirect_map_[config_.mNatSimulatorConfig->mRedirectAddress] =
631 config_.mNatSimulatorConfig->mRedirectTargets;
632 }
633 test_nat->enabled_ = true;
634 SetNat(test_nat);
635 }
636
637 // Create the handler objects
638 ice_handler_vtbl_ = new nr_ice_handler_vtbl();
639 ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
640 ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready;
641 ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed;
642 ice_handler_vtbl_->ice_connected = &NrIceCtx::ice_connected;
643 ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd;
644 ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking;
645 ice_handler_vtbl_->ice_disconnected = &NrIceCtx::ice_disconnected;
646
647 ice_handler_ = new nr_ice_handler();
648 ice_handler_->vtbl = ice_handler_vtbl_;
649 ice_handler_->obj = this;
650
651 // Create the peer ctx. Because we do not support parallel forking, we
652 // only have one peer ctx.
653 std::string peer_name = name_ + ":default";
654 r = nr_ice_peer_ctx_create(ctx_, ice_handler_,
655 const_cast<char*>(peer_name.c_str()), &peer_);
656 if (r) {
657 MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name_ << "'");
658 return false;
659 }
660
661 nsresult rv;
662 sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
663
664 if (!NS_SUCCEEDED(rv)) return false;
665
666 return true;
667 }
668
SetNat(const RefPtr<TestNat> & aNat)669 int NrIceCtx::SetNat(const RefPtr<TestNat>& aNat) {
670 nat_ = aNat;
671 nr_socket_factory* fac;
672 int r = nat_->create_socket_factory(&fac);
673 if (r) {
674 return r;
675 }
676 nr_ice_ctx_set_socket_factory(ctx_, fac);
677 return 0;
678 }
679
680 // ONLY USE THIS FOR TESTING. Will cause totally unpredictable and possibly very
681 // bad effects if ICE is still live.
internal_DeinitializeGlobal()682 void NrIceCtx::internal_DeinitializeGlobal() {
683 NR_reg_del((char*)"stun");
684 NR_reg_del((char*)"ice");
685 RLogConnector::DestroyInstance();
686 nr_crypto_vtbl = nullptr;
687 initialized = false;
688 }
689
internal_SetTimerAccelarator(int divider)690 void NrIceCtx::internal_SetTimerAccelarator(int divider) {
691 ctx_->test_timer_divider = divider;
692 }
693
AccumulateStats(const NrIceStats & stats)694 void NrIceCtx::AccumulateStats(const NrIceStats& stats) {
695 nr_accumulate_count(&(ctx_->stats.stun_retransmits), stats.stun_retransmits);
696 nr_accumulate_count(&(ctx_->stats.turn_401s), stats.turn_401s);
697 nr_accumulate_count(&(ctx_->stats.turn_403s), stats.turn_403s);
698 nr_accumulate_count(&(ctx_->stats.turn_438s), stats.turn_438s);
699 }
700
Destroy()701 NrIceStats NrIceCtx::Destroy() {
702 // designed to be called more than once so if stats are desired, this can be
703 // called just prior to the destructor
704 MOZ_MTLOG(ML_DEBUG, "Destroying ICE ctx '" << name_ << "'");
705 for (auto& idAndStream : streams_) {
706 idAndStream.second->Close();
707 }
708
709 NrIceStats stats;
710 if (ctx_) {
711 stats.stun_retransmits = ctx_->stats.stun_retransmits;
712 stats.turn_401s = ctx_->stats.turn_401s;
713 stats.turn_403s = ctx_->stats.turn_403s;
714 stats.turn_438s = ctx_->stats.turn_438s;
715 }
716
717 if (peer_) {
718 nr_ice_peer_ctx_destroy(&peer_);
719 }
720 if (ctx_) {
721 nr_ice_ctx_destroy(&ctx_);
722 }
723
724 delete ice_handler_vtbl_;
725 delete ice_handler_;
726
727 ice_handler_vtbl_ = nullptr;
728 ice_handler_ = nullptr;
729 proxy_config_ = nullptr;
730 streams_.clear();
731
732 return stats;
733 }
734
735 NrIceCtx::~NrIceCtx() = default;
736
destroy_peer_ctx()737 void NrIceCtx::destroy_peer_ctx() { nr_ice_peer_ctx_destroy(&peer_); }
738
SetControlling(Controlling controlling)739 nsresult NrIceCtx::SetControlling(Controlling controlling) {
740 if (!ice_controlling_set_) {
741 peer_->controlling = (controlling == ICE_CONTROLLING) ? 1 : 0;
742 ice_controlling_set_ = true;
743
744 MOZ_MTLOG(ML_DEBUG,
745 "ICE ctx " << name_ << " setting controlling to" << controlling);
746 }
747 return NS_OK;
748 }
749
GetControlling()750 NrIceCtx::Controlling NrIceCtx::GetControlling() {
751 return (peer_->controlling) ? ICE_CONTROLLING : ICE_CONTROLLED;
752 }
753
SetStunServers(const std::vector<NrIceStunServer> & stun_servers)754 nsresult NrIceCtx::SetStunServers(
755 const std::vector<NrIceStunServer>& stun_servers) {
756 if (stun_servers.empty()) return NS_OK;
757
758 // We assume nr_ice_stun_server is memmoveable. That's true right now.
759 std::vector<nr_ice_stun_server> servers;
760
761 for (size_t i = 0; i < stun_servers.size(); ++i) {
762 nr_ice_stun_server server;
763 nsresult rv = stun_servers[i].ToNicerStunStruct(&server);
764 if (NS_WARN_IF(NS_FAILED(rv))) {
765 MOZ_MTLOG(ML_ERROR, "Couldn't convert STUN server for '" << name_ << "'");
766 } else {
767 servers.push_back(server);
768 }
769 }
770
771 int r = nr_ice_ctx_set_stun_servers(ctx_, &servers[0],
772 static_cast<int>(servers.size()));
773 if (r) {
774 MOZ_MTLOG(ML_ERROR, "Couldn't set STUN servers for '" << name_ << "'");
775 return NS_ERROR_FAILURE;
776 }
777
778 return NS_OK;
779 }
780
781 // TODO(ekr@rtfm.com): This is just SetStunServers with s/Stun/Turn
782 // Could we do a template or something?
SetTurnServers(const std::vector<NrIceTurnServer> & turn_servers)783 nsresult NrIceCtx::SetTurnServers(
784 const std::vector<NrIceTurnServer>& turn_servers) {
785 if (turn_servers.empty()) return NS_OK;
786
787 // We assume nr_ice_turn_server is memmoveable. That's true right now.
788 std::vector<nr_ice_turn_server> servers;
789
790 for (size_t i = 0; i < turn_servers.size(); ++i) {
791 nr_ice_turn_server server;
792 nsresult rv = turn_servers[i].ToNicerTurnStruct(&server);
793 if (NS_WARN_IF(NS_FAILED(rv))) {
794 MOZ_MTLOG(ML_ERROR, "Couldn't convert TURN server for '" << name_ << "'");
795 } else {
796 servers.push_back(server);
797 }
798 }
799
800 int r = nr_ice_ctx_set_turn_servers(ctx_, &servers[0],
801 static_cast<int>(servers.size()));
802 if (r) {
803 MOZ_MTLOG(ML_ERROR, "Couldn't set TURN servers for '" << name_ << "'");
804 // TODO(ekr@rtfm.com): This leaks the username/password. Need to free that.
805 return NS_ERROR_FAILURE;
806 }
807
808 return NS_OK;
809 }
810
SetResolver(nr_resolver * resolver)811 nsresult NrIceCtx::SetResolver(nr_resolver* resolver) {
812 int r = nr_ice_ctx_set_resolver(ctx_, resolver);
813
814 if (r) {
815 MOZ_MTLOG(ML_ERROR, "Couldn't set resolver for '" << name_ << "'");
816 return NS_ERROR_FAILURE;
817 }
818
819 return NS_OK;
820 }
821
SetProxyConfig(NrSocketProxyConfig && config)822 nsresult NrIceCtx::SetProxyConfig(NrSocketProxyConfig&& config) {
823 proxy_config_.reset(new NrSocketProxyConfig(std::move(config)));
824 return NS_OK;
825 }
826
SetCtxFlags(bool default_route_only)827 void NrIceCtx::SetCtxFlags(bool default_route_only) {
828 ASSERT_ON_THREAD(sts_target_);
829
830 if (default_route_only) {
831 nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS);
832 } else {
833 nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS);
834 }
835 }
836
StartGathering(bool default_route_only,bool obfuscate_host_addresses)837 nsresult NrIceCtx::StartGathering(bool default_route_only,
838 bool obfuscate_host_addresses) {
839 ASSERT_ON_THREAD(sts_target_);
840
841 obfuscate_host_addresses_ = obfuscate_host_addresses;
842
843 SetCtxFlags(default_route_only);
844
845 // This might start gathering for the first time, or again after
846 // renegotiation, or might do nothing at all if gathering has already
847 // finished.
848 int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this);
849
850 if (!r) {
851 SetGatheringState(ICE_CTX_GATHER_COMPLETE);
852 } else if (r == R_WOULDBLOCK) {
853 SetGatheringState(ICE_CTX_GATHER_STARTED);
854 } else {
855 SetGatheringState(ICE_CTX_GATHER_COMPLETE);
856 MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't gather ICE candidates for '"
857 << name_ << "', error=" << r);
858 SetConnectionState(ICE_CTX_FAILED);
859 return NS_ERROR_FAILURE;
860 }
861
862 return NS_OK;
863 }
864
FindStream(nr_ice_media_stream * stream)865 RefPtr<NrIceMediaStream> NrIceCtx::FindStream(nr_ice_media_stream* stream) {
866 for (auto& idAndStream : streams_) {
867 if (idAndStream.second->HasStream(stream)) {
868 return idAndStream.second;
869 }
870 }
871
872 return nullptr;
873 }
874
GetGlobalAttributes()875 std::vector<std::string> NrIceCtx::GetGlobalAttributes() {
876 char** attrs = nullptr;
877 int attrct;
878 int r;
879 std::vector<std::string> ret;
880
881 r = nr_ice_get_global_attributes(ctx_, &attrs, &attrct);
882 if (r) {
883 MOZ_MTLOG(ML_ERROR,
884 "Couldn't get ufrag and password for '" << name_ << "'");
885 return ret;
886 }
887
888 for (int i = 0; i < attrct; i++) {
889 ret.push_back(std::string(attrs[i]));
890 RFREE(attrs[i]);
891 }
892 RFREE(attrs);
893
894 return ret;
895 }
896
ParseGlobalAttributes(std::vector<std::string> attrs)897 nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) {
898 std::vector<char*> attrs_in;
899 attrs_in.reserve(attrs.size());
900 for (auto& attr : attrs) {
901 attrs_in.push_back(const_cast<char*>(attr.c_str()));
902 }
903
904 int r = nr_ice_peer_ctx_parse_global_attributes(
905 peer_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size());
906 if (r) {
907 MOZ_MTLOG(ML_ERROR,
908 "Couldn't parse global attributes for " << name_ << "'");
909 return NS_ERROR_FAILURE;
910 }
911
912 return NS_OK;
913 }
914
HasStreamsToConnect() const915 bool NrIceCtx::HasStreamsToConnect() const {
916 for (auto& idAndStream : streams_) {
917 if (idAndStream.second->state() != NrIceMediaStream::ICE_CLOSED) {
918 return true;
919 }
920 }
921 return false;
922 }
923
StartChecks()924 nsresult NrIceCtx::StartChecks() {
925 int r;
926 if (!HasStreamsToConnect()) {
927 // Nothing to do
928 return NS_OK;
929 }
930
931 r = nr_ice_peer_ctx_pair_candidates(peer_);
932 if (r) {
933 MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't pair candidates on " << name_);
934 SetConnectionState(ICE_CTX_FAILED);
935 return NS_ERROR_FAILURE;
936 }
937
938 r = nr_ice_peer_ctx_start_checks2(peer_, 1);
939 if (r) {
940 if (r == R_NOT_FOUND) {
941 MOZ_MTLOG(ML_INFO, "Couldn't start peer checks on "
942 << name_ << ", assuming trickle ICE");
943 } else {
944 MOZ_MTLOG(ML_ERROR,
945 "ICE FAILED: Couldn't start peer checks on " << name_);
946 SetConnectionState(ICE_CTX_FAILED);
947 return NS_ERROR_FAILURE;
948 }
949 }
950
951 return NS_OK;
952 }
953
gather_cb(NR_SOCKET s,int h,void * arg)954 void NrIceCtx::gather_cb(NR_SOCKET s, int h, void* arg) {
955 NrIceCtx* ctx = static_cast<NrIceCtx*>(arg);
956
957 ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE);
958 }
959
Finalize()960 nsresult NrIceCtx::Finalize() {
961 int r = nr_ice_ctx_finalize(ctx_, peer_);
962
963 if (r) {
964 MOZ_MTLOG(ML_ERROR, "Couldn't finalize " << name_ << "'");
965 return NS_ERROR_FAILURE;
966 }
967
968 return NS_OK;
969 }
970
UpdateNetworkState(bool online)971 void NrIceCtx::UpdateNetworkState(bool online) {
972 MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): updating network state to "
973 << (online ? "online" : "offline"));
974 if (connection_state_ == ICE_CTX_CLOSED) {
975 return;
976 }
977
978 if (online) {
979 nr_ice_peer_ctx_refresh_consent_all_streams(peer_);
980 } else {
981 nr_ice_peer_ctx_disconnect_all_streams(peer_);
982 }
983 }
984
SetConnectionState(ConnectionState state)985 void NrIceCtx::SetConnectionState(ConnectionState state) {
986 if (state == connection_state_) return;
987
988 MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " << connection_state_
989 << "->" << state);
990 connection_state_ = state;
991
992 if (connection_state_ == ICE_CTX_FAILED) {
993 MOZ_MTLOG(ML_INFO,
994 "NrIceCtx(" << name_ << "): dumping r_log ringbuffer... ");
995 std::deque<std::string> logs;
996 RLogConnector::GetInstance()->GetAny(0, &logs);
997 for (auto& log : logs) {
998 MOZ_MTLOG(ML_INFO, log);
999 }
1000 }
1001
1002 SignalConnectionStateChange(this, state);
1003 }
1004
SetGatheringState(GatheringState state)1005 void NrIceCtx::SetGatheringState(GatheringState state) {
1006 if (state == gathering_state_) return;
1007
1008 MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << name_ << "): gathering state "
1009 << gathering_state_ << "->" << state);
1010 gathering_state_ = state;
1011
1012 SignalGatheringStateChange(this, state);
1013 }
1014
GenerateObfuscatedAddress(nr_ice_candidate * candidate,std::string * mdns_address,std::string * actual_address)1015 void NrIceCtx::GenerateObfuscatedAddress(nr_ice_candidate* candidate,
1016 std::string* mdns_address,
1017 std::string* actual_address) {
1018 if (candidate->type == HOST && obfuscate_host_addresses_) {
1019 char addr[64];
1020 if (nr_transport_addr_get_addrstring(&candidate->addr, addr,
1021 sizeof(addr))) {
1022 return;
1023 }
1024
1025 *actual_address = addr;
1026
1027 const auto& iter = obfuscated_host_addresses_.find(*actual_address);
1028 if (iter != obfuscated_host_addresses_.end()) {
1029 *mdns_address = iter->second;
1030 } else {
1031 nsresult rv;
1032 nsCOMPtr<nsIUUIDGenerator> uuidgen =
1033 do_GetService("@mozilla.org/uuid-generator;1", &rv);
1034 // If this fails, we'll return a zero UUID rather than something
1035 // unexpected.
1036 nsID id = {};
1037 id.Clear();
1038 if (NS_SUCCEEDED(rv)) {
1039 rv = uuidgen->GenerateUUIDInPlace(&id);
1040 if (NS_FAILED(rv)) {
1041 id.Clear();
1042 }
1043 }
1044
1045 char chars[NSID_LENGTH];
1046 id.ToProvidedString(chars);
1047 // The string will look like {64888863-a253-424a-9b30-1ed285d20142},
1048 // we want to trim off the braces.
1049 const char* ptr_to_id = chars;
1050 ++ptr_to_id;
1051 chars[NSID_LENGTH - 2] = 0;
1052
1053 std::ostringstream o;
1054 o << ptr_to_id << ".local";
1055 *mdns_address = o.str();
1056
1057 obfuscated_host_addresses_[*actual_address] = *mdns_address;
1058 }
1059 candidate->mdns_addr = r_strdup(mdns_address->c_str());
1060 }
1061 }
1062
1063 } // namespace mozilla
1064
1065 // Reimplement nr_ice_compute_codeword to avoid copyright issues
nr_ice_compute_codeword(char * buf,int len,char * codeword)1066 void nr_ice_compute_codeword(char* buf, int len, char* codeword) {
1067 UINT4 c;
1068
1069 r_crc32(buf, len, &c);
1070
1071 PL_Base64Encode(reinterpret_cast<char*>(&c), 3, codeword);
1072 codeword[4] = 0;
1073 }
1074
nr_socket_local_create(void * obj,nr_transport_addr * addr,nr_socket ** sockp)1075 int nr_socket_local_create(void* obj, nr_transport_addr* addr,
1076 nr_socket** sockp) {
1077 using namespace mozilla;
1078
1079 RefPtr<NrSocketBase> sock;
1080 int r, _status;
1081 shared_ptr<NrSocketProxyConfig> config = nullptr;
1082
1083 if (obj) {
1084 config = static_cast<NrIceCtx*>(obj)->GetProxyConfig();
1085 }
1086
1087 r = NrSocketBase::CreateSocket(addr, &sock, config);
1088 if (r) {
1089 ABORT(r);
1090 }
1091
1092 r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp);
1093 if (r) ABORT(r);
1094
1095 _status = 0;
1096
1097 {
1098 // We will release this reference in destroy(), not exactly the normal
1099 // ownership model, but it is what it is.
1100 NrSocketBase* dummy = sock.forget().take();
1101 (void)dummy;
1102 }
1103
1104 abort:
1105 return _status;
1106 }
1107