/* * This file is part of the Nice GLib ICE library. * * (C) 2006-2009 Collabora Ltd. * Contact: Youness Alaoui * (C) 2006-2009 Nokia Corporation. All rights reserved. * Contact: Kai Vehmanen * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Nice GLib ICE library. * * The Initial Developers of the Original Code are Collabora Ltd and Nokia * Corporation. All Rights Reserved. * * Contributors: * Dafydd Harries, Collabora Ltd. * Youness Alaoui, Collabora Ltd. * Kai Vehmanen, Nokia * * Alternatively, the contents of this file may be used under the terms of the * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * case the provisions of LGPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * LGPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replace * them with the notice and other provisions required by the LGPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the LGPL. */ /* * @file component.c * @brief ICE component functions */ #ifdef HAVE_CONFIG_H # include #endif #include #include "debug.h" #include "component.h" #include "agent-priv.h" static void socket_source_detach (SocketSource *source) { if (source->source != NULL) { g_source_destroy (source->source); g_source_unref (source->source); } source->source = NULL; } static void socket_source_free (SocketSource *source) { nice_socket_free (source->socket); socket_source_detach (source); g_slice_free (SocketSource, source); } Component * component_new (guint id) { Component *component; component = g_slice_new0 (Component); component->id = id; component->state = NICE_COMPONENT_STATE_DISCONNECTED; component->restart_candidate = NULL; component->tcp = NULL; return component; } 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->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_full (cmp->socket_sources, (GDestroyNotify) socket_source_free); 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; } if (cmp->ctx != NULL) { g_main_context_unref (cmp->ctx); cmp->ctx = NULL; } g_slice_free (Component, cmp); } /* * Finds a candidate pair that has matching foundation ids. * * @return TRUE if pair found, pointer to pair stored at 'pair' */ gboolean component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair) { GSList *i; CandidatePair result; memset (&result, 0, sizeof(result)); for (i = cmp->local_candidates; i; i = i->next) { NiceCandidate *candidate = i->data; if (strncmp (candidate->foundation, lfoundation, NICE_CANDIDATE_MAX_FOUNDATION) == 0) { result.local = candidate; break; } } for (i = cmp->remote_candidates; i; i = i->next) { NiceCandidate *candidate = i->data; if (strncmp (candidate->foundation, rfoundation, NICE_CANDIDATE_MAX_FOUNDATION) == 0) { result.remote = candidate; break; } } if (result.local && result.remote) { result.priority = agent_candidate_pair_priority (agent, result.local, result.remote); if (pair) *pair = result; return TRUE; } return FALSE; } /* * Resets the component state to that of a ICE restarted * session. */ gboolean component_restart (Component *cmp) { GSList *i; for (i = cmp->remote_candidates; i; i = i->next) { NiceCandidate *candidate = i->data; /* note: do not remove the remote candidate that is * currently part of the 'selected pair', see ICE * 9.1.1.1. "ICE Restarts" (ID-19) */ if (candidate == cmp->selected_pair.remote) { if (cmp->restart_candidate) nice_candidate_free (cmp->restart_candidate); cmp->restart_candidate = candidate; } else nice_candidate_free (candidate); } g_slist_free (cmp->remote_candidates), cmp->remote_candidates = NULL; 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->incoming_checks); cmp->incoming_checks = NULL; /* note: component state managed by agent */ return TRUE; } /* * 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; } /* * Finds a remote candidate with matching address and * transport. * * @return pointer to candidate or NULL if not found */ NiceCandidate * component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport) { GSList *i; for (i = component->remote_candidates; i; i = i->next) { NiceCandidate *candidate = i->data; if (nice_address_equal(&candidate->addr, addr) && candidate->transport == transport) return candidate; } return NULL; } /* * Sets the desired remote candidate as the selected pair * * It will start sending on the highest priority pair available with * this candidate. */ NiceCandidate * component_set_selected_remote_candidate (NiceAgent *agent, Component *component, NiceCandidate *candidate) { NiceCandidate *local = NULL; NiceCandidate *remote = NULL; guint64 priority = 0; GSList *item = NULL; for (item = component->local_candidates; item; item = g_slist_next (item)) { NiceCandidate *tmp = item->data; guint64 tmp_prio = 0; if (tmp->transport != candidate->transport || tmp->addr.s.addr.sa_family != candidate->addr.s.addr.sa_family || tmp->type != NICE_CANDIDATE_TYPE_HOST) continue; tmp_prio = agent_candidate_pair_priority (agent, tmp, candidate); if (tmp_prio > priority) { priority = tmp_prio; local = tmp; } } if (local == NULL) return NULL; remote = component_find_remote_candidate (component, &candidate->addr, candidate->transport); if (!remote) { remote = nice_candidate_copy (candidate); component->remote_candidates = g_slist_append (component->remote_candidates, remote); agent_signal_new_remote_candidate (agent, remote); } 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 = local; component->selected_pair.remote = remote; component->selected_pair.priority = priority; return local; } static gint _find_socket_source (gconstpointer a, gconstpointer b) { const SocketSource *source_a = a; const NiceSocket *socket_b = b; return (source_a->socket == socket_b) ? 0 : 1; } /* This takes ownership of socket and source. * It attaches the source to the component’s context. */ void component_add_socket_source (Component *component, NiceSocket *socket, GSource *source) { GSList *l; SocketSource *socket_source; g_assert (component != NULL); g_assert (socket != NULL); g_assert (source != NULL); /* Find an existing SocketSource in the component which contains socket, or * create a new one. */ l = g_slist_find_custom (component->socket_sources, socket, _find_socket_source); if (l != NULL) { socket_source = l->data; } else { socket_source = g_slice_new0 (SocketSource); socket_source->socket = socket; component->socket_sources = g_slist_prepend (component->socket_sources, socket_source); } /* Add the source. */ g_assert (socket_source->source == NULL); g_assert (component->ctx != NULL); socket_source->source = source; g_source_attach (source, component->ctx); } void component_add_detached_socket (Component *component, NiceSocket *socket) { SocketSource *socket_source; socket_source = g_slice_new0 (SocketSource); socket_source->socket = socket; socket_source->source = NULL; component->socket_sources = g_slist_prepend (component->socket_sources, socket_source); } /** * component_detach_socket_source: * @component: a #Component * @socket: the socket to detach the source for * * Detach the #GSource for the single specified @socket. Leave the socket itself * untouched. * * If the @socket doesn’t exist in this @component, do nothing. */ void component_detach_socket_source (Component *component, NiceSocket *socket) { GSList *l; SocketSource *socket_source; /* Find the SocketSource for the socket. */ l = g_slist_find_custom (component->socket_sources, socket, _find_socket_source); if (l == NULL) return; /* Detach the source. */ socket_source = l->data; socket_source_detach (socket_source); } /* * Detaches socket handles of @component from the main context. Leaves the * sockets themselves untouched. */ void component_detach_socket_sources (Component *component) { GSList *i; for (i = component->socket_sources; i != NULL; i = i->next) { SocketSource *socket_source = i->data; nice_debug ("Detach source %p.", socket_source->source); socket_source_detach (socket_source); } } void component_free_socket_sources (Component *component) { g_slist_free_full (component->socket_sources, (GDestroyNotify) socket_source_free); component->socket_sources = NULL; }