Example #1
0
static void
socket_source_free (SocketSource *source)
{
  socket_source_detach (source);
  nice_socket_free (source->socket);

  g_slice_free (SocketSource, source);
}
Example #2
0
static void
socket_close (NiceSocket *sock)
{
  TurnTcpPriv *priv = sock->priv;

  if (priv->base_socket)
    nice_socket_free (priv->base_socket);

  g_slice_free(TurnTcpPriv, sock->priv);
}
static void
socket_close (NiceSocket *sock)
{
  PseudoSSLPriv *priv = sock->priv;

  if (priv->base_socket)
    nice_socket_free (priv->base_socket);

  nice_socket_free_send_queue (&priv->send_queue);

  g_slice_free(PseudoSSLPriv, sock->priv);
  sock->priv = NULL;
}
Example #4
0
static void
socket_close (NiceSocket *sock)
{
  PseudoSSLPriv *priv = sock->priv;

  if (priv->base_socket)
    nice_socket_free (priv->base_socket);

  g_queue_foreach (&priv->send_queue, (GFunc) free_to_be_sent, NULL);
  g_queue_clear (&priv->send_queue);

  g_slice_free(PseudoSSLPriv, sock->priv);
}
static gint
socket_recv_messages (NiceSocket *sock,
    NiceInputMessage *recv_messages, guint n_recv_messages)
{
  PseudoSSLPriv *priv = sock->priv;

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

  if (priv->handshaken) {
    if (priv->base_socket) {
      /* Fast path: once we’ve done the handshake, pass straight through to the
       * base socket. */
      return nice_socket_recv_messages (priv->base_socket,
          recv_messages, n_recv_messages);
    }
  } else {
    guint8 data[MAX(sizeof(SSL_SERVER_GOOGLE_HANDSHAKE),
          sizeof(SSL_SERVER_MSOC_HANDSHAKE))];
    gint ret = -1;
    GInputVector local_recv_buf = { data, sizeof(data) };
    NiceInputMessage local_recv_message = { &local_recv_buf, 1, NULL, 0 };


    if (priv->compatibility == NICE_PSEUDOSSL_SOCKET_COMPATIBILITY_MSOC) {
      local_recv_buf.size = sizeof(SSL_SERVER_MSOC_HANDSHAKE);
    } else {
      local_recv_buf.size = sizeof(SSL_SERVER_GOOGLE_HANDSHAKE);
    }
    if (priv->base_socket) {
      ret = nice_socket_recv_messages (priv->base_socket,
          &local_recv_message, 1);
    }

    if (ret <= 0) {
      return ret;
    } else if (ret == 1 && server_handshake_valid(sock, &local_recv_buf,
            local_recv_message.length)) {
      priv->handshaken = TRUE;
      nice_socket_flush_send_queue (priv->base_socket, &priv->send_queue);
    } else {
      if (priv->base_socket)
        nice_socket_free (priv->base_socket);
      priv->base_socket = NULL;

      return -1;
    }
  }
  return 0;
}
Example #6
0
static void
socket_close (NiceSocket *sock)
{
  HttpPriv *priv = sock->priv;

  if (priv->base_socket)
    nice_socket_free (priv->base_socket);

  if (priv->username)
    g_free (priv->username);

  if (priv->password)
    g_free (priv->password);

  if (priv->recv_buf)
    g_free (priv->recv_buf);

  nice_socket_free_send_queue (&priv->send_queue);

  g_slice_free(HttpPriv, sock->priv);
  sock->priv = NULL;
}
Example #7
0
static gint
socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
{
  PseudoSSLPriv *priv = sock->priv;

  if (priv->handshaken) {
    if (priv->base_socket)
      return nice_socket_recv (priv->base_socket, from, len, buf);
  } else {
    gchar data[sizeof(SSL_SERVER_HANDSHAKE)];
    gint ret  = -1;

    if (priv->base_socket)
      ret = nice_socket_recv (priv->base_socket, from, sizeof(data), data);

    if (ret <= 0) {
      return ret;
    } else if ((guint) ret == sizeof(SSL_SERVER_HANDSHAKE) &&
        memcmp(SSL_SERVER_HANDSHAKE, data, sizeof(SSL_SERVER_HANDSHAKE)) == 0) {
      struct to_be_sent *tbs = NULL;
      priv->handshaken = TRUE;
      while ((tbs = g_queue_pop_head (&priv->send_queue))) {
        nice_socket_send (priv->base_socket, &tbs->to, tbs->length, tbs->buf);
        g_free (tbs->buf);
        g_slice_free (struct to_be_sent, tbs);
      }
    } else {
      if (priv->base_socket)
        nice_socket_free (priv->base_socket);
      priv->base_socket = NULL;

      return -1;
    }
  }
  return 0;
}
Example #8
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;
}
Example #9
0
void
component_free (Component *cmp)
{
  GSList *i;
  GList *item;

  for (i = cmp->local_candidates; i; i = i->next) {
    NiceCandidate *candidate = i->data;
    nice_candidate_free (candidate);
  }

  for (i = cmp->remote_candidates; i; i = i->next) {
    NiceCandidate *candidate = i->data;
    nice_candidate_free (candidate);
  }

  if (cmp->restart_candidate)
    nice_candidate_free (cmp->restart_candidate),
      cmp->restart_candidate = NULL;

  for (i = cmp->sockets; i; i = i->next) {
    NiceSocket *udpsocket = i->data;
    nice_socket_free (udpsocket);
  }

  for (i = cmp->gsources; i; i = i->next) {
    GSource *source = i->data;
    g_source_destroy (source);
    g_source_unref (source);
  }
 
  for (i = cmp->incoming_checks; i; i = i->next) {
    IncomingCheck *icheck = i->data;
    g_free (icheck->username);
    g_slice_free (IncomingCheck, icheck);
  }

  g_slist_free (cmp->local_candidates);
  g_slist_free (cmp->remote_candidates);
  g_slist_free (cmp->sockets);
  g_slist_free (cmp->gsources);
  g_slist_free (cmp->incoming_checks);

  for (item = cmp->turn_servers; item; item = g_list_next (item)) {
    TurnServer *turn = item->data;
    g_free (turn->username);
    g_free (turn->password);
    g_slice_free (TurnServer, turn);
  }
  g_list_free (cmp->turn_servers);

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

  if (cmp->tcp_clock) {
    g_source_destroy (cmp->tcp_clock);
    g_source_unref (cmp->tcp_clock);
    cmp->tcp_clock = NULL;
  }
  if (cmp->tcp) {
    pseudo_tcp_socket_close (cmp->tcp, TRUE);
    g_object_unref (cmp->tcp);
    cmp->tcp = NULL;
  }
  if (cmp->tcp_data != NULL) {
    g_slice_free (TcpUserData, cmp->tcp_data);
    cmp->tcp_data = NULL;
  }

  g_slice_free (Component, cmp);
}
Example #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;
}
Example #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;
}