static void socket_source_free (SocketSource *source) { socket_source_detach (source); nice_socket_free (source->socket); g_slice_free (SocketSource, source); }
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; }
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; }
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; }
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; }
/* 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; }
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); }
/* * 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; }
/* * 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; }