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