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_ctx.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: #ifdef WIN32 michael@0: #include michael@0: #else michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "stun.h" michael@0: #include "ice_ctx.h" michael@0: #include "ice_reg.h" michael@0: #include "nr_crypto.h" michael@0: #include "async_timer.h" michael@0: #include "util.h" michael@0: michael@0: michael@0: int LOG_ICE = 0; michael@0: michael@0: static int nr_ice_random_string(char *str, int len); michael@0: static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out); michael@0: #ifdef USE_TURN michael@0: static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out); michael@0: #endif /* USE_TURN */ michael@0: static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); michael@0: static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand); michael@0: michael@0: int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out) michael@0: { michael@0: int r,_status; michael@0: nr_ice_stun_server *servers = 0; michael@0: int i; michael@0: NR_registry child; michael@0: char *addr=0; michael@0: UINT2 port; michael@0: in_addr_t addr_int; michael@0: michael@0: if(!(servers=RCALLOC(sizeof(nr_ice_stun_server)*ct))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: for(i=0;istun_servers){ michael@0: RFREE(ctx->stun_servers); michael@0: ctx->stun_server_ct=0; michael@0: } michael@0: michael@0: if (ct) { michael@0: if(!(ctx->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*ct))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: memcpy(ctx->stun_servers,servers,sizeof(nr_ice_stun_server)*ct); michael@0: ctx->stun_server_ct = ct; michael@0: } michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers,int ct) michael@0: { michael@0: int _status; michael@0: michael@0: if(ctx->turn_servers){ michael@0: RFREE(ctx->turn_servers); michael@0: ctx->turn_server_ct=0; michael@0: } michael@0: michael@0: if(ct) { michael@0: if(!(ctx->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*ct))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: memcpy(ctx->turn_servers,servers,sizeof(nr_ice_turn_server)*ct); michael@0: ctx->turn_server_ct = ct; michael@0: } michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_ctx_set_local_addrs(nr_ice_ctx *ctx,nr_local_addr *addrs,int ct) michael@0: { michael@0: int _status,i,r; michael@0: michael@0: if(ctx->local_addrs) { michael@0: RFREE(ctx->local_addrs); michael@0: ctx->local_addr_ct=0; michael@0: ctx->local_addrs=0; michael@0: } michael@0: michael@0: if (ct) { michael@0: if(!(ctx->local_addrs=RCALLOC(sizeof(nr_local_addr)*ct))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: for (i=0;ilocal_addrs+i,addrs+i)) { michael@0: ABORT(r); michael@0: } michael@0: } michael@0: ctx->local_addr_ct = ct; michael@0: } michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver) michael@0: { michael@0: int _status; michael@0: michael@0: if (ctx->resolver) { michael@0: ABORT(R_ALREADY); michael@0: } michael@0: michael@0: ctx->resolver = resolver; michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *ip) michael@0: { michael@0: int _status; michael@0: michael@0: if (ctx->interface_prioritizer) { michael@0: ABORT(R_ALREADY); michael@0: } michael@0: michael@0: ctx->interface_prioritizer = ip; michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: #ifdef USE_TURN michael@0: int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out) michael@0: { michael@0: int r,_status; michael@0: nr_ice_turn_server *servers = 0; michael@0: int i; michael@0: NR_registry child; michael@0: char *addr=0; michael@0: UINT2 port; michael@0: in_addr_t addr_int; michael@0: Data data={0}; michael@0: michael@0: if(!(servers=RCALLOC(sizeof(nr_ice_turn_server)*ct))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: for(i=0;idata = data.data; michael@0: servers[i].password->len = data.len; michael@0: data.data=0; michael@0: } michael@0: michael@0: servers[i].turn_server.index=i; michael@0: michael@0: RFREE(addr); michael@0: addr=0; michael@0: } michael@0: michael@0: *out = servers; michael@0: michael@0: _status=0; michael@0: abort: michael@0: RFREE(data.data); michael@0: RFREE(addr); michael@0: if (_status) RFREE(servers); michael@0: return(_status); michael@0: } michael@0: #endif /* USE_TURN */ michael@0: michael@0: int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) michael@0: { michael@0: nr_ice_ctx *ctx=0; michael@0: int r,_status; michael@0: char buf[100]; michael@0: michael@0: if(r=r_log_register("ice", &LOG_ICE)) michael@0: ABORT(r); michael@0: michael@0: if(!(ctx=RCALLOC(sizeof(nr_ice_ctx)))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: ctx->flags=flags; michael@0: michael@0: if(!(ctx->label=r_strdup(label))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: if(r=nr_ice_random_string(buf,8)) michael@0: ABORT(r); michael@0: if(!(ctx->ufrag=r_strdup(buf))) michael@0: ABORT(r); michael@0: if(r=nr_ice_random_string(buf,32)) michael@0: ABORT(r); michael@0: if(!(ctx->pwd=r_strdup(buf))) michael@0: ABORT(r); michael@0: michael@0: /* Get the STUN servers */ michael@0: if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX, michael@0: (unsigned int *)&ctx->stun_server_ct)||ctx->stun_server_ct==0) { michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE(%s): No STUN servers specified", ctx->label); michael@0: ctx->stun_server_ct=0; michael@0: } michael@0: michael@0: /* 255 is the max for our priority algorithm */ michael@0: if(ctx->stun_server_ct>255){ michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN servers specified: max=255", ctx->label); michael@0: ctx->stun_server_ct=255; michael@0: } michael@0: michael@0: if(ctx->stun_server_ct>0){ michael@0: if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct,&ctx->stun_servers)){ michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load STUN servers from registry", ctx->label); michael@0: ctx->stun_server_ct=0; michael@0: ABORT(r); michael@0: } michael@0: } michael@0: michael@0: #ifdef USE_TURN michael@0: /* Get the TURN servers */ michael@0: if(r=NR_reg_get_child_count(NR_ICE_REG_TURN_SRV_PRFX, michael@0: (unsigned int *)&ctx->turn_server_ct)||ctx->turn_server_ct==0) { michael@0: r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): No TURN servers specified", ctx->label); michael@0: ctx->turn_server_ct=0; michael@0: } michael@0: #else michael@0: ctx->turn_server_ct=0; michael@0: #endif /* USE_TURN */ michael@0: michael@0: ctx->local_addrs=0; michael@0: ctx->local_addr_ct=0; michael@0: michael@0: /* 255 is the max for our priority algorithm */ michael@0: if((ctx->stun_server_ct+ctx->turn_server_ct)>255){ michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN/TURN servers specified: max=255", ctx->label); michael@0: ctx->turn_server_ct=255-ctx->stun_server_ct; michael@0: } michael@0: michael@0: #ifdef USE_TURN michael@0: if(ctx->turn_server_ct>0){ michael@0: if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct,&ctx->turn_servers)){ michael@0: ctx->turn_server_ct=0; michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load TURN servers from registry", ctx->label); michael@0: ABORT(r); michael@0: } michael@0: } michael@0: #endif /* USE_TURN */ michael@0: michael@0: michael@0: ctx->Ta = 20; michael@0: michael@0: STAILQ_INIT(&ctx->streams); michael@0: STAILQ_INIT(&ctx->sockets); michael@0: STAILQ_INIT(&ctx->foundations); michael@0: STAILQ_INIT(&ctx->peers); michael@0: STAILQ_INIT(&ctx->ids); michael@0: michael@0: *ctxp=ctx; michael@0: michael@0: _status=0; michael@0: abort: michael@0: if(_status) michael@0: nr_ice_ctx_destroy_cb(0,0,ctx); michael@0: michael@0: return(_status); michael@0: } michael@0: michael@0: static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) michael@0: { michael@0: nr_ice_ctx *ctx=cb_arg; michael@0: nr_ice_foundation *f1,*f2; michael@0: nr_ice_media_stream *s1,*s2; michael@0: int i; michael@0: nr_ice_stun_id *id1,*id2; michael@0: michael@0: RFREE(ctx->label); michael@0: michael@0: RFREE(ctx->stun_servers); michael@0: michael@0: RFREE(ctx->local_addrs); michael@0: michael@0: for (i = 0; i < ctx->turn_server_ct; i++) { michael@0: RFREE(ctx->turn_servers[i].username); michael@0: r_data_destroy(&ctx->turn_servers[i].password); michael@0: } michael@0: RFREE(ctx->turn_servers); michael@0: michael@0: f1=STAILQ_FIRST(&ctx->foundations); michael@0: while(f1){ michael@0: f2=STAILQ_NEXT(f1,entry); michael@0: RFREE(f1); michael@0: f1=f2; michael@0: } michael@0: RFREE(ctx->pwd); michael@0: RFREE(ctx->ufrag); michael@0: michael@0: STAILQ_FOREACH_SAFE(s1, &ctx->streams, entry, s2){ michael@0: STAILQ_REMOVE(&ctx->streams,s1,nr_ice_media_stream_,entry); michael@0: nr_ice_media_stream_destroy(&s1); michael@0: } michael@0: michael@0: STAILQ_FOREACH_SAFE(id1, &ctx->ids, entry, id2){ michael@0: STAILQ_REMOVE(&ctx->ids,id1,nr_ice_stun_id_,entry); michael@0: RFREE(id1); michael@0: } michael@0: michael@0: nr_resolver_destroy(&ctx->resolver); michael@0: nr_interface_prioritizer_destroy(&ctx->interface_prioritizer); michael@0: michael@0: RFREE(ctx); michael@0: } michael@0: michael@0: int nr_ice_ctx_destroy(nr_ice_ctx **ctxp) michael@0: { michael@0: if(!ctxp || !*ctxp) michael@0: return(0); michael@0: michael@0: (*ctxp)->done_cb=0; michael@0: (*ctxp)->trickle_cb=0; michael@0: michael@0: NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp); michael@0: michael@0: *ctxp=0; michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg) michael@0: { michael@0: int r,_status; michael@0: nr_ice_candidate *cand=cb_arg; michael@0: nr_ice_ctx *ctx; michael@0: michael@0: michael@0: assert(cb_arg); michael@0: if (!cb_arg) michael@0: return; michael@0: ctx = cand->ctx; michael@0: michael@0: ctx->uninitialized_candidates--; michael@0: michael@0: if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) { michael@0: int was_pruned = 0; michael@0: michael@0: if (r=nr_ice_component_maybe_prune_candidate(ctx, cand->component, michael@0: cand, &was_pruned)) { michael@0: r_log(LOG_ICE, LOG_NOTICE, "ICE(%s): Problem pruning candidates",ctx->label); michael@0: } michael@0: michael@0: /* If we are initialized, the candidate wasn't pruned, michael@0: and we have a trickle ICE callback fire the callback */ michael@0: if (ctx->trickle_cb && !was_pruned) { michael@0: ctx->trickle_cb(ctx->trickle_cb_arg, ctx, cand->stream, cand->component_id, cand); michael@0: michael@0: if (r=nr_ice_ctx_pair_new_trickle_candidates(ctx, cand)) { michael@0: r_log(LOG_ICE,LOG_ERR, "ICE(%s): All could not pair new trickle candidate",ctx->label); michael@0: /* But continue */ michael@0: } michael@0: } michael@0: } michael@0: michael@0: if(ctx->uninitialized_candidates==0){ michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label); michael@0: ctx->state=NR_ICE_STATE_INITIALIZED; michael@0: if (ctx->done_cb) { michael@0: ctx->done_cb(0,0,ctx->cb_arg); michael@0: } michael@0: else { michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No done_cb. We were probably destroyed.",ctx->label); michael@0: } michael@0: } michael@0: else { michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Waiting for %d candidates to be initialized",ctx->label, ctx->uninitialized_candidates); michael@0: } michael@0: } michael@0: michael@0: static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand) michael@0: { michael@0: int r,_status; michael@0: nr_ice_peer_ctx *pctx; michael@0: michael@0: pctx=STAILQ_FIRST(&ctx->peers); michael@0: while(pctx){ michael@0: if (pctx->state == NR_ICE_PEER_STATE_PAIRED) { michael@0: r = nr_ice_peer_ctx_pair_new_trickle_candidate(ctx, pctx, cand); michael@0: if (r) michael@0: ABORT(r); michael@0: } michael@0: michael@0: pctx=STAILQ_NEXT(pctx,entry); michael@0: } michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: michael@0: #define MAXADDRS 100 // Ridiculously high michael@0: int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg) michael@0: { michael@0: int r,_status; michael@0: nr_ice_media_stream *stream; michael@0: nr_local_addr addrs[MAXADDRS]; michael@0: int i,addr_ct; michael@0: michael@0: r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label); michael@0: ctx->state=NR_ICE_STATE_INITIALIZING; michael@0: ctx->done_cb=done_cb; michael@0: ctx->cb_arg=cb_arg; michael@0: michael@0: if(STAILQ_EMPTY(&ctx->streams)) { michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): Missing streams to initialize",ctx->label); michael@0: ABORT(R_BAD_ARGS); michael@0: } michael@0: michael@0: /* First, gather all the local addresses we have */ michael@0: if(r=nr_stun_find_local_addresses(addrs,MAXADDRS,&addr_ct)) { michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to find local addresses",ctx->label); michael@0: ABORT(r); michael@0: } michael@0: michael@0: /* Sort interfaces by preference */ michael@0: if(ctx->interface_prioritizer) { michael@0: for(i=0;iinterface_prioritizer,addrs+i)) { michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to add interface ",ctx->label); michael@0: ABORT(r); michael@0: } michael@0: } michael@0: if(r=nr_interface_prioritizer_sort_preference(ctx->interface_prioritizer)) { michael@0: r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to sort interface by preference",ctx->label); michael@0: ABORT(r); michael@0: } michael@0: } michael@0: michael@0: if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) { michael@0: ABORT(r); michael@0: } michael@0: michael@0: /* Initialize all the media stream/component pairs */ michael@0: stream=STAILQ_FIRST(&ctx->streams); michael@0: while(stream){ michael@0: if(r=nr_ice_media_stream_initialize(ctx,stream)) michael@0: ABORT(r); michael@0: michael@0: stream=STAILQ_NEXT(stream,entry); michael@0: } michael@0: michael@0: if(ctx->uninitialized_candidates) michael@0: ABORT(R_WOULDBLOCK); michael@0: michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) michael@0: { michael@0: int r,_status; michael@0: michael@0: if(r=nr_ice_media_stream_create(ctx,label,components,streamp)) michael@0: ABORT(r); michael@0: michael@0: STAILQ_INSERT_TAIL(&ctx->streams,*streamp,entry); michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp) michael@0: { michael@0: char **attrs=0; michael@0: int _status; michael@0: char *tmp=0; michael@0: michael@0: if(!(attrs=RCALLOC(sizeof(char *)*2))) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: if(!(tmp=RMALLOC(100))) michael@0: ABORT(R_NO_MEMORY); michael@0: snprintf(tmp,100,"ice-ufrag:%s",ctx->ufrag); michael@0: attrs[0]=tmp; michael@0: michael@0: if(!(tmp=RMALLOC(100))) michael@0: ABORT(R_NO_MEMORY); michael@0: snprintf(tmp,100,"ice-pwd:%s",ctx->pwd); michael@0: attrs[1]=tmp; michael@0: michael@0: *attrctp=2; michael@0: *attrsp=attrs; michael@0: michael@0: _status=0; michael@0: abort: michael@0: if (_status){ michael@0: if (attrs){ michael@0: RFREE(attrs[0]); michael@0: RFREE(attrs[1]); michael@0: } michael@0: RFREE(attrs); michael@0: } michael@0: return(_status); michael@0: } michael@0: michael@0: static int nr_ice_random_string(char *str, int len) michael@0: { michael@0: unsigned char bytes[100]; michael@0: int needed; michael@0: int r,_status; michael@0: michael@0: if(len%2) ABORT(R_BAD_ARGS); michael@0: needed=len/2; michael@0: michael@0: if(needed>sizeof(bytes)) ABORT(R_BAD_ARGS); michael@0: michael@0: //memset(bytes,0,needed); michael@0: michael@0: if(r=nr_crypto_random_bytes(bytes,needed)) michael@0: ABORT(r); michael@0: michael@0: if(r=nr_bin2hex(bytes,needed,(unsigned char *)str)) michael@0: ABORT(r); michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: /* This is incredibly annoying: we now have a datagram but we don't michael@0: know which peer it's from, and we need to be able to tell the michael@0: API user. So, offer it to each peer and if one bites, assume michael@0: the others don't want it michael@0: */ michael@0: int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len) michael@0: { michael@0: nr_ice_peer_ctx *pctx; michael@0: int r; michael@0: michael@0: pctx=STAILQ_FIRST(&ctx->peers); michael@0: while(pctx){ michael@0: r=nr_ice_peer_ctx_deliver_packet_maybe(pctx, comp, source_addr, data, len); michael@0: if(!r) michael@0: break; michael@0: michael@0: pctx=STAILQ_NEXT(pctx,entry); michael@0: } michael@0: michael@0: if(!pctx) michael@0: r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Packet received from %s which doesn't match any known peer",ctx->label,source_addr->as_string); michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]) michael@0: { michael@0: nr_ice_stun_id *xid; michael@0: michael@0: xid=STAILQ_FIRST(&ctx->ids); michael@0: while(xid){ michael@0: if (!memcmp(xid->id, id, 12)) michael@0: return 1; michael@0: michael@0: xid=STAILQ_NEXT(xid,entry); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg) michael@0: { michael@0: int _status; michael@0: nr_ice_stun_id *xid; michael@0: michael@0: xid = RCALLOC(sizeof(*xid)); michael@0: if (!xid) michael@0: ABORT(R_NO_MEMORY); michael@0: michael@0: assert(sizeof(xid->id) == sizeof(msg->header.id)); michael@0: #if __STDC_VERSION__ >= 201112L michael@0: _Static_assert(sizeof(xid->id) == sizeof(msg->header.id),"Message ID Size Mismatch"); michael@0: #endif michael@0: memcpy(xid->id, &msg->header.id, sizeof(xid->id)); michael@0: michael@0: STAILQ_INSERT_TAIL(&ctx->ids,xid,entry); michael@0: michael@0: _status=0; michael@0: abort: michael@0: return(_status); michael@0: } michael@0: michael@0: michael@0: /* Clean up some of the resources (mostly file descriptors) used michael@0: by candidates we didn't choose. Note that this still leaves michael@0: a fair amount of non-system stuff floating around. This gets michael@0: cleaned up when you destroy the ICE ctx */ michael@0: int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx) michael@0: { michael@0: nr_ice_media_stream *lstr,*rstr; michael@0: michael@0: r_log(LOG_ICE,LOG_DEBUG,"Finalizing ICE ctx %s, peer=%s",ctx->label,pctx->label); michael@0: /* michael@0: First find the peer stream, if any michael@0: */ michael@0: lstr=STAILQ_FIRST(&ctx->streams); michael@0: while(lstr){ michael@0: rstr=STAILQ_FIRST(&pctx->peer_streams); michael@0: michael@0: while(rstr){ michael@0: if(rstr->local_stream==lstr) michael@0: break; michael@0: michael@0: rstr=STAILQ_NEXT(rstr,entry); michael@0: } michael@0: michael@0: nr_ice_media_stream_finalize(lstr,rstr); michael@0: michael@0: lstr=STAILQ_NEXT(lstr,entry); michael@0: } michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: michael@0: int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg) michael@0: { michael@0: ctx->trickle_cb = cb; michael@0: ctx->trickle_cb_arg = cb_arg; michael@0: michael@0: return 0; michael@0: }