1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/mtransport/nricemediastream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,505 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Original author: ekr@rtfm.com 1.11 + 1.12 +// Some of this code is cut-and-pasted from nICEr. Copyright is: 1.13 + 1.14 +/* 1.15 +Copyright (c) 2007, Adobe Systems, Incorporated 1.16 +All rights reserved. 1.17 + 1.18 +Redistribution and use in source and binary forms, with or without 1.19 +modification, are permitted provided that the following conditions are 1.20 +met: 1.21 + 1.22 +* Redistributions of source code must retain the above copyright 1.23 + notice, this list of conditions and the following disclaimer. 1.24 + 1.25 +* Redistributions in binary form must reproduce the above copyright 1.26 + notice, this list of conditions and the following disclaimer in the 1.27 + documentation and/or other materials provided with the distribution. 1.28 + 1.29 +* Neither the name of Adobe Systems, Network Resonance nor the names of its 1.30 + contributors may be used to endorse or promote products derived from 1.31 + this software without specific prior written permission. 1.32 + 1.33 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.34 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.35 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.36 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.37 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.38 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.39 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.40 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.41 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.42 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.43 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.44 +*/ 1.45 + 1.46 + 1.47 +#include <string> 1.48 +#include <vector> 1.49 + 1.50 +#include "logging.h" 1.51 +#include "nsError.h" 1.52 +#include "mozilla/Scoped.h" 1.53 + 1.54 +// nICEr includes 1.55 +extern "C" { 1.56 +#include "nr_api.h" 1.57 +#include "registry.h" 1.58 +#include "async_timer.h" 1.59 +#include "ice_util.h" 1.60 +#include "transport_addr.h" 1.61 +#include "nr_crypto.h" 1.62 +#include "nr_socket.h" 1.63 +#include "nr_socket_local.h" 1.64 +#include "stun_client_ctx.h" 1.65 +#include "stun_server_ctx.h" 1.66 +#include "ice_ctx.h" 1.67 +#include "ice_candidate.h" 1.68 +#include "ice_handler.h" 1.69 +} 1.70 + 1.71 +// Local includes 1.72 +#include "nricectx.h" 1.73 +#include "nricemediastream.h" 1.74 + 1.75 +namespace mozilla { 1.76 + 1.77 +MOZ_MTLOG_MODULE("mtransport") 1.78 + 1.79 +static bool ToNrIceAddr(nr_transport_addr &addr, 1.80 + NrIceAddr *out) { 1.81 + int r; 1.82 + char addrstring[INET6_ADDRSTRLEN + 1]; 1.83 + 1.84 + r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring)); 1.85 + if (r) 1.86 + return false; 1.87 + out->host = addrstring; 1.88 + 1.89 + int port; 1.90 + r = nr_transport_addr_get_port(&addr, &port); 1.91 + if (r) 1.92 + return false; 1.93 + 1.94 + out->port = port; 1.95 + 1.96 + switch (addr.protocol) { 1.97 + case IPPROTO_TCP: 1.98 + out->transport = kNrIceTransportTcp; 1.99 + break; 1.100 + case IPPROTO_UDP: 1.101 + out->transport = kNrIceTransportUdp; 1.102 + break; 1.103 + default: 1.104 + MOZ_CRASH(); 1.105 + return false; 1.106 + } 1.107 + 1.108 + return true; 1.109 +} 1.110 + 1.111 +static bool ToNrIceCandidate(const nr_ice_candidate& candc, 1.112 + NrIceCandidate* out) { 1.113 + MOZ_ASSERT(out); 1.114 + int r; 1.115 + // Const-cast because the internal nICEr code isn't const-correct. 1.116 + nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc); 1.117 + 1.118 + if (!ToNrIceAddr(cand->addr, &out->cand_addr)) 1.119 + return false; 1.120 + 1.121 + if (cand->isock) { 1.122 + nr_transport_addr addr; 1.123 + r = nr_socket_getaddr(cand->isock->sock, &addr); 1.124 + if (r) 1.125 + return false; 1.126 + 1.127 + if (!ToNrIceAddr(addr, &out->local_addr)) 1.128 + return false; 1.129 + } 1.130 + 1.131 + NrIceCandidate::Type type; 1.132 + 1.133 + switch (cand->type) { 1.134 + case HOST: 1.135 + type = NrIceCandidate::ICE_HOST; 1.136 + break; 1.137 + case SERVER_REFLEXIVE: 1.138 + type = NrIceCandidate::ICE_SERVER_REFLEXIVE; 1.139 + break; 1.140 + case PEER_REFLEXIVE: 1.141 + type = NrIceCandidate::ICE_PEER_REFLEXIVE; 1.142 + break; 1.143 + case RELAYED: 1.144 + type = NrIceCandidate::ICE_RELAYED; 1.145 + break; 1.146 + default: 1.147 + return false; 1.148 + } 1.149 + 1.150 + out->type = type; 1.151 + out->codeword = candc.codeword; 1.152 + return true; 1.153 +} 1.154 + 1.155 +// Make an NrIceCandidate from the candidate |cand|. 1.156 +// This is not a member fxn because we want to hide the 1.157 +// defn of nr_ice_candidate but we pass by reference. 1.158 +static NrIceCandidate* MakeNrIceCandidate(const nr_ice_candidate& candc) { 1.159 + ScopedDeletePtr<NrIceCandidate> out(new NrIceCandidate()); 1.160 + 1.161 + if (!ToNrIceCandidate(candc, out)) { 1.162 + return nullptr; 1.163 + } 1.164 + return out.forget(); 1.165 +} 1.166 + 1.167 +// NrIceMediaStream 1.168 +RefPtr<NrIceMediaStream> 1.169 +NrIceMediaStream::Create(NrIceCtx *ctx, 1.170 + const std::string& name, 1.171 + int components) { 1.172 + RefPtr<NrIceMediaStream> stream = 1.173 + new NrIceMediaStream(ctx, name, components); 1.174 + 1.175 + int r = nr_ice_add_media_stream(ctx->ctx(), 1.176 + const_cast<char *>(name.c_str()), 1.177 + components, &stream->stream_); 1.178 + if (r) { 1.179 + MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '" 1.180 + << name << "'"); 1.181 + return nullptr; 1.182 + } 1.183 + 1.184 + return stream; 1.185 +} 1.186 + 1.187 +NrIceMediaStream::~NrIceMediaStream() { 1.188 + // We do not need to destroy anything. All major resources 1.189 + // are attached to the ice ctx. 1.190 +} 1.191 + 1.192 +nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>& 1.193 + attributes) { 1.194 + if (!stream_) 1.195 + return NS_ERROR_FAILURE; 1.196 + 1.197 + std::vector<char *> attributes_in; 1.198 + 1.199 + for (size_t i=0; i<attributes.size(); ++i) { 1.200 + attributes_in.push_back(const_cast<char *>(attributes[i].c_str())); 1.201 + } 1.202 + 1.203 + // Still need to call nr_ice_ctx_parse_stream_attributes. 1.204 + int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_->peer(), 1.205 + stream_, 1.206 + attributes_in.size() ? 1.207 + &attributes_in[0] : nullptr, 1.208 + attributes_in.size()); 1.209 + if (r) { 1.210 + MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream " 1.211 + << name_ << "'"); 1.212 + return NS_ERROR_FAILURE; 1.213 + } 1.214 + 1.215 + return NS_OK; 1.216 +} 1.217 + 1.218 +// Parse trickle ICE candidate 1.219 +nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) { 1.220 + int r; 1.221 + 1.222 + MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->name() << ")/STREAM(" << 1.223 + name() << ") : parsing trickle candidate " << candidate); 1.224 + 1.225 + r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_->peer(), 1.226 + stream_, 1.227 + const_cast<char *>( 1.228 + candidate.c_str()) 1.229 + ); 1.230 + if (r) { 1.231 + if (r == R_ALREADY) { 1.232 + MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '" 1.233 + << name_ << "' because it is completed"); 1.234 + 1.235 + } else { 1.236 + MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '" 1.237 + << name_ << "'"); 1.238 + return NS_ERROR_FAILURE; 1.239 + } 1.240 + } 1.241 + 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +// Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled. 1.246 +nsresult NrIceMediaStream::GetActivePair(int component, 1.247 + NrIceCandidate **localp, 1.248 + NrIceCandidate **remotep) { 1.249 + int r; 1.250 + nr_ice_candidate *local_int; 1.251 + nr_ice_candidate *remote_int; 1.252 + 1.253 + r = nr_ice_media_stream_get_active(ctx_->peer(), 1.254 + stream_, 1.255 + component, 1.256 + &local_int, &remote_int); 1.257 + // If result is R_REJECTED then component is unpaired or disabled. 1.258 + if (r == R_REJECTED) 1.259 + return NS_ERROR_NOT_AVAILABLE; 1.260 + 1.261 + if (r) 1.262 + return NS_ERROR_FAILURE; 1.263 + 1.264 + ScopedDeletePtr<NrIceCandidate> local( 1.265 + MakeNrIceCandidate(*local_int)); 1.266 + if (!local) 1.267 + return NS_ERROR_FAILURE; 1.268 + 1.269 + ScopedDeletePtr<NrIceCandidate> remote( 1.270 + MakeNrIceCandidate(*remote_int)); 1.271 + if (!remote) 1.272 + return NS_ERROR_FAILURE; 1.273 + 1.274 + if (localp) 1.275 + *localp = local.forget(); 1.276 + if (remotep) 1.277 + *remotep = remote.forget(); 1.278 + 1.279 + return NS_OK; 1.280 +} 1.281 + 1.282 + 1.283 +nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>* 1.284 + out_pairs) const { 1.285 + MOZ_ASSERT(out_pairs); 1.286 + 1.287 + // Get the check_list on the peer stream (this is where the check_list 1.288 + // actually lives, not in stream_) 1.289 + nr_ice_media_stream* peer_stream; 1.290 + int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); 1.291 + if (r != 0) { 1.292 + return NS_ERROR_FAILURE; 1.293 + } 1.294 + 1.295 + nr_ice_cand_pair *p1; 1.296 + out_pairs->clear(); 1.297 + 1.298 + TAILQ_FOREACH(p1, &peer_stream->check_list, entry) { 1.299 + MOZ_ASSERT(p1); 1.300 + MOZ_ASSERT(p1->local); 1.301 + MOZ_ASSERT(p1->remote); 1.302 + NrIceCandidatePair pair; 1.303 + 1.304 + switch (p1->state) { 1.305 + case NR_ICE_PAIR_STATE_FROZEN: 1.306 + pair.state = NrIceCandidatePair::State::STATE_FROZEN; 1.307 + break; 1.308 + case NR_ICE_PAIR_STATE_WAITING: 1.309 + pair.state = NrIceCandidatePair::State::STATE_WAITING; 1.310 + break; 1.311 + case NR_ICE_PAIR_STATE_IN_PROGRESS: 1.312 + pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS; 1.313 + break; 1.314 + case NR_ICE_PAIR_STATE_FAILED: 1.315 + pair.state = NrIceCandidatePair::State::STATE_FAILED; 1.316 + break; 1.317 + case NR_ICE_PAIR_STATE_SUCCEEDED: 1.318 + pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED; 1.319 + break; 1.320 + case NR_ICE_PAIR_STATE_CANCELLED: 1.321 + pair.state = NrIceCandidatePair::State::STATE_CANCELLED; 1.322 + break; 1.323 + default: 1.324 + MOZ_ASSERT(0); 1.325 + } 1.326 + 1.327 + pair.priority = p1->priority; 1.328 + pair.nominated = p1->peer_nominated || p1->nominated; 1.329 + pair.selected = p1->remote->component && 1.330 + p1->remote->component->active == p1; 1.331 + pair.codeword = p1->codeword; 1.332 + 1.333 + if (!ToNrIceCandidate(*(p1->local), &pair.local) || 1.334 + !ToNrIceCandidate(*(p1->remote), &pair.remote)) { 1.335 + return NS_ERROR_FAILURE; 1.336 + } 1.337 + 1.338 + out_pairs->push_back(pair); 1.339 + } 1.340 + 1.341 + return NS_OK; 1.342 +} 1.343 + 1.344 +nsresult NrIceMediaStream::GetDefaultCandidate(int component, 1.345 + std::string *addrp, 1.346 + int *portp) { 1.347 + nr_ice_candidate *cand; 1.348 + int r; 1.349 + 1.350 + r = nr_ice_media_stream_get_default_candidate(stream_, 1.351 + component, &cand); 1.352 + if (r) { 1.353 + if (ctx_->generating_trickle()) { 1.354 + // Generate default trickle candidates. 1.355 + // draft-ivov-mmusic-trickle-ice-01.txt says to use port 9 1.356 + // but "::" instead of "0.0.0.0". Since we don't do any 1.357 + // IPv6 we are ignoring that for now. 1.358 + *addrp = "0.0.0.0"; 1.359 + *portp = 9; 1.360 + } 1.361 + else { 1.362 + MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '" 1.363 + << name_ << "'"); 1.364 + 1.365 + return NS_ERROR_NOT_AVAILABLE; 1.366 + } 1.367 + } 1.368 + else { 1.369 + char addr[64]; // Enough for IPv6 with colons. 1.370 + r = nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr)); 1.371 + if (r) 1.372 + return NS_ERROR_FAILURE; 1.373 + 1.374 + int port; 1.375 + r=nr_transport_addr_get_port(&cand->addr,&port); 1.376 + if (r) 1.377 + return NS_ERROR_FAILURE; 1.378 + 1.379 + *addrp = addr; 1.380 + *portp = port; 1.381 + } 1.382 + 1.383 + return NS_OK; 1.384 +} 1.385 + 1.386 +std::vector<std::string> NrIceMediaStream::GetCandidates() const { 1.387 + char **attrs = 0; 1.388 + int attrct; 1.389 + int r; 1.390 + std::vector<std::string> ret; 1.391 + 1.392 + r = nr_ice_media_stream_get_attributes(stream_, 1.393 + &attrs, &attrct); 1.394 + if (r) { 1.395 + MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '" 1.396 + << name_ << "'"); 1.397 + return ret; 1.398 + } 1.399 + 1.400 + for (int i=0; i<attrct; i++) { 1.401 + ret.push_back(attrs[i]); 1.402 + RFREE(attrs[i]); 1.403 + } 1.404 + 1.405 + RFREE(attrs); 1.406 + 1.407 + return ret; 1.408 +} 1.409 + 1.410 +static nsresult GetCandidatesFromStream( 1.411 + nr_ice_media_stream *stream, 1.412 + std::vector<NrIceCandidate> *candidates) { 1.413 + MOZ_ASSERT(candidates); 1.414 + nr_ice_component* comp=STAILQ_FIRST(&stream->components); 1.415 + while(comp){ 1.416 + if (comp->state != NR_ICE_COMPONENT_DISABLED) { 1.417 + nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates); 1.418 + while(cand){ 1.419 + NrIceCandidate new_cand; 1.420 + // This can fail if the candidate is server reflexive or relayed, and 1.421 + // has not yet received a response (ie; it doesn't know its address 1.422 + // yet). For the purposes of this code, this isn't a candidate we're 1.423 + // interested in, since it is not fully baked yet. 1.424 + if (ToNrIceCandidate(*cand, &new_cand)) { 1.425 + candidates->push_back(new_cand); 1.426 + } 1.427 + cand=TAILQ_NEXT(cand,entry_comp); 1.428 + } 1.429 + } 1.430 + comp=STAILQ_NEXT(comp,entry); 1.431 + } 1.432 + 1.433 + return NS_OK; 1.434 +} 1.435 + 1.436 +nsresult NrIceMediaStream::GetLocalCandidates( 1.437 + std::vector<NrIceCandidate>* candidates) const { 1.438 + return GetCandidatesFromStream(stream_, candidates); 1.439 +} 1.440 + 1.441 +nsresult NrIceMediaStream::GetRemoteCandidates( 1.442 + std::vector<NrIceCandidate>* candidates) const { 1.443 + nr_ice_media_stream* peer_stream; 1.444 + int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); 1.445 + if (r != 0) { 1.446 + return NS_ERROR_FAILURE; 1.447 + } 1.448 + 1.449 + return GetCandidatesFromStream(peer_stream, candidates); 1.450 +} 1.451 + 1.452 + 1.453 +nsresult NrIceMediaStream::DisableComponent(int component_id) { 1.454 + if (!stream_) 1.455 + return NS_ERROR_FAILURE; 1.456 + 1.457 + int r = nr_ice_media_stream_disable_component(stream_, 1.458 + component_id); 1.459 + if (r) { 1.460 + MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" << 1.461 + component_id); 1.462 + return NS_ERROR_FAILURE; 1.463 + } 1.464 + 1.465 + return NS_OK; 1.466 +} 1.467 + 1.468 +nsresult NrIceMediaStream::SendPacket(int component_id, 1.469 + const unsigned char *data, 1.470 + size_t len) { 1.471 + if (!stream_) 1.472 + return NS_ERROR_FAILURE; 1.473 + 1.474 + int r = nr_ice_media_stream_send(ctx_->peer(), stream_, 1.475 + component_id, 1.476 + const_cast<unsigned char *>(data), len); 1.477 + if (r) { 1.478 + MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'"); 1.479 + if (r == R_WOULDBLOCK) { 1.480 + return NS_BASE_STREAM_WOULD_BLOCK; 1.481 + } 1.482 + 1.483 + return NS_BASE_STREAM_OSERROR; 1.484 + } 1.485 + 1.486 + return NS_OK; 1.487 +} 1.488 + 1.489 + 1.490 +void NrIceMediaStream::Ready() { 1.491 + // This function is called whenever a stream becomes ready, but it 1.492 + // gets fired multiple times when a stream gets nominated repeatedly. 1.493 + if (state_ != ICE_OPEN) { 1.494 + MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'"); 1.495 + state_ = ICE_OPEN; 1.496 + SignalReady(this); 1.497 + } 1.498 + else { 1.499 + MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'"); 1.500 + } 1.501 +} 1.502 + 1.503 +void NrIceMediaStream::Close() { 1.504 + MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'"); 1.505 + state_ = ICE_CLOSED; 1.506 + stream_ = nullptr; 1.507 +} 1.508 +} // close namespace