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