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