media/mtransport/third_party/nICEr/src/ice/ice_component.c

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /*
michael@0 2 Copyright (c) 2007, Adobe Systems, Incorporated
michael@0 3 All rights reserved.
michael@0 4
michael@0 5 Redistribution and use in source and binary forms, with or without
michael@0 6 modification, are permitted provided that the following conditions are
michael@0 7 met:
michael@0 8
michael@0 9 * Redistributions of source code must retain the above copyright
michael@0 10 notice, this list of conditions and the following disclaimer.
michael@0 11
michael@0 12 * Redistributions in binary form must reproduce the above copyright
michael@0 13 notice, this list of conditions and the following disclaimer in the
michael@0 14 documentation and/or other materials provided with the distribution.
michael@0 15
michael@0 16 * Neither the name of Adobe Systems, Network Resonance nor the names of its
michael@0 17 contributors may be used to endorse or promote products derived from
michael@0 18 this software without specific prior written permission.
michael@0 19
michael@0 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 31 */
michael@0 32
michael@0 33
michael@0 34
michael@0 35 static char *RCSSTRING __UNUSED__="$Id: ice_component.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
michael@0 36
michael@0 37 #include <string.h>
michael@0 38 #include <assert.h>
michael@0 39 #include <nr_api.h>
michael@0 40 #include <registry.h>
michael@0 41 #include <async_timer.h>
michael@0 42 #include "ice_ctx.h"
michael@0 43 #include "ice_codeword.h"
michael@0 44 #include "stun.h"
michael@0 45 #include "nr_socket_local.h"
michael@0 46 #include "nr_socket_turn.h"
michael@0 47 #include "nr_socket_buffered_stun.h"
michael@0 48 #include "ice_reg.h"
michael@0 49
michael@0 50 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error);
michael@0 51 static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp);
michael@0 52
michael@0 53 /* This function takes ownership of the contents of req (but not req itself) */
michael@0 54 static int nr_ice_pre_answer_request_create(nr_socket *sock, nr_stun_server_request *req, nr_ice_pre_answer_request **parp)
michael@0 55 {
michael@0 56 int r, _status;
michael@0 57 nr_ice_pre_answer_request *par = 0;
michael@0 58 nr_stun_message_attribute *attr;
michael@0 59
michael@0 60 if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request))))
michael@0 61 ABORT(R_NO_MEMORY);
michael@0 62
michael@0 63 par->req = *req; /* Struct assignment */
michael@0 64 memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */
michael@0 65
michael@0 66 if (r=nr_socket_getaddr(sock, &par->local_addr))
michael@0 67 ABORT(r);
michael@0 68 if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr))
michael@0 69 ABORT(R_INTERNAL);
michael@0 70 if (!(par->username = r_strdup(attr->u.username)))
michael@0 71 ABORT(R_NO_MEMORY);
michael@0 72
michael@0 73 *parp=par;
michael@0 74 _status=0;
michael@0 75 abort:
michael@0 76 if (_status) {
michael@0 77 /* Erase the request so we don't free it */
michael@0 78 memset(&par->req, 0, sizeof(nr_stun_server_request));
michael@0 79 nr_ice_pre_answer_request_destroy(&par);
michael@0 80 }
michael@0 81
michael@0 82 return(_status);
michael@0 83 }
michael@0 84
michael@0 85 static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp)
michael@0 86 {
michael@0 87 nr_ice_pre_answer_request *par;
michael@0 88
michael@0 89 if (!parp || !*parp)
michael@0 90 return(0);
michael@0 91
michael@0 92 par = *parp;
michael@0 93 *parp = 0;
michael@0 94
michael@0 95 nr_stun_message_destroy(&par->req.request);
michael@0 96 nr_stun_message_destroy(&par->req.response);
michael@0 97
michael@0 98 RFREE(par->username);
michael@0 99
michael@0 100 return(0);
michael@0 101 }
michael@0 102
michael@0 103 int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp)
michael@0 104 {
michael@0 105 int _status;
michael@0 106 nr_ice_component *comp=0;
michael@0 107
michael@0 108 if(!(comp=RCALLOC(sizeof(nr_ice_component))))
michael@0 109 ABORT(R_NO_MEMORY);
michael@0 110
michael@0 111 comp->state=NR_ICE_COMPONENT_UNPAIRED;
michael@0 112 comp->component_id=component_id;
michael@0 113 comp->stream=stream;
michael@0 114 comp->ctx=stream->ctx;
michael@0 115
michael@0 116 STAILQ_INIT(&comp->sockets);
michael@0 117 TAILQ_INIT(&comp->candidates);
michael@0 118 STAILQ_INIT(&comp->pre_answer_reqs);
michael@0 119
michael@0 120 STAILQ_INSERT_TAIL(&stream->components,comp,entry);
michael@0 121
michael@0 122 _status=0;
michael@0 123 abort:
michael@0 124 return(_status);
michael@0 125 }
michael@0 126
michael@0 127 int nr_ice_component_destroy(nr_ice_component **componentp)
michael@0 128 {
michael@0 129 nr_ice_component *component;
michael@0 130 nr_ice_socket *s1,*s2;
michael@0 131 nr_ice_candidate *c1,*c2;
michael@0 132 nr_ice_pre_answer_request *r1,*r2;
michael@0 133
michael@0 134 if(!componentp || !*componentp)
michael@0 135 return(0);
michael@0 136
michael@0 137 component=*componentp;
michael@0 138 *componentp=0;
michael@0 139
michael@0 140 /* Detach ourselves from the sockets */
michael@0 141 if (component->local_component){
michael@0 142 nr_ice_socket *isock=STAILQ_FIRST(&component->local_component->sockets);
michael@0 143 while(isock){
michael@0 144 nr_stun_server_remove_client(isock->stun_server, component);
michael@0 145 isock=STAILQ_NEXT(isock, entry);
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 /* candidates MUST be destroyed before the sockets so that
michael@0 150 they can deregister */
michael@0 151 TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){
michael@0 152 TAILQ_REMOVE(&component->candidates,c1,entry_comp);
michael@0 153 nr_ice_candidate_destroy(&c1);
michael@0 154 }
michael@0 155
michael@0 156 STAILQ_FOREACH_SAFE(s1, &component->sockets, entry, s2){
michael@0 157 STAILQ_REMOVE(&component->sockets,s1,nr_ice_socket_,entry);
michael@0 158 nr_ice_socket_destroy(&s1);
michael@0 159 }
michael@0 160
michael@0 161 STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){
michael@0 162 STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
michael@0 163 nr_ice_pre_answer_request_destroy(&r1);
michael@0 164 }
michael@0 165
michael@0 166 if(component->keepalive_timer)
michael@0 167 NR_async_timer_cancel(component->keepalive_timer);
michael@0 168 nr_stun_client_ctx_destroy(&component->keepalive_ctx);
michael@0 169
michael@0 170 RFREE(component);
michael@0 171 return(0);
michael@0 172 }
michael@0 173
michael@0 174 static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
michael@0 175 {
michael@0 176 nr_socket *sock;
michael@0 177 nr_ice_socket *isock=0;
michael@0 178 nr_ice_candidate *cand=0;
michael@0 179 int i;
michael@0 180 int j;
michael@0 181 char label[256];
michael@0 182 int r,_status;
michael@0 183
michael@0 184 /* Now one ice_socket for each address */
michael@0 185 for(i=0;i<addr_ct;i++){
michael@0 186 char suppress;
michael@0 187
michael@0 188 if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
michael@0 189 if(r!=R_NOT_FOUND)
michael@0 190 ABORT(r);
michael@0 191 }
michael@0 192 else{
michael@0 193 if(suppress)
michael@0 194 continue;
michael@0 195 }
michael@0 196 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string);
michael@0 197 if(r=nr_socket_local_create(&addrs[i].addr,&sock)){
michael@0 198 r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string);
michael@0 199 continue;
michael@0 200 }
michael@0 201
michael@0 202 if(r=nr_ice_socket_create(ctx,component,sock,&isock))
michael@0 203 ABORT(r);
michael@0 204 /* Create one host candidate */
michael@0 205 if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,
michael@0 206 component->component_id,&cand))
michael@0 207 ABORT(r);
michael@0 208
michael@0 209 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
michael@0 210 component->candidate_ct++;
michael@0 211 cand=0;
michael@0 212
michael@0 213 /* And a srvrflx candidate for each STUN server */
michael@0 214 for(j=0;j<ctx->stun_server_ct;j++){
michael@0 215 if(r=nr_ice_candidate_create(ctx,component,
michael@0 216 isock,sock,SERVER_REFLEXIVE,
michael@0 217 &ctx->stun_servers[j],component->component_id,&cand))
michael@0 218 ABORT(r);
michael@0 219 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
michael@0 220 component->candidate_ct++;
michael@0 221 cand=0;
michael@0 222 }
michael@0 223
michael@0 224 #ifdef USE_TURN
michael@0 225 /* And both a srvrflx and relayed candidate for each TURN server */
michael@0 226 for(j=0;j<ctx->turn_server_ct;j++){
michael@0 227 nr_socket *turn_sock;
michael@0 228 nr_ice_candidate *srvflx_cand;
michael@0 229
michael@0 230 /* Skip non-UDP */
michael@0 231 if (ctx->turn_servers[j].transport != IPPROTO_UDP)
michael@0 232 continue;
michael@0 233
michael@0 234 /* srvrflx */
michael@0 235 if(r=nr_ice_candidate_create(ctx,component,
michael@0 236 isock,sock,SERVER_REFLEXIVE,
michael@0 237 &ctx->turn_servers[j].turn_server,component->component_id,&cand))
michael@0 238 ABORT(r);
michael@0 239 cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
michael@0 240 cand->done_cb=nr_ice_initialize_finished_cb;
michael@0 241 cand->cb_arg=cand;
michael@0 242
michael@0 243 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
michael@0 244 component->candidate_ct++;
michael@0 245 srvflx_cand=cand;
michael@0 246
michael@0 247 /* relayed*/
michael@0 248 if(r=nr_socket_turn_create(sock, &turn_sock))
michael@0 249 ABORT(r);
michael@0 250 if(r=nr_ice_candidate_create(ctx,component,
michael@0 251 isock,turn_sock,RELAYED,
michael@0 252 &ctx->turn_servers[j].turn_server,component->component_id,&cand))
michael@0 253 ABORT(r);
michael@0 254 cand->u.relayed.srvflx_candidate=srvflx_cand;
michael@0 255 cand->u.relayed.server=&ctx->turn_servers[j];
michael@0 256 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
michael@0 257 component->candidate_ct++;
michael@0 258
michael@0 259 cand=0;
michael@0 260 }
michael@0 261 #endif /* USE_TURN */
michael@0 262
michael@0 263 /* Create a STUN server context for this socket */
michael@0 264 snprintf(label, sizeof(label), "server(%s)", addrs[i].addr.as_string);
michael@0 265 if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
michael@0 266 ABORT(r);
michael@0 267 if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
michael@0 268 ABORT(r);
michael@0 269
michael@0 270 /* Add the default STUN credentials so that we can respond before
michael@0 271 we hear about the peer. */
michael@0 272 if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
michael@0 273 ABORT(r);
michael@0 274
michael@0 275 STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
michael@0 276 }
michael@0 277
michael@0 278 _status = 0;
michael@0 279 abort:
michael@0 280 return(_status);
michael@0 281 }
michael@0 282
michael@0 283 static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
michael@0 284 {
michael@0 285 nr_ice_socket *isock=0;
michael@0 286 nr_ice_candidate *cand=0;
michael@0 287 int i;
michael@0 288 int j;
michael@0 289 char label[256];
michael@0 290 int r,_status;
michael@0 291
michael@0 292 /* Create a new relayed candidate for each addr/TURN server pair */
michael@0 293 for(i=0;i<addr_ct;i++){
michael@0 294 char suppress;
michael@0 295
michael@0 296 if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
michael@0 297 if(r!=R_NOT_FOUND)
michael@0 298 ABORT(r);
michael@0 299 }
michael@0 300 else{
michael@0 301 if(suppress)
michael@0 302 continue;
michael@0 303 }
michael@0 304
michael@0 305 #ifdef USE_TURN
michael@0 306 for(j=0;j<ctx->turn_server_ct;j++){
michael@0 307 nr_transport_addr addr;
michael@0 308 nr_socket *sock;
michael@0 309 nr_socket *buffered_sock;
michael@0 310 nr_socket *turn_sock;
michael@0 311
michael@0 312 /* Skip non-TCP */
michael@0 313 if (ctx->turn_servers[j].transport != IPPROTO_TCP)
michael@0 314 continue;
michael@0 315
michael@0 316 /* Create a local socket */
michael@0 317 if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr)))
michael@0 318 ABORT(r);
michael@0 319 addr.protocol = IPPROTO_TCP;
michael@0 320 if ((r=nr_transport_addr_fmt_addr_string(&addr)))
michael@0 321 ABORT(r);
michael@0 322 if((r=nr_socket_local_create(&addr, &sock))){
michael@0 323 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string);
michael@0 324 continue;
michael@0 325 }
michael@0 326 /* Wrap it */
michael@0 327 if((r=nr_socket_buffered_stun_create(sock, NR_STUN_MAX_MESSAGE_SIZE, &buffered_sock)))
michael@0 328 ABORT(r);
michael@0 329
michael@0 330 /* The TURN socket */
michael@0 331 if(r=nr_socket_turn_create(buffered_sock, &turn_sock))
michael@0 332 ABORT(r);
michael@0 333
michael@0 334 /* Create an ICE socket */
michael@0 335 if((r=nr_ice_socket_create(ctx, component, buffered_sock, &isock)))
michael@0 336 ABORT(r);
michael@0 337
michael@0 338 /* Attach ourselves to it */
michael@0 339 if(r=nr_ice_candidate_create(ctx,component,
michael@0 340 isock,turn_sock,RELAYED,
michael@0 341 &ctx->turn_servers[j].turn_server,component->component_id,&cand))
michael@0 342 ABORT(r);
michael@0 343 cand->u.relayed.srvflx_candidate=NULL;
michael@0 344 cand->u.relayed.server=&ctx->turn_servers[j];
michael@0 345 TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
michael@0 346 component->candidate_ct++;
michael@0 347 cand=0;
michael@0 348
michael@0 349 /* Create a STUN server context for this socket */
michael@0 350 snprintf(label, sizeof(label), "server(%s)", addr.as_string);
michael@0 351 if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
michael@0 352 ABORT(r);
michael@0 353 if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
michael@0 354 ABORT(r);
michael@0 355
michael@0 356 /* Add the default STUN credentials so that we can respond before
michael@0 357 we hear about the peer.*/
michael@0 358 if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
michael@0 359 ABORT(r);
michael@0 360
michael@0 361 STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
michael@0 362 }
michael@0 363 }
michael@0 364 #endif
michael@0 365
michael@0 366 _status = 0;
michael@0 367 abort:
michael@0 368 return(_status);
michael@0 369 }
michael@0 370
michael@0 371
michael@0 372 /* Make all the candidates we can make at the beginning */
michael@0 373 int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component)
michael@0 374 {
michael@0 375 int r,_status;
michael@0 376 nr_local_addr *addrs=ctx->local_addrs;
michael@0 377 int addr_ct=ctx->local_addr_ct;
michael@0 378 char *lufrag;
michael@0 379 char *lpwd;
michael@0 380 Data pwd;
michael@0 381 nr_ice_candidate *cand;
michael@0 382
michael@0 383 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id);
michael@0 384
michael@0 385 if(addr_ct==0){
michael@0 386 r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label);
michael@0 387 ABORT(R_NOT_FOUND);
michael@0 388 }
michael@0 389
michael@0 390 /* Note: we need to recompute these because
michael@0 391 we have not yet computed the values in the peer media stream.*/
michael@0 392 lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
michael@0 393 assert(lufrag);
michael@0 394 if (!lufrag)
michael@0 395 ABORT(R_INTERNAL);
michael@0 396 lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
michael@0 397 assert(lpwd);
michael@0 398 if (!lpwd)
michael@0 399 ABORT(R_INTERNAL);
michael@0 400 INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
michael@0 401
michael@0 402 /* Initialize the UDP candidates */
michael@0 403 if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd))
michael@0 404 ABORT(r);
michael@0 405 /* And the TCP candidates */
michael@0 406 if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd))
michael@0 407 ABORT(r);
michael@0 408
michael@0 409 /* count the candidates that will be initialized */
michael@0 410 cand=TAILQ_FIRST(&component->candidates);
michael@0 411 if(!cand){
michael@0 412 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create any valid candidates",ctx->label);
michael@0 413 ABORT(R_NOT_FOUND);
michael@0 414 }
michael@0 415
michael@0 416 while(cand){
michael@0 417 ctx->uninitialized_candidates++;
michael@0 418 cand=TAILQ_NEXT(cand,entry_comp);
michael@0 419 }
michael@0 420
michael@0 421 /* Now initialize all the candidates */
michael@0 422 cand=TAILQ_FIRST(&component->candidates);
michael@0 423 while(cand){
michael@0 424 if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){
michael@0 425 if(r=nr_ice_candidate_initialize(cand,nr_ice_initialize_finished_cb,cand)){
michael@0 426 if(r!=R_WOULDBLOCK){
michael@0 427 ctx->uninitialized_candidates--;
michael@0 428 cand->state=NR_ICE_CAND_STATE_FAILED;
michael@0 429 }
michael@0 430 }
michael@0 431 }
michael@0 432 cand=TAILQ_NEXT(cand,entry_comp);
michael@0 433 }
michael@0 434 _status=0;
michael@0 435 abort:
michael@0 436 return(_status);
michael@0 437 }
michael@0 438
michael@0 439 static int nr_ice_any_peer_paired(nr_ice_candidate* cand) {
michael@0 440 nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers);
michael@0 441 while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){
michael@0 442 /* Is it worth actually looking through the check lists? Probably not. */
michael@0 443 pctx=STAILQ_NEXT(pctx,entry);
michael@0 444 }
michael@0 445 return pctx != NULL;
michael@0 446 }
michael@0 447
michael@0 448 /*
michael@0 449 Compare this newly initialized candidate against the other initialized
michael@0 450 candidates and discard the lower-priority one if they are redundant.
michael@0 451
michael@0 452 This algorithm combined with the other algorithms, favors
michael@0 453 host > srflx > relay
michael@0 454 */
michael@0 455 int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned)
michael@0 456 {
michael@0 457 nr_ice_candidate *c2, *tmp = NULL;
michael@0 458
michael@0 459 *was_pruned = 0;
michael@0 460 c2 = TAILQ_FIRST(&comp->candidates);
michael@0 461 while(c2){
michael@0 462 if((c1 != c2) &&
michael@0 463 (c2->state == NR_ICE_CAND_STATE_INITIALIZED) &&
michael@0 464 !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) &&
michael@0 465 !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
michael@0 466
michael@0 467 if((c1->type == c2->type) ||
michael@0 468 (c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
michael@0 469 (c2->type==HOST && c1->type == SERVER_REFLEXIVE)){
michael@0 470
michael@0 471 /*
michael@0 472 These are redundant. Remove the lower pri one, or if pairing has
michael@0 473 already occurred, remove the newest one.
michael@0 474
michael@0 475 Since this algorithmis run whenever a new candidate
michael@0 476 is initialized, there should at most one duplicate.
michael@0 477 */
michael@0 478 if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) {
michael@0 479 tmp = c1;
michael@0 480 *was_pruned = 1;
michael@0 481 }
michael@0 482 else {
michael@0 483 tmp = c2;
michael@0 484 }
michael@0 485 break;
michael@0 486 }
michael@0 487 }
michael@0 488
michael@0 489 c2=TAILQ_NEXT(c2,entry_comp);
michael@0 490 }
michael@0 491
michael@0 492 if (tmp) {
michael@0 493 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate",
michael@0 494 ctx->label,tmp->label);
michael@0 495
michael@0 496 TAILQ_REMOVE(&comp->candidates,tmp,entry_comp);
michael@0 497 comp->candidate_ct--;
michael@0 498 TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock);
michael@0 499
michael@0 500 nr_ice_candidate_destroy(&tmp);
michael@0 501 }
michael@0 502
michael@0 503 return 0;
michael@0 504 }
michael@0 505
michael@0 506 /* Section 7.2.1 */
michael@0 507 static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error)
michael@0 508 {
michael@0 509 nr_ice_cand_pair *pair;
michael@0 510 nr_ice_candidate *pcand=0;
michael@0 511 nr_stun_message *sreq=req->request;
michael@0 512 nr_stun_message_attribute *attr;
michael@0 513 int component_id_matched;
michael@0 514 int local_addr_matched;
michael@0 515 int remote_addr_matched;
michael@0 516 nr_ice_cand_pair *found_invalid=0;
michael@0 517 int r=0,_status;
michael@0 518
michael@0 519 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string);
michael@0 520
michael@0 521 if (comp->state == NR_ICE_COMPONENT_DISABLED)
michael@0 522 ABORT(R_REJECTED);
michael@0 523
michael@0 524 /* Check for role conficts (7.2.1.1) */
michael@0 525 if(comp->stream->pctx->controlling){
michael@0 526 if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){
michael@0 527 /* OK, there is a conflict. Who's right? */
michael@0 528 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlling",comp->stream->pctx->label);
michael@0 529
michael@0 530 if(attr->u.ice_controlling > comp->stream->pctx->tiebreaker){
michael@0 531 /* They are: switch */
michael@0 532 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): switching to controlled",comp->stream->pctx->label);
michael@0 533
michael@0 534 comp->stream->pctx->controlling=0;
michael@0 535 }
michael@0 536 else {
michael@0 537 /* We are: throw an error */
michael@0 538 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label);
michael@0 539
michael@0 540 *error=487;
michael@0 541 ABORT(R_REJECTED);
michael@0 542 }
michael@0 543 }
michael@0 544 }
michael@0 545 else{
michael@0 546 if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLED,&attr)){
michael@0 547 /* OK, there is a conflict. Who's right? */
michael@0 548 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlled",comp->stream->pctx->label);
michael@0 549
michael@0 550 if(attr->u.ice_controlling < comp->stream->pctx->tiebreaker){
michael@0 551 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): switching to controlling",comp->stream->pctx->label);
michael@0 552
michael@0 553 /* They are: switch */
michael@0 554 comp->stream->pctx->controlling=1;
michael@0 555 }
michael@0 556 else {
michael@0 557 /* We are: throw an error */
michael@0 558 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label);
michael@0 559
michael@0 560 *error=487;
michael@0 561 ABORT(R_REJECTED);
michael@0 562 }
michael@0 563 }
michael@0 564 }
michael@0 565
michael@0 566 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr->as_string);
michael@0 567
michael@0 568 pair=TAILQ_FIRST(&comp->stream->check_list);
michael@0 569 while(pair){
michael@0 570 component_id_matched = 0;
michael@0 571 local_addr_matched = 0;
michael@0 572 remote_addr_matched = 0;
michael@0 573
michael@0 574 if(pair->remote->component->component_id!=comp->component_id)
michael@0 575 goto next_pair;
michael@0 576 component_id_matched = 1;
michael@0 577
michael@0 578 if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
michael@0 579 goto next_pair;
michael@0 580 local_addr_matched=1;
michael@0 581
michael@0 582
michael@0 583 if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
michael@0 584 goto next_pair;
michael@0 585 remote_addr_matched = 1;
michael@0 586
michael@0 587 if(pair->state==NR_ICE_PAIR_STATE_FAILED){
michael@0 588 found_invalid=pair;
michael@0 589 goto next_pair;
michael@0 590 }
michael@0 591
michael@0 592 if (local_addr_matched && remote_addr_matched){
michael@0 593 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
michael@0 594 break; /* OK, this is a known pair */
michael@0 595 }
michael@0 596
michael@0 597 next_pair:
michael@0 598 pair=TAILQ_NEXT(pair,entry);
michael@0 599 }
michael@0 600
michael@0 601 if(!pair){
michael@0 602 if(!found_invalid){
michael@0 603 /* First find our local component candidate */
michael@0 604 nr_ice_candidate *cand;
michael@0 605
michael@0 606 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label);
michael@0 607 cand=TAILQ_FIRST(&comp->local_component->candidates);
michael@0 608 while(cand){
michael@0 609 if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
michael@0 610 break;
michael@0 611
michael@0 612 cand=TAILQ_NEXT(cand,entry_comp);
michael@0 613 }
michael@0 614
michael@0 615 /* Well, this really shouldn't happen, but it's an error from the
michael@0 616 other side, so we just throw an error and keep going */
michael@0 617 if(!cand){
michael@0 618 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string);
michael@0 619
michael@0 620 *error=400;
michael@0 621 ABORT(R_NOT_FOUND);
michael@0 622 }
michael@0 623
michael@0 624 /* We now need to make a peer reflexive */
michael@0 625 if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) {
michael@0 626 *error=(r==R_NO_MEMORY)?500:400;
michael@0 627 ABORT(r);
michael@0 628 }
michael@0 629 if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){
michael@0 630 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label);
michael@0 631 *error=487;
michael@0 632 ABORT(R_BAD_DATA);
michael@0 633 }
michael@0 634 pcand->priority=attr->u.priority;
michael@0 635 pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
michael@0 636
michael@0 637 if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand,
michael@0 638 &pair)) {
michael@0 639 *error=(r==R_NO_MEMORY)?500:400;
michael@0 640 ABORT(r);
michael@0 641 }
michael@0 642 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
michael@0 643
michael@0 644 if(r=nr_ice_component_insert_pair(comp,pair)) {
michael@0 645 *error=(r==R_NO_MEMORY)?500:400;
michael@0 646 nr_ice_candidate_pair_destroy(&pair);
michael@0 647 ABORT(r);
michael@0 648 }
michael@0 649
michael@0 650 /* Do this last, since any call to ABORT will destroy pcand */
michael@0 651 TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp);
michael@0 652 pcand=0;
michael@0 653 }
michael@0 654 else{
michael@0 655 /* OK, there was a pair, it's just invalid: According to Section
michael@0 656 7.2.1.4, we need to resurrect it
michael@0 657 */
michael@0 658 if(found_invalid->state == NR_ICE_PAIR_STATE_FAILED){
michael@0 659 pair=found_invalid;
michael@0 660
michael@0 661 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair, resurrecting: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
michael@0 662 nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_WAITING);
michael@0 663 }
michael@0 664 else{
michael@0 665 /* This shouldn't happen */
michael@0 666 r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on invalid pair that was not in state FAILED; this should not happen: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
michael@0 667 *error=500;
michael@0 668 ABORT(R_BAD_DATA);
michael@0 669 }
michael@0 670 }
michael@0 671 }
michael@0 672
michael@0 673 /* OK, we've got a pair to work with. Turn it on */
michael@0 674 assert(pair);
michael@0 675 if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){
michael@0 676 if(comp->stream->pctx->controlling){
michael@0 677 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword);
michael@0 678 }
michael@0 679 else{
michael@0 680 /* If this is the first time we've noticed this is nominated...*/
michael@0 681 pair->peer_nominated=1;
michael@0 682
michael@0 683 if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){
michael@0 684 pair->nominated=1;
michael@0 685
michael@0 686 if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) {
michael@0 687 *error=(r==R_NO_MEMORY)?500:400;
michael@0 688 ABORT(r);
michael@0 689 }
michael@0 690 }
michael@0 691 }
michael@0 692 }
michael@0 693
michael@0 694 if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) {
michael@0 695 *error=(r==R_NO_MEMORY)?500:400;
michael@0 696 ABORT(r);
michael@0 697 }
michael@0 698
michael@0 699 _status=0;
michael@0 700 abort:
michael@0 701 if(_status){
michael@0 702 nr_ice_candidate_destroy(&pcand);
michael@0 703 assert(*error != 0);
michael@0 704 if(r!=R_NO_MEMORY) assert(*error != 500);
michael@0 705 }
michael@0 706 return(_status);
michael@0 707 }
michael@0 708
michael@0 709 static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
michael@0 710 {
michael@0 711 nr_ice_component *comp=cb_arg;
michael@0 712 nr_transport_addr local_addr;
michael@0 713 int r,_status;
michael@0 714
michael@0 715 /* Find the candidate pair that this maps to */
michael@0 716 if(r=nr_socket_getaddr(sock,&local_addr)) {
michael@0 717 *error=500;
michael@0 718 ABORT(r);
michael@0 719 }
michael@0 720
michael@0 721 if (r=nr_ice_component_process_incoming_check(comp, &local_addr, req, error))
michael@0 722 ABORT(r);
michael@0 723
michael@0 724 _status=0;
michael@0 725 abort:
michael@0 726 return(_status);
michael@0 727 }
michael@0 728
michael@0 729 int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced)
michael@0 730 {
michael@0 731 nr_ice_pre_answer_request *r1,*r2;
michael@0 732 nr_ice_component *comp = pcomp->local_component;
michael@0 733 int r,_status;
michael@0 734
michael@0 735 if (serviced)
michael@0 736 *serviced = 0;
michael@0 737
michael@0 738 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id);
michael@0 739
michael@0 740 STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) {
michael@0 741 if (!strcmp(r1->username, username)) {
michael@0 742 int error = 0;
michael@0 743
michael@0 744 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id);
michael@0 745 r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error);
michael@0 746 if (r) {
michael@0 747 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error);
michael@0 748 }
michael@0 749 (*serviced)++;
michael@0 750 STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
michael@0 751 nr_ice_pre_answer_request_destroy(&r1);
michael@0 752 }
michael@0 753 }
michael@0 754
michael@0 755 _status=0;
michael@0 756 return(_status);
michael@0 757 }
michael@0 758
michael@0 759 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
michael@0 760 {
michael@0 761 int r, _status;
michael@0 762 nr_ice_candidate *pcand;
michael@0 763 nr_ice_cand_pair *pair=0;
michael@0 764 char codeword[5];
michael@0 765
michael@0 766 nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
michael@0 767 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label);
michael@0 768
michael@0 769 switch(lcand->type){
michael@0 770 case HOST:
michael@0 771 break;
michael@0 772 case SERVER_REFLEXIVE:
michael@0 773 case PEER_REFLEXIVE:
michael@0 774 /* Don't actually pair these candidates */
michael@0 775 goto done;
michael@0 776 break;
michael@0 777 case RELAYED:
michael@0 778 break;
michael@0 779 default:
michael@0 780 assert(0);
michael@0 781 ABORT(R_INTERNAL);
michael@0 782 break;
michael@0 783 }
michael@0 784
michael@0 785 pcand=TAILQ_FIRST(&pcomp->candidates);
michael@0 786 while(pcand){
michael@0 787 /*
michael@0 788 Two modes, depending on |pair_all_remote|
michael@0 789
michael@0 790 1. Pair remote candidates which have not been paired
michael@0 791 (used in initial pairing or in processing the other side's
michael@0 792 trickle candidates).
michael@0 793 2. Pair any remote candidate (used when processing our own
michael@0 794 trickle candidates).
michael@0 795 */
michael@0 796 if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) {
michael@0 797 /* If we are pairing our own trickle candidates, the remote candidate should
michael@0 798 all be paired */
michael@0 799 if (pair_all_remote)
michael@0 800 assert (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_PAIRED);
michael@0 801
michael@0 802 nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
michael@0 803 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label);
michael@0 804
michael@0 805 if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
michael@0 806 ABORT(r);
michael@0 807
michael@0 808 if(r=nr_ice_component_insert_pair(pcomp, pair))
michael@0 809 ABORT(r);
michael@0 810 }
michael@0 811
michael@0 812 pcand=TAILQ_NEXT(pcand,entry_comp);
michael@0 813 }
michael@0 814
michael@0 815 done:
michael@0 816 _status = 0;
michael@0 817 abort:
michael@0 818 return(_status);
michael@0 819 }
michael@0 820
michael@0 821 int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp)
michael@0 822 {
michael@0 823 nr_ice_candidate *lcand, *pcand;
michael@0 824 nr_ice_socket *isock;
michael@0 825 int r,_status;
michael@0 826
michael@0 827 r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======");
michael@0 828
michael@0 829 /* Create the candidate pairs */
michael@0 830 lcand=TAILQ_FIRST(&lcomp->candidates);
michael@0 831 while(lcand){
michael@0 832 if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) {
michael@0 833 if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0)))
michael@0 834 ABORT(r);
michael@0 835 }
michael@0 836
michael@0 837 lcand=TAILQ_NEXT(lcand,entry_comp);
michael@0 838 }
michael@0 839
michael@0 840 /* Mark all peer candidates as paired */
michael@0 841 pcand=TAILQ_FIRST(&pcomp->candidates);
michael@0 842 while(pcand){
michael@0 843 pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
michael@0 844
michael@0 845 pcand=TAILQ_NEXT(pcand,entry_comp);
michael@0 846
michael@0 847 }
michael@0 848
michael@0 849 /* Now register the STUN server callback for this component.
michael@0 850 Note that this is a per-component CB so we only need to
michael@0 851 do this once.
michael@0 852 */
michael@0 853 if (pcomp->state != NR_ICE_COMPONENT_RUNNING) {
michael@0 854 isock=STAILQ_FIRST(&lcomp->sockets);
michael@0 855 while(isock){
michael@0 856 if(r=nr_stun_server_add_client(isock->stun_server,pctx->label,
michael@0 857 pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) {
michael@0 858 ABORT(r);
michael@0 859 }
michael@0 860 isock=STAILQ_NEXT(isock,entry);
michael@0 861 }
michael@0 862 }
michael@0 863
michael@0 864 pcomp->state = NR_ICE_COMPONENT_RUNNING;
michael@0 865
michael@0 866 _status=0;
michael@0 867 abort:
michael@0 868 return(_status);
michael@0 869 }
michael@0 870
michael@0 871 /* Fires when we have an incoming candidate that doesn't correspond to an existing
michael@0 872 remote peer. This is either pre-answer or just spurious. Store it in the
michael@0 873 component for use when we see the actual answer, at which point we need
michael@0 874 to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb.
michael@0 875 */
michael@0 876 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
michael@0 877 {
michael@0 878 int r, _status;
michael@0 879 nr_ice_component *comp = (nr_ice_component *)cb_arg;
michael@0 880 nr_ice_pre_answer_request *par = 0;
michael@0 881 r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s",
michael@0 882 comp->ctx->label, comp->stream->label, comp->component_id, req->src_addr.as_string);
michael@0 883
michael@0 884 if (r=nr_ice_pre_answer_request_create(sock, req, &par))
michael@0 885 ABORT(r);
michael@0 886
michael@0 887 *dont_free = 1;
michael@0 888 STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry);
michael@0 889
michael@0 890 _status=0;
michael@0 891 abort:
michael@0 892 return 0;
michael@0 893 }
michael@0 894
michael@0 895 int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
michael@0 896 {
michael@0 897 int r,_status;
michael@0 898 int fire_cb=0;
michael@0 899 nr_ice_cand_pair *p2;
michael@0 900
michael@0 901 if(!comp->nominated)
michael@0 902 fire_cb=1;
michael@0 903
michael@0 904 /* Are we changing what the nominated pair is? */
michael@0 905 if(comp->nominated){
michael@0 906 if(comp->nominated->priority > pair->priority)
michael@0 907 return(0);
michael@0 908 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword);
michael@0 909 }
michael@0 910
michael@0 911 /* Set the new nominated pair */
michael@0 912 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
michael@0 913 comp->state=NR_ICE_COMPONENT_NOMINATED;
michael@0 914 comp->nominated=pair;
michael@0 915 comp->active=pair;
michael@0 916
michael@0 917 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
michael@0 918
michael@0 919 /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */
michael@0 920 p2=TAILQ_FIRST(&comp->stream->check_list);
michael@0 921 while(p2){
michael@0 922 if((p2 != pair) &&
michael@0 923 (p2->remote->component->component_id == comp->component_id) &&
michael@0 924 ((p2->state == NR_ICE_PAIR_STATE_FROZEN) ||
michael@0 925 (p2->state == NR_ICE_PAIR_STATE_WAITING))) {
michael@0 926 r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword);
michael@0 927
michael@0 928 if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2))
michael@0 929 ABORT(r);
michael@0 930 }
michael@0 931
michael@0 932 p2=TAILQ_NEXT(p2,entry);
michael@0 933 }
michael@0 934 r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id);
michael@0 935
michael@0 936 if(r=nr_ice_media_stream_component_nominated(comp->stream,comp))
michael@0 937 ABORT(r);
michael@0 938
michael@0 939 _status=0;
michael@0 940 abort:
michael@0 941 return(_status);
michael@0 942 }
michael@0 943
michael@0 944 int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
michael@0 945 {
michael@0 946 int r,_status;
michael@0 947 nr_ice_cand_pair *p2;
michael@0 948
michael@0 949 assert(pair->state == NR_ICE_PAIR_STATE_FAILED);
michael@0 950
michael@0 951 p2=TAILQ_FIRST(&comp->stream->check_list);
michael@0 952 while(p2){
michael@0 953 if(comp->component_id==p2->local->component_id){
michael@0 954 switch(p2->state){
michael@0 955 case NR_ICE_PAIR_STATE_FROZEN:
michael@0 956 case NR_ICE_PAIR_STATE_WAITING:
michael@0 957 case NR_ICE_PAIR_STATE_IN_PROGRESS:
michael@0 958 /* answer component status cannot be determined yet */
michael@0 959 goto done;
michael@0 960 break;
michael@0 961 case NR_ICE_PAIR_STATE_SUCCEEDED:
michael@0 962 /* the component will succeed */
michael@0 963 goto done;
michael@0 964 break;
michael@0 965 case NR_ICE_PAIR_STATE_FAILED:
michael@0 966 case NR_ICE_PAIR_STATE_CANCELLED:
michael@0 967 /* states that will never be recovered from */
michael@0 968 break;
michael@0 969 default:
michael@0 970 assert(0);
michael@0 971 break;
michael@0 972 }
michael@0 973 }
michael@0 974
michael@0 975 p2=TAILQ_NEXT(p2,entry);
michael@0 976 }
michael@0 977
michael@0 978 /* all the pairs in the component are in their final states with
michael@0 979 * none of them being SUCCEEDED, so the component fails entirely,
michael@0 980 * tell the media stream that this component has failed */
michael@0 981
michael@0 982 if(r=nr_ice_media_stream_component_failed(comp->stream,comp))
michael@0 983 ABORT(r);
michael@0 984
michael@0 985 done:
michael@0 986 _status=0;
michael@0 987 abort:
michael@0 988 return(_status);
michael@0 989 }
michael@0 990
michael@0 991 int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp)
michael@0 992 {
michael@0 993 nr_ice_cand_pair **pairs=0;
michael@0 994 int ct=0;
michael@0 995 nr_ice_cand_pair *pair;
michael@0 996 int r,_status;
michael@0 997
michael@0 998 /* Size the array */
michael@0 999 pair=TAILQ_FIRST(&comp->stream->check_list);
michael@0 1000 while(pair){
michael@0 1001 if (comp->component_id == pair->local->component_id)
michael@0 1002 ct++;
michael@0 1003
michael@0 1004 pair=TAILQ_NEXT(pair,entry);
michael@0 1005 }
michael@0 1006
michael@0 1007 /* Make and fill the array */
michael@0 1008 if(!(pairs=RCALLOC(sizeof(nr_ice_cand_pair *)*ct)))
michael@0 1009 ABORT(R_NO_MEMORY);
michael@0 1010
michael@0 1011 ct=0;
michael@0 1012 pair=TAILQ_FIRST(&comp->stream->check_list);
michael@0 1013 while(pair){
michael@0 1014 if (comp->component_id == pair->local->component_id)
michael@0 1015 pairs[ct++]=pair;
michael@0 1016
michael@0 1017 pair=TAILQ_NEXT(pair,entry);
michael@0 1018 }
michael@0 1019
michael@0 1020 if (pctx->handler) {
michael@0 1021 if(r=pctx->handler->vtbl->select_pair(pctx->handler->obj,
michael@0 1022 comp->stream,comp->component_id,pairs,ct))
michael@0 1023 ABORT(r);
michael@0 1024 }
michael@0 1025
michael@0 1026 _status=0;
michael@0 1027 abort:
michael@0 1028 RFREE(pairs);
michael@0 1029 return(_status);
michael@0 1030 }
michael@0 1031
michael@0 1032
michael@0 1033 static void nr_ice_component_keepalive_cb(NR_SOCKET s, int how, void *cb_arg)
michael@0 1034 {
michael@0 1035 nr_ice_component *comp=cb_arg;
michael@0 1036 UINT4 keepalive_timeout;
michael@0 1037
michael@0 1038 assert(comp->keepalive_ctx);
michael@0 1039
michael@0 1040 if(NR_reg_get_uint4(NR_ICE_REG_KEEPALIVE_TIMER,&keepalive_timeout)){
michael@0 1041 keepalive_timeout=15000; /* Default */
michael@0 1042 }
michael@0 1043
michael@0 1044
michael@0 1045 if(comp->keepalive_needed)
michael@0 1046 nr_stun_client_force_retransmit(comp->keepalive_ctx);
michael@0 1047
michael@0 1048 comp->keepalive_needed=1;
michael@0 1049 NR_ASYNC_TIMER_SET(keepalive_timeout,nr_ice_component_keepalive_cb,cb_arg,&comp->keepalive_timer);
michael@0 1050 }
michael@0 1051
michael@0 1052
michael@0 1053 /* Close the underlying sockets for everything but the nominated candidate */
michael@0 1054 int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp)
michael@0 1055 {
michael@0 1056 nr_ice_socket *isock=0;
michael@0 1057 int r,_status;
michael@0 1058 nr_ice_socket *s1,*s2;
michael@0 1059
michael@0 1060 if(rcomp->state==NR_ICE_COMPONENT_NOMINATED){
michael@0 1061 assert(rcomp->active == rcomp->nominated);
michael@0 1062 isock=rcomp->nominated->local->isock;
michael@0 1063 }
michael@0 1064
michael@0 1065 STAILQ_FOREACH_SAFE(s1, &lcomp->sockets, entry, s2){
michael@0 1066 if(s1!=isock){
michael@0 1067 STAILQ_REMOVE(&lcomp->sockets,s1,nr_ice_socket_,entry);
michael@0 1068 nr_ice_socket_destroy(&s1);
michael@0 1069 }
michael@0 1070 }
michael@0 1071
michael@0 1072 /* Set up the keepalives for the chosen socket */
michael@0 1073 if(r=nr_stun_client_ctx_create("keepalive",rcomp->nominated->local->osock,
michael@0 1074 &rcomp->nominated->remote->addr,0,&rcomp->keepalive_ctx))
michael@0 1075 ABORT(r);
michael@0 1076 if(r=nr_stun_client_start(rcomp->keepalive_ctx,NR_STUN_CLIENT_MODE_KEEPALIVE,0,0))
michael@0 1077 ABORT(r);
michael@0 1078 nr_ice_component_keepalive_cb(0,0,rcomp);
michael@0 1079
michael@0 1080
michael@0 1081 _status=0;
michael@0 1082 abort:
michael@0 1083
michael@0 1084 return(_status);
michael@0 1085 }
michael@0 1086
michael@0 1087
michael@0 1088 int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair)
michael@0 1089 {
michael@0 1090 int r,_status;
michael@0 1091
michael@0 1092 /* Pairs for peer reflexive are marked SUCCEEDED immediately */
michael@0 1093 if (pair->state != NR_ICE_PAIR_STATE_FROZEN &&
michael@0 1094 pair->state != NR_ICE_PAIR_STATE_SUCCEEDED){
michael@0 1095 assert(0);
michael@0 1096 ABORT(R_BAD_ARGS);
michael@0 1097 }
michael@0 1098
michael@0 1099 if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair))
michael@0 1100 ABORT(r);
michael@0 1101
michael@0 1102 /* Make sure the check timer is running, if the stream was previously
michael@0 1103 * started. We will not start streams just because a pair was created. */
michael@0 1104 if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE){
michael@0 1105 if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) {
michael@0 1106 r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string);
michael@0 1107 ABORT(R_INTERNAL);
michael@0 1108 }
michael@0 1109 }
michael@0 1110
michael@0 1111 _status=0;
michael@0 1112 abort:
michael@0 1113 return(_status);
michael@0 1114 }
michael@0 1115

mercurial