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