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