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