Esempio n. 1
0
static gboolean
nice_output_stream_close (GOutputStream *stream, GCancellable *cancellable,
    GError **error)
{
  NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
  NiceComponent *component = NULL;
  NiceStream *_stream = NULL;
  NiceAgent *agent;  /* owned */

  /* Has the agent disappeared? */
  agent = g_weak_ref_get (&priv->agent_ref);
  if (agent == NULL)
    return TRUE;

  agent_lock ();

  /* Shut down the write side of the pseudo-TCP stream. */
  if (agent_find_component (agent, priv->stream_id, priv->component_id,
          &_stream, &component) && agent->reliable &&
      !pseudo_tcp_socket_is_closed (component->tcp)) {
    pseudo_tcp_socket_shutdown (component->tcp, PSEUDO_TCP_SHUTDOWN_WR);
  }

  agent_unlock ();

  g_object_unref (agent);

  return TRUE;
}
Esempio n. 2
0
/*
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate*
discovery_add_server_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
  NiceCandidateTransport transport,
  NiceSocket *base_socket,
  gboolean nat_assisted)
{
  NiceCandidate *candidate;
  NiceComponent *component;
  NiceStream *stream;
  gboolean result = FALSE;

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
  candidate->transport = transport;
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;

  /* step: link to the base candidate+socket */
  candidate->sockptr = base_socket;
  candidate->base_addr = base_socket->addr;

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, nat_assisted);
  } else {
    candidate->priority =  nice_candidate_ice_priority (candidate,
        agent->reliable, nat_assisted);
  }

  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);

  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
  if (result) {
    agent_signal_new_candidate (agent, candidate);
  }
  else {
    /* error: duplicate candidate */
    nice_candidate_free (candidate), candidate = NULL;
  }

  return candidate;
}
Esempio n. 3
0
static GSource *
nice_output_stream_create_source (GPollableOutputStream *stream,
    GCancellable *cancellable)
{
  NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
  GSource *component_source = NULL;
  NiceComponent *component = NULL;
  NiceStream *_stream = NULL;
  NiceAgent *agent;  /* owned */

  component_source = g_pollable_source_new (G_OBJECT (stream));

  if (cancellable) {
    GSource *cancellable_source = g_cancellable_source_new (cancellable);

    g_source_set_dummy_callback (cancellable_source);
    g_source_add_child_source (component_source, cancellable_source);
    g_source_unref (cancellable_source);
  }

  /* Closed streams cannot have sources. */
  if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream)))
    return component_source;

  /* Has the agent disappeared? */
  agent = g_weak_ref_get (&priv->agent_ref);
  if (agent == NULL)
    return component_source;

  agent_lock ();

  /* Grab the socket for this component. */
  if (!agent_find_component (agent, priv->stream_id, priv->component_id,
          &_stream, &component)) {
    g_warning ("Could not find component %u in stream %u", priv->component_id,
        priv->stream_id);
    goto done;
  }

   if (component->tcp_writable_cancellable) {
    GSource *cancellable_source =
        g_cancellable_source_new (component->tcp_writable_cancellable);

    g_source_set_dummy_callback (cancellable_source);
    g_source_add_child_source (component_source, cancellable_source);
    g_source_unref (cancellable_source);
  }

done:
  agent_unlock ();

  g_object_unref (agent);

  return component_source;
}
Esempio n. 4
0
static gboolean
nice_input_stream_is_readable (GPollableInputStream *stream)
{
  NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv;
  Component *component = NULL;
  Stream *_stream = NULL;
  gboolean retval = FALSE;
  GSList *i;
  NiceAgent *agent;  /* owned */

  /* Closed streams are not readable. */
  if (g_input_stream_is_closed (G_INPUT_STREAM (stream)))
    return FALSE;

  /* Has the agent disappeared? */
  agent = g_weak_ref_get (&priv->agent_ref);
  if (agent == NULL)
    return FALSE;

  agent_lock (agent);

  if (!agent_find_component (agent, priv->stream_id, priv->component_id,
          &_stream, &component)) {
    g_warning ("Could not find component %u in stream %u", priv->component_id,
        priv->stream_id);
    goto done;
  }

  /* If it’s a reliable agent, see if there’s any pending data in the pseudo-TCP
   * buffer. */
  if (agent->reliable &&
      pseudo_tcp_socket_get_available_bytes (component->tcp) > 0) {
    retval = TRUE;
    goto done;
  }

  /* Check whether any of the component’s FDs are pollable. */
  for (i = component->socket_sources; i != NULL; i = i->next) {
    SocketSource *socket_source = i->data;
    NiceSocket *nicesock = socket_source->socket;

    if (g_socket_condition_check (nicesock->fileno, G_IO_IN) != 0) {
      retval = TRUE;
      break;
    }
  }

done:
  agent_unlock (agent);

  g_object_unref (agent);

  return retval;
}
Esempio n. 5
0
static gboolean
nice_output_stream_is_writable (GPollableOutputStream *stream)
{
  NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
  NiceComponent *component = NULL;
  NiceStream *_stream = NULL;
  gboolean retval = FALSE;
  NiceAgent *agent;  /* owned */

  /* Closed streams are not writeable. */
  if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream)))
    return FALSE;

  /* Has the agent disappeared? */
  agent = g_weak_ref_get (&priv->agent_ref);
  if (agent == NULL)
    return FALSE;

  agent_lock ();

  if (!agent_find_component (agent, priv->stream_id, priv->component_id,
          &_stream, &component)) {
    g_warning ("Could not find component %u in stream %u", priv->component_id,
        priv->stream_id);
    goto done;
  }
  if (component->selected_pair.local != NULL) {
    NiceSocket *sockptr = component->selected_pair.local->sockptr;

    /* If it’s a reliable agent, see if there’s any space in the pseudo-TCP
     * output buffer. */
    if (!nice_socket_is_reliable (sockptr)) {
      retval = pseudo_tcp_socket_can_send (component->tcp);
    } else {
      retval = (g_socket_condition_check (sockptr->fileno, G_IO_OUT) != 0);
    }
  }

done:
  agent_unlock ();

  g_object_unref (agent);

  return retval;
}
Esempio n. 6
0
/*
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id' for each TCP_PASSIVE and TCP_ACTIVE candidates for each
 * base address.
 *
 * @return pointer to the created candidate, or NULL on error
 */
void
discovery_discover_tcp_server_reflexive_candidates (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
  NiceSocket *base_socket)
{
  NiceComponent *component;
  NiceStream *stream;
  NiceAddress base_addr = base_socket->addr;
  GSList *i;

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return;

  nice_address_set_port (&base_addr, 0);
  for (i = component->local_candidates; i; i = i ->next) {
    NiceCandidate *c = i->data;
    NiceAddress caddr;

    caddr = c->addr;
    nice_address_set_port (&caddr, 0);
    if (agent->force_relay == FALSE &&
        c->transport != NICE_CANDIDATE_TRANSPORT_UDP &&
        c->type == NICE_CANDIDATE_TYPE_HOST &&
        nice_address_equal (&base_addr, &caddr)) {
      nice_address_set_port (address, nice_address_get_port (&c->addr));
      discovery_add_server_reflexive_candidate (
          agent,
          stream_id,
          component_id,
          address,
          c->transport,
          c->sockptr,
          FALSE);
    }
  }
}
Esempio n. 7
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;
}
Esempio n. 8
0
static gboolean
component_source_prepare (GSource *source, gint *timeout_)
{
  ComponentSource *component_source = (ComponentSource *) source;
  NiceAgent *agent;
  NiceComponent *component;
  GSList *parentl, *childl;

  agent = g_weak_ref_get (&component_source->agent_ref);
  if (!agent)
    return FALSE;

  /* Needed due to accessing the Component. */
  agent_lock ();

  if (!agent_find_component (agent,
          component_source->stream_id, component_source->component_id, NULL,
          &component))
    goto done;


  if (component->socket_sources_age ==
      component_source->component_socket_sources_age)
    goto done;

  /* If the age has changed, either
   *  - one or more new socket has been prepended
   *  - old sockets have been removed
   */

  /* Add the new child sources. */

  for (parentl = component->socket_sources; parentl; parentl = parentl->next) {
    SocketSource *parent_socket_source = parentl->data;
    SocketSource *child_socket_source;

    if (parent_socket_source->socket->fileno == NULL)
      continue;

    /* Iterating the list of socket sources every time isn't a big problem
     * because the number of pairs is limited ~100 normally, so there will
     * rarely be more than 10.
     */
    childl = g_slist_find_custom (component_source->socket_sources,
        parent_socket_source->socket, _find_socket_source);

    /* If we have reached this state, then all sources new sources have been
     * added, because they are always prepended.
     */
    if (childl)
      break;

    child_socket_source = g_slice_new0 (SocketSource);
    child_socket_source->socket = parent_socket_source->socket;
    child_socket_source->source =
        g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN,
            NULL);
    g_source_set_dummy_callback (child_socket_source->source);
    g_source_add_child_source (source, child_socket_source->source);
    g_source_unref (child_socket_source->source);
    component_source->socket_sources =
        g_slist_prepend (component_source->socket_sources, child_socket_source);
  }


  for (childl = component_source->socket_sources;
       childl;) {
    SocketSource *child_socket_source = childl->data;
    GSList *next = childl->next;

    parentl = g_slist_find_custom (component->socket_sources,
      child_socket_source->socket, _find_socket_source);

    /* If this is not a currently used socket, remove the relevant source */
    if (!parentl) {
      g_source_remove_child_source (source, child_socket_source->source);
      g_slice_free (SocketSource, child_socket_source);
      component_source->socket_sources =
          g_slist_delete_link (component_source->socket_sources, childl);
    }

    childl = next;
  }


  /* Update the age. */
  component_source->component_socket_sources_age = component->socket_sources_age;

 done:

  agent_unlock_and_emit (agent);
  g_object_unref (agent);

  /* We can’t be sure if the ComponentSource itself needs to be dispatched until
   * poll() is called on all the child sources. */
  return FALSE;
}
Esempio n. 9
0
/*
 * Creates a peer reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate*
discovery_add_peer_reflexive_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
  NiceSocket *base_socket,
  NiceCandidate *local,
  NiceCandidate *remote)
{
  NiceCandidate *candidate;
  NiceComponent *component;
  NiceStream *stream;
  gboolean result;

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
  if (local)
    candidate->transport = local->transport;
  else if (remote)
    candidate->transport = conn_check_match_transport (remote->transport);
  else {
    if (base_socket->type == NICE_SOCKET_TYPE_UDP_BSD ||
        base_socket->type == NICE_SOCKET_TYPE_UDP_TURN)
      candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
    else
      candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
  }
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->sockptr = base_socket;
  candidate->base_addr = base_socket->addr;

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
  } else {
    candidate->priority = nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
  }

  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
  priv_assign_foundation (agent, candidate);

  if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
       agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
      remote && local) {
    guchar *new_username = NULL;
    guchar *decoded_local = NULL;
    guchar *decoded_remote = NULL;
    gsize local_size;
    gsize remote_size;
    g_free(candidate->username);
    g_free(candidate->password);

    decoded_local = g_base64_decode (local->username, &local_size);
    decoded_remote = g_base64_decode (remote->username, &remote_size);

    new_username = g_new0(guchar, local_size + remote_size);
    memcpy(new_username, decoded_local, local_size);
    memcpy(new_username + local_size, decoded_remote, remote_size);

    candidate->username = g_base64_encode (new_username, local_size + remote_size);
    g_free(new_username);
    g_free(decoded_local);
    g_free(decoded_remote);

    candidate->password = g_strdup(local->password);
  } else if (local) {
    g_free(candidate->username);
    g_free(candidate->password);

    candidate->username = g_strdup(local->username);
    candidate->password = g_strdup(local->password);
  }

  result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
  if (result != TRUE) {
    /* error: memory allocation, or duplicate candidate */
    nice_candidate_free (candidate), candidate = NULL;
  }

  return candidate;
}
Esempio n. 10
0
/*
 * Creates a server reflexive candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
NiceCandidate*
discovery_add_relay_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
  NiceCandidateTransport transport,
  NiceSocket *base_socket,
  TurnServer *turn)
{
  NiceCandidate *candidate;
  NiceComponent *component;
  NiceStream *stream;
  NiceSocket *relay_socket = NULL;

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return NULL;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
  candidate->transport = transport;
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->turn = turn_server_ref (turn);

  /* step: link to the base candidate+socket */
  relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
      base_socket, &turn->server,
      turn->username, turn->password,
      agent_to_turn_socket_compatibility (agent));
  if (!relay_socket)
    goto errors;

  candidate->sockptr = relay_socket;
  candidate->base_addr = base_socket->addr;

  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
  } else {
    candidate->priority =  nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
  }

  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
  priv_generate_candidate_credentials (agent, candidate);

  /* Google uses the turn username as the candidate username */
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    g_free (candidate->username);
    candidate->username = g_strdup (turn->username);
  }

  priv_assign_foundation (agent, candidate);

  if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
    goto errors;

  nice_component_attach_socket (component, relay_socket);
  agent_signal_new_candidate (agent, candidate);

  return candidate;

errors:
  nice_candidate_free (candidate);
  if (relay_socket)
    nice_socket_free (relay_socket);
  return NULL;
}
Esempio n. 11
0
/*
 * Creates a local host candidate for 'component_id' of stream
 * 'stream_id'.
 *
 * @return pointer to the created candidate, or NULL on error
 */
HostCandidateResult discovery_add_local_host_candidate (
  NiceAgent *agent,
  guint stream_id,
  guint component_id,
  NiceAddress *address,
  NiceCandidateTransport transport,
  NiceCandidate **outcandidate)
{
  NiceCandidate *candidate;
  NiceComponent *component;
  NiceStream *stream;
  NiceSocket *nicesock = NULL;
  HostCandidateResult res = HOST_CANDIDATE_FAILED;

  if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
    return res;

  candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
  candidate->transport = transport;
  candidate->stream_id = stream_id;
  candidate->component_id = component_id;
  candidate->addr = *address;
  candidate->base_addr = *address;
  if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
    candidate->priority = nice_candidate_jingle_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
             agent->compatibility == NICE_COMPATIBILITY_OC2007)  {
    candidate->priority = nice_candidate_msn_priority (candidate);
  } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
    candidate->priority =  nice_candidate_ms_ice_priority (candidate,
        agent->reliable, FALSE);
  } else {
    candidate->priority = nice_candidate_ice_priority (candidate,
        agent->reliable, FALSE);
  }

  candidate->priority = ensure_unique_priority (component,
      candidate->priority);
  priv_generate_candidate_credentials (agent, candidate);
  priv_assign_foundation (agent, candidate);

  /* note: candidate username and password are left NULL as stream
     level ufrag/password are used */
  if (transport == NICE_CANDIDATE_TRANSPORT_UDP) {
    nicesock = nice_udp_bsd_socket_new (address);
  } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
    nicesock = nice_tcp_active_socket_new (agent->main_context, address);
  } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
    nicesock = nice_tcp_passive_socket_new (agent->main_context, address);
  } else {
    /* TODO: Add TCP-SO */
  }
  if (!nicesock) {
    res = HOST_CANDIDATE_CANT_CREATE_SOCKET;
    goto errors;
  }

  candidate->sockptr = nicesock;
  candidate->addr = nicesock->addr;
  candidate->base_addr = nicesock->addr;

  if (!priv_add_local_candidate_pruned (agent, stream_id, component,
          candidate)) {
    res = HOST_CANDIDATE_REDUNDANT;
    goto errors;
  }

  _priv_set_socket_tos (agent, nicesock, stream->tos);
  nice_component_attach_socket (component, nicesock);

  *outcandidate = candidate;

  return HOST_CANDIDATE_SUCCESS;

errors:
  nice_candidate_free (candidate);
  if (nicesock)
    nice_socket_free (nicesock);
  return res;
}