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