Пример #1
0
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);
    }
  }
Пример #2
0
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;
}
Пример #3
0
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);
}
Пример #4
0
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);
}
Пример #5
0
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);
}