2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2007-2009 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
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.
25 * Youness Alaoui, Collabora Ltd.
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.
41 * @brief ICE candidate discovery functions
57 #include "agent-priv.h"
58 #include "component.h"
59 #include "discovery.h"
60 #include "stun/usages/bind.h"
61 #include "stun/usages/turn.h"
65 * Frees the CandidateDiscovery structure pointed to
66 * by 'user data'. Compatible with g_slist_free_full().
68 static void discovery_free_item (CandidateDiscovery *cand)
71 turn_server_unref (cand->turn);
73 g_slice_free (CandidateDiscovery, cand);
77 * Frees all discovery related resources for the agent.
79 void discovery_free (NiceAgent *agent)
81 g_slist_free_full (agent->discovery_list,
82 (GDestroyNotify) discovery_free_item);
83 agent->discovery_list = NULL;
84 agent->discovery_unsched_items = 0;
86 if (agent->discovery_timer_source != NULL) {
87 g_source_destroy (agent->discovery_timer_source);
88 g_source_unref (agent->discovery_timer_source);
89 agent->discovery_timer_source = NULL;
94 * Prunes the list of discovery processes for items related
95 * to stream 'stream_id'.
97 * @return TRUE on success, FALSE on a fatal error
99 void discovery_prune_stream (NiceAgent *agent, guint stream_id)
103 for (i = agent->discovery_list; i ; ) {
104 CandidateDiscovery *cand = i->data;
105 GSList *next = i->next;
107 if (cand->stream_id == stream_id) {
108 agent->discovery_list = g_slist_remove (agent->discovery_list, cand);
109 discovery_free_item (cand);
114 if (agent->discovery_list == NULL) {
115 /* noone using the timer anymore, clean it up */
116 discovery_free (agent);
121 * Prunes the list of discovery processes for items related
124 * @return TRUE on success, FALSE on a fatal error
126 void discovery_prune_socket (NiceAgent *agent, NiceSocket *sock)
130 for (i = agent->discovery_list; i ; ) {
131 CandidateDiscovery *discovery = i->data;
132 GSList *next = i->next;
134 if (discovery->nicesock == sock) {
135 agent->discovery_list = g_slist_remove (agent->discovery_list, discovery);
136 discovery_free_item (discovery);
141 if (agent->discovery_list == NULL) {
142 /* noone using the timer anymore, clean it up */
143 discovery_free (agent);
148 * Frees a CandidateRefresh and calls destroy callback if it has been set.
150 void refresh_free (NiceAgent *agent, CandidateRefresh *cand)
152 nice_debug ("Agent %p : Freeing candidate refresh %p", agent, cand);
154 agent->refresh_list = g_slist_remove (agent->refresh_list, cand);
156 if (cand->timer_source != NULL) {
157 g_source_destroy (cand->timer_source);
158 g_clear_pointer (&cand->timer_source, g_source_unref);
161 if (cand->tick_source) {
162 g_source_destroy (cand->tick_source);
163 g_clear_pointer (&cand->tick_source, g_source_unref);
166 if (cand->destroy_source) {
167 g_source_destroy (cand->destroy_source);
168 g_source_unref (cand->destroy_source);
171 if (cand->destroy_cb) {
172 cand->destroy_cb (cand->destroy_cb_data);
175 g_slice_free (CandidateRefresh, cand);
178 static gboolean on_refresh_remove_timeout (NiceAgent *agent,
179 CandidateRefresh *cand)
181 switch (stun_timer_refresh (&cand->timer)) {
182 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
184 StunTransactionId id;
186 nice_debug ("Agent %p : TURN deallocate for refresh %p timed out",
189 stun_message_id (&cand->stun_message, id);
190 stun_agent_forget_transaction (&cand->stun_agent, id);
192 refresh_free (agent, cand);
195 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
196 nice_debug ("Agent %p : Retransmitting TURN deallocate for refresh %p",
199 agent_socket_send (cand->nicesock, &cand->server,
200 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
203 case STUN_USAGE_TIMER_RETURN_SUCCESS:
204 agent_timeout_add_with_context (agent, &cand->tick_source,
205 "TURN deallocate retransmission", stun_timer_remainder (&cand->timer),
206 (NiceTimeoutLockedCallback) on_refresh_remove_timeout, cand);
212 return G_SOURCE_REMOVE;
216 * Closes the port associated with the candidate refresh on the TURN server by
217 * sending a refresh request that has zero lifetime. After a response is
218 * received or the request times out, 'cand' gets freed and 'cb' is called.
220 static gboolean refresh_remove_async (NiceAgent *agent, gpointer pointer)
226 size_t buffer_len = 0;
227 CandidateRefresh *cand = (CandidateRefresh *) pointer;
228 StunUsageTurnCompatibility turn_compat = agent_to_turn_compatibility (agent);
230 nice_debug ("Agent %p : Sending request to remove TURN allocation "
231 "for refresh %p", agent, cand);
233 if (cand->timer_source != NULL) {
234 g_source_destroy (cand->timer_source);
235 g_source_unref (cand->timer_source);
236 cand->timer_source = NULL;
239 g_source_destroy (cand->destroy_source);
240 g_source_unref (cand->destroy_source);
241 cand->destroy_source = NULL;
243 username = (uint8_t *)cand->candidate->turn->username;
244 username_len = (size_t) strlen (cand->candidate->turn->username);
245 password = (uint8_t *)cand->candidate->turn->password;
246 password_len = (size_t) strlen (cand->candidate->turn->password);
248 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
249 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
250 username = cand->candidate->turn->decoded_username;
251 password = cand->candidate->turn->decoded_password;
252 username_len = cand->candidate->turn->decoded_username_len;
253 password_len = cand->candidate->turn->decoded_password_len;
256 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
257 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
258 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, 0,
259 username, username_len,
260 password, password_len,
261 agent_to_turn_compatibility (agent));
263 if (buffer_len > 0) {
264 agent_socket_send (cand->nicesock, &cand->server, buffer_len,
265 (gchar *)cand->stun_buffer);
267 stun_timer_start (&cand->timer, agent->stun_initial_timeout,
268 agent->stun_max_retransmissions);
270 agent_timeout_add_with_context (agent, &cand->tick_source,
271 "TURN deallocate retransmission", stun_timer_remainder (&cand->timer),
272 (NiceTimeoutLockedCallback) on_refresh_remove_timeout, cand);
274 return G_SOURCE_REMOVE;
281 NiceTimeoutLockedCallback cb;
282 } RefreshPruneAsyncData;
284 static void on_refresh_removed (RefreshPruneAsyncData *data)
286 if (data->items_to_free == 0 || --(data->items_to_free) == 0) {
287 GSource *timeout_source = NULL;
288 agent_timeout_add_with_context (data->agent, &timeout_source,
289 "Async refresh prune", 0, data->cb, data->user_data);
291 g_source_unref (timeout_source);
296 static void refresh_prune_async (NiceAgent *agent, GSList *refreshes,
297 NiceTimeoutLockedCallback function, gpointer user_data)
299 RefreshPruneAsyncData *data = g_new0 (RefreshPruneAsyncData, 1);
304 data->user_data = user_data;
307 for (it = refreshes; it; it = it->next) {
308 CandidateRefresh *cand = it->data;
313 timeout += agent->timer_ta;
314 cand->disposing = TRUE;
315 cand->destroy_cb = (GDestroyNotify) on_refresh_removed;
316 cand->destroy_cb_data = data;
318 agent_timeout_add_with_context(agent, &cand->destroy_source,
319 "TURN refresh remove async", timeout, refresh_remove_async, cand);
321 ++data->items_to_free;
324 if (data->items_to_free == 0) {
325 /* Stream doesn't have any refreshes to remove. Invoke our callback once to
326 * schedule client's callback function. */
327 on_refresh_removed (data);
331 void refresh_prune_agent_async (NiceAgent *agent,
332 NiceTimeoutLockedCallback function, gpointer user_data)
334 refresh_prune_async (agent, agent->refresh_list, function, user_data);
338 * Removes the candidate refreshes related to 'stream' and asynchronously
339 * closes the associated port allocations on TURN server. Invokes 'function'
340 * when the process finishes.
342 void refresh_prune_stream_async (NiceAgent *agent, NiceStream *stream,
343 NiceTimeoutLockedCallback function)
345 GSList *refreshes = NULL;
348 for (i = agent->refresh_list; i ; i = i->next) {
349 CandidateRefresh *cand = i->data;
351 /* Don't free the candidate refresh to the currently selected local candidate
352 * unless the whole pair is being destroyed.
354 if (cand->stream_id == stream->id) {
355 refreshes = g_slist_append (refreshes, cand);
359 refresh_prune_async (agent, refreshes, function, stream);
360 g_slist_free (refreshes);
364 * Removes the candidate refreshes related to 'candidate'. The function does not
365 * close any associated port allocations on TURN server. Its purpose is in
366 * situations when an error is detected in socket communication that prevents
367 * sending more requests to the server.
369 void refresh_prune_candidate (NiceAgent *agent, NiceCandidate *candidate)
373 for (i = agent->refresh_list; i;) {
374 GSList *next = i->next;
375 CandidateRefresh *refresh = i->data;
377 if (refresh->candidate == candidate) {
378 refresh_free(agent, refresh);
386 * Removes the candidate refreshes related to 'candidate' and asynchronously
387 * closes the associated port allocations on TURN server. Invokes 'function'
388 * when the process finishes.
390 void refresh_prune_candidate_async (NiceAgent *agent, NiceCandidate *candidate,
391 NiceTimeoutLockedCallback function)
393 GSList *refreshes = NULL;
396 for (i = agent->refresh_list; i; i = i->next) {
397 CandidateRefresh *refresh = i->data;
399 if (refresh->candidate == candidate) {
400 refreshes = g_slist_append (refreshes, refresh);
404 refresh_prune_async (agent, refreshes, function, candidate);
405 g_slist_free (refreshes);
409 * Adds a new local candidate. Implements the candidate pruning
410 * defined in ICE spec section 4.1.3 "Eliminating Redundant
411 * Candidates" (ID-19).
413 static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *candidate)
417 g_assert (candidate != NULL);
419 for (i = component->local_candidates; i ; i = i->next) {
420 NiceCandidate *c = i->data;
422 if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
423 nice_address_equal (&c->addr, &candidate->addr) &&
424 c->transport == candidate->transport) {
425 nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
430 component->local_candidates = g_slist_append (component->local_candidates,
432 conn_check_add_for_local_candidate(agent, stream_id, component, candidate);
437 static guint priv_highest_remote_foundation (NiceComponent *component)
441 gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];
443 for (highest = 1;; highest++) {
444 gboolean taken = FALSE;
446 g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "remote%u",
448 for (i = component->remote_candidates; i; i = i->next) {
449 NiceCandidate *cand = i->data;
450 if (strncmp (foundation, cand->foundation,
451 NICE_CANDIDATE_MAX_FOUNDATION) == 0) {
460 g_return_val_if_reached (highest);
463 /* From RFC 5245 section 4.1.3:
465 * for reflexive and relayed candidates, the STUN or TURN servers
466 * used to obtain them have the same IP address.
469 priv_compare_turn_servers (TurnServer *turn1, TurnServer *turn2)
473 if (turn1 == NULL || turn2 == NULL)
476 return nice_address_equal_no_port (&turn1->server, &turn2->server);
480 * Assings a foundation to the candidate.
482 * Implements the mechanism described in ICE sect
483 * 4.1.1.3 "Computing Foundations" (ID-19).
485 static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
489 for (i = agent->streams; i; i = i->next) {
490 NiceStream *stream = i->data;
491 for (j = stream->components; j; j = j->next) {
492 NiceComponent *component = j->data;
493 for (k = component->local_candidates; k; k = k->next) {
494 NiceCandidate *n = k->data;
496 /* note: candidate must not on the local candidate list */
497 g_assert (candidate != n);
499 if (candidate->type == n->type &&
500 candidate->transport == n->transport &&
501 nice_address_equal_no_port (&candidate->base_addr, &n->base_addr) &&
502 (candidate->type != NICE_CANDIDATE_TYPE_RELAYED ||
503 priv_compare_turn_servers (candidate->turn, n->turn)) &&
504 !(agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
505 n->type == NICE_CANDIDATE_TYPE_RELAYED)) {
506 /* note: currently only one STUN server per stream at a
507 * time is supported, so there is no need to check
508 * for candidates that would otherwise share the
509 * foundation, but have different STUN servers */
510 g_strlcpy (candidate->foundation, n->foundation,
511 NICE_CANDIDATE_MAX_FOUNDATION);
513 g_free (candidate->username);
514 candidate->username = g_strdup (n->username);
517 g_free (candidate->password);
518 candidate->password = g_strdup (n->password);
526 g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
527 "%u", agent->next_candidate_id++);
530 static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *candidate)
533 guint next_remote_id;
534 NiceComponent *component = NULL;
536 for (i = agent->streams; i; i = i->next) {
537 NiceStream *stream = i->data;
538 for (j = stream->components; j; j = j->next) {
539 NiceComponent *c = j->data;
541 if (c->id == candidate->component_id)
544 for (k = c->remote_candidates; k; k = k->next) {
545 NiceCandidate *n = k->data;
547 /* note: candidate must not on the remote candidate list */
548 g_assert (candidate != n);
550 if (candidate->type == n->type &&
551 candidate->transport == n->transport &&
552 candidate->stream_id == n->stream_id &&
553 nice_address_equal_no_port (&candidate->addr, &n->addr)) {
554 /* note: No need to check for STUN/TURN servers, as these candidate
555 * will always be peer reflexive, never relayed or serve reflexive.
557 g_strlcpy (candidate->foundation, n->foundation,
558 NICE_CANDIDATE_MAX_FOUNDATION);
560 g_free (candidate->username);
561 candidate->username = g_strdup (n->username);
564 g_free (candidate->password);
565 candidate->password = g_strdup (n->password);
574 next_remote_id = priv_highest_remote_foundation (component);
575 g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
576 "remote%u", next_remote_id);
582 void priv_generate_candidate_credentials (NiceAgent *agent,
583 NiceCandidate *candidate)
586 if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
587 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
591 g_free (candidate->username);
592 g_free (candidate->password);
594 nice_rng_generate_bytes (agent->rng, 32, (gchar *)username);
595 nice_rng_generate_bytes (agent->rng, 16, (gchar *)password);
597 candidate->username = g_base64_encode (username, 32);
598 candidate->password = g_base64_encode (password, 16);
600 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
603 g_free (candidate->username);
604 g_free (candidate->password);
605 candidate->password = NULL;
607 nice_rng_generate_bytes_print (agent->rng, 16, (gchar *)username);
609 candidate->username = g_strndup (username, 16);
616 priv_local_host_candidate_duplicate_port (NiceAgent *agent,
617 NiceCandidate *candidate)
621 if (candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE)
624 for (i = agent->streams; i; i = i->next) {
625 NiceStream *stream = i->data;
627 for (j = stream->components; j; j = j->next) {
628 NiceComponent *component = j->data;
630 for (k = component->local_candidates; k; k = k->next) {
631 NiceCandidate *c = k->data;
633 if (candidate->transport == c->transport &&
634 nice_address_ip_version (&candidate->addr) ==
635 nice_address_ip_version (&c->addr) &&
636 nice_address_get_port (&candidate->addr) ==
637 nice_address_get_port (&c->addr))
646 * Creates a local host candidate for 'component_id' of stream
649 * @return pointer to the created candidate, or NULL on error
651 HostCandidateResult discovery_add_local_host_candidate (
655 NiceAddress *address,
656 NiceCandidateTransport transport,
657 NiceCandidate **outcandidate)
659 NiceCandidate *candidate;
660 NiceComponent *component;
662 NiceSocket *nicesock = NULL;
663 HostCandidateResult res = HOST_CANDIDATE_FAILED;
665 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
668 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
669 candidate->transport = transport;
670 candidate->stream_id = stream_id;
671 candidate->component_id = component_id;
672 candidate->addr = *address;
673 candidate->base_addr = *address;
674 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
675 candidate->priority = nice_candidate_jingle_priority (candidate);
676 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
677 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
678 candidate->priority = nice_candidate_msn_priority (candidate);
679 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
680 candidate->priority = nice_candidate_ms_ice_priority (candidate,
681 agent->reliable, FALSE);
683 candidate->priority = nice_candidate_ice_priority (candidate,
684 agent->reliable, FALSE);
687 priv_generate_candidate_credentials (agent, candidate);
688 priv_assign_foundation (agent, candidate);
690 /* note: candidate username and password are left NULL as stream
691 level ufrag/password are used */
692 if (transport == NICE_CANDIDATE_TRANSPORT_UDP) {
693 nicesock = nice_udp_bsd_socket_new (address);
694 } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
695 nicesock = nice_tcp_active_socket_new (agent->main_context, address);
696 } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
697 nicesock = nice_tcp_passive_socket_new (agent->main_context, address);
699 /* TODO: Add TCP-SO */
702 res = HOST_CANDIDATE_CANT_CREATE_SOCKET;
706 candidate->sockptr = nicesock;
707 candidate->addr = nicesock->addr;
708 candidate->base_addr = nicesock->addr;
710 if (priv_local_host_candidate_duplicate_port (agent, candidate)) {
711 res = HOST_CANDIDATE_DUPLICATE_PORT;
715 if (!priv_add_local_candidate_pruned (agent, stream_id, component,
717 res = HOST_CANDIDATE_REDUNDANT;
721 _priv_set_socket_tos (agent, nicesock, stream->tos);
722 nice_component_attach_socket (component, nicesock);
724 *outcandidate = candidate;
726 return HOST_CANDIDATE_SUCCESS;
729 nice_candidate_free (candidate);
731 nice_socket_free (nicesock);
736 * Creates a server reflexive candidate for 'component_id' of stream
739 * @return pointer to the created candidate, or NULL on error
742 discovery_add_server_reflexive_candidate (
746 NiceAddress *address,
747 NiceCandidateTransport transport,
748 NiceSocket *base_socket,
749 gboolean nat_assisted)
751 NiceCandidate *candidate;
752 NiceComponent *component;
754 gboolean result = FALSE;
756 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
759 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
760 candidate->transport = transport;
761 candidate->stream_id = stream_id;
762 candidate->component_id = component_id;
763 candidate->addr = *address;
765 /* step: link to the base candidate+socket */
766 candidate->sockptr = base_socket;
767 candidate->base_addr = base_socket->addr;
769 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
770 candidate->priority = nice_candidate_jingle_priority (candidate);
771 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
772 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
773 candidate->priority = nice_candidate_msn_priority (candidate);
774 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
775 candidate->priority = nice_candidate_ms_ice_priority (candidate,
776 agent->reliable, nat_assisted);
778 candidate->priority = nice_candidate_ice_priority (candidate,
779 agent->reliable, nat_assisted);
782 priv_generate_candidate_credentials (agent, candidate);
783 priv_assign_foundation (agent, candidate);
785 result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
787 agent_signal_new_candidate (agent, candidate);
790 /* error: duplicate candidate */
791 nice_candidate_free (candidate), candidate = NULL;
798 * Creates a server reflexive candidate for 'component_id' of stream
799 * 'stream_id' for each TCP_PASSIVE and TCP_ACTIVE candidates for each
802 * @return pointer to the created candidate, or NULL on error
805 discovery_discover_tcp_server_reflexive_candidates (
809 NiceAddress *address,
810 NiceSocket *base_socket)
812 NiceComponent *component;
814 NiceAddress base_addr = base_socket->addr;
817 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
820 nice_address_set_port (&base_addr, 0);
821 for (i = component->local_candidates; i; i = i ->next) {
822 NiceCandidate *c = i->data;
826 nice_address_set_port (&caddr, 0);
827 if (agent->force_relay == FALSE &&
828 c->transport != NICE_CANDIDATE_TRANSPORT_UDP &&
829 c->type == NICE_CANDIDATE_TYPE_HOST &&
830 nice_address_equal (&base_addr, &caddr)) {
831 nice_address_set_port (address, nice_address_get_port (&c->addr));
832 discovery_add_server_reflexive_candidate (
845 * Creates a server reflexive candidate for 'component_id' of stream
848 * @return pointer to the created candidate, or NULL on error
851 discovery_add_relay_candidate (
855 NiceAddress *address,
856 NiceCandidateTransport transport,
857 NiceSocket *base_socket,
860 NiceCandidate *candidate;
861 NiceComponent *component;
863 NiceSocket *relay_socket = NULL;
865 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
868 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
869 candidate->transport = transport;
870 candidate->stream_id = stream_id;
871 candidate->component_id = component_id;
872 candidate->addr = *address;
873 candidate->turn = turn_server_ref (turn);
875 /* step: link to the base candidate+socket */
876 relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
877 base_socket, &turn->server,
878 turn->username, turn->password,
879 agent_to_turn_socket_compatibility (agent));
883 candidate->sockptr = relay_socket;
884 candidate->base_addr = base_socket->addr;
886 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
887 candidate->priority = nice_candidate_jingle_priority (candidate);
888 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
889 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
890 candidate->priority = nice_candidate_msn_priority (candidate);
891 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
892 candidate->priority = nice_candidate_ms_ice_priority (candidate,
893 agent->reliable, FALSE);
895 candidate->priority = nice_candidate_ice_priority (candidate,
896 agent->reliable, FALSE);
899 priv_generate_candidate_credentials (agent, candidate);
901 /* Google uses the turn username as the candidate username */
902 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
903 g_free (candidate->username);
904 candidate->username = g_strdup (turn->username);
907 priv_assign_foundation (agent, candidate);
909 if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
912 nice_component_attach_socket (component, relay_socket);
913 agent_signal_new_candidate (agent, candidate);
918 nice_candidate_free (candidate);
920 nice_socket_free (relay_socket);
925 * Creates a peer reflexive candidate for 'component_id' of stream
928 * @return pointer to the created candidate, or NULL on error
931 discovery_add_peer_reflexive_candidate (
936 NiceAddress *address,
937 NiceSocket *base_socket,
938 NiceCandidate *local,
939 NiceCandidate *remote)
941 NiceCandidate *candidate;
942 NiceComponent *component;
946 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
949 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
951 candidate->transport = local->transport;
953 candidate->transport = conn_check_match_transport (remote->transport);
955 if (base_socket->type == NICE_SOCKET_TYPE_UDP_BSD ||
956 base_socket->type == NICE_SOCKET_TYPE_UDP_TURN)
957 candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
959 candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
961 candidate->stream_id = stream_id;
962 candidate->component_id = component_id;
963 candidate->addr = *address;
964 candidate->sockptr = base_socket;
965 candidate->base_addr = base_socket->addr;
966 /* We don't ensure priority uniqueness in this case, since the
967 * discovered candidate receives the same priority than its
968 * parent pair, by design, RFC 5245, sect 7.1.3.2.1.
969 * Discovering Peer Reflexive Candidates (the priority from the
972 candidate->priority = priority;
973 priv_assign_foundation (agent, candidate);
975 if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
976 agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
978 guchar *new_username = NULL;
979 guchar *decoded_local = NULL;
980 guchar *decoded_remote = NULL;
983 g_free(candidate->username);
984 g_free(candidate->password);
986 decoded_local = g_base64_decode (local->username, &local_size);
987 decoded_remote = g_base64_decode (remote->username, &remote_size);
989 new_username = g_new0(guchar, local_size + remote_size);
990 memcpy(new_username, decoded_local, local_size);
991 memcpy(new_username + local_size, decoded_remote, remote_size);
993 candidate->username = g_base64_encode (new_username, local_size + remote_size);
994 g_free(new_username);
995 g_free(decoded_local);
996 g_free(decoded_remote);
998 candidate->password = g_strdup(local->password);
1000 g_free(candidate->username);
1001 g_free(candidate->password);
1003 candidate->username = g_strdup(local->username);
1004 candidate->password = g_strdup(local->password);
1007 result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
1008 if (result != TRUE) {
1009 /* error: memory allocation, or duplicate candidate */
1010 nice_candidate_free (candidate), candidate = NULL;
1018 * Adds a new peer reflexive candidate to the list of known
1019 * remote candidates. The candidate is however not paired with
1020 * existing local candidates.
1022 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
1024 * @return pointer to the created candidate, or NULL on error
1026 NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
1029 NiceComponent *component,
1031 const NiceAddress *remote_address,
1032 NiceSocket *nicesock,
1033 NiceCandidate *local,
1034 NiceCandidate *remote)
1036 NiceCandidate *candidate;
1038 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
1040 candidate->addr = *remote_address;
1041 candidate->base_addr = *remote_address;
1043 candidate->transport = remote->transport;
1045 candidate->transport = conn_check_match_transport (local->transport);
1047 if (nicesock->type == NICE_SOCKET_TYPE_UDP_BSD ||
1048 nicesock->type == NICE_SOCKET_TYPE_UDP_TURN)
1049 candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
1051 candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1053 candidate->sockptr = nicesock;
1054 candidate->stream_id = stream->id;
1055 candidate->component_id = component->id;
1057 /* if the check didn't contain the PRIORITY attribute, then the priority will
1058 * be 0, which is invalid... */
1059 if (priority != 0) {
1060 candidate->priority = priority;
1061 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1062 candidate->priority = nice_candidate_jingle_priority (candidate);
1063 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
1064 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
1065 candidate->priority = nice_candidate_msn_priority (candidate);
1066 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1067 candidate->priority = nice_candidate_ms_ice_priority (candidate,
1068 agent->reliable, FALSE);
1070 candidate->priority = nice_candidate_ice_priority (candidate,
1071 agent->reliable, FALSE);
1074 priv_assign_remote_foundation (agent, candidate);
1076 if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
1077 agent->compatibility == NICE_COMPATIBILITY_OC2007) &&
1079 guchar *new_username = NULL;
1080 guchar *decoded_local = NULL;
1081 guchar *decoded_remote = NULL;
1084 g_free(candidate->username);
1085 g_free (candidate->password);
1087 decoded_local = g_base64_decode (local->username, &local_size);
1088 decoded_remote = g_base64_decode (remote->username, &remote_size);
1090 new_username = g_new0(guchar, local_size + remote_size);
1091 memcpy(new_username, decoded_remote, remote_size);
1092 memcpy(new_username + remote_size, decoded_local, local_size);
1094 candidate->username = g_base64_encode (new_username, local_size + remote_size);
1095 g_free(new_username);
1096 g_free(decoded_local);
1097 g_free(decoded_remote);
1099 candidate->password = g_strdup(remote->password);
1100 } else if (remote) {
1101 g_free (candidate->username);
1102 g_free (candidate->password);
1103 candidate->username = g_strdup(remote->username);
1104 candidate->password = g_strdup(remote->password);
1107 /* note: candidate username and password are left NULL as stream
1108 level ufrag/password are used */
1110 component->remote_candidates = g_slist_append (component->remote_candidates,
1113 agent_signal_new_remote_candidate (agent, candidate);
1119 * Timer callback that handles scheduling new candidate discovery
1120 * processes (paced by the Ta timer), and handles running of the
1121 * existing discovery processes.
1123 * This function is designed for the g_timeout_add() interface.
1125 * @return will return FALSE when no more pending timers.
1127 static gboolean priv_discovery_tick_unlocked (NiceAgent *agent)
1129 CandidateDiscovery *cand;
1131 int not_done = 0; /* note: track whether to continue timer */
1132 int need_pacing = 0;
1133 size_t buffer_len = 0;
1136 static int tick_counter = 0;
1137 if (tick_counter++ % 50 == 0)
1138 nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
1141 for (i = agent->discovery_list; i ; i = i->next) {
1144 if (cand->pending != TRUE) {
1145 cand->pending = TRUE;
1147 if (agent->discovery_unsched_items)
1148 --agent->discovery_unsched_items;
1150 if (nice_debug_is_enabled ()) {
1151 gchar tmpbuf[INET6_ADDRSTRLEN];
1152 nice_address_to_string (&cand->server, tmpbuf);
1153 nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.",
1154 agent, cand->type, tmpbuf);
1156 if (nice_address_is_valid (&cand->server) &&
1157 (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
1158 cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
1159 NiceComponent *component;
1161 if (agent_find_component (agent, cand->stream_id,
1162 cand->component_id, NULL, &component) &&
1163 (component->state == NICE_COMPONENT_STATE_DISCONNECTED ||
1164 component->state == NICE_COMPONENT_STATE_FAILED))
1165 agent_signal_component_state_change (agent,
1168 NICE_COMPONENT_STATE_GATHERING);
1170 if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
1171 buffer_len = stun_usage_bind_create (&cand->stun_agent,
1172 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
1173 } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
1174 uint8_t *username = (uint8_t *)cand->turn->username;
1175 gsize username_len = strlen (cand->turn->username);
1176 uint8_t *password = (uint8_t *)cand->turn->password;
1177 gsize password_len = strlen (cand->turn->password);
1178 StunUsageTurnCompatibility turn_compat =
1179 agent_to_turn_compatibility (agent);
1181 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
1182 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
1183 username = cand->turn->decoded_username;
1184 password = cand->turn->decoded_password;
1185 username_len = cand->turn->decoded_username_len;
1186 password_len = cand->turn->decoded_password_len;
1189 buffer_len = stun_usage_turn_create (&cand->stun_agent,
1190 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
1191 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
1192 STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
1194 username, username_len,
1195 password, password_len,
1199 if (buffer_len > 0 &&
1200 agent_socket_send (cand->nicesock, &cand->server, buffer_len,
1201 (gchar *)cand->stun_buffer) >= 0) {
1202 /* case: success, start waiting for the result */
1203 if (nice_socket_is_reliable (cand->nicesock)) {
1204 stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout);
1206 stun_timer_start (&cand->timer,
1207 agent->stun_initial_timeout,
1208 agent->stun_max_retransmissions);
1211 cand->next_tick = g_get_monotonic_time ();
1214 /* case: error in starting discovery, start the next discovery */
1215 nice_debug ("Agent %p : Error starting discovery, skipping the item.",
1218 cand->stun_message.buffer = NULL;
1219 cand->stun_message.buffer_len = 0;
1224 /* allocate relayed candidates */
1225 g_assert_not_reached ();
1227 ++not_done; /* note: new discovery scheduled */
1233 if (cand->done != TRUE) {
1234 gint64 now = g_get_monotonic_time ();
1236 if (cand->stun_message.buffer == NULL) {
1237 nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
1240 else if (now >= cand->next_tick) {
1241 switch (stun_timer_refresh (&cand->timer)) {
1242 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1245 /* case: error, abort processing */
1246 StunTransactionId id;
1248 stun_message_id (&cand->stun_message, id);
1249 stun_agent_forget_transaction (&cand->stun_agent, id);
1252 cand->stun_message.buffer = NULL;
1253 cand->stun_message.buffer_len = 0;
1254 nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
1257 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1259 /* case: not ready complete, so schedule next timeout */
1260 unsigned int timeout = stun_timer_remainder (&cand->timer);
1262 stun_debug ("STUN transaction retransmitted (timeout %dms).",
1266 agent_socket_send (cand->nicesock, &cand->server,
1267 stun_message_length (&cand->stun_message),
1268 (gchar *)cand->stun_buffer);
1270 /* note: convert from milli to microseconds for g_time_val_add() */
1271 cand->next_tick = now + (timeout * 1000);
1273 ++not_done; /* note: retry later */
1277 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1279 unsigned int timeout = stun_timer_remainder (&cand->timer);
1281 cand->next_tick = now + (timeout * 1000);
1283 ++not_done; /* note: retry later */
1287 /* Nothing to do. */
1292 ++not_done; /* note: discovery not expired yet */
1300 if (not_done == 0) {
1301 nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
1303 discovery_free (agent);
1305 agent_gathering_done (agent);
1307 /* note: no pending timers, return FALSE to stop timer */
1314 static gboolean priv_discovery_tick_agent_locked (NiceAgent *agent,
1319 ret = priv_discovery_tick_unlocked (agent);
1321 if (agent->discovery_timer_source != NULL) {
1322 g_source_destroy (agent->discovery_timer_source);
1323 g_source_unref (agent->discovery_timer_source);
1324 agent->discovery_timer_source = NULL;
1332 * Initiates the candidate discovery process by starting
1333 * the necessary timers.
1335 * @pre agent->discovery_list != NULL // unsched discovery items available
1337 void discovery_schedule (NiceAgent *agent)
1339 g_assert (agent->discovery_list != NULL);
1341 if (agent->discovery_unsched_items > 0) {
1343 if (agent->discovery_timer_source == NULL) {
1344 /* step: run first iteration immediately */
1345 gboolean res = priv_discovery_tick_unlocked (agent);
1347 agent_timeout_add_with_context (agent, &agent->discovery_timer_source,
1348 "Candidate discovery tick", agent->timer_ta,
1349 priv_discovery_tick_agent_locked, NULL);