Line data Source code
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 :
52 0 : 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 0 : if(pair->pctx->controlling)
57 : {
58 0 : controlling_priority=pair->local->priority;
59 0 : controlled_priority=pair->remote->priority;
60 : }
61 : else{
62 0 : controlling_priority=pair->remote->priority;
63 0 : controlled_priority=pair->local->priority;
64 : }
65 0 : pair->priority=(MIN(controlling_priority, controlled_priority))<<32 |
66 0 : (MAX(controlling_priority, controlled_priority))<<1 |
67 0 : (controlled_priority > controlling_priority?0:1);
68 0 : }
69 :
70 0 : 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 0 : nr_ice_cand_pair *pair=0;
73 : int r,_status;
74 : UINT4 RTO;
75 : nr_ice_candidate tmpcand;
76 : UINT8 t_priority;
77 :
78 0 : if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair))))
79 0 : ABORT(R_NO_MEMORY);
80 :
81 0 : pair->pctx=pctx;
82 :
83 0 : nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand);
84 :
85 0 : if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|",
86 0 : rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL))
87 0 : ABORT(r);
88 :
89 0 : nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
90 0 : pair->local=lcand;
91 0 : pair->remote=rcand;
92 :
93 0 : 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 0 : 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 0 : if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|",
104 : rcand->foundation,NULL))
105 0 : ABORT(r);
106 :
107 : /* Compute the RTO per S 16 */
108 0 : 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 0 : memcpy(&tmpcand, lcand, sizeof(tmpcand));
113 0 : tmpcand.type = PEER_REFLEXIVE;
114 0 : if (r=nr_ice_candidate_compute_priority(&tmpcand))
115 0 : ABORT(r);
116 0 : t_priority = tmpcand.priority;
117 :
118 : /* Our sending context */
119 0 : if(r=nr_stun_client_ctx_create(pair->as_string,
120 : lcand->osock,
121 0 : &rcand->addr,RTO,&pair->stun_client))
122 0 : ABORT(r);
123 0 : if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(rcand->stream->l2r_user)))
124 0 : ABORT(R_NO_MEMORY);
125 0 : if(r=r_data_copy(&pair->stun_client->params.ice_binding_request.password,
126 0 : &rcand->stream->l2r_pass))
127 0 : ABORT(r);
128 : /* TODO(ekr@rtfm.com): Do we need to frob this when we change role. Bug 890667 */
129 0 : pair->stun_client->params.ice_binding_request.control = pctx->controlling?
130 0 : NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
131 0 : pair->stun_client->params.ice_binding_request.priority=t_priority;
132 :
133 0 : pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker;
134 :
135 0 : *pairp=pair;
136 :
137 0 : _status=0;
138 : abort:
139 0 : if(_status){
140 0 : nr_ice_candidate_pair_destroy(&pair);
141 : }
142 0 : return(_status);
143 : }
144 :
145 0 : int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp)
146 : {
147 : nr_ice_cand_pair *pair;
148 :
149 0 : if(!pairp || !*pairp)
150 0 : return(0);
151 :
152 0 : pair=*pairp;
153 0 : *pairp=0;
154 :
155 : // record stats back to the ice ctx on destruction
156 0 : if (pair->stun_client) {
157 0 : nr_ice_accumulate_count(&(pair->local->ctx->stats.stun_retransmits), pair->stun_client->retransmit_ct);
158 : }
159 :
160 0 : RFREE(pair->as_string);
161 0 : RFREE(pair->foundation);
162 0 : nr_ice_socket_deregister(pair->local->isock,pair->stun_client_handle);
163 0 : if (pair->stun_client) {
164 0 : RFREE(pair->stun_client->params.ice_binding_request.username);
165 0 : RFREE(pair->stun_client->params.ice_binding_request.password.data);
166 0 : nr_stun_client_ctx_destroy(&pair->stun_client);
167 : }
168 :
169 0 : NR_async_timer_cancel(pair->stun_cb_timer);
170 0 : NR_async_timer_cancel(pair->restart_role_change_cb_timer);
171 0 : NR_async_timer_cancel(pair->restart_nominated_cb_timer);
172 :
173 0 : RFREE(pair);
174 0 : return(0);
175 : }
176 :
177 0 : int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
178 : {
179 0 : assert(pair->state==NR_ICE_PAIR_STATE_FROZEN);
180 :
181 0 : nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
182 :
183 0 : return(0);
184 : }
185 :
186 0 : static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg)
187 : {
188 : int r,_status;
189 0 : nr_ice_cand_pair *pair=cb_arg;
190 0 : nr_ice_cand_pair *actual_pair=0;
191 0 : 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 0 : pair->stun_cb_timer=0;
200 :
201 0 : r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s",
202 0 : 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 0 : if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED)
208 0 : goto done;
209 :
210 0 : switch(pair->stun_client->state){
211 : case NR_STUN_CLIENT_STATE_FAILED:
212 0 : sres=pair->stun_client->response;
213 0 : 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 0 : nr_ice_peer_ctx_switch_controlling_role(pair->pctx);
220 :
221 0 : return;
222 : }
223 : /* Fall through */
224 : case NR_STUN_CLIENT_STATE_TIMED_OUT:
225 0 : nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
226 0 : break;
227 : case NR_STUN_CLIENT_STATE_DONE:
228 : /* make sure the addresses match up S 7.1.2.2 */
229 0 : response_src=&pair->stun_client->peer_addr;
230 0 : request_dst=&pair->remote->addr;
231 0 : if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
232 0 : 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 0 : nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
234 0 : break;
235 : }
236 0 : request_src=&pair->stun_client->my_addr;
237 0 : nr_socket_getaddr(pair->local->osock,&response_dst);
238 0 : if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
239 0 : 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 0 : nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
241 0 : break;
242 : }
243 :
244 0 : 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 0 : 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 0 : nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
254 0 : break;
255 : }
256 0 : 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 0 : nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED);
258 : }
259 0 : 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 0 : cand=TAILQ_FIRST(&pair->local->component->candidates);
264 0 : while(cand){
265 0 : if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
266 0 : 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 0 : assert(cand->type != HOST);
268 0 : break;
269 : }
270 :
271 0 : cand=TAILQ_NEXT(cand,entry_comp);
272 : }
273 :
274 0 : if(!cand) {
275 : /* OK, nothing found, must be a new peer reflexive */
276 0 : 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 0 : goto done;
280 : }
281 0 : if(r=nr_ice_candidate_create(pair->pctx->ctx,
282 0 : pair->local->component,pair->local->isock,pair->local->osock,
283 0 : PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand))
284 0 : ABORT(r);
285 0 : if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr))
286 0 : ABORT(r);
287 0 : cand->state=NR_ICE_CAND_STATE_INITIALIZED;
288 0 : TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp);
289 : } else {
290 : /* Check if we have a pair for this candidate already. */
291 0 : if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) {
292 0 : 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 0 : if(!actual_pair) {
297 0 : if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair))
298 0 : ABORT(r);
299 :
300 0 : if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair))
301 0 : ABORT(r);
302 :
303 : /* If the original pair was nominated, make us nominated too. */
304 0 : if(pair->peer_nominated)
305 0 : actual_pair->peer_nominated=1;
306 :
307 : /* Now mark the orig pair failed */
308 0 : nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
309 : }
310 :
311 0 : assert(actual_pair);
312 0 : nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED);
313 0 : pair=actual_pair;
314 :
315 : }
316 :
317 : /* Should we set nominated? */
318 0 : if(pair->pctx->controlling){
319 0 : if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)
320 0 : pair->nominated=1;
321 : }
322 : else{
323 0 : if(pair->peer_nominated)
324 0 : 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 0 : pair->remote->component->valid_pairs++;
331 :
332 : /* S 7.1.2.2: unfreeze other pairs with the same foundation*/
333 0 : if(r=nr_ice_media_stream_unfreeze_pairs_foundation(pair->remote->stream,pair->foundation))
334 0 : ABORT(r);
335 :
336 : /* Deal with this pair being nominated */
337 0 : if(pair->nominated){
338 0 : if(r=nr_ice_component_nominated_pair(pair->remote->component, pair))
339 0 : ABORT(r);
340 : }
341 :
342 0 : break;
343 : default:
344 0 : 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 0 : if(pair->pctx->controlling && !(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)){
350 :
351 0 : if(r=nr_ice_component_select_pair(pair->pctx,pair->remote->component)){
352 0 : if(r!=R_NOT_FOUND)
353 0 : ABORT(r);
354 : }
355 : }
356 :
357 : done:
358 0 : _status=0;
359 : abort:
360 0 : return;
361 : }
362 :
363 0 : 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 0 : nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_IN_PROGRESS);
369 :
370 : /* Start STUN */
371 0 : if(pair->pctx->controlling && (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION))
372 0 : mode=NR_ICE_CLIENT_MODE_USE_CANDIDATE;
373 : else
374 0 : mode=NR_ICE_CLIENT_MODE_BINDING_REQUEST;
375 :
376 0 : nr_stun_client_reset(pair->stun_client);
377 :
378 0 : if(r=nr_stun_client_start(pair->stun_client,mode,nr_ice_candidate_pair_stun_cb,pair))
379 0 : ABORT(r);
380 :
381 0 : 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 0 : assert(0);
386 : }
387 :
388 0 : _status=0;
389 : abort:
390 0 : if(_status){
391 : /* Don't fire the CB, but schedule it to fire ASAP */
392 0 : assert(!pair->stun_cb_timer);
393 0 : NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_stun_cb,pair, &pair->stun_cb_timer);
394 0 : _status=0;
395 : }
396 0 : }
397 :
398 0 : 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 0 : if(r=nr_ice_socket_register_stun_client(pair->local->isock,pair->stun_client,&pair->stun_client_handle))
404 0 : ABORT(r);
405 :
406 0 : nr_ice_candidate_pair_restart(pctx, pair);
407 :
408 0 : _status=0;
409 : abort:
410 0 : return(_status);
411 : }
412 :
413 0 : 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 0 : if(r=nr_ice_candidate_pair_create(pair->pctx, pair->local, pair->remote, ©))
419 0 : ABORT(r);
420 :
421 : /* Preserve nomination status */
422 0 : copy->peer_nominated= pair->peer_nominated;
423 0 : copy->nominated = pair->nominated;
424 :
425 0 : 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 0 : if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy))
427 0 : ABORT(r);
428 0 : nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,copy);
429 :
430 0 : copy->triggered = 1;
431 0 : nr_ice_candidate_pair_set_state(copy->pctx,copy,NR_ICE_PAIR_STATE_WAITING);
432 :
433 0 : _status=0;
434 : abort:
435 0 : return(_status);
436 : }
437 :
438 0 : 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 0 : if(pair->state==NR_ICE_PAIR_STATE_CANCELLED) {
443 0 : r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Ignoring matching but canceled pair",pctx->label,pair->codeword);
444 0 : return(0);
445 0 : } else if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) {
446 0 : r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for succeeded pair",pctx->label,pair->codeword);
447 0 : return(0);
448 : }
449 :
450 : /* Do not run this logic more than once on a given pair */
451 0 : if(!pair->triggered){
452 0 : 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 0 : pair->triggered=1;
455 :
456 0 : 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 0 : 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 0 : 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 0 : 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 0 : nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,pair);
469 0 : 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 0 : if(r=nr_ice_candidate_copy_for_triggered_check(pair))
477 0 : ABORT(r);
478 0 : nr_ice_candidate_pair_cancel(pair->pctx,pair,1);
479 0 : break;
480 : default:
481 : /* all states are handled - a new/unknown state should not
482 : * automatically enter the start_checks() below */
483 0 : 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 0 : if(r=nr_ice_media_stream_start_checks(pair->pctx,pair->remote->stream))
490 0 : ABORT(r);
491 : }
492 :
493 0 : _status=0;
494 : abort:
495 0 : return(_status);
496 : }
497 :
498 0 : int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state)
499 : {
500 0 : if(pair->state != NR_ICE_PAIR_STATE_FAILED){
501 : /* If it's already running we need to terminate the stun */
502 0 : if(pair->state==NR_ICE_PAIR_STATE_IN_PROGRESS){
503 0 : if(move_to_wait_state) {
504 0 : nr_stun_client_wait(pair->stun_client);
505 : } else {
506 0 : nr_stun_client_cancel(pair->stun_client);
507 : }
508 : }
509 0 : nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_CANCELLED);
510 : }
511 :
512 0 : return(0);
513 : }
514 :
515 0 : int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair)
516 : {
517 : int r,_status;
518 :
519 0 : if(!pair){
520 0 : r_log(LOG_ICE,LOG_ERR,"ICE-PAIR: No pair chosen");
521 0 : ABORT(R_BAD_ARGS);
522 : }
523 :
524 0 : if(pair->state!=NR_ICE_PAIR_STATE_SUCCEEDED){
525 0 : 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 0 : if(!pair->restart_nominated_cb_timer)
534 0 : 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 0 : if(r=nr_ice_component_nominated_pair(pair->remote->component, pair))
538 0 : ABORT(r);
539 : }
540 :
541 0 : _status=0;
542 : abort:
543 0 : return(_status);
544 : }
545 :
546 0 : 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 0 : r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): setting pair to state %s: %s",
551 0 : 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 0 : if(pair->state!=NR_ICE_PAIR_STATE_WAITING){
567 0 : if(state==NR_ICE_PAIR_STATE_WAITING)
568 0 : pctx->waiting_pairs++;
569 : }
570 : else{
571 0 : if(state!=NR_ICE_PAIR_STATE_WAITING)
572 0 : pctx->waiting_pairs--;
573 :
574 0 : assert(pctx->waiting_pairs>=0);
575 : }
576 0 : pair->state=state;
577 :
578 :
579 0 : if(pair->state==NR_ICE_PAIR_STATE_FAILED ||
580 0 : pair->state==NR_ICE_PAIR_STATE_CANCELLED){
581 0 : if(r=nr_ice_component_failed_pair(pair->remote->component, pair))
582 0 : ABORT(r);
583 : }
584 :
585 0 : _status=0;
586 : abort:
587 0 : return(_status);
588 : }
589 :
590 0 : 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 0 : return(0);
595 : }
596 :
597 :
598 0 : int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
599 : {
600 0 : if(pair->triggered_check_queue_entry.tqe_next ||
601 0 : pair->triggered_check_queue_entry.tqe_prev)
602 0 : return(0);
603 :
604 0 : TAILQ_INSERT_TAIL(head,pair,triggered_check_queue_entry);
605 :
606 0 : return(0);
607 : }
608 :
609 0 : 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 0 : c1=TAILQ_FIRST(head);
614 0 : while(c1){
615 0 : if(c1->priority < pair->priority){
616 0 : TAILQ_INSERT_BEFORE(c1,pair,check_queue_entry);
617 0 : break;
618 : }
619 :
620 0 : c1=TAILQ_NEXT(c1,check_queue_entry);
621 : }
622 0 : if(!c1) TAILQ_INSERT_TAIL(head,pair,check_queue_entry);
623 :
624 0 : return(0);
625 : }
626 :
627 0 : void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg)
628 : {
629 0 : nr_ice_cand_pair *pair=cb_arg;
630 : int r,_status;
631 :
632 0 : pair->restart_nominated_cb_timer=0;
633 :
634 0 : 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 0 : nr_stun_client_reset(pair->stun_client);
637 :
638 0 : if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair))
639 0 : ABORT(r);
640 :
641 0 : if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))
642 0 : ABORT(r);
643 :
644 0 : _status=0;
645 : abort:
646 0 : return;
647 : }
648 :
649 0 : static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg)
650 : {
651 0 : nr_ice_cand_pair *pair=cb_arg;
652 :
653 0 : pair->restart_role_change_cb_timer=0;
654 :
655 0 : 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 0 : nr_ice_candidate_pair_restart(pair->pctx, pair);
658 0 : }
659 :
660 0 : void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair)
661 : {
662 0 : pair->stun_client->params.ice_binding_request.control = pair->pctx->controlling ? NR_ICE_CONTROLLING : NR_ICE_CONTROLLED;
663 0 : nr_ice_candidate_pair_set_priority(pair);
664 :
665 0 : 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 0 : if(!pair->restart_role_change_cb_timer)
670 0 : NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_role_change_cb,pair,&pair->restart_role_change_cb_timer);
671 : }
672 0 : }
673 :
674 0 : 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 0 : snprintf(as_string,
680 : sizeof(as_string),
681 : "%s|%s(%s|%s)",
682 0 : lcand->addr.as_string,
683 0 : rcand->addr.as_string,
684 : lcand->label,
685 : rcand->label);
686 :
687 0 : nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword);
688 0 : }
689 :
|