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