|
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_candidate.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; |
|
36 |
|
37 #include <csi_platform.h> |
|
38 #include <assert.h> |
|
39 #include <stdio.h> |
|
40 #include <string.h> |
|
41 #include <sys/queue.h> |
|
42 #include <sys/types.h> |
|
43 #ifdef WIN32 |
|
44 #include <winsock2.h> |
|
45 #else |
|
46 #include <sys/socket.h> |
|
47 #include <netinet/in.h> |
|
48 #include <arpa/inet.h> |
|
49 #endif |
|
50 #include "nr_api.h" |
|
51 #include "registry.h" |
|
52 #include "nr_socket.h" |
|
53 #include "async_timer.h" |
|
54 |
|
55 #include "stun_client_ctx.h" |
|
56 #include "stun_server_ctx.h" |
|
57 #include "turn_client_ctx.h" |
|
58 #include "ice_ctx.h" |
|
59 #include "ice_candidate.h" |
|
60 #include "ice_codeword.h" |
|
61 #include "ice_reg.h" |
|
62 #include "ice_util.h" |
|
63 #include "nr_socket_turn.h" |
|
64 #include "nr_socket.h" |
|
65 |
|
66 static int next_automatic_preference = 224; |
|
67 |
|
68 static int nr_ice_candidate_initialize2(nr_ice_candidate *cand); |
|
69 static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand); |
|
70 static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand); |
|
71 static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg); |
|
72 #ifdef USE_TURN |
|
73 static int nr_ice_start_relay_turn(nr_ice_candidate *cand); |
|
74 static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg); |
|
75 static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr); |
|
76 #endif /* USE_TURN */ |
|
77 |
|
78 void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand) |
|
79 { |
|
80 char as_string[1024]; |
|
81 |
|
82 snprintf(as_string, |
|
83 sizeof(as_string), |
|
84 "%s(%s)", |
|
85 cand->addr.as_string, |
|
86 cand->label); |
|
87 |
|
88 nr_ice_compute_codeword(as_string,strlen(as_string),cand->codeword); |
|
89 } |
|
90 |
|
91 char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0}; |
|
92 |
|
93 static const char *nr_ctype_name(nr_ice_candidate_type ctype) { |
|
94 assert(ctype<CTYPE_MAX && ctype>0); |
|
95 if (ctype <= 0 || ctype >= CTYPE_MAX) { |
|
96 return "ERROR"; |
|
97 } |
|
98 return nr_ice_candidate_type_names[ctype]; |
|
99 } |
|
100 |
|
101 static int nr_ice_candidate_format_stun_label(char *label, size_t size, nr_ice_candidate *cand) |
|
102 { |
|
103 int _status; |
|
104 |
|
105 *label = 0; |
|
106 switch(cand->stun_server->type) { |
|
107 case NR_ICE_STUN_SERVER_TYPE_ADDR: |
|
108 snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type), cand->base.as_string, |
|
109 cand->stun_server->u.addr.as_string); |
|
110 break; |
|
111 case NR_ICE_STUN_SERVER_TYPE_DNSNAME: |
|
112 snprintf(label, size, "%s(%s|%s:%u)", nr_ctype_name(cand->type), cand->base.as_string, |
|
113 cand->stun_server->u.dnsname.host, cand->stun_server->u.dnsname.port); |
|
114 break; |
|
115 default: |
|
116 assert(0); |
|
117 ABORT(R_BAD_ARGS); |
|
118 } |
|
119 |
|
120 _status=0; |
|
121 abort: |
|
122 return(_status); |
|
123 } |
|
124 |
|
125 int nr_ice_candidate_create(nr_ice_ctx *ctx,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp) |
|
126 { |
|
127 nr_ice_candidate *cand=0; |
|
128 nr_ice_candidate *tmp=0; |
|
129 int r,_status; |
|
130 char label[512]; |
|
131 |
|
132 if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) |
|
133 ABORT(R_NO_MEMORY); |
|
134 cand->state=NR_ICE_CAND_STATE_CREATED; |
|
135 cand->ctx=ctx; |
|
136 cand->isock=isock; |
|
137 cand->osock=osock; |
|
138 cand->type=ctype; |
|
139 cand->stun_server=stun_server; |
|
140 cand->component_id=component_id; |
|
141 cand->component=comp; |
|
142 cand->stream=comp->stream; |
|
143 |
|
144 /* Extract the addr as the base */ |
|
145 if(r=nr_socket_getaddr(cand->isock->sock,&cand->base)) |
|
146 ABORT(r); |
|
147 |
|
148 switch(ctype) { |
|
149 case HOST: |
|
150 snprintf(label, sizeof(label), "host(%s)", cand->base.as_string); |
|
151 break; |
|
152 |
|
153 case SERVER_REFLEXIVE: |
|
154 if(r=nr_ice_candidate_format_stun_label(label, sizeof(label),cand)) |
|
155 ABORT(r); |
|
156 break; |
|
157 |
|
158 case RELAYED: |
|
159 if(r=nr_ice_candidate_format_stun_label(label, sizeof(label),cand)) |
|
160 ABORT(r); |
|
161 break; |
|
162 |
|
163 case PEER_REFLEXIVE: |
|
164 snprintf(label, sizeof(label), "prflx"); |
|
165 break; |
|
166 |
|
167 default: |
|
168 assert(0); /* Can't happen */ |
|
169 ABORT(R_BAD_ARGS); |
|
170 } |
|
171 if(!(cand->label=r_strdup(label))) |
|
172 ABORT(R_NO_MEMORY); |
|
173 |
|
174 if(r=nr_ice_get_foundation(ctx,cand)) |
|
175 ABORT(r); |
|
176 if(r=nr_ice_candidate_compute_priority(cand)) |
|
177 ABORT(r); |
|
178 |
|
179 TAILQ_FOREACH(tmp,&isock->candidates,entry_sock){ |
|
180 if(cand->priority==tmp->priority){ |
|
181 r_log(LOG_ICE,LOG_ERR,"ICE(%s): duplicate priority %u candidate %s and candidate %s", |
|
182 ctx->label,cand->priority,cand->label,tmp->label); |
|
183 } |
|
184 } |
|
185 |
|
186 if(ctype==RELAYED) |
|
187 cand->u.relayed.turn_sock=osock; |
|
188 |
|
189 |
|
190 /* Add the candidate to the isock list*/ |
|
191 TAILQ_INSERT_TAIL(&isock->candidates,cand,entry_sock); |
|
192 |
|
193 nr_ice_candidate_compute_codeword(cand); |
|
194 |
|
195 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): created candidate %s with type %s", |
|
196 ctx->label,cand->label,nr_ctype_name(ctype)); |
|
197 |
|
198 *candp=cand; |
|
199 |
|
200 _status=0; |
|
201 abort: |
|
202 if (_status){ |
|
203 r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to create candidate of type %s", ctx->label,nr_ctype_name(ctype)); |
|
204 nr_ice_candidate_destroy(&cand); |
|
205 } |
|
206 return(_status); |
|
207 } |
|
208 |
|
209 |
|
210 /* Create a peer reflexive candidate */ |
|
211 int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp) |
|
212 { |
|
213 nr_ice_candidate *cand=0; |
|
214 nr_ice_candidate_type ctype=PEER_REFLEXIVE; |
|
215 int r,_status; |
|
216 |
|
217 if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) |
|
218 ABORT(R_NO_MEMORY); |
|
219 if(!(cand->label=r_strdup(label))) |
|
220 ABORT(R_NO_MEMORY); |
|
221 |
|
222 cand->state=NR_ICE_CAND_STATE_INITIALIZED; |
|
223 cand->ctx=ctx; |
|
224 cand->type=ctype; |
|
225 cand->component_id=comp->component_id; |
|
226 cand->component=comp; |
|
227 cand->stream=comp->stream; |
|
228 |
|
229 |
|
230 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): creating candidate with type %s", |
|
231 ctx->label,label,nr_ctype_name(ctype)); |
|
232 |
|
233 if(r=nr_transport_addr_copy(&cand->base,addr)) |
|
234 ABORT(r); |
|
235 if(r=nr_transport_addr_copy(&cand->addr,addr)) |
|
236 ABORT(r); |
|
237 /* Bogus foundation */ |
|
238 if(!(cand->foundation=r_strdup(cand->addr.as_string))) |
|
239 ABORT(R_NO_MEMORY); |
|
240 |
|
241 nr_ice_candidate_compute_codeword(cand); |
|
242 |
|
243 *candp=cand; |
|
244 |
|
245 _status=0; |
|
246 abort: |
|
247 if (_status){ |
|
248 nr_ice_candidate_destroy(&cand); |
|
249 } |
|
250 return(_status); |
|
251 } |
|
252 |
|
253 int nr_ice_candidate_destroy(nr_ice_candidate **candp) |
|
254 { |
|
255 nr_ice_candidate *cand=0; |
|
256 |
|
257 if(!candp || !*candp) |
|
258 return(0); |
|
259 |
|
260 cand=*candp; |
|
261 |
|
262 switch(cand->type){ |
|
263 case HOST: |
|
264 break; |
|
265 #ifdef USE_TURN |
|
266 case RELAYED: |
|
267 if (cand->u.relayed.turn_handle) |
|
268 nr_ice_socket_deregister(cand->isock, cand->u.relayed.turn_handle); |
|
269 nr_turn_client_ctx_destroy(&cand->u.relayed.turn); |
|
270 nr_socket_destroy(&cand->u.relayed.turn_sock); |
|
271 break; |
|
272 #endif /* USE_TURN */ |
|
273 case SERVER_REFLEXIVE: |
|
274 if (cand->u.srvrflx.stun_handle) |
|
275 nr_ice_socket_deregister(cand->isock, cand->u.srvrflx.stun_handle); |
|
276 nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); |
|
277 break; |
|
278 default: |
|
279 break; |
|
280 } |
|
281 |
|
282 NR_async_timer_cancel(cand->delay_timer); |
|
283 NR_async_timer_cancel(cand->ready_cb_timer); |
|
284 if(cand->resolver_handle){ |
|
285 nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle); |
|
286 } |
|
287 |
|
288 RFREE(cand->foundation); |
|
289 RFREE(cand->label); |
|
290 RFREE(cand); |
|
291 |
|
292 return(0); |
|
293 } |
|
294 |
|
295 void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg) |
|
296 { |
|
297 nr_ice_candidate *cand=cb_arg; |
|
298 nr_ice_candidate_destroy(&cand); |
|
299 } |
|
300 |
|
301 /* This algorithm is not super-fast, but I don't think we need a hash |
|
302 table just yet and it produces a small foundation string */ |
|
303 static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) |
|
304 { |
|
305 nr_ice_foundation *foundation; |
|
306 int i=0; |
|
307 char fnd[20]; |
|
308 int _status; |
|
309 |
|
310 foundation=STAILQ_FIRST(&ctx->foundations); |
|
311 while(foundation){ |
|
312 if(nr_transport_addr_cmp(&cand->base,&foundation->addr,NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) |
|
313 goto next; |
|
314 if(cand->type != foundation->type) |
|
315 goto next; |
|
316 if(cand->stun_server != foundation->stun_server) |
|
317 goto next; |
|
318 |
|
319 snprintf(fnd,sizeof(fnd),"%d",i); |
|
320 if(!(cand->foundation=r_strdup(fnd))) |
|
321 ABORT(R_NO_MEMORY); |
|
322 return(0); |
|
323 |
|
324 next: |
|
325 foundation=STAILQ_NEXT(foundation,entry); |
|
326 i++; |
|
327 } |
|
328 |
|
329 if(!(foundation=RCALLOC(sizeof(nr_ice_foundation)))) |
|
330 ABORT(R_NO_MEMORY); |
|
331 nr_transport_addr_copy(&foundation->addr,&cand->base); |
|
332 foundation->type=cand->type; |
|
333 foundation->stun_server=cand->stun_server; |
|
334 STAILQ_INSERT_TAIL(&ctx->foundations,foundation,entry); |
|
335 |
|
336 snprintf(fnd,sizeof(fnd),"%d",i); |
|
337 if(!(cand->foundation=r_strdup(fnd))) |
|
338 ABORT(R_NO_MEMORY); |
|
339 |
|
340 _status=0; |
|
341 abort: |
|
342 return(_status); |
|
343 } |
|
344 |
|
345 int nr_ice_candidate_compute_priority(nr_ice_candidate *cand) |
|
346 { |
|
347 UCHAR type_preference; |
|
348 UCHAR interface_preference; |
|
349 UCHAR stun_priority; |
|
350 int r,_status; |
|
351 |
|
352 switch(cand->type){ |
|
353 case HOST: |
|
354 if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST,&type_preference)) |
|
355 ABORT(r); |
|
356 stun_priority=0; |
|
357 break; |
|
358 case RELAYED: |
|
359 if(cand->base.protocol == IPPROTO_UDP) { |
|
360 if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED,&type_preference)) |
|
361 ABORT(r); |
|
362 } |
|
363 else if(cand->base.protocol == IPPROTO_TCP) { |
|
364 if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED_TCP,&type_preference)) |
|
365 ABORT(r); |
|
366 |
|
367 } |
|
368 else { |
|
369 r_log(LOG_ICE,LOG_ERR,"Unknown protocol type %u", |
|
370 (unsigned int)cand->base.protocol); |
|
371 ABORT(R_INTERNAL); |
|
372 } |
|
373 stun_priority=255-cand->stun_server->index; |
|
374 break; |
|
375 case SERVER_REFLEXIVE: |
|
376 if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX,&type_preference)) |
|
377 ABORT(r); |
|
378 stun_priority=255-cand->stun_server->index; |
|
379 break; |
|
380 case PEER_REFLEXIVE: |
|
381 if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX,&type_preference)) |
|
382 ABORT(r); |
|
383 stun_priority=0; |
|
384 break; |
|
385 default: |
|
386 ABORT(R_INTERNAL); |
|
387 } |
|
388 |
|
389 if(type_preference > 126) |
|
390 r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference); |
|
391 |
|
392 if(!cand->ctx->interface_prioritizer) { |
|
393 /* Prioritizer is not set, read from registry */ |
|
394 if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname, |
|
395 &interface_preference)) { |
|
396 if (r==R_NOT_FOUND) { |
|
397 if (next_automatic_preference == 1) { |
|
398 r_log(LOG_ICE,LOG_ERR,"Out of preference values. Can't assign one for interface %s",cand->base.ifname); |
|
399 ABORT(R_NOT_FOUND); |
|
400 } |
|
401 r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname, |
|
402 next_automatic_preference); |
|
403 if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){ |
|
404 ABORT(r); |
|
405 } |
|
406 interface_preference=next_automatic_preference; |
|
407 next_automatic_preference--; |
|
408 } |
|
409 else { |
|
410 ABORT(r); |
|
411 } |
|
412 } |
|
413 } |
|
414 else { |
|
415 char key_of_interface[MAXIFNAME + 41]; |
|
416 nr_transport_addr addr; |
|
417 |
|
418 if(r=nr_socket_getaddr(cand->isock->sock, &addr)) |
|
419 ABORT(r); |
|
420 |
|
421 if(r=nr_transport_addr_fmt_ifname_addr_string(&addr,key_of_interface, |
|
422 sizeof(key_of_interface))) { |
|
423 ABORT(r); |
|
424 } |
|
425 if(r=nr_interface_prioritizer_get_priority(cand->ctx->interface_prioritizer, |
|
426 key_of_interface,&interface_preference)) { |
|
427 ABORT(r); |
|
428 } |
|
429 } |
|
430 |
|
431 cand->priority= |
|
432 (type_preference << 24) | |
|
433 (interface_preference << 16) | |
|
434 (stun_priority << 8) | |
|
435 (256 - cand->component_id); |
|
436 |
|
437 /* S 4.1.2 */ |
|
438 assert(cand->priority>=1&&cand->priority<=2147483647); |
|
439 |
|
440 _status=0; |
|
441 abort: |
|
442 return(_status); |
|
443 } |
|
444 |
|
445 static void nr_ice_candidate_fire_ready_cb(NR_SOCKET s, int how, void *cb_arg) |
|
446 { |
|
447 nr_ice_candidate *cand = cb_arg; |
|
448 |
|
449 cand->ready_cb_timer = 0; |
|
450 cand->ready_cb(0, 0, cand->ready_cb_arg); |
|
451 } |
|
452 |
|
453 int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg) |
|
454 { |
|
455 int r,_status; |
|
456 int protocol=NR_RESOLVE_PROTOCOL_STUN; |
|
457 int transport=IPPROTO_UDP; |
|
458 cand->done_cb=ready_cb; |
|
459 cand->cb_arg=cb_arg; |
|
460 |
|
461 switch(cand->type){ |
|
462 case HOST: |
|
463 if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr)) |
|
464 ABORT(r); |
|
465 cand->osock=cand->isock->sock; |
|
466 cand->state=NR_ICE_CAND_STATE_INITIALIZED; |
|
467 // Post this so that it doesn't happen in-line |
|
468 cand->ready_cb = ready_cb; |
|
469 cand->ready_cb_arg = cb_arg; |
|
470 NR_ASYNC_TIMER_SET(0, nr_ice_candidate_fire_ready_cb, (void *)cand, &cand->ready_cb_timer); |
|
471 break; |
|
472 #ifdef USE_TURN |
|
473 case RELAYED: |
|
474 protocol=NR_RESOLVE_PROTOCOL_TURN; |
|
475 transport=cand->u.relayed.server->transport; |
|
476 /* Fall through */ |
|
477 #endif |
|
478 case SERVER_REFLEXIVE: |
|
479 cand->state=NR_ICE_CAND_STATE_INITIALIZING; |
|
480 |
|
481 if(cand->stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) { |
|
482 /* Just copy the address */ |
|
483 if (r=nr_transport_addr_copy(&cand->stun_server_addr, |
|
484 &cand->stun_server->u.addr)) { |
|
485 r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not copy STUN server addr", cand->label); |
|
486 ABORT(r); |
|
487 } |
|
488 |
|
489 if(r=nr_ice_candidate_initialize2(cand)) |
|
490 ABORT(r); |
|
491 } |
|
492 else { |
|
493 nr_resolver_resource resource; |
|
494 resource.domain_name=cand->stun_server->u.dnsname.host; |
|
495 resource.port=cand->stun_server->u.dnsname.port; |
|
496 resource.stun_turn=protocol; |
|
497 resource.transport_protocol=transport; |
|
498 |
|
499 /* Try to resolve */ |
|
500 if(!cand->ctx->resolver) { |
|
501 r_log(LOG_ICE, LOG_ERR, "ICE-CANDIDATE(%s): Can't use DNS names without a resolver", cand->label); |
|
502 ABORT(R_BAD_ARGS); |
|
503 } |
|
504 |
|
505 if(r=nr_resolver_resolve(cand->ctx->resolver, |
|
506 &resource, |
|
507 nr_ice_candidate_resolved_cb, |
|
508 (void *)cand, |
|
509 &cand->resolver_handle)){ |
|
510 r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not invoke DNS resolver",cand->label); |
|
511 ABORT(r); |
|
512 } |
|
513 } |
|
514 break; |
|
515 default: |
|
516 ABORT(R_INTERNAL); |
|
517 } |
|
518 |
|
519 nr_ice_candidate_compute_codeword(cand); |
|
520 |
|
521 _status=0; |
|
522 abort: |
|
523 if(_status && _status!=R_WOULDBLOCK) |
|
524 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
525 return(_status); |
|
526 } |
|
527 |
|
528 |
|
529 static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr) |
|
530 { |
|
531 nr_ice_candidate *cand=cb_arg; |
|
532 int r,_status; |
|
533 |
|
534 cand->resolver_handle=0; |
|
535 |
|
536 if(addr){ |
|
537 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): resolved candidate %s. addr=%s", |
|
538 cand->ctx->label,cand->label,addr->as_string); |
|
539 } |
|
540 else { |
|
541 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to resolve candidate %s.", |
|
542 cand->ctx->label,cand->label); |
|
543 ABORT(R_NOT_FOUND); |
|
544 } |
|
545 |
|
546 /* Copy the address */ |
|
547 if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr)) |
|
548 ABORT(r); |
|
549 |
|
550 /* Now start initializing */ |
|
551 if(r=nr_ice_candidate_initialize2(cand)) |
|
552 ABORT(r); |
|
553 |
|
554 _status=0; |
|
555 abort: |
|
556 if(_status && _status!=R_WOULDBLOCK) { |
|
557 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
558 cand->done_cb(0,0,cand->cb_arg); |
|
559 } |
|
560 return(_status); |
|
561 } |
|
562 |
|
563 static int nr_ice_candidate_initialize2(nr_ice_candidate *cand) |
|
564 { |
|
565 int r,_status; |
|
566 |
|
567 switch(cand->type){ |
|
568 case HOST: |
|
569 assert(0); /* Can't happen */ |
|
570 ABORT(R_INTERNAL); |
|
571 break; |
|
572 #ifdef USE_TURN |
|
573 case RELAYED: |
|
574 if(r=nr_ice_start_relay_turn(cand)) |
|
575 ABORT(r); |
|
576 ABORT(R_WOULDBLOCK); |
|
577 break; |
|
578 #endif /* USE_TURN */ |
|
579 case SERVER_REFLEXIVE: |
|
580 /* Need to start stun */ |
|
581 if(r=nr_ice_srvrflx_start_stun(cand)) |
|
582 ABORT(r); |
|
583 cand->osock=cand->isock->sock; |
|
584 ABORT(R_WOULDBLOCK); |
|
585 break; |
|
586 default: |
|
587 ABORT(R_INTERNAL); |
|
588 } |
|
589 |
|
590 _status=0; |
|
591 abort: |
|
592 if(_status && _status!=R_WOULDBLOCK) |
|
593 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
594 return(_status); |
|
595 } |
|
596 |
|
597 static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg) |
|
598 { |
|
599 nr_ice_candidate *cand=cb_arg; |
|
600 int r,_status; |
|
601 |
|
602 cand->delay_timer=0; |
|
603 |
|
604 /* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart |
|
605 * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the |
|
606 * TODO: server may not have understood the 0.96-style request */ |
|
607 if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH, nr_ice_srvrflx_stun_finished_cb, cand)) |
|
608 ABORT(r); |
|
609 |
|
610 if(r=nr_ice_ctx_remember_id(cand->ctx, cand->u.srvrflx.stun->request)) |
|
611 ABORT(r); |
|
612 |
|
613 if(r=nr_ice_socket_register_stun_client(cand->isock,cand->u.srvrflx.stun,&cand->u.srvrflx.stun_handle)) |
|
614 ABORT(r); |
|
615 |
|
616 _status=0; |
|
617 abort: |
|
618 if(_status){ |
|
619 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
620 } |
|
621 |
|
622 return; |
|
623 } |
|
624 |
|
625 static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand) |
|
626 { |
|
627 int r,_status; |
|
628 |
|
629 assert(!cand->delay_timer); |
|
630 if(r=nr_stun_client_ctx_create(cand->label, cand->isock->sock, |
|
631 &cand->stun_server_addr, cand->stream->ctx->gather_rto, |
|
632 &cand->u.srvrflx.stun)) |
|
633 ABORT(r); |
|
634 |
|
635 NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_srvrflx_start_stun_timer_cb,cand,&cand->delay_timer); |
|
636 cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; |
|
637 |
|
638 _status=0; |
|
639 abort: |
|
640 if(_status){ |
|
641 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
642 } |
|
643 return(_status); |
|
644 } |
|
645 |
|
646 #ifdef USE_TURN |
|
647 static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg) |
|
648 { |
|
649 nr_ice_candidate *cand=cb_arg; |
|
650 int r,_status; |
|
651 |
|
652 cand->delay_timer=0; |
|
653 |
|
654 if(r=nr_turn_client_allocate(cand->u.relayed.turn, nr_ice_turn_allocated_cb, cb_arg)) |
|
655 ABORT(r); |
|
656 |
|
657 if(r=nr_ice_socket_register_turn_client(cand->isock, cand->u.relayed.turn, |
|
658 cand->osock, &cand->u.relayed.turn_handle)) |
|
659 ABORT(r); |
|
660 |
|
661 _status=0; |
|
662 abort: |
|
663 if(_status){ |
|
664 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
665 } |
|
666 return; |
|
667 } |
|
668 |
|
669 static int nr_ice_start_relay_turn(nr_ice_candidate *cand) |
|
670 { |
|
671 int r,_status; |
|
672 assert(!cand->delay_timer); |
|
673 if(r=nr_turn_client_ctx_create(cand->label, cand->isock->sock, |
|
674 cand->u.relayed.server->username, |
|
675 cand->u.relayed.server->password, |
|
676 &cand->stun_server_addr, |
|
677 &cand->u.relayed.turn)) |
|
678 ABORT(r); |
|
679 |
|
680 if(r=nr_socket_turn_set_ctx(cand->osock, cand->u.relayed.turn)) |
|
681 ABORT(r); |
|
682 |
|
683 NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_start_relay_turn_timer_cb,cand,&cand->delay_timer); |
|
684 cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; |
|
685 |
|
686 _status=0; |
|
687 abort: |
|
688 if(_status){ |
|
689 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
690 } |
|
691 return(_status); |
|
692 } |
|
693 #endif /* USE_TURN */ |
|
694 |
|
695 static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg) |
|
696 { |
|
697 int _status; |
|
698 nr_ice_candidate *cand=cb_arg; |
|
699 |
|
700 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): %s",cand->ctx->label,cand->label,__FUNCTION__); |
|
701 |
|
702 /* Deregister to suppress duplicates */ |
|
703 if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */ |
|
704 nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle); |
|
705 cand->u.srvrflx.stun_handle=0; |
|
706 } |
|
707 |
|
708 switch(cand->u.srvrflx.stun->state){ |
|
709 /* OK, we should have a mapped address */ |
|
710 case NR_STUN_CLIENT_STATE_DONE: |
|
711 /* Copy the address */ |
|
712 nr_transport_addr_copy(&cand->addr, &cand->u.srvrflx.stun->results.stun_binding_response.mapped_addr); |
|
713 nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); |
|
714 cand->state=NR_ICE_CAND_STATE_INITIALIZED; |
|
715 /* Execute the ready callback */ |
|
716 cand->done_cb(0,0,cand->cb_arg); |
|
717 break; |
|
718 |
|
719 /* This failed, so go to the next STUN server if there is one */ |
|
720 case NR_STUN_CLIENT_STATE_FAILED: |
|
721 ABORT(R_NOT_FOUND); |
|
722 break; |
|
723 default: |
|
724 ABORT(R_INTERNAL); |
|
725 } |
|
726 _status = 0; |
|
727 abort: |
|
728 if(_status){ |
|
729 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
730 cand->done_cb(0,0,cand->cb_arg); |
|
731 } |
|
732 } |
|
733 |
|
734 #ifdef USE_TURN |
|
735 static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg) |
|
736 { |
|
737 int r,_status; |
|
738 nr_ice_candidate *cand=cb_arg; |
|
739 nr_turn_client_ctx *turn=cand->u.relayed.turn; |
|
740 char *label; |
|
741 nr_transport_addr relay_addr; |
|
742 |
|
743 switch(turn->state){ |
|
744 /* OK, we should have a mapped address */ |
|
745 case NR_TURN_CLIENT_STATE_ALLOCATED: |
|
746 if (r=nr_turn_client_get_relayed_address(turn, &relay_addr)) |
|
747 ABORT(r); |
|
748 |
|
749 if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|", |
|
750 relay_addr.as_string,")",NULL)) |
|
751 ABORT(r); |
|
752 |
|
753 r_log(LOG_ICE,LOG_DEBUG,"TURN-CLIENT(%s)/CAND(%s): Switching from TURN to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label); |
|
754 |
|
755 /* Copy the relayed address into the candidate addr and |
|
756 into the candidate base. Note that we need to keep the |
|
757 ifname in the base. */ |
|
758 if (r=nr_transport_addr_copy(&cand->addr, &relay_addr)) |
|
759 ABORT(r); |
|
760 if (r=nr_transport_addr_copy_keep_ifname(&cand->base, &relay_addr)) /* Need to keep interface for priority calculation */ |
|
761 ABORT(r); |
|
762 |
|
763 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): new relay base=%s addr=%s", cand->ctx->label, cand->label, cand->base.as_string, cand->addr.as_string); |
|
764 |
|
765 RFREE(cand->label); |
|
766 cand->label=label; |
|
767 cand->state=NR_ICE_CAND_STATE_INITIALIZED; |
|
768 |
|
769 /* We also need to activate the associated STUN candidate */ |
|
770 if(cand->u.relayed.srvflx_candidate){ |
|
771 nr_ice_candidate *cand2=cand->u.relayed.srvflx_candidate; |
|
772 |
|
773 if (r=nr_turn_client_get_mapped_address(cand->u.relayed.turn, &cand2->addr)) |
|
774 ABORT(r); |
|
775 |
|
776 cand2->state=NR_ICE_CAND_STATE_INITIALIZED; |
|
777 cand2->done_cb(0,0,cand2->cb_arg); |
|
778 } |
|
779 |
|
780 /* Execute the ready callback */ |
|
781 cand->done_cb(0,0,cand->cb_arg); |
|
782 cand = 0; |
|
783 |
|
784 break; |
|
785 |
|
786 case NR_TURN_CLIENT_STATE_FAILED: |
|
787 case NR_TURN_CLIENT_STATE_CANCELLED: |
|
788 r_log(NR_LOG_TURN, LOG_WARNING, |
|
789 "ICE-CANDIDATE(%s): nr_turn_allocated_cb called with state %d", |
|
790 cand->label, turn->state); |
|
791 /* This failed, so go to the next TURN server if there is one */ |
|
792 ABORT(R_NOT_FOUND); |
|
793 break; |
|
794 default: |
|
795 assert(0); /* should never happen */ |
|
796 ABORT(R_INTERNAL); |
|
797 } |
|
798 |
|
799 _status=0; |
|
800 abort: |
|
801 if(_status){ |
|
802 if (cand) { |
|
803 r_log(NR_LOG_TURN, LOG_WARNING, |
|
804 "ICE-CANDIDATE(%s): nr_turn_allocated_cb failed", cand->label); |
|
805 cand->state=NR_ICE_CAND_STATE_FAILED; |
|
806 cand->done_cb(0,0,cand->cb_arg); |
|
807 |
|
808 if(cand->u.relayed.srvflx_candidate){ |
|
809 nr_ice_candidate *cand2=cand->u.relayed.srvflx_candidate; |
|
810 |
|
811 cand2->state=NR_ICE_CAND_STATE_FAILED; |
|
812 cand2->done_cb(0,0,cand2->cb_arg); |
|
813 } |
|
814 } |
|
815 } |
|
816 } |
|
817 #endif /* USE_TURN */ |
|
818 |
|
819 /* Format the candidate attribute as per ICE S 15.1 */ |
|
820 int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen) |
|
821 { |
|
822 int r,_status; |
|
823 char addr[64]; |
|
824 int port; |
|
825 int len; |
|
826 |
|
827 assert(!strcmp(nr_ice_candidate_type_names[HOST], "host")); |
|
828 assert(!strcmp(nr_ice_candidate_type_names[RELAYED], "relay")); |
|
829 |
|
830 if(r=nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr))) |
|
831 ABORT(r); |
|
832 if(r=nr_transport_addr_get_port(&cand->addr,&port)) |
|
833 ABORT(r); |
|
834 snprintf(attr,maxlen,"candidate:%s %d UDP %u %s %d typ %s", |
|
835 cand->foundation, cand->component_id, cand->priority, addr, port, |
|
836 nr_ctype_name(cand->type)); |
|
837 |
|
838 len=strlen(attr); attr+=len; maxlen-=len; |
|
839 |
|
840 /* raddr, rport */ |
|
841 switch(cand->type){ |
|
842 case HOST: |
|
843 break; |
|
844 case SERVER_REFLEXIVE: |
|
845 case PEER_REFLEXIVE: |
|
846 if(r=nr_transport_addr_get_addrstring(&cand->base,addr,sizeof(addr))) |
|
847 ABORT(r); |
|
848 if(r=nr_transport_addr_get_port(&cand->base,&port)) |
|
849 ABORT(r); |
|
850 |
|
851 snprintf(attr,maxlen," raddr %s rport %d",addr,port); |
|
852 break; |
|
853 case RELAYED: |
|
854 // comes from XorMappedAddress via AllocateResponse |
|
855 if(r=nr_transport_addr_get_addrstring(&cand->base,addr,sizeof(addr))) |
|
856 ABORT(r); |
|
857 if(r=nr_transport_addr_get_port(&cand->base,&port)) |
|
858 ABORT(r); |
|
859 |
|
860 snprintf(attr,maxlen," raddr %s rport %d",addr,port); |
|
861 break; |
|
862 default: |
|
863 assert(0); |
|
864 ABORT(R_INTERNAL); |
|
865 break; |
|
866 } |
|
867 _status=0; |
|
868 abort: |
|
869 return(_status); |
|
870 } |
|
871 |