From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../third_party/nICEr/src/ice/ice_candidate.c | 1052 ++++++++++++++++++++ 1 file changed, 1052 insertions(+) create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c (limited to 'dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c') diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c new file mode 100644 index 0000000000..b7cf2c9a99 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c @@ -0,0 +1,1052 @@ +/* +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. +*/ + +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif +#include "nr_api.h" +#include "registry.h" +#include "nr_socket.h" +#include "async_timer.h" + +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "turn_client_ctx.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +#include "ice_codeword.h" +#include "ice_reg.h" +#include "ice_util.h" +#include "nr_socket_turn.h" +#include "nr_socket.h" +#include "nr_socket_multi_tcp.h" + +static int next_automatic_preference = 127; + +static int nr_ice_candidate_initialize2(nr_ice_candidate *cand); +static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand); +static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand); +static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg); +#ifdef USE_TURN +static int nr_ice_start_relay_turn(nr_ice_candidate *cand); +static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg); +static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr); +#endif /* USE_TURN */ + +void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand) + { + char as_string[1024]; + + snprintf(as_string, + sizeof(as_string), + "%s(%s)", + cand->addr.as_string, + cand->label); + + nr_ice_compute_codeword(as_string,strlen(as_string),cand->codeword); + } + +char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0}; +char *nr_ice_candidate_tcp_type_names[]={0,"active","passive","so",0}; + +static const char *nr_ctype_name(nr_ice_candidate_type ctype) { + assert(ctype0); + if (ctype <= 0 || ctype >= CTYPE_MAX) { + return "ERROR"; + } + return nr_ice_candidate_type_names[ctype]; +} + +static const char *nr_tcp_type_name(nr_socket_tcp_type tcp_type) { + assert(tcp_type0); + if (tcp_type <= 0 || tcp_type >= TCP_TYPE_MAX) { + return "ERROR"; + } + return nr_ice_candidate_tcp_type_names[tcp_type]; +} + +static int nr_ice_candidate_format_stun_label(char *label, size_t size, nr_ice_candidate *cand) + { + *label = 0; + snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type), + cand->base.as_string, cand->stun_server->addr.as_string); + + return (0); + } + +int nr_ice_candidate_create(nr_ice_ctx *ctx,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp) + { + assert(!(comp->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || ctype == RELAYED); + nr_ice_candidate *cand=0; + nr_ice_candidate *tmp=0; + int r,_status; + char label[512]; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + cand->state=NR_ICE_CAND_STATE_CREATED; + cand->ctx=ctx; + cand->isock=isock; + cand->osock=osock; + cand->type=ctype; + cand->tcp_type=tcp_type; + cand->stun_server=stun_server; + cand->component_id=component_id; + cand->component=comp; + cand->stream=comp->stream; + + /* Extract the addr as the base */ + if(r=nr_socket_getaddr(cand->isock->sock,&cand->base)) + ABORT(r); + + switch(ctype) { + case HOST: + snprintf(label, sizeof(label), "host(%s)", cand->base.as_string); + break; + + case SERVER_REFLEXIVE: + if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand)) + ABORT(r); + break; + + case RELAYED: + if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand)) + ABORT(r); + break; + + case PEER_REFLEXIVE: + snprintf(label, sizeof(label), "prflx"); + break; + + default: + assert(0); /* Can't happen */ + ABORT(R_BAD_ARGS); + } + + if (tcp_type) { + const char* ttype = nr_tcp_type_name(tcp_type); + const int tlen = strlen(ttype)+1; /* plus space */ + const size_t llen=strlen(label); + if (snprintf(label+llen, sizeof(label)-llen, " %s", ttype) != tlen) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): truncated tcp type added to buffer", + ctx->label); + } + } + + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + if(r=nr_ice_get_foundation(ctx,cand)) + ABORT(r); + if(r=nr_ice_candidate_compute_priority(cand)) + ABORT(r); + + TAILQ_FOREACH(tmp,&isock->candidates,entry_sock){ + if(cand->priority==tmp->priority){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): duplicate priority %u candidate %s and candidate %s", + ctx->label,cand->priority,cand->label,tmp->label); + } + } + + if(ctype==RELAYED) + cand->u.relayed.turn_sock=osock; + + + /* Add the candidate to the isock list*/ + TAILQ_INSERT_TAIL(&isock->candidates,cand,entry_sock); + + nr_ice_candidate_compute_codeword(cand); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): created candidate %s with type %s", + ctx->label,cand->codeword,cand->label,nr_ctype_name(ctype)); + + *candp=cand; + + _status=0; + abort: + if (_status){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to create candidate of type %s", ctx->label,nr_ctype_name(ctype)); + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + + +/* Create a peer reflexive candidate */ +int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + nr_ice_candidate_type ctype=PEER_REFLEXIVE; + int r,_status; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + cand->state=NR_ICE_CAND_STATE_INITIALIZED; + cand->ctx=ctx; + cand->type=ctype; + cand->component_id=comp->component_id; + cand->component=comp; + cand->stream=comp->stream; + + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): creating candidate with type %s", + ctx->label,label,nr_ctype_name(ctype)); + + if(r=nr_transport_addr_copy(&cand->base,addr)) + ABORT(r); + if(r=nr_transport_addr_copy(&cand->addr,addr)) + ABORT(r); + /* Bogus foundation */ + if(!(cand->foundation=r_strdup(cand->addr.as_string))) + ABORT(R_NO_MEMORY); + + nr_ice_candidate_compute_codeword(cand); + + *candp=cand; + + _status=0; + abort: + if (_status){ + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + +static void nr_ice_candidate_mark_done(nr_ice_candidate *cand, int state) + { + if (!cand) { + assert(0); + return; + } + + /* If this is a relay candidate, there's likely to be a srflx that is + * piggybacking on it. Make sure it is marked done too. */ + if ((cand->type == RELAYED) && cand->u.relayed.srvflx_candidate) { + nr_ice_candidate *srflx=cand->u.relayed.srvflx_candidate; + if (state == NR_ICE_CAND_STATE_INITIALIZED && + nr_turn_client_get_mapped_address(cand->u.relayed.turn, + &srflx->addr)) { + r_log(LOG_ICE, LOG_WARNING, "ICE(%s)/CAND(%s): Failed to get mapped address from TURN allocate response, srflx failed.", cand->ctx->label, cand->label); + nr_ice_candidate_mark_done(srflx, NR_ICE_CAND_STATE_FAILED); + } else { + nr_ice_candidate_mark_done(srflx, state); + } + } + + NR_async_cb done_cb=cand->done_cb; + cand->done_cb=0; + cand->state=state; + /* This might destroy cand! */ + if (done_cb) { + done_cb(0,0,cand->cb_arg); + } + } + +int nr_ice_candidate_destroy(nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + + if(!candp || !*candp) + return(0); + + cand=*candp; + + nr_ice_candidate_stop_gathering(cand); + + switch(cand->type){ + case HOST: + break; +#ifdef USE_TURN + case RELAYED: + // record stats back to the ice ctx on destruction + if (cand->u.relayed.turn) { + nr_accumulate_count(&(cand->ctx->stats.turn_401s), cand->u.relayed.turn->cnt_401s); + nr_accumulate_count(&(cand->ctx->stats.turn_403s), cand->u.relayed.turn->cnt_403s); + nr_accumulate_count(&(cand->ctx->stats.turn_438s), cand->u.relayed.turn->cnt_438s); + + nr_turn_stun_ctx* stun_ctx; + stun_ctx = STAILQ_FIRST(&cand->u.relayed.turn->stun_ctxs); + while (stun_ctx) { + nr_accumulate_count(&(cand->ctx->stats.stun_retransmits), stun_ctx->stun->retransmit_ct); + + stun_ctx = STAILQ_NEXT(stun_ctx, entry); + } + } + if (cand->u.relayed.turn_handle) + nr_ice_socket_deregister(cand->isock, cand->u.relayed.turn_handle); + if (cand->u.relayed.srvflx_candidate) + cand->u.relayed.srvflx_candidate->u.srvrflx.relay_candidate=0; + nr_turn_client_ctx_destroy(&cand->u.relayed.turn); + nr_socket_destroy(&cand->u.relayed.turn_sock); + break; +#endif /* USE_TURN */ + case SERVER_REFLEXIVE: + if (cand->u.srvrflx.stun_handle) + nr_ice_socket_deregister(cand->isock, cand->u.srvrflx.stun_handle); + if (cand->u.srvrflx.relay_candidate) + cand->u.srvrflx.relay_candidate->u.relayed.srvflx_candidate=0; + nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); + break; + default: + break; + } + + RFREE(cand->mdns_addr); + RFREE(cand->foundation); + RFREE(cand->label); + RFREE(cand); + + return(0); + } + +void nr_ice_candidate_stop_gathering(nr_ice_candidate *cand) + { + if (cand->state == NR_ICE_CAND_STATE_INITIALIZING) { + /* Make sure the ICE ctx isn't still waiting around for this candidate + * to init. */ + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + + NR_async_timer_cancel(cand->delay_timer); + cand->delay_timer=0; + NR_async_timer_cancel(cand->ready_cb_timer); + cand->ready_cb_timer=0; + + if(cand->resolver_handle){ + nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle); + cand->resolver_handle=0; + } + } + +/* This algorithm is not super-fast, but I don't think we need a hash + table just yet and it produces a small foundation string */ +static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) + { + nr_ice_foundation *foundation; + int i=0; + char fnd[20]; + int _status; + + foundation=STAILQ_FIRST(&ctx->foundations); + while(foundation){ + if(nr_transport_addr_cmp(&cand->base,&foundation->addr,NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + goto next; + // cast necessary because there is no guarantee that enum is signed. + // foundation->type should probably match nr_ice_candidate_type + if((int)cand->type != foundation->type) + goto next; + if(cand->type == SERVER_REFLEXIVE || cand->type == RELAYED) { + if(nr_transport_addr_cmp(&cand->stun_server->addr, &foundation->stun_server_addr, NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + goto next; + } + + snprintf(fnd,sizeof(fnd),"%d",i); + if(!(cand->foundation=r_strdup(fnd))) + ABORT(R_NO_MEMORY); + return(0); + + next: + foundation=STAILQ_NEXT(foundation,entry); + i++; + } + + if(!(foundation=RCALLOC(sizeof(nr_ice_foundation)))) + ABORT(R_NO_MEMORY); + nr_transport_addr_copy(&foundation->addr,&cand->base); + foundation->type=cand->type; + if(cand->type == SERVER_REFLEXIVE || cand->type == RELAYED) { + nr_transport_addr_copy(&foundation->stun_server_addr, &cand->stun_server->addr); + } + STAILQ_INSERT_TAIL(&ctx->foundations,foundation,entry); + + snprintf(fnd,sizeof(fnd),"%d",i); + if(!(cand->foundation=r_strdup(fnd))) + ABORT(R_NO_MEMORY); + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_compute_priority(nr_ice_candidate *cand) + { + UCHAR type_preference; + UCHAR interface_preference; + UCHAR stun_priority; + UCHAR direction_priority=0; + int r,_status; + + if (cand->base.protocol != IPPROTO_UDP && cand->base.protocol != IPPROTO_TCP){ + r_log(LOG_ICE,LOG_ERR,"Unknown protocol type %u", + (unsigned int)cand->base.protocol); + ABORT(R_INTERNAL); + } + + switch(cand->type){ + case HOST: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST_TCP,&type_preference)) + ABORT(r); + } + stun_priority=0; + break; + case RELAYED: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED_TCP,&type_preference)) + ABORT(r); + } + stun_priority=31-cand->stun_server->id; + break; + case SERVER_REFLEXIVE: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP,&type_preference)) + ABORT(r); + } + stun_priority=31-cand->stun_server->id; + break; + case PEER_REFLEXIVE: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP,&type_preference)) + ABORT(r); + } + stun_priority=0; + break; + default: + ABORT(R_INTERNAL); + } + + if(cand->base.protocol == IPPROTO_TCP){ + switch (cand->tcp_type) { + case TCP_TYPE_ACTIVE: + if (cand->type == HOST) + direction_priority=6; + else + direction_priority=4; + break; + case TCP_TYPE_PASSIVE: + if (cand->type == HOST) + direction_priority=4; + else + direction_priority=2; + break; + case TCP_TYPE_SO: + if (cand->type == HOST) + direction_priority=2; + else + direction_priority=6; + break; + case TCP_TYPE_NONE: + break; + case TCP_TYPE_MAX: + default: + assert(0); + ABORT(R_INTERNAL); + } + } + + if(type_preference > 126) + r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference); + + if(!cand->ctx->interface_prioritizer) { + /* Prioritizer is not set, read from registry */ + if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname, + &interface_preference)) { + if (r==R_NOT_FOUND) { + if (next_automatic_preference == 1) { + r_log(LOG_ICE,LOG_ERR,"Out of preference values. Can't assign one for interface %s",cand->base.ifname); + ABORT(R_NOT_FOUND); + } + r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname, + next_automatic_preference); + if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){ + ABORT(r); + } + interface_preference=next_automatic_preference << 1; + next_automatic_preference--; + if (cand->base.ip_version == NR_IPV6) { + /* Prefer IPV6 over IPV4 on the same interface. */ + interface_preference += 1; + } + } + else { + ABORT(r); + } + } + } + else { + char key_of_interface[MAXIFNAME + 41]; + nr_transport_addr addr; + + if(r=nr_socket_getaddr(cand->isock->sock, &addr)) + ABORT(r); + + if(r=nr_transport_addr_fmt_ifname_addr_string(&addr,key_of_interface, + sizeof(key_of_interface))) { + ABORT(r); + } + if(r=nr_interface_prioritizer_get_priority(cand->ctx->interface_prioritizer, + key_of_interface,&interface_preference)) { + ABORT(r); + } + } + + assert(stun_priority < 32); + assert(direction_priority < 8); + + cand->priority= + (type_preference << 24) | + (interface_preference << 16) | + (direction_priority << 13) | + (stun_priority << 8) | + (256 - cand->component_id); + + /* S 4.1.2 */ + assert(cand->priority>=1&&cand->priority<=2147483647); + + _status=0; + abort: + return(_status); + } + +static void nr_ice_candidate_fire_ready_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand = cb_arg; + + cand->ready_cb_timer = 0; + cand->ready_cb(0, 0, cand->ready_cb_arg); + } + +static int nr_ice_candidate_use_nr_resolver(nr_transport_addr *addr) + { + if(addr->fqdn[0] == 0) { + return 0; + } + + char use = 0; + if(addr->protocol == IPPROTO_UDP) { + NR_reg_get_char(NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP, &use); + } else if(addr->protocol == IPPROTO_TCP) { + NR_reg_get_char(NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP, &use); + } else { + assert(0); + } + + return use; + } + +int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg) + { + int r,_status; + int protocol=NR_RESOLVE_PROTOCOL_STUN; + cand->done_cb=ready_cb; + cand->cb_arg=cb_arg; + cand->state=NR_ICE_CAND_STATE_INITIALIZING; + + switch(cand->type){ + case HOST: + if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr)) + ABORT(r); + cand->osock=cand->isock->sock; + // Post this so that it doesn't happen in-line + cand->ready_cb = ready_cb; + cand->ready_cb_arg = cb_arg; + NR_ASYNC_TIMER_SET(0, nr_ice_candidate_fire_ready_cb, (void *)cand, &cand->ready_cb_timer); + break; +#ifdef USE_TURN + case RELAYED: + protocol=NR_RESOLVE_PROTOCOL_TURN; + /* Fall through */ +#endif + case SERVER_REFLEXIVE: + if (nr_transport_addr_cmp(&cand->base, &cand->stun_server->addr, + NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL)) { + r_log(LOG_ICE, LOG_INFO, + "ICE-CANDIDATE(%s): Skipping srflx/relayed candidate because " + "of IP version/transport mis-match with STUN/TURN server " + "(%u/%s - %u/%s).", + cand->label, cand->base.ip_version, + cand->base.protocol == IPPROTO_UDP ? "UDP" : "TCP", + cand->stun_server->addr.ip_version, + cand->stun_server->addr.protocol == IPPROTO_UDP ? "UDP" + : "TCP"); + ABORT(R_NOT_FOUND); /* Same error code when DNS lookup fails */ + } + + if(nr_ice_candidate_use_nr_resolver(&cand->stun_server->addr)) { + r_log( + LOG_ICE, LOG_DEBUG, + "ICE-CANDIDATE(%s): Starting DNS resolution (%u/%s - %u/%s).", + cand->label, cand->base.ip_version, + cand->base.protocol == IPPROTO_UDP ? "UDP" : "TCP", + cand->stun_server->addr.ip_version, + cand->stun_server->addr.protocol == IPPROTO_UDP ? "UDP" : "TCP"); + nr_resolver_resource resource; + int port; + resource.domain_name = cand->stun_server->addr.fqdn; + if (r = nr_transport_addr_get_port(&cand->stun_server->addr, &port)) { + ABORT(r); + } + resource.port = (uint16_t)port; + resource.stun_turn=protocol; + resource.transport_protocol = cand->stun_server->addr.protocol; + + switch (cand->base.ip_version) { + case NR_IPV4: + resource.address_family=AF_INET; + break; + case NR_IPV6: + resource.address_family=AF_INET6; + break; + default: + assert(0); + ABORT(R_BAD_ARGS); + } + + /* Try to resolve */ + if(!cand->ctx->resolver) { + r_log(LOG_ICE, LOG_ERR, "ICE-CANDIDATE(%s): Can't use DNS names without a resolver", cand->label); + ABORT(R_BAD_ARGS); + } + + if(r=nr_resolver_resolve(cand->ctx->resolver, + &resource, + nr_ice_candidate_resolved_cb, + (void *)cand, + &cand->resolver_handle)){ + r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not invoke DNS resolver",cand->label); + ABORT(r); + } + } else { + /* No nr_resolver for this, just copy the address and finish init */ + if (r = nr_transport_addr_copy(&cand->stun_server_addr, + &cand->stun_server->addr)) { + r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not copy STUN server addr", cand->label); + ABORT(r); + } + + if(r=nr_ice_candidate_initialize2(cand)) + ABORT(r); + } + break; + default: + ABORT(R_INTERNAL); + } + + nr_ice_candidate_compute_codeword(cand); + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + return(_status); + } + + +static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->resolver_handle=0; + + if(addr){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): resolved candidate %s. addr=%s", + cand->ctx->label,cand->label,addr->as_string); + } + else { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to resolve candidate %s.", + cand->ctx->label,cand->label); + ABORT(R_NOT_FOUND); + } + + if (nr_transport_addr_check_compatibility(addr, &cand->base)) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Skipping STUN server because of link local mis-match for candidate %s",cand->ctx->label,cand->label); + ABORT(R_NOT_FOUND); + } + + /* Copy the address */ + if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr)) + ABORT(r); + + if (cand->tcp_type == TCP_TYPE_PASSIVE || cand->tcp_type == TCP_TYPE_SO){ + if (r=nr_socket_multi_tcp_stun_server_connect(cand->osock, addr)) + ABORT(r); + } + + /* Now start initializing */ + if(r=nr_ice_candidate_initialize2(cand)) + ABORT(r); + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) { + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + return(_status); + } + +static int nr_ice_candidate_initialize2(nr_ice_candidate *cand) + { + int r,_status; + + switch(cand->type){ + case HOST: + assert(0); /* Can't happen */ + ABORT(R_INTERNAL); + break; +#ifdef USE_TURN + case RELAYED: + if(r=nr_ice_start_relay_turn(cand)) + ABORT(r); + ABORT(R_WOULDBLOCK); + break; +#endif /* USE_TURN */ + case SERVER_REFLEXIVE: + /* Need to start stun */ + if(r=nr_ice_srvrflx_start_stun(cand)) + ABORT(r); + cand->osock=cand->isock->sock; + ABORT(R_WOULDBLOCK); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + +/* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart + * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the + * TODO: server may not have understood the 0.96-style request */ + if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH, nr_ice_srvrflx_stun_finished_cb, cand)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(cand->ctx, cand->u.srvrflx.stun->request)) + ABORT(r); + + if(r=nr_ice_socket_register_stun_client(cand->isock,cand->u.srvrflx.stun,&cand->u.srvrflx.stun_handle)) + ABORT(r); + + _status=0; + abort: + if (_status && (cand->u.srvrflx.stun->state==NR_STUN_CLIENT_STATE_RUNNING)) { + nr_stun_client_failed(cand->u.srvrflx.stun); + } + return; + } + +static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand) + { + int r,_status; + + assert(!cand->delay_timer); + if(r=nr_stun_client_ctx_create(cand->label, cand->isock->sock, + &cand->stun_server_addr, cand->stream->ctx->gather_rto, + &cand->u.srvrflx.stun)) + ABORT(r); + + NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_srvrflx_start_stun_timer_cb,cand,&cand->delay_timer); + cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; + + _status=0; + abort: + return(_status); + } + +#ifdef USE_TURN +static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + + if(r=nr_turn_client_allocate(cand->u.relayed.turn, nr_ice_turn_allocated_cb, cb_arg)) + ABORT(r); + + if(r=nr_ice_socket_register_turn_client(cand->isock, cand->u.relayed.turn, + cand->osock, &cand->u.relayed.turn_handle)) + ABORT(r); + + _status=0; + abort: + if(_status && (cand->u.relayed.turn->state==NR_TURN_CLIENT_STATE_ALLOCATING)){ + nr_turn_client_failed(cand->u.relayed.turn); + } + return; + } + +static int nr_ice_start_relay_turn(nr_ice_candidate *cand) + { + int r,_status; + assert(!cand->delay_timer); + if(r=nr_turn_client_ctx_create(cand->label, cand->isock->sock, + cand->u.relayed.server->username, + cand->u.relayed.server->password, + &cand->stun_server_addr, + cand->component->ctx, + &cand->u.relayed.turn)) + ABORT(r); + + if(r=nr_socket_turn_set_ctx(cand->osock, cand->u.relayed.turn)) + ABORT(r); + + NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_start_relay_turn_timer_cb,cand,&cand->delay_timer); + cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; + + _status=0; + abort: + return(_status); + } +#endif /* USE_TURN */ + +static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg) + { + int _status; + nr_ice_candidate *cand=cb_arg; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): %s",cand->ctx->label,cand->label,__FUNCTION__); + + /* Deregister to suppress duplicates */ + if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle); + cand->u.srvrflx.stun_handle=0; + } + + switch(cand->u.srvrflx.stun->state){ + /* OK, we should have a mapped address */ + case NR_STUN_CLIENT_STATE_DONE: + /* Copy the address */ + nr_transport_addr_copy(&cand->addr, &cand->u.srvrflx.stun->results.stun_binding_response.mapped_addr); + cand->addr.protocol=cand->base.protocol; + nr_transport_addr_fmt_addr_string(&cand->addr); + nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED); + cand=0; + break; + + /* This failed, so go to the next STUN server if there is one */ + case NR_STUN_CLIENT_STATE_FAILED: + ABORT(R_NOT_FOUND); + break; + default: + ABORT(R_INTERNAL); + } + _status = 0; + abort: + if(_status){ + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + } + +#ifdef USE_TURN +static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_candidate *cand=cb_arg; + nr_turn_client_ctx *turn=cand->u.relayed.turn; + char *label; + nr_transport_addr relay_addr; + + switch(turn->state){ + /* OK, we should have a mapped address */ + case NR_TURN_CLIENT_STATE_ALLOCATED: + if (r=nr_turn_client_get_relayed_address(turn, &relay_addr)) + ABORT(r); + + if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|", + relay_addr.as_string,")",NULL)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"TURN-CLIENT(%s)/CAND(%s): Switching from TURN to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label); + + /* Copy the relayed address into the candidate addr and + into the candidate base. Note that we need to keep the + ifname in the base. */ + if (r=nr_transport_addr_copy(&cand->addr, &relay_addr)) + ABORT(r); + if (r=nr_transport_addr_copy_keep_ifname(&cand->base, &relay_addr)) /* Need to keep interface for priority calculation */ + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): new relay base=%s addr=%s", cand->ctx->label, cand->label, cand->base.as_string, cand->addr.as_string); + + RFREE(cand->label); + cand->label=label; + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED); + cand = 0; + + break; + + case NR_TURN_CLIENT_STATE_FAILED: + case NR_TURN_CLIENT_STATE_CANCELLED: + r_log(NR_LOG_TURN, LOG_WARNING, + "ICE-CANDIDATE(%s): nr_turn_allocated_cb called with state %d", + cand->label, turn->state); + /* This failed, so go to the next TURN server if there is one */ + ABORT(R_NOT_FOUND); + break; + default: + assert(0); /* should never happen */ + ABORT(R_INTERNAL); + } + + _status=0; + abort: + if(_status){ + if (cand) { + r_log(NR_LOG_TURN, LOG_WARNING, + "ICE-CANDIDATE(%s): nr_turn_allocated_cb failed", cand->label); + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + } + } +#endif /* USE_TURN */ + +/* Format the candidate attribute as per ICE S 15.1 */ +int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen, int obfuscate_srflx_addr) + { + int r,_status; + char addr[64]; + int port; + int len; + nr_transport_addr *raddr; + + assert(!strcmp(nr_ice_candidate_type_names[HOST], "host")); + assert(!strcmp(nr_ice_candidate_type_names[RELAYED], "relay")); + + if (cand->mdns_addr) { + /* mdns_addr is NSID_LENGTH which is 39, - 2 for removing the "{" and "}" + + 6 for ".local" for a total of 43. */ + strncpy(addr, cand->mdns_addr, sizeof(addr) - 1); + } else { + if(r=nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr))) + ABORT(r); + } + if(r=nr_transport_addr_get_port(&cand->addr,&port)) + ABORT(r); + /* https://tools.ietf.org/html/rfc6544#section-4.5 */ + if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type==TCP_TYPE_ACTIVE) + port=9; + snprintf(attr,maxlen,"candidate:%s %d %s %u %s %d typ %s", + cand->foundation, cand->component_id, cand->addr.protocol==IPPROTO_UDP?"UDP":"TCP",cand->priority, addr, port, + nr_ctype_name(cand->type)); + + len=strlen(attr); attr+=len; maxlen-=len; + + /* raddr, rport */ + raddr = (cand->stream->flags & + (NR_ICE_CTX_FLAGS_RELAY_ONLY | + NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES)) ? + &cand->addr : &cand->base; + + switch(cand->type){ + case HOST: + break; + case SERVER_REFLEXIVE: + if (obfuscate_srflx_addr) { + snprintf(attr,maxlen," raddr 0.0.0.0 rport 0"); + } else { + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + } + break; + case PEER_REFLEXIVE: + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + break; + case RELAYED: + // comes from XorMappedAddress via AllocateResponse + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type){ + len=strlen(attr); + attr+=len; + maxlen-=len; + snprintf(attr,maxlen," tcptype %s",nr_tcp_type_name(cand->tcp_type)); + } + + _status=0; + abort: + return(_status); + } + -- cgit v1.2.3