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