Exemplo n.º 1
0
static gboolean priv_discovery_tick (gpointer pointer)
{
  NiceAgent *agent = pointer;
  gboolean ret;

  agent_lock();
  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in priv_discovery_tick");
    agent_unlock ();
    return FALSE;
  }

  ret = priv_discovery_tick_unlocked (pointer);
  if (ret == FALSE) {
    if (agent->discovery_timer_source != NULL) {
      g_source_destroy (agent->discovery_timer_source);
      g_source_unref (agent->discovery_timer_source);
      agent->discovery_timer_source = NULL;
    }
  }
  agent_unlock_and_emit (agent);

  return ret;
}
Exemplo n.º 2
0
/* Must be called with the agent lock released as it could dispose of
 * NiceIOStreams. */
void
component_free (Component *cmp)
{
  /* Component should have been closed already. */
  g_warn_if_fail (cmp->local_candidates == NULL);
  g_warn_if_fail (cmp->remote_candidates == NULL);
  g_warn_if_fail (cmp->incoming_checks == NULL);

  g_clear_object (&cmp->tcp);
  g_clear_object (&cmp->stop_cancellable);
  g_clear_object (&cmp->iostream);
  g_mutex_clear (&cmp->io_mutex);

  if (cmp->stop_cancellable_source != NULL) {
    g_source_destroy (cmp->stop_cancellable_source);
    g_source_unref (cmp->stop_cancellable_source);
  }

  if (cmp->ctx != NULL) {
    g_main_context_unref (cmp->ctx);
    cmp->ctx = NULL;
  }

  g_main_context_unref (cmp->own_ctx);

  g_slice_free (Component, cmp);

  g_atomic_int_inc (&n_components_destroyed);
  nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)",
      n_components_created, n_components_destroyed);
}
Exemplo n.º 3
0
static void
nice_component_init (NiceComponent *component)
{
  g_atomic_int_inc (&n_components_created);
  nice_debug ("Created NiceComponent (%u created, %u destroyed)",
      n_components_created, n_components_destroyed);

  component->id = 0;
  component->state = NICE_COMPONENT_STATE_DISCONNECTED;
  component->restart_candidate = NULL;
  component->tcp = NULL;
  component->agent = NULL;
  component->stream = NULL;

  g_mutex_init (&component->io_mutex);
  g_queue_init (&component->pending_io_messages);
  component->io_callback_id = 0;

  component->own_ctx = g_main_context_new ();
  component->stop_cancellable = g_cancellable_new ();
  component->stop_cancellable_source =
      g_cancellable_source_new (component->stop_cancellable);
  g_source_set_dummy_callback (component->stop_cancellable_source);
  g_source_attach (component->stop_cancellable_source, component->own_ctx);
  component->ctx = g_main_context_ref (component->own_ctx);

  /* Start off with a fresh main context and all I/O paused. This
   * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages()
   * are called. */
  nice_component_set_io_context (component, NULL);
  nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL);

  g_queue_init (&component->queued_tcp_packets);
}
Exemplo n.º 4
0
/* Must be called with the agent lock released as it could dispose of
 * NiceIOStreams. */
static void
nice_component_finalize (GObject *obj)
{
  NiceComponent *cmp;

  cmp = NICE_COMPONENT (obj);

  /* Component should have been closed already. */
  g_warn_if_fail (cmp->local_candidates == NULL);
  g_warn_if_fail (cmp->remote_candidates == NULL);
  g_warn_if_fail (cmp->incoming_checks == NULL);

  g_clear_object (&cmp->tcp);
  g_clear_object (&cmp->stop_cancellable);
  g_clear_object (&cmp->iostream);
  g_mutex_clear (&cmp->io_mutex);

  if (cmp->stop_cancellable_source != NULL) {
    g_source_destroy (cmp->stop_cancellable_source);
    g_source_unref (cmp->stop_cancellable_source);
  }

  if (cmp->ctx != NULL) {
    g_main_context_unref (cmp->ctx);
    cmp->ctx = NULL;
  }

  g_main_context_unref (cmp->own_ctx);

  g_atomic_int_inc (&n_components_destroyed);
  nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)",
      n_components_created, n_components_destroyed);

  G_OBJECT_CLASS (nice_component_parent_class)->finalize (obj);
}
Exemplo n.º 5
0
/*
 * Changes the selected pair for the component to 'pair'. Does not
 * emit the "selected-pair-changed" signal.
 */ 
void
nice_component_update_selected_pair (NiceComponent *component, const CandidatePair *pair)
{
  g_assert (component);
  g_assert (pair);
  nice_debug ("setting SELECTED PAIR for component %u: %s:%s (prio:%"
      G_GUINT64_FORMAT ").", component->id, pair->local->foundation,
      pair->remote->foundation, pair->priority);

  if (component->selected_pair.local &&
      component->selected_pair.local == component->turn_candidate) {
    refresh_prune_candidate (component->agent, component->turn_candidate);
    discovery_prune_socket (component->agent,
        component->turn_candidate->sockptr);
    conn_check_prune_socket (component->agent, component->stream, component,
        component->turn_candidate->sockptr);
    nice_component_detach_socket (component, component->turn_candidate->sockptr);
    nice_candidate_free (component->turn_candidate);
    component->turn_candidate = NULL;
  }

  nice_component_clear_selected_pair (component);

  component->selected_pair.local = pair->local;
  component->selected_pair.remote = pair->remote;
  component->selected_pair.priority = pair->priority;
  component->selected_pair.prflx_priority = pair->prflx_priority;
}
Exemplo n.º 6
0
/* This must be called with the agent lock *held*. */
void
nice_component_emit_io_callback (NiceComponent *component,
    const guint8 *buf, gsize buf_len)
{
  NiceAgent *agent;
  guint stream_id, component_id;
  NiceAgentRecvFunc io_callback;
  gpointer io_user_data;

  g_assert (component != NULL);
  g_assert (buf != NULL);
  g_assert (buf_len > 0);

  agent = component->agent;
  stream_id = component->stream->id;
  component_id = component->id;

  g_mutex_lock (&component->io_mutex);
  io_callback = component->io_callback;
  io_user_data = component->io_user_data;
  g_mutex_unlock (&component->io_mutex);

  /* Allow this to be called with a NULL io_callback, since the caller can’t
   * lock io_mutex to check beforehand. */
  if (io_callback == NULL)
    return;

  g_assert (NICE_IS_AGENT (agent));
  g_assert (stream_id > 0);
  g_assert (component_id > 0);
  g_assert (io_callback != NULL);

  /* Only allocate a closure if the callback is being deferred to an idle
   * handler. */
  if (g_main_context_is_owner (component->ctx)) {
    /* Thread owns the main context, so invoke the callback directly. */
    agent_unlock_and_emit (agent);
    io_callback (agent, stream_id,
        component_id, buf_len, (gchar *) buf, io_user_data);
    agent_lock ();
  } else {
    IOCallbackData *data;

    g_mutex_lock (&component->io_mutex);

    /* Slow path: Current thread doesn’t own the Component’s context at the
     * moment, so schedule the callback in an idle handler. */
    data = io_callback_data_new (buf, buf_len);
    g_queue_push_tail (&component->pending_io_messages,
        data);  /* transfer ownership */

    nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);

    nice_component_schedule_io_callback (component);

    g_mutex_unlock (&component->io_mutex);
  }
}
Exemplo n.º 7
0
static void
nice_stream_init (NiceStream *stream)
{
  g_atomic_int_inc (&n_streams_created);
  nice_debug ("Created NiceStream (%u created, %u destroyed)",
      n_streams_created, n_streams_destroyed);

  stream->n_components = 0;
  stream->initial_binding_request_received = FALSE;
}
Exemplo n.º 8
0
/*
 * Detaches socket handles of @component from the main context. Leaves the
 * sockets themselves untouched.
 *
 * Must *not* take the agent lock, since it’s called from within
 * nice_component_set_io_context(), which holds the Component’s I/O lock.
 */
void
nice_component_detach_all_sockets (NiceComponent *component)
{
  GSList *i;

  for (i = component->socket_sources; i != NULL; i = i->next) {
    SocketSource *socket_source = i->data;
    nice_debug ("Detach source %p, socket %p.", socket_source->source,
        socket_source->socket);
    socket_source_detach (socket_source);
  }
}
Exemplo n.º 9
0
void
nice_component_free_socket_sources (NiceComponent *component)
{
  nice_debug ("Free socket sources for component %p.", component);

  g_slist_free_full (component->socket_sources,
      (GDestroyNotify) socket_source_free);
  component->socket_sources = NULL;
  component->socket_sources_age++;

  nice_component_clear_selected_pair (component);
}
Exemplo n.º 10
0
/* Reattaches socket handles of @component to the main context.
 *
 * Must *not* take the agent lock, since it’s called from within
 * nice_component_set_io_context(), which holds the Component’s I/O lock. */
static void
nice_component_reattach_all_sockets (NiceComponent *component)
{
  GSList *i;

  for (i = component->socket_sources; i != NULL; i = i->next) {
    SocketSource *socket_source = i->data;
    nice_debug ("Reattach source %p.", socket_source->source);
    socket_source_detach (socket_source);
    socket_source_attach (socket_source, component->ctx);
  }
}
Exemplo n.º 11
0
static void
socket_source_detach (SocketSource *source)
{
  nice_debug ("Detaching source %p (socket %p, FD %d) from context %p",
      source->source, source->socket,
      (source->socket->fileno != NULL) ?
          g_socket_get_fd (source->socket->fileno) : 0,
      (source->source != NULL) ? g_source_get_context (source->source) : 0);

  if (source->source != NULL) {
    g_source_destroy (source->source);
    g_source_unref (source->source);
  }
  source->source = NULL;
}
Exemplo n.º 12
0
/* Must be called with the agent lock released as it could dispose of
 * NiceIOStreams. */
static void
nice_stream_finalize (GObject *obj)
{
  NiceStream *stream;

  stream = NICE_STREAM (obj);

  g_free (stream->name);
  g_slist_free_full (stream->components, (GDestroyNotify) g_object_unref);

  g_atomic_int_inc (&n_streams_destroyed);
  nice_debug ("Destroyed NiceStream (%u created, %u destroyed)",
      n_streams_created, n_streams_destroyed);

  G_OBJECT_CLASS (nice_stream_parent_class)->finalize (obj);
}
Exemplo n.º 13
0
/* Must *not* take the agent lock, since it’s called from within
 * component_set_io_context(), which holds the Component’s I/O lock. */
static void
socket_source_attach (SocketSource *socket_source, GMainContext *context)
{
  GSource *source;

  /* Create a source. */
  source = g_socket_create_source (socket_source->socket->fileno,
      G_IO_IN, NULL);
  g_source_set_callback (source, (GSourceFunc) component_io_cb,
      socket_source, NULL);

  /* Add the source. */
  nice_debug ("Attaching source %p (socket %p, FD %d) to context %p", source,
      socket_source->socket, g_socket_get_fd (socket_source->socket->fileno),
      context);

  g_assert (socket_source->source == NULL);
  socket_source->source = source;
  g_source_attach (source, context);
}
Exemplo n.º 14
0
/*
 * Changes the selected pair for the component to 'pair'. Does not
 * emit the "selected-pair-changed" signal.
 */ 
void component_update_selected_pair (Component *component, const CandidatePair *pair)
{
  g_assert (component);
  g_assert (pair);
  nice_debug ("setting SELECTED PAIR for component %u: %s:%s (prio:%"
      G_GUINT64_FORMAT ").", component->id, pair->local->foundation,
      pair->remote->foundation, pair->priority);

  if (component->selected_pair.keepalive.tick_source != NULL) {
    g_source_destroy (component->selected_pair.keepalive.tick_source);
    g_source_unref (component->selected_pair.keepalive.tick_source);
    component->selected_pair.keepalive.tick_source = NULL;
  }

  memset (&component->selected_pair, 0, sizeof(CandidatePair));

  component->selected_pair.local = pair->local;
  component->selected_pair.remote = pair->remote;
  component->selected_pair.priority = pair->priority;

}
Exemplo n.º 15
0
/* This takes ownership of the socket.
 * It creates and attaches a source to the component’s context. */
void
component_attach_socket (Component *component, NiceSocket *nicesock)
{
  GSList *l;
  SocketSource *socket_source;

  g_assert (component != NULL);
  g_assert (nicesock != NULL);

  g_assert (component->ctx != NULL);

  if (nicesock->fileno == NULL)
    return;

  /* Find an existing SocketSource in the component which contains @socket, or
   * create a new one.
   *
   * Whenever a source is added or remove to socket_sources, socket_sources_age
   * must be incremented.
   */
  l = g_slist_find_custom (component->socket_sources, nicesock,
          _find_socket_source);
  if (l != NULL) {
    socket_source = l->data;
  } else {
    socket_source = g_slice_new0 (SocketSource);
    socket_source->socket = nicesock;
    socket_source->component = component;
    component->socket_sources =
        g_slist_prepend (component->socket_sources, socket_source);
    component->socket_sources_age++;
  }

  /* Create and attach a source */
  nice_debug ("Component %p (agent %p): Attach source (stream %u).",
      component, component->agent, component->stream->id);
  socket_source_attach (socket_source, component->ctx);
}
Exemplo n.º 16
0
/*
 * Adds a new local candidate. Implements the candidate pruning
 * defined in ICE spec section 4.1.3 "Eliminating Redundant
 * Candidates" (ID-19).
 */
static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *candidate)
{
  GSList *i;

  g_assert (candidate != NULL);

  for (i = component->local_candidates; i ; i = i->next) {
    NiceCandidate *c = i->data;

    if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
        nice_address_equal (&c->addr, &candidate->addr) &&
        c->transport == candidate->transport) {
      nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
      return FALSE;
    }
  }

  component->local_candidates = g_slist_append (component->local_candidates,
      candidate);
  conn_check_add_for_local_candidate(agent, stream_id, component, candidate);

  return TRUE;
}
Exemplo n.º 17
0
static gboolean
socket_send_more (
  GSocket *gsocket,
  GIOCondition condition,
  gpointer data)
{
  NiceSocket *sock = (NiceSocket *) data;
  TcpPriv *priv = sock->priv;

  agent_lock (NULL);

  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in tcp-bsd.c:socket_send_more");
    agent_unlock (NULL);
    return FALSE;
  }

  /* connection hangs up or queue was emptied */
  if (condition & G_IO_HUP ||
      nice_socket_flush_send_queue_to_socket (sock->fileno,
          &priv->send_queue)) {
    g_source_destroy (priv->io_source);
    g_source_unref (priv->io_source);
    priv->io_source = NULL;

    agent_unlock (NULL);

    if (priv->writable_cb)
      priv->writable_cb (sock, priv->writable_data);

    return FALSE;
  }

  agent_unlock (NULL);
  return TRUE;
}
Exemplo n.º 18
0
/**
 * nice_component_detach_socket:
 * @component: a #NiceComponent
 * @socket: the socket to detach the source for
 *
 * Detach the #GSource for the single specified @socket. It also closes it
 * and frees it!
 *
 * If the @socket doesn’t exist in this @component, do nothing.
 */
static void
nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock)
{
  GSList *l;
  SocketSource *socket_source;

  nice_debug ("Detach socket %p.", nicesock);

  /* Remove the socket from various lists. */
  for (l = component->incoming_checks; l != NULL;) {
    IncomingCheck *icheck = l->data;
    GSList *next = l->next;

    if (icheck->local_socket == nicesock) {
      component->incoming_checks =
          g_slist_delete_link (component->incoming_checks, l);
      incoming_check_free (icheck);
    }

    l = next;
  }

  /* Find the SocketSource for the socket. */
  l = g_slist_find_custom (component->socket_sources, nicesock,
          _find_socket_source);
  if (l == NULL)
    return;

  /* Detach the source. */
  socket_source = l->data;
  component->socket_sources = g_slist_delete_link (component->socket_sources, l);
  component->socket_sources_age++;

  socket_source_detach (socket_source);
  socket_source_free (socket_source);
}
Exemplo n.º 19
0
/* FIXME: The current implementation of socket_recv_message() is a fast
 * pass-through to nice_socket_recv_message() if the HTTP socket is connected,
 * but is a slow state machine otherwise, using multiple memcpy()s. Spruce it up
 * to better to use the recv_messages to avoid the memcpy()s. */
static gint
socket_recv_messages (NiceSocket *sock,
    NiceInputMessage *recv_messages, guint n_recv_messages)
{
  HttpPriv *priv = sock->priv;
  gint ret = -1;

  /* Socket has been closed: */
  if (sock->priv == NULL)
    return 0;

  if (priv->state == HTTP_STATE_CONNECTED) {
    guint i;

    /* Fast path: pass through to the base socket once we’re connected. */
    if (priv->base_socket) {
      ret = nice_socket_recv_messages (priv->base_socket,
          recv_messages, n_recv_messages);
    }

    if (ret <= 0)
      return ret;

    /* After successfully receiving into at least one NiceInputMessage,
     * update the from address in each valid NiceInputMessage. */
    for (i = 0; i < (guint) ret; i++) {
      if (recv_messages[i].from != NULL)
        *recv_messages[i].from = priv->addr;
    }

    return ret;
  } else {
    /* Slow path: read into a local ring buffer until we’re parsed enough of the
     * headers. Double the buffer in size every time it fills up. */
    gboolean has_wrapped;
    GInputVector local_recv_bufs[2];
    NiceInputMessage local_recv_message = { local_recv_bufs, 2, NULL, 0 };

    /* Has the buffer filled up? Start with an initial buffer of 1KB, which
     * should cover the average size of HTTP response headers. Source:
     * http://dev.chromium.org/spdy/spdy-whitepaper */
    if (priv->recv_buf_fill == priv->recv_buf_length) {
      priv->recv_buf_length = MAX (priv->recv_buf_length * 2, 1024);
      priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_buf_length);
    }

    assert_ring_buffer_valid (priv);

    /* Read some data into the buffer. Use two GInputVectors: one for the tail
     * of the buffer and one for the head. */
    has_wrapped =
        (priv->recv_buf_pos + priv->recv_buf_fill) > priv->recv_buf_length;

    if (has_wrapped) {
      local_recv_bufs[0].buffer =
           priv->recv_buf + (priv->recv_buf_pos + priv->recv_buf_fill) %
           priv->recv_buf_length;
      local_recv_bufs[0].size = priv->recv_buf_length - priv->recv_buf_fill;
      local_recv_bufs[1].buffer = NULL;
      local_recv_bufs[1].size = 0;
    } else {
      local_recv_bufs[0].buffer =
          priv->recv_buf + priv->recv_buf_pos + priv->recv_buf_fill;
      local_recv_bufs[0].size =
          priv->recv_buf_length - (priv->recv_buf_pos + priv->recv_buf_fill);
      local_recv_bufs[1].buffer = priv->recv_buf;
      local_recv_bufs[1].size = priv->recv_buf_pos;
    }

    if (priv->base_socket) {
      ret = nice_socket_recv_messages (priv->base_socket,
          &local_recv_message, 1);
    }

    if (ret <= 0)
      return ret;

    /* Update the buffer’s metadata. */
    priv->recv_buf_fill += local_recv_message.length;
    assert_ring_buffer_valid (priv);

    /* Fall through and try parsing the newly received data. */
  }

#define GET_BYTE(pos) \
  priv->recv_buf[(pos + priv->recv_buf_pos) % priv->recv_buf_length]
#define EAT_WHITESPACE(pos) \
  while (pos < priv->recv_buf_fill && GET_BYTE(pos) == ' ') \
    pos++; \
  if (pos >= priv->recv_buf_fill) \
    goto not_enough_data;

retry:
  nice_debug ("Receiving from HTTP proxy (state %d) : %" G_GSSIZE_FORMAT " \n"
      "'%s'", priv->state, priv->recv_buf_fill,
      priv->recv_buf + priv->recv_buf_pos);

  switch (priv->state) {
    case HTTP_STATE_INIT:
      {
        /* This is a logical position in the recv_buf; add
         * (priv->recv_buf + priv->recv_buf_pos) to get the actual byte in
         * memory. */
        guint pos = 0;

        /* Eat leading whitespace and check we have enough data. */
        EAT_WHITESPACE (pos);

        if (pos + 7 > priv->recv_buf_fill)
          goto not_enough_data;
        if (GET_BYTE (pos + 0) != 'H' ||
            GET_BYTE (pos + 1) != 'T' ||
            GET_BYTE (pos + 2) != 'T' ||
            GET_BYTE (pos + 3) != 'P' ||
            GET_BYTE (pos + 4) != '/' ||
            GET_BYTE (pos + 5) != '1' ||
            GET_BYTE (pos + 6) != '.')
          goto error;
        pos += 7;

        if (pos >= priv->recv_buf_fill)
          goto not_enough_data;
        if (GET_BYTE (pos) != '0' && GET_BYTE (pos) != '1')
          goto error;
        pos++;

        /* Make sure we have a space after the HTTP version */
        if (pos >= priv->recv_buf_fill)
          goto not_enough_data;
        if (GET_BYTE (pos) != ' ')
          goto error;

        EAT_WHITESPACE (pos);

        /* Check for a successful 2xx code */
        if (pos + 3 > priv->recv_buf_fill)
          goto not_enough_data;
        if (GET_BYTE (pos) != '2' ||
            GET_BYTE (pos + 1) < '0' || GET_BYTE (pos + 1) > '9' ||
            GET_BYTE (pos + 2) < '0' || GET_BYTE (pos + 2) > '9')
          goto error;

        /* Clear any trailing chars */
        while (pos + 1 < priv->recv_buf_fill &&
            GET_BYTE (pos) != '\r' && GET_BYTE (pos + 1) != '\n')
          pos++;
        if (pos + 1 >= priv->recv_buf_fill)
          goto not_enough_data;
        pos += 2;

        /* Consume the data we just parsed. */
        priv->recv_buf_pos = (priv->recv_buf_pos + pos) % priv->recv_buf_length;
        priv->recv_buf_fill -= pos;

        priv->content_length = 0;
        priv->state = HTTP_STATE_HEADERS;

        goto retry;
      }
      break;
    case HTTP_STATE_HEADERS:
      {
        guint pos = 0;

        if (pos + 15 < priv->recv_buf_fill &&
            (GET_BYTE (pos +  0) == 'C' || GET_BYTE (pos +  0) == 'c') &&
            (GET_BYTE (pos +  1) == 'o' || GET_BYTE (pos +  1) == 'O') &&
            (GET_BYTE (pos +  2) == 'n' || GET_BYTE (pos +  2) == 'N') &&
            (GET_BYTE (pos +  3) == 't' || GET_BYTE (pos +  3) == 'T') &&
            (GET_BYTE (pos +  4) == 'e' || GET_BYTE (pos +  4) == 'E') &&
            (GET_BYTE (pos +  5) == 'n' || GET_BYTE (pos +  5) == 'N') &&
            (GET_BYTE (pos +  6) == 't' || GET_BYTE (pos +  6) == 'T') &&
             GET_BYTE (pos +  7) == '-' &&
            (GET_BYTE (pos +  8) == 'L' || GET_BYTE (pos +  8) == 'l') &&
            (GET_BYTE (pos +  9) == 'e' || GET_BYTE (pos +  9) == 'E') &&
            (GET_BYTE (pos + 10) == 'n' || GET_BYTE (pos + 10) == 'N') &&
            (GET_BYTE (pos + 11) == 'g' || GET_BYTE (pos + 11) == 'G') &&
            (GET_BYTE (pos + 12) == 't' || GET_BYTE (pos + 12) == 'T') &&
            (GET_BYTE (pos + 13) == 'h' || GET_BYTE (pos + 13) == 'H') &&
             GET_BYTE (pos + 14) == ':') {
          /* Found a Content-Length header. Parse and store the value. Note that
           * the HTTP standard allows for arbitrarily-big content lengths. We
           * limit it to G_MAXSIZE for sanity’s sake.
           *
           * The code below is equivalent to strtoul(input, NULL, 10), but
           * operates on a ring buffer. */
          pos += 15;
          EAT_WHITESPACE (pos);
          priv->content_length = 0;

          while (TRUE) {
            guint8 byte = GET_BYTE (pos);
            gint val = g_ascii_digit_value (byte);

            if (byte == '\r') {
              /* Reached the end of the value; fall out to the code below which
               * will grab the \n. */
              break;
            } else if (val == -1) {
              priv->content_length = 0;
              goto error;
            }

            /* Check for overflow. Don’t flag it as an error; just fall through
             * to the code below which will skip to the \r\n. */
            if (priv->content_length > G_MAXSIZE / 10 ||
                priv->content_length * 10 > G_MAXSIZE - val) {
              priv->content_length = 0;
              break;
            }

            priv->content_length = (priv->content_length * 10) + val;

            if (pos + 1 > priv->recv_buf_fill)
              goto not_enough_data;
            pos++;
          }
        }

        /* Skip over the header. */
        while (pos + 1 < priv->recv_buf_fill &&
            GET_BYTE (pos) != '\r' && GET_BYTE (pos + 1) != '\n')
          pos++;

        nice_debug ("pos = %u, fill = %" G_GSSIZE_FORMAT,
            pos, priv->recv_buf_fill);

        if (pos + 1 >= priv->recv_buf_fill)
          goto not_enough_data;
        pos += 2;

        /* Consume the data we just parsed. */
        priv->recv_buf_pos = (priv->recv_buf_pos + pos) % priv->recv_buf_length;
        priv->recv_buf_fill -= pos;

        if (pos == 2)
          priv->state = HTTP_STATE_BODY;

        goto retry;
      }
      break;
    case HTTP_STATE_BODY:
      {
        gsize consumed;

        if (priv->content_length == 0) {
          priv->state = HTTP_STATE_CONNECTED;
          goto retry;
        }

        if (priv->recv_buf_fill == 0)
          goto not_enough_data;

        consumed = MIN (priv->content_length, priv->recv_buf_fill);

        priv->recv_buf_pos =
            (priv->recv_buf_pos + consumed) % priv->recv_buf_length;
        priv->recv_buf_fill -= consumed;
        priv->content_length -= consumed;

        goto retry;
      }
      break;
    case HTTP_STATE_CONNECTED:
      {
        gsize len;

        len = memcpy_ring_buffer_to_input_messages (priv,
            recv_messages, n_recv_messages);

        /* Send the pending data */
        nice_socket_flush_send_queue (priv->base_socket,
            &priv->send_queue);

        return len;
      }
      break;
    case HTTP_STATE_ERROR:
    default:
      /* Unknown status */
      goto error;
  }

 not_enough_data:
  return 0;

 error:
  nice_debug ("http error");
  if (priv->base_socket)
    nice_socket_free (priv->base_socket);
  priv->base_socket = NULL;
  priv->state = HTTP_STATE_ERROR;

  return -1;
}
Exemplo n.º 20
0
/* This is called with the global agent lock released. It does not take that
 * lock, but does take the io_mutex. */
static gboolean
emit_io_callback_cb (gpointer user_data)
{
  NiceComponent *component = user_data;
  IOCallbackData *data;
  NiceAgentRecvFunc io_callback;
  gpointer io_user_data;
  guint stream_id, component_id;
  NiceAgent *agent;

  agent = component->agent;

  g_object_ref (agent);

  stream_id = component->stream->id;
  component_id = component->id;

  g_mutex_lock (&component->io_mutex);

  /* The members of Component are guaranteed not to have changed since this
   * GSource was attached in nice_component_emit_io_callback(). The Component’s agent
   * and stream are immutable after construction, as are the stream and
   * component IDs. The callback and its user data may have changed, but are
   * guaranteed to be non-%NULL at the start as the idle source is removed when
   * the callback is set to %NULL. They may become %NULL during the io_callback,
   * so must be re-checked every loop iteration. The data buffer is copied into
   * the #IOCallbackData closure.
   *
   * If the component is destroyed (which happens if the agent or stream are
   * destroyed) between attaching the GSource and firing it, the GSource is
   * detached during dispose and this callback is never invoked. If the
   * agent is destroyed during an io_callback, its weak pointer will be
   * nullified. Similarly, the Component needs to be re-queried for after every
   * iteration, just in case the client has removed the stream in the
   * callback. */
  while (TRUE) {
    io_callback = component->io_callback;
    io_user_data = component->io_user_data;
    data = g_queue_peek_head (&component->pending_io_messages);

    if (data == NULL || io_callback == NULL)
      break;

    g_mutex_unlock (&component->io_mutex);

    io_callback (agent, stream_id, component_id,
        data->buf_len - data->offset, (gchar *) data->buf + data->offset,
        io_user_data);

    /* Check for the user destroying things underneath our feet. */
    if (!agent_find_component (agent, stream_id, component_id,
            NULL, &component)) {
      nice_debug ("%s: Agent or component destroyed.", G_STRFUNC);
      goto done;
    }

    g_queue_pop_head (&component->pending_io_messages);
    io_callback_data_free (data);

    g_mutex_lock (&component->io_mutex);
  }

  component->io_callback_id = 0;
  g_mutex_unlock (&component->io_mutex);

 done:
  g_object_unref (agent);

  return G_SOURCE_REMOVE;
}
Exemplo n.º 21
0
static gboolean
socket_send_more (
  GSocket *gsocket,
  GIOCondition condition,
  gpointer data)
{
  NiceSocket *sock = (NiceSocket *) data;
  TcpPriv *priv = sock->priv;
  struct to_be_sent *tbs = NULL;
  GError *gerr = NULL;

  agent_lock ();

  if (g_source_is_destroyed (g_main_current_source ())) {
    nice_debug ("Source was destroyed. "
        "Avoided race condition in tcp-bsd.c:socket_send_more");
    agent_unlock ();
    return FALSE;
  }

  while ((tbs = g_queue_pop_head (&priv->send_queue)) != NULL) {
    int ret;

    if(condition & G_IO_HUP) {
      /* connection hangs up */
      ret = -1;
    } else {
      GOutputVector local_bufs = { tbs->buf, tbs->length };
      ret = g_socket_send_message (sock->fileno, NULL, &local_bufs, 1, NULL, 0,
          G_SOCKET_MSG_NONE, NULL, &gerr);
    }

    if (ret < 0) {
      if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
        GOutputVector local_buf = { tbs->buf, tbs->length };
        NiceOutputMessage local_message = {&local_buf, 1};

        add_to_be_sent (sock, &local_message, 0, local_buf.size, TRUE);
        free_to_be_sent (tbs);
        g_error_free (gerr);
        break;
      }
      g_clear_error (&gerr);
    } else if (ret < (int) tbs->length) {
      GOutputVector local_buf = { tbs->buf + ret, tbs->length - ret };
      NiceOutputMessage local_message = {&local_buf, 1};

      add_to_be_sent (sock, &local_message, 0, local_buf.size, TRUE);
      free_to_be_sent (tbs);
      break;
    }

    free_to_be_sent (tbs);
  }

  if (g_queue_is_empty (&priv->send_queue)) {
    g_source_destroy (priv->io_source);
    g_source_unref (priv->io_source);
    priv->io_source = NULL;

    agent_unlock ();
    return FALSE;
  }

  agent_unlock ();
  return TRUE;
}
Exemplo n.º 22
0
/* 
 * Timer callback that handles scheduling new candidate discovery
 * processes (paced by the Ta timer), and handles running of the 
 * existing discovery processes.
 *
 * This function is designed for the g_timeout_add() interface.
 *
 * @return will return FALSE when no more pending timers.
 */
static gboolean priv_discovery_tick_unlocked (gpointer pointer)
{
  CandidateDiscovery *cand;
  NiceAgent *agent = pointer;
  GSList *i;
  int not_done = 0; /* note: track whether to continue timer */
  size_t buffer_len = 0;

  {
    static int tick_counter = 0;
    if (tick_counter++ % 50 == 0)
      nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
  }

  for (i = agent->discovery_list; i ; i = i->next) {
    cand = i->data;

    if (cand->pending != TRUE) {
      cand->pending = TRUE;

      if (agent->discovery_unsched_items)
	--agent->discovery_unsched_items;

      if (nice_debug_is_enabled ()) {
        gchar tmpbuf[INET6_ADDRSTRLEN];
        nice_address_to_string (&cand->server, tmpbuf);
        nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.",
            agent, cand->type, tmpbuf);
      }
      if (nice_address_is_valid (&cand->server) &&
          (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
              cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {

        if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED ||
            cand->component->state == NICE_COMPONENT_STATE_FAILED)
          agent_signal_component_state_change (agent,
					       cand->stream->id,
					       cand->component->id,
					       NICE_COMPONENT_STATE_GATHERING);

        if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
          buffer_len = stun_usage_bind_create (&cand->stun_agent,
              &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
        } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
          uint8_t *username = (uint8_t *)cand->turn->username;
          gsize username_len = strlen (cand->turn->username);
          uint8_t *password = (uint8_t *)cand->turn->password;
          gsize password_len = strlen (cand->turn->password);
          StunUsageTurnCompatibility turn_compat =
              agent_to_turn_compatibility (agent);

          if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
              turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
            username = g_base64_decode ((gchar *)username, &username_len);
            password = g_base64_decode ((gchar *)password, &password_len);
          }

          buffer_len = stun_usage_turn_create (&cand->stun_agent,
              &cand->stun_message,  cand->stun_buffer, sizeof(cand->stun_buffer),
              cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
              STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
              -1, -1,
              username, username_len,
              password, password_len,
              turn_compat);

          if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
              turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
            g_free (username);
            g_free (password);
          }
        }

	if (buffer_len > 0) {
          if (nice_socket_is_reliable (cand->nicesock)) {
            stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout);
          } else {
            stun_timer_start (&cand->timer,
                agent->stun_initial_timeout,
                agent->stun_max_retransmissions);
          }

          /* send the conncheck */
          agent_socket_send (cand->nicesock, &cand->server,
              buffer_len, (gchar *)cand->stun_buffer);

	  /* case: success, start waiting for the result */
	  g_get_current_time (&cand->next_tick);

	} else {
	  /* case: error in starting discovery, start the next discovery */
	  cand->done = TRUE;
	  cand->stun_message.buffer = NULL;
	  cand->stun_message.buffer_len = 0;
	  continue;
	}
      }
      else
	/* allocate relayed candidates */
	g_assert_not_reached ();

      ++not_done; /* note: new discovery scheduled */
    }

    if (cand->done != TRUE) {
      GTimeVal now;

      g_get_current_time (&now);

      if (cand->stun_message.buffer == NULL) {
	nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
	cand->done = TRUE;
      }
      else if (priv_timer_expired (&cand->next_tick, &now)) {
        switch (stun_timer_refresh (&cand->timer)) {
          case STUN_USAGE_TIMER_RETURN_TIMEOUT:
            {
              /* Time out */
              /* case: error, abort processing */
              StunTransactionId id;

              stun_message_id (&cand->stun_message, id);
              stun_agent_forget_transaction (&cand->stun_agent, id);

              cand->done = TRUE;
              cand->stun_message.buffer = NULL;
              cand->stun_message.buffer_len = 0;
              nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
              break;
            }
          case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
            {
              /* case: not ready complete, so schedule next timeout */
              unsigned int timeout = stun_timer_remainder (&cand->timer);

              stun_debug ("STUN transaction retransmitted (timeout %dms).",
                  timeout);

              /* retransmit */
              agent_socket_send (cand->nicesock, &cand->server,
                  stun_message_length (&cand->stun_message),
                  (gchar *)cand->stun_buffer);

              /* note: convert from milli to microseconds for g_time_val_add() */
              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

              ++not_done; /* note: retry later */
              break;
            }
          case STUN_USAGE_TIMER_RETURN_SUCCESS:
            {
              unsigned int timeout = stun_timer_remainder (&cand->timer);

              cand->next_tick = now;
              g_time_val_add (&cand->next_tick, timeout * 1000);

              ++not_done; /* note: retry later */
              break;
            }
          default:
            /* Nothing to do. */
            break;
	}

      } else {
	++not_done; /* note: discovery not expired yet */
      }
    }
  }

  if (not_done == 0) {
    nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);

    discovery_free (agent);

    agent_gathering_done (agent);

    /* note: no pending timers, return FALSE to stop timer */
    return FALSE;
  }

  return TRUE;
}