Add method in agent to force the selection of a remote candidate
[sipe-libnice.git] / agent / agent.c
blob3eecbfe51956e71f1acdc1ef3c1c47adf3ba5680
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006, 2007 Collabora Ltd.
5 * Contact: Dafydd Harries
6 * (C) 2006, 2007 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
17 * License.
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
24 * Contributors:
25 * Dafydd Harries, Collabora Ltd.
26 * Kai Vehmanen, Nokia
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
39 /**
40 * @file agent.c
41 * @brief ICE agent API implementation
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif
48 #include <string.h>
49 #include <errno.h>
51 #include <sys/select.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
56 #include <glib.h>
58 #include "stun/usages/bind.h"
60 #include "udp.h"
61 #include "candidate.h"
62 #include "component.h"
63 #include "conncheck.h"
64 #include "discovery.h"
65 #include "agent.h"
66 #include "agent-priv.h"
67 #include "agent-signals-marshal.h"
69 #include "stream.h"
71 /* This is the max size of a UDP packet
72 * will it work tcp relaying??
74 #define MAX_BUFFER_SIZE 65536
75 #define DEFAULT_STUN_PORT 3478
78 G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT);
80 enum
82 PROP_SOCKET_FACTORY = 1,
83 PROP_COMPATIBILITY,
84 PROP_MAIN_CONTEXT,
85 PROP_STUN_SERVER,
86 PROP_STUN_SERVER_PORT,
87 PROP_TURN_SERVER,
88 PROP_TURN_SERVER_PORT,
89 PROP_CONTROLLING_MODE,
90 PROP_FULL_MODE,
91 PROP_STUN_PACING_TIMER,
92 PROP_MAX_CONNECTIVITY_CHECKS
96 enum
98 SIGNAL_COMPONENT_STATE_CHANGED,
99 SIGNAL_CANDIDATE_GATHERING_DONE,
100 SIGNAL_NEW_SELECTED_PAIR,
101 SIGNAL_NEW_CANDIDATE,
102 SIGNAL_NEW_REMOTE_CANDIDATE,
103 SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
104 N_SIGNALS,
108 static guint signals[N_SIGNALS];
110 static gboolean priv_attach_stream_component (NiceAgent *agent,
111 Stream *stream,
112 Component *component);
113 static void priv_detach_stream_component (Stream *stream, Component *component);
115 Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
117 GSList *i;
119 for (i = agent->streams; i; i = i->next)
121 Stream *s = i->data;
123 if (s->id == stream_id)
124 return s;
127 return NULL;
131 gboolean
132 agent_find_component (
133 NiceAgent *agent,
134 guint stream_id,
135 guint component_id,
136 Stream **stream,
137 Component **component)
139 Stream *s;
140 Component *c;
142 s = agent_find_stream (agent, stream_id);
144 if (s == NULL)
145 return FALSE;
147 c = stream_find_component_by_id (s, component_id);
149 if (c == NULL)
150 return FALSE;
152 if (stream)
153 *stream = s;
155 if (component)
156 *component = c;
158 return TRUE;
162 static void
163 nice_agent_dispose (GObject *object);
165 static void
166 nice_agent_get_property (
167 GObject *object,
168 guint property_id,
169 GValue *value,
170 GParamSpec *pspec);
172 static void
173 nice_agent_set_property (
174 GObject *object,
175 guint property_id,
176 const GValue *value,
177 GParamSpec *pspec);
180 static void
181 nice_agent_class_init (NiceAgentClass *klass)
183 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
185 gobject_class->get_property = nice_agent_get_property;
186 gobject_class->set_property = nice_agent_set_property;
187 gobject_class->dispose = nice_agent_dispose;
189 /* install properties */
191 g_object_class_install_property (gobject_class, PROP_SOCKET_FACTORY,
192 g_param_spec_pointer (
193 "socket-factory",
194 "UDP socket factory",
195 "The socket factory used to create new UDP sockets",
196 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
199 g_object_class_install_property (gobject_class, PROP_MAIN_CONTEXT,
200 g_param_spec_pointer (
201 "main-context",
202 "The GMainContext to use for timeouts",
203 "The GMainContext to use for timeouts",
204 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
206 g_object_class_install_property (gobject_class, PROP_COMPATIBILITY,
207 g_param_spec_uint (
208 "compatibility",
209 "ICE specification compatibility",
210 "The compatibility mode for the agent",
211 NICE_COMPATIBILITY_ID19, NICE_COMPATIBILITY_LAST,
212 NICE_COMPATIBILITY_ID19,
213 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
215 g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
216 g_param_spec_string (
217 "stun-server",
218 "STUN server",
219 "The STUN server used to obtain server-reflexive candidates",
220 NULL,
221 G_PARAM_READWRITE));
223 g_object_class_install_property (gobject_class, PROP_STUN_SERVER_PORT,
224 g_param_spec_uint (
225 "stun-server-port",
226 "STUN server port",
227 "The STUN server used to obtain server-reflexive candidates",
228 1, 65536,
229 1, /* not a construct property, ignored */
230 G_PARAM_READWRITE));
232 g_object_class_install_property (gobject_class, PROP_TURN_SERVER,
233 g_param_spec_string (
234 "turn-server",
235 "TURN server",
236 "The TURN server used to obtain relay candidates",
237 NULL,
238 G_PARAM_READWRITE));
240 g_object_class_install_property (gobject_class, PROP_TURN_SERVER_PORT,
241 g_param_spec_uint (
242 "turn-server-port",
243 "TURN server port",
244 "The TURN server used to obtain relay candidates",
245 1, 65536,
246 1, /* not a construct property, ignored */
247 G_PARAM_READWRITE));
249 g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
250 g_param_spec_boolean (
251 "controlling-mode",
252 "ICE controlling mode",
253 "Whether the agent is in controlling mode",
254 FALSE, /* not a construct property, ignored */
255 G_PARAM_READWRITE));
257 g_object_class_install_property (gobject_class, PROP_FULL_MODE,
258 g_param_spec_boolean (
259 "full-mode",
260 "ICE full mode",
261 "Whether agent runs in ICE full mode",
262 TRUE, /* use full mode by default */
263 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
265 g_object_class_install_property (gobject_class, PROP_STUN_PACING_TIMER,
266 g_param_spec_uint (
267 "stun-pacing-timer",
268 "STUN pacing timer",
269 "Timer 'Ta' (msecs) used in the IETF ICE specification for pacing candidate gathering and sending of connectivity checks",
270 1, 0xffffffff,
271 NICE_AGENT_TIMER_TA_DEFAULT,
272 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
274 /* note: according to spec recommendation in sect 5.7.3 (ID-19) */
275 g_object_class_install_property (gobject_class, PROP_MAX_CONNECTIVITY_CHECKS,
276 g_param_spec_uint (
277 "max-connectivity-checks",
278 "Maximum number of connectivity checks",
279 "Upper limit for the total number of connectivity checks performed",
280 0, 0xffffffff,
281 0, /* default set in init */
282 G_PARAM_READWRITE));
284 /* install signals */
286 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer self) */
287 signals[SIGNAL_COMPONENT_STATE_CHANGED] =
288 g_signal_new (
289 "component-state-changed",
290 G_OBJECT_CLASS_TYPE (klass),
291 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
293 NULL,
294 NULL,
295 agent_marshal_VOID__UINT_UINT_UINT,
296 G_TYPE_NONE,
298 G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
299 G_TYPE_INVALID);
301 /* signature: void cb(NiceAgent *agent, gpointer self) */
302 signals[SIGNAL_CANDIDATE_GATHERING_DONE] =
303 g_signal_new (
304 "candidate-gathering-done",
305 G_OBJECT_CLASS_TYPE (klass),
306 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
308 NULL,
309 NULL,
310 agent_marshal_VOID__VOID,
311 G_TYPE_NONE,
313 G_TYPE_INVALID);
315 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id,
316 gchar *lfoundation, gchar* rfoundation, gpointer self) */
317 signals[SIGNAL_NEW_SELECTED_PAIR] =
318 g_signal_new (
319 "new-selected-pair",
320 G_OBJECT_CLASS_TYPE (klass),
321 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
323 NULL,
324 NULL,
325 agent_marshal_VOID__UINT_UINT_STRING_STRING,
326 G_TYPE_NONE,
328 G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING,
329 G_TYPE_INVALID);
331 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
332 signals[SIGNAL_NEW_CANDIDATE] =
333 g_signal_new (
334 "new-candidate",
335 G_OBJECT_CLASS_TYPE (klass),
336 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
338 NULL,
339 NULL,
340 agent_marshal_VOID__UINT_UINT_STRING,
341 G_TYPE_NONE,
343 G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
344 G_TYPE_INVALID);
346 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
347 signals[SIGNAL_NEW_REMOTE_CANDIDATE] =
348 g_signal_new (
349 "new-remote-candidate",
350 G_OBJECT_CLASS_TYPE (klass),
351 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
353 NULL,
354 NULL,
355 agent_marshal_VOID__UINT_UINT_STRING,
356 G_TYPE_NONE,
358 G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
359 G_TYPE_INVALID);
361 /* signature: void cb(NiceAgent *agent, guint stream_id, gpointer self) */
362 signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED] =
363 g_signal_new (
364 "initial-binding-request-received",
365 G_OBJECT_CLASS_TYPE (klass),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
368 NULL,
369 NULL,
370 agent_marshal_VOID__UINT,
371 G_TYPE_NONE,
373 G_TYPE_UINT,
374 G_TYPE_INVALID);
378 static void priv_generate_tie_breaker (NiceAgent *agent)
380 nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker);
383 static void
384 nice_agent_init (NiceAgent *agent)
386 agent->next_candidate_id = 1;
387 agent->next_stream_id = 1;
389 /* set defaults; not construct params, so set here */
390 agent->stun_server_port = DEFAULT_STUN_PORT;
391 agent->turn_server_port = DEFAULT_STUN_PORT;
392 agent->controlling_mode = TRUE;
393 agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
395 agent->discovery_list = NULL;
396 agent->discovery_unsched_items = 0;
397 agent->discovery_timer_id = 0;
398 agent->conncheck_timer_id = 0;
399 agent->keepalive_timer_id = 0;
400 agent->compatibility = NICE_COMPATIBILITY_ID19;
402 stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
403 STUN_COMPATIBILITY_3489BIS,
404 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
405 STUN_AGENT_USAGE_USE_FINGERPRINT);
407 agent->rng = nice_rng_new ();
408 priv_generate_tie_breaker (agent);
410 g_static_rec_mutex_init (&agent->mutex);
415 * nice_agent_new:
416 * @factory: a NiceUDPSocketFactory used for allocating sockets
418 * Create a new NiceAgent.
420 * Returns: the new agent
422 NICEAPI_EXPORT NiceAgent *
423 nice_agent_new (NiceUDPSocketFactory *factory,
424 GMainContext *ctx, NiceCompatibility compat)
426 NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
427 "socket-factory", factory,
428 "compatibility", compat,
429 "main-context", ctx,
430 NULL);
432 return agent;
436 static void
437 nice_agent_get_property (
438 GObject *object,
439 guint property_id,
440 GValue *value,
441 GParamSpec *pspec)
443 NiceAgent *agent = NICE_AGENT (object);
445 g_static_rec_mutex_lock (&agent->mutex);
447 switch (property_id)
449 case PROP_SOCKET_FACTORY:
450 g_value_set_pointer (value, agent->socket_factory);
451 break;
453 case PROP_MAIN_CONTEXT:
454 g_value_set_pointer (value, agent->main_context);
455 break;
457 case PROP_COMPATIBILITY:
458 g_value_set_uint (value, agent->compatibility);
459 break;
461 case PROP_STUN_SERVER:
462 g_value_set_string (value, agent->stun_server_ip);
463 break;
465 case PROP_STUN_SERVER_PORT:
466 g_value_set_uint (value, agent->stun_server_port);
467 break;
469 case PROP_TURN_SERVER:
470 g_value_set_string (value, agent->turn_server_ip);
471 break;
473 case PROP_TURN_SERVER_PORT:
474 g_value_set_uint (value, agent->turn_server_port);
475 break;
477 case PROP_CONTROLLING_MODE:
478 g_value_set_boolean (value, agent->controlling_mode);
479 break;
481 case PROP_FULL_MODE:
482 g_value_set_boolean (value, agent->full_mode);
483 break;
485 case PROP_STUN_PACING_TIMER:
486 g_value_set_uint (value, agent->timer_ta);
487 break;
489 case PROP_MAX_CONNECTIVITY_CHECKS:
490 g_value_set_uint (value, agent->max_conn_checks);
491 /* XXX: should we prune the list of already existing checks? */
492 break;
494 default:
495 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
498 g_static_rec_mutex_unlock (&agent->mutex);
502 static void
503 nice_agent_set_property (
504 GObject *object,
505 guint property_id,
506 const GValue *value,
507 GParamSpec *pspec)
509 NiceAgent *agent = NICE_AGENT (object);
511 g_static_rec_mutex_lock (&agent->mutex);
513 switch (property_id)
515 case PROP_SOCKET_FACTORY:
516 agent->socket_factory = g_value_get_pointer (value);
517 break;
519 case PROP_MAIN_CONTEXT:
520 agent->main_context = g_value_get_pointer (value);
521 break;
523 case PROP_COMPATIBILITY:
524 agent->compatibility = g_value_get_uint (value);
525 if (agent->compatibility == NICE_COMPATIBILITY_ID19) {
526 stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
527 STUN_COMPATIBILITY_3489BIS,
528 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
529 STUN_AGENT_USAGE_USE_FINGERPRINT);
530 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
531 stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
532 STUN_COMPATIBILITY_RFC3489,
533 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
534 STUN_AGENT_USAGE_IGNORE_CREDENTIALS);
537 break;
539 case PROP_STUN_SERVER:
540 agent->stun_server_ip = g_value_dup_string (value);
541 break;
543 case PROP_STUN_SERVER_PORT:
544 agent->stun_server_port = g_value_get_uint (value);
545 break;
547 case PROP_TURN_SERVER:
548 agent->turn_server_ip = g_value_dup_string (value);
549 break;
551 case PROP_CONTROLLING_MODE:
552 agent->controlling_mode = g_value_get_boolean (value);
553 break;
555 case PROP_FULL_MODE:
556 agent->full_mode = g_value_get_boolean (value);
557 break;
559 case PROP_STUN_PACING_TIMER:
560 agent->timer_ta = g_value_get_uint (value);
561 break;
563 case PROP_MAX_CONNECTIVITY_CHECKS:
564 agent->max_conn_checks = g_value_get_uint (value);
565 break;
567 default:
568 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
571 g_static_rec_mutex_unlock (&agent->mutex);
575 void agent_gathering_done (NiceAgent *agent)
578 GSList *i, *j, *k, *l, *m;
580 for (i = agent->streams; i; i = i->next) {
581 Stream *stream = i->data;
582 for (j = stream->components; j; j = j->next) {
583 Component *component = j->data;
585 for (k = component->local_candidates; k; k = k->next) {
586 NiceCandidate *local_candidate = k->data;
588 for (l = component->remote_candidates; l; l = l->next) {
589 NiceCandidate *remote_candidate = l->data;
591 for (m = stream->conncheck_list; m; m = m->next) {
592 CandidateCheckPair *p = m->data;
594 if (p->local == local_candidate && p->remote == remote_candidate)
595 break;
597 if (m == NULL) {
598 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
605 agent_signal_gathering_done (agent);
608 void agent_signal_gathering_done (NiceAgent *agent)
610 g_signal_emit (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], 0);
613 void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream)
615 if (stream->initial_binding_request_received != TRUE) {
616 stream->initial_binding_request_received = TRUE;
617 g_signal_emit (agent, signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED], 0, stream->id);
621 void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id, guint component_id, const gchar *local_foundation, const gchar *remote_foundation)
623 Component *component;
624 gchar *lf_copy;
625 gchar *rf_copy;
627 if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
628 return;
630 lf_copy = g_strdup (local_foundation);
631 rf_copy = g_strdup (remote_foundation);
634 g_signal_emit (agent, signals[SIGNAL_NEW_SELECTED_PAIR], 0,
635 stream_id, component_id, lf_copy, rf_copy);
637 g_free (lf_copy);
638 g_free (rf_copy);
641 void agent_signal_new_candidate (NiceAgent *agent, NiceCandidate *candidate)
643 g_signal_emit (agent, signals[SIGNAL_NEW_CANDIDATE], 0,
644 candidate->stream_id,
645 candidate->component_id,
646 candidate->foundation);
649 void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate)
651 g_signal_emit (agent, signals[SIGNAL_NEW_REMOTE_CANDIDATE], 0,
652 candidate->stream_id,
653 candidate->component_id,
654 candidate->foundation);
657 void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state)
659 Component *component;
661 if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
662 return;
664 if (component->state != state && state < NICE_COMPONENT_STATE_LAST) {
665 g_debug ("Agent %p : stream %u component %u STATE-CHANGE %u -> %u.", agent,
666 stream_id, component_id, component->state, state);
668 component->state = state;
670 g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0,
671 stream_id, component_id, state);
675 guint64
676 agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote)
678 if (agent->controlling_mode == TRUE)
679 return nice_candidate_pair_priority (local->priority, remote->priority);
681 return nice_candidate_pair_priority (remote->priority, local->priority);
684 static gboolean
685 priv_add_srv_rfx_candidate_discovery (NiceAgent *agent, NiceCandidate *host_candidate, const gchar *stun_server_ip, const guint stun_server_port, Stream *stream, guint component_id, NiceAddress *addr)
687 CandidateDiscovery *cdisco;
688 GSList *modified_list;
690 /* note: no need to check for redundant candidates, as this is
691 * done later on in the process */
693 cdisco = g_slice_new0 (CandidateDiscovery);
694 if (cdisco) {
695 modified_list = g_slist_append (agent->discovery_list, cdisco);
697 if (modified_list) {
698 cdisco->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
699 cdisco->socket = host_candidate->sockptr->fileno;
700 cdisco->nicesock = host_candidate->sockptr;
701 cdisco->server_addr = stun_server_ip;
702 cdisco->server_port = stun_server_port;
703 cdisco->interface = addr;
704 cdisco->stream = stream;
705 cdisco->component = stream_find_component_by_id (stream, component_id);
706 cdisco->agent = agent;
707 g_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n", agent, cdisco);
708 agent->discovery_list = modified_list;
709 ++agent->discovery_unsched_items;
712 return TRUE;
715 return FALSE;
719 * nice_agent_add_stream:
720 * @agent: a NiceAgent
721 * @n_components: number of components
723 * Add a data stream to @agent.
725 * @pre local addresses must be set with nice_agent_add_local_address()
727 * Returns: the ID of the new stream, 0 on failure
729 NICEAPI_EXPORT guint
730 nice_agent_add_stream (
731 NiceAgent *agent,
732 guint n_components)
734 Stream *stream;
735 GSList *modified_list = NULL;
736 guint ret = 0;
738 g_static_rec_mutex_lock (&agent->mutex);
740 if (!agent->local_addresses) {
741 goto done;
744 stream = stream_new (n_components);
745 if (stream) {
746 modified_list = g_slist_append (agent->streams, stream);
747 if (modified_list) {
748 stream->id = agent->next_stream_id++;
749 g_debug ("Agent %p : allocating stream id %u (%p)", agent, stream->id, stream);
751 stream_initialize_credentials (stream, agent->rng);
753 agent->streams = modified_list;
755 else
756 stream_free (stream);
759 ret = stream->id;
761 done:
762 g_static_rec_mutex_unlock (&agent->mutex);
763 return ret;
768 * nice_agent_gather_candidates:
770 * start the candidate gathering process
773 NICEAPI_EXPORT void
774 nice_agent_gather_candidates (
775 NiceAgent *agent,
776 guint stream_id)
778 guint n;
779 GSList *i;
780 Stream *stream;
782 g_static_rec_mutex_lock (&agent->mutex);
784 stream = agent_find_stream (agent, stream_id);
785 if (stream == NULL) {
786 goto done;
789 g_debug ("Agent %p : In %s mode, starting candidate gathering.", agent, agent->full_mode ? "ICE-FULL" : "ICE-LITE");
791 /* generate a local host candidate for each local address */
793 for (i = agent->local_addresses; i; i = i->next)
795 NiceAddress *addr = i->data;
796 NiceCandidate *host_candidate;
798 for (n = 0; n < stream->n_components; n++) {
799 host_candidate = discovery_add_local_host_candidate (agent, stream->id,
800 n + 1, addr);
802 if (!host_candidate) {
803 g_error ("No host candidate??");
804 break;
807 if (agent->full_mode &&
808 agent->stun_server_ip) {
810 gboolean res =
811 priv_add_srv_rfx_candidate_discovery (agent,
812 host_candidate,
813 agent->stun_server_ip,
814 agent->stun_server_port,
815 stream,
816 n + 1 /* component-id */,
817 addr);
819 if (res != TRUE) {
820 /* note: memory allocation failure, return error */
821 g_error ("Memory allocation failure?");
828 /* note: no async discoveries pending, signal that we are ready */
829 if (agent->discovery_unsched_items == 0) {
830 agent_gathering_done (agent);
831 } else {
832 g_assert (agent->discovery_list);
833 discovery_schedule (agent);
836 done:
838 g_static_rec_mutex_unlock (&agent->mutex);
841 static void priv_remove_keepalive_timer (NiceAgent *agent)
843 if (agent->keepalive_timer_id) {
844 g_source_remove (agent->keepalive_timer_id),
845 agent->keepalive_timer_id = 0;
850 * nice_agent_remove_stream:
851 * @agent: a NiceAgent
852 * @stream_id: the ID of the stream to remove
854 NICEAPI_EXPORT void
855 nice_agent_remove_stream (
856 NiceAgent *agent,
857 guint stream_id)
859 /* note that streams/candidates can be in use by other threads */
861 Stream *stream;
862 GSList *i;
864 g_static_rec_mutex_lock (&agent->mutex);
865 stream = agent_find_stream (agent, stream_id);
867 if (!stream) {
868 goto done;
871 /* note: remove items with matching stream_ids from both lists */
872 conn_check_prune_stream (agent, stream);
873 discovery_prune_stream (agent, stream_id);
875 /* remove the stream itself */
876 for (i = stream->components; i; i = i->next) {
877 priv_detach_stream_component (stream, (Component *) i->data);
880 agent->streams = g_slist_remove (agent->streams, stream);
881 stream_free (stream);
883 if (!agent->streams)
884 priv_remove_keepalive_timer (agent);
886 done:
887 g_static_rec_mutex_unlock (&agent->mutex);
891 * nice_agent_add_local_address:
892 * @agent: A NiceAgent
893 * @addr: the address of a local IP interface
895 * Inform the agent of the presence of an address that a local
896 * network interface is bound to.
898 * @return FALSE on fatal (memory allocation) errors
900 NICEAPI_EXPORT gboolean
901 nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr)
903 NiceAddress *dup;
904 GSList *modified_list;
905 gboolean ret = FALSE;
907 g_static_rec_mutex_lock (&agent->mutex);
909 dup = nice_address_dup (addr);
910 nice_address_set_port (dup, 0);
911 modified_list = g_slist_append (agent->local_addresses, dup);
912 if (modified_list) {
913 agent->local_addresses = modified_list;
915 ret = TRUE;
916 goto done;
919 done:
920 g_static_rec_mutex_unlock (&agent->mutex);
921 return ret;
925 * Adds a new, or updates an existing, remote candidate.
927 * @return TRUE if candidate was succesfully added or
928 * update, otherwise FALSE
930 static gboolean priv_add_remote_candidate (
931 NiceAgent *agent,
932 guint stream_id,
933 guint component_id,
934 NiceCandidateType type,
935 const NiceAddress *addr,
936 const NiceAddress *base_addr,
937 NiceCandidateTransport transport,
938 guint32 priority,
939 const gchar *username,
940 const gchar *password,
941 const gchar *foundation)
943 Component *component;
944 NiceCandidate *candidate;
945 gchar *username_dup = NULL, *password_dup = NULL;
946 gboolean error_flag = FALSE;
948 if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
949 return FALSE;
951 /* step: check whether the candidate already exists */
952 candidate = component_find_remote_candidate(component, addr, transport);
953 if (candidate) {
954 g_debug ("Agent %p : Update existing remote candidate %p.", agent, candidate);
955 /* case 1: an existing candidate, update the attributes */
956 candidate->type = type;
957 if (base_addr)
958 candidate->base_addr = *base_addr;
959 candidate->priority = priority;
960 if (foundation)
961 strncpy(candidate->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION);
962 /* note: username and password must remain the same during
963 * a session; see sect 9.1.2 in ICE ID-19 */
964 if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0)
965 error_flag = TRUE;
967 else {
968 /* case 2: add a new candidate */
969 if (username)
970 username_dup = g_strdup (username);
971 if (password)
972 password_dup = g_strdup (password);
974 candidate = nice_candidate_new (type);
975 if (candidate) {
976 GSList *modified_list = g_slist_append (component->remote_candidates, candidate);
977 if (modified_list) {
978 component->remote_candidates = modified_list;
980 candidate->stream_id = stream_id;
981 candidate->component_id = component_id;
983 candidate->type = type;
984 if (addr)
985 candidate->addr = *addr;
986 #ifndef NDEBUG
988 gchar tmpbuf[INET6_ADDRSTRLEN];
989 nice_address_to_string (addr, tmpbuf);
990 g_debug ("Agent %p : Adding remote candidate with addr [%s]:%u.", agent, tmpbuf,
991 nice_address_get_port (addr));
993 #endif
995 if (base_addr)
996 candidate->base_addr = *base_addr;
998 candidate->transport = transport;
999 candidate->priority = priority;
1000 candidate->username = username_dup;
1001 candidate->password = password_dup;
1003 if (foundation)
1004 g_strlcpy (candidate->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION);
1006 if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0)
1007 error_flag = TRUE;
1009 else /* memory alloc error: list insert */
1010 error_flag = TRUE;
1012 else /* memory alloc error: candidate creation */
1013 error_flag = TRUE;
1016 if (error_flag) {
1017 if (candidate)
1018 nice_candidate_free (candidate);
1019 g_free (username_dup);
1020 g_free (password_dup);
1021 return FALSE;
1024 return TRUE;
1028 * Sets the remote credentials for stream 'stream_id'.
1030 * Note: stream credentials do not override per-candidate
1031 * credentials if set
1033 * @agent: a NiceAgent
1034 * @stream_id: identifier returnedby nice_agent_add_stream()
1035 * @ufrag: NULL-terminated string containing an ICE username fragment
1036 * @pwd: NULL-terminated string containing an ICE password
1038 * @return TRUE on success
1040 NICEAPI_EXPORT gboolean
1041 nice_agent_set_remote_credentials (
1042 NiceAgent *agent,
1043 guint stream_id,
1044 const gchar *ufrag, const gchar *pwd)
1046 Stream *stream;
1047 gboolean ret = FALSE;
1049 g_static_rec_mutex_lock (&agent->mutex);
1051 stream = agent_find_stream (agent, stream_id);
1052 /* note: oddly enough, ufrag and pwd can be empty strings */
1053 if (stream && ufrag && pwd) {
1055 g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG);
1056 g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD);
1058 ret = TRUE;
1059 goto done;
1062 done:
1063 g_static_rec_mutex_unlock (&agent->mutex);
1064 return ret;
1068 * Gets the local credentials for stream 'stream_id'.
1070 * @agent: a NiceAgent
1071 * @stream_id: identifier returnedby nice_agent_add_stream()
1072 * @ufrag: a pointer to a NULL-terminated string containing
1073 * an ICE username fragment [OUT]
1074 * @pwd: a pointer to a NULL-terminated string containing an ICE
1075 * password [OUT]
1077 * @return TRUE on success
1079 NICEAPI_EXPORT gboolean
1080 nice_agent_get_local_credentials (
1081 NiceAgent *agent,
1082 guint stream_id,
1083 const gchar **ufrag, const gchar **pwd)
1085 Stream *stream;
1086 gboolean ret = TRUE;
1088 g_static_rec_mutex_lock (&agent->mutex);
1090 stream = agent_find_stream (agent, stream_id);
1091 if (stream == NULL) {
1092 goto done;
1095 if (!ufrag || !pwd) {
1096 goto done;
1099 *ufrag = stream->local_ufrag;
1100 *pwd = stream->local_password;
1101 ret = TRUE;
1103 done:
1105 g_static_rec_mutex_unlock (&agent->mutex);
1106 return ret;
1110 * nice_agent_add_remote_candidate
1111 * @agent: a NiceAgent
1112 * @stream_id: the ID of the stream the candidate is for
1113 * @component_id: the ID of the component the candidate is for
1114 * @type: the type of the new candidate
1115 * @addr: the new candidate's IP address
1116 * @username: the new candidate's username (XXX: candidates don't have usernames)
1117 * @password: the new candidate's password (XXX: candidates don't have usernames)
1119 * Add a candidate our peer has informed us about to the agent's list.
1121 * Note: NICE_AGENT_MAX_REMOTE_CANDIDATES is the absolute
1122 * maximum limit for remote candidates
1123 * @return FALSE on fatal (memory alloc) errors
1125 NICEAPI_EXPORT gboolean
1126 nice_agent_add_remote_candidate (
1127 NiceAgent *agent,
1128 guint stream_id,
1129 guint component_id,
1130 NiceCandidateType type,
1131 NiceAddress *addr,
1132 const gchar *username,
1133 const gchar *password)
1136 /* XXX: to be deprecated */
1138 g_static_rec_mutex_lock (&agent->mutex);
1140 /* XXX: should we allow use of this method without an
1141 * initial call to nice_agent_set_remote_candidates()
1142 * with an empty set? */
1144 gboolean ret =
1145 priv_add_remote_candidate (agent,
1146 stream_id,
1147 component_id,
1148 type,
1149 addr,
1150 NULL,
1151 NICE_CANDIDATE_TRANSPORT_UDP,
1153 username,
1154 password,
1155 NULL);
1157 /* XXX/later: for each component, generate a new check with the new
1158 candidate, see below set_remote_candidates() */
1161 g_static_rec_mutex_unlock (&agent->mutex);
1162 return ret;
1166 * nice_agent_set_remote_candidates
1167 * @agent: a NiceAgent
1168 * @stream_id: the ID of the stream the candidate is for
1169 * @component_id: the ID of the component the candidate is for
1170 * @candidates: a list of NiceCandidate items describing the candidates
1172 * Sets the remote candidates for a component of a stream. Replaces
1173 * any existing remote candidates.
1175 * Note: NICE_AGENT_MAX_REMOTE_CANDIDATES is the absolute
1176 * maximum limit for remote candidates
1178 * @return number of candidates added, negative on fatal (memory
1179 * allocs) errors
1181 NICEAPI_EXPORT int
1182 nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint component_id, const GSList *candidates)
1184 const GSList *i;
1185 int added = 0;
1188 if (agent->discovery_unsched_items > 0)
1189 return -1;
1191 g_static_rec_mutex_lock (&agent->mutex);
1193 for (i = candidates; i && added >= 0; i = i->next) {
1194 NiceCandidate *d = (NiceCandidate*) i->data;
1195 gboolean res =
1196 priv_add_remote_candidate (agent,
1197 stream_id,
1198 component_id,
1199 d->type,
1200 &d->addr,
1201 &d->base_addr,
1202 d->transport,
1203 d->priority,
1204 d->username,
1205 d->password,
1206 d->foundation);
1207 if (res)
1208 ++added;
1209 else
1210 added = -1;
1213 conn_check_remote_candidates_set(agent);
1215 if (added > 0) {
1216 gboolean res = conn_check_schedule_next (agent);
1217 if (res != TRUE)
1218 g_debug ("Agent %p : Warning: unable to schedule any conn checks!", agent);
1221 g_static_rec_mutex_unlock (&agent->mutex);
1222 return added;
1227 * Reads data from a ready, nonblocking socket attached to an ICE
1228 * stream component.
1230 * @return number of octets received, or negative on error
1232 static gint
1233 _nice_agent_recv (
1234 NiceAgent *agent,
1235 Stream *stream,
1236 Component *component,
1237 NiceUDPSocket *udp_socket,
1238 guint buf_len,
1239 gchar *buf)
1241 NiceAddress from;
1242 gint len;
1244 len = nice_udp_socket_recv (udp_socket, &from,
1245 buf_len, buf);
1247 #ifndef NDEBUG
1248 if (len >= 0) {
1249 gchar tmpbuf[INET6_ADDRSTRLEN];
1250 nice_address_to_string (&from, tmpbuf);
1251 g_debug ("Agent %p : Packet received on local socket %u from [%s]:%u (%u octets).", agent,
1252 udp_socket->fileno, tmpbuf, nice_address_get_port (&from), len);
1254 #endif
1256 if (len == 0)
1257 return 0;
1259 if ((guint)len > buf_len)
1261 /* buffer is not big enough to accept this packet */
1262 /* XXX: test this case */
1263 return 0;
1266 if (stun_message_validate_buffer_length ((uint8_t *) buf, (size_t) len) == len) {
1267 /* If the retval is no 0, its not a valid stun packet, probably data */
1268 if (conn_check_handle_inbound_stun (agent, stream, component, udp_socket,
1269 &from, buf, len) == FALSE) {
1270 /* unhandled STUN, pass to client */
1271 return len;
1273 } else {
1274 /* not STUN, pass to client */
1275 return len;
1278 /* handled STUN message*/
1279 return 0;
1283 * nice_agent_recv:
1284 * @agent: a NiceAgent
1285 * @stream_id: the ID of the stream to recieve data from
1286 * @component_id: the ID of the component to receive data from
1287 * @buf_len: the size of @buf
1288 * @buf: the buffer to read data into
1290 * Receive data on a particular component.
1292 * Returns: the amount of data read into @buf
1294 NICEAPI_EXPORT guint
1295 nice_agent_recv (
1296 NiceAgent *agent,
1297 guint stream_id,
1298 guint component_id,
1299 guint buf_len,
1300 gchar *buf)
1302 gint len = 0;
1303 fd_set fds;
1304 guint max_fd = 0;
1305 gint num_readable;
1306 GSList *i;
1307 Stream *stream;
1308 Component *component;
1309 guint ret = 0;
1311 g_static_rec_mutex_lock (&agent->mutex);
1312 if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) {
1313 goto done;
1316 FD_ZERO (&fds);
1318 for (i = component->sockets; i; i = i->next)
1320 NiceUDPSocket *sockptr = i->data;
1322 FD_SET (sockptr->fileno, &fds);
1323 max_fd = MAX (sockptr->fileno, max_fd);
1326 /* Loop on candidate sockets until we find one that has non-STUN data
1327 * waiting on it.
1330 for (;;)
1332 num_readable = select (max_fd + 1, &fds, NULL, NULL, NULL);
1333 g_assert (num_readable >= 0);
1335 if (num_readable > 0)
1337 guint j;
1339 for (j = 0; j <= max_fd; j++)
1340 if (FD_ISSET (j, &fds))
1342 NiceUDPSocket *socket;
1344 socket = component_find_udp_socket_by_fd (component, j);
1345 g_assert (socket);
1347 len = _nice_agent_recv (agent, stream, component, socket,
1348 buf_len, buf);
1350 if (len >= 0) {
1351 ret = len;
1352 goto done;
1358 /* note: commented out to avoid compiler warnings
1360 * g_assert_not_reached (); */
1361 done:
1362 g_static_rec_mutex_unlock (&agent->mutex);
1363 return ret;
1366 NICEAPI_EXPORT guint
1367 nice_agent_recv_sock (
1368 NiceAgent *agent,
1369 guint stream_id,
1370 guint component_id,
1371 guint sock,
1372 guint buf_len,
1373 gchar *buf)
1375 NiceUDPSocket *socket;
1376 Stream *stream;
1377 Component *component;
1378 guint ret = 0;
1380 g_static_rec_mutex_lock (&agent->mutex);
1381 if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) {
1382 goto done;
1385 socket = component_find_udp_socket_by_fd (component, sock);
1386 g_assert (socket);
1388 ret = _nice_agent_recv (agent, stream, component,
1389 socket, buf_len, buf);
1391 done:
1392 g_static_rec_mutex_unlock (&agent->mutex);
1393 return ret;
1398 * nice_agent_poll_read:
1399 * @agent: A NiceAgent
1400 * @other_fds: A GSList of other file descriptors to poll
1402 * Polls the agent's sockets until at least one of them is readable, and
1403 * additionally if @other_fds is not NULL, polls those for readability too.
1404 * @other_fds should contain the file descriptors directly, i.e. using
1405 * GUINT_TO_POINTER.
1407 * Returns: A list of file descriptors from @other_fds that are readable
1409 NICEAPI_EXPORT GSList *
1410 nice_agent_poll_read (
1411 NiceAgent *agent,
1412 GSList *other_fds,
1413 NiceAgentRecvFunc func,
1414 gpointer data)
1416 fd_set fds;
1417 guint max_fd = 0;
1418 gint num_readable;
1419 GSList *ret = NULL;
1420 GSList *i;
1421 guint j;
1423 g_static_rec_mutex_lock (&agent->mutex);
1425 FD_ZERO (&fds);
1427 for (i = agent->streams; i; i = i->next)
1429 GSList *j, *k;
1430 Stream *stream = i->data;
1432 for (k = stream->components; k; k = k->next)
1434 Component *component = k->data;
1436 for (j = component->sockets; j; j = j->next)
1438 NiceUDPSocket *sockptr = j->data;
1440 FD_SET (sockptr->fileno, &fds);
1441 max_fd = MAX (sockptr->fileno, max_fd);
1446 for (i = other_fds; i; i = i->next)
1448 guint fileno;
1450 fileno = GPOINTER_TO_UINT (i->data);
1451 FD_SET (fileno, &fds);
1452 max_fd = MAX (fileno, max_fd);
1455 num_readable = select (max_fd + 1, &fds, NULL, NULL, NULL);
1457 if (num_readable < 1) {
1458 /* none readable, or error */
1459 goto done;
1462 for (j = 0; j <= max_fd; j++)
1463 if (FD_ISSET (j, &fds))
1465 if (g_slist_find (other_fds, GUINT_TO_POINTER (j))) {
1466 GSList *modified_list = g_slist_append (ret, GUINT_TO_POINTER (j));
1467 if (modified_list == NULL) {
1468 g_slist_free (ret);
1469 goto done;
1471 ret = modified_list;
1473 else
1475 NiceUDPSocket *socket = NULL;
1476 Stream *stream = NULL;
1477 Component *component = NULL;
1478 gchar buf[MAX_BUFFER_SIZE];
1479 guint len;
1481 for (i = agent->streams; i; i = i->next)
1483 Stream *s = i->data;
1484 Component *c = stream_find_component_by_fd (s, j);
1486 socket = component_find_udp_socket_by_fd (c, j);
1488 if (socket != NULL) {
1489 stream = s;
1490 component = c;
1491 break;
1495 if (socket == NULL || stream == NULL || component == NULL)
1496 break;
1498 len = _nice_agent_recv (agent, stream, component,
1499 socket, MAX_BUFFER_SIZE, buf);
1501 if (len && func != NULL)
1502 func (agent, stream->id, component->id, len, buf,
1503 data);
1507 done:
1508 g_static_rec_mutex_unlock (&agent->mutex);
1509 return ret;
1515 * Sends a data payload over a stream component.
1517 * @pre component state MUST be NICE_COMPONENT_STATE_READY,
1518 * or as a special case, in any state if component was
1519 * in READY state before and was then restarted
1521 * @return number of bytes sent, or negative error code
1523 NICEAPI_EXPORT gint
1524 nice_agent_send (
1525 NiceAgent *agent,
1526 guint stream_id,
1527 guint component_id,
1528 guint len,
1529 const gchar *buf)
1531 Stream *stream;
1532 Component *component;
1533 guint ret = -1;
1535 g_static_rec_mutex_lock (&agent->mutex);
1537 if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) {
1538 goto done;
1541 if (component->selected_pair.local != NULL)
1543 NiceUDPSocket *sock;
1544 NiceAddress *addr;
1546 #ifndef NDEBUG
1547 gchar tmpbuf[INET6_ADDRSTRLEN];
1548 nice_address_to_string (&component->selected_pair.remote->addr, tmpbuf);
1550 g_debug ("Agent %p : s%d:%d: sending %d bytes to [%s]:%d", agent, stream_id, component_id,
1551 len, tmpbuf,
1552 nice_address_get_port (&component->selected_pair.remote->addr));
1553 #endif
1555 sock = component->selected_pair.local->sockptr;
1556 addr = &component->selected_pair.remote->addr;
1557 nice_udp_socket_send (sock, addr, len, buf);
1558 component->media_after_tick = TRUE;
1560 ret = len;
1561 goto done;
1564 done:
1565 g_static_rec_mutex_unlock (&agent->mutex);
1566 return ret;
1571 * nice_agent_get_local_candidates:
1572 * @agent: A NiceAgent
1574 * The caller owns the returned GSList but not the candidates contained within
1575 * it. To get full results, the client should wait for the
1576 * 'candidates-gathering-done' signal.
1578 * Returns: a GSList of local candidates (NiceCandidate) belonging to @agent
1580 NICEAPI_EXPORT GSList *
1581 nice_agent_get_local_candidates (
1582 NiceAgent *agent,
1583 guint stream_id,
1584 guint component_id)
1586 Component *component;
1587 GSList * ret = NULL;
1588 GSList * item = NULL;
1590 g_static_rec_mutex_lock (&agent->mutex);
1591 if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
1593 goto done;
1596 for (item = component->local_candidates; item; item = item->next)
1597 ret = g_slist_append (ret, nice_candidate_copy (item->data));
1599 done:
1600 g_static_rec_mutex_unlock (&agent->mutex);
1601 return ret;
1606 * nice_agent_get_remote_candidates:
1607 * @agent: A NiceAgent
1609 * The caller owns the returned GSList but not the candidates contained within
1610 * it.
1612 * Note: the list of remote candidates can change during processing.
1613 * The client should register for the "new-remote-candidate" signal to
1614 * get notification of new remote candidates.
1616 * Returns: a GSList of remote candidates (NiceCandidate) belonging to @agent
1618 NICEAPI_EXPORT GSList *
1619 nice_agent_get_remote_candidates (
1620 NiceAgent *agent,
1621 guint stream_id,
1622 guint component_id)
1624 Component *component;
1625 GSList *ret = NULL, *item = NULL;
1627 g_static_rec_mutex_lock (&agent->mutex);
1628 if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
1630 goto done;
1633 for (item = component->remote_candidates; item; item = item->next)
1634 ret = g_slist_append (ret, nice_candidate_copy (item->data));
1636 done:
1637 g_static_rec_mutex_unlock (&agent->mutex);
1638 return ret;
1642 * nice_agent_restart
1643 * @agent: A NiceAgent
1645 * Restarts the session as defined in ICE spec (ID-19). This function
1646 * needs to be called both when initiating (ICE spec section 9.1.1.1.
1647 * "ICE Restarts"), as well as when reacting (spec section 9.2.1.1.
1648 * "Detecting ICE Restart") to a restart.
1650 * Returns: FALSE on error
1652 gboolean
1653 nice_agent_restart (
1654 NiceAgent *agent)
1656 GSList *i;
1657 gboolean res = TRUE;
1659 g_static_rec_mutex_lock (&agent->mutex);
1661 /* step: clean up all connectivity checks */
1662 conn_check_free (agent);
1664 /* step: regenerate tie-breaker value */
1665 priv_generate_tie_breaker (agent);
1667 for (i = agent->streams; i && res; i = i->next) {
1668 Stream *stream = i->data;
1670 /* step: reset local credentials for the stream and
1671 * clean up the list of remote candidates */
1672 res = stream_restart (stream, agent->rng);
1675 g_static_rec_mutex_unlock (&agent->mutex);
1676 return res;
1679 static void
1680 nice_agent_dispose (GObject *object)
1682 GSList *i;
1683 NiceAgent *agent = NICE_AGENT (object);
1685 /* step: free resources for the binding discovery timers */
1686 discovery_free (agent);
1687 g_assert (agent->discovery_list == NULL);
1689 /* step: free resources for the connectivity check timers */
1690 conn_check_free (agent);
1692 priv_remove_keepalive_timer (agent);
1694 for (i = agent->local_addresses; i; i = i->next)
1696 NiceAddress *a = i->data;
1698 nice_address_free (a);
1701 g_slist_free (agent->local_addresses);
1702 agent->local_addresses = NULL;
1704 for (i = agent->streams; i; i = i->next)
1706 Stream *s = i->data;
1708 stream_free (s);
1711 g_slist_free (agent->streams);
1712 agent->streams = NULL;
1714 g_free (agent->stun_server_ip);
1715 agent->stun_server_ip = NULL;
1716 g_free (agent->turn_server_ip);
1717 agent->turn_server_ip = NULL;
1719 nice_rng_free (agent->rng);
1720 agent->rng = NULL;
1722 if (G_OBJECT_CLASS (nice_agent_parent_class)->dispose)
1723 G_OBJECT_CLASS (nice_agent_parent_class)->dispose (object);
1725 g_static_rec_mutex_free (&agent->mutex);
1729 typedef struct _IOCtx IOCtx;
1731 struct _IOCtx
1733 NiceAgent *agent;
1734 Stream *stream;
1735 Component *component;
1736 NiceUDPSocket *socket;
1740 static IOCtx *
1741 io_ctx_new (
1742 NiceAgent *agent,
1743 Stream *stream,
1744 Component *component,
1745 NiceUDPSocket *socket)
1747 IOCtx *ctx;
1749 ctx = g_slice_new0 (IOCtx);
1750 if (ctx) {
1751 ctx->agent = agent;
1752 ctx->stream = stream;
1753 ctx->component = component;
1754 ctx->socket = socket;
1756 return ctx;
1760 static void
1761 io_ctx_free (IOCtx *ctx)
1763 g_slice_free (IOCtx, ctx);
1766 static gboolean
1767 nice_agent_g_source_cb (
1768 GIOChannel *source,
1769 G_GNUC_UNUSED
1770 GIOCondition condition,
1771 gpointer data)
1773 /* return value is whether to keep the source */
1775 IOCtx *ctx = data;
1776 NiceAgent *agent = ctx->agent;
1777 Stream *stream = ctx->stream;
1778 Component *component = ctx->component;
1779 gchar buf[MAX_BUFFER_SIZE];
1780 guint len;
1782 g_static_rec_mutex_lock (&agent->mutex);
1784 /* note: dear compiler, these are for you: */
1785 (void)source;
1787 len = _nice_agent_recv (agent, stream, component, ctx->socket,
1788 MAX_BUFFER_SIZE, buf);
1790 if (len > 0 && component->g_source_io_cb)
1791 component->g_source_io_cb (agent, stream->id, component->id,
1792 len, buf, component->data);
1794 g_static_rec_mutex_unlock (&agent->mutex);
1795 return TRUE;
1799 * Attaches one socket handle to the main loop event context
1802 void
1803 priv_attach_stream_component_socket (NiceAgent *agent,
1804 Stream *stream,
1805 Component *component,
1806 NiceUDPSocket *udp_socket)
1808 GIOChannel *io;
1809 GSource *source;
1810 IOCtx *ctx;
1812 if (!component->ctx)
1813 return;
1815 io = g_io_channel_unix_new (udp_socket->fileno);
1816 /* note: without G_IO_ERR the glib mainloop goes into
1817 * busyloop if errors are encountered */
1818 source = g_io_create_watch (io, G_IO_IN | G_IO_ERR);
1819 ctx = io_ctx_new (agent, stream, component, udp_socket);
1820 g_source_set_callback (source, (GSourceFunc) nice_agent_g_source_cb,
1821 ctx, (GDestroyNotify) io_ctx_free);
1822 g_debug ("Agent %p : Attach source %p (stream %u).", agent, source, stream->id);
1823 g_source_attach (source, component->ctx);
1824 component->gsources = g_slist_append (component->gsources, source);
1829 * Attaches socket handles of 'stream' to the main eventloop
1830 * context.
1833 static gboolean
1834 priv_attach_stream_component (NiceAgent *agent,
1835 Stream *stream,
1836 Component *component)
1838 GSList *i;
1840 for (i = component->sockets; i; i = i->next)
1841 priv_attach_stream_component_socket (agent, stream, component, i->data);
1843 return TRUE;
1847 * Detaches socket handles of 'stream' from the main eventloop
1848 * context.
1851 static void priv_detach_stream_component (Stream *stream, Component *component)
1853 GSList *i;
1855 for (i = component->gsources; i; i = i->next) {
1856 GSource *source = i->data;
1857 g_debug ("Detach source %p (stream %u).", source, stream->id);
1858 g_source_destroy (source);
1861 g_slist_free (component->gsources);
1862 component->gsources = NULL;
1865 NICEAPI_EXPORT gboolean
1866 nice_agent_attach_recv (
1867 NiceAgent *agent,
1868 guint stream_id,
1869 guint component_id,
1870 GMainContext *ctx,
1871 NiceAgentRecvFunc func,
1872 gpointer data)
1874 Component *component = NULL;
1875 Stream *stream = NULL;
1876 gboolean ret = FALSE;
1878 g_static_rec_mutex_lock (&agent->mutex);
1880 /* attach candidates */
1882 /* step: check that params specify an existing pair */
1883 if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) {
1884 g_warning ("Could not find component %u in stream %u", component_id,
1885 stream_id);
1886 goto done;
1889 if (component->g_source_io_cb && func == NULL)
1890 priv_detach_stream_component (stream, component);
1892 ret = TRUE;
1894 if (func && ctx) {
1895 component->g_source_io_cb = func;
1896 component->data = data;
1897 component->ctx = ctx;
1898 priv_attach_stream_component (agent, stream, component);
1899 } else {
1900 component->g_source_io_cb = NULL;
1901 component->data = NULL;
1902 component->ctx = NULL;
1906 done:
1907 g_static_rec_mutex_unlock (&agent->mutex);
1908 return ret;
1912 * Sets the selected candidate pair for media transmission
1913 * for given stream component. Calling this function will
1914 * disable all further ICE processing (connection check,
1915 * state machine updates, etc). Note that keepalives will
1916 * continue to be sent.
1918 NICEAPI_EXPORT gboolean
1919 nice_agent_set_selected_pair (
1920 NiceAgent *agent,
1921 guint stream_id,
1922 guint component_id,
1923 const gchar *lfoundation,
1924 const gchar *rfoundation)
1926 Component *component;
1927 Stream *stream;
1928 CandidatePair pair;
1929 gboolean ret = FALSE;
1931 g_static_rec_mutex_lock (&agent->mutex);
1933 /* step: check that params specify an existing pair */
1934 if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) {
1935 goto done;
1938 if (!component_find_pair (component, agent, lfoundation, rfoundation, &pair)){
1939 goto done;
1942 /* step: stop connectivity checks (note: for the whole stream) */
1943 conn_check_prune_stream (agent, stream);
1945 /* step: change component state */
1946 agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY);
1948 /* step: set the selected pair */
1949 component_update_selected_pair (component, &pair);
1950 agent_signal_new_selected_pair (agent, stream_id, component_id, lfoundation, rfoundation);
1952 ret = TRUE;
1954 done:
1955 g_static_rec_mutex_unlock (&agent->mutex);
1956 return ret;
1960 guint agent_timeout_add_with_context (NiceAgent *agent, guint interval,
1961 GSourceFunc function, gpointer data)
1963 GSource *source;
1964 guint id;
1966 g_return_val_if_fail (function != NULL, 0);
1968 source = g_timeout_source_new (interval);
1970 g_source_set_callback (source, function, data, NULL);
1971 id = g_source_attach (source, agent->main_context);
1972 g_source_unref (source);
1974 return id;
1979 * nice_agent_set_selected_remote_candidate:
1980 * @agent: a #NiceAgent
1981 * @stream_id: the stream id
1982 * @component_id: the component id
1983 * @candidate: the #NiceCandidate to force
1985 * Sets the selected remote candidate for media transmission
1986 * for given stream component. Calling this function will
1987 * disable all further ICE processing (connection check,
1988 * state machine updates, etc). Note that keepalives will
1989 * continue to be sent.
1991 * Returns: %TRUE on success, %FALSE on failure
1993 NICEAPI_EXPORT gboolean
1994 nice_agent_set_selected_remote_candidate (
1995 NiceAgent *agent,
1996 guint stream_id,
1997 guint component_id,
1998 NiceCandidate *candidate)
2000 Component *component;
2001 Stream *stream;
2002 NiceCandidate *lcandidate = NULL;
2003 gboolean ret = FALSE;
2005 g_static_rec_mutex_lock (&agent->mutex);
2007 /* step: check if the component exists*/
2008 if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) {
2009 goto done;
2012 /* step: stop connectivity checks (note: for the whole stream) */
2013 conn_check_prune_stream (agent, stream);
2016 /* step: set the selected pair */
2017 lcandidate = component_set_selected_remote_candidate (agent, component,
2018 candidate);
2019 if (!lcandidate)
2020 goto done;
2022 /* step: change component state */
2023 agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY);
2025 agent_signal_new_selected_pair (agent, stream_id, component_id,
2026 lcandidate->foundation,
2027 candidate->foundation);
2029 ret = TRUE;
2031 done:
2032 g_static_rec_mutex_unlock (&agent->mutex);
2033 return ret;