1 /*
2 Copyright (c) 2007, Adobe Systems, Incorporated
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9 * Redistributions of source code must retain the above copyright
10   notice, this list of conditions and the following disclaimer.
11 
12 * Redistributions in binary form must reproduce the above copyright
13   notice, this list of conditions and the following disclaimer in the
14   documentation and/or other materials provided with the distribution.
15 
16 * Neither the name of Adobe Systems, Network Resonance nor the names of its
17   contributors may be used to endorse or promote products derived from
18   this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 #include <string.h>
34 #include <assert.h>
35 #include <nr_api.h>
36 #include <r_assoc.h>
37 #include <async_timer.h>
38 #include "ice_util.h"
39 #include "ice_ctx.h"
40 
41 static char *nr_ice_media_stream_states[]={"INVALID",
42   "UNPAIRED","FROZEN","ACTIVE","CONNECTED","FAILED"
43 };
44 
45 int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
46 
nr_ice_media_stream_create(nr_ice_ctx * ctx,const char * label,const char * ufrag,const char * pwd,int components,nr_ice_media_stream ** streamp)47 int nr_ice_media_stream_create(nr_ice_ctx *ctx,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp)
48   {
49     int r,_status;
50     nr_ice_media_stream *stream=0;
51     nr_ice_component *comp=0;
52     int i;
53 
54     if(!(stream=RCALLOC(sizeof(nr_ice_media_stream))))
55       ABORT(R_NO_MEMORY);
56 
57     if(!(stream->label=r_strdup(label)))
58       ABORT(R_NO_MEMORY);
59 
60     if(!(stream->ufrag=r_strdup(ufrag)))
61       ABORT(R_NO_MEMORY);
62 
63     if(!(stream->pwd=r_strdup(pwd)))
64       ABORT(R_NO_MEMORY);
65 
66     stream->ctx=ctx;
67 
68     STAILQ_INIT(&stream->components);
69     for(i=0;i<components;i++){
70       /* component-id must be > 0, so increment by 1 */
71       if(r=nr_ice_component_create(stream, i+1, &comp))
72         ABORT(r);
73 
74     }
75 
76     TAILQ_INIT(&stream->check_list);
77     TAILQ_INIT(&stream->trigger_check_queue);
78 
79     stream->disconnected = 0;
80     stream->component_ct=components;
81     stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED;
82     stream->obsolete = 0;
83     stream->r2l_user = 0;
84     stream->l2r_user = 0;
85     stream->flags = ctx->flags;
86     if(ctx->stun_server_ct_cfg) {
87       if(!(stream->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*(ctx->stun_server_ct_cfg))))
88         ABORT(R_NO_MEMORY);
89 
90       memcpy(stream->stun_servers,ctx->stun_servers_cfg,sizeof(nr_ice_stun_server)*(ctx->stun_server_ct_cfg));
91       stream->stun_server_ct = ctx->stun_server_ct_cfg;
92     }
93 
94     if(ctx->turn_server_ct_cfg) {
95       if(!(stream->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*(ctx->turn_server_ct_cfg))))
96         ABORT(R_NO_MEMORY);
97 
98       for(int i = 0; i < ctx->turn_server_ct_cfg; ++i) {
99         nr_ice_turn_server *dst = &stream->turn_servers[i];
100         nr_ice_turn_server *src = &ctx->turn_servers_cfg[i];
101         memcpy(&dst->turn_server, &src->turn_server, sizeof(nr_ice_stun_server));
102         dst->username = r_strdup(src->username);
103         r_data_create(&dst->password, src->password->data, src->password->len);
104       }
105       stream->turn_server_ct = ctx->turn_server_ct_cfg;
106     }
107 
108     r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): flags %d",stream->label,stream->flags);
109     *streamp=stream;
110 
111     _status=0;
112   abort:
113     if(_status){
114       nr_ice_media_stream_destroy(&stream);
115     }
116     return(_status);
117   }
118 
nr_ice_media_stream_destroy(nr_ice_media_stream ** streamp)119 int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp)
120   {
121     nr_ice_media_stream *stream;
122     nr_ice_component *c1,*c2;
123     nr_ice_cand_pair *p1,*p2;
124     if(!streamp || !*streamp)
125       return(0);
126 
127     stream=*streamp;
128     *streamp=0;
129 
130     STAILQ_FOREACH_SAFE(c1, &stream->components, entry, c2){
131       STAILQ_REMOVE(&stream->components,c1,nr_ice_component_,entry);
132       nr_ice_component_destroy(&c1);
133     }
134 
135     /* Note: all the entries from the trigger check queue are held in here as
136      *       well, so we only clean up the super set. */
137     TAILQ_FOREACH_SAFE(p1, &stream->check_list, check_queue_entry, p2){
138       TAILQ_REMOVE(&stream->check_list,p1,check_queue_entry);
139       nr_ice_candidate_pair_destroy(&p1);
140     }
141 
142     RFREE(stream->label);
143 
144     RFREE(stream->ufrag);
145     RFREE(stream->pwd);
146     RFREE(stream->r2l_user);
147     RFREE(stream->l2r_user);
148     r_data_zfree(&stream->r2l_pass);
149     r_data_zfree(&stream->l2r_pass);
150 
151     RFREE(stream->stun_servers);
152     for (int i = 0; i < stream->turn_server_ct; i++) {
153         RFREE(stream->turn_servers[i].username);
154         r_data_destroy(&stream->turn_servers[i].password);
155     }
156     RFREE(stream->turn_servers);
157 
158     if(stream->timer)
159       NR_async_timer_cancel(stream->timer);
160 
161     RFREE(stream);
162 
163     return(0);
164   }
165 
nr_ice_media_stream_initialize(nr_ice_ctx * ctx,nr_ice_media_stream * stream)166 int nr_ice_media_stream_initialize(nr_ice_ctx *ctx, nr_ice_media_stream *stream)
167   {
168     int r,_status;
169     nr_ice_component *comp;
170 
171     assert(!stream->obsolete);
172 
173     comp=STAILQ_FIRST(&stream->components);
174     while(comp){
175       if(r=nr_ice_component_initialize(ctx,comp))
176         ABORT(r);
177       comp=STAILQ_NEXT(comp,entry);
178     }
179 
180     _status=0;
181   abort:
182     return(_status);
183   }
184 
185 
nr_ice_media_stream_get_attributes(nr_ice_media_stream * stream,char *** attrsp,int * attrctp)186 int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp, int *attrctp)
187   {
188     int attrct=2;
189     nr_ice_component *comp;
190     char **attrs=0;
191     int index=0;
192     nr_ice_candidate *cand;
193     int r,_status;
194     char *tmp=0;
195 
196     *attrctp=0;
197 
198     /* First find out how many attributes we need */
199     comp=STAILQ_FIRST(&stream->components);
200     while(comp){
201       if (comp->state != NR_ICE_COMPONENT_DISABLED) {
202         cand = TAILQ_FIRST(&comp->candidates);
203         while(cand){
204           if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) {
205             ++attrct;
206           }
207 
208           cand = TAILQ_NEXT(cand, entry_comp);
209         }
210       }
211       comp=STAILQ_NEXT(comp,entry);
212     }
213 
214     /* Make the array we'll need */
215     if(!(attrs=RCALLOC(sizeof(char *)*attrct)))
216       ABORT(R_NO_MEMORY);
217     for(index=0;index<attrct;index++){
218       if(!(attrs[index]=RMALLOC(NR_ICE_MAX_ATTRIBUTE_SIZE)))
219         ABORT(R_NO_MEMORY);
220     }
221 
222     index=0;
223     /* Now format the attributes */
224     comp=STAILQ_FIRST(&stream->components);
225     while(comp){
226       if (comp->state != NR_ICE_COMPONENT_DISABLED) {
227         nr_ice_candidate *cand;
228 
229         cand=TAILQ_FIRST(&comp->candidates);
230         while(cand){
231           if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) {
232             assert(index < attrct);
233 
234             if (index >= attrct)
235               ABORT(R_INTERNAL);
236 
237             if(r=nr_ice_format_candidate_attribute(cand, attrs[index],NR_ICE_MAX_ATTRIBUTE_SIZE, 0))
238               ABORT(r);
239 
240             index++;
241           }
242 
243           cand=TAILQ_NEXT(cand,entry_comp);
244         }
245       }
246       comp=STAILQ_NEXT(comp,entry);
247     }
248 
249     /* Now, ufrag and pwd */
250     if(!(tmp=RMALLOC(100)))
251       ABORT(R_NO_MEMORY);
252     snprintf(tmp,100,"ice-ufrag:%s",stream->ufrag);
253     attrs[index++]=tmp;
254 
255     if(!(tmp=RMALLOC(100)))
256       ABORT(R_NO_MEMORY);
257     snprintf(tmp,100,"ice-pwd:%s",stream->pwd);
258     attrs[index++]=tmp;
259 
260     *attrsp=attrs;
261     *attrctp=attrct;
262 
263     _status=0;
264   abort:
265     if(_status){
266       if(attrs){
267         for(index=0;index<attrct;index++){
268           RFREE(attrs[index]);
269         }
270         RFREE(attrs);
271       }
272     }
273     return(_status);
274   }
275 
276 /* Get a default candidate per 4.1.4 */
nr_ice_media_stream_get_default_candidate(nr_ice_media_stream * stream,int component,nr_ice_candidate ** candp)277 int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp)
278   {
279     int r,_status;
280     nr_ice_component *comp;
281 
282     comp=STAILQ_FIRST(&stream->components);
283     while(comp){
284       if (comp->component_id == component)
285         break;
286 
287       comp=STAILQ_NEXT(comp,entry);
288     }
289 
290     if (!comp)
291       ABORT(R_NOT_FOUND);
292 
293     /* If there aren't any IPV4 candidates, try IPV6 */
294     if((r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV4)) &&
295        (r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV6))) {
296       ABORT(r);
297     }
298 
299     _status=0;
300   abort:
301     return(_status);
302   }
303 
304 
nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx * pctx,nr_ice_media_stream * lstream,nr_ice_media_stream * pstream)305 int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream)
306   {
307     int r,_status;
308     nr_ice_component *pcomp,*lcomp;
309 
310     pcomp=STAILQ_FIRST(&pstream->components);
311     lcomp=STAILQ_FIRST(&lstream->components);
312     while(pcomp){
313       if ((lcomp->state != NR_ICE_COMPONENT_DISABLED) &&
314           (pcomp->state != NR_ICE_COMPONENT_DISABLED)) {
315         if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp))
316           ABORT(r);
317       }
318 
319       lcomp=STAILQ_NEXT(lcomp,entry);
320       pcomp=STAILQ_NEXT(pcomp,entry);
321     };
322 
323     if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) {
324       nr_ice_media_stream_set_state(pstream, NR_ICE_MEDIA_STREAM_CHECKS_FROZEN);
325     }
326 
327     _status=0;
328   abort:
329     return(_status);
330   }
331 
nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx * pctx,nr_ice_media_stream * lstream,nr_ice_media_stream * pstream,int * serviced)332 int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, nr_ice_media_stream *pstream, int *serviced)
333   {
334     nr_ice_component *pcomp;
335     int r,_status;
336     char *user = 0;
337 
338     if (serviced)
339       *serviced = 0;
340 
341     pcomp=STAILQ_FIRST(&pstream->components);
342     while(pcomp){
343       int serviced_inner=0;
344 
345       /* Flush all the pre-answer requests */
346       if(r=nr_ice_component_service_pre_answer_requests(pctx, pcomp, pstream->r2l_user, &serviced_inner))
347         ABORT(r);
348       if (serviced)
349         *serviced += serviced_inner;
350 
351       pcomp=STAILQ_NEXT(pcomp,entry);
352     }
353 
354     _status=0;
355    abort:
356     RFREE(user);
357     return(_status);
358   }
359 
360 /* S 5.8 -- run the first pair from the triggered check queue (even after
361  * checks have completed S 8.1.2) or run the highest priority WAITING pair or
362  * if not available FROZEN pair from the check queue */
nr_ice_media_stream_check_timer_cb(NR_SOCKET s,int h,void * cb_arg)363 static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg)
364   {
365     int r,_status;
366     nr_ice_media_stream *stream=cb_arg;
367     nr_ice_cand_pair *pair = 0;
368     int timer_multiplier=stream->pctx->active_streams ? stream->pctx->active_streams : 1;
369     int timer_val=stream->pctx->ctx->Ta*timer_multiplier;
370 
371     assert(timer_val>0);
372 
373     r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label);
374     stream->timer=0;
375 
376     /* The trigger check queue has the highest priority */
377     pair=TAILQ_FIRST(&stream->trigger_check_queue);
378     while(pair){
379       if(pair->state==NR_ICE_PAIR_STATE_WAITING){
380         /* Remove the pair from he trigger check queue */
381         r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Removing pair from trigger check queue %s",stream->pctx->label,pair->as_string);
382         TAILQ_REMOVE(&stream->trigger_check_queue,pair,triggered_check_queue_entry);
383         break;
384       }
385       pair=TAILQ_NEXT(pair,triggered_check_queue_entry);
386     }
387 
388     if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
389       if(!pair){
390         /* Find the highest priority WAITING check and move it to RUNNING */
391         pair=TAILQ_FIRST(&stream->check_list);
392         while(pair){
393           if(pair->state==NR_ICE_PAIR_STATE_WAITING)
394             break;
395           pair=TAILQ_NEXT(pair,check_queue_entry);
396         }
397       }
398 
399       /* Hmmm... No WAITING. Let's look for FROZEN */
400       if(!pair){
401         pair=TAILQ_FIRST(&stream->check_list);
402 
403         while(pair){
404           if(pair->state==NR_ICE_PAIR_STATE_FROZEN){
405             if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
406               ABORT(r);
407             break;
408           }
409           pair=TAILQ_NEXT(pair,check_queue_entry);
410         }
411       }
412     }
413 
414     if(pair){
415       nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */
416       NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer);
417     }
418     else {
419       r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): no FROZEN/WAITING pairs for %s",stream->pctx->label,stream->label);
420     }
421 
422     _status=0;
423   abort:
424     if (_status) {
425       // cb doesn't return anything, but we should probably log that we aborted
426       // This also quiets the unused variable warnings.
427       r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer cb for media stream %s abort with status: %d",
428         stream->pctx->label,stream->label, _status);
429     }
430     return;
431   }
432 
433 /* Start checks for this media stream (aka check list) */
nr_ice_media_stream_start_checks(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream)434 int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
435   {
436     int r,_status;
437 
438     /* Don't start the check timer if the stream is failed */
439     if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
440       assert(0);
441       ABORT(R_INTERNAL);
442     }
443 
444     if (stream->local_stream->obsolete) {
445       assert(0);
446       ABORT(R_INTERNAL);
447     }
448 
449     /* Even if the stream is completed already remote can still create a new
450      * triggered check request which needs to fire, but not change our stream
451      * state. */
452     if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
453       if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)) {
454         ABORT(r);
455       }
456     }
457 
458     if (!stream->timer) {
459       r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): Starting check timer for stream.",pctx->label,stream->label);
460       nr_ice_media_stream_check_timer_cb(0,0,stream);
461     }
462 
463     nr_ice_peer_ctx_stream_started_checks(pctx, stream);
464 
465     _status=0;
466   abort:
467     return(_status);
468   }
469 
470 /* Start checks for this media stream (aka check list) S 5.7 */
nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream)471 int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
472   {
473     int r,_status;
474     r_assoc *assoc=0;
475     nr_ice_cand_pair *pair=0;
476 
477     /* Already seen assoc */
478     if(r=r_assoc_create(&assoc,r_assoc_crc32_hash_compute,5))
479       ABORT(r);
480 
481     /* S 5.7.4. Set the highest priority pairs in each foundation to WAITING */
482     pair=TAILQ_FIRST(&stream->check_list);
483     while(pair){
484       void *v;
485 
486       if(r=r_assoc_fetch(assoc,pair->foundation,strlen(pair->foundation),&v)){
487         if(r!=R_NOT_FOUND)
488           ABORT(r);
489         if(r=nr_ice_candidate_pair_unfreeze(pctx,pair))
490           ABORT(r);
491 
492         if(r=r_assoc_insert(assoc,pair->foundation,strlen(pair->foundation),
493           0,0,0,R_ASSOC_NEW))
494           ABORT(r);
495       }
496 
497       /* Already exists... fall through */
498       pair=TAILQ_NEXT(pair,check_queue_entry);
499     }
500 
501     _status=0;
502   abort:
503     r_assoc_destroy(&assoc);
504     return(_status);
505   }
506 
nr_ice_media_stream_unfreeze_pairs_match(nr_ice_media_stream * stream,char * foundation)507 static int nr_ice_media_stream_unfreeze_pairs_match(nr_ice_media_stream *stream, char *foundation)
508   {
509     nr_ice_cand_pair *pair;
510     int r,_status;
511     int unfroze=0;
512 
513     pair=TAILQ_FIRST(&stream->check_list);
514     while(pair){
515       if(pair->state==NR_ICE_PAIR_STATE_FROZEN &&
516         !strcmp(foundation,pair->foundation)){
517         if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
518           ABORT(r);
519         unfroze++;
520       }
521       pair=TAILQ_NEXT(pair,check_queue_entry);
522     }
523 
524     if(!unfroze)
525       return(R_NOT_FOUND);
526 
527     _status=0;
528   abort:
529     return(_status);
530   }
531 
532 /* S 7.1.2.2 */
nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream * stream,char * foundation)533 int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation)
534   {
535     int r,_status;
536     nr_ice_media_stream *str;
537        nr_ice_component *comp;
538     int invalid_comps=0;
539 
540     /* 1. Unfreeze all frozen pairs with the same foundation
541        in this stream */
542     if(r=nr_ice_media_stream_unfreeze_pairs_match(stream,foundation)){
543       if(r!=R_NOT_FOUND)
544         ABORT(r);
545     }
546 
547     /* 2. See if there is a pair in the valid list for every component */
548     comp=STAILQ_FIRST(&stream->components);
549     while(comp){
550       if(!comp->valid_pairs)
551         invalid_comps++;
552 
553       comp=STAILQ_NEXT(comp,entry);
554     }
555 
556     /* If there is a pair in the valid list for every component... */
557     /* Now go through the check lists for the other streams */
558     str=STAILQ_FIRST(&stream->pctx->peer_streams);
559     while(str){
560       if(str!=stream && !str->local_stream->obsolete){
561         switch(str->ice_state){
562           case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE:
563             /* Unfreeze matching pairs */
564             if(r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation)){
565               if(r!=R_NOT_FOUND)
566                 ABORT(r);
567             }
568             break;
569           case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN:
570             /* Unfreeze matching pairs if any */
571             r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation);
572             if(r){
573               if(r!=R_NOT_FOUND)
574                 ABORT(r);
575 
576               /* OK, no matching pairs: execute the algorithm from 5.7
577                  for this stream */
578               if(r=nr_ice_media_stream_unfreeze_pairs(str->pctx,str))
579                 ABORT(r);
580             }
581             if(r=nr_ice_media_stream_start_checks(str->pctx,str))
582               ABORT(r);
583 
584             break;
585           default:
586             break;
587         }
588       }
589 
590       str=STAILQ_NEXT(str,entry);
591     }
592 
593     _status=0;
594   abort:
595     return(_status);
596   }
597 
598 
nr_ice_media_stream_dump_state(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,int log_level)599 void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level)
600   {
601     nr_ice_cand_pair *pair;
602     nr_ice_component *comp;
603 
604     if(stream->local_stream){
605       /* stream has a corresponding local_stream */
606       nr_ice_media_stream_dump_state(stream->local_stream->pctx,stream->local_stream, log_level);
607       r_log(LOG_ICE,log_level,"ICE-PEER(%s)/STREAM(%s): state dump", stream->pctx->label, stream->label);
608     } else {
609       r_log(LOG_ICE,log_level,"ICE(%s)/STREAM(%s): state dump", stream->ctx->label, stream->label);
610     }
611 
612     pair=TAILQ_FIRST(&stream->check_list);
613     while(pair){
614       nr_ice_candidate_pair_dump_state(pair, log_level);
615       pair=TAILQ_NEXT(pair,check_queue_entry);
616     }
617 
618     comp=STAILQ_FIRST(&stream->components);
619     while(comp){
620       nr_ice_component_dump_state(comp, log_level);
621       comp=STAILQ_NEXT(comp,entry);
622     }
623   }
624 
nr_ice_media_stream_set_state(nr_ice_media_stream * str,int state)625 int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state)
626   {
627     /* Make no-change a no-op */
628     if (state == str->ice_state)
629       return 0;
630 
631     assert((size_t)state < sizeof(nr_ice_media_stream_states)/sizeof(char *));
632     assert((size_t)str->ice_state < sizeof(nr_ice_media_stream_states)/sizeof(char *));
633 
634     r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): stream %s state %s->%s",
635       str->pctx->label,str->label,
636       nr_ice_media_stream_states[str->ice_state],
637       nr_ice_media_stream_states[state]);
638 
639     if(state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)
640       str->pctx->active_streams++;
641     if(str->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)
642       str->pctx->active_streams--;
643 
644     r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): %d active streams",
645       str->pctx->label, str->pctx->active_streams);
646 
647     str->ice_state=state;
648     if (state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
649       nr_ice_media_stream_dump_state(str->pctx,str,LOG_ERR);
650     }
651 
652     return(0);
653   }
654 
nr_ice_media_stream_stop_checking(nr_ice_media_stream * str)655 void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str)
656   {
657     nr_ice_cand_pair *p;
658     nr_ice_component *comp;
659 
660     /* Cancel candidate pairs */
661     p=TAILQ_FIRST(&str->check_list);
662     while(p){
663       nr_ice_candidate_pair_cancel(p->pctx,p,0);
664       p=TAILQ_NEXT(p,check_queue_entry);
665     }
666 
667     if(str->timer) {
668       NR_async_timer_cancel(str->timer);
669       str->timer = 0;
670     }
671 
672     /* Cancel consent timers in case it is running already */
673     comp=STAILQ_FIRST(&str->components);
674     while(comp){
675       nr_ice_component_consent_destroy(comp);
676       comp=STAILQ_NEXT(comp,entry);
677     }
678   }
679 
nr_ice_media_stream_set_obsolete(nr_ice_media_stream * str)680 void nr_ice_media_stream_set_obsolete(nr_ice_media_stream *str)
681   {
682     nr_ice_component *c1,*c2;
683     str->obsolete = 1;
684 
685     STAILQ_FOREACH_SAFE(c1, &str->components, entry, c2){
686       nr_ice_component_stop_gathering(c1);
687     }
688 
689     nr_ice_media_stream_stop_checking(str);
690   }
691 
nr_ice_media_stream_is_done_gathering(nr_ice_media_stream * str)692 int nr_ice_media_stream_is_done_gathering(nr_ice_media_stream *str)
693   {
694     nr_ice_component *comp;
695     comp=STAILQ_FIRST(&str->components);
696     while(comp){
697       if(!nr_ice_component_is_done_gathering(comp)) {
698         return 0;
699       }
700       comp=STAILQ_NEXT(comp,entry);
701     }
702     return 1;
703   }
704 
nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream * stream)705 void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream)
706   {
707     nr_ice_component *comp;
708 
709     comp=STAILQ_FIRST(&stream->components);
710     while(comp){
711       if(comp->disconnected) {
712         nr_ice_component_refresh_consent_now(comp);
713       }
714 
715       comp=STAILQ_NEXT(comp,entry);
716     }
717   }
718 
nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream * stream)719 void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream)
720   {
721     nr_ice_component *comp;
722 
723     comp=STAILQ_FIRST(&stream->components);
724     while(comp){
725       comp->disconnected = 1;
726 
727       comp=STAILQ_NEXT(comp,entry);
728     }
729   }
730 
nr_ice_media_stream_set_disconnected(nr_ice_media_stream * stream,int disconnected)731 void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected)
732   {
733     if (stream->disconnected == disconnected) {
734       return;
735     }
736 
737     if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
738       return;
739     }
740     stream->disconnected = disconnected;
741 
742     if (disconnected == NR_ICE_MEDIA_STREAM_DISCONNECTED) {
743       if (!stream->local_stream->obsolete) {
744         nr_ice_peer_ctx_disconnected(stream->pctx);
745       }
746     } else {
747       nr_ice_peer_ctx_check_if_connected(stream->pctx);
748     }
749   }
750 
nr_ice_media_stream_check_if_connected(nr_ice_media_stream * stream)751 int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream)
752   {
753     nr_ice_component *comp;
754 
755     comp=STAILQ_FIRST(&stream->components);
756     while(comp){
757       if((comp->state != NR_ICE_COMPONENT_DISABLED) &&
758          (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) &&
759          comp->disconnected)
760         break;
761 
762       comp=STAILQ_NEXT(comp,entry);
763     }
764 
765     /* At least one disconnected component */
766     if(comp)
767       goto done;
768 
769     nr_ice_media_stream_set_disconnected(stream, NR_ICE_MEDIA_STREAM_CONNECTED);
770 
771   done:
772     return(0);
773   }
774 
775 /* S OK, this component has a nominated. If every component has a nominated,
776    the stream is ready */
nr_ice_media_stream_component_nominated(nr_ice_media_stream * stream,nr_ice_component * component)777 void nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component)
778   {
779     nr_ice_component *comp;
780 
781     comp=STAILQ_FIRST(&stream->components);
782     while(comp){
783       if((comp->state != NR_ICE_COMPONENT_DISABLED) &&
784          (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) &&
785          !comp->nominated)
786         break;
787 
788       comp=STAILQ_NEXT(comp,entry);
789     }
790 
791     /* At least one un-nominated component */
792     if(comp)
793       return;
794 
795     /* All done... */
796     r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all active components have nominated candidate pairs",stream->pctx->label,stream->label);
797     nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED);
798 
799     /* Cancel our timer */
800     if(stream->timer){
801       NR_async_timer_cancel(stream->timer);
802       stream->timer=0;
803     }
804 
805     if (stream->pctx->handler && !stream->local_stream->obsolete) {
806       stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream);
807     }
808 
809     /* Now tell the peer_ctx that we're connected */
810     nr_ice_peer_ctx_check_if_connected(stream->pctx);
811   }
812 
nr_ice_media_stream_component_failed(nr_ice_media_stream * stream,nr_ice_component * component)813 void nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component)
814   {
815     component->state=NR_ICE_COMPONENT_FAILED;
816 
817     /* at least one component failed in this media stream, so the entire
818      * media stream is marked failed */
819 
820     nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_FAILED);
821 
822     nr_ice_media_stream_stop_checking(stream);
823 
824     if (stream->pctx->handler && !stream->local_stream->obsolete) {
825       stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream);
826     }
827 
828     /* Now tell the peer_ctx that we've failed */
829     nr_ice_peer_ctx_check_if_connected(stream->pctx);
830   }
831 
nr_ice_media_stream_get_best_candidate(nr_ice_media_stream * str,int component,nr_ice_candidate ** candp)832 int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp)
833   {
834     nr_ice_candidate *cand;
835     nr_ice_candidate *best_cand=0;
836     nr_ice_component *comp;
837     int r,_status;
838 
839     if(r=nr_ice_media_stream_find_component(str,component,&comp))
840       ABORT(r);
841 
842     cand=TAILQ_FIRST(&comp->candidates);
843     while(cand){
844       if(cand->state==NR_ICE_CAND_STATE_INITIALIZED){
845         if(!best_cand || (cand->priority>best_cand->priority))
846           best_cand=cand;
847 
848       }
849       cand=TAILQ_NEXT(cand,entry_comp);
850     }
851 
852     if(!best_cand)
853       ABORT(R_NOT_FOUND);
854 
855     *candp=best_cand;
856 
857     _status=0;
858   abort:
859     return(_status);
860   }
861 
862 
863 /* OK, we have the stream the user created, but that reflects the base
864    ICE ctx, not the peer_ctx. So, find the related stream in the pctx,
865    and then find the component */
nr_ice_media_stream_find_component(nr_ice_media_stream * str,int comp_id,nr_ice_component ** compp)866 int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp)
867   {
868     int _status;
869     nr_ice_component *comp;
870 
871     comp=STAILQ_FIRST(&str->components);
872     while(comp){
873       if(comp->component_id==comp_id)
874         break;
875 
876       comp=STAILQ_NEXT(comp,entry);
877     }
878     if(!comp)
879       ABORT(R_NOT_FOUND);
880 
881     *compp=comp;
882 
883     _status=0;
884   abort:
885     return(_status);
886   }
887 
nr_ice_media_stream_send(nr_ice_peer_ctx * pctx,nr_ice_media_stream * str,int component,UCHAR * data,int len)888 int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len)
889   {
890     int r,_status;
891     nr_ice_component *comp;
892 
893     /* First find the peer component */
894     if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp))
895       ABORT(r);
896 
897     /* Do we have an active pair yet? We should... */
898     if(!comp->active)
899       ABORT(R_NOT_FOUND);
900 
901     /* Does fresh ICE consent exist? */
902     if(!comp->can_send)
903       ABORT(R_FAILED);
904 
905     /* OK, write to that pair, which means:
906        1. Use the socket on our local side.
907        2. Use the address on the remote side
908     */
909     if(r=nr_socket_sendto(comp->active->local->osock,data,len,0,
910                           &comp->active->remote->addr)) {
911       if ((r==R_IO_ERROR) || (r==R_EOD)) {
912         nr_ice_component_disconnected(comp);
913       }
914       ABORT(r);
915     }
916 
917     // accumulate the sent bytes for the active candidate pair
918     comp->active->bytes_sent += len;
919     gettimeofday(&comp->active->last_sent, 0);
920 
921     _status=0;
922   abort:
923     return(_status);
924   }
925 
926 /* Returns R_REJECTED if the component is unpaired or has been disabled. */
nr_ice_media_stream_get_active(nr_ice_peer_ctx * pctx,nr_ice_media_stream * str,int component,nr_ice_candidate ** local,nr_ice_candidate ** remote)927 int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote)
928   {
929     int r,_status;
930     nr_ice_component *comp;
931 
932     /* First find the peer component */
933     if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp))
934       ABORT(r);
935 
936     if (comp->state == NR_ICE_COMPONENT_UNPAIRED ||
937         comp->state == NR_ICE_COMPONENT_DISABLED)
938       ABORT(R_REJECTED);
939 
940     if(!comp->active)
941       ABORT(R_NOT_FOUND);
942 
943     if (local) *local = comp->active->local;
944     if (remote) *remote = comp->active->remote;
945 
946     _status=0;
947   abort:
948     return(_status);
949   }
950 
nr_ice_media_stream_addrs(nr_ice_peer_ctx * pctx,nr_ice_media_stream * str,int component,nr_transport_addr * local,nr_transport_addr * remote)951 int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote)
952   {
953     int r,_status;
954     nr_ice_component *comp;
955 
956     /* First find the peer component */
957     if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp))
958       ABORT(r);
959 
960     /* Do we have an active pair yet? We should... */
961     if(!comp->active)
962       ABORT(R_BAD_ARGS);
963 
964     /* Use the socket on our local side */
965     if(r=nr_socket_getaddr(comp->active->local->osock,local))
966       ABORT(r);
967 
968     /* Use the address on the remote side */
969     if(r=nr_transport_addr_copy(remote,&comp->active->remote->addr))
970       ABORT(r);
971 
972     _status=0;
973   abort:
974     return(_status);
975   }
976 
977 
978 
nr_ice_media_stream_finalize(nr_ice_media_stream * lstr,nr_ice_media_stream * rstr)979 int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr)
980   {
981     nr_ice_component *lcomp,*rcomp;
982 
983     r_log(LOG_ICE,LOG_DEBUG,"Finalizing media stream %s, peer=%s",lstr->label,
984       rstr?rstr->label:"NONE");
985 
986     lcomp=STAILQ_FIRST(&lstr->components);
987     if(rstr)
988       rcomp=STAILQ_FIRST(&rstr->components);
989     else
990       rcomp=0;
991 
992     while(lcomp){
993       nr_ice_component_finalize(lcomp,rcomp);
994 
995       lcomp=STAILQ_NEXT(lcomp,entry);
996       if(rcomp){
997         rcomp=STAILQ_NEXT(rcomp,entry);
998       }
999     }
1000 
1001     return(0);
1002   }
1003 
nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx * pctx,nr_ice_media_stream * pstream,nr_ice_candidate * cand)1004 int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand)
1005   {
1006     int r,_status;
1007     nr_ice_component *comp;
1008 
1009     if ((r=nr_ice_media_stream_find_component(pstream, cand->component_id, &comp)))
1010       ABORT(R_NOT_FOUND);
1011 
1012     if (r=nr_ice_component_pair_candidate(pctx, comp, cand, 1))
1013       ABORT(r);
1014 
1015     _status=0;
1016   abort:
1017     return(_status);
1018   }
1019 
nr_ice_media_stream_get_consent_status(nr_ice_media_stream * stream,int component_id,int * can_send,struct timeval * ts)1020 int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int
1021 component_id, int *can_send, struct timeval *ts)
1022   {
1023     int r,_status;
1024     nr_ice_component *comp;
1025 
1026     if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp)))
1027       ABORT(r);
1028 
1029     *can_send = comp->can_send;
1030     ts->tv_sec = comp->consent_last_seen.tv_sec;
1031     ts->tv_usec = comp->consent_last_seen.tv_usec;
1032     _status=0;
1033   abort:
1034     return(_status);
1035   }
1036 
nr_ice_media_stream_disable_component(nr_ice_media_stream * stream,int component_id)1037 int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id)
1038   {
1039     int r,_status;
1040     nr_ice_component *comp;
1041 
1042     if (stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED)
1043       ABORT(R_FAILED);
1044 
1045     if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp)))
1046       ABORT(r);
1047 
1048     /* Can only disable before pairing */
1049     if (comp->state != NR_ICE_COMPONENT_UNPAIRED &&
1050         comp->state != NR_ICE_COMPONENT_DISABLED)
1051       ABORT(R_FAILED);
1052 
1053     comp->state = NR_ICE_COMPONENT_DISABLED;
1054 
1055     _status=0;
1056   abort:
1057     return(_status);
1058   }
1059 
nr_ice_media_stream_role_change(nr_ice_media_stream * stream)1060 void nr_ice_media_stream_role_change(nr_ice_media_stream *stream)
1061   {
1062     nr_ice_cand_pair *pair,*temp_pair;
1063     /* Changing role causes candidate pair priority to change, which requires
1064      * re-sorting the check list. */
1065     nr_ice_cand_pair_head old_checklist;
1066 
1067     /* Move check_list to old_checklist (not POD, have to do the hard way) */
1068     TAILQ_INIT(&old_checklist);
1069     TAILQ_FOREACH_SAFE(pair,&stream->check_list,check_queue_entry,temp_pair) {
1070       TAILQ_REMOVE(&stream->check_list,pair,check_queue_entry);
1071       TAILQ_INSERT_TAIL(&old_checklist,pair,check_queue_entry);
1072     }
1073 
1074     /* Re-insert into the check list */
1075     TAILQ_FOREACH_SAFE(pair,&old_checklist,check_queue_entry,temp_pair) {
1076       TAILQ_REMOVE(&old_checklist,pair,check_queue_entry);
1077       nr_ice_candidate_pair_role_change(pair);
1078       nr_ice_candidate_pair_insert(&stream->check_list,pair);
1079     }
1080   }
1081 
nr_ice_media_stream_find_pair(nr_ice_media_stream * str,nr_ice_candidate * lcand,nr_ice_candidate * rcand,nr_ice_cand_pair ** pair)1082 int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *lcand, nr_ice_candidate *rcand, nr_ice_cand_pair **pair)
1083   {
1084     nr_ice_cand_pair_head *head = &str->check_list;
1085     nr_ice_cand_pair *c1;
1086 
1087     c1=TAILQ_FIRST(head);
1088     while(c1){
1089       if(c1->local == lcand &&
1090          c1->remote == rcand) {
1091         *pair=c1;
1092         return(0);
1093       }
1094 
1095       c1=TAILQ_NEXT(c1,check_queue_entry);
1096     }
1097 
1098     return(R_NOT_FOUND);
1099   }
1100