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, &copy))
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