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
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
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.
46 #include "nr_socket_proxy_config.h"
47 #include "mozilla/UniquePtr.h"
48 #include "mozilla/Unused.h"
57 #include "nsComponentManagerUtils.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"
72 #include "async_timer.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"
83 #include "stun_server_ctx.h"
84 #include "stun_util.h"
85 #include "ice_codeword.h"
87 #include "ice_candidate.h"
88 #include "ice_handler.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"
100 #include "mdns_service/mdns_service.h"
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
,
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
;
139 static int nr_crypto_nss_hmac(UCHAR
* key
, size_t keyl
, UCHAR
* buf
, size_t bufl
,
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;
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
,
157 if (!skey
) goto abort
;
159 hmac_ctx
= PK11_CreateContextBySymKey(mech
, CKA_SIGN
, skey
, ¶m
);
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);
176 if (hmac_ctx
) PK11_DestroyContext(hmac_ctx
, PR_TRUE
);
177 if (skey
) PK11_FreeSymKey(skey
);
178 if (slot
) PK11_FreeSlot(slot
);
183 static int nr_crypto_nss_md5(UCHAR
* buf
, size_t bufl
, UCHAR
* result
) {
184 int err
= R_INTERNAL
;
187 const SECHashObject
* ho
= HASH_GetHashObject(HASH_AlgMD5
);
191 MOZ_ASSERT(ho
->length
== 16);
193 rv
= HASH_HashBuf(ho
->type
, result
, buf
, bufl
);
194 if (rv
!= SECSuccess
) goto abort
;
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 {
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
;
215 // Refuse to try TLS without an FQDN
216 return NS_ERROR_INVALID_ARG
;
220 MOZ_MTLOG(ML_ERROR
, "Unsupported STUN server transport: " << transport_
);
221 return NS_ERROR_FAILURE
;
225 r
= nr_praddr_to_transport_addr(&addr_
, &server
->u
.addr
, server
->transport
,
228 return NS_ERROR_FAILURE
;
230 server
->type
= NR_ICE_STUN_SERVER_TYPE_ADDR
;
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
;
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());
261 RFREE(server
->username
);
262 return NS_ERROR_OUT_OF_MEMORY
;
268 NrIceCtx::NrIceCtx(const std::string
& name
, const Config
& aConfig
)
269 : connection_state_(ICE_CTX_INIT
),
270 gathering_state_(ICE_CTX_GATHER_INIT
),
272 ice_controlling_set_(false),
276 ice_handler_vtbl_(nullptr),
277 ice_handler_(nullptr),
281 proxy_config_(nullptr),
282 obfuscate_host_addresses_(false) {}
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()) {
296 RefPtr
<NrIceMediaStream
> NrIceCtx::CreateStream(const std::string
& id
,
297 const std::string
& name
,
299 if (streams_
.count(id
)) {
304 RefPtr
<NrIceMediaStream
> stream
=
305 new NrIceMediaStream(this, id
, name
, components
);
306 streams_
[id
] = 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
;
315 preexisting_stream
->Close();
318 if (streams_
.empty()) {
319 SetGatheringState(ICE_CTX_GATHER_INIT
);
324 int NrIceCtx::select_pair(void* obj
, nr_ice_media_stream
* stream
,
325 int component_id
, nr_ice_cand_pair
** potentials
,
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
);
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
);
340 NrIceCtx
* ctx
= static_cast<NrIceCtx
*>(obj
);
342 RefPtr
<NrIceMediaStream
> s
= ctx
->FindStream(stream
);
344 // Streams which do not exist should never be ready.
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
);
358 NrIceCtx
* ctx
= static_cast<NrIceCtx
*>(obj
);
359 RefPtr
<NrIceMediaStream
> s
= ctx
->FindStream(stream
);
361 // Streams which do not exist should never fail.
364 ctx
->SetConnectionState(ICE_CTX_FAILED
);
369 int NrIceCtx::ice_checking(void* obj
, nr_ice_peer_ctx
* pctx
) {
370 MOZ_MTLOG(ML_DEBUG
, "ice_checking called");
373 NrIceCtx
* ctx
= static_cast<NrIceCtx
*>(obj
);
375 ctx
->SetConnectionState(ICE_CTX_CHECKING
);
380 int NrIceCtx::ice_connected(void* obj
, nr_ice_peer_ctx
* pctx
) {
381 MOZ_MTLOG(ML_DEBUG
, "ice_connected called");
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
);
394 int NrIceCtx::ice_disconnected(void* obj
, nr_ice_peer_ctx
* pctx
) {
395 MOZ_MTLOG(ML_DEBUG
, "ice_disconnected called");
398 NrIceCtx
* ctx
= static_cast<NrIceCtx
*>(obj
);
400 ctx
->SetConnectionState(ICE_CTX_DISCONNECTED
);
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
) {
409 NrIceCtx
* ctx
= static_cast<NrIceCtx
*>(obj
);
410 RefPtr
<NrIceMediaStream
> s
= ctx
->FindStream(stream
);
412 // Streams which do not exist should never have packets.
415 s
->SignalPacketReceived(s
, component_id
, msg
, len
);
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
);
425 NrIceCtx
* ctx
= static_cast<NrIceCtx
*>(arg
);
426 RefPtr
<NrIceMediaStream
> s
= ctx
->FindStream(stream
);
429 // This stream has been removed because it is inactive
434 s
->SignalCandidate(s
, "", stream
->ufrag
, "", "");
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_
);
450 MOZ_MTLOG(ML_INFO
, "NrIceCtx(" << ctx
->name_
<< "): trickling candidate "
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
460 NR_reg_init(NR_REG_MODE_LOCAL
);
461 nr_crypto_vtbl
= &nr_ice_crypto_nss_vtbl
;
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(),
505 #define MAXADDRS 100 // mirrors setting in ice_ctx.c
508 nsTArray
<NrIceStunAddr
> NrIceCtx::GetStunAddrs() {
509 nsTArray
<NrIceStunAddr
> addrs
;
511 nr_local_addr local_addrs
[MAXADDRS
];
514 // most likely running on parent process and need crypto vtbl
515 // initialized on Windows (Linux and OSX don't seem to care)
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");
524 for (int i
= 0; i
< addr_ct
; ++i
) {
525 NrIceStunAddr
addr(&local_addrs
[i
]);
526 addrs
.AppendElement(addr
);
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
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
;
555 case ICE_POLICY_NO_HOST
:
556 flags
|= NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES
;
562 r
= nr_ice_ctx_create(const_cast<char*>(name_
.c_str()), flags
, &ctx_
);
565 MOZ_MTLOG(ML_ERROR
, "Couldn't create ICE ctx for '" << name_
<< "'");
569 // override default factory to capture optional proxy config when creating
571 nr_socket_factory
* factory
;
572 r
= nr_socket_factory_create_int(this, &ctx_socket_factory_vtbl
, &factory
);
575 MOZ_MTLOG(LogLevel::Error
, "Couldn't create ctx socket factory.");
578 nr_ice_ctx_set_socket_factory(ctx_
, factory
);
580 nr_interface_prioritizer
* prioritizer
= CreateInterfacePrioritizer();
582 MOZ_MTLOG(LogLevel::Error
, "Couldn't create interface prioritizer.");
586 r
= nr_ice_ctx_set_interface_prioritizer(ctx_
, prioritizer
);
588 MOZ_MTLOG(LogLevel::Error
, "Couldn't set interface prioritizer.");
592 if (generating_trickle()) {
593 r
= nr_ice_ctx_set_trickle_cb(ctx_
, &NrIceCtx::trickle_cb
, this);
595 MOZ_MTLOG(ML_ERROR
, "Couldn't set trickle cb for '" << name_
<< "'");
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;
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_
);
632 MOZ_MTLOG(ML_ERROR
, "Couldn't create ICE peer ctx for '" << name_
<< "'");
637 sts_target_
= do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID
, &rv
);
639 if (!NS_SUCCEEDED(rv
)) return false;
644 int NrIceCtx::SetNat(const RefPtr
<TestNat
>& aNat
) {
646 nr_socket_factory
* fac
;
647 int r
= nat_
->create_socket_factory(&fac
);
651 nr_ice_ctx_set_socket_factory(ctx_
, fac
);
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;
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();
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
;
693 nr_ice_peer_ctx_destroy(&peer_
);
696 nr_ice_ctx_destroy(&ctx_
);
699 delete ice_handler_vtbl_
;
702 ice_handler_vtbl_
= nullptr;
703 ice_handler_
= nullptr;
704 proxy_config_
= nullptr;
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;
720 "ICE ctx " << name_
<< " setting controlling to" << controlling
);
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
]);
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());
745 MOZ_MTLOG(ML_ERROR
, "Couldn't set STUN server for '" << name_
<< "'");
746 return NS_ERROR_FAILURE
;
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
]);
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());
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.
779 nsresult
NrIceCtx::SetResolver(nr_resolver
* resolver
) {
780 int r
= nr_ice_ctx_set_resolver(ctx_
, resolver
);
783 MOZ_MTLOG(ML_ERROR
, "Couldn't set resolver for '" << name_
<< "'");
784 return NS_ERROR_FAILURE
;
790 nsresult
NrIceCtx::SetProxyConfig(NrSocketProxyConfig
&& config
) {
791 proxy_config_
.reset(new NrSocketProxyConfig(std::move(config
)));
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
);
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
816 int r
= nr_ice_gather(ctx_
, &NrIceCtx::gather_cb
, this);
819 SetGatheringState(ICE_CTX_GATHER_COMPLETE
);
820 } else if (r
== R_WOULDBLOCK
) {
821 SetGatheringState(ICE_CTX_GATHER_STARTED
);
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
;
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
;
843 std::vector
<std::string
> NrIceCtx::GetGlobalAttributes() {
844 char** attrs
= nullptr;
847 std::vector
<std::string
> ret
;
849 r
= nr_ice_get_global_attributes(ctx_
, &attrs
, &attrct
);
852 "Couldn't get ufrag and password for '" << name_
<< "'");
856 for (int i
= 0; i
< attrct
; i
++) {
857 ret
.push_back(std::string(attrs
[i
]));
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());
876 "Couldn't parse global attributes for " << name_
<< "'");
877 return NS_ERROR_FAILURE
;
883 bool NrIceCtx::HasStreamsToConnect() const {
884 for (auto& idAndStream
: streams_
) {
885 if (idAndStream
.second
->state() != NrIceMediaStream::ICE_CLOSED
) {
892 nsresult
NrIceCtx::StartChecks() {
894 if (!HasStreamsToConnect()) {
899 r
= nr_ice_peer_ctx_pair_candidates(peer_
);
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);
908 if (r
== R_NOT_FOUND
) {
909 MOZ_MTLOG(ML_INFO
, "Couldn't start peer checks on "
910 << name_
<< ", assuming trickle ICE");
913 "ICE FAILED: Couldn't start peer checks on " << name_
);
914 SetConnectionState(ICE_CTX_FAILED
);
915 return NS_ERROR_FAILURE
;
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_
);
932 MOZ_MTLOG(ML_ERROR
, "Couldn't finalize " << name_
<< "'");
933 return NS_ERROR_FAILURE
;
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
) {
947 nr_ice_peer_ctx_refresh_consent_all_streams(peer_
);
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_
958 connection_state_
= state
;
960 if (connection_state_
== ICE_CTX_FAILED
) {
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_
) {
988 if (nr_transport_addr_get_addrstring(&candidate
->addr
, addr
,
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
;
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
1006 if (NS_SUCCEEDED(rv
)) {
1007 rv
= uuidgen
->GenerateUUIDInPlace(&id
);
1008 if (NS_FAILED(rv
)) {
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
;
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
) {
1037 r_crc32(buf
, len
, &c
);
1039 PL_Base64Encode(reinterpret_cast<char*>(&c
), 3, codeword
);
1043 int nr_socket_local_create(void* obj
, nr_transport_addr
* addr
,
1044 nr_socket
** sockp
) {
1045 using namespace mozilla
;
1047 RefPtr
<NrSocketBase
> sock
;
1049 shared_ptr
<NrSocketProxyConfig
> config
= nullptr;
1052 config
= static_cast<NrIceCtx
*>(obj
)->GetProxyConfig();
1055 r
= NrSocketBase::CreateSocket(addr
, &sock
, config
);
1060 r
= nr_socket_create_int(static_cast<void*>(sock
), sock
->vtbl(), sockp
);
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();