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