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, &param);
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