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_pair.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
36
37 #include <assert.h>
38 #include <string.h>
39 #include <nr_api.h>
40 #include "async_timer.h"
41 #include "ice_ctx.h"
42 #include "ice_util.h"
43 #include "ice_codeword.h"
44 #include "stun.h"
45
46 static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"};
47
48 static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg);
49 static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair,
50 nr_ice_candidate *lcand, nr_ice_candidate *rcand);
51
nr_ice_candidate_pair_set_priority(nr_ice_cand_pair * pair)52 static void nr_ice_candidate_pair_set_priority(nr_ice_cand_pair *pair)
53 {
54 /* Priority computation S 5.7.2 */
55 UINT8 controlling_priority, controlled_priority;
56 if(pair->pctx->controlling)
57 {
58 controlling_priority=pair->local->priority;
59 controlled_priority=pair->remote->priority;
60 }
61 else{
62 controlling_priority=pair->remote->priority;
63 controlled_priority=pair->local->priority;
64 }
65 pair->priority=(MIN(controlling_priority, controlled_priority))<<32 |
66 (MAX(controlling_priority, controlled_priority))<<1 |
67 (controlled_priority > controlling_priority?0:1);
68 }
69
nr_ice_candidate_pair_create(nr_ice_peer_ctx * pctx,nr_ice_candidate * lcand,nr_ice_candidate * rcand,nr_ice_cand_pair ** pairp)70 int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp)
71 {
72 nr_ice_cand_pair *pair=0;
73 int r,_status;
74 UINT4 RTO;
75 nr_ice_candidate tmpcand;
76 UINT8 t_priority;
77
78 if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair))))
79 ABORT(R_NO_MEMORY);
80
81 pair->pctx=pctx;
82
83 nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand);
84
85 if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|",
86 rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL))
87 ABORT(r);
88
89 nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
90 pair->local=lcand;
91 pair->remote=rcand;
92
93 nr_ice_candidate_pair_set_priority(pair);
94
95 /*
96 TODO(bcampen@mozilla.com): Would be nice to log why this candidate was
97 created (initial pair generation, triggered check, and new trickle
98 candidate seem to be the possibilities here).
99 */
100 r_log(LOG_ICE,LOG_INFO,"ICE(%s)/CAND-PAIR(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx)",pctx->ctx->label,pair->codeword,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority);
101
102 /* Foundation */
103 if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|",
104 rcand->foundation,NULL))
105 ABORT(r);
106
107 /* Compute the RTO per S 16 */
108 RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs));
109
110 /* Make a bogus candidate to compute a theoretical peer reflexive
111 * priority per S 7.1.1.1 */
112 memcpy(&tmpcand, lcand, sizeof(tmpcand));
113 tmpcand.type = PEER_REFLEXIVE;
114 if (r=nr_ice_candidate_compute_priority(&tmpcand))
115 ABORT(r);
116 t_priority = tmpcand.priority;
117
118 /* Our sending context */
119 if(r=nr_stun_client_ctx_create(pair->as_string,
120 lcand->osock,
121 &rcand->addr,RTO,&pair->stun_client))
122 ABORT(r);
123 if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(rcand->stream->l2r_user)))
124 ABORT(R_NO_MEMORY);
125 if(r=r_data_copy(&pair->stun_client->params.ice_binding_request.password,
126 &rcand->stream->l2r_pass))
127 ABORT(r);
128 /* TODO(ekr@rtfm.com): Do we need to frob this when we change role. Bug 890667 */
129 pair->stun_client->params.ice_binding_request.control = pctx->controlling?
130 NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
131 pair->stun_client->params.ice_binding_request.priority=t_priority;
132
133 pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker;
134
135 *pairp=pair;
136
137 _status=0;
138 abort:
139 if(_status){
140 nr_ice_candidate_pair_destroy(&pair);
141 }
142 return(_status);
143 }
144
nr_ice_candidate_pair_destroy(nr_ice_cand_pair ** pairp)145 int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp)
146 {
147 nr_ice_cand_pair *pair;
148
149 if(!pairp || !*pairp)
150 return(0);
151
152 pair=*pairp;
153 *pairp=0;
154
155 // record stats back to the ice ctx on destruction
156 if (pair->stun_client) {
157 nr_ice_accumulate_count(&(pair->local->ctx->stats.stun_retransmits), pair->stun_client->retransmit_ct);
158 }
159
160 RFREE(pair->as_string);
161 RFREE(pair->foundation);
162 nr_ice_socket_deregister(pair->local->isock,pair->stun_client_handle);
163 if (pair->stun_client) {
164 RFREE(pair->stun_client->params.ice_binding_request.username);
165 RFREE(pair->stun_client->params.ice_binding_request.password.data);
166 nr_stun_client_ctx_destroy(&pair->stun_client);
167 }
168
169 NR_async_timer_cancel(pair->stun_cb_timer);
170 NR_async_timer_cancel(pair->restart_role_change_cb_timer);
171 NR_async_timer_cancel(pair->restart_nominated_cb_timer);
172
173 RFREE(pair);
174 return(0);
175 }
176
nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx * pctx,nr_ice_cand_pair * pair)177 int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
178 {
179 assert(pair->state==NR_ICE_PAIR_STATE_FROZEN);
180
181 nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
182
183 return(0);
184 }
185
nr_ice_candidate_pair_stun_cb(NR_SOCKET s,int how,void * cb_arg)186 static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg)
187 {
188 int r,_status;
189 nr_ice_cand_pair *pair=cb_arg;
190 nr_ice_cand_pair *actual_pair=0;
191 nr_ice_candidate *cand=0;
192 nr_stun_message *sres;
193 nr_transport_addr *request_src;
194 nr_transport_addr *request_dst;
195 nr_transport_addr *response_src;
196 nr_transport_addr response_dst;
197 nr_stun_message_attribute *attr;
198
199 pair->stun_cb_timer=0;
200
201 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s",
202 pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string);
203
204 /* This ordinarily shouldn't happen, but can if we're
205 doing the second check to confirm nomination.
206 Just bail out */
207 if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED)
208 goto done;
209
210 switch(pair->stun_client->state){
211 case NR_STUN_CLIENT_STATE_FAILED:
212 sres=pair->stun_client->response;
213 if(sres && nr_stun_message_has_attribute(sres,NR_STUN_ATTR_ERROR_CODE,&attr)&&attr->u.error_code.number==487){
214
215 /*
216 * Flip the controlling bit; subsequent 487s for other pairs will be
217 * ignored, since we abandon their STUN transactions.
218 */
219 nr_ice_peer_ctx_switch_controlling_role(pair->pctx);
220
221 return;
222 }
223 /* Fall through */
224 case NR_STUN_CLIENT_STATE_TIMED_OUT:
225 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
226 break;
227 case NR_STUN_CLIENT_STATE_DONE:
228 /* make sure the addresses match up S 7.1.2.2 */
229 response_src=&pair->stun_client->peer_addr;
230 request_dst=&pair->remote->addr;
231 if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
232 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Peer address mismatch %s != %s",pair->pctx->label,pair->codeword,response_src->as_string,request_dst->as_string);
233 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
234 break;
235 }
236 request_src=&pair->stun_client->my_addr;
237 nr_socket_getaddr(pair->local->osock,&response_dst);
238 if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
239 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Local address mismatch %s != %s",pair->pctx->label,pair->codeword,request_src->as_string,response_dst.as_string);
240 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
241 break;
242 }
243
244 if(strlen(pair->stun_client->results.ice_binding_response.mapped_addr.as_string)==0){
245 /* we're using the mapped_addr returned by the server to lookup our
246 * candidate, but if the server fails to do that we can't perform
247 * the lookup -- this may be a BUG because if we've gotten here
248 * then the transaction ID check succeeded, and perhaps we should
249 * just assume that it's the server we're talking to and that our
250 * peer is ok, but I'm not sure how that'll interact with the
251 * peer reflexive logic below */
252 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->codeword,pair->as_string);
253 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
254 break;
255 }
256 else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
257 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED);
258 }
259 else if(pair->stun_client->state == NR_STUN_CLIENT_STATE_DONE) {
260 /* OK, this didn't correspond to a pair on the check list, but
261 it probably matches one of our candidates */
262
263 cand=TAILQ_FIRST(&pair->local->component->candidates);
264 while(cand){
265 if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
266 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): found pre-existing local candidate of type %d for mapped address %s", pair->pctx->label,cand->type,cand->addr.as_string);
267 assert(cand->type != HOST);
268 break;
269 }
270
271 cand=TAILQ_NEXT(cand,entry_comp);
272 }
273
274 if(!cand) {
275 /* OK, nothing found, must be a new peer reflexive */
276 if (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) {
277 /* Any STUN response with a reflexive address in it is unwanted
278 when we'll send on relay only. Bail since cand is used below. */
279 goto done;
280 }
281 if(r=nr_ice_candidate_create(pair->pctx->ctx,
282 pair->local->component,pair->local->isock,pair->local->osock,
283 PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand))
284 ABORT(r);
285 if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr))
286 ABORT(r);
287 cand->state=NR_ICE_CAND_STATE_INITIALIZED;
288 TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp);
289 } else {
290 /* Check if we have a pair for this candidate already. */
291 if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) {
292 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no pair exists for %s and %s", pair->pctx->label,cand->addr.as_string, pair->remote->addr.as_string);
293 }
294 }
295
296 if(!actual_pair) {
297 if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair))
298 ABORT(r);
299
300 if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair))
301 ABORT(r);
302
303 /* If the original pair was nominated, make us nominated too. */
304 if(pair->peer_nominated)
305 actual_pair->peer_nominated=1;
306
307 /* Now mark the orig pair failed */
308 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
309 }
310
311 assert(actual_pair);
312 nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED);
313 pair=actual_pair;
314
315 }
316
317 /* Should we set nominated? */
318 if(pair->pctx->controlling){
319 if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)
320 pair->nominated=1;
321 }
322 else{
323 if(pair->peer_nominated)
324 pair->nominated=1;
325 }
326
327
328 /* increment the number of valid pairs in the component */
329 /* We don't bother to maintain a separate valid list */
330 pair->remote->component->valid_pairs++;
331
332 /* S 7.1.2.2: unfreeze other pairs with the same foundation*/
333 if(r=nr_ice_media_stream_unfreeze_pairs_foundation(pair->remote->stream,pair->foundation))
334 ABORT(r);
335
336 /* Deal with this pair being nominated */
337 if(pair->nominated){
338 if(r=nr_ice_component_nominated_pair(pair->remote->component, pair))
339 ABORT(r);
340 }
341
342 break;
343 default:
344 ABORT(R_INTERNAL);
345 }
346
347 /* If we're controlling but in regular mode, ask the handler
348 if he wants to nominate something and stop... */
349 if(pair->pctx->controlling && !(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)){
350
351 if(r=nr_ice_component_select_pair(pair->pctx,pair->remote->component)){
352 if(r!=R_NOT_FOUND)
353 ABORT(r);
354 }
355 }
356
357 done:
358 _status=0;
359 abort:
360 return;
361 }
362
nr_ice_candidate_pair_restart(nr_ice_peer_ctx * pctx,nr_ice_cand_pair * pair)363 static void nr_ice_candidate_pair_restart(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
364 {
365 int r,_status;
366 UINT4 mode;
367
368 nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_IN_PROGRESS);
369
370 /* Start STUN */
371 if(pair->pctx->controlling && (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION))
372 mode=NR_ICE_CLIENT_MODE_USE_CANDIDATE;
373 else
374 mode=NR_ICE_CLIENT_MODE_BINDING_REQUEST;
375
376 nr_stun_client_reset(pair->stun_client);
377
378 if(r=nr_stun_client_start(pair->stun_client,mode,nr_ice_candidate_pair_stun_cb,pair))
379 ABORT(r);
380
381 if ((r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))) {
382 /* ignore if this fails (which it shouldn't) because it's only an
383 * optimization and the cleanup routines are not going to do the right
384 * thing if this fails */
385 assert(0);
386 }
387
388 _status=0;
389 abort:
390 if(_status){
391 /* Don't fire the CB, but schedule it to fire ASAP */
392 assert(!pair->stun_cb_timer);
393 NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_stun_cb,pair, &pair->stun_cb_timer);
394 _status=0;
395 }
396 }
397
nr_ice_candidate_pair_start(nr_ice_peer_ctx * pctx,nr_ice_cand_pair * pair)398 int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
399 {
400 int r,_status;
401
402 /* Register the stun ctx for when responses come in*/
403 if(r=nr_ice_socket_register_stun_client(pair->local->isock,pair->stun_client,&pair->stun_client_handle))
404 ABORT(r);
405
406 nr_ice_candidate_pair_restart(pctx, pair);
407
408 _status=0;
409 abort:
410 return(_status);
411 }
412
nr_ice_candidate_copy_for_triggered_check(nr_ice_cand_pair * pair)413 static int nr_ice_candidate_copy_for_triggered_check(nr_ice_cand_pair *pair)
414 {
415 int r,_status;
416 nr_ice_cand_pair *copy;
417
418 if(r=nr_ice_candidate_pair_create(pair->pctx, pair->local, pair->remote, ©))
419 ABORT(r);
420
421 /* Preserve nomination status */
422 copy->peer_nominated= pair->peer_nominated;
423 copy->nominated = pair->nominated;
424
425 r_log(LOG_ICE,LOG_INFO,"CAND-PAIR(%s): Adding pair to check list and trigger check queue: %s",pair->codeword,pair->as_string);
426 if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy))
427 ABORT(r);
428 nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,copy);
429
430 copy->triggered = 1;
431 nr_ice_candidate_pair_set_state(copy->pctx,copy,NR_ICE_PAIR_STATE_WAITING);
432
433 _status=0;
434 abort:
435 return(_status);
436 }
437
nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx * pctx,nr_ice_cand_pair * pair)438 int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
439 {
440 int r,_status;
441
442 if(pair->state==NR_ICE_PAIR_STATE_CANCELLED) {
443 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Ignoring matching but canceled pair",pctx->label,pair->codeword);
444 return(0);
445 } else if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) {
446 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for succeeded pair",pctx->label,pair->codeword);
447 return(0);
448 }
449
450 /* Do not run this logic more than once on a given pair */
451 if(!pair->triggered){
452 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string);
453
454 pair->triggered=1;
455
456 switch(pair->state){
457 case NR_ICE_PAIR_STATE_FAILED:
458 /* OK, there was a pair, it's just invalid: According to Section
459 * 7.2.1.4, we need to resurrect it */
460 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on failed pair, resurrecting: %s",pctx->label,pair->codeword,pair->as_string);
461 /* fall through */
462 case NR_ICE_PAIR_STATE_FROZEN:
463 nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
464 /* fall through even further */
465 case NR_ICE_PAIR_STATE_WAITING:
466 /* Append it additionally to the trigger check queue */
467 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): Inserting pair to trigger check queue: %s",pctx->label,pair->codeword,pair->as_string);
468 nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,pair);
469 break;
470 case NR_ICE_PAIR_STATE_IN_PROGRESS:
471 /* Instead of trying to maintain two stun contexts on the same pair,
472 * and handling heterogenous responses and error conditions, we instead
473 * create a second pair that is identical except that it has the
474 * |triggered| bit set. We also cancel the original pair, but it can
475 * still succeed on its own in the special waiting state. */
476 if(r=nr_ice_candidate_copy_for_triggered_check(pair))
477 ABORT(r);
478 nr_ice_candidate_pair_cancel(pair->pctx,pair,1);
479 break;
480 default:
481 /* all states are handled - a new/unknown state should not
482 * automatically enter the start_checks() below */
483 assert(0);
484 break;
485 }
486
487 /* Ensure that the timers are running to start checks on the topmost entry
488 * of the triggered check queue. */
489 if(r=nr_ice_media_stream_start_checks(pair->pctx,pair->remote->stream))
490 ABORT(r);
491 }
492
493 _status=0;
494 abort:
495 return(_status);
496 }
497
nr_ice_candidate_pair_cancel(nr_ice_peer_ctx * pctx,nr_ice_cand_pair * pair,int move_to_wait_state)498 int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state)
499 {
500 if(pair->state != NR_ICE_PAIR_STATE_FAILED){
501 /* If it's already running we need to terminate the stun */
502 if(pair->state==NR_ICE_PAIR_STATE_IN_PROGRESS){
503 if(move_to_wait_state) {
504 nr_stun_client_wait(pair->stun_client);
505 } else {
506 nr_stun_client_cancel(pair->stun_client);
507 }
508 }
509 nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_CANCELLED);
510 }
511
512 return(0);
513 }
514
nr_ice_candidate_pair_select(nr_ice_cand_pair * pair)515 int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair)
516 {
517 int r,_status;
518
519 if(!pair){
520 r_log(LOG_ICE,LOG_ERR,"ICE-PAIR: No pair chosen");
521 ABORT(R_BAD_ARGS);
522 }
523
524 if(pair->state!=NR_ICE_PAIR_STATE_SUCCEEDED){
525 r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): tried to install non-succeeded pair, ignoring: %s",pair->pctx->label,pair->codeword,pair->as_string);
526 }
527 else{
528 /* Ok, they chose one */
529 /* 1. Send a new request with nominated. Do it as a scheduled
530 event to avoid reentrancy issues. Only do this if it hasn't
531 happened already (though this shouldn't happen.)
532 */
533 if(!pair->restart_nominated_cb_timer)
534 NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_nominated_cb,pair,&pair->restart_nominated_cb_timer);
535
536 /* 2. Tell ourselves this pair is ready */
537 if(r=nr_ice_component_nominated_pair(pair->remote->component, pair))
538 ABORT(r);
539 }
540
541 _status=0;
542 abort:
543 return(_status);
544 }
545
nr_ice_candidate_pair_set_state(nr_ice_peer_ctx * pctx,nr_ice_cand_pair * pair,int state)546 int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state)
547 {
548 int r,_status;
549
550 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): setting pair to state %s: %s",
551 pctx->label,pair->codeword,nr_ice_cand_pair_states[state],pair->as_string);
552
553 /* NOTE: This function used to reference pctx->state instead of
554 pair->state and the assignment to pair->state was at the top
555 of this function. Because pctx->state was never changed, this seems to have
556 been a typo. The natural logic is "if the state changed
557 decrement the counter" so this implies we should be checking
558 the pair state rather than the pctx->state.
559
560 This didn't cause big problems because waiting_pairs was only
561 used for pacing, so the pacing just was kind of broken.
562
563 This note is here as a reminder until we do more testing
564 and make sure that in fact this was a typo.
565 */
566 if(pair->state!=NR_ICE_PAIR_STATE_WAITING){
567 if(state==NR_ICE_PAIR_STATE_WAITING)
568 pctx->waiting_pairs++;
569 }
570 else{
571 if(state!=NR_ICE_PAIR_STATE_WAITING)
572 pctx->waiting_pairs--;
573
574 assert(pctx->waiting_pairs>=0);
575 }
576 pair->state=state;
577
578
579 if(pair->state==NR_ICE_PAIR_STATE_FAILED ||
580 pair->state==NR_ICE_PAIR_STATE_CANCELLED){
581 if(r=nr_ice_component_failed_pair(pair->remote->component, pair))
582 ABORT(r);
583 }
584
585 _status=0;
586 abort:
587 return(_status);
588 }
589
nr_ice_candidate_pair_dump_state(nr_ice_cand_pair * pair,FILE * out)590 int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out)
591 {
592 /*r_log(LOG_ICE,LOG_DEBUG,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);*/
593
594 return(0);
595 }
596
597
nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head * head,nr_ice_cand_pair * pair)598 int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
599 {
600 if(pair->triggered_check_queue_entry.tqe_next ||
601 pair->triggered_check_queue_entry.tqe_prev)
602 return(0);
603
604 TAILQ_INSERT_TAIL(head,pair,triggered_check_queue_entry);
605
606 return(0);
607 }
608
nr_ice_candidate_pair_insert(nr_ice_cand_pair_head * head,nr_ice_cand_pair * pair)609 int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
610 {
611 nr_ice_cand_pair *c1;
612
613 c1=TAILQ_FIRST(head);
614 while(c1){
615 if(c1->priority < pair->priority){
616 TAILQ_INSERT_BEFORE(c1,pair,check_queue_entry);
617 break;
618 }
619
620 c1=TAILQ_NEXT(c1,check_queue_entry);
621 }
622 if(!c1) TAILQ_INSERT_TAIL(head,pair,check_queue_entry);
623
624 return(0);
625 }
626
nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s,int how,void * cb_arg)627 void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg)
628 {
629 nr_ice_cand_pair *pair=cb_arg;
630 int r,_status;
631
632 pair->restart_nominated_cb_timer=0;
633
634 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): Restarting pair as nominated: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string);
635
636 nr_stun_client_reset(pair->stun_client);
637
638 if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair))
639 ABORT(r);
640
641 if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))
642 ABORT(r);
643
644 _status=0;
645 abort:
646 return;
647 }
648
nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s,int how,void * cb_arg)649 static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg)
650 {
651 nr_ice_cand_pair *pair=cb_arg;
652
653 pair->restart_role_change_cb_timer=0;
654
655 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s):COMP(%d): Restarting pair as %s: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->pctx->controlling ? "CONTROLLING" : "CONTROLLED",pair->as_string);
656
657 nr_ice_candidate_pair_restart(pair->pctx, pair);
658 }
659
nr_ice_candidate_pair_role_change(nr_ice_cand_pair * pair)660 void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair)
661 {
662 pair->stun_client->params.ice_binding_request.control = pair->pctx->controlling ? NR_ICE_CONTROLLING : NR_ICE_CONTROLLED;
663 nr_ice_candidate_pair_set_priority(pair);
664
665 if(pair->state == NR_ICE_PAIR_STATE_IN_PROGRESS) {
666 /* We could try only restarting in-progress pairs when they receive their
667 * 487, but this ends up being simpler, because any extra 487 are dropped.
668 */
669 if(!pair->restart_role_change_cb_timer)
670 NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_role_change_cb,pair,&pair->restart_role_change_cb_timer);
671 }
672 }
673
nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair * pair,nr_ice_candidate * lcand,nr_ice_candidate * rcand)674 static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair,
675 nr_ice_candidate *lcand, nr_ice_candidate *rcand)
676 {
677 char as_string[2048];
678
679 snprintf(as_string,
680 sizeof(as_string),
681 "%s|%s(%s|%s)",
682 lcand->addr.as_string,
683 rcand->addr.as_string,
684 lcand->label,
685 rcand->label);
686
687 nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword);
688 }
689
690