示例#1
0
static void nr_turn_client_connected_cb(NR_SOCKET s, int how, void *cb_arg)
{
  int r, _status;
  nr_turn_client_ctx *ctx = (nr_turn_client_ctx *)cb_arg;

  /* Cancel the connection failure timer */
  NR_async_timer_cancel(ctx->connected_timer_handle);
  ctx->connected_timer_handle=0;

  /* Assume we connected successfully */
  if (ctx->state == NR_TURN_CLIENT_STATE_ALLOCATION_WAIT) {
    if ((r=nr_turn_stun_ctx_start(STAILQ_FIRST(&ctx->stun_ctxs))))
      ABORT(r);
    ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
  }
  else {
    ctx->state = NR_TURN_CLIENT_STATE_CONNECTED;
  }

  _status = 0;
abort:
  if (_status) {
    nr_turn_client_failed(ctx);
  }
}
示例#2
0
int nr_turn_client_allocate(nr_turn_client_ctx *ctx,
                            NR_async_cb finished_cb, void *cb_arg)
{
  nr_turn_stun_ctx *stun = 0;
  int r,_status;

  if(ctx->state == NR_TURN_CLIENT_STATE_FAILED ||
     ctx->state == NR_TURN_CLIENT_STATE_CANCELLED){
    /* TURN TCP contexts can fail before we ever try to form an allocation,
     * since the TCP connection can fail. It is also conceivable that a TURN
     * TCP context could be cancelled before we are done forming all
     * allocations (although we do not do this at the time this code was
     * written) */
    assert(ctx->turn_server_addr.protocol == IPPROTO_TCP);
    ABORT(R_NOT_FOUND);
  }

  assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED);

  ctx->finished_cb=finished_cb;
  ctx->cb_arg=cb_arg;

  if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST,
                                 nr_turn_client_allocate_cb,
                                 nr_turn_client_error_cb,
                                 &stun)))
    ABORT(r);
  stun->stun->params.allocate_request.lifetime_secs =
      TURN_LIFETIME_REQUEST_SECONDS;

  if (ctx->state == NR_TURN_CLIENT_STATE_INITTED) {
      if ((r=nr_turn_stun_ctx_start(stun)))
        ABORT(r);
      ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
  } else {
      ABORT(R_ALREADY);
  }

  _status=0;
abort:
  if (_status) {
    nr_turn_client_failed(ctx);
  }

  return(_status);
}
示例#3
0
int nr_turn_client_allocate(nr_turn_client_ctx *ctx,
                            NR_async_cb finished_cb, void *cb_arg)
{
  nr_turn_stun_ctx *stun = 0;
  int r,_status;

  assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED ||
         ctx->state == NR_TURN_CLIENT_STATE_CONNECTED);

  ctx->finished_cb=finished_cb;
  ctx->cb_arg=cb_arg;

  if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST,
                                 nr_turn_client_allocate_cb,
                                 nr_turn_client_error_cb,
                                 &stun)))
    ABORT(r);
  stun->stun->params.allocate_request.lifetime_secs =
      TURN_LIFETIME_REQUEST_SECONDS;

  switch(ctx->state) {
    case NR_TURN_CLIENT_STATE_INITTED:
      /* We are waiting for connect before we can allocate */
      ctx->state = NR_TURN_CLIENT_STATE_ALLOCATION_WAIT;
      break;
    case NR_TURN_CLIENT_STATE_CONNECTED:
      if ((r=nr_turn_stun_ctx_start(stun)))
        ABORT(r);
      ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
      break;
    default:
      ABORT(R_ALREADY);
  }

  _status=0;
abort:
  if (_status) {
    nr_turn_client_failed(ctx);
  }

  return(_status);
}
示例#4
0
static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg)
{
  nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
  int r,_status;

  r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh timer fired",
        ctx->tctx->label);

  ctx->tctx->refresh_timer_handle=0;
  if ((r=nr_turn_stun_ctx_start(ctx))) {
    ABORT(r);
  }

  _status=0;
abort:
  if (_status) {
    nr_turn_client_failed(ctx->tctx);
  }
  return;
}
示例#5
0
/* The permissions model is as follows:

   - We keep a list of all the permissions we have ever requested
     along with when they were last established.
   - Whenever someone sends a packet, we automatically create/
     refresh the permission.

   This means that permissions automatically time out if
   unused.

*/
int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, nr_transport_addr *addr)
{
  int r, _status;
  nr_turn_permission *perm = 0;
  UINT8 now;
  UINT8 turn_permission_refresh = (TURN_PERMISSION_LIFETIME_SECONDS -
                                   TURN_REFRESH_SLACK_SECONDS) * TURN_USECS_PER_S;

  if ((r=nr_turn_permission_find(ctx, addr, &perm))) {
    if (r == R_NOT_FOUND) {
      if ((r=nr_turn_permission_create(ctx, addr, &perm)))
        ABORT(r);
    }
    else {
      ABORT(r);
    }
  }

  assert(perm);

  /* Now check that the permission is up-to-date */
  now = r_gettimeint();

  if ((now - perm->last_used) > turn_permission_refresh) {
    r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Permission for %s requires refresh",
          ctx->label, perm->addr.as_string);

    if ((r=nr_turn_stun_ctx_start(perm->stun)))
      ABORT(r);

    perm->last_used = now;  /* Update the time now so we don't retry on
                               next packet */
  }

  _status=0;
abort:
  return(_status);
}
示例#6
0
static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg)
{
  int r, _status;
  nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;

  ctx->last_error_code = ctx->stun->error_code;

  switch (ctx->stun->state) {
    case NR_STUN_CLIENT_STATE_DONE:
      /* Save the realm and nonce */
      if (ctx->stun->realm && (!ctx->tctx->realm || strcmp(ctx->stun->realm,
                                                           ctx->tctx->realm))) {
        RFREE(ctx->tctx->realm);
        ctx->tctx->realm = r_strdup(ctx->stun->realm);
        if (!ctx->tctx->realm)
          ABORT(R_NO_MEMORY);
      }
      if (ctx->stun->nonce && (!ctx->tctx->nonce || strcmp(ctx->stun->nonce,
                                                           ctx->tctx->nonce))) {
        RFREE(ctx->tctx->nonce);
        ctx->tctx->nonce = r_strdup(ctx->stun->nonce);
        if (!ctx->tctx->nonce)
          ABORT(R_NO_MEMORY);
      }

      ctx->retry_ct=0;
      ctx->success_cb(0, 0, ctx);
      break;

    case NR_STUN_CLIENT_STATE_FAILED:
      /* Special case: if this is an authentication error,
         we retry once. This allows the 401/438 nonce retry
         paradigm. After that, we fail */
      /* TODO([email protected]): 401 needs a #define */
      /* TODO([email protected]): Add alternate-server (Mozilla bug 857688) */
      if (ctx->stun->error_code == 401 || ctx->stun->error_code == 438) {
        if (ctx->retry_ct > 0) {
          r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Exceeded the number of retries", ctx->tctx->label);
          ABORT(R_FAILED);
        }

        if (!ctx->stun->nonce) {
          r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no nonce", ctx->tctx->label);
          ABORT(R_FAILED);
        }
        if (!ctx->stun->realm) {
          r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no realm", ctx->tctx->label);
          ABORT(R_FAILED);
        }

        /* Try to retry */
        if ((r=nr_turn_stun_set_auth_params(ctx, ctx->stun->realm,
                                            ctx->stun->nonce)))
          ABORT(r);

        ctx->stun->error_code = 0;  /* Reset to avoid inf-looping */

        if ((r=nr_turn_stun_ctx_start(ctx))) {
          r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", ctx->tctx->label);
          ABORT(r);
        }

        ctx->retry_ct++;
      }
      else {
        ABORT(R_FAILED);
      }
      break;

    case NR_STUN_CLIENT_STATE_TIMED_OUT:
      ABORT(R_FAILED);
      break;

    case NR_STUN_CLIENT_STATE_CANCELLED:
      assert(0);  /* Shouldn't happen */
      return;
      break;

    default:
      assert(0);  /* Shouldn't happen */
      return;
  }

  _status=0;
abort:
  if (_status) {
    ctx->error_cb(0, 0, ctx);
  }
}