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; }
/* 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); }
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); }
/* 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); }
/* * 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; }
/* 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); } }
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; }
/* * 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); } }
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); }
/* 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); } }
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; }
/* 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); }
/* 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); }
/* * 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; }
/* 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); }
/* * 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; }
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; }
/** * 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); }
/* 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; }
/* 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; }
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; }
/* * 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; }