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
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.
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.
38 * @brief ICE candidate discovery functions
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"
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
;
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
);
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
);
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
)
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
);
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
;
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
) {
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
);
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
)
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
);
231 void refresh_cancel (CandidateRefresh
*refresh
)
233 refresh
->agent
->refresh_list
= g_slist_remove (refresh
->agent
->refresh_list
,
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
);
257 modified_list
= g_slist_append (component
->local_candidates
,
260 component
->local_candidates
= modified_list
;
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
)
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
);
300 g_free (candidate
->username
);
301 candidate
->username
= g_strdup (n
->username
);
304 g_free (candidate
->password
);
305 candidate
->password
= g_strdup (n
->password
);
313 g_snprintf (candidate
->foundation
, NICE_CANDIDATE_MAX_FOUNDATION
, "%u", agent
->next_candidate_id
++);
317 void priv_generate_candidate_credentials (NiceAgent
*agent
,
318 NiceCandidate
*candidate
)
321 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
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
) {
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
357 * @return pointer to the created candidate, or NULL on error
359 NiceCandidate
*discovery_add_local_host_candidate (
363 NiceAddress
*address
)
365 NiceCandidate
*candidate
;
366 Component
*component
;
368 NiceSocket
*udp_socket
= NULL
;
369 gboolean errors
= FALSE
;
371 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
))
374 candidate
= nice_candidate_new (NICE_CANDIDATE_TYPE_HOST
);
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
);
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
);
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
);
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 */
416 /* error: memory allocation, or duplicate candidates */
420 /* error: socket new */
425 /* clean up after errors */
428 nice_candidate_free (candidate
), candidate
= NULL
;
430 nice_socket_free (udp_socket
);
437 * Creates a server reflexive candidate for 'component_id' of stream
440 * @return pointer to the created candidate, or NULL on error
443 discovery_add_server_reflexive_candidate (
447 NiceAddress
*address
,
448 NiceSocket
*base_socket
)
450 NiceCandidate
*candidate
;
451 Component
*component
;
453 gboolean result
= FALSE
;
455 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
))
458 candidate
= nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
);
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
);
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
);
481 agent_signal_new_candidate (agent
, candidate
);
484 /* error: memory allocation, or duplicate candidatet */
485 nice_candidate_free (candidate
), candidate
= NULL
;
494 * Creates a server reflexive candidate for 'component_id' of stream
497 * @return pointer to the created candidate, or NULL on error
500 discovery_add_relay_candidate (
504 NiceAddress
*address
,
505 NiceSocket
*base_socket
,
508 NiceCandidate
*candidate
;
509 Component
*component
;
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
))
518 candidate
= nice_candidate_new (NICE_CANDIDATE_TYPE_RELAYED
);
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
);
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
));
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
);
554 GSList
*modified_list
= g_slist_append (component
->sockets
, relay_socket
);
556 /* success: store a pointer to the sockaddr */
557 component
->sockets
= modified_list
;
559 agent_signal_new_candidate (agent
, candidate
);
561 /* error: memory allocation, or duplicate candidate */
565 /* error: socket factory make */
569 /* error: udp socket memory allocation */
573 /* clean up after errors */
576 nice_candidate_free (candidate
), candidate
= NULL
;
578 nice_socket_free (relay_socket
);
584 * Creates a peer reflexive candidate for 'component_id' of stream
587 * @return pointer to the created candidate, or NULL on error
590 discovery_add_peer_reflexive_candidate (
594 NiceAddress
*address
,
595 NiceSocket
*base_socket
,
596 NiceCandidate
*local
,
597 NiceCandidate
*remote
)
599 NiceCandidate
*candidate
;
600 Component
*component
;
603 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
))
606 candidate
= nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE
);
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
);
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
&&
629 guchar
*new_username
= NULL
;
630 guchar
*decoded_local
= NULL
;
631 guchar
*decoded_remote
= NULL
;
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
;
666 static guint
priv_highest_remote_foundation (Component
*component
)
670 gchar foundation
[NICE_CANDIDATE_MAX_FOUNDATION
];
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) {
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 (
698 Component
*component
,
700 const NiceAddress
*remote_address
,
701 NiceSocket
*udp_socket
,
702 NiceCandidate
*local
,
703 NiceCandidate
*remote
)
705 NiceCandidate
*candidate
;
707 /* XXX: for use compiler */
710 candidate
= nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE
);
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
&&
728 guchar
*new_username
= NULL
;
729 guchar
*decoded_local
= NULL
;
730 guchar
*decoded_remote
= NULL
;
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
,
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
;
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
;
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
) {
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
,
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
,
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
);
864 /* case: error in starting discovery, start the next discovery */
866 cand
->stun_message
.buffer
= NULL
;
867 cand
->stun_message
.buffer_len
= 0;
872 /* allocate relayed candidates */
873 g_assert_not_reached ();
875 ++not_done
; /* note: new discovery scheduled */
878 if (cand
->done
!= TRUE
) {
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
);
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 */
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
);
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",
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 */
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 */
929 ++not_done
; /* note: discovery not expired yet */
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 */
948 static gboolean
priv_discovery_tick (gpointer pointer
)
950 NiceAgent
*agent
= pointer
;
953 g_static_rec_mutex_lock (&agent
->mutex
);
954 ret
= priv_discovery_tick_unlocked (pointer
);
955 g_static_rec_mutex_unlock (&agent
->mutex
);
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
);
976 agent
->discovery_timer_source
= agent_timeout_add_with_context (agent
, agent
->timer_ta
, priv_discovery_tick
, agent
);