int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) { nr_stun_client_ctx *ctx=0; int r,_status; if ((r=nr_stun_startup())) ABORT(r); if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) ABORT(R_NO_MEMORY); ctx->state=NR_STUN_CLIENT_STATE_INITTED; if(!(ctx->label=r_strdup(label))) ABORT(R_NO_MEMORY); ctx->sock=sock; nr_socket_getaddr(sock,&ctx->my_addr); nr_transport_addr_copy(&ctx->peer_addr,peer); if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) { if (RTO != 0) ctx->rto_ms = RTO; else ctx->rto_ms = 100; } if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor)) ctx->retransmission_backoff_factor = 2.0; if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits)) ctx->maximum_transmits = 7; if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->final_retransmit_backoff_ms)) ctx->final_retransmit_backoff_ms = 16 * ctx->rto_ms; *ctxp=ctx; _status=0; abort: if(_status) { nr_stun_client_ctx_destroy(&ctx); } return(_status); }
static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) { int r,_status; nr_ice_cand_pair *pair=cb_arg,*orig_pair; nr_ice_candidate *cand=0; nr_stun_message *sres; nr_transport_addr *request_src; nr_transport_addr *request_dst; nr_transport_addr *response_src; nr_transport_addr response_dst; nr_stun_message_attribute *attr; pair->stun_cb_timer=0; r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s", pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string); /* This ordinarily shouldn't happen, but can if we're doing the second check to confirm nomination. Just bail out */ if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) goto done; switch(pair->stun_client->state){ case NR_STUN_CLIENT_STATE_FAILED: sres=pair->stun_client->response; if(sres && nr_stun_message_has_attribute(sres,NR_STUN_ATTR_ERROR_CODE,&attr)&&attr->u.error_code.number==487){ /* * Flip the controlling bit; subsequent 487s for other pairs will be * ignored, since we abandon their STUN transactions. */ nr_ice_peer_ctx_switch_controlling_role(pair->pctx); return; } /* Fall through */ case NR_STUN_CLIENT_STATE_TIMED_OUT: nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); break; case NR_STUN_CLIENT_STATE_DONE: /* make sure the addresses match up S 7.1.2.2 */ response_src=&pair->stun_client->peer_addr; request_dst=&pair->remote->addr; if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Peer address mismatch %s != %s",pair->pctx->label,pair->codeword,response_src->as_string,request_dst->as_string); nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); break; } request_src=&pair->stun_client->my_addr; nr_socket_getaddr(pair->local->osock,&response_dst); if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Local address mismatch %s != %s",pair->pctx->label,pair->codeword,request_src->as_string,response_dst.as_string); nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); break; } if(strlen(pair->stun_client->results.ice_binding_response.mapped_addr.as_string)==0){ /* we're using the mapped_addr returned by the server to lookup our * candidate, but if the server fails to do that we can't perform * the lookup -- this may be a BUG because if we've gotten here * then the transaction ID check succeeded, and perhaps we should * just assume that it's the server we're talking to and that our * peer is ok, but I'm not sure how that'll interact with the * peer reflexive logic below */ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->codeword,pair->as_string); nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); break; } else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); } else{ /* OK, this didn't correspond to a pair on the check list, but it probably matches one of our candidates */ cand=TAILQ_FIRST(&pair->local->component->candidates); while(cand){ if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) break; cand=TAILQ_NEXT(cand,entry_comp); } /* OK, nothing found, must be peer reflexive */ if(!cand){ if(r=nr_ice_candidate_create(pair->pctx->ctx, pair->local->component,pair->local->isock,pair->local->osock, PEER_REFLEXIVE,0,pair->local->component->component_id,&cand)) ABORT(r); if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr)) ABORT(r); cand->state=NR_ICE_CAND_STATE_INITIALIZED; TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp); } /* Note: we stomp the existing pair! */ orig_pair=pair; if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &pair)) ABORT(r); nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); if(r=nr_ice_component_insert_pair(pair->remote->component,pair)) ABORT(r); /* If the original pair was nominated, make us nominated, since we replace him*/ if(orig_pair->peer_nominated) pair->peer_nominated=1; /* Now mark the orig pair failed */ nr_ice_candidate_pair_set_state(orig_pair->pctx,orig_pair,NR_ICE_PAIR_STATE_FAILED); } /* Should we set nominated? */ if(pair->pctx->controlling){ if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION) pair->nominated=1; } else{ if(pair->peer_nominated) pair->nominated=1; } /* increment the number of valid pairs in the component */ /* We don't bother to maintain a separate valid list */ pair->remote->component->valid_pairs++; /* S 7.1.2.2: unfreeze other pairs with the same foundation*/ if(r=nr_ice_media_stream_unfreeze_pairs_foundation(pair->remote->stream,pair->foundation)) ABORT(r); /* Deal with this pair being nominated */ if(pair->nominated){ if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) ABORT(r); } break; default: ABORT(R_INTERNAL); } /* If we're controlling but in regular mode, ask the handler if he wants to nominate something and stop... */ if(pair->pctx->controlling && !(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)){ if(r=nr_ice_component_select_pair(pair->pctx,pair->remote->component)){ if(r!=R_NOT_FOUND) ABORT(r); } } done: _status=0; abort: return; }
int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) { nr_stun_client_ctx *ctx=0; char allow_loopback; int r,_status; if ((r=nr_stun_startup())) ABORT(r); if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) ABORT(R_NO_MEMORY); ctx->state=NR_STUN_CLIENT_STATE_INITTED; if(!(ctx->label=r_strdup(label))) ABORT(R_NO_MEMORY); ctx->sock=sock; nr_socket_getaddr(sock,&ctx->my_addr); nr_transport_addr_copy(&ctx->peer_addr,peer); if (RTO != 0) { ctx->rto_ms = RTO; } else if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) { ctx->rto_ms = 100; } if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor)) ctx->retransmission_backoff_factor = 2.0; if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits)) ctx->maximum_transmits = 7; if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->maximum_transmits_timeout_ms)) ctx->maximum_transmits_timeout_ms = 16 * ctx->rto_ms; ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD; if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) || !allow_loopback) { ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK; } if (ctx->my_addr.protocol == IPPROTO_TCP) { /* Because TCP is reliable there is only one final timeout value. * We store the timeout value for TCP in here, because timeout_ms gets * reset to 0 in client_reset() which gets called from client_start() */ ctx->maximum_transmits_timeout_ms = ctx->rto_ms * pow(ctx->retransmission_backoff_factor, ctx->maximum_transmits); ctx->maximum_transmits = 1; } *ctxp=ctx; _status=0; abort: if(_status){ nr_stun_client_ctx_destroy(&ctx); } return(_status); }