Bug 1652557: don't skip test_bug767684.html when xorigin iframes and fission are...
[gecko.git] / media / mtransport / nricectx.cpp
blob0a7e976a39c9be032a3c07f3e14e9dd97cc20777
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/. */
7 // Original author: ekr@rtfm.com
9 // Some of this code is cut-and-pasted from nICEr. Copyright is:
12 Copyright (c) 2007, Adobe Systems, Incorporated
13 All rights reserved.
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
17 met:
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
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.
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.
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.
43 #include <string>
44 #include <vector>
46 #include "nr_socket_proxy_config.h"
47 #include "mozilla/UniquePtr.h"
48 #include "mozilla/Unused.h"
50 #include "logging.h"
51 #include "nspr.h"
52 #include "nss.h"
53 #include "pk11pub.h"
54 #include "plbase64.h"
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"
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"
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"
99 extern "C" {
100 #include "mdns_service/mdns_service.h"
103 namespace mozilla {
105 using std::shared_ptr;
107 TimeStamp nr_socket_short_term_violation_time() {
108 return NrSocketBase::short_term_violation_time();
111 TimeStamp nr_socket_long_term_violation_time() {
112 return NrSocketBase::long_term_violation_time();
115 MOZ_MTLOG_MODULE("mtransport")
117 const char kNrIceTransportUdp[] = "udp";
118 const char kNrIceTransportTcp[] = "tcp";
119 const char kNrIceTransportTls[] = "tls";
121 static bool initialized = false;
123 static int noop(void** obj) { return 0; }
125 static nr_socket_factory_vtbl ctx_socket_factory_vtbl = {nr_socket_local_create,
126 noop};
128 // Implement NSPR-based crypto algorithms
129 static int nr_crypto_nss_random_bytes(UCHAR* buf, size_t len) {
130 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
131 if (!slot) return R_INTERNAL;
133 SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), buf, len);
134 if (rv != SECSuccess) return R_INTERNAL;
136 return 0;
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;
152 slot = PK11_GetInternalKeySlot();
153 if (!slot) goto abort;
155 skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_SIGN, &keyi,
156 nullptr);
157 if (!skey) goto abort;
159 hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, skey, &param);
160 if (!hmac_ctx) goto abort;
162 status = PK11_DigestBegin(hmac_ctx);
163 if (status != SECSuccess) goto abort;
165 status = PK11_DigestOp(hmac_ctx, buf, bufl);
166 if (status != SECSuccess) goto abort;
168 status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20);
169 if (status != SECSuccess) goto abort;
171 MOZ_ASSERT(hmac_len == 20);
173 err = 0;
175 abort:
176 if (hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE);
177 if (skey) PK11_FreeSymKey(skey);
178 if (slot) PK11_FreeSlot(slot);
180 return err;
183 static int nr_crypto_nss_md5(UCHAR* buf, size_t bufl, UCHAR* result) {
184 int err = R_INTERNAL;
185 SECStatus rv;
187 const SECHashObject* ho = HASH_GetHashObject(HASH_AlgMD5);
188 MOZ_ASSERT(ho);
189 if (!ho) goto abort;
191 MOZ_ASSERT(ho->length == 16);
193 rv = HASH_HashBuf(ho->type, result, buf, bufl);
194 if (rv != SECSuccess) goto abort;
196 err = 0;
197 abort:
198 return err;
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};
204 nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server* server) const {
205 int r;
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;
218 server->tls = 1;
219 } else {
220 MOZ_MTLOG(ML_ERROR, "Unsupported STUN server transport: " << transport_);
221 return NS_ERROR_FAILURE;
224 if (has_addr_) {
225 r = nr_praddr_to_transport_addr(&addr_, &server->u.addr, server->transport,
227 if (r) {
228 return NS_ERROR_FAILURE;
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;
239 return NS_OK;
242 nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server* server) const {
243 memset(server, 0, sizeof(nr_ice_turn_server));
245 nsresult rv = ToNicerStunStruct(&server->turn_server);
246 if (NS_FAILED(rv)) return rv;
248 if (!(server->username = r_strdup(username_.c_str())))
249 return NS_ERROR_OUT_OF_MEMORY;
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.
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;
265 return NS_OK;
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) {}
284 /* static */
285 RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& aName,
286 const Config& aConfig) {
287 RefPtr<NrIceCtx> ctx = new NrIceCtx(aName, aConfig);
289 if (!ctx->Initialize()) {
290 return nullptr;
293 return ctx;
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;
304 RefPtr<NrIceMediaStream> stream =
305 new NrIceMediaStream(this, id, name, components);
306 streams_[id] = stream;
307 return stream;
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();
318 if (streams_.empty()) {
319 SetGatheringState(ICE_CTX_GATHER_INIT);
323 // Handler callbacks
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);
331 return 0;
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);
339 // Get the ICE ctx.
340 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
342 RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
344 // Streams which do not exist should never be ready.
345 MOZ_ASSERT(s);
347 s->Ready();
349 return 0;
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);
357 // Get the ICE ctx
358 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
359 RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
361 // Streams which do not exist should never fail.
362 MOZ_ASSERT(s);
364 ctx->SetConnectionState(ICE_CTX_FAILED);
365 s->Failed();
366 return 0;
369 int NrIceCtx::ice_checking(void* obj, nr_ice_peer_ctx* pctx) {
370 MOZ_MTLOG(ML_DEBUG, "ice_checking called");
372 // Get the ICE ctx
373 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
375 ctx->SetConnectionState(ICE_CTX_CHECKING);
377 return 0;
380 int NrIceCtx::ice_connected(void* obj, nr_ice_peer_ctx* pctx) {
381 MOZ_MTLOG(ML_DEBUG, "ice_connected called");
383 // Get the ICE ctx
384 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
386 // This is called even on failed contexts.
387 if (ctx->connection_state() != ICE_CTX_FAILED) {
388 ctx->SetConnectionState(ICE_CTX_CONNECTED);
391 return 0;
394 int NrIceCtx::ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) {
395 MOZ_MTLOG(ML_DEBUG, "ice_disconnected called");
397 // Get the ICE ctx
398 NrIceCtx* ctx = static_cast<NrIceCtx*>(obj);
400 ctx->SetConnectionState(ICE_CTX_DISCONNECTED);
402 return 0;
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);
412 // Streams which do not exist should never have packets.
413 MOZ_ASSERT(s);
415 s->SignalPacketReceived(s, component_id, msg, len);
417 return 0;
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);
428 if (!s) {
429 // This stream has been removed because it is inactive
430 return;
433 if (!candidate) {
434 s->SignalCandidate(s, "", stream->ufrag, "", "");
435 return;
438 std::string actual_addr;
439 std::string mdns_addr;
440 ctx->GenerateObfuscatedAddress(candidate, &mdns_addr, &actual_addr);
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;
450 MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate "
451 << candidate_str);
453 s->SignalCandidate(s, candidate_str, stream->ufrag, mdns_addr, actual_addr);
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;
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);
483 NR_reg_set_char((char*)NR_ICE_REG_ICE_TCP_DISABLE, !aConfig.mTcpEnabled);
485 if (aConfig.mAllowLoopback) {
486 NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1);
489 if (aConfig.mAllowLinkLocal) {
490 NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, 1);
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()));
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);
505 #define MAXADDRS 100 // mirrors setting in ice_ctx.c
507 /* static */
508 nsTArray<NrIceStunAddr> NrIceCtx::GetStunAddrs() {
509 nsTArray<NrIceStunAddr> addrs;
511 nr_local_addr local_addrs[MAXADDRS];
512 int addr_ct = 0;
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;
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);
530 return addrs;
533 void NrIceCtx::SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs) {
534 nr_local_addr* local_addrs;
535 local_addrs = new nr_local_addr[addrs.Length()];
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()));
541 nr_ice_set_local_addresses(ctx_, local_addrs, addrs.Length());
543 delete[] local_addrs;
546 bool NrIceCtx::Initialize() {
547 // Create the ICE context
548 int r;
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;
562 r = nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ctx_);
564 if (r) {
565 MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'");
566 return false;
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);
574 if (r) {
575 MOZ_MTLOG(LogLevel::Error, "Couldn't create ctx socket factory.");
576 return false;
578 nr_ice_ctx_set_socket_factory(ctx_, factory);
580 nr_interface_prioritizer* prioritizer = CreateInterfacePrioritizer();
581 if (!prioritizer) {
582 MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer.");
583 return false;
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;
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;
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);
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;
622 ice_handler_ = new nr_ice_handler();
623 ice_handler_->vtbl = ice_handler_vtbl_;
624 ice_handler_->obj = this;
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;
636 nsresult rv;
637 sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
639 if (!NS_SUCCEEDED(rv)) return false;
641 return true;
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;
651 nr_ice_ctx_set_socket_factory(ctx_, fac);
652 return 0;
655 // ONLY USE THIS FOR TESTING. Will cause totally unpredictable and possibly very
656 // bad effects if ICE is still live.
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;
665 void NrIceCtx::internal_SetTimerAccelarator(int divider) {
666 ctx_->test_timer_divider = divider;
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);
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();
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;
692 if (peer_) {
693 nr_ice_peer_ctx_destroy(&peer_);
695 if (ctx_) {
696 nr_ice_ctx_destroy(&ctx_);
699 delete ice_handler_vtbl_;
700 delete ice_handler_;
702 ice_handler_vtbl_ = nullptr;
703 ice_handler_ = nullptr;
704 proxy_config_ = nullptr;
705 streams_.clear();
707 return stats;
710 NrIceCtx::~NrIceCtx() = default;
712 void NrIceCtx::destroy_peer_ctx() { nr_ice_peer_ctx_destroy(&peer_); }
714 nsresult NrIceCtx::SetControlling(Controlling controlling) {
715 if (!ice_controlling_set_) {
716 peer_->controlling = (controlling == ICE_CONTROLLING) ? 1 : 0;
717 ice_controlling_set_ = true;
719 MOZ_MTLOG(ML_DEBUG,
720 "ICE ctx " << name_ << " setting controlling to" << controlling);
722 return NS_OK;
725 NrIceCtx::Controlling NrIceCtx::GetControlling() {
726 return (peer_->controlling) ? ICE_CONTROLLING : ICE_CONTROLLED;
729 nsresult NrIceCtx::SetStunServers(
730 const std::vector<NrIceStunServer>& stun_servers) {
731 if (stun_servers.empty()) return NS_OK;
733 auto servers = MakeUnique<nr_ice_stun_server[]>(stun_servers.size());
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;
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;
749 return NS_OK;
752 // TODO(ekr@rtfm.com): This is just SetStunServers with s/Stun/Turn
753 // Could we do a template or something?
754 nsresult NrIceCtx::SetTurnServers(
755 const std::vector<NrIceTurnServer>& turn_servers) {
756 if (turn_servers.empty()) return NS_OK;
758 auto servers = MakeUnique<nr_ice_turn_server[]>(turn_servers.size());
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;
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;
774 // TODO(ekr@rtfm.com): This leaks the username/password. Need to free that.
776 return NS_OK;
779 nsresult NrIceCtx::SetResolver(nr_resolver* resolver) {
780 int r = nr_ice_ctx_set_resolver(ctx_, resolver);
782 if (r) {
783 MOZ_MTLOG(ML_ERROR, "Couldn't set resolver for '" << name_ << "'");
784 return NS_ERROR_FAILURE;
787 return NS_OK;
790 nsresult NrIceCtx::SetProxyConfig(NrSocketProxyConfig&& config) {
791 proxy_config_.reset(new NrSocketProxyConfig(std::move(config)));
792 return NS_OK;
795 void NrIceCtx::SetCtxFlags(bool default_route_only) {
796 ASSERT_ON_THREAD(sts_target_);
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);
805 nsresult NrIceCtx::StartGathering(bool default_route_only,
806 bool obfuscate_host_addresses) {
807 ASSERT_ON_THREAD(sts_target_);
809 obfuscate_host_addresses_ = obfuscate_host_addresses;
811 SetCtxFlags(default_route_only);
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);
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;
830 return NS_OK;
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;
840 return nullptr;
843 std::vector<std::string> NrIceCtx::GetGlobalAttributes() {
844 char** attrs = nullptr;
845 int attrct;
846 int r;
847 std::vector<std::string> ret;
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;
856 for (int i = 0; i < attrct; i++) {
857 ret.push_back(std::string(attrs[i]));
858 RFREE(attrs[i]);
860 RFREE(attrs);
862 return ret;
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()));
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;
880 return NS_OK;
883 bool NrIceCtx::HasStreamsToConnect() const {
884 for (auto& idAndStream : streams_) {
885 if (idAndStream.second->state() != NrIceMediaStream::ICE_CLOSED) {
886 return true;
889 return false;
892 nsresult NrIceCtx::StartChecks() {
893 int r;
894 if (!HasStreamsToConnect()) {
895 // Nothing to do
896 return NS_OK;
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;
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;
919 return NS_OK;
922 void NrIceCtx::gather_cb(NR_SOCKET s, int h, void* arg) {
923 NrIceCtx* ctx = static_cast<NrIceCtx*>(arg);
925 ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE);
928 nsresult NrIceCtx::Finalize() {
929 int r = nr_ice_ctx_finalize(ctx_, peer_);
931 if (r) {
932 MOZ_MTLOG(ML_ERROR, "Couldn't finalize " << name_ << "'");
933 return NS_ERROR_FAILURE;
936 return NS_OK;
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;
946 if (online) {
947 nr_ice_peer_ctx_refresh_consent_all_streams(peer_);
948 } else {
949 nr_ice_peer_ctx_disconnect_all_streams(peer_);
953 void NrIceCtx::SetConnectionState(ConnectionState state) {
954 if (state == connection_state_) return;
956 MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " << connection_state_
957 << "->" << state);
958 connection_state_ = state;
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);
970 SignalConnectionStateChange(this, state);
973 void NrIceCtx::SetGatheringState(GatheringState state) {
974 if (state == gathering_state_) return;
976 MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << name_ << "): gathering state "
977 << gathering_state_ << "->" << state);
978 gathering_state_ = state;
980 SignalGatheringStateChange(this, state);
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;
993 *actual_address = addr;
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();
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;
1021 std::ostringstream o;
1022 o << ptr_to_id << ".local";
1023 *mdns_address = o.str();
1025 obfuscated_host_addresses_[*actual_address] = *mdns_address;
1027 candidate->mdns_addr = r_strdup(mdns_address->c_str());
1031 } // namespace mozilla
1033 // Reimplement nr_ice_compute_codeword to avoid copyright issues
1034 void nr_ice_compute_codeword(char* buf, int len, char* codeword) {
1035 UINT4 c;
1037 r_crc32(buf, len, &c);
1039 PL_Base64Encode(reinterpret_cast<char*>(&c), 3, codeword);
1040 codeword[4] = 0;
1043 int nr_socket_local_create(void* obj, nr_transport_addr* addr,
1044 nr_socket** sockp) {
1045 using namespace mozilla;
1047 RefPtr<NrSocketBase> sock;
1048 int r, _status;
1049 shared_ptr<NrSocketProxyConfig> config = nullptr;
1051 if (obj) {
1052 config = static_cast<NrIceCtx*>(obj)->GetProxyConfig();
1055 r = NrSocketBase::CreateSocket(addr, &sock, config);
1056 if (r) {
1057 ABORT(r);
1060 r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp);
1061 if (r) ABORT(r);
1063 _status = 0;
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;
1072 abort:
1073 return _status;