michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: // Original author: ekr@rtfm.com michael@0: michael@0: // Some of this code is cut-and-pasted from nICEr. Copyright is: michael@0: michael@0: /* michael@0: Copyright (c) 2007, Adobe Systems, Incorporated michael@0: All rights reserved. michael@0: michael@0: Redistribution and use in source and binary forms, with or without michael@0: modification, are permitted provided that the following conditions are michael@0: met: michael@0: michael@0: * Redistributions of source code must retain the above copyright michael@0: notice, this list of conditions and the following disclaimer. michael@0: michael@0: * Redistributions in binary form must reproduce the above copyright michael@0: notice, this list of conditions and the following disclaimer in the michael@0: documentation and/or other materials provided with the distribution. michael@0: michael@0: * Neither the name of Adobe Systems, Network Resonance nor the names of its michael@0: contributors may be used to endorse or promote products derived from michael@0: this software without specific prior written permission. michael@0: michael@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "logging.h" michael@0: #include "nspr.h" michael@0: #include "nss.h" michael@0: #include "pk11pub.h" michael@0: #include "plbase64.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "ScopedNSSTypes.h" michael@0: michael@0: // nICEr includes michael@0: extern "C" { michael@0: #include "nr_api.h" michael@0: #include "registry.h" michael@0: #include "async_timer.h" michael@0: #include "r_crc32.h" michael@0: #include "r_memory.h" michael@0: #include "ice_reg.h" michael@0: #include "ice_util.h" michael@0: #include "transport_addr.h" michael@0: #include "nr_crypto.h" michael@0: #include "nr_socket.h" michael@0: #include "nr_socket_local.h" michael@0: #include "stun_client_ctx.h" michael@0: #include "stun_server_ctx.h" michael@0: #include "ice_codeword.h" michael@0: #include "ice_ctx.h" michael@0: #include "ice_candidate.h" michael@0: #include "ice_handler.h" michael@0: } michael@0: michael@0: // Local includes michael@0: #include "nricectx.h" michael@0: #include "nricemediastream.h" michael@0: #include "nr_socket_prsock.h" michael@0: #include "nrinterfaceprioritizer.h" michael@0: #include "rlogringbuffer.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: MOZ_MTLOG_MODULE("mtransport") michael@0: michael@0: const char kNrIceTransportUdp[] = "udp"; michael@0: const char kNrIceTransportTcp[] = "tcp"; michael@0: michael@0: static bool initialized = false; michael@0: michael@0: // Implement NSPR-based crypto algorithms michael@0: static int nr_crypto_nss_random_bytes(UCHAR *buf, int len) { michael@0: ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); michael@0: if (!slot) michael@0: return R_INTERNAL; michael@0: michael@0: SECStatus rv = PK11_GenerateRandomOnSlot(slot, buf, len); michael@0: if (rv != SECSuccess) michael@0: return R_INTERNAL; michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static int nr_crypto_nss_hmac(UCHAR *key, int keyl, UCHAR *buf, int bufl, michael@0: UCHAR *result) { michael@0: CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC; michael@0: PK11SlotInfo *slot = 0; michael@0: MOZ_ASSERT(keyl > 0); michael@0: SECItem keyi = { siBuffer, key, static_cast(keyl)}; michael@0: PK11SymKey *skey = 0; michael@0: PK11Context *hmac_ctx = 0; michael@0: SECStatus status; michael@0: unsigned int hmac_len; michael@0: SECItem param = { siBuffer, nullptr, 0 }; michael@0: int err = R_INTERNAL; michael@0: michael@0: slot = PK11_GetInternalKeySlot(); michael@0: if (!slot) michael@0: goto abort; michael@0: michael@0: skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, michael@0: CKA_SIGN, &keyi, nullptr); michael@0: if (!skey) michael@0: goto abort; michael@0: michael@0: michael@0: hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, michael@0: skey, ¶m); michael@0: if (!hmac_ctx) michael@0: goto abort; michael@0: michael@0: status = PK11_DigestBegin(hmac_ctx); michael@0: if (status != SECSuccess) michael@0: goto abort; michael@0: michael@0: status = PK11_DigestOp(hmac_ctx, buf, bufl); michael@0: if (status != SECSuccess) michael@0: goto abort; michael@0: michael@0: status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20); michael@0: if (status != SECSuccess) michael@0: goto abort; michael@0: michael@0: MOZ_ASSERT(hmac_len == 20); michael@0: michael@0: err = 0; michael@0: michael@0: abort: michael@0: if(hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE); michael@0: if (skey) PK11_FreeSymKey(skey); michael@0: if (slot) PK11_FreeSlot(slot); michael@0: michael@0: return err; michael@0: } michael@0: michael@0: static int nr_crypto_nss_md5(UCHAR *buf, int bufl, UCHAR *result) { michael@0: int err = R_INTERNAL; michael@0: SECStatus rv; michael@0: michael@0: const SECHashObject *ho = HASH_GetHashObject(HASH_AlgMD5); michael@0: MOZ_ASSERT(ho); michael@0: if (!ho) michael@0: goto abort; michael@0: michael@0: MOZ_ASSERT(ho->length == 16); michael@0: michael@0: rv = HASH_HashBuf(ho->type, result, buf, bufl); michael@0: if (rv != SECSuccess) michael@0: goto abort; michael@0: michael@0: err = 0; michael@0: abort: michael@0: return err; michael@0: } michael@0: michael@0: static nr_ice_crypto_vtbl nr_ice_crypto_nss_vtbl = { michael@0: nr_crypto_nss_random_bytes, michael@0: nr_crypto_nss_hmac, michael@0: nr_crypto_nss_md5 michael@0: }; michael@0: michael@0: michael@0: michael@0: michael@0: nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server, michael@0: const std::string &transport) const { michael@0: int r; michael@0: int transport_int; michael@0: michael@0: memset(server, 0, sizeof(nr_ice_stun_server)); michael@0: if (transport == kNrIceTransportUdp) { michael@0: transport_int = IPPROTO_UDP; michael@0: } else if (transport == kNrIceTransportTcp) { michael@0: transport_int = IPPROTO_TCP; michael@0: } else { michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (has_addr_) { michael@0: r = nr_praddr_to_transport_addr(&addr_, &server->u.addr, michael@0: transport_int, 0); michael@0: if (r) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: server->type=NR_ICE_STUN_SERVER_TYPE_ADDR; michael@0: } michael@0: else { michael@0: MOZ_ASSERT(sizeof(server->u.dnsname.host) > host_.size()); michael@0: PL_strncpyz(server->u.dnsname.host, host_.c_str(), michael@0: sizeof(server->u.dnsname.host)); michael@0: server->u.dnsname.port = port_; michael@0: server->type=NR_ICE_STUN_SERVER_TYPE_DNSNAME; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server *server) const { michael@0: memset(server, 0, sizeof(nr_ice_turn_server)); michael@0: michael@0: nsresult rv = ToNicerStunStruct(&server->turn_server, transport_); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (transport_ == kNrIceTransportUdp) { michael@0: server->transport = IPPROTO_UDP; michael@0: } else if (transport_ == kNrIceTransportTcp) { michael@0: server->transport = IPPROTO_TCP; michael@0: } else { michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (username_.empty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (password_.empty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: if (!(server->username=r_strdup(username_.c_str()))) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // TODO(ekr@rtfm.com): handle non-ASCII passwords somehow? michael@0: // STUN requires they be SASLpreped, but we don't know if michael@0: // they are at this point. michael@0: michael@0: // C++03 23.2.4, Paragraph 1 stipulates that the elements michael@0: // in std::vector must be contiguous, and can therefore be michael@0: // used as input to functions expecting C arrays. michael@0: int r = r_data_create(&server->password, michael@0: const_cast(&password_[0]), michael@0: password_.size()); michael@0: if (r) { michael@0: RFREE(server->username); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Handler callbacks michael@0: int NrIceCtx::select_pair(void *obj,nr_ice_media_stream *stream, michael@0: int component_id, nr_ice_cand_pair **potentials, michael@0: int potential_ct) { michael@0: MOZ_MTLOG(ML_DEBUG, "select pair called: potential_ct = " michael@0: << potential_ct); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int NrIceCtx::stream_ready(void *obj, nr_ice_media_stream *stream) { michael@0: MOZ_MTLOG(ML_DEBUG, "stream_ready called"); michael@0: michael@0: // Get the ICE ctx. michael@0: NrIceCtx *ctx = static_cast(obj); michael@0: michael@0: RefPtr s = ctx->FindStream(stream); michael@0: michael@0: // Streams which do not exist should never be ready. michael@0: MOZ_ASSERT(s); michael@0: michael@0: s->Ready(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int NrIceCtx::stream_failed(void *obj, nr_ice_media_stream *stream) { michael@0: MOZ_MTLOG(ML_DEBUG, "stream_failed called"); michael@0: michael@0: // Get the ICE ctx michael@0: NrIceCtx *ctx = static_cast(obj); michael@0: RefPtr s = ctx->FindStream(stream); michael@0: michael@0: // Streams which do not exist should never fail. michael@0: MOZ_ASSERT(s); michael@0: michael@0: ctx->SetConnectionState(ICE_CTX_FAILED); michael@0: s -> SignalFailed(s); michael@0: return 0; michael@0: } michael@0: michael@0: int NrIceCtx::ice_completed(void *obj, nr_ice_peer_ctx *pctx) { michael@0: MOZ_MTLOG(ML_DEBUG, "ice_completed called"); michael@0: michael@0: // Get the ICE ctx michael@0: NrIceCtx *ctx = static_cast(obj); michael@0: michael@0: // This is called even on failed contexts. michael@0: if (ctx->connection_state() != ICE_CTX_FAILED) { michael@0: ctx->SetConnectionState(ICE_CTX_OPEN); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int NrIceCtx::msg_recvd(void *obj, nr_ice_peer_ctx *pctx, michael@0: nr_ice_media_stream *stream, int component_id, michael@0: UCHAR *msg, int len) { michael@0: // Get the ICE ctx michael@0: NrIceCtx *ctx = static_cast(obj); michael@0: RefPtr s = ctx->FindStream(stream); michael@0: michael@0: // Streams which do not exist should never have packets. michael@0: MOZ_ASSERT(s); michael@0: michael@0: s->SignalPacketReceived(s, component_id, msg, len); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: void NrIceCtx::trickle_cb(void *arg, nr_ice_ctx *ice_ctx, michael@0: nr_ice_media_stream *stream, michael@0: int component_id, michael@0: nr_ice_candidate *candidate) { michael@0: // Get the ICE ctx michael@0: NrIceCtx *ctx = static_cast(arg); michael@0: RefPtr s = ctx->FindStream(stream); michael@0: michael@0: // Streams which do not exist shouldn't have candidates. michael@0: MOZ_ASSERT(s); michael@0: michael@0: // Format the candidate. michael@0: char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE]; michael@0: int r = nr_ice_format_candidate_attribute(candidate, candidate_str, michael@0: sizeof(candidate_str)); michael@0: MOZ_ASSERT(!r); michael@0: if (r) michael@0: return; michael@0: michael@0: MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate " michael@0: << candidate_str); michael@0: michael@0: s->SignalCandidate(s, candidate_str); michael@0: } michael@0: michael@0: RefPtr NrIceCtx::Create(const std::string& name, michael@0: bool offerer, michael@0: bool set_interface_priorities) { michael@0: michael@0: RefPtr ctx = new NrIceCtx(name, offerer); michael@0: michael@0: // Initialize the crypto callbacks and logging stuff michael@0: if (!initialized) { michael@0: NR_reg_init(NR_REG_MODE_LOCAL); michael@0: RLogRingBuffer::CreateInstance(); michael@0: nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl; michael@0: initialized = true; michael@0: michael@0: // Set the priorites for candidate type preferences. michael@0: // These numbers come from RFC 5245 S. 4.1.2.2 michael@0: NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_SRV_RFLX, 100); michael@0: NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_PEER_RFLX, 110); michael@0: NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_HOST, 126); michael@0: NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED, 5); michael@0: NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED_TCP, 0); michael@0: michael@0: if (set_interface_priorities) { michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.rl0", 255); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.wi0", 254); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.lo0", 253); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.en1", 252); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.en0", 251); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.eth0", 252); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.eth1", 251); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.eth2", 249); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.ppp", 250); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.ppp0", 249); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.en2", 248); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.en3", 247); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.em0", 251); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.em1", 252); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet0", 240); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet1", 241); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet3", 239); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet4", 238); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet5", 237); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet6", 236); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet7", 235); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.vmnet8", 234); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.virbr0", 233); michael@0: NR_reg_set_uchar((char *)"ice.pref.interface.wlan0", 232); michael@0: } michael@0: michael@0: NR_reg_set_uint4((char *)"stun.client.maximum_transmits",5); michael@0: } michael@0: michael@0: // Create the ICE context michael@0: int r; michael@0: michael@0: UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER: michael@0: NR_ICE_CTX_FLAGS_ANSWERER; michael@0: flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; michael@0: michael@0: r = nr_ice_ctx_create(const_cast(name.c_str()), flags, michael@0: &ctx->ctx_); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name << "'"); michael@0: return nullptr; michael@0: } michael@0: michael@0: #ifdef USE_INTERFACE_PRIORITIZER michael@0: nr_interface_prioritizer *prioritizer = CreateInterfacePrioritizer(); michael@0: if (!prioritizer) { michael@0: MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create interface prioritizer."); michael@0: return nullptr; michael@0: } michael@0: michael@0: r = nr_ice_ctx_set_interface_prioritizer(ctx->ctx_, prioritizer); michael@0: if (r) { michael@0: MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set interface prioritizer."); michael@0: return nullptr; michael@0: } michael@0: #endif // USE_INTERFACE_PRIORITIZER michael@0: michael@0: if (ctx->generating_trickle()) { michael@0: r = nr_ice_ctx_set_trickle_cb(ctx->ctx_, &NrIceCtx::trickle_cb, ctx); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name << "'"); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: // Create the handler objects michael@0: ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl(); michael@0: ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; michael@0: ctx->ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; michael@0: ctx->ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; michael@0: ctx->ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed; michael@0: ctx->ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; michael@0: michael@0: ctx->ice_handler_ = new nr_ice_handler(); michael@0: ctx->ice_handler_->vtbl = ctx->ice_handler_vtbl_; michael@0: ctx->ice_handler_->obj = ctx; michael@0: michael@0: // Create the peer ctx. Because we do not support parallel forking, we michael@0: // only have one peer ctx. michael@0: std::string peer_name = name + ":default"; michael@0: r = nr_ice_peer_ctx_create(ctx->ctx_, ctx->ice_handler_, michael@0: const_cast(peer_name.c_str()), michael@0: &ctx->peer_); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name << "'"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult rv; michael@0: ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); michael@0: michael@0: if (!NS_SUCCEEDED(rv)) michael@0: return nullptr; michael@0: michael@0: return ctx; michael@0: } michael@0: michael@0: michael@0: NrIceCtx::~NrIceCtx() { michael@0: MOZ_MTLOG(ML_DEBUG, "Destroying ICE ctx '" << name_ <<"'"); michael@0: nr_ice_peer_ctx_destroy(&peer_); michael@0: nr_ice_ctx_destroy(&ctx_); michael@0: delete ice_handler_vtbl_; michael@0: delete ice_handler_; michael@0: } michael@0: michael@0: RefPtr michael@0: NrIceCtx::CreateStream(const std::string& name, int components) { michael@0: RefPtr stream = michael@0: NrIceMediaStream::Create(this, name, components); michael@0: michael@0: streams_.push_back(stream); michael@0: michael@0: return stream; michael@0: } michael@0: michael@0: void NrIceCtx::destroy_peer_ctx() { michael@0: nr_ice_peer_ctx_destroy(&peer_); michael@0: } michael@0: michael@0: nsresult NrIceCtx::SetControlling(Controlling controlling) { michael@0: peer_->controlling = (controlling == ICE_CONTROLLING)? 1 : 0; michael@0: michael@0: MOZ_MTLOG(ML_DEBUG, "ICE ctx " << name_ << " setting controlling to" << michael@0: controlling); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult NrIceCtx::SetStunServers(const std::vector& michael@0: stun_servers) { michael@0: if (stun_servers.empty()) michael@0: return NS_OK; michael@0: michael@0: ScopedDeleteArray servers( michael@0: new nr_ice_stun_server[stun_servers.size()]); michael@0: michael@0: for (size_t i=0; i < stun_servers.size(); ++i) { michael@0: nsresult rv = stun_servers[i].ToNicerStunStruct(&servers[i]); michael@0: if (NS_FAILED(rv)) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't set STUN server for '" << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: int r = nr_ice_ctx_set_stun_servers(ctx_, servers, stun_servers.size()); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't set STUN server for '" << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // TODO(ekr@rtfm.com): This is just SetStunServers with s/Stun/Turn michael@0: // Could we do a template or something? michael@0: nsresult NrIceCtx::SetTurnServers(const std::vector& michael@0: turn_servers) { michael@0: if (turn_servers.empty()) michael@0: return NS_OK; michael@0: michael@0: ScopedDeleteArray servers( michael@0: new nr_ice_turn_server[turn_servers.size()]); michael@0: michael@0: for (size_t i=0; i < turn_servers.size(); ++i) { michael@0: nsresult rv = turn_servers[i].ToNicerTurnStruct(&servers[i]); michael@0: if (NS_FAILED(rv)) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't set TURN server for '" << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: int r = nr_ice_ctx_set_turn_servers(ctx_, servers, turn_servers.size()); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't set TURN server for '" << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // TODO(ekr@rtfm.com): This leaks the username/password. Need to free that. michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult NrIceCtx::SetResolver(nr_resolver *resolver) { michael@0: int r = nr_ice_ctx_set_resolver(ctx_, resolver); michael@0: michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't set resolver for '" << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult NrIceCtx::StartGathering() { michael@0: MOZ_ASSERT(ctx_->state == ICE_CTX_INIT); michael@0: if (ctx_->state != ICE_CTX_INIT) { michael@0: MOZ_MTLOG(ML_ERROR, "ICE ctx in the wrong state for gathering: '" michael@0: << name_ << "'"); michael@0: SetConnectionState(ICE_CTX_FAILED); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: int r = nr_ice_initialize(ctx_, &NrIceCtx::initialized_cb, michael@0: this); michael@0: michael@0: if (r && r != R_WOULDBLOCK) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't gather ICE candidates for '" michael@0: << name_ << "'"); michael@0: SetConnectionState(ICE_CTX_FAILED); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: SetGatheringState(ICE_CTX_GATHER_STARTED); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: RefPtr NrIceCtx::FindStream( michael@0: nr_ice_media_stream *stream) { michael@0: for (size_t i=0; istream() == stream) { michael@0: return streams_[i]; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: std::vector NrIceCtx::GetGlobalAttributes() { michael@0: char **attrs = 0; michael@0: int attrct; michael@0: int r; michael@0: std::vector ret; michael@0: michael@0: r = nr_ice_get_global_attributes(ctx_, &attrs, &attrct); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't get ufrag and password for '" michael@0: << name_ << "'"); michael@0: return ret; michael@0: } michael@0: michael@0: for (int i=0; i attrs) { michael@0: std::vector attrs_in; michael@0: michael@0: for (size_t i=0; i(attrs[i].c_str())); michael@0: } michael@0: michael@0: int r = nr_ice_peer_ctx_parse_global_attributes(peer_, michael@0: attrs_in.size() ? michael@0: &attrs_in[0] : nullptr, michael@0: attrs_in.size()); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't parse global attributes for " michael@0: << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult NrIceCtx::StartChecks() { michael@0: int r; michael@0: michael@0: r=nr_ice_peer_ctx_pair_candidates(peer_); michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't pair candidates on " michael@0: << name_ << "'"); michael@0: SetConnectionState(ICE_CTX_FAILED); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: r = nr_ice_peer_ctx_start_checks2(peer_,1); michael@0: if (r) { michael@0: if (r == R_NOT_FOUND) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't start peer checks on " michael@0: << name_ << "' assuming trickle ICE"); michael@0: } else { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't start peer checks on " michael@0: << name_ << "'"); michael@0: SetConnectionState(ICE_CTX_FAILED); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: SetConnectionState(ICE_CTX_CHECKING); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: void NrIceCtx::initialized_cb(NR_SOCKET s, int h, void *arg) { michael@0: NrIceCtx *ctx = static_cast(arg); michael@0: michael@0: ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE); michael@0: } michael@0: michael@0: nsresult NrIceCtx::Finalize() { michael@0: int r = nr_ice_ctx_finalize(ctx_, peer_); michael@0: michael@0: if (r) { michael@0: MOZ_MTLOG(ML_ERROR, "Couldn't finalize " michael@0: << name_ << "'"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void NrIceCtx::SetConnectionState(ConnectionState state) { michael@0: if (state == connection_state_) michael@0: return; michael@0: michael@0: MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " << michael@0: connection_state_ << "->" << state); michael@0: connection_state_ = state; michael@0: michael@0: SignalConnectionStateChange(this, state); michael@0: } michael@0: michael@0: void NrIceCtx::SetGatheringState(GatheringState state) { michael@0: if (state == gathering_state_) michael@0: return; michael@0: michael@0: MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << name_ << "): gathering state " << michael@0: gathering_state_ << "->" << state); michael@0: gathering_state_ = state; michael@0: michael@0: SignalGatheringStateChange(this, state); michael@0: } michael@0: michael@0: } // close namespace michael@0: michael@0: // Reimplement nr_ice_compute_codeword to avoid copyright issues michael@0: void nr_ice_compute_codeword(char *buf, int len,char *codeword) { michael@0: UINT4 c; michael@0: michael@0: r_crc32(buf,len,&c); michael@0: michael@0: PL_Base64Encode(reinterpret_cast(&c), 3, codeword); michael@0: codeword[4] = 0; michael@0: michael@0: return; michael@0: } michael@0: