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 "logging.h"
47 #include "nsError.h"
48
49 // nICEr includes
50 extern "C" {
51 #include "nr_api.h"
52 #include "registry.h"
53 #include "async_timer.h"
54 #include "ice_util.h"
55 #include "transport_addr.h"
56 #include "nr_crypto.h"
57 #include "nr_socket.h"
58 #include "nr_socket_local.h"
59 #include "stun_client_ctx.h"
60 #include "stun_server_ctx.h"
61 #include "ice_ctx.h"
62 #include "ice_candidate.h"
63 #include "ice_handler.h"
64 }
65
66 // Local includes
67 #include "nricectx.h"
68 #include "nricemediastream.h"
69
70 namespace mozilla {
71
72 MOZ_MTLOG_MODULE("mtransport")
73
ToNrIceAddr(nr_transport_addr & addr,NrIceAddr * out)74 static bool ToNrIceAddr(nr_transport_addr &addr, NrIceAddr *out) {
75 int r;
76 char addrstring[INET6_ADDRSTRLEN + 1];
77
78 r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
79 if (r) return false;
80 out->host = addrstring;
81
82 int port;
83 r = nr_transport_addr_get_port(&addr, &port);
84 if (r) return false;
85
86 out->port = port;
87
88 switch (addr.protocol) {
89 case IPPROTO_TCP:
90 if (addr.tls_host[0] != '\0') {
91 out->transport = kNrIceTransportTls;
92 } else {
93 out->transport = kNrIceTransportTcp;
94 }
95 break;
96 case IPPROTO_UDP:
97 out->transport = kNrIceTransportUdp;
98 break;
99 default:
100 MOZ_CRASH();
101 return false;
102 }
103
104 return true;
105 }
106
ToNrIceCandidate(const nr_ice_candidate & candc,NrIceCandidate * out)107 static bool ToNrIceCandidate(const nr_ice_candidate &candc,
108 NrIceCandidate *out) {
109 MOZ_ASSERT(out);
110 int r;
111 // Const-cast because the internal nICEr code isn't const-correct.
112 nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc);
113
114 if (!ToNrIceAddr(cand->addr, &out->cand_addr)) return false;
115
116 if (cand->isock) {
117 nr_transport_addr addr;
118 r = nr_socket_getaddr(cand->isock->sock, &addr);
119 if (r) return false;
120
121 if (!ToNrIceAddr(addr, &out->local_addr)) return false;
122 }
123
124 NrIceCandidate::Type type;
125
126 switch (cand->type) {
127 case HOST:
128 type = NrIceCandidate::ICE_HOST;
129 break;
130 case SERVER_REFLEXIVE:
131 type = NrIceCandidate::ICE_SERVER_REFLEXIVE;
132 break;
133 case PEER_REFLEXIVE:
134 type = NrIceCandidate::ICE_PEER_REFLEXIVE;
135 break;
136 case RELAYED:
137 type = NrIceCandidate::ICE_RELAYED;
138 break;
139 default:
140 return false;
141 }
142
143 NrIceCandidate::TcpType tcp_type;
144 switch (cand->tcp_type) {
145 case TCP_TYPE_ACTIVE:
146 tcp_type = NrIceCandidate::ICE_ACTIVE;
147 break;
148 case TCP_TYPE_PASSIVE:
149 tcp_type = NrIceCandidate::ICE_PASSIVE;
150 break;
151 case TCP_TYPE_SO:
152 tcp_type = NrIceCandidate::ICE_SO;
153 break;
154 default:
155 tcp_type = NrIceCandidate::ICE_NONE;
156 break;
157 }
158
159 out->type = type;
160 out->tcp_type = tcp_type;
161 out->codeword = candc.codeword;
162 out->label = candc.label;
163 out->trickled = candc.trickled;
164 return true;
165 }
166
167 // Make an NrIceCandidate from the candidate |cand|.
168 // This is not a member fxn because we want to hide the
169 // defn of nr_ice_candidate but we pass by reference.
MakeNrIceCandidate(const nr_ice_candidate & candc)170 static UniquePtr<NrIceCandidate> MakeNrIceCandidate(
171 const nr_ice_candidate &candc) {
172 UniquePtr<NrIceCandidate> out(new NrIceCandidate());
173
174 if (!ToNrIceCandidate(candc, out.get())) {
175 return nullptr;
176 }
177 return out;
178 }
179
180 // NrIceMediaStream
Create(NrIceCtx * ctx,const std::string & name,int components)181 RefPtr<NrIceMediaStream> NrIceMediaStream::Create(NrIceCtx *ctx,
182 const std::string &name,
183 int components) {
184 RefPtr<NrIceMediaStream> stream = new NrIceMediaStream(ctx, name, components);
185 MOZ_ASSERT(stream->ctx_ == ctx->ctx());
186
187 int r = nr_ice_add_media_stream(ctx->ctx(), const_cast<char *>(name.c_str()),
188 components, &stream->stream_);
189 if (r) {
190 MOZ_MTLOG(ML_ERROR,
191 "Couldn't create ICE media stream for '" << name << "'");
192 return nullptr;
193 }
194
195 return stream;
196 }
197
NrIceMediaStream(NrIceCtx * ctx,const std::string & name,size_t components)198 NrIceMediaStream::NrIceMediaStream(NrIceCtx *ctx, const std::string &name,
199 size_t components)
200 : state_(ICE_CONNECTING),
201 ctx_(ctx->ctx()),
202 ctx_peer_(ctx->peer()),
203 name_(name),
204 components_(components),
205 stream_(nullptr),
206 level_(0),
207 has_parsed_attrs_(false) {}
208
~NrIceMediaStream()209 NrIceMediaStream::~NrIceMediaStream() {
210 // We do not need to destroy anything. All major resources
211 // are attached to the ice ctx.
212 }
213
ParseAttributes(std::vector<std::string> & attributes)214 nsresult NrIceMediaStream::ParseAttributes(
215 std::vector<std::string> &attributes) {
216 if (!stream_) return NS_ERROR_FAILURE;
217
218 std::vector<char *> attributes_in;
219
220 for (auto &attribute : attributes) {
221 attributes_in.push_back(const_cast<char *>(attribute.c_str()));
222 }
223
224 // Still need to call nr_ice_ctx_parse_stream_attributes.
225 int r = nr_ice_peer_ctx_parse_stream_attributes(
226 ctx_peer_, stream_, attributes_in.empty() ? nullptr : &attributes_in[0],
227 attributes_in.size());
228 if (r) {
229 MOZ_MTLOG(ML_ERROR,
230 "Couldn't parse attributes for stream " << name_ << "'");
231 return NS_ERROR_FAILURE;
232 }
233
234 has_parsed_attrs_ = true;
235 return NS_OK;
236 }
237
238 // Parse trickle ICE candidate
ParseTrickleCandidate(const std::string & candidate)239 nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string &candidate) {
240 int r;
241
242 MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->label << ")/STREAM(" << name()
243 << ") : parsing trickle candidate "
244 << candidate);
245
246 r = nr_ice_peer_ctx_parse_trickle_candidate(
247 ctx_peer_, stream_, const_cast<char *>(candidate.c_str()));
248 if (r) {
249 if (r == R_ALREADY) {
250 MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '"
251 << name_ << "' because it is completed");
252
253 } else {
254 MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
255 << name_ << "'");
256 return NS_ERROR_FAILURE;
257 }
258 }
259
260 return NS_OK;
261 }
262
263 // Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled.
GetActivePair(int component,UniquePtr<NrIceCandidate> * localp,UniquePtr<NrIceCandidate> * remotep)264 nsresult NrIceMediaStream::GetActivePair(int component,
265 UniquePtr<NrIceCandidate> *localp,
266 UniquePtr<NrIceCandidate> *remotep) {
267 int r;
268 nr_ice_candidate *local_int;
269 nr_ice_candidate *remote_int;
270
271 if (!stream_) {
272 return NS_ERROR_NOT_AVAILABLE;
273 }
274
275 r = nr_ice_media_stream_get_active(ctx_peer_, stream_, component, &local_int,
276 &remote_int);
277 // If result is R_REJECTED then component is unpaired or disabled.
278 if (r == R_REJECTED) return NS_ERROR_NOT_AVAILABLE;
279
280 if (r) return NS_ERROR_FAILURE;
281
282 UniquePtr<NrIceCandidate> local(MakeNrIceCandidate(*local_int));
283 if (!local) return NS_ERROR_FAILURE;
284
285 UniquePtr<NrIceCandidate> remote(MakeNrIceCandidate(*remote_int));
286 if (!remote) return NS_ERROR_FAILURE;
287
288 if (localp) *localp = Move(local);
289 if (remotep) *remotep = Move(remote);
290
291 return NS_OK;
292 }
293
GetCandidatePairs(std::vector<NrIceCandidatePair> * out_pairs) const294 nsresult NrIceMediaStream::GetCandidatePairs(
295 std::vector<NrIceCandidatePair> *out_pairs) const {
296 MOZ_ASSERT(out_pairs);
297 if (!stream_) {
298 return NS_ERROR_NOT_AVAILABLE;
299 }
300
301 // If we haven't at least started checking then there is nothing to report
302 if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
303 return NS_OK;
304 }
305
306 // Get the check_list on the peer stream (this is where the check_list
307 // actually lives, not in stream_)
308 nr_ice_media_stream *peer_stream;
309 int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
310 if (r != 0) {
311 return NS_ERROR_FAILURE;
312 }
313
314 nr_ice_cand_pair *p1, *p2;
315 out_pairs->clear();
316
317 TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) {
318 MOZ_ASSERT(p1);
319 MOZ_ASSERT(p1->local);
320 MOZ_ASSERT(p1->remote);
321 NrIceCandidatePair pair;
322
323 p2 = TAILQ_FIRST(&peer_stream->check_list);
324 while (p2) {
325 if (p1 == p2) {
326 /* Don't compare with our self. */
327 p2 = TAILQ_NEXT(p2, check_queue_entry);
328 continue;
329 }
330 if (strncmp(p1->codeword, p2->codeword, sizeof(p1->codeword)) == 0) {
331 /* In case of duplicate pairs we only report the one winning pair */
332 if (((p2->remote->component && (p2->remote->component->active == p2)) &&
333 !(p1->remote->component &&
334 (p1->remote->component->active == p1))) ||
335 ((p2->peer_nominated || p2->nominated) &&
336 !(p1->peer_nominated || p1->nominated)) ||
337 (p2->priority > p1->priority) ||
338 ((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) &&
339 (p1->state != NR_ICE_PAIR_STATE_SUCCEEDED)) ||
340 ((p2->state != NR_ICE_PAIR_STATE_CANCELLED) &&
341 (p1->state == NR_ICE_PAIR_STATE_CANCELLED))) {
342 /* p2 is a better pair. */
343 break;
344 }
345 }
346 p2 = TAILQ_NEXT(p2, check_queue_entry);
347 }
348 if (p2) {
349 /* p2 points to a duplicate but better pair so skip this one */
350 continue;
351 }
352
353 switch (p1->state) {
354 case NR_ICE_PAIR_STATE_FROZEN:
355 pair.state = NrIceCandidatePair::State::STATE_FROZEN;
356 break;
357 case NR_ICE_PAIR_STATE_WAITING:
358 pair.state = NrIceCandidatePair::State::STATE_WAITING;
359 break;
360 case NR_ICE_PAIR_STATE_IN_PROGRESS:
361 pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS;
362 break;
363 case NR_ICE_PAIR_STATE_FAILED:
364 pair.state = NrIceCandidatePair::State::STATE_FAILED;
365 break;
366 case NR_ICE_PAIR_STATE_SUCCEEDED:
367 pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED;
368 break;
369 case NR_ICE_PAIR_STATE_CANCELLED:
370 pair.state = NrIceCandidatePair::State::STATE_CANCELLED;
371 break;
372 default:
373 MOZ_ASSERT(0);
374 }
375
376 pair.priority = p1->priority;
377 pair.nominated = p1->peer_nominated || p1->nominated;
378 pair.component_id = p1->remote->component->component_id;
379
380 // As discussed with drno: a component's can_send field (set to true
381 // by ICE consent) is a very close approximation for writable and
382 // readable. Note: the component for the local candidate never has
383 // the can_send member set to true, remote for both readable and
384 // writable. (mjf)
385 pair.writable = p1->remote->component->can_send;
386 pair.readable = p1->remote->component->can_send;
387 pair.selected =
388 p1->remote->component && p1->remote->component->active == p1;
389 pair.codeword = p1->codeword;
390 pair.bytes_sent = p1->bytes_sent;
391 pair.bytes_recvd = p1->bytes_recvd;
392 pair.ms_since_last_send =
393 p1->last_sent.tv_sec * 1000 + p1->last_sent.tv_usec / 1000;
394 pair.ms_since_last_recv =
395 p1->last_recvd.tv_sec * 1000 + p1->last_recvd.tv_usec / 1000;
396
397 if (!ToNrIceCandidate(*(p1->local), &pair.local) ||
398 !ToNrIceCandidate(*(p1->remote), &pair.remote)) {
399 return NS_ERROR_FAILURE;
400 }
401
402 out_pairs->push_back(pair);
403 }
404
405 return NS_OK;
406 }
407
GetDefaultCandidate(int component,NrIceCandidate * candidate) const408 nsresult NrIceMediaStream::GetDefaultCandidate(
409 int component, NrIceCandidate *candidate) const {
410 nr_ice_candidate *cand;
411
412 int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);
413 if (r) {
414 MOZ_MTLOG(ML_ERROR,
415 "Couldn't get default ICE candidate for '" << name_ << "'");
416 return NS_ERROR_FAILURE;
417 }
418
419 if (!ToNrIceCandidate(*cand, candidate)) {
420 MOZ_MTLOG(ML_ERROR,
421 "Failed to convert default ICE candidate for '" << name_ << "'");
422 return NS_ERROR_FAILURE;
423 }
424
425 return NS_OK;
426 }
427
GetCandidates() const428 std::vector<std::string> NrIceMediaStream::GetCandidates() const {
429 char **attrs = nullptr;
430 int attrct;
431 int r;
432 std::vector<std::string> ret;
433
434 if (!stream_) {
435 return ret;
436 }
437
438 r = nr_ice_media_stream_get_attributes(stream_, &attrs, &attrct);
439 if (r) {
440 MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '" << name_ << "'");
441 return ret;
442 }
443
444 for (int i = 0; i < attrct; i++) {
445 ret.push_back(attrs[i]);
446 RFREE(attrs[i]);
447 }
448
449 RFREE(attrs);
450
451 return ret;
452 }
453
GetCandidatesFromStream(nr_ice_media_stream * stream,std::vector<NrIceCandidate> * candidates)454 static nsresult GetCandidatesFromStream(
455 nr_ice_media_stream *stream, std::vector<NrIceCandidate> *candidates) {
456 MOZ_ASSERT(candidates);
457 nr_ice_component *comp = STAILQ_FIRST(&stream->components);
458 while (comp) {
459 if (comp->state != NR_ICE_COMPONENT_DISABLED) {
460 nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates);
461 while (cand) {
462 NrIceCandidate new_cand;
463 // This can fail if the candidate is server reflexive or relayed, and
464 // has not yet received a response (ie; it doesn't know its address
465 // yet). For the purposes of this code, this isn't a candidate we're
466 // interested in, since it is not fully baked yet.
467 if (ToNrIceCandidate(*cand, &new_cand)) {
468 candidates->push_back(new_cand);
469 }
470 cand = TAILQ_NEXT(cand, entry_comp);
471 }
472 }
473 comp = STAILQ_NEXT(comp, entry);
474 }
475
476 return NS_OK;
477 }
478
GetLocalCandidates(std::vector<NrIceCandidate> * candidates) const479 nsresult NrIceMediaStream::GetLocalCandidates(
480 std::vector<NrIceCandidate> *candidates) const {
481 if (!stream_) {
482 return NS_ERROR_NOT_AVAILABLE;
483 }
484
485 return GetCandidatesFromStream(stream_, candidates);
486 }
487
GetRemoteCandidates(std::vector<NrIceCandidate> * candidates) const488 nsresult NrIceMediaStream::GetRemoteCandidates(
489 std::vector<NrIceCandidate> *candidates) const {
490 if (!stream_) {
491 return NS_ERROR_NOT_AVAILABLE;
492 }
493
494 // If we haven't at least started checking then there is nothing to report
495 if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
496 return NS_OK;
497 }
498
499 nr_ice_media_stream *peer_stream;
500 int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
501 if (r != 0) {
502 return NS_ERROR_FAILURE;
503 }
504
505 return GetCandidatesFromStream(peer_stream, candidates);
506 }
507
DisableComponent(int component_id)508 nsresult NrIceMediaStream::DisableComponent(int component_id) {
509 if (!stream_) return NS_ERROR_FAILURE;
510
511 int r = nr_ice_media_stream_disable_component(stream_, component_id);
512 if (r) {
513 MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" << component_id);
514 return NS_ERROR_FAILURE;
515 }
516
517 return NS_OK;
518 }
519
GetConsentStatus(int component_id,bool * can_send,struct timeval * ts)520 nsresult NrIceMediaStream::GetConsentStatus(int component_id, bool *can_send,
521 struct timeval *ts) {
522 if (!stream_) return NS_ERROR_FAILURE;
523
524 nr_ice_media_stream *peer_stream;
525 int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
526 if (r) {
527 MOZ_MTLOG(ML_ERROR, "Failed to find peer stream for '"
528 << name_ << "':" << component_id);
529 return NS_ERROR_FAILURE;
530 }
531
532 int send = 0;
533 r = nr_ice_media_stream_get_consent_status(peer_stream, component_id, &send,
534 ts);
535 if (r) {
536 MOZ_MTLOG(ML_ERROR, "Failed to get consent status for '"
537 << name_ << "':" << component_id);
538 return NS_ERROR_FAILURE;
539 }
540 *can_send = !!send;
541
542 return NS_OK;
543 }
544
SendPacket(int component_id,const unsigned char * data,size_t len)545 nsresult NrIceMediaStream::SendPacket(int component_id,
546 const unsigned char *data, size_t len) {
547 if (!stream_) return NS_ERROR_FAILURE;
548
549 int r = nr_ice_media_stream_send(ctx_peer_, stream_, component_id,
550 const_cast<unsigned char *>(data), len);
551 if (r) {
552 MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'");
553 if (r == R_WOULDBLOCK) {
554 return NS_BASE_STREAM_WOULD_BLOCK;
555 }
556
557 return NS_BASE_STREAM_OSERROR;
558 }
559
560 return NS_OK;
561 }
562
Ready()563 void NrIceMediaStream::Ready() {
564 // This function is called whenever a stream becomes ready, but it
565 // gets fired multiple times when a stream gets nominated repeatedly.
566 if (state_ != ICE_OPEN) {
567 MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'");
568 state_ = ICE_OPEN;
569 SignalReady(this);
570 } else {
571 MOZ_MTLOG(ML_DEBUG,
572 "Stream ready callback fired again for '" << name_ << "'");
573 }
574 }
575
Close()576 void NrIceMediaStream::Close() {
577 MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
578 state_ = ICE_CLOSED;
579
580 if (stream_) {
581 int r = nr_ice_remove_media_stream(ctx_, &stream_);
582 if (r) {
583 MOZ_ASSERT(false, "Failed to remove stream");
584 MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
585 }
586 }
587 }
588
589 } // namespace mozilla
590