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