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