Fix bug where a discovered peer-reflexive pair does not get nominated
[sipe-libnice.git] / agent / discovery.c
blob9e7e0dd6980eb310be0c3c92b48fda1c900cda23
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2007 Nokia Corporation. All rights reserved.
5 * Contact: Kai Vehmanen
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is the Nice GLib ICE library.
19 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
20 * Corporation. All Rights Reserved.
22 * Contributors:
23 * Kai Vehmanen, Nokia
25 * Alternatively, the contents of this file may be used under the terms of the
26 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
27 * case the provisions of LGPL are applicable instead of those above. If you
28 * wish to allow use of your version of this file only under the terms of the
29 * LGPL and not to allow others to use your version of this file under the
30 * MPL, indicate your decision by deleting the provisions above and replace
31 * them with the notice and other provisions required by the LGPL. If you do
32 * not delete the provisions above, a recipient may use your version of this
33 * file under either the MPL or the LGPL.
36 /**
37 * @file discovery.c
38 * @brief ICE candidate discovery functions
41 #ifdef HAVE_CONFIG_H
42 # include <config.h>
43 #endif
45 #include <glib.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <errno.h>
51 #include "debug.h"
53 #include "agent.h"
54 #include "agent-priv.h"
55 #include "agent-signals-marshal.h"
56 #include "component.h"
57 #include "discovery.h"
58 #include "stun/usages/bind.h"
59 #include "stun/usages/turn.h"
60 #include "socket.h"
62 static inline int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
64 return (now->tv_sec == timer->tv_sec) ?
65 now->tv_usec >= timer->tv_usec :
66 now->tv_sec >= timer->tv_sec;
69 /**
70 * Frees the CandidateDiscovery structure pointed to
71 * by 'user data'. Compatible with g_slist_foreach().
73 void discovery_free_item (gpointer data, gpointer user_data)
75 CandidateDiscovery *cand = data;
76 g_assert (user_data == NULL);
77 g_free (cand->msn_turn_username);
78 g_free (cand->msn_turn_password);
79 g_slice_free (CandidateDiscovery, cand);
82 /**
83 * Frees all discovery related resources for the agent.
85 void discovery_free (NiceAgent *agent)
87 if (agent->discovery_list) {
88 GSList *tmp = agent->discovery_list;
89 agent->discovery_list = NULL;
91 g_slist_foreach (tmp, discovery_free_item, NULL);
92 g_slist_free (tmp);
94 agent->discovery_unsched_items = 0;
96 if (agent->discovery_timer_source != NULL) {
97 g_source_destroy (agent->discovery_timer_source);
98 g_source_unref (agent->discovery_timer_source);
99 agent->discovery_timer_source = NULL;
104 * Prunes the list of discovery processes for items related
105 * to stream 'stream_id'.
107 * @return TRUE on success, FALSE on a fatal error
109 void discovery_prune_stream (NiceAgent *agent, guint stream_id)
111 GSList *i;
113 for (i = agent->discovery_list; i ; ) {
114 CandidateDiscovery *cand = i->data;
115 GSList *next = i->next;
117 if (cand->stream->id == stream_id) {
118 agent->discovery_list = g_slist_remove (agent->discovery_list, cand);
119 discovery_free_item (cand, NULL);
121 i = next;
124 if (agent->discovery_list == NULL) {
125 /* noone using the timer anymore, clean it up */
126 discovery_free (agent);
132 * Frees the CandidateDiscovery structure pointed to
133 * by 'user data'. Compatible with g_slist_foreach().
135 void refresh_free_item (gpointer data, gpointer user_data)
137 CandidateRefresh *cand = data;
138 NiceAgent *agent = cand->agent;
139 uint8_t *username;
140 size_t username_len;
141 uint8_t *password;
142 size_t password_len;
143 size_t buffer_len = 0;
145 g_assert (user_data == NULL);
147 if (cand->timer_source != NULL) {
148 g_source_destroy (cand->timer_source);
149 g_source_unref (cand->timer_source);
150 cand->timer_source = NULL;
152 if (cand->tick_source != NULL) {
153 g_source_destroy (cand->tick_source);
154 g_source_unref (cand->tick_source);
155 cand->tick_source = NULL;
158 username = (uint8_t *)cand->turn->username;
159 username_len = (size_t) strlen (cand->turn->username);
160 password = (uint8_t *)cand->turn->password;
161 password_len = (size_t) strlen (cand->turn->password);
163 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
164 username = g_base64_decode ((gchar *)username, &username_len);
165 password = g_base64_decode ((gchar *)password, &password_len);
168 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
169 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
170 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, 0,
171 username, username_len,
172 password, password_len,
173 agent_to_turn_compatibility (agent));
175 if (buffer_len > 0) {
176 /* send the refresh twice since we won't do retransmissions */
177 nice_socket_send (cand->nicesock, &cand->server,
178 buffer_len, (gchar *)cand->stun_buffer);
179 nice_socket_send (cand->nicesock, &cand->server,
180 buffer_len, (gchar *)cand->stun_buffer);
184 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
185 g_free (username);
186 g_free (password);
189 g_slice_free (CandidateRefresh, cand);
193 * Frees all discovery related resources for the agent.
195 void refresh_free (NiceAgent *agent)
197 if (agent->refresh_list) {
198 GSList *tmp = agent->refresh_list;
199 agent->refresh_list = NULL;
201 g_slist_foreach (tmp, refresh_free_item, NULL);
202 g_slist_free (tmp);
208 * Prunes the list of discovery processes for items related
209 * to stream 'stream_id'.
211 * @return TRUE on success, FALSE on a fatal error
213 void refresh_prune_stream (NiceAgent *agent, guint stream_id)
215 GSList *i;
217 for (i = agent->refresh_list; i ;) {
218 CandidateRefresh *cand = i->data;
219 GSList *next = i->next;
221 if (cand->stream->id == stream_id) {
222 agent->refresh_list = g_slist_remove (agent->refresh_list, cand);
223 refresh_free_item (cand, NULL);
226 i = next;
231 void refresh_cancel (CandidateRefresh *refresh)
233 refresh->agent->refresh_list = g_slist_remove (refresh->agent->refresh_list,
234 refresh);
235 refresh_free_item (refresh, NULL);
239 * Adds a new local candidate. Implements the candidate pruning
240 * defined in ICE spec section 4.1.3 "Eliminating Redundant
241 * Candidates" (ID-19).
243 static gboolean priv_add_local_candidate_pruned (Component *component, NiceCandidate *candidate)
245 GSList *modified_list, *i;
247 for (i = component->local_candidates; i ; i = i->next) {
248 NiceCandidate *c = i->data;
250 if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
251 nice_address_equal (&c->addr, &candidate->addr)) {
252 nice_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
253 return FALSE;
257 modified_list = g_slist_append (component->local_candidates,
258 candidate);
259 if (modified_list) {
260 component->local_candidates = modified_list;
263 return TRUE;
267 * Assings a foundation to the candidate.
269 * Implements the mechanism described in ICE sect
270 * 4.1.1.3 "Computing Foundations" (ID-19).
272 static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
274 GSList *i, *j, *k;
276 for (i = agent->streams; i; i = i->next) {
277 Stream *stream = i->data;
278 for (j = stream->components; j; j = j->next) {
279 Component *component = j->data;
280 for (k = component->local_candidates; k; k = k->next) {
281 NiceCandidate *n = k->data;
282 NiceAddress temp = n->base_addr;
284 /* note: candidate must not on the local candidate list */
285 g_assert (candidate != n);
287 /* note: ports are not to be compared */
288 nice_address_set_port (&temp,
289 nice_address_get_port (&candidate->base_addr));
291 if (candidate->type == n->type &&
292 candidate->stream_id == n->stream_id &&
293 nice_address_equal (&candidate->base_addr, &temp)) {
294 /* note: currently only one STUN/TURN server per stream at a
295 * time is supported, so there is no need to check
296 * for candidates that would otherwise share the
297 * foundation, but have different STUN/TURN servers */
298 memcpy (candidate->foundation, n->foundation, NICE_CANDIDATE_MAX_FOUNDATION);
299 if (n->username) {
300 g_free (candidate->username);
301 candidate->username = g_strdup (n->username);
303 if (n->password) {
304 g_free (candidate->password);
305 candidate->password = g_strdup (n->password);
307 return;
313 g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", agent->next_candidate_id++);
316 static
317 void priv_generate_candidate_credentials (NiceAgent *agent,
318 NiceCandidate *candidate)
321 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
322 guchar username[32];
323 guchar password[16];
325 if (candidate->username)
326 g_free (candidate->username);
327 if (candidate->password)
328 g_free (candidate->password);
330 nice_rng_generate_bytes (agent->rng, 32, (gchar *)username);
331 nice_rng_generate_bytes (agent->rng, 16, (gchar *)password);
333 candidate->username = g_base64_encode (username, 32);
334 candidate->password = g_base64_encode (password, 16);
336 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
337 gchar username[16];
339 if (candidate->username)
340 g_free (candidate->username);
341 if (candidate->password)
342 g_free (candidate->password);
343 candidate->password = NULL;
345 nice_rng_generate_bytes_print (agent->rng, 16, (gchar *)username);
347 candidate->username = g_strndup (username, 16);
354 * Creates a local host candidate for 'component_id' of stream
355 * 'stream_id'.
357 * @return pointer to the created candidate, or NULL on error
359 NiceCandidate *discovery_add_local_host_candidate (
360 NiceAgent *agent,
361 guint stream_id,
362 guint component_id,
363 NiceAddress *address)
365 NiceCandidate *candidate;
366 Component *component;
367 Stream *stream;
368 NiceSocket *udp_socket = NULL;
369 gboolean errors = FALSE;
371 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
372 return NULL;
374 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
375 if (candidate) {
376 candidate->stream_id = stream_id;
377 candidate->component_id = component_id;
378 candidate->addr = *address;
379 candidate->base_addr = *address;
380 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
381 candidate->priority = nice_candidate_jingle_priority (candidate);
382 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
383 candidate->priority = nice_candidate_msn_priority (candidate);
384 } else {
385 candidate->priority = nice_candidate_ice_priority (candidate);
388 priv_generate_candidate_credentials (agent, candidate);
389 priv_assign_foundation (agent, candidate);
391 /* note: candidate username and password are left NULL as stream
392 level ufrag/password are used */
393 udp_socket = nice_udp_bsd_socket_new (address);
394 if (udp_socket) {
395 gboolean result;
397 agent_attach_stream_component_socket (agent, stream,
398 component, udp_socket);
400 candidate->sockptr = udp_socket;
401 candidate->addr = udp_socket->addr;
402 candidate->base_addr = udp_socket->addr;
404 result = priv_add_local_candidate_pruned (component, candidate);
406 if (result == TRUE) {
407 GSList *modified_list = g_slist_append (component->sockets, udp_socket);
408 if (modified_list) {
409 /* success: store a pointer to the sockaddr */
410 component->sockets = modified_list;
411 agent_signal_new_candidate (agent, candidate);
412 } else { /* error: list memory allocation */
413 candidate = NULL; /* note: candidate already owned by component */
415 } else {
416 /* error: memory allocation, or duplicate candidates */
417 errors = TRUE;
419 } else {
420 /* error: socket new */
421 errors = TRUE;
425 /* clean up after errors */
426 if (errors) {
427 if (candidate)
428 nice_candidate_free (candidate), candidate = NULL;
429 if (udp_socket)
430 nice_socket_free (udp_socket);
433 return candidate;
437 * Creates a server reflexive candidate for 'component_id' of stream
438 * 'stream_id'.
440 * @return pointer to the created candidate, or NULL on error
442 NiceCandidate*
443 discovery_add_server_reflexive_candidate (
444 NiceAgent *agent,
445 guint stream_id,
446 guint component_id,
447 NiceAddress *address,
448 NiceSocket *base_socket)
450 NiceCandidate *candidate;
451 Component *component;
452 Stream *stream;
453 gboolean result = FALSE;
455 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
456 return NULL;
458 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
459 if (candidate) {
460 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
461 candidate->priority = nice_candidate_jingle_priority (candidate);
462 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
463 candidate->priority = nice_candidate_msn_priority (candidate);
464 } else {
465 candidate->priority = nice_candidate_ice_priority_full
466 (NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE, 0, component_id);
468 candidate->stream_id = stream_id;
469 candidate->component_id = component_id;
470 candidate->addr = *address;
472 /* step: link to the base candidate+socket */
473 candidate->sockptr = base_socket;
474 candidate->base_addr = base_socket->addr;
476 priv_generate_candidate_credentials (agent, candidate);
477 priv_assign_foundation (agent, candidate);
479 result = priv_add_local_candidate_pruned (component, candidate);
480 if (result) {
481 agent_signal_new_candidate (agent, candidate);
483 else {
484 /* error: memory allocation, or duplicate candidatet */
485 nice_candidate_free (candidate), candidate = NULL;
489 return candidate;
494 * Creates a server reflexive candidate for 'component_id' of stream
495 * 'stream_id'.
497 * @return pointer to the created candidate, or NULL on error
499 NiceCandidate*
500 discovery_add_relay_candidate (
501 NiceAgent *agent,
502 guint stream_id,
503 guint component_id,
504 NiceAddress *address,
505 NiceSocket *base_socket,
506 TurnServer *turn)
508 NiceCandidate *candidate;
509 Component *component;
510 Stream *stream;
511 gboolean result = FALSE;
512 gboolean errors = FALSE;
513 NiceSocket *relay_socket = NULL;
515 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
516 return NULL;
518 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED);
519 if (candidate) {
520 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
521 candidate->priority = nice_candidate_jingle_priority (candidate);
522 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
523 candidate->priority = nice_candidate_msn_priority (candidate);
524 } else {
525 candidate->priority = nice_candidate_ice_priority_full
526 (NICE_CANDIDATE_TYPE_PREF_RELAYED, 0, component_id);
528 candidate->stream_id = stream_id;
529 candidate->component_id = component_id;
530 candidate->addr = *address;
531 candidate->turn = turn;
533 /* step: link to the base candidate+socket */
534 relay_socket = nice_turn_socket_new (agent, address,
535 base_socket, &turn->server,
536 turn->username, turn->password,
537 agent_to_turn_socket_compatibility (agent));
538 if (relay_socket) {
539 candidate->sockptr = relay_socket;
540 candidate->base_addr = base_socket->addr;
542 priv_generate_candidate_credentials (agent, candidate);
544 /* Google uses the turn username as the candidate username */
545 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
546 g_free (candidate->username);
547 candidate->username = g_strdup (turn->username);
550 priv_assign_foundation (agent, candidate);
552 result = priv_add_local_candidate_pruned (component, candidate);
553 if (result) {
554 GSList *modified_list = g_slist_append (component->sockets, relay_socket);
555 if (modified_list) {
556 /* success: store a pointer to the sockaddr */
557 component->sockets = modified_list;
559 agent_signal_new_candidate (agent, candidate);
560 } else {
561 /* error: memory allocation, or duplicate candidate */
562 errors = TRUE;
564 } else {
565 /* error: socket factory make */
566 errors = TRUE;
568 } else {
569 /* error: udp socket memory allocation */
570 errors = TRUE;
573 /* clean up after errors */
574 if (errors) {
575 if (candidate)
576 nice_candidate_free (candidate), candidate = NULL;
577 if (relay_socket)
578 nice_socket_free (relay_socket);
580 return candidate;
584 * Creates a peer reflexive candidate for 'component_id' of stream
585 * 'stream_id'.
587 * @return pointer to the created candidate, or NULL on error
589 NiceCandidate*
590 discovery_add_peer_reflexive_candidate (
591 NiceAgent *agent,
592 guint stream_id,
593 guint component_id,
594 NiceAddress *address,
595 NiceSocket *base_socket,
596 NiceCandidate *local,
597 NiceCandidate *remote)
599 NiceCandidate *candidate;
600 Component *component;
601 Stream *stream;
603 if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
604 return NULL;
606 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
607 if (candidate) {
608 gboolean result;
610 candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
611 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
612 candidate->priority = nice_candidate_jingle_priority (candidate);
613 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
614 candidate->priority = nice_candidate_msn_priority (candidate);
615 } else {
616 candidate->priority = nice_candidate_ice_priority_full
617 (NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component_id);
619 candidate->stream_id = stream_id;
620 candidate->component_id = component_id;
621 candidate->addr = *address;
622 candidate->base_addr = base_socket->addr;
625 priv_assign_foundation (agent, candidate);
627 if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
628 remote && local) {
629 guchar *new_username = NULL;
630 guchar *decoded_local = NULL;
631 guchar *decoded_remote = NULL;
632 gsize local_size;
633 gsize remote_size;
634 g_free(candidate->username);
635 g_free(candidate->password);
637 decoded_local = g_base64_decode (local->username, &local_size);
638 decoded_remote = g_base64_decode (remote->username, &remote_size);
640 new_username = g_new0(guchar, local_size + remote_size);
641 memcpy(new_username, decoded_local, local_size);
642 memcpy(new_username + local_size, decoded_remote, remote_size);
644 candidate->username = g_base64_encode (new_username, local_size + remote_size);
645 g_free(new_username);
646 g_free(decoded_local);
647 g_free(decoded_remote);
649 candidate->password = g_strdup(local->password);
652 /* step: link to the base candidate+socket */
653 candidate->sockptr = base_socket;
654 candidate->base_addr = base_socket->addr;
656 result = priv_add_local_candidate_pruned (component, candidate);
657 if (result != TRUE) {
658 /* error: memory allocation, or duplicate candidatet */
659 nice_candidate_free (candidate), candidate = NULL;
663 return candidate;
666 static guint priv_highest_remote_foundation (Component *component)
668 GSList *i;
669 guint highest = 0;
670 gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];
672 for (;;) {
673 g_snprintf (foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", highest);
674 for (i = component->remote_candidates; i; i = i->next) {
675 NiceCandidate *cand = i->data;
676 if (strncmp (foundation, cand->foundation,
677 NICE_CANDIDATE_MAX_FOUNDATION) != 0) {
678 return highest;
683 return highest;
687 * Adds a new peer reflexive candidate to the list of known
688 * remote candidates. The candidate is however not paired with
689 * existing local candidates.
691 * See ICE sect 7.2.1.3 "Learning Peer Reflexive Candidates" (ID-19).
693 * @return pointer to the created candidate, or NULL on error
695 NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
696 NiceAgent *agent,
697 Stream *stream,
698 Component *component,
699 guint32 priority,
700 const NiceAddress *remote_address,
701 NiceSocket *udp_socket,
702 NiceCandidate *local,
703 NiceCandidate *remote)
705 NiceCandidate *candidate;
707 /* XXX: for use compiler */
708 (void)udp_socket;
710 candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
711 if (candidate) {
712 GSList *modified_list;
714 guint next_remote_id = priv_highest_remote_foundation (component);
716 candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
717 candidate->addr = *remote_address;
718 candidate->base_addr = *remote_address;
719 candidate->priority = priority;
720 candidate->stream_id = stream->id;
721 candidate->component_id = component->id;
723 g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION,
724 "%u", next_remote_id);
726 if (agent->compatibility == NICE_COMPATIBILITY_MSN &&
727 remote && local) {
728 guchar *new_username = NULL;
729 guchar *decoded_local = NULL;
730 guchar *decoded_remote = NULL;
731 gsize local_size;
732 gsize remote_size;
733 g_free(candidate->username);
734 g_free (candidate->password);
736 decoded_local = g_base64_decode (local->username, &local_size);
737 decoded_remote = g_base64_decode (remote->username, &remote_size);
739 new_username = g_new0(guchar, local_size + remote_size);
740 memcpy(new_username, decoded_remote, remote_size);
741 memcpy(new_username + remote_size, decoded_local, local_size);
743 candidate->username = g_base64_encode (new_username, local_size + remote_size);
744 g_free(new_username);
745 g_free(decoded_local);
746 g_free(decoded_remote);
748 candidate->password = g_strdup(remote->password);
749 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
750 g_free (candidate->username);
751 g_free (candidate->password);
752 candidate->username = g_strdup(remote->username);
753 candidate->password = g_strdup(remote->password);
757 candidate->sockptr = NULL; /* not stored for remote candidates */
758 /* note: candidate username and password are left NULL as stream
759 level ufrag/password are used */
761 modified_list = g_slist_append (component->remote_candidates,
762 candidate);
763 if (modified_list) {
764 component->remote_candidates = modified_list;
765 agent_signal_new_remote_candidate (agent, candidate);
767 else { /* error: memory alloc / list */
768 nice_candidate_free (candidate), candidate = NULL;
772 return candidate;
775 /**
776 * Timer callback that handles scheduling new candidate discovery
777 * processes (paced by the Ta timer), and handles running of the
778 * existing discovery processes.
780 * This function is designed for the g_timeout_add() interface.
782 * @return will return FALSE when no more pending timers.
784 static gboolean priv_discovery_tick_unlocked (gpointer pointer)
786 CandidateDiscovery *cand;
787 NiceAgent *agent = pointer;
788 GSList *i;
789 int not_done = 0; /* note: track whether to continue timer */
790 size_t buffer_len = 0;
793 static int tick_counter = 0;
794 if (tick_counter++ % 50 == 0)
795 nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list);
798 for (i = agent->discovery_list; i ; i = i->next) {
799 cand = i->data;
801 if (cand->pending != TRUE) {
802 cand->pending = TRUE;
804 if (agent->discovery_unsched_items)
805 --agent->discovery_unsched_items;
808 gchar tmpbuf[INET6_ADDRSTRLEN];
809 nice_address_to_string (&cand->server, tmpbuf);
810 nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.\n",
811 agent, cand->type, tmpbuf);
813 if (nice_address_is_valid (&cand->server) &&
814 (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
815 cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
817 agent_signal_component_state_change (agent,
818 cand->stream->id,
819 cand->component->id,
820 NICE_COMPONENT_STATE_GATHERING);
822 if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
823 buffer_len = stun_usage_bind_create (&cand->stun_agent,
824 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer));
825 } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) {
826 uint8_t *username = (uint8_t *)cand->turn->username;
827 size_t username_len = (size_t) strlen (cand->turn->username);
828 uint8_t *password = (uint8_t *)cand->turn->password;
829 size_t password_len = (size_t) strlen (cand->turn->password);
831 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
832 username = g_base64_decode ((gchar *)username, &username_len);
833 password = g_base64_decode ((gchar *)password, &password_len);
836 buffer_len = stun_usage_turn_create (&cand->stun_agent,
837 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
838 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
839 STUN_USAGE_TURN_REQUEST_PORT_NORMAL,
840 -1, -1,
841 username, username_len,
842 password, password_len,
843 agent_to_turn_compatibility (agent));
845 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
846 g_free (cand->msn_turn_username);
847 g_free (cand->msn_turn_password);
848 cand->msn_turn_username = username;
849 cand->msn_turn_password = password;
853 if (buffer_len > 0) {
854 stun_timer_start (&cand->timer);
856 /* send the conncheck */
857 nice_socket_send (cand->nicesock, &cand->server,
858 buffer_len, (gchar *)cand->stun_buffer);
860 /* case: success, start waiting for the result */
861 g_get_current_time (&cand->next_tick);
863 } else {
864 /* case: error in starting discovery, start the next discovery */
865 cand->done = TRUE;
866 cand->stun_message.buffer = NULL;
867 cand->stun_message.buffer_len = 0;
868 continue;
871 else
872 /* allocate relayed candidates */
873 g_assert_not_reached ();
875 ++not_done; /* note: new discovery scheduled */
878 if (cand->done != TRUE) {
879 GTimeVal now;
881 g_get_current_time (&now);
883 if (cand->stun_message.buffer == NULL) {
884 nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent);
885 cand->done = TRUE;
887 else if (priv_timer_expired (&cand->next_tick, &now)) {
888 switch (stun_timer_refresh (&cand->timer)) {
889 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
890 /* case: error, abort processing */
891 cand->done = TRUE;
892 cand->stun_message.buffer = NULL;
893 cand->stun_message.buffer_len = 0;
894 nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent);
895 break;
896 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
898 /* case: not ready complete, so schedule next timeout */
899 unsigned int timeout = stun_timer_remainder (&cand->timer);
901 stun_debug ("STUN transaction retransmitted (timeout %dms).\n",
902 timeout);
904 /* retransmit */
905 nice_socket_send (cand->nicesock, &cand->server,
906 stun_message_length (&cand->stun_message),
907 (gchar *)cand->stun_buffer);
909 /* note: convert from milli to microseconds for g_time_val_add() */
910 cand->next_tick = now;
911 g_time_val_add (&cand->next_tick, timeout * 1000);
913 ++not_done; /* note: retry later */
914 break;
916 case STUN_USAGE_TIMER_RETURN_SUCCESS:
918 unsigned int timeout = stun_timer_remainder (&cand->timer);
920 cand->next_tick = now;
921 g_time_val_add (&cand->next_tick, timeout * 1000);
923 ++not_done; /* note: retry later */
924 break;
928 } else {
929 ++not_done; /* note: discovery not expired yet */
934 if (not_done == 0) {
935 nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent);
937 discovery_free (agent);
939 agent_gathering_done (agent);
941 /* note: no pending timers, return FALSE to stop timer */
942 return FALSE;
945 return TRUE;
948 static gboolean priv_discovery_tick (gpointer pointer)
950 NiceAgent *agent = pointer;
951 gboolean ret;
953 g_static_rec_mutex_lock (&agent->mutex);
954 ret = priv_discovery_tick_unlocked (pointer);
955 g_static_rec_mutex_unlock (&agent->mutex);
957 return ret;
961 * Initiates the candidate discovery process by starting
962 * the necessary timers.
964 * @pre agent->discovery_list != NULL // unsched discovery items available
966 void discovery_schedule (NiceAgent *agent)
968 g_assert (agent->discovery_list != NULL);
970 if (agent->discovery_unsched_items > 0) {
972 if (agent->discovery_timer_source == NULL) {
973 /* step: run first iteration immediately */
974 gboolean res = priv_discovery_tick_unlocked (agent);
975 if (res == TRUE) {
976 agent->discovery_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_discovery_tick, agent);