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