diff -r 000000000000 -r 6474c204b198 media/mtransport/third_party/nICEr/src/ice/ice_component.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1115 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_component.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include +#include +#include +#include +#include +#include "ice_ctx.h" +#include "ice_codeword.h" +#include "stun.h" +#include "nr_socket_local.h" +#include "nr_socket_turn.h" +#include "nr_socket_buffered_stun.h" +#include "ice_reg.h" + +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); +static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp); + +/* This function takes ownership of the contents of req (but not req itself) */ +static int nr_ice_pre_answer_request_create(nr_socket *sock, nr_stun_server_request *req, nr_ice_pre_answer_request **parp) + { + int r, _status; + nr_ice_pre_answer_request *par = 0; + nr_stun_message_attribute *attr; + + if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request)))) + ABORT(R_NO_MEMORY); + + par->req = *req; /* Struct assignment */ + memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */ + + if (r=nr_socket_getaddr(sock, &par->local_addr)) + ABORT(r); + if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr)) + ABORT(R_INTERNAL); + if (!(par->username = r_strdup(attr->u.username))) + ABORT(R_NO_MEMORY); + + *parp=par; + _status=0; + abort: + if (_status) { + /* Erase the request so we don't free it */ + memset(&par->req, 0, sizeof(nr_stun_server_request)); + nr_ice_pre_answer_request_destroy(&par); + } + + return(_status); + } + +static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp) + { + nr_ice_pre_answer_request *par; + + if (!parp || !*parp) + return(0); + + par = *parp; + *parp = 0; + + nr_stun_message_destroy(&par->req.request); + nr_stun_message_destroy(&par->req.response); + + RFREE(par->username); + + return(0); + } + +int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp) + { + int _status; + nr_ice_component *comp=0; + + if(!(comp=RCALLOC(sizeof(nr_ice_component)))) + ABORT(R_NO_MEMORY); + + comp->state=NR_ICE_COMPONENT_UNPAIRED; + comp->component_id=component_id; + comp->stream=stream; + comp->ctx=stream->ctx; + + STAILQ_INIT(&comp->sockets); + TAILQ_INIT(&comp->candidates); + STAILQ_INIT(&comp->pre_answer_reqs); + + STAILQ_INSERT_TAIL(&stream->components,comp,entry); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_destroy(nr_ice_component **componentp) + { + nr_ice_component *component; + nr_ice_socket *s1,*s2; + nr_ice_candidate *c1,*c2; + nr_ice_pre_answer_request *r1,*r2; + + if(!componentp || !*componentp) + return(0); + + component=*componentp; + *componentp=0; + + /* Detach ourselves from the sockets */ + if (component->local_component){ + nr_ice_socket *isock=STAILQ_FIRST(&component->local_component->sockets); + while(isock){ + nr_stun_server_remove_client(isock->stun_server, component); + isock=STAILQ_NEXT(isock, entry); + } + } + + /* candidates MUST be destroyed before the sockets so that + they can deregister */ + TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){ + TAILQ_REMOVE(&component->candidates,c1,entry_comp); + nr_ice_candidate_destroy(&c1); + } + + STAILQ_FOREACH_SAFE(s1, &component->sockets, entry, s2){ + STAILQ_REMOVE(&component->sockets,s1,nr_ice_socket_,entry); + nr_ice_socket_destroy(&s1); + } + + STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){ + STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); + nr_ice_pre_answer_request_destroy(&r1); + } + + if(component->keepalive_timer) + NR_async_timer_cancel(component->keepalive_timer); + nr_stun_client_ctx_destroy(&component->keepalive_ctx); + + RFREE(component); + return(0); + } + +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) + { + nr_socket *sock; + nr_ice_socket *isock=0; + nr_ice_candidate *cand=0; + int i; + int j; + char label[256]; + int r,_status; + + /* Now one ice_socket for each address */ + for(i=0;ilabel,addrs[i].addr.as_string); + if(r=nr_socket_local_create(&addrs[i].addr,&sock)){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string); + continue; + } + + if(r=nr_ice_socket_create(ctx,component,sock,&isock)) + ABORT(r); + /* Create one host candidate */ + if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0, + component->component_id,&cand)) + ABORT(r); + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + + /* And a srvrflx candidate for each STUN server */ + for(j=0;jstun_server_ct;j++){ + if(r=nr_ice_candidate_create(ctx,component, + isock,sock,SERVER_REFLEXIVE, + &ctx->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + +#ifdef USE_TURN + /* And both a srvrflx and relayed candidate for each TURN server */ + for(j=0;jturn_server_ct;j++){ + nr_socket *turn_sock; + nr_ice_candidate *srvflx_cand; + + /* Skip non-UDP */ + if (ctx->turn_servers[j].transport != IPPROTO_UDP) + continue; + + /* srvrflx */ + if(r=nr_ice_candidate_create(ctx,component, + isock,sock,SERVER_REFLEXIVE, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */ + cand->done_cb=nr_ice_initialize_finished_cb; + cand->cb_arg=cand; + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + srvflx_cand=cand; + + /* relayed*/ + if(r=nr_socket_turn_create(sock, &turn_sock)) + ABORT(r); + if(r=nr_ice_candidate_create(ctx,component, + isock,turn_sock,RELAYED, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + cand->u.relayed.srvflx_candidate=srvflx_cand; + cand->u.relayed.server=&ctx->turn_servers[j]; + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + + cand=0; + } +#endif /* USE_TURN */ + + /* Create a STUN server context for this socket */ + snprintf(label, sizeof(label), "server(%s)", addrs[i].addr.as_string); + if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server)) + ABORT(r); + if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle)) + ABORT(r); + + /* Add the default STUN credentials so that we can respond before + we hear about the peer. */ + if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component)) + ABORT(r); + + STAILQ_INSERT_TAIL(&component->sockets,isock,entry); + } + + _status = 0; + abort: + return(_status); + } + +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) + { + nr_ice_socket *isock=0; + nr_ice_candidate *cand=0; + int i; + int j; + char label[256]; + int r,_status; + + /* Create a new relayed candidate for each addr/TURN server pair */ + for(i=0;iturn_server_ct;j++){ + nr_transport_addr addr; + nr_socket *sock; + nr_socket *buffered_sock; + nr_socket *turn_sock; + + /* Skip non-TCP */ + if (ctx->turn_servers[j].transport != IPPROTO_TCP) + continue; + + /* Create a local socket */ + if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr))) + ABORT(r); + addr.protocol = IPPROTO_TCP; + if ((r=nr_transport_addr_fmt_addr_string(&addr))) + ABORT(r); + if((r=nr_socket_local_create(&addr, &sock))){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string); + continue; + } + /* Wrap it */ + if((r=nr_socket_buffered_stun_create(sock, NR_STUN_MAX_MESSAGE_SIZE, &buffered_sock))) + ABORT(r); + + /* The TURN socket */ + if(r=nr_socket_turn_create(buffered_sock, &turn_sock)) + ABORT(r); + + /* Create an ICE socket */ + if((r=nr_ice_socket_create(ctx, component, buffered_sock, &isock))) + ABORT(r); + + /* Attach ourselves to it */ + if(r=nr_ice_candidate_create(ctx,component, + isock,turn_sock,RELAYED, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + cand->u.relayed.srvflx_candidate=NULL; + cand->u.relayed.server=&ctx->turn_servers[j]; + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + + /* Create a STUN server context for this socket */ + snprintf(label, sizeof(label), "server(%s)", addr.as_string); + if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server)) + ABORT(r); + if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle)) + ABORT(r); + + /* Add the default STUN credentials so that we can respond before + we hear about the peer.*/ + if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component)) + ABORT(r); + + STAILQ_INSERT_TAIL(&component->sockets,isock,entry); + } + } +#endif + + _status = 0; + abort: + return(_status); + } + + +/* Make all the candidates we can make at the beginning */ +int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component) + { + int r,_status; + nr_local_addr *addrs=ctx->local_addrs; + int addr_ct=ctx->local_addr_ct; + char *lufrag; + char *lpwd; + Data pwd; + nr_ice_candidate *cand; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id); + + if(addr_ct==0){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label); + ABORT(R_NOT_FOUND); + } + + /* Note: we need to recompute these because + we have not yet computed the values in the peer media stream.*/ + lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag; + assert(lufrag); + if (!lufrag) + ABORT(R_INTERNAL); + lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd; + assert(lpwd); + if (!lpwd) + ABORT(R_INTERNAL); + INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd)); + + /* Initialize the UDP candidates */ + if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd)) + ABORT(r); + /* And the TCP candidates */ + if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd)) + ABORT(r); + + /* count the candidates that will be initialized */ + cand=TAILQ_FIRST(&component->candidates); + if(!cand){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create any valid candidates",ctx->label); + ABORT(R_NOT_FOUND); + } + + while(cand){ + ctx->uninitialized_candidates++; + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* Now initialize all the candidates */ + cand=TAILQ_FIRST(&component->candidates); + while(cand){ + if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){ + if(r=nr_ice_candidate_initialize(cand,nr_ice_initialize_finished_cb,cand)){ + if(r!=R_WOULDBLOCK){ + ctx->uninitialized_candidates--; + cand->state=NR_ICE_CAND_STATE_FAILED; + } + } + } + cand=TAILQ_NEXT(cand,entry_comp); + } + _status=0; + abort: + return(_status); + } + +static int nr_ice_any_peer_paired(nr_ice_candidate* cand) { + nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers); + while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){ + /* Is it worth actually looking through the check lists? Probably not. */ + pctx=STAILQ_NEXT(pctx,entry); + } + return pctx != NULL; +} + +/* + Compare this newly initialized candidate against the other initialized + candidates and discard the lower-priority one if they are redundant. + + This algorithm combined with the other algorithms, favors + host > srflx > relay + */ +int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned) + { + nr_ice_candidate *c2, *tmp = NULL; + + *was_pruned = 0; + c2 = TAILQ_FIRST(&comp->candidates); + while(c2){ + if((c1 != c2) && + (c2->state == NR_ICE_CAND_STATE_INITIALIZED) && + !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) && + !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + + if((c1->type == c2->type) || + (c1->type==HOST && c2->type == SERVER_REFLEXIVE) || + (c2->type==HOST && c1->type == SERVER_REFLEXIVE)){ + + /* + These are redundant. Remove the lower pri one, or if pairing has + already occurred, remove the newest one. + + Since this algorithmis run whenever a new candidate + is initialized, there should at most one duplicate. + */ + if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) { + tmp = c1; + *was_pruned = 1; + } + else { + tmp = c2; + } + break; + } + } + + c2=TAILQ_NEXT(c2,entry_comp); + } + + if (tmp) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate", + ctx->label,tmp->label); + + TAILQ_REMOVE(&comp->candidates,tmp,entry_comp); + comp->candidate_ct--; + TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock); + + nr_ice_candidate_destroy(&tmp); + } + + return 0; + } + +/* Section 7.2.1 */ +static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error) + { + nr_ice_cand_pair *pair; + nr_ice_candidate *pcand=0; + nr_stun_message *sreq=req->request; + nr_stun_message_attribute *attr; + int component_id_matched; + int local_addr_matched; + int remote_addr_matched; + nr_ice_cand_pair *found_invalid=0; + int r=0,_status; + + 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); + + if (comp->state == NR_ICE_COMPONENT_DISABLED) + ABORT(R_REJECTED); + + /* Check for role conficts (7.2.1.1) */ + if(comp->stream->pctx->controlling){ + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){ + /* OK, there is a conflict. Who's right? */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlling",comp->stream->pctx->label); + + if(attr->u.ice_controlling > comp->stream->pctx->tiebreaker){ + /* They are: switch */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): switching to controlled",comp->stream->pctx->label); + + comp->stream->pctx->controlling=0; + } + else { + /* We are: throw an error */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); + + *error=487; + ABORT(R_REJECTED); + } + } + } + else{ + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLED,&attr)){ + /* OK, there is a conflict. Who's right? */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlled",comp->stream->pctx->label); + + if(attr->u.ice_controlling < comp->stream->pctx->tiebreaker){ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): switching to controlling",comp->stream->pctx->label); + + /* They are: switch */ + comp->stream->pctx->controlling=1; + } + else { + /* We are: throw an error */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); + + *error=487; + ABORT(R_REJECTED); + } + } + } + + 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); + + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + component_id_matched = 0; + local_addr_matched = 0; + remote_addr_matched = 0; + + if(pair->remote->component->component_id!=comp->component_id) + goto next_pair; + component_id_matched = 1; + + if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + goto next_pair; + local_addr_matched=1; + + + if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + goto next_pair; + remote_addr_matched = 1; + + if(pair->state==NR_ICE_PAIR_STATE_FAILED){ + found_invalid=pair; + goto next_pair; + } + + if (local_addr_matched && remote_addr_matched){ + 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); + break; /* OK, this is a known pair */ + } + + next_pair: + pair=TAILQ_NEXT(pair,entry); + } + + if(!pair){ + if(!found_invalid){ + /* First find our local component candidate */ + nr_ice_candidate *cand; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label); + cand=TAILQ_FIRST(&comp->local_component->candidates); + while(cand){ + if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* Well, this really shouldn't happen, but it's an error from the + other side, so we just throw an error and keep going */ + if(!cand){ + 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); + + *error=400; + ABORT(R_NOT_FOUND); + } + + /* We now need to make a peer reflexive */ + if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label); + *error=487; + ABORT(R_BAD_DATA); + } + pcand->priority=attr->u.priority; + pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + + if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand, + &pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + + if(r=nr_ice_component_insert_pair(comp,pair)) { + *error=(r==R_NO_MEMORY)?500:400; + nr_ice_candidate_pair_destroy(&pair); + ABORT(r); + } + + /* Do this last, since any call to ABORT will destroy pcand */ + TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp); + pcand=0; + } + else{ + /* OK, there was a pair, it's just invalid: According to Section + 7.2.1.4, we need to resurrect it + */ + if(found_invalid->state == NR_ICE_PAIR_STATE_FAILED){ + pair=found_invalid; + + 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); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_WAITING); + } + else{ + /* This shouldn't happen */ + 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); + *error=500; + ABORT(R_BAD_DATA); + } + } + } + + /* OK, we've got a pair to work with. Turn it on */ + assert(pair); + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){ + if(comp->stream->pctx->controlling){ + 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); + } + else{ + /* If this is the first time we've noticed this is nominated...*/ + pair->peer_nominated=1; + + if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){ + pair->nominated=1; + + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + } + } + } + + if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + + _status=0; + abort: + if(_status){ + nr_ice_candidate_destroy(&pcand); + assert(*error != 0); + if(r!=R_NO_MEMORY) assert(*error != 500); + } + return(_status); + } + +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) + { + nr_ice_component *comp=cb_arg; + nr_transport_addr local_addr; + int r,_status; + + /* Find the candidate pair that this maps to */ + if(r=nr_socket_getaddr(sock,&local_addr)) { + *error=500; + ABORT(r); + } + + if (r=nr_ice_component_process_incoming_check(comp, &local_addr, req, error)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced) + { + nr_ice_pre_answer_request *r1,*r2; + nr_ice_component *comp = pcomp->local_component; + int r,_status; + + if (serviced) + *serviced = 0; + + 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); + + STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) { + if (!strcmp(r1->username, username)) { + int error = 0; + + 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); + r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error); + if (r) { + 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); + } + (*serviced)++; + STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); + nr_ice_pre_answer_request_destroy(&r1); + } + } + + _status=0; + return(_status); + } + +int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote) + { + int r, _status; + nr_ice_candidate *pcand; + nr_ice_cand_pair *pair=0; + char codeword[5]; + + nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label); + + switch(lcand->type){ + case HOST: + break; + case SERVER_REFLEXIVE: + case PEER_REFLEXIVE: + /* Don't actually pair these candidates */ + goto done; + break; + case RELAYED: + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + pcand=TAILQ_FIRST(&pcomp->candidates); + while(pcand){ + /* + Two modes, depending on |pair_all_remote| + + 1. Pair remote candidates which have not been paired + (used in initial pairing or in processing the other side's + trickle candidates). + 2. Pair any remote candidate (used when processing our own + trickle candidates). + */ + if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) { + /* If we are pairing our own trickle candidates, the remote candidate should + all be paired */ + if (pair_all_remote) + assert (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_PAIRED); + + nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label); + + if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) + ABORT(r); + + if(r=nr_ice_component_insert_pair(pcomp, pair)) + ABORT(r); + } + + pcand=TAILQ_NEXT(pcand,entry_comp); + } + + done: + _status = 0; + abort: + return(_status); + } + +int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp) + { + nr_ice_candidate *lcand, *pcand; + nr_ice_socket *isock; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======"); + + /* Create the candidate pairs */ + lcand=TAILQ_FIRST(&lcomp->candidates); + while(lcand){ + if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) { + if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0))) + ABORT(r); + } + + lcand=TAILQ_NEXT(lcand,entry_comp); + } + + /* Mark all peer candidates as paired */ + pcand=TAILQ_FIRST(&pcomp->candidates); + while(pcand){ + pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + + pcand=TAILQ_NEXT(pcand,entry_comp); + + } + + /* Now register the STUN server callback for this component. + Note that this is a per-component CB so we only need to + do this once. + */ + if (pcomp->state != NR_ICE_COMPONENT_RUNNING) { + isock=STAILQ_FIRST(&lcomp->sockets); + while(isock){ + if(r=nr_stun_server_add_client(isock->stun_server,pctx->label, + pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) { + ABORT(r); + } + isock=STAILQ_NEXT(isock,entry); + } + } + + pcomp->state = NR_ICE_COMPONENT_RUNNING; + + _status=0; + abort: + return(_status); + } + +/* Fires when we have an incoming candidate that doesn't correspond to an existing + remote peer. This is either pre-answer or just spurious. Store it in the + component for use when we see the actual answer, at which point we need + to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb. + */ +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) + { + int r, _status; + nr_ice_component *comp = (nr_ice_component *)cb_arg; + nr_ice_pre_answer_request *par = 0; + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s", + comp->ctx->label, comp->stream->label, comp->component_id, req->src_addr.as_string); + + if (r=nr_ice_pre_answer_request_create(sock, req, &par)) + ABORT(r); + + *dont_free = 1; + STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry); + + _status=0; + abort: + return 0; + } + +int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + int r,_status; + int fire_cb=0; + nr_ice_cand_pair *p2; + + if(!comp->nominated) + fire_cb=1; + + /* Are we changing what the nominated pair is? */ + if(comp->nominated){ + if(comp->nominated->priority > pair->priority) + return(0); + 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); + } + + /* Set the new nominated pair */ + 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); + comp->state=NR_ICE_COMPONENT_NOMINATED; + comp->nominated=pair; + comp->active=pair; + + 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); + + /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */ + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if((p2 != pair) && + (p2->remote->component->component_id == comp->component_id) && + ((p2->state == NR_ICE_PAIR_STATE_FROZEN) || + (p2->state == NR_ICE_PAIR_STATE_WAITING))) { + 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); + + if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2)) + ABORT(r); + } + + p2=TAILQ_NEXT(p2,entry); + } + 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); + + if(r=nr_ice_media_stream_component_nominated(comp->stream,comp)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + int r,_status; + nr_ice_cand_pair *p2; + + assert(pair->state == NR_ICE_PAIR_STATE_FAILED); + + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if(comp->component_id==p2->local->component_id){ + switch(p2->state){ + case NR_ICE_PAIR_STATE_FROZEN: + case NR_ICE_PAIR_STATE_WAITING: + case NR_ICE_PAIR_STATE_IN_PROGRESS: + /* answer component status cannot be determined yet */ + goto done; + break; + case NR_ICE_PAIR_STATE_SUCCEEDED: + /* the component will succeed */ + goto done; + break; + case NR_ICE_PAIR_STATE_FAILED: + case NR_ICE_PAIR_STATE_CANCELLED: + /* states that will never be recovered from */ + break; + default: + assert(0); + break; + } + } + + p2=TAILQ_NEXT(p2,entry); + } + + /* all the pairs in the component are in their final states with + * none of them being SUCCEEDED, so the component fails entirely, + * tell the media stream that this component has failed */ + + if(r=nr_ice_media_stream_component_failed(comp->stream,comp)) + ABORT(r); + + done: + _status=0; + abort: + return(_status); + } + +int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp) + { + nr_ice_cand_pair **pairs=0; + int ct=0; + nr_ice_cand_pair *pair; + int r,_status; + + /* Size the array */ + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + if (comp->component_id == pair->local->component_id) + ct++; + + pair=TAILQ_NEXT(pair,entry); + } + + /* Make and fill the array */ + if(!(pairs=RCALLOC(sizeof(nr_ice_cand_pair *)*ct))) + ABORT(R_NO_MEMORY); + + ct=0; + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + if (comp->component_id == pair->local->component_id) + pairs[ct++]=pair; + + pair=TAILQ_NEXT(pair,entry); + } + + if (pctx->handler) { + if(r=pctx->handler->vtbl->select_pair(pctx->handler->obj, + comp->stream,comp->component_id,pairs,ct)) + ABORT(r); + } + + _status=0; + abort: + RFREE(pairs); + return(_status); + } + + +static void nr_ice_component_keepalive_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + UINT4 keepalive_timeout; + + assert(comp->keepalive_ctx); + + if(NR_reg_get_uint4(NR_ICE_REG_KEEPALIVE_TIMER,&keepalive_timeout)){ + keepalive_timeout=15000; /* Default */ + } + + + if(comp->keepalive_needed) + nr_stun_client_force_retransmit(comp->keepalive_ctx); + + comp->keepalive_needed=1; + NR_ASYNC_TIMER_SET(keepalive_timeout,nr_ice_component_keepalive_cb,cb_arg,&comp->keepalive_timer); + } + + +/* Close the underlying sockets for everything but the nominated candidate */ +int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp) + { + nr_ice_socket *isock=0; + int r,_status; + nr_ice_socket *s1,*s2; + + if(rcomp->state==NR_ICE_COMPONENT_NOMINATED){ + assert(rcomp->active == rcomp->nominated); + isock=rcomp->nominated->local->isock; + } + + STAILQ_FOREACH_SAFE(s1, &lcomp->sockets, entry, s2){ + if(s1!=isock){ + STAILQ_REMOVE(&lcomp->sockets,s1,nr_ice_socket_,entry); + nr_ice_socket_destroy(&s1); + } + } + + /* Set up the keepalives for the chosen socket */ + if(r=nr_stun_client_ctx_create("keepalive",rcomp->nominated->local->osock, + &rcomp->nominated->remote->addr,0,&rcomp->keepalive_ctx)) + ABORT(r); + if(r=nr_stun_client_start(rcomp->keepalive_ctx,NR_STUN_CLIENT_MODE_KEEPALIVE,0,0)) + ABORT(r); + nr_ice_component_keepalive_cb(0,0,rcomp); + + + _status=0; + abort: + + return(_status); + } + + +int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair) + { + int r,_status; + + /* Pairs for peer reflexive are marked SUCCEEDED immediately */ + if (pair->state != NR_ICE_PAIR_STATE_FROZEN && + pair->state != NR_ICE_PAIR_STATE_SUCCEEDED){ + assert(0); + ABORT(R_BAD_ARGS); + } + + if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair)) + ABORT(r); + + /* Make sure the check timer is running, if the stream was previously + * started. We will not start streams just because a pair was created. */ + if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE){ + if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) { + 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); + ABORT(R_INTERNAL); + } + } + + _status=0; + abort: + return(_status); + } +