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_peer_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
36
37 #include <string.h>
38 #include <assert.h>
39 #include <registry.h>
40 #include <nr_api.h>
41 #include "ice_ctx.h"
42 #include "ice_peer_ctx.h"
43 #include "ice_media_stream.h"
44 #include "ice_util.h"
45 #include "nr_crypto.h"
46 #include "async_timer.h"
47 #include "ice_reg.h"
48
49 static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
50 static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct);
51 static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate, int trickled);
52 static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx);
53
nr_ice_peer_ctx_create(nr_ice_ctx * ctx,nr_ice_handler * handler,char * label,nr_ice_peer_ctx ** pctxp)54 int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp)
55 {
56 int r,_status;
57 nr_ice_peer_ctx *pctx=0;
58
59 if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx))))
60 ABORT(R_NO_MEMORY);
61
62 pctx->state = NR_ICE_PEER_STATE_UNPAIRED;
63
64 if(!(pctx->label=r_strdup(label)))
65 ABORT(R_NO_MEMORY);
66
67 pctx->ctx=ctx;
68 pctx->handler=handler;
69
70 /* Decide controlling vs. controlled */
71 if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){
72 pctx->controlling=0;
73 } else {
74 pctx->controlling=1;
75 }
76 if(r=nr_crypto_random_bytes((UCHAR *)&pctx->tiebreaker,8))
77 ABORT(r);
78
79 STAILQ_INIT(&pctx->peer_streams);
80
81 STAILQ_INSERT_TAIL(&ctx->peers,pctx,entry);
82
83 *pctxp=pctx;
84
85 _status = 0;
86 abort:
87 if(_status){
88 nr_ice_peer_ctx_destroy_cb(0,0,pctx);
89 }
90 return(_status);
91 }
92
93
94
nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,char ** attrs,int attr_ct)95 int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct)
96 {
97 nr_ice_media_stream *pstream=0;
98 nr_ice_component *comp,*comp2;
99 char *lufrag,*rufrag;
100 char *lpwd,*rpwd;
101 int r,_status;
102
103 /*
104 Note: use component_ct from our own stream since components other
105 than this offered by the other side are unusable */
106 if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,stream->component_ct,&pstream))
107 ABORT(r);
108
109 /* Match up the local and remote components */
110 comp=STAILQ_FIRST(&stream->components);
111 comp2=STAILQ_FIRST(&pstream->components);
112 while(comp){
113 comp2->local_component=comp;
114
115 comp=STAILQ_NEXT(comp,entry);
116 comp2=STAILQ_NEXT(comp2,entry);
117 }
118
119 pstream->local_stream=stream;
120 pstream->pctx=pctx;
121
122 if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct))
123 ABORT(r);
124
125 /* Now that we have the ufrag and password, compute all the username/password
126 pairs */
127 lufrag=stream->ufrag?stream->ufrag:pctx->ctx->ufrag;
128 lpwd=stream->pwd?stream->pwd:pctx->ctx->pwd;
129 assert(lufrag);
130 assert(lpwd);
131 rufrag=pstream->ufrag?pstream->ufrag:pctx->peer_ufrag;
132 rpwd=pstream->pwd?pstream->pwd:pctx->peer_pwd;
133 if (!rufrag || !rpwd)
134 ABORT(R_BAD_DATA);
135
136 if(r=nr_concat_strings(&pstream->r2l_user,lufrag,":",rufrag,NULL))
137 ABORT(r);
138 if(r=nr_concat_strings(&pstream->l2r_user,rufrag,":",lufrag,NULL))
139 ABORT(r);
140 if(r=r_data_make(&pstream->r2l_pass, (UCHAR *)lpwd, strlen(lpwd)))
141 ABORT(r);
142 if(r=r_data_make(&pstream->l2r_pass, (UCHAR *)rpwd, strlen(rpwd)))
143 ABORT(r);
144
145 STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry);
146
147 _status=0;
148 abort:
149 return(_status);
150 }
151
nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,nr_ice_media_stream * pstream,char ** attrs,int attr_ct)152 static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct)
153 {
154 int r;
155 int i;
156
157 for(i=0;i<attr_ct;i++){
158 if(!strncmp(attrs[i],"ice-",4)){
159 if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) {
160 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label);
161 continue;
162 }
163 }
164 else if (!strncmp(attrs[i],"candidate",9)){
165 if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i],0)) {
166 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label);
167 continue;
168 }
169 }
170 else {
171 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus attribute: %s",pctx->ctx->label,pctx->label,attrs[i]);
172 }
173 }
174
175 /* Doesn't fail because we just skip errors */
176 return(0);
177 }
178
nr_ice_ctx_parse_candidate(nr_ice_peer_ctx * pctx,nr_ice_media_stream * pstream,char * candidate,int trickled)179 static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate, int trickled)
180 {
181 nr_ice_candidate *cand=0;
182 nr_ice_component *comp;
183 int j;
184 int r, _status;
185
186 if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand))
187 ABORT(r);
188 if(cand->component_id-1>=pstream->component_ct){
189 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label);
190 ABORT(R_BAD_DATA);
191 }
192
193 /* set the trickled flag on the candidate */
194 cand->trickled = trickled;
195
196 /* Not the fastest way to find a component, but it's what we got */
197 j=1;
198 for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){
199 if(j==cand->component_id)
200 break;
201
202 j++;
203 }
204
205 if(!comp){
206 r_log(LOG_ICE,LOG_WARNING,"Peer answered with more components than we offered");
207 ABORT(R_BAD_DATA);
208 }
209
210 if (comp->state == NR_ICE_COMPONENT_DISABLED) {
211 r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled remote component");
212 ABORT(R_BAD_DATA);
213 }
214 if (comp->local_component->state == NR_ICE_COMPONENT_DISABLED) {
215 r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled local component");
216 ABORT(R_BAD_DATA);
217 }
218
219 cand->component=comp;
220
221 TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp);
222
223 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): creating peer candidate",
224 pctx->label,cand->label);
225
226 _status=0;
227 abort:
228 if (_status) {
229 nr_ice_candidate_destroy(&cand);
230 }
231 return(_status);
232 }
233
nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,nr_ice_media_stream ** pstreamp)234 int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp)
235 {
236 int _status;
237 nr_ice_media_stream *pstream;
238
239 /* Because we don't have forward pointers, iterate through all the
240 peer streams to find one that matches us */
241 pstream=STAILQ_FIRST(&pctx->peer_streams);
242 while(pstream) {
243 if (pstream->local_stream == stream)
244 break;
245
246 pstream = STAILQ_NEXT(pstream, entry);
247 }
248 if (!pstream) {
249 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label);
250 ABORT(R_NOT_FOUND);
251 }
252
253 *pstreamp = pstream;
254
255 _status=0;
256 abort:
257 return(_status);
258 }
259
nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx * pctx,nr_ice_media_stream ** pstreamp)260 int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp)
261 {
262 int r,_status;
263
264 STAILQ_REMOVE(&pctx->peer_streams,*pstreamp,nr_ice_media_stream_,entry);
265
266 if(r=nr_ice_media_stream_destroy(pstreamp)) {
267 ABORT(r);
268 }
269
270 _status=0;
271 abort:
272 return(_status);
273 }
274
nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream,char * candidate)275 int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate)
276 {
277 nr_ice_media_stream *pstream;
278 int r,_status;
279 int needs_pairing = 0;
280
281 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) parsing trickle ICE candidate %s",pctx->ctx->label,pctx->label,candidate);
282 r = nr_ice_peer_ctx_find_pstream(pctx, stream, &pstream);
283 if (r)
284 ABORT(r);
285
286 switch(pstream->ice_state) {
287 case NR_ICE_MEDIA_STREAM_UNPAIRED:
288 break;
289 case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN:
290 case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE:
291 needs_pairing = 1;
292 break;
293 default:
294 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state);
295 ABORT(R_ALREADY);
296 break;
297 }
298
299 if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate,1)){
300 ABORT(r);
301 }
302
303 /* If ICE is running (i.e., we are in FROZEN or ACTIVE states)
304 then we need to pair this new candidate. For now we
305 just re-pair the stream which is inefficient but still
306 fine because we suppress duplicate pairing */
307 if (needs_pairing) {
308 if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) {
309 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label);
310 ABORT(r);
311 }
312
313 /* Start the remote trickle grace timeout if it hasn't been started by
314 another trickled candidate or from the SDP. */
315 if (!pctx->trickle_grace_period_timer) {
316 nr_ice_peer_ctx_start_trickle_timer(pctx);
317 }
318
319 /* Start checks if this stream is not checking yet or if it has checked
320 all the available candidates but not had a completed check for all
321 components.
322
323 Note that this is not compliant with RFC 5245, but consistent with
324 the libjingle trickle ICE behavior. Note that we will not restart
325 checks if either (a) the stream has failed or (b) all components
326 have a successful pair because the switch statement above jumps
327 will in both states.
328
329 TODO(ekr@rtfm.com): restart checks.
330 TODO(ekr@rtfm.com): update when the trickle ICE RFC is published
331 */
332 if (!pstream->timer) {
333 if(r=nr_ice_media_stream_start_checks(pctx, pstream)) {
334 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to start checks",pctx->ctx->label,pctx->label,stream->label);
335 ABORT(r);
336 }
337 }
338 }
339
340 _status=0;
341 abort:
342 return(_status);
343
344 }
345
346
nr_ice_peer_ctx_trickle_wait_cb(NR_SOCKET s,int how,void * cb_arg)347 static void nr_ice_peer_ctx_trickle_wait_cb(NR_SOCKET s, int how, void *cb_arg)
348 {
349 nr_ice_peer_ctx *pctx=cb_arg;
350 nr_ice_media_stream *stream;
351 nr_ice_component *comp;
352
353 pctx->trickle_grace_period_timer=0;
354
355 r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) Trickle grace period is over; marking every component with only failed pairs as failed.",pctx->ctx->label,pctx->label);
356
357 stream=STAILQ_FIRST(&pctx->peer_streams);
358 while(stream){
359 comp=STAILQ_FIRST(&stream->components);
360 while(comp){
361 nr_ice_component_check_if_failed(comp);
362 comp=STAILQ_NEXT(comp,entry);
363 }
364 stream=STAILQ_NEXT(stream,entry);
365 }
366 }
367
nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx * pctx)368 static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx)
369 {
370 UINT4 grace_period_timeout=0;
371
372 if(pctx->trickle_grace_period_timer) {
373 NR_async_timer_cancel(pctx->trickle_grace_period_timer);
374 pctx->trickle_grace_period_timer=0;
375 }
376
377 NR_reg_get_uint4(NR_ICE_REG_TRICKLE_GRACE_PERIOD,&grace_period_timeout);
378
379 if (grace_period_timeout) {
380 /* If we're doing trickle, we need to allow a grace period for new
381 * trickle candidates to arrive in case the pairs we have fail quickly. */
382 NR_ASYNC_TIMER_SET(grace_period_timeout,nr_ice_peer_ctx_trickle_wait_cb,pctx,&pctx->trickle_grace_period_timer);
383 }
384 }
385
nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx * pctx)386 int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx)
387 {
388 nr_ice_media_stream *stream;
389 int r,_status;
390
391 if(pctx->peer_lite && !pctx->controlling) {
392 if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_LITE){
393 r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite");
394 ABORT(R_BAD_DATA);
395 }
396 nr_ice_peer_ctx_switch_controlling_role(pctx);
397 }
398
399 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) pairing candidates",pctx->ctx->label,pctx->label);
400
401 if(STAILQ_EMPTY(&pctx->peer_streams)) {
402 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attributes",pctx->ctx->label,pctx->label);
403 ABORT(R_FAILED);
404 }
405
406 /* Set this first; if we fail partway through, we do not want to end
407 * up in UNPAIRED after creating some pairs. */
408 pctx->state = NR_ICE_PEER_STATE_PAIRED;
409
410 stream=STAILQ_FIRST(&pctx->peer_streams);
411 while(stream){
412 if(r=nr_ice_media_stream_pair_candidates(pctx, stream->local_stream,
413 stream))
414 ABORT(r);
415
416 stream=STAILQ_NEXT(stream,entry);
417 }
418
419
420 _status=0;
421 abort:
422 return(_status);
423 }
424
425
nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx * ctx,nr_ice_peer_ctx * pctx,nr_ice_candidate * cand)426 int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand)
427 {
428 int r, _status;
429 nr_ice_media_stream *pstream;
430
431 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) pairing local trickle ICE candidate %s",pctx->ctx->label,pctx->label,cand->label);
432 if ((r = nr_ice_peer_ctx_find_pstream(pctx, cand->stream, &pstream)))
433 ABORT(r);
434
435 if ((r = nr_ice_media_stream_pair_new_trickle_candidate(pctx, pstream, cand)))
436 ABORT(r);
437
438 /* Start the remote trickle grace timeout if it hasn't been started
439 already. */
440 if (!pctx->trickle_grace_period_timer) {
441 nr_ice_peer_ctx_start_trickle_timer(pctx);
442 }
443
444 _status=0;
445 abort:
446 return _status;
447 }
448
nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx * pctx,nr_ice_media_stream * lstream,int component_id)449 int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id)
450 {
451 int r, _status;
452 nr_ice_media_stream *pstream;
453 nr_ice_component *component;
454
455 if ((r=nr_ice_peer_ctx_find_pstream(pctx, lstream, &pstream)))
456 ABORT(r);
457
458 /* We shouldn't be calling this after we have started pairing */
459 if (pstream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED)
460 ABORT(R_FAILED);
461
462 if ((r=nr_ice_media_stream_find_component(pstream, component_id,
463 &component)))
464 ABORT(r);
465
466 component->state = NR_ICE_COMPONENT_DISABLED;
467
468 _status=0;
469 abort:
470 return(_status);
471 }
472
nr_ice_peer_ctx_destroy_cb(NR_SOCKET s,int how,void * cb_arg)473 static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
474 {
475 nr_ice_peer_ctx *pctx=cb_arg;
476 nr_ice_media_stream *str1,*str2;
477
478 NR_async_timer_cancel(pctx->connected_cb_timer);
479 RFREE(pctx->label);
480 RFREE(pctx->peer_ufrag);
481 RFREE(pctx->peer_pwd);
482
483 STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){
484 STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry);
485 nr_ice_media_stream_destroy(&str1);
486 }
487 assert(pctx->ctx);
488 if (pctx->ctx)
489 STAILQ_REMOVE(&pctx->ctx->peers, pctx, nr_ice_peer_ctx_, entry);
490
491 if(pctx->trickle_grace_period_timer) {
492 NR_async_timer_cancel(pctx->trickle_grace_period_timer);
493 pctx->trickle_grace_period_timer=0;
494 }
495
496 RFREE(pctx);
497 }
498
nr_ice_peer_ctx_destroy(nr_ice_peer_ctx ** pctxp)499 int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp)
500 {
501
502 if(!pctxp || !*pctxp)
503 return(0);
504
505 /* Stop calling the handler */
506 (*pctxp)->handler = 0;
507
508 NR_ASYNC_SCHEDULE(nr_ice_peer_ctx_destroy_cb,*pctxp);
509
510 *pctxp=0;
511
512 return(0);
513 }
514
515
516 /* Start the checks for the first media stream (S 5.7)
517 The rest remain FROZEN */
nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx * pctx)518 int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx)
519 {
520 return nr_ice_peer_ctx_start_checks2(pctx, 0);
521 }
522
523 /* Start checks for some media stream.
524
525 If allow_non_first == 0, then we only look at the first stream,
526 which is 5245-complaint.
527
528 If allow_non_first == 1 then we find the first non-empty stream
529 This is not compliant with RFC 5245 but is necessary to make trickle ICE
530 work plausibly
531 */
nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx * pctx,int allow_non_first)532 int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
533 {
534 int r,_status;
535 nr_ice_media_stream *stream;
536 int started = 0;
537
538 /* Might have added some streams */
539 pctx->reported_connected = 0;
540 NR_async_timer_cancel(pctx->connected_cb_timer);
541 pctx->connected_cb_timer = 0;
542 pctx->checks_started = 0;
543
544 if((r=nr_ice_peer_ctx_check_if_connected(pctx))) {
545 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial connected check failed",pctx->ctx->label,pctx->label);
546 ABORT(r);
547 }
548
549 if (pctx->reported_connected) {
550 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) in %s all streams were done",pctx->ctx->label,pctx->label,__FUNCTION__);
551 return (0);
552 }
553
554 stream=STAILQ_FIRST(&pctx->peer_streams);
555 if(!stream)
556 ABORT(R_FAILED);
557
558 while (stream) {
559 assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED);
560
561 if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
562 if(!TAILQ_EMPTY(&stream->check_list))
563 break;
564
565 if(!allow_non_first){
566 /* This test applies if:
567
568 1. allow_non_first is 0 (i.e., non-trickle ICE)
569 2. the first stream has an empty check list.
570
571 But in the non-trickle ICE case, the other side should have provided
572 some candidates or ICE is pretty much not going to work and we're
573 just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and
574 immediate termination here.
575 */
576 r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label);
577 ABORT(R_FAILED);
578 }
579 }
580
581 stream=STAILQ_NEXT(stream, entry);
582 }
583
584 if (!stream) {
585 /*
586 We fail above if we aren't doing trickle, and this is not all that
587 unusual in the trickle case.
588 */
589 r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label);
590 }
591 else if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
592 if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream))
593 ABORT(r);
594 if(r=nr_ice_media_stream_start_checks(pctx,stream))
595 ABORT(r);
596 ++started;
597 }
598
599 stream=STAILQ_FIRST(&pctx->peer_streams);
600 while (stream) {
601 int serviced = 0;
602 if (r=nr_ice_media_stream_service_pre_answer_requests(pctx, stream->local_stream, stream, &serviced))
603 ABORT(r);
604
605 if (serviced) {
606 ++started;
607 }
608 else {
609 r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with pre-answer requests",pctx->ctx->label,pctx->label);
610 }
611
612
613 stream=STAILQ_NEXT(stream, entry);
614 }
615
616 if (!started) {
617 r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no checks to start",pctx->ctx->label,pctx->label);
618 ABORT(R_NOT_FOUND);
619 }
620 else {
621 /* Start grace period timer for more remote trickle candidates. */
622 nr_ice_peer_ctx_start_trickle_timer(pctx);
623 }
624
625 _status=0;
626 abort:
627 return(_status);
628 }
629
nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx * pctx,nr_ice_media_stream * stream)630 void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
631 {
632 if (!pctx->checks_started) {
633 r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) is now checking",pctx->ctx->label,pctx->label);
634 pctx->checks_started = 1;
635 if (pctx->handler && pctx->handler->vtbl->ice_checking) {
636 pctx->handler->vtbl->ice_checking(pctx->handler->obj, pctx);
637 }
638 }
639 }
640
641 #ifndef NDEBUG
nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx * pctx,FILE * out)642 int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out)
643 {
644 int r,_status;
645 nr_ice_media_stream *stream;
646
647 fprintf(out,"PEER %s STATE DUMP\n",pctx->label);
648 fprintf(out,"==========================================\n");
649 stream=STAILQ_FIRST(&pctx->peer_streams);
650 while(stream){
651 if(r=nr_ice_media_stream_dump_state(pctx,stream,out))
652 ABORT(r);
653
654 stream=STAILQ_NEXT(stream,entry);
655 }
656 fprintf(out,"==========================================\n");
657
658 _status=0;
659 abort:
660 return(_status);
661 }
662 #endif
663
nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx * pctx)664 void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx)
665 {
666 nr_ice_media_stream *str;
667
668 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): refreshing consent on all streams",pctx->label);
669
670 str=STAILQ_FIRST(&pctx->peer_streams);
671 while(str) {
672 nr_ice_media_stream_refresh_consent_all(str);
673 str=STAILQ_NEXT(str,entry);
674 }
675 }
676
nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx * pctx)677 void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx)
678 {
679 if (pctx->reported_connected &&
680 pctx->handler &&
681 pctx->handler->vtbl->ice_disconnected) {
682 pctx->handler->vtbl->ice_disconnected(pctx->handler->obj, pctx);
683
684 pctx->reported_connected = 0;
685 }
686 }
687
nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx * pctx)688 void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx)
689 {
690 nr_ice_media_stream *str;
691
692 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): disconnecting all streams",pctx->label);
693
694 str=STAILQ_FIRST(&pctx->peer_streams);
695 while(str) {
696 nr_ice_media_stream_disconnect_all_components(str);
697
698 /* The first stream to be disconnected will cause the peer ctx to signal
699 the disconnect up. */
700 nr_ice_media_stream_set_disconnected(str, NR_ICE_MEDIA_STREAM_DISCONNECTED);
701
702 str=STAILQ_NEXT(str,entry);
703 }
704 }
705
nr_ice_peer_ctx_connected(nr_ice_peer_ctx * pctx)706 void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx)
707 {
708 /* Fire the handler callback to say we're done */
709 if (pctx->reported_connected &&
710 pctx->handler &&
711 pctx->handler->vtbl->ice_connected) {
712 pctx->handler->vtbl->ice_connected(pctx->handler->obj, pctx);
713 }
714 }
715
nr_ice_peer_ctx_fire_connected(NR_SOCKET s,int how,void * cb_arg)716 static void nr_ice_peer_ctx_fire_connected(NR_SOCKET s, int how, void *cb_arg)
717 {
718 nr_ice_peer_ctx *pctx=cb_arg;
719
720 pctx->connected_cb_timer=0;
721
722 nr_ice_peer_ctx_connected(pctx);
723 }
724
725 /* Examine all the streams to see if we're
726 maybe miraculously connected */
nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx * pctx)727 int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx)
728 {
729 int _status;
730 nr_ice_media_stream *str;
731 int failed=0;
732 int succeeded=0;
733
734 str=STAILQ_FIRST(&pctx->peer_streams);
735 while(str){
736 if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED){
737 succeeded++;
738 }
739 else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){
740 failed++;
741 }
742 else{
743 break;
744 }
745 str=STAILQ_NEXT(str,entry);
746 }
747
748 if(str)
749 goto done; /* Something isn't done */
750
751 /* OK, we're finished, one way or another */
752 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): all checks completed success=%d fail=%d",pctx->label,succeeded,failed);
753
754 /* Schedule a connected notification for the first connected event.
755 IMPORTANT: This is done in a callback because we expect destructors
756 of various kinds to be fired from here */
757 if (!pctx->reported_connected) {
758 pctx->reported_connected = 1;
759 assert(!pctx->connected_cb_timer);
760 NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_connected,pctx,&pctx->connected_cb_timer);
761 }
762
763 done:
764 _status=0;
765 return(_status);
766 }
767
768
769 /* Given a component in the main ICE ctx, find the relevant component in
770 the peer_ctx */
nr_ice_peer_ctx_find_component(nr_ice_peer_ctx * pctx,nr_ice_media_stream * str,int component_id,nr_ice_component ** compp)771 int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp)
772 {
773 nr_ice_media_stream *pstr;
774 int r,_status;
775
776 pstr=STAILQ_FIRST(&pctx->peer_streams);
777 while(pstr){
778 if(pstr->local_stream==str)
779 break;
780
781 pstr=STAILQ_NEXT(pstr,entry);
782 }
783 if(!pstr)
784 ABORT(R_BAD_ARGS);
785
786 if(r=nr_ice_media_stream_find_component(pstr,component_id,compp))
787 ABORT(r);
788
789 _status=0;
790 abort:
791 return(_status);
792 }
793
794 /*
795 This packet may be for us.
796
797 1. Find the matching peer component
798 2. Examine the packet source address to see if it matches
799 one of the peer candidates.
800 3. Fire the relevant callback handler if there is a match
801
802 Return 0 if match, R_REJECTED if no match, other errors
803 if we can't even find the component or something like that.
804 */
805
nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx * pctx,nr_ice_component * comp,nr_transport_addr * source_addr,UCHAR * data,int len)806 int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len)
807 {
808 nr_ice_component *peer_comp;
809 nr_ice_candidate *cand;
810 int r,_status;
811
812 if(r=nr_ice_peer_ctx_find_component(pctx, comp->stream, comp->component_id,
813 &peer_comp))
814 ABORT(r);
815
816 /* OK, we've found the component, now look for matches */
817 cand=TAILQ_FIRST(&peer_comp->candidates);
818 while(cand){
819 if(!nr_transport_addr_cmp(source_addr,&cand->addr,
820 NR_TRANSPORT_ADDR_CMP_MODE_ALL))
821 break;
822
823 cand=TAILQ_NEXT(cand,entry_comp);
824 }
825
826 if(!cand)
827 ABORT(R_REJECTED);
828
829 // accumulate the received bytes for the active candidate pair
830 if (peer_comp->active) {
831 peer_comp->active->bytes_recvd += len;
832 gettimeofday(&peer_comp->active->last_recvd, 0);
833 }
834
835 /* OK, there's a match. Call the handler */
836
837 if (pctx->handler) {
838 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label);
839
840 pctx->handler->vtbl->msg_recvd(pctx->handler->obj,
841 pctx,comp->stream,comp->component_id,data,len);
842 }
843
844 _status=0;
845 abort:
846 return(_status);
847 }
848
nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx * pctx)849 void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx)
850 {
851 int controlling = !(pctx->controlling);
852 if(pctx->controlling_conflict_resolved) {
853 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) %s called more than once; "
854 "this probably means the peer is confused. Not switching roles.",
855 pctx->ctx->label,pctx->label,__FUNCTION__);
856 return;
857 }
858
859 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): detected "
860 "role conflict. Switching to %s",
861 pctx->label,
862 controlling ? "controlling" : "controlled");
863
864 pctx->controlling = controlling;
865 pctx->controlling_conflict_resolved = 1;
866
867 if(pctx->state == NR_ICE_PEER_STATE_PAIRED) {
868 /* We have formed candidate pairs. We need to inform them. */
869 nr_ice_media_stream *pstream=STAILQ_FIRST(&pctx->peer_streams);
870 while(pstream) {
871 nr_ice_media_stream_role_change(pstream);
872 pstream = STAILQ_NEXT(pstream, entry);
873 }
874 }
875 }
876
877