|
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_component.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 <registry.h> |
|
41 #include <async_timer.h> |
|
42 #include "ice_ctx.h" |
|
43 #include "ice_codeword.h" |
|
44 #include "stun.h" |
|
45 #include "nr_socket_local.h" |
|
46 #include "nr_socket_turn.h" |
|
47 #include "nr_socket_buffered_stun.h" |
|
48 #include "ice_reg.h" |
|
49 |
|
50 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error); |
|
51 static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp); |
|
52 |
|
53 /* This function takes ownership of the contents of req (but not req itself) */ |
|
54 static int nr_ice_pre_answer_request_create(nr_socket *sock, nr_stun_server_request *req, nr_ice_pre_answer_request **parp) |
|
55 { |
|
56 int r, _status; |
|
57 nr_ice_pre_answer_request *par = 0; |
|
58 nr_stun_message_attribute *attr; |
|
59 |
|
60 if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request)))) |
|
61 ABORT(R_NO_MEMORY); |
|
62 |
|
63 par->req = *req; /* Struct assignment */ |
|
64 memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */ |
|
65 |
|
66 if (r=nr_socket_getaddr(sock, &par->local_addr)) |
|
67 ABORT(r); |
|
68 if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr)) |
|
69 ABORT(R_INTERNAL); |
|
70 if (!(par->username = r_strdup(attr->u.username))) |
|
71 ABORT(R_NO_MEMORY); |
|
72 |
|
73 *parp=par; |
|
74 _status=0; |
|
75 abort: |
|
76 if (_status) { |
|
77 /* Erase the request so we don't free it */ |
|
78 memset(&par->req, 0, sizeof(nr_stun_server_request)); |
|
79 nr_ice_pre_answer_request_destroy(&par); |
|
80 } |
|
81 |
|
82 return(_status); |
|
83 } |
|
84 |
|
85 static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp) |
|
86 { |
|
87 nr_ice_pre_answer_request *par; |
|
88 |
|
89 if (!parp || !*parp) |
|
90 return(0); |
|
91 |
|
92 par = *parp; |
|
93 *parp = 0; |
|
94 |
|
95 nr_stun_message_destroy(&par->req.request); |
|
96 nr_stun_message_destroy(&par->req.response); |
|
97 |
|
98 RFREE(par->username); |
|
99 |
|
100 return(0); |
|
101 } |
|
102 |
|
103 int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp) |
|
104 { |
|
105 int _status; |
|
106 nr_ice_component *comp=0; |
|
107 |
|
108 if(!(comp=RCALLOC(sizeof(nr_ice_component)))) |
|
109 ABORT(R_NO_MEMORY); |
|
110 |
|
111 comp->state=NR_ICE_COMPONENT_UNPAIRED; |
|
112 comp->component_id=component_id; |
|
113 comp->stream=stream; |
|
114 comp->ctx=stream->ctx; |
|
115 |
|
116 STAILQ_INIT(&comp->sockets); |
|
117 TAILQ_INIT(&comp->candidates); |
|
118 STAILQ_INIT(&comp->pre_answer_reqs); |
|
119 |
|
120 STAILQ_INSERT_TAIL(&stream->components,comp,entry); |
|
121 |
|
122 _status=0; |
|
123 abort: |
|
124 return(_status); |
|
125 } |
|
126 |
|
127 int nr_ice_component_destroy(nr_ice_component **componentp) |
|
128 { |
|
129 nr_ice_component *component; |
|
130 nr_ice_socket *s1,*s2; |
|
131 nr_ice_candidate *c1,*c2; |
|
132 nr_ice_pre_answer_request *r1,*r2; |
|
133 |
|
134 if(!componentp || !*componentp) |
|
135 return(0); |
|
136 |
|
137 component=*componentp; |
|
138 *componentp=0; |
|
139 |
|
140 /* Detach ourselves from the sockets */ |
|
141 if (component->local_component){ |
|
142 nr_ice_socket *isock=STAILQ_FIRST(&component->local_component->sockets); |
|
143 while(isock){ |
|
144 nr_stun_server_remove_client(isock->stun_server, component); |
|
145 isock=STAILQ_NEXT(isock, entry); |
|
146 } |
|
147 } |
|
148 |
|
149 /* candidates MUST be destroyed before the sockets so that |
|
150 they can deregister */ |
|
151 TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){ |
|
152 TAILQ_REMOVE(&component->candidates,c1,entry_comp); |
|
153 nr_ice_candidate_destroy(&c1); |
|
154 } |
|
155 |
|
156 STAILQ_FOREACH_SAFE(s1, &component->sockets, entry, s2){ |
|
157 STAILQ_REMOVE(&component->sockets,s1,nr_ice_socket_,entry); |
|
158 nr_ice_socket_destroy(&s1); |
|
159 } |
|
160 |
|
161 STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){ |
|
162 STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); |
|
163 nr_ice_pre_answer_request_destroy(&r1); |
|
164 } |
|
165 |
|
166 if(component->keepalive_timer) |
|
167 NR_async_timer_cancel(component->keepalive_timer); |
|
168 nr_stun_client_ctx_destroy(&component->keepalive_ctx); |
|
169 |
|
170 RFREE(component); |
|
171 return(0); |
|
172 } |
|
173 |
|
174 static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd) |
|
175 { |
|
176 nr_socket *sock; |
|
177 nr_ice_socket *isock=0; |
|
178 nr_ice_candidate *cand=0; |
|
179 int i; |
|
180 int j; |
|
181 char label[256]; |
|
182 int r,_status; |
|
183 |
|
184 /* Now one ice_socket for each address */ |
|
185 for(i=0;i<addr_ct;i++){ |
|
186 char suppress; |
|
187 |
|
188 if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){ |
|
189 if(r!=R_NOT_FOUND) |
|
190 ABORT(r); |
|
191 } |
|
192 else{ |
|
193 if(suppress) |
|
194 continue; |
|
195 } |
|
196 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string); |
|
197 if(r=nr_socket_local_create(&addrs[i].addr,&sock)){ |
|
198 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string); |
|
199 continue; |
|
200 } |
|
201 |
|
202 if(r=nr_ice_socket_create(ctx,component,sock,&isock)) |
|
203 ABORT(r); |
|
204 /* Create one host candidate */ |
|
205 if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0, |
|
206 component->component_id,&cand)) |
|
207 ABORT(r); |
|
208 |
|
209 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); |
|
210 component->candidate_ct++; |
|
211 cand=0; |
|
212 |
|
213 /* And a srvrflx candidate for each STUN server */ |
|
214 for(j=0;j<ctx->stun_server_ct;j++){ |
|
215 if(r=nr_ice_candidate_create(ctx,component, |
|
216 isock,sock,SERVER_REFLEXIVE, |
|
217 &ctx->stun_servers[j],component->component_id,&cand)) |
|
218 ABORT(r); |
|
219 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); |
|
220 component->candidate_ct++; |
|
221 cand=0; |
|
222 } |
|
223 |
|
224 #ifdef USE_TURN |
|
225 /* And both a srvrflx and relayed candidate for each TURN server */ |
|
226 for(j=0;j<ctx->turn_server_ct;j++){ |
|
227 nr_socket *turn_sock; |
|
228 nr_ice_candidate *srvflx_cand; |
|
229 |
|
230 /* Skip non-UDP */ |
|
231 if (ctx->turn_servers[j].transport != IPPROTO_UDP) |
|
232 continue; |
|
233 |
|
234 /* srvrflx */ |
|
235 if(r=nr_ice_candidate_create(ctx,component, |
|
236 isock,sock,SERVER_REFLEXIVE, |
|
237 &ctx->turn_servers[j].turn_server,component->component_id,&cand)) |
|
238 ABORT(r); |
|
239 cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */ |
|
240 cand->done_cb=nr_ice_initialize_finished_cb; |
|
241 cand->cb_arg=cand; |
|
242 |
|
243 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); |
|
244 component->candidate_ct++; |
|
245 srvflx_cand=cand; |
|
246 |
|
247 /* relayed*/ |
|
248 if(r=nr_socket_turn_create(sock, &turn_sock)) |
|
249 ABORT(r); |
|
250 if(r=nr_ice_candidate_create(ctx,component, |
|
251 isock,turn_sock,RELAYED, |
|
252 &ctx->turn_servers[j].turn_server,component->component_id,&cand)) |
|
253 ABORT(r); |
|
254 cand->u.relayed.srvflx_candidate=srvflx_cand; |
|
255 cand->u.relayed.server=&ctx->turn_servers[j]; |
|
256 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); |
|
257 component->candidate_ct++; |
|
258 |
|
259 cand=0; |
|
260 } |
|
261 #endif /* USE_TURN */ |
|
262 |
|
263 /* Create a STUN server context for this socket */ |
|
264 snprintf(label, sizeof(label), "server(%s)", addrs[i].addr.as_string); |
|
265 if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server)) |
|
266 ABORT(r); |
|
267 if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle)) |
|
268 ABORT(r); |
|
269 |
|
270 /* Add the default STUN credentials so that we can respond before |
|
271 we hear about the peer. */ |
|
272 if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component)) |
|
273 ABORT(r); |
|
274 |
|
275 STAILQ_INSERT_TAIL(&component->sockets,isock,entry); |
|
276 } |
|
277 |
|
278 _status = 0; |
|
279 abort: |
|
280 return(_status); |
|
281 } |
|
282 |
|
283 static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd) |
|
284 { |
|
285 nr_ice_socket *isock=0; |
|
286 nr_ice_candidate *cand=0; |
|
287 int i; |
|
288 int j; |
|
289 char label[256]; |
|
290 int r,_status; |
|
291 |
|
292 /* Create a new relayed candidate for each addr/TURN server pair */ |
|
293 for(i=0;i<addr_ct;i++){ |
|
294 char suppress; |
|
295 |
|
296 if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){ |
|
297 if(r!=R_NOT_FOUND) |
|
298 ABORT(r); |
|
299 } |
|
300 else{ |
|
301 if(suppress) |
|
302 continue; |
|
303 } |
|
304 |
|
305 #ifdef USE_TURN |
|
306 for(j=0;j<ctx->turn_server_ct;j++){ |
|
307 nr_transport_addr addr; |
|
308 nr_socket *sock; |
|
309 nr_socket *buffered_sock; |
|
310 nr_socket *turn_sock; |
|
311 |
|
312 /* Skip non-TCP */ |
|
313 if (ctx->turn_servers[j].transport != IPPROTO_TCP) |
|
314 continue; |
|
315 |
|
316 /* Create a local socket */ |
|
317 if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr))) |
|
318 ABORT(r); |
|
319 addr.protocol = IPPROTO_TCP; |
|
320 if ((r=nr_transport_addr_fmt_addr_string(&addr))) |
|
321 ABORT(r); |
|
322 if((r=nr_socket_local_create(&addr, &sock))){ |
|
323 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string); |
|
324 continue; |
|
325 } |
|
326 /* Wrap it */ |
|
327 if((r=nr_socket_buffered_stun_create(sock, NR_STUN_MAX_MESSAGE_SIZE, &buffered_sock))) |
|
328 ABORT(r); |
|
329 |
|
330 /* The TURN socket */ |
|
331 if(r=nr_socket_turn_create(buffered_sock, &turn_sock)) |
|
332 ABORT(r); |
|
333 |
|
334 /* Create an ICE socket */ |
|
335 if((r=nr_ice_socket_create(ctx, component, buffered_sock, &isock))) |
|
336 ABORT(r); |
|
337 |
|
338 /* Attach ourselves to it */ |
|
339 if(r=nr_ice_candidate_create(ctx,component, |
|
340 isock,turn_sock,RELAYED, |
|
341 &ctx->turn_servers[j].turn_server,component->component_id,&cand)) |
|
342 ABORT(r); |
|
343 cand->u.relayed.srvflx_candidate=NULL; |
|
344 cand->u.relayed.server=&ctx->turn_servers[j]; |
|
345 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); |
|
346 component->candidate_ct++; |
|
347 cand=0; |
|
348 |
|
349 /* Create a STUN server context for this socket */ |
|
350 snprintf(label, sizeof(label), "server(%s)", addr.as_string); |
|
351 if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server)) |
|
352 ABORT(r); |
|
353 if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle)) |
|
354 ABORT(r); |
|
355 |
|
356 /* Add the default STUN credentials so that we can respond before |
|
357 we hear about the peer.*/ |
|
358 if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component)) |
|
359 ABORT(r); |
|
360 |
|
361 STAILQ_INSERT_TAIL(&component->sockets,isock,entry); |
|
362 } |
|
363 } |
|
364 #endif |
|
365 |
|
366 _status = 0; |
|
367 abort: |
|
368 return(_status); |
|
369 } |
|
370 |
|
371 |
|
372 /* Make all the candidates we can make at the beginning */ |
|
373 int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component) |
|
374 { |
|
375 int r,_status; |
|
376 nr_local_addr *addrs=ctx->local_addrs; |
|
377 int addr_ct=ctx->local_addr_ct; |
|
378 char *lufrag; |
|
379 char *lpwd; |
|
380 Data pwd; |
|
381 nr_ice_candidate *cand; |
|
382 |
|
383 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id); |
|
384 |
|
385 if(addr_ct==0){ |
|
386 r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label); |
|
387 ABORT(R_NOT_FOUND); |
|
388 } |
|
389 |
|
390 /* Note: we need to recompute these because |
|
391 we have not yet computed the values in the peer media stream.*/ |
|
392 lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag; |
|
393 assert(lufrag); |
|
394 if (!lufrag) |
|
395 ABORT(R_INTERNAL); |
|
396 lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd; |
|
397 assert(lpwd); |
|
398 if (!lpwd) |
|
399 ABORT(R_INTERNAL); |
|
400 INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd)); |
|
401 |
|
402 /* Initialize the UDP candidates */ |
|
403 if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd)) |
|
404 ABORT(r); |
|
405 /* And the TCP candidates */ |
|
406 if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd)) |
|
407 ABORT(r); |
|
408 |
|
409 /* count the candidates that will be initialized */ |
|
410 cand=TAILQ_FIRST(&component->candidates); |
|
411 if(!cand){ |
|
412 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create any valid candidates",ctx->label); |
|
413 ABORT(R_NOT_FOUND); |
|
414 } |
|
415 |
|
416 while(cand){ |
|
417 ctx->uninitialized_candidates++; |
|
418 cand=TAILQ_NEXT(cand,entry_comp); |
|
419 } |
|
420 |
|
421 /* Now initialize all the candidates */ |
|
422 cand=TAILQ_FIRST(&component->candidates); |
|
423 while(cand){ |
|
424 if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){ |
|
425 if(r=nr_ice_candidate_initialize(cand,nr_ice_initialize_finished_cb,cand)){ |
|
426 if(r!=R_WOULDBLOCK){ |
|
427 ctx->uninitialized_candidates--; |
|
428 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
429 } |
|
430 } |
|
431 } |
|
432 cand=TAILQ_NEXT(cand,entry_comp); |
|
433 } |
|
434 _status=0; |
|
435 abort: |
|
436 return(_status); |
|
437 } |
|
438 |
|
439 static int nr_ice_any_peer_paired(nr_ice_candidate* cand) { |
|
440 nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers); |
|
441 while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){ |
|
442 /* Is it worth actually looking through the check lists? Probably not. */ |
|
443 pctx=STAILQ_NEXT(pctx,entry); |
|
444 } |
|
445 return pctx != NULL; |
|
446 } |
|
447 |
|
448 /* |
|
449 Compare this newly initialized candidate against the other initialized |
|
450 candidates and discard the lower-priority one if they are redundant. |
|
451 |
|
452 This algorithm combined with the other algorithms, favors |
|
453 host > srflx > relay |
|
454 */ |
|
455 int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned) |
|
456 { |
|
457 nr_ice_candidate *c2, *tmp = NULL; |
|
458 |
|
459 *was_pruned = 0; |
|
460 c2 = TAILQ_FIRST(&comp->candidates); |
|
461 while(c2){ |
|
462 if((c1 != c2) && |
|
463 (c2->state == NR_ICE_CAND_STATE_INITIALIZED) && |
|
464 !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) && |
|
465 !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ |
|
466 |
|
467 if((c1->type == c2->type) || |
|
468 (c1->type==HOST && c2->type == SERVER_REFLEXIVE) || |
|
469 (c2->type==HOST && c1->type == SERVER_REFLEXIVE)){ |
|
470 |
|
471 /* |
|
472 These are redundant. Remove the lower pri one, or if pairing has |
|
473 already occurred, remove the newest one. |
|
474 |
|
475 Since this algorithmis run whenever a new candidate |
|
476 is initialized, there should at most one duplicate. |
|
477 */ |
|
478 if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) { |
|
479 tmp = c1; |
|
480 *was_pruned = 1; |
|
481 } |
|
482 else { |
|
483 tmp = c2; |
|
484 } |
|
485 break; |
|
486 } |
|
487 } |
|
488 |
|
489 c2=TAILQ_NEXT(c2,entry_comp); |
|
490 } |
|
491 |
|
492 if (tmp) { |
|
493 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate", |
|
494 ctx->label,tmp->label); |
|
495 |
|
496 TAILQ_REMOVE(&comp->candidates,tmp,entry_comp); |
|
497 comp->candidate_ct--; |
|
498 TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock); |
|
499 |
|
500 nr_ice_candidate_destroy(&tmp); |
|
501 } |
|
502 |
|
503 return 0; |
|
504 } |
|
505 |
|
506 /* Section 7.2.1 */ |
|
507 static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error) |
|
508 { |
|
509 nr_ice_cand_pair *pair; |
|
510 nr_ice_candidate *pcand=0; |
|
511 nr_stun_message *sreq=req->request; |
|
512 nr_stun_message_attribute *attr; |
|
513 int component_id_matched; |
|
514 int local_addr_matched; |
|
515 int remote_addr_matched; |
|
516 nr_ice_cand_pair *found_invalid=0; |
|
517 int r=0,_status; |
|
518 |
|
519 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string); |
|
520 |
|
521 if (comp->state == NR_ICE_COMPONENT_DISABLED) |
|
522 ABORT(R_REJECTED); |
|
523 |
|
524 /* Check for role conficts (7.2.1.1) */ |
|
525 if(comp->stream->pctx->controlling){ |
|
526 if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){ |
|
527 /* OK, there is a conflict. Who's right? */ |
|
528 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlling",comp->stream->pctx->label); |
|
529 |
|
530 if(attr->u.ice_controlling > comp->stream->pctx->tiebreaker){ |
|
531 /* They are: switch */ |
|
532 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): switching to controlled",comp->stream->pctx->label); |
|
533 |
|
534 comp->stream->pctx->controlling=0; |
|
535 } |
|
536 else { |
|
537 /* We are: throw an error */ |
|
538 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); |
|
539 |
|
540 *error=487; |
|
541 ABORT(R_REJECTED); |
|
542 } |
|
543 } |
|
544 } |
|
545 else{ |
|
546 if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLED,&attr)){ |
|
547 /* OK, there is a conflict. Who's right? */ |
|
548 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlled",comp->stream->pctx->label); |
|
549 |
|
550 if(attr->u.ice_controlling < comp->stream->pctx->tiebreaker){ |
|
551 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): switching to controlling",comp->stream->pctx->label); |
|
552 |
|
553 /* They are: switch */ |
|
554 comp->stream->pctx->controlling=1; |
|
555 } |
|
556 else { |
|
557 /* We are: throw an error */ |
|
558 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); |
|
559 |
|
560 *error=487; |
|
561 ABORT(R_REJECTED); |
|
562 } |
|
563 } |
|
564 } |
|
565 |
|
566 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr->as_string); |
|
567 |
|
568 pair=TAILQ_FIRST(&comp->stream->check_list); |
|
569 while(pair){ |
|
570 component_id_matched = 0; |
|
571 local_addr_matched = 0; |
|
572 remote_addr_matched = 0; |
|
573 |
|
574 if(pair->remote->component->component_id!=comp->component_id) |
|
575 goto next_pair; |
|
576 component_id_matched = 1; |
|
577 |
|
578 if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) |
|
579 goto next_pair; |
|
580 local_addr_matched=1; |
|
581 |
|
582 |
|
583 if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) |
|
584 goto next_pair; |
|
585 remote_addr_matched = 1; |
|
586 |
|
587 if(pair->state==NR_ICE_PAIR_STATE_FAILED){ |
|
588 found_invalid=pair; |
|
589 goto next_pair; |
|
590 } |
|
591 |
|
592 if (local_addr_matched && remote_addr_matched){ |
|
593 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string); |
|
594 break; /* OK, this is a known pair */ |
|
595 } |
|
596 |
|
597 next_pair: |
|
598 pair=TAILQ_NEXT(pair,entry); |
|
599 } |
|
600 |
|
601 if(!pair){ |
|
602 if(!found_invalid){ |
|
603 /* First find our local component candidate */ |
|
604 nr_ice_candidate *cand; |
|
605 |
|
606 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label); |
|
607 cand=TAILQ_FIRST(&comp->local_component->candidates); |
|
608 while(cand){ |
|
609 if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) |
|
610 break; |
|
611 |
|
612 cand=TAILQ_NEXT(cand,entry_comp); |
|
613 } |
|
614 |
|
615 /* Well, this really shouldn't happen, but it's an error from the |
|
616 other side, so we just throw an error and keep going */ |
|
617 if(!cand){ |
|
618 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string); |
|
619 |
|
620 *error=400; |
|
621 ABORT(R_NOT_FOUND); |
|
622 } |
|
623 |
|
624 /* We now need to make a peer reflexive */ |
|
625 if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) { |
|
626 *error=(r==R_NO_MEMORY)?500:400; |
|
627 ABORT(r); |
|
628 } |
|
629 if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){ |
|
630 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label); |
|
631 *error=487; |
|
632 ABORT(R_BAD_DATA); |
|
633 } |
|
634 pcand->priority=attr->u.priority; |
|
635 pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED; |
|
636 |
|
637 if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand, |
|
638 &pair)) { |
|
639 *error=(r==R_NO_MEMORY)?500:400; |
|
640 ABORT(r); |
|
641 } |
|
642 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN); |
|
643 |
|
644 if(r=nr_ice_component_insert_pair(comp,pair)) { |
|
645 *error=(r==R_NO_MEMORY)?500:400; |
|
646 nr_ice_candidate_pair_destroy(&pair); |
|
647 ABORT(r); |
|
648 } |
|
649 |
|
650 /* Do this last, since any call to ABORT will destroy pcand */ |
|
651 TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp); |
|
652 pcand=0; |
|
653 } |
|
654 else{ |
|
655 /* OK, there was a pair, it's just invalid: According to Section |
|
656 7.2.1.4, we need to resurrect it |
|
657 */ |
|
658 if(found_invalid->state == NR_ICE_PAIR_STATE_FAILED){ |
|
659 pair=found_invalid; |
|
660 |
|
661 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair, resurrecting: %s",comp->stream->pctx->label,pair->codeword,pair->as_string); |
|
662 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_WAITING); |
|
663 } |
|
664 else{ |
|
665 /* This shouldn't happen */ |
|
666 r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair that was not in state FAILED; this should not happen: %s",comp->stream->pctx->label,pair->codeword,pair->as_string); |
|
667 *error=500; |
|
668 ABORT(R_BAD_DATA); |
|
669 } |
|
670 } |
|
671 } |
|
672 |
|
673 /* OK, we've got a pair to work with. Turn it on */ |
|
674 assert(pair); |
|
675 if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){ |
|
676 if(comp->stream->pctx->controlling){ |
|
677 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword); |
|
678 } |
|
679 else{ |
|
680 /* If this is the first time we've noticed this is nominated...*/ |
|
681 pair->peer_nominated=1; |
|
682 |
|
683 if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){ |
|
684 pair->nominated=1; |
|
685 |
|
686 if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) { |
|
687 *error=(r==R_NO_MEMORY)?500:400; |
|
688 ABORT(r); |
|
689 } |
|
690 } |
|
691 } |
|
692 } |
|
693 |
|
694 if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) { |
|
695 *error=(r==R_NO_MEMORY)?500:400; |
|
696 ABORT(r); |
|
697 } |
|
698 |
|
699 _status=0; |
|
700 abort: |
|
701 if(_status){ |
|
702 nr_ice_candidate_destroy(&pcand); |
|
703 assert(*error != 0); |
|
704 if(r!=R_NO_MEMORY) assert(*error != 500); |
|
705 } |
|
706 return(_status); |
|
707 } |
|
708 |
|
709 static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error) |
|
710 { |
|
711 nr_ice_component *comp=cb_arg; |
|
712 nr_transport_addr local_addr; |
|
713 int r,_status; |
|
714 |
|
715 /* Find the candidate pair that this maps to */ |
|
716 if(r=nr_socket_getaddr(sock,&local_addr)) { |
|
717 *error=500; |
|
718 ABORT(r); |
|
719 } |
|
720 |
|
721 if (r=nr_ice_component_process_incoming_check(comp, &local_addr, req, error)) |
|
722 ABORT(r); |
|
723 |
|
724 _status=0; |
|
725 abort: |
|
726 return(_status); |
|
727 } |
|
728 |
|
729 int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced) |
|
730 { |
|
731 nr_ice_pre_answer_request *r1,*r2; |
|
732 nr_ice_component *comp = pcomp->local_component; |
|
733 int r,_status; |
|
734 |
|
735 if (serviced) |
|
736 *serviced = 0; |
|
737 |
|
738 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id); |
|
739 |
|
740 STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) { |
|
741 if (!strcmp(r1->username, username)) { |
|
742 int error = 0; |
|
743 |
|
744 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id); |
|
745 r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error); |
|
746 if (r) { |
|
747 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error); |
|
748 } |
|
749 (*serviced)++; |
|
750 STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); |
|
751 nr_ice_pre_answer_request_destroy(&r1); |
|
752 } |
|
753 } |
|
754 |
|
755 _status=0; |
|
756 return(_status); |
|
757 } |
|
758 |
|
759 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote) |
|
760 { |
|
761 int r, _status; |
|
762 nr_ice_candidate *pcand; |
|
763 nr_ice_cand_pair *pair=0; |
|
764 char codeword[5]; |
|
765 |
|
766 nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword); |
|
767 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label); |
|
768 |
|
769 switch(lcand->type){ |
|
770 case HOST: |
|
771 break; |
|
772 case SERVER_REFLEXIVE: |
|
773 case PEER_REFLEXIVE: |
|
774 /* Don't actually pair these candidates */ |
|
775 goto done; |
|
776 break; |
|
777 case RELAYED: |
|
778 break; |
|
779 default: |
|
780 assert(0); |
|
781 ABORT(R_INTERNAL); |
|
782 break; |
|
783 } |
|
784 |
|
785 pcand=TAILQ_FIRST(&pcomp->candidates); |
|
786 while(pcand){ |
|
787 /* |
|
788 Two modes, depending on |pair_all_remote| |
|
789 |
|
790 1. Pair remote candidates which have not been paired |
|
791 (used in initial pairing or in processing the other side's |
|
792 trickle candidates). |
|
793 2. Pair any remote candidate (used when processing our own |
|
794 trickle candidates). |
|
795 */ |
|
796 if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) { |
|
797 /* If we are pairing our own trickle candidates, the remote candidate should |
|
798 all be paired */ |
|
799 if (pair_all_remote) |
|
800 assert (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_PAIRED); |
|
801 |
|
802 nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); |
|
803 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label); |
|
804 |
|
805 if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) |
|
806 ABORT(r); |
|
807 |
|
808 if(r=nr_ice_component_insert_pair(pcomp, pair)) |
|
809 ABORT(r); |
|
810 } |
|
811 |
|
812 pcand=TAILQ_NEXT(pcand,entry_comp); |
|
813 } |
|
814 |
|
815 done: |
|
816 _status = 0; |
|
817 abort: |
|
818 return(_status); |
|
819 } |
|
820 |
|
821 int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp) |
|
822 { |
|
823 nr_ice_candidate *lcand, *pcand; |
|
824 nr_ice_socket *isock; |
|
825 int r,_status; |
|
826 |
|
827 r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======"); |
|
828 |
|
829 /* Create the candidate pairs */ |
|
830 lcand=TAILQ_FIRST(&lcomp->candidates); |
|
831 while(lcand){ |
|
832 if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) { |
|
833 if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0))) |
|
834 ABORT(r); |
|
835 } |
|
836 |
|
837 lcand=TAILQ_NEXT(lcand,entry_comp); |
|
838 } |
|
839 |
|
840 /* Mark all peer candidates as paired */ |
|
841 pcand=TAILQ_FIRST(&pcomp->candidates); |
|
842 while(pcand){ |
|
843 pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; |
|
844 |
|
845 pcand=TAILQ_NEXT(pcand,entry_comp); |
|
846 |
|
847 } |
|
848 |
|
849 /* Now register the STUN server callback for this component. |
|
850 Note that this is a per-component CB so we only need to |
|
851 do this once. |
|
852 */ |
|
853 if (pcomp->state != NR_ICE_COMPONENT_RUNNING) { |
|
854 isock=STAILQ_FIRST(&lcomp->sockets); |
|
855 while(isock){ |
|
856 if(r=nr_stun_server_add_client(isock->stun_server,pctx->label, |
|
857 pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) { |
|
858 ABORT(r); |
|
859 } |
|
860 isock=STAILQ_NEXT(isock,entry); |
|
861 } |
|
862 } |
|
863 |
|
864 pcomp->state = NR_ICE_COMPONENT_RUNNING; |
|
865 |
|
866 _status=0; |
|
867 abort: |
|
868 return(_status); |
|
869 } |
|
870 |
|
871 /* Fires when we have an incoming candidate that doesn't correspond to an existing |
|
872 remote peer. This is either pre-answer or just spurious. Store it in the |
|
873 component for use when we see the actual answer, at which point we need |
|
874 to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb. |
|
875 */ |
|
876 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error) |
|
877 { |
|
878 int r, _status; |
|
879 nr_ice_component *comp = (nr_ice_component *)cb_arg; |
|
880 nr_ice_pre_answer_request *par = 0; |
|
881 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s", |
|
882 comp->ctx->label, comp->stream->label, comp->component_id, req->src_addr.as_string); |
|
883 |
|
884 if (r=nr_ice_pre_answer_request_create(sock, req, &par)) |
|
885 ABORT(r); |
|
886 |
|
887 *dont_free = 1; |
|
888 STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry); |
|
889 |
|
890 _status=0; |
|
891 abort: |
|
892 return 0; |
|
893 } |
|
894 |
|
895 int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) |
|
896 { |
|
897 int r,_status; |
|
898 int fire_cb=0; |
|
899 nr_ice_cand_pair *p2; |
|
900 |
|
901 if(!comp->nominated) |
|
902 fire_cb=1; |
|
903 |
|
904 /* Are we changing what the nominated pair is? */ |
|
905 if(comp->nominated){ |
|
906 if(comp->nominated->priority > pair->priority) |
|
907 return(0); |
|
908 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword); |
|
909 } |
|
910 |
|
911 /* Set the new nominated pair */ |
|
912 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string); |
|
913 comp->state=NR_ICE_COMPONENT_NOMINATED; |
|
914 comp->nominated=pair; |
|
915 comp->active=pair; |
|
916 |
|
917 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string); |
|
918 |
|
919 /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */ |
|
920 p2=TAILQ_FIRST(&comp->stream->check_list); |
|
921 while(p2){ |
|
922 if((p2 != pair) && |
|
923 (p2->remote->component->component_id == comp->component_id) && |
|
924 ((p2->state == NR_ICE_PAIR_STATE_FROZEN) || |
|
925 (p2->state == NR_ICE_PAIR_STATE_WAITING))) { |
|
926 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword); |
|
927 |
|
928 if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2)) |
|
929 ABORT(r); |
|
930 } |
|
931 |
|
932 p2=TAILQ_NEXT(p2,entry); |
|
933 } |
|
934 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id); |
|
935 |
|
936 if(r=nr_ice_media_stream_component_nominated(comp->stream,comp)) |
|
937 ABORT(r); |
|
938 |
|
939 _status=0; |
|
940 abort: |
|
941 return(_status); |
|
942 } |
|
943 |
|
944 int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) |
|
945 { |
|
946 int r,_status; |
|
947 nr_ice_cand_pair *p2; |
|
948 |
|
949 assert(pair->state == NR_ICE_PAIR_STATE_FAILED); |
|
950 |
|
951 p2=TAILQ_FIRST(&comp->stream->check_list); |
|
952 while(p2){ |
|
953 if(comp->component_id==p2->local->component_id){ |
|
954 switch(p2->state){ |
|
955 case NR_ICE_PAIR_STATE_FROZEN: |
|
956 case NR_ICE_PAIR_STATE_WAITING: |
|
957 case NR_ICE_PAIR_STATE_IN_PROGRESS: |
|
958 /* answer component status cannot be determined yet */ |
|
959 goto done; |
|
960 break; |
|
961 case NR_ICE_PAIR_STATE_SUCCEEDED: |
|
962 /* the component will succeed */ |
|
963 goto done; |
|
964 break; |
|
965 case NR_ICE_PAIR_STATE_FAILED: |
|
966 case NR_ICE_PAIR_STATE_CANCELLED: |
|
967 /* states that will never be recovered from */ |
|
968 break; |
|
969 default: |
|
970 assert(0); |
|
971 break; |
|
972 } |
|
973 } |
|
974 |
|
975 p2=TAILQ_NEXT(p2,entry); |
|
976 } |
|
977 |
|
978 /* all the pairs in the component are in their final states with |
|
979 * none of them being SUCCEEDED, so the component fails entirely, |
|
980 * tell the media stream that this component has failed */ |
|
981 |
|
982 if(r=nr_ice_media_stream_component_failed(comp->stream,comp)) |
|
983 ABORT(r); |
|
984 |
|
985 done: |
|
986 _status=0; |
|
987 abort: |
|
988 return(_status); |
|
989 } |
|
990 |
|
991 int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp) |
|
992 { |
|
993 nr_ice_cand_pair **pairs=0; |
|
994 int ct=0; |
|
995 nr_ice_cand_pair *pair; |
|
996 int r,_status; |
|
997 |
|
998 /* Size the array */ |
|
999 pair=TAILQ_FIRST(&comp->stream->check_list); |
|
1000 while(pair){ |
|
1001 if (comp->component_id == pair->local->component_id) |
|
1002 ct++; |
|
1003 |
|
1004 pair=TAILQ_NEXT(pair,entry); |
|
1005 } |
|
1006 |
|
1007 /* Make and fill the array */ |
|
1008 if(!(pairs=RCALLOC(sizeof(nr_ice_cand_pair *)*ct))) |
|
1009 ABORT(R_NO_MEMORY); |
|
1010 |
|
1011 ct=0; |
|
1012 pair=TAILQ_FIRST(&comp->stream->check_list); |
|
1013 while(pair){ |
|
1014 if (comp->component_id == pair->local->component_id) |
|
1015 pairs[ct++]=pair; |
|
1016 |
|
1017 pair=TAILQ_NEXT(pair,entry); |
|
1018 } |
|
1019 |
|
1020 if (pctx->handler) { |
|
1021 if(r=pctx->handler->vtbl->select_pair(pctx->handler->obj, |
|
1022 comp->stream,comp->component_id,pairs,ct)) |
|
1023 ABORT(r); |
|
1024 } |
|
1025 |
|
1026 _status=0; |
|
1027 abort: |
|
1028 RFREE(pairs); |
|
1029 return(_status); |
|
1030 } |
|
1031 |
|
1032 |
|
1033 static void nr_ice_component_keepalive_cb(NR_SOCKET s, int how, void *cb_arg) |
|
1034 { |
|
1035 nr_ice_component *comp=cb_arg; |
|
1036 UINT4 keepalive_timeout; |
|
1037 |
|
1038 assert(comp->keepalive_ctx); |
|
1039 |
|
1040 if(NR_reg_get_uint4(NR_ICE_REG_KEEPALIVE_TIMER,&keepalive_timeout)){ |
|
1041 keepalive_timeout=15000; /* Default */ |
|
1042 } |
|
1043 |
|
1044 |
|
1045 if(comp->keepalive_needed) |
|
1046 nr_stun_client_force_retransmit(comp->keepalive_ctx); |
|
1047 |
|
1048 comp->keepalive_needed=1; |
|
1049 NR_ASYNC_TIMER_SET(keepalive_timeout,nr_ice_component_keepalive_cb,cb_arg,&comp->keepalive_timer); |
|
1050 } |
|
1051 |
|
1052 |
|
1053 /* Close the underlying sockets for everything but the nominated candidate */ |
|
1054 int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp) |
|
1055 { |
|
1056 nr_ice_socket *isock=0; |
|
1057 int r,_status; |
|
1058 nr_ice_socket *s1,*s2; |
|
1059 |
|
1060 if(rcomp->state==NR_ICE_COMPONENT_NOMINATED){ |
|
1061 assert(rcomp->active == rcomp->nominated); |
|
1062 isock=rcomp->nominated->local->isock; |
|
1063 } |
|
1064 |
|
1065 STAILQ_FOREACH_SAFE(s1, &lcomp->sockets, entry, s2){ |
|
1066 if(s1!=isock){ |
|
1067 STAILQ_REMOVE(&lcomp->sockets,s1,nr_ice_socket_,entry); |
|
1068 nr_ice_socket_destroy(&s1); |
|
1069 } |
|
1070 } |
|
1071 |
|
1072 /* Set up the keepalives for the chosen socket */ |
|
1073 if(r=nr_stun_client_ctx_create("keepalive",rcomp->nominated->local->osock, |
|
1074 &rcomp->nominated->remote->addr,0,&rcomp->keepalive_ctx)) |
|
1075 ABORT(r); |
|
1076 if(r=nr_stun_client_start(rcomp->keepalive_ctx,NR_STUN_CLIENT_MODE_KEEPALIVE,0,0)) |
|
1077 ABORT(r); |
|
1078 nr_ice_component_keepalive_cb(0,0,rcomp); |
|
1079 |
|
1080 |
|
1081 _status=0; |
|
1082 abort: |
|
1083 |
|
1084 return(_status); |
|
1085 } |
|
1086 |
|
1087 |
|
1088 int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair) |
|
1089 { |
|
1090 int r,_status; |
|
1091 |
|
1092 /* Pairs for peer reflexive are marked SUCCEEDED immediately */ |
|
1093 if (pair->state != NR_ICE_PAIR_STATE_FROZEN && |
|
1094 pair->state != NR_ICE_PAIR_STATE_SUCCEEDED){ |
|
1095 assert(0); |
|
1096 ABORT(R_BAD_ARGS); |
|
1097 } |
|
1098 |
|
1099 if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair)) |
|
1100 ABORT(r); |
|
1101 |
|
1102 /* Make sure the check timer is running, if the stream was previously |
|
1103 * started. We will not start streams just because a pair was created. */ |
|
1104 if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE){ |
|
1105 if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) { |
|
1106 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string); |
|
1107 ABORT(R_INTERNAL); |
|
1108 } |
|
1109 } |
|
1110 |
|
1111 _status=0; |
|
1112 abort: |
|
1113 return(_status); |
|
1114 } |
|
1115 |