static void nr_stun_client_fire_finished_cb(nr_stun_client_ctx *ctx) { if (ctx->finished_cb) { NR_async_cb finished_cb = ctx->finished_cb; ctx->finished_cb = 0; /* prevent 2nd call */ /* finished_cb call must be absolutely last thing in function * because as a side effect this ctx may be operated on in the * callback */ finished_cb(0,0,ctx->cb_arg); } }
static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg) { int _status; nr_stun_client_ctx *ctx=cb_arg; struct timeval now; INT8 ms_waited; /* Prevent this timer from being cancelled later */ ctx->timer_handle=0; /* Shouldn't happen */ if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED) ABORT(R_REJECTED); gettimeofday(&now, 0); if (r_timeval_diff_ms(&now, &ctx->timer_set, &ms_waited)) { r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired",ctx->label); } else { r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired (after %llu ms)",ctx->label, ms_waited); } if (ctx->request_ct >= ctx->maximum_transmits) { r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timed out",ctx->label); ctx->state=NR_STUN_CLIENT_STATE_TIMED_OUT; ABORT(R_FAILED); } if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) ABORT(R_NOT_PERMITTED); /* as a side effect will reset the timer */ nr_stun_client_send_request(ctx); _status = 0; abort: if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { /* Cancel the timer firing */ if (ctx->timer_handle) { NR_async_timer_cancel(ctx->timer_handle); ctx->timer_handle=0; } if (ctx->finished_cb) { NR_async_cb finished_cb = ctx->finished_cb; ctx->finished_cb = 0; /* prevent 2nd call */ /* finished_cb call must be absolutely last thing in function * because as a side effect this ctx may be operated on in the * callback */ finished_cb(0,0,ctx->cb_arg); } } return; }
int nr_turn_client_process_response(nr_turn_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *turn_server_addr) { int r,_status; nr_stun_client_ctx *stun_ctx; assert(ctx->phase >= 0 && ctx->phase < NUMBER_OF_STUN_CTX); if (ctx->phase < 0 || ctx->phase > NUMBER_OF_STUN_CTX) ABORT(R_INTERNAL); /* should never happen */ stun_ctx = ctx->stun_ctx[ctx->phase]; if (ctx->state != NR_TURN_CLIENT_STATE_RUNNING && ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) { #if 0 //TODO: !nn! it really shouldn't be the case that we're processing a response in this state, //TODO: but perhaps we failed and we haven't taken ourself off the //TODO: ASYNC wait list, so an outstanding packet can cause us this grief assert(0); ABORT(R_INTERNAL); /* should never happen */ #else //TODO: !nn! fix so we can never get into this state r_log(NR_LOG_TURN,LOG_ERR,"TURN-CLIENT(%s): dropping packet in phase %s", ctx->label, TURN_PHASE_LABEL[ctx->phase]); return R_INTERNAL; #endif } if (ctx->phase != NR_TURN_CLIENT_PHASE_INITIALIZED) { r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Received response in phase %s", ctx->label, TURN_PHASE_LABEL[ctx->phase]); } else { ABORT(R_INTERNAL); } if ((r=nr_stun_client_process_response(stun_ctx, msg, len, turn_server_addr))) ABORT(r); _status=0; abort: if (ctx->state != NR_TURN_CLIENT_STATE_RUNNING) { if (ctx->finished_cb) { NR_async_cb finished_cb = ctx->finished_cb; ctx->finished_cb = 0; /* prevent 2nd call */ /* finished_cb call must be absolutely last thing in function * because as a side effect this ctx may be operated on in the * callback */ finished_cb(0,0,ctx->cb_arg); } } return(_status); }
int nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state) { int r,_status; assert(ctx->phase >= -1 && ctx->phase < NUMBER_OF_STUN_CTX); switch (ctx->state) { //case NR_TURN_CLIENT_STATE_ALLOCATING: case NR_TURN_CLIENT_STATE_RUNNING: case NR_TURN_CLIENT_STATE_ALLOCATED: /* these are the acceptable states */ break; default: assert(0); ABORT(R_INTERNAL); /* should never happen */ break; } if (ctx->phase != NR_TURN_CLIENT_PHASE_INITIALIZED && ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) { r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Finishing phase %s", ctx->label, TURN_PHASE_LABEL[ctx->phase]); } switch (stun_ctx_state) { case NR_STUN_CLIENT_STATE_DONE: /* sanity check that NR_TURN_CLIENT_PHASE_SET_ACTIVE_DEST is the final phase */ assert(NUMBER_OF_STUN_CTX == NR_TURN_CLIENT_PHASE_SET_ACTIVE_DEST+1); if (ctx->phase == NR_TURN_CLIENT_PHASE_SET_ACTIVE_DEST) { #if 0 ctx->state = NR_TURN_CLIENT_STATE_ACTIVE; #else assert(0); ABORT(R_INTERNAL); #endif r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Active", ctx->label); } else if (ctx->phase == NR_TURN_CLIENT_PHASE_ALLOCATE_REQUEST2 && ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) { ctx->state = NR_TURN_CLIENT_STATE_ALLOCATED; r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Allocated", ctx->label); } else { ++(ctx->phase); if (ctx->phase > NUMBER_OF_STUN_CTX) { ABORT(R_INTERNAL); } ctx->state=NR_TURN_CLIENT_STATE_RUNNING; r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Starting phase %s", ctx->label, TURN_PHASE_LABEL[ctx->phase]); if ((r=nr_stun_client_start(ctx->stun_ctx[ctx->phase], TURN_PHASE_MODE[ctx->phase], nr_turn_client_cb, ctx))) ABORT(r); } break; case NR_STUN_CLIENT_STATE_FAILED: ctx->state = NR_TURN_CLIENT_STATE_FAILED; r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Failed in phase %s", ctx->label, TURN_PHASE_LABEL[ctx->phase]); ABORT(R_FAILED); break; case NR_STUN_CLIENT_STATE_TIMED_OUT: ctx->state = NR_TURN_CLIENT_STATE_TIMED_OUT; r_log(NR_LOG_TURN,LOG_DEBUG,"TURN-CLIENT(%s): Timed out", ctx->label); ABORT(R_INTERRUPTED); break; default: assert(0); ABORT(R_INTERNAL); /* should never happen */ break; } _status=0; abort: if (ctx->state != NR_TURN_CLIENT_STATE_RUNNING) { if (ctx->finished_cb) { NR_async_cb finished_cb = ctx->finished_cb; ctx->finished_cb = 0; /* prevent 2nd call */ /* finished_cb call must be absolutely last thing in function * because as a side effect this ctx may be operated on in the * callback */ finished_cb(0,0,ctx->cb_arg); } } return(_status); }
int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr) { int r,_status; char string[256]; Data *password = 0; nr_stun_message_attribute *attr; nr_transport_addr *mapped_addr = 0; if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED) ABORT(R_REJECTED); if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) ABORT(R_REJECTED); r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Received ", ctx->label); r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); /* determine password */ switch (ctx->mode) { case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: password = ctx->params.stun_binding_request.password; break; case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: /* do nothing */ break; case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: /* do nothing */ break; #ifdef USE_ICE case NR_ICE_CLIENT_MODE_BINDING_REQUEST: password = &ctx->params.ice_binding_request.password; break; case NR_ICE_CLIENT_MODE_USE_CANDIDATE: password = &ctx->params.ice_binding_request.password; break; #endif /* USE_ICE */ #ifdef USE_TURN case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST1: /* do nothing */ break; case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST2: /* do nothing */ break; case NR_TURN_CLIENT_MODE_SET_ACTIVE_DEST_REQUEST: /* do nothing */ break; case NR_TURN_CLIENT_MODE_SEND_INDICATION: /* do nothing -- we just got our DATA-INDICATION */ break; #endif /* USE_TURN */ default: assert(0); ABORT(R_FAILED); break; } #ifdef USE_TURN if (ctx->mode == NR_TURN_CLIENT_MODE_SEND_INDICATION) { /* SEND-INDICATION gets a DATA-INDICATION back, which will always * be a different transaction, so don't perform the check in that * case */ } #endif /* USE_TURN */ if ((r=nr_stun_message_create2(&ctx->response, msg, len))) ABORT(r); if ((r=nr_stun_decode_message(ctx->response, nr_stun_client_get_password, password))) ABORT(r); if ((r=nr_stun_receive_message(ctx->request, ctx->response))) ABORT(r); /* TODO: !nn! currently using password!=0 to mean that auth is required, * TODO: !nn! but we should probably pass that in explicitly via the * TODO: !nn! usage (ctx->mode?) */ if (password) { if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, 0)) { if ((r=nr_stun_receive_response_long_term_auth(ctx->response, ctx))) ABORT(r); } else { if ((r=nr_stun_receive_response_short_term_auth(ctx->response))) ABORT(r); } } if (NR_STUN_GET_TYPE_CLASS(ctx->response->header.type) == NR_CLASS_RESPONSE) { if ((r=nr_stun_process_success_response(ctx->response))) ABORT(r); } else { if ((r=nr_stun_process_error_response(ctx->response))) { ABORT(r); } else { /* drop the error on the floor */ ABORT(R_FAILED); } } /* TODO: !nn! this should be moved to individual message receive/processing sections */ switch (ctx->mode) { case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) ABORT(R_BAD_DATA); if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) ABORT(R_BAD_DATA); mapped_addr = &ctx->results.stun_binding_response.mapped_addr; break; case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) ABORT(R_BAD_DATA); mapped_addr = &ctx->results.stun_binding_response.mapped_addr; break; case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0) && ! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) ABORT(R_BAD_DATA); mapped_addr = &ctx->results.stun_binding_response_stund_0_96.mapped_addr; break; #ifdef USE_ICE case NR_ICE_CLIENT_MODE_BINDING_REQUEST: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) ABORT(R_BAD_DATA); if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) ABORT(R_BAD_DATA); mapped_addr = &ctx->results.stun_binding_response.mapped_addr; break; case NR_ICE_CLIENT_MODE_USE_CANDIDATE: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) ABORT(R_BAD_DATA); if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) ABORT(R_BAD_DATA); mapped_addr = &ctx->results.stun_binding_response.mapped_addr; break; #endif /* USE_ICE */ #ifdef USE_TURN case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST1: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_REALM, &attr)) ABORT(R_BAD_DATA); ctx->results.allocate_response1.realm = attr->u.realm; if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, &attr)) ABORT(R_BAD_DATA); ctx->results.allocate_response1.nonce = attr->u.nonce; break; case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST2: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_USERNAME, 0)) ABORT(R_BAD_DATA); if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) ABORT(R_BAD_DATA); if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) ABORT(R_BAD_DATA); if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_RELAY_ADDRESS, &attr)) { if ((r=nr_transport_addr_copy( &ctx->results.allocate_response2.relay_addr, &attr->u.relay_address))) ABORT(r); } else { if ((r=nr_transport_addr_copy(&ctx->results.allocate_response2.relay_addr, peer_addr))) ABORT(r); } mapped_addr = &ctx->results.allocate_response2.mapped_addr; break; case NR_TURN_CLIENT_MODE_SET_ACTIVE_DEST_REQUEST: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) ABORT(R_BAD_DATA); break; case NR_TURN_CLIENT_MODE_SEND_INDICATION: if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_REMOTE_ADDRESS, 0)) ABORT(R_BAD_DATA); break; #endif /* USE_TURN */ default: assert(0); ABORT(R_FAILED); break; } /* make sure we have the most up-to-date address from this peer */ if (nr_transport_addr_cmp(&ctx->peer_addr, peer_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Peer moved from %s to %s", ctx->label, ctx->peer_addr.as_string, peer_addr->as_string); nr_transport_addr_copy(&ctx->peer_addr, peer_addr); } if (mapped_addr) { if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, &attr)) { if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.xor_mapped_address.unmasked))) ABORT(r); } else if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, &attr)) { if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.mapped_address))) ABORT(r); } else ABORT(R_BAD_DATA); r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received mapped address: %s", ctx->label, mapped_addr->as_string); } ctx->state=NR_STUN_CLIENT_STATE_DONE; _status=0; abort: if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { /* Cancel the timer firing */ if (ctx->timer_handle) { NR_async_timer_cancel(ctx->timer_handle); ctx->timer_handle = 0; } /* Fire the callback */ if (ctx->finished_cb) { NR_async_cb finished_cb = ctx->finished_cb; ctx->finished_cb = 0; /* prevent 2nd call */ /* finished_cb call must be absolutely last thing in function * because as a side effect this ctx may be operated on in the * callback */ finished_cb(0,0,ctx->cb_arg); } } return(_status); }