2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2006-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.
26 * Youness Alaoui, Collabora Ltd.
27 * Dafydd Harries, Collabora Ltd.
29 * Alternatively, the contents of this file may be used under the terms of the
30 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
31 * case the provisions of LGPL are applicable instead of those above. If you
32 * wish to allow use of your version of this file only under the terms of the
33 * LGPL and not to allow others to use your version of this file under the
34 * MPL, indicate your decision by deleting the provisions above and replace
35 * them with the notice and other provisions required by the LGPL. If you do
36 * not delete the provisions above, a recipient may use your version of this
37 * file under either the MPL or the LGPL.
42 * @brief ICE connectivity checks
57 #include "agent-priv.h"
58 #include "conncheck.h"
59 #include "discovery.h"
60 #include "stun/usages/ice.h"
61 #include "stun/usages/bind.h"
62 #include "stun/usages/turn.h"
64 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
);
65 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
);
66 static guint
priv_prune_pending_checks (Stream
*stream
, guint component_id
);
67 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
);
68 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
);
69 static size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
70 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
71 uint8_t *dest
, guint dest_len
, gboolean inbound
);
72 static size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
73 NiceCandidate
*remote
, uint8_t **password
);
75 static int priv_timer_expired (GTimeVal
*timer
, GTimeVal
*now
)
77 return (now
->tv_sec
== timer
->tv_sec
) ?
78 now
->tv_usec
>= timer
->tv_usec
:
79 now
->tv_sec
>= timer
->tv_sec
;
83 * Finds the next connectivity check in WAITING state.
85 static CandidateCheckPair
*priv_conn_check_find_next_waiting (GSList
*conn_check_list
)
89 /* note: list is sorted in priority order to first waiting check has
90 * the highest priority */
92 for (i
= conn_check_list
; i
; i
= i
->next
) {
93 CandidateCheckPair
*p
= i
->data
;
94 if (p
->state
== NICE_CHECK_WAITING
)
102 * Initiates a new connectivity check for a ICE candidate pair.
104 * @return TRUE on success, FALSE on error
106 static gboolean
priv_conn_check_initiate (NiceAgent
*agent
, CandidateCheckPair
*pair
)
108 /* XXX: from ID-16 onwards, the checks should not be sent
109 * immediately, but be put into the "triggered queue",
110 * see "7.2.1.4 Triggered Checks"
112 g_get_current_time (&pair
->next_tick
);
113 g_time_val_add (&pair
->next_tick
, agent
->timer_ta
* 1000);
114 pair
->state
= NICE_CHECK_IN_PROGRESS
;
115 nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent
, pair
);
116 conn_check_send (agent
, pair
);
121 * Unfreezes the next connectivity check in the list. Follows the
122 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
123 * (ID-19), with some exceptions (see comments in code).
125 * See also sect 7.1.2.2.3 (Updating Pair States), and
126 * priv_conn_check_unfreeze_related().
128 * @return TRUE on success, and FALSE if no frozen candidates were found.
130 static gboolean
priv_conn_check_unfreeze_next (NiceAgent
*agent
)
132 CandidateCheckPair
*pair
= NULL
;
135 /* XXX: the unfreezing is implemented a bit differently than in the
136 * current ICE spec, but should still be interoperate:
137 * - checks are not grouped by foundation
138 * - one frozen check is unfrozen (lowest component-id, highest
142 for (i
= agent
->streams
; i
; i
= i
->next
) {
143 Stream
*stream
= i
->data
;
144 guint64 max_frozen_priority
= 0;
147 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
148 CandidateCheckPair
*p
= j
->data
;
150 /* XXX: the prio check could be removed as the pairs are sorted
153 if (p
->state
== NICE_CHECK_FROZEN
) {
154 if (p
->priority
> max_frozen_priority
) {
155 max_frozen_priority
= p
->priority
;
166 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent
, pair
, pair
->stream_id
, pair
->component_id
, pair
->foundation
);
167 pair
->state
= NICE_CHECK_WAITING
;
168 nice_debug ("Agent %p : pair %p state WAITING", agent
, pair
);
176 * Unfreezes the next next connectivity check in the list after
177 * check 'success_check' has successfully completed.
179 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
181 * @param agent context
182 * @param ok_check a connectivity check that has just completed
184 * @return TRUE on success, and FALSE if no frozen candidates were found.
186 static void priv_conn_check_unfreeze_related (NiceAgent
*agent
, Stream
*stream
, CandidateCheckPair
*ok_check
)
192 g_assert (ok_check
->state
== NICE_CHECK_SUCCEEDED
);
194 g_assert (stream
->id
== ok_check
->stream_id
);
196 /* step: perform the step (1) of 'Updating Pair States' */
197 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
198 CandidateCheckPair
*p
= i
->data
;
200 if (p
->stream_id
== ok_check
->stream_id
) {
201 if (p
->state
== NICE_CHECK_FROZEN
&&
202 strcmp (p
->foundation
, ok_check
->foundation
) == 0) {
203 nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent
, p
, ok_check
);
204 p
->state
= NICE_CHECK_WAITING
;
205 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
211 /* step: perform the step (2) of 'Updating Pair States' */
212 stream
= agent_find_stream (agent
, ok_check
->stream_id
);
213 if (stream_all_components_ready (stream
)) {
214 /* step: unfreeze checks from other streams */
215 for (i
= agent
->streams
; i
; i
= i
->next
) {
217 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
218 CandidateCheckPair
*p
= j
->data
;
220 if (p
->stream_id
== s
->id
&&
221 p
->stream_id
!= ok_check
->stream_id
) {
222 if (p
->state
== NICE_CHECK_FROZEN
&&
223 strcmp (p
->foundation
, ok_check
->foundation
) == 0) {
224 nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent
, p
, s
->id
, ok_check
);
225 p
->state
= NICE_CHECK_WAITING
;
226 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
232 /* note: only unfreeze check from one stream at a time */
239 priv_conn_check_unfreeze_next (agent
);
243 * Helper function for connectivity check timer callback that
244 * runs through the stream specific part of the state machine.
246 * @param schedule if TRUE, schedule a new check
248 * @return will return FALSE when no more pending timers.
250 static gboolean
priv_conn_check_tick_stream (Stream
*stream
, NiceAgent
*agent
, GTimeVal
*now
)
252 gboolean keep_timer_going
= FALSE
;
253 guint s_inprogress
= 0, s_succeeded
= 0, s_discovered
= 0,
254 s_nominated
= 0, s_waiting_for_nomination
= 0;
255 guint frozen
= 0, waiting
= 0;
258 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
259 CandidateCheckPair
*p
= i
->data
;
261 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
262 if (p
->stun_message
.buffer
== NULL
) {
263 nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent
);
264 p
->state
= NICE_CHECK_FAILED
;
265 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
266 } else if (priv_timer_expired (&p
->next_tick
, now
)) {
267 switch (stun_timer_refresh (&p
->timer
)) {
268 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
270 /* case: error, abort processing */
271 StunTransactionId id
;
273 nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent
, p
);
274 p
->state
= NICE_CHECK_FAILED
;
275 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
277 stun_message_id (&p
->stun_message
, id
);
278 stun_agent_forget_transaction (&agent
->stun_agent
, id
);
280 p
->stun_message
.buffer
= NULL
;
281 p
->stun_message
.buffer_len
= 0;
286 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
288 /* case: not ready, so schedule a new timeout */
289 unsigned int timeout
= stun_timer_remainder (&p
->timer
);
290 nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
293 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
,
294 stun_message_length (&p
->stun_message
),
295 (gchar
*)p
->stun_buffer
);
298 /* note: convert from milli to microseconds for g_time_val_add() */
300 g_time_val_add (&p
->next_tick
, timeout
* 1000);
302 keep_timer_going
= TRUE
;
305 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
307 unsigned int timeout
= stun_timer_remainder (&p
->timer
);
309 /* note: convert from milli to microseconds for g_time_val_add() */
311 g_time_val_add (&p
->next_tick
, timeout
* 1000);
313 keep_timer_going
= TRUE
;
320 if (p
->state
== NICE_CHECK_FROZEN
)
322 else if (p
->state
== NICE_CHECK_IN_PROGRESS
)
324 else if (p
->state
== NICE_CHECK_WAITING
)
326 else if (p
->state
== NICE_CHECK_SUCCEEDED
)
328 else if (p
->state
== NICE_CHECK_DISCOVERED
)
331 if ((p
->state
== NICE_CHECK_SUCCEEDED
|| p
->state
== NICE_CHECK_DISCOVERED
)
334 else if ((p
->state
== NICE_CHECK_SUCCEEDED
||
335 p
->state
== NICE_CHECK_DISCOVERED
) && !p
->nominated
)
336 ++s_waiting_for_nomination
;
339 /* note: keep the timer going as long as there is work to be done */
341 keep_timer_going
= TRUE
;
343 /* note: if some components have established connectivity,
344 * but yet no nominated pair, keep timer going */
345 if (s_nominated
< stream
->n_components
&&
346 s_waiting_for_nomination
) {
347 keep_timer_going
= TRUE
;
348 if (agent
->controlling_mode
) {
350 for (n
= 0; n
< stream
->n_components
; n
++) {
351 for (k
= stream
->conncheck_list
; k
; k
= k
->next
) {
352 CandidateCheckPair
*p
= k
->data
;
353 /* note: highest priority item selected (list always sorted) */
354 if (p
->state
== NICE_CHECK_SUCCEEDED
||
355 p
->state
== NICE_CHECK_DISCOVERED
) {
356 nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent
, p
);
358 priv_conn_check_initiate (agent
, p
);
359 break; /* move to the next component */
366 static int tick_counter
= 0;
367 if (tick_counter
++ % 50 == 0 || keep_timer_going
!= TRUE
)
368 nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
369 "%u waiting, %u succeeded, %u discovered, %u nominated, "
370 "%u waiting-for-nom.", agent
,
371 tick_counter
, frozen
, s_inprogress
, waiting
, s_succeeded
,
372 s_discovered
, s_nominated
, s_waiting_for_nomination
);
375 return keep_timer_going
;
381 * Timer callback that handles initiating and managing connectivity
382 * checks (paced by the Ta timer).
384 * This function is designed for the g_timeout_add() interface.
386 * @return will return FALSE when no more pending timers.
388 static gboolean
priv_conn_check_tick_unlocked (gpointer pointer
)
390 CandidateCheckPair
*pair
= NULL
;
391 NiceAgent
*agent
= pointer
;
392 gboolean keep_timer_going
= FALSE
;
396 /* step: process ongoing STUN transactions */
397 g_get_current_time (&now
);
399 /* step: find the highest priority waiting check and send it */
400 for (i
= agent
->streams
; i
; i
= i
->next
) {
401 Stream
*stream
= i
->data
;
403 pair
= priv_conn_check_find_next_waiting (stream
->conncheck_list
);
409 priv_conn_check_initiate (agent
, pair
);
410 keep_timer_going
= TRUE
;
412 keep_timer_going
= priv_conn_check_unfreeze_next (agent
);
415 for (j
= agent
->streams
; j
; j
= j
->next
) {
416 Stream
*stream
= j
->data
;
418 priv_conn_check_tick_stream (stream
, agent
, &now
);
420 keep_timer_going
= res
;
423 /* step: stop timer if no work left */
424 if (keep_timer_going
!= TRUE
) {
425 nice_debug ("Agent %p : %s: stopping conncheck timer", agent
, G_STRFUNC
);
426 for (i
= agent
->streams
; i
; i
= i
->next
) {
427 Stream
*stream
= i
->data
;
428 priv_update_check_list_failed_components (agent
, stream
);
429 for (j
= stream
->components
; j
; j
= j
->next
) {
430 Component
*component
= j
->data
;
431 priv_update_check_list_state_for_ready (agent
, stream
, component
);
435 /* Stopping the timer so destroy the source.. this will allow
436 the timer to be reset if we get a set_remote_candidates after this
438 if (agent
->conncheck_timer_source
!= NULL
) {
439 g_source_destroy (agent
->conncheck_timer_source
);
440 g_source_unref (agent
->conncheck_timer_source
);
441 agent
->conncheck_timer_source
= NULL
;
444 /* XXX: what to signal, is all processing now really done? */
445 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent
);
448 return keep_timer_going
;
451 static gboolean
priv_conn_check_tick (gpointer pointer
)
456 if (g_source_is_destroyed (g_main_current_source ())) {
457 nice_debug ("Source was destroyed. "
458 "Avoided race condition in priv_conn_check_tick");
462 ret
= priv_conn_check_tick_unlocked (pointer
);
468 static gboolean
priv_conn_keepalive_retransmissions_tick (gpointer pointer
)
470 CandidatePair
*pair
= (CandidatePair
*) pointer
;
474 /* A race condition might happen where the mutex above waits for the lock
475 * and in the meantime another thread destroys the source.
476 * In that case, we don't need to run our retransmission tick since it should
477 * have been cancelled */
478 if (g_source_is_destroyed (g_main_current_source ())) {
479 nice_debug ("Source was destroyed. "
480 "Avoided race condition in priv_conn_keepalive_retransmissions_tick");
485 g_source_destroy (pair
->keepalive
.tick_source
);
486 g_source_unref (pair
->keepalive
.tick_source
);
487 pair
->keepalive
.tick_source
= NULL
;
489 switch (stun_timer_refresh (&pair
->keepalive
.timer
)) {
490 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
493 StunTransactionId id
;
496 stun_message_id (&pair
->keepalive
.stun_message
, id
);
497 stun_agent_forget_transaction (&pair
->keepalive
.agent
->stun_agent
, id
);
499 if (pair
->keepalive
.agent
->media_after_tick
) {
500 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
501 "but media was received. Suspecting keepalive lost because of "
502 "network bottleneck", pair
->keepalive
.agent
);
504 pair
->keepalive
.stun_message
.buffer
= NULL
;
506 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
507 "peer probably lost connection", pair
->keepalive
.agent
);
508 agent_signal_component_state_change (pair
->keepalive
.agent
,
509 pair
->keepalive
.stream_id
, pair
->keepalive
.component_id
,
510 NICE_COMPONENT_STATE_FAILED
);
514 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
516 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
517 stun_message_length (&pair
->keepalive
.stun_message
),
518 (gchar
*)pair
->keepalive
.stun_buffer
);
520 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
521 pair
->keepalive
.agent
);
522 pair
->keepalive
.tick_source
=
523 agent_timeout_add_with_context (pair
->keepalive
.agent
,
524 stun_timer_remainder (&pair
->keepalive
.timer
),
525 priv_conn_keepalive_retransmissions_tick
, pair
);
527 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
528 pair
->keepalive
.tick_source
=
529 agent_timeout_add_with_context (pair
->keepalive
.agent
,
530 stun_timer_remainder (&pair
->keepalive
.timer
),
531 priv_conn_keepalive_retransmissions_tick
, pair
);
542 * Timer callback that handles initiating and managing connectivity
543 * checks (paced by the Ta timer).
545 * This function is designed for the g_timeout_add() interface.
547 * @return will return FALSE when no more pending timers.
549 static gboolean
priv_conn_keepalive_tick_unlocked (NiceAgent
*agent
)
553 gboolean ret
= FALSE
;
556 /* case 1: session established and media flowing
557 * (ref ICE sect 10 "Keepalives" ID-19) */
558 for (i
= agent
->streams
; i
; i
= i
->next
) {
560 Stream
*stream
= i
->data
;
561 for (j
= stream
->components
; j
; j
= j
->next
) {
562 Component
*component
= j
->data
;
563 if (component
->selected_pair
.local
!= NULL
) {
564 CandidatePair
*p
= &component
->selected_pair
;
566 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
) {
567 guint32 priority
= nice_candidate_ice_priority_full (
568 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
, 1,
569 p
->local
->component_id
);
570 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
572 priv_create_username (agent
, agent_find_stream (agent
, stream
->id
),
573 component
->id
, p
->remote
, p
->local
, uname
, sizeof (uname
),
575 uint8_t *password
= NULL
;
576 size_t password_len
= priv_get_password (agent
,
577 agent_find_stream (agent
, stream
->id
), p
->remote
, &password
);
578 gchar tmpbuf
[INET6_ADDRSTRLEN
];
580 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
581 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
582 "socket=%u (c-id:%u), username='%s' (%d), "
583 "password='%s' (%d), priority=%u.", agent
,
584 tmpbuf
, nice_address_get_port (&p
->remote
->addr
),
585 g_socket_get_fd(((NiceSocket
*)p
->local
->sockptr
)->fileno
), component
->id
,
586 uname
, uname_len
, password
, password_len
, priority
);
589 buf_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
590 &p
->keepalive
.stun_message
, p
->keepalive
.stun_buffer
,
591 sizeof(p
->keepalive
.stun_buffer
),
592 uname
, uname_len
, password
, password_len
,
593 agent
->controlling_mode
, agent
->controlling_mode
, priority
,
596 agent_to_ice_compatibility (agent
));
598 nice_debug ("Agent %p: conncheck created %d - %p",
599 agent
, buf_len
, p
->keepalive
.stun_message
.buffer
);
602 stun_timer_start (&p
->keepalive
.timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
603 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
605 agent
->media_after_tick
= FALSE
;
607 /* send the conncheck */
608 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
,
609 buf_len
, (gchar
*)p
->keepalive
.stun_buffer
);
611 if (p
->keepalive
.tick_source
!= NULL
) {
612 g_source_destroy (p
->keepalive
.tick_source
);
613 g_source_unref (p
->keepalive
.tick_source
);
614 p
->keepalive
.tick_source
= NULL
;
617 p
->keepalive
.stream_id
= stream
->id
;
618 p
->keepalive
.component_id
= component
->id
;
619 p
->keepalive
.agent
= agent
;
621 p
->keepalive
.tick_source
=
622 agent_timeout_add_with_context (p
->keepalive
.agent
,
623 stun_timer_remainder (&p
->keepalive
.timer
),
624 priv_conn_keepalive_retransmissions_tick
, p
);
630 buf_len
= stun_usage_bind_keepalive (&agent
->stun_agent
,
631 &p
->keepalive
.stun_message
, p
->keepalive
.stun_buffer
,
632 sizeof(p
->keepalive
.stun_buffer
));
635 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
, buf_len
,
636 (gchar
*)p
->keepalive
.stun_buffer
);
638 nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
639 agent
, p
, (int) buf_len
);
648 /* case 2: connectivity establishment ongoing
649 * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */
650 for (i
= agent
->streams
; i
; i
= i
->next
) {
651 Stream
*stream
= i
->data
;
652 for (j
= stream
->components
; j
; j
= j
->next
) {
653 Component
*component
= j
->data
;
654 if (component
->state
< NICE_COMPONENT_STATE_READY
&&
655 agent
->stun_server_ip
) {
656 NiceAddress stun_server
;
657 if (nice_address_set_from_string (&stun_server
, agent
->stun_server_ip
)) {
658 StunAgent stun_agent
;
659 uint8_t stun_buffer
[STUN_MAX_MESSAGE_SIZE
];
660 StunMessage stun_message
;
661 size_t buffer_len
= 0;
663 nice_address_set_port (&stun_server
, agent
->stun_server_port
);
665 stun_agent_init (&stun_agent
, STUN_ALL_KNOWN_ATTRIBUTES
,
666 STUN_COMPATIBILITY_RFC3489
, 0);
668 buffer_len
= stun_usage_bind_create (&stun_agent
,
669 &stun_message
, stun_buffer
, sizeof(stun_buffer
));
671 for (k
= component
->local_candidates
; k
; k
= k
->next
) {
672 NiceCandidate
*candidate
= (NiceCandidate
*) k
->data
;
673 if (candidate
->type
== NICE_CANDIDATE_TYPE_HOST
) {
674 /* send the conncheck */
675 nice_debug ("Agent %p : resending STUN on %s to keep the "
676 "candidate alive.", agent
, candidate
->foundation
);
677 nice_socket_send (candidate
->sockptr
, &stun_server
,
678 buffer_len
, (gchar
*)stun_buffer
);
687 nice_debug ("Agent %p : %s: stopping keepalive timer", agent
, G_STRFUNC
);
697 static gboolean
priv_conn_keepalive_tick (gpointer pointer
)
699 NiceAgent
*agent
= pointer
;
703 if (g_source_is_destroyed (g_main_current_source ())) {
704 nice_debug ("Source was destroyed. "
705 "Avoided race condition in priv_conn_keepalive_tick");
710 ret
= priv_conn_keepalive_tick_unlocked (agent
);
712 if (agent
->keepalive_timer_source
) {
713 g_source_destroy (agent
->keepalive_timer_source
);
714 g_source_unref (agent
->keepalive_timer_source
);
715 agent
->keepalive_timer_source
= NULL
;
723 static gboolean
priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer
)
725 CandidateRefresh
*cand
= (CandidateRefresh
*) pointer
;
729 /* A race condition might happen where the mutex above waits for the lock
730 * and in the meantime another thread destroys the source.
731 * In that case, we don't need to run our retransmission tick since it should
732 * have been cancelled */
733 if (g_source_is_destroyed (g_main_current_source ())) {
734 nice_debug ("Source was destroyed. "
735 "Avoided race condition in priv_turn_allocate_refresh_retransmissions_tick");
741 g_source_destroy (cand
->tick_source
);
742 g_source_unref (cand
->tick_source
);
743 cand
->tick_source
= NULL
;
745 switch (stun_timer_refresh (&cand
->timer
)) {
746 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
749 StunTransactionId id
;
751 stun_message_id (&cand
->stun_message
, id
);
752 stun_agent_forget_transaction (&cand
->stun_agent
, id
);
754 refresh_cancel (cand
);
757 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
759 nice_socket_send (cand
->nicesock
, &cand
->server
,
760 stun_message_length (&cand
->stun_message
), (gchar
*)cand
->stun_buffer
);
762 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
763 stun_timer_remainder (&cand
->timer
),
764 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
766 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
767 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
768 stun_timer_remainder (&cand
->timer
),
769 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
778 static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh
*cand
)
784 size_t buffer_len
= 0;
785 StunUsageTurnCompatibility turn_compat
=
786 agent_to_turn_compatibility (cand
->agent
);
788 username
= (uint8_t *)cand
->turn
->username
;
789 username_len
= (size_t) strlen (cand
->turn
->username
);
790 password
= (uint8_t *)cand
->turn
->password
;
791 password_len
= (size_t) strlen (cand
->turn
->password
);
793 if (turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_MSN
||
794 turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_OC2007
) {
795 username
= g_base64_decode ((gchar
*)username
, &username_len
);
796 password
= g_base64_decode ((gchar
*)password
, &password_len
);
799 buffer_len
= stun_usage_turn_create_refresh (&cand
->stun_agent
,
800 &cand
->stun_message
, cand
->stun_buffer
, sizeof(cand
->stun_buffer
),
801 cand
->stun_resp_msg
.buffer
== NULL
? NULL
: &cand
->stun_resp_msg
, -1,
802 username
, username_len
,
803 password
, password_len
,
806 if (turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_MSN
||
807 turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_OC2007
) {
808 g_free (cand
->msn_turn_username
);
809 g_free (cand
->msn_turn_password
);
810 cand
->msn_turn_username
= username
;
811 cand
->msn_turn_password
= password
;
814 nice_debug ("Agent %p : Sending allocate Refresh %d", cand
->agent
, buffer_len
);
816 if (cand
->tick_source
!= NULL
) {
817 g_source_destroy (cand
->tick_source
);
818 g_source_unref (cand
->tick_source
);
819 cand
->tick_source
= NULL
;
822 if (buffer_len
> 0) {
823 stun_timer_start (&cand
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
824 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
826 /* send the refresh */
827 nice_socket_send (cand
->nicesock
, &cand
->server
,
828 buffer_len
, (gchar
*)cand
->stun_buffer
);
830 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
831 stun_timer_remainder (&cand
->timer
),
832 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
839 * Timer callback that handles refreshing TURN allocations
841 * This function is designed for the g_timeout_add() interface.
843 * @return will return FALSE when no more pending timers.
845 static gboolean
priv_turn_allocate_refresh_tick (gpointer pointer
)
847 CandidateRefresh
*cand
= (CandidateRefresh
*) pointer
;
850 if (g_source_is_destroyed (g_main_current_source ())) {
851 nice_debug ("Source was destroyed. "
852 "Avoided race condition in priv_turn_allocate_refresh_tick");
857 priv_turn_allocate_refresh_tick_unlocked (cand
);
865 * Initiates the next pending connectivity check.
867 * @return TRUE if a pending check was scheduled
869 gboolean
conn_check_schedule_next (NiceAgent
*agent
)
871 gboolean res
= priv_conn_check_unfreeze_next (agent
);
872 nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent
, res
);
874 if (agent
->discovery_unsched_items
> 0)
875 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent
);
877 /* step: call once imediately */
878 res
= priv_conn_check_tick_unlocked ((gpointer
) agent
);
879 nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent
, res
);
881 /* step: schedule timer if not running yet */
882 if (res
&& agent
->conncheck_timer_source
== NULL
) {
883 agent
->conncheck_timer_source
= agent_timeout_add_with_context (agent
, agent
->timer_ta
, priv_conn_check_tick
, agent
);
886 /* step: also start the keepalive timer */
887 if (agent
->keepalive_timer_source
== NULL
) {
888 agent
->keepalive_timer_source
= agent_timeout_add_with_context (agent
, NICE_AGENT_TIMER_TR_DEFAULT
, priv_conn_keepalive_tick
, agent
);
891 nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent
, res
);
896 * Compares two connectivity check items. Checkpairs are sorted
897 * in descending priority order, with highest priority item at
898 * the start of the list.
900 gint
conn_check_compare (const CandidateCheckPair
*a
, const CandidateCheckPair
*b
)
902 if (a
->priority
> b
->priority
)
904 else if (a
->priority
< b
->priority
)
910 * Preprocesses a new connectivity check by going through list
911 * of a any stored early incoming connectivity checks from
912 * the remote peer. If a matching incoming check has been already
913 * received, update the state of the new outgoing check 'pair'.
915 * @param agent context pointer
916 * @param stream which stream (of the agent)
917 * @param component pointer to component object to which 'pair'has been added
918 * @param pair newly added connectivity check
920 static void priv_preprocess_conn_check_pending_data (NiceAgent
*agent
, Stream
*stream
, Component
*component
, CandidateCheckPair
*pair
)
923 for (i
= component
->incoming_checks
; i
; i
= i
->next
) {
924 IncomingCheck
*icheck
= i
->data
;
925 if (nice_address_equal (&icheck
->from
, &pair
->remote
->addr
) &&
926 icheck
->local_socket
== pair
->local
->sockptr
) {
927 nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent
, pair
, icheck
, agent
, stream
->id
, component
->id
);
928 if (icheck
->use_candidate
)
929 priv_mark_pair_nominated (agent
, stream
, component
, pair
->remote
);
930 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, pair
->remote
, icheck
->use_candidate
);
936 * Handle any processing steps for connectivity checks after
937 * remote candidates have been set. This function handles
938 * the special case where answerer has sent us connectivity
939 * checks before the answer (containing candidate information),
940 * reaches us. The special case is documented in sect 7.2
941 * if ICE spec (ID-19).
943 void conn_check_remote_candidates_set(NiceAgent
*agent
)
945 GSList
*i
, *j
, *k
, *l
, *m
, *n
;
947 for (i
= agent
->streams
; i
; i
= i
->next
) {
948 Stream
*stream
= i
->data
;
949 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
950 CandidateCheckPair
*pair
= j
->data
;
951 Component
*component
= stream_find_component_by_id (stream
, pair
->component_id
);
952 gboolean match
= FALSE
;
954 /* performn delayed processing of spec steps section 7.2.1.4,
955 and section 7.2.1.5 */
956 priv_preprocess_conn_check_pending_data (agent
, stream
, component
, pair
);
958 for (k
= component
->incoming_checks
; k
; k
= k
->next
) {
959 IncomingCheck
*icheck
= k
->data
;
960 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
961 * be handled separately */
962 for (l
= component
->remote_candidates
; l
; l
= l
->next
) {
963 NiceCandidate
*cand
= l
->data
;
964 if (nice_address_equal (&icheck
->from
, &cand
->addr
)) {
970 /* note: we have gotten an incoming connectivity check from
971 * an address that is not a known remote candidate */
973 NiceCandidate
*local_candidate
= NULL
;
974 NiceCandidate
*remote_candidate
= NULL
;
976 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
977 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
978 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
979 /* We need to find which local candidate was used */
980 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
983 nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
984 "stored pending check", agent
);
986 for (m
= component
->remote_candidates
;
987 m
!= NULL
&& remote_candidate
== NULL
; m
= m
->next
) {
988 for (n
= component
->local_candidates
; n
; n
= n
->next
) {
989 NiceCandidate
*rcand
= m
->data
;
990 NiceCandidate
*lcand
= n
->data
;
992 uname_len
= priv_create_username (agent
, stream
,
993 component
->id
, rcand
, lcand
,
994 uname
, sizeof (uname
), TRUE
);
996 stun_debug ("pending check, comparing username '");
997 stun_debug_bytes (icheck
->username
,
998 icheck
->username
? icheck
->username_len
: 0);
999 stun_debug ("' (%d) with '", icheck
->username_len
);
1000 stun_debug_bytes (uname
, uname_len
);
1001 stun_debug ("' (%d) : %d\n",
1002 uname_len
, icheck
->username
&&
1003 uname_len
== icheck
->username_len
&&
1004 memcmp (icheck
->username
, uname
, uname_len
) == 0);
1006 if (icheck
->username
&&
1007 uname_len
== icheck
->username_len
&&
1008 memcmp (uname
, icheck
->username
, icheck
->username_len
) == 0) {
1009 local_candidate
= lcand
;
1010 remote_candidate
= rcand
;
1017 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1018 local_candidate
== NULL
) {
1019 /* if we couldn't match the username, then the matching remote
1020 * candidate hasn't been received yet.. we must wait */
1021 nice_debug ("Agent %p : Username check failed. pending check has "
1022 "to wait to be processed", agent
);
1024 NiceCandidate
*candidate
;
1026 nice_debug ("Agent %p : Discovered peer reflexive from early i-check",
1029 discovery_learn_remote_peer_reflexive_candidate (agent
,
1034 icheck
->local_socket
,
1035 local_candidate
, remote_candidate
);
1037 conn_check_add_for_candidate (agent
, stream
->id
, component
, candidate
);
1039 if (icheck
->use_candidate
)
1040 priv_mark_pair_nominated (agent
, stream
, component
, candidate
);
1041 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, candidate
, icheck
->use_candidate
);
1047 /* Once we process the pending checks, we should free them to avoid
1048 * reprocessing them again if a dribble-mode set_remote_candidates
1050 for (m
= component
->incoming_checks
; m
; m
= m
->next
) {
1051 IncomingCheck
*icheck
= m
->data
;
1052 g_free (icheck
->username
);
1053 g_slice_free (IncomingCheck
, icheck
);
1055 g_slist_free (component
->incoming_checks
);
1056 component
->incoming_checks
= NULL
;
1062 * Enforces the upper limit for connectivity checks as described
1063 * in ICE spec section 5.7.3 (ID-19). See also
1064 * conn_check_add_for_candidate().
1066 static GSList
*priv_limit_conn_check_list_size (GSList
*conncheck_list
, guint upper_limit
)
1068 guint list_len
= g_slist_length (conncheck_list
);
1070 GSList
*result
= conncheck_list
;
1072 if (list_len
> upper_limit
) {
1073 nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
1074 "Maximum connchecks allowed : %d", list_len
, upper_limit
);
1075 c
= list_len
- upper_limit
;
1076 if (c
== list_len
) {
1077 /* case: delete whole list */
1078 g_slist_foreach (conncheck_list
, conn_check_free_item
, NULL
);
1079 g_slist_free (conncheck_list
),
1083 /* case: remove 'c' items from list end (lowest priority) */
1087 i
= g_slist_nth (conncheck_list
, c
- 1);
1093 /* delete the rest of the connectivity check list */
1094 g_slist_foreach (tmp
, conn_check_free_item
, NULL
);
1104 * Changes the selected pair for the component if 'pair' is nominated
1105 * and has higher priority than the currently selected pair. See
1106 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
1108 static gboolean
priv_update_selected_pair (NiceAgent
*agent
, Component
*component
, CandidateCheckPair
*pair
)
1110 g_assert (component
);
1112 if (pair
->priority
> component
->selected_pair
.priority
) {
1113 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1114 "(prio:%" G_GUINT64_FORMAT
").", agent
, component
->id
, pair
->local
->foundation
,
1115 pair
->remote
->foundation
, pair
->priority
);
1117 if (component
->selected_pair
.keepalive
.tick_source
!= NULL
) {
1118 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
1119 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
1120 component
->selected_pair
.keepalive
.tick_source
= NULL
;
1123 memset (&component
->selected_pair
, 0, sizeof(CandidatePair
));
1124 component
->selected_pair
.local
= pair
->local
;
1125 component
->selected_pair
.remote
= pair
->remote
;
1126 component
->selected_pair
.priority
= pair
->priority
;
1128 priv_conn_keepalive_tick_unlocked (agent
);
1130 agent_signal_new_selected_pair (agent
, pair
->stream_id
, component
->id
, pair
->local
->foundation
, pair
->remote
->foundation
);
1138 * Updates the check list state.
1140 * Implements parts of the algorithm described in
1141 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
1142 * component, all checks have been completed and have
1143 * failed, mark that component's state to NICE_CHECK_FAILED.
1145 * Sends a component state changesignal via 'agent'.
1147 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
)
1150 /* note: emitting a signal might cause the client
1151 * to remove the stream, thus the component count
1152 * must be fetched before entering the loop*/
1153 guint c
, components
= stream
->n_components
;
1155 /* note: iterate the conncheck list for each component separately */
1156 for (c
= 0; c
< components
; c
++) {
1157 Component
*comp
= NULL
;
1158 if (!agent_find_component (agent
, stream
->id
, c
+1, NULL
, &comp
))
1161 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1162 CandidateCheckPair
*p
= i
->data
;
1164 if (p
->stream_id
== stream
->id
&&
1165 p
->component_id
== (c
+ 1)) {
1166 if (p
->state
!= NICE_CHECK_FAILED
)
1171 /* note: all checks have failed
1172 * Set the component to FAILED only if it actually had remote candidates
1174 if (i
== NULL
&& comp
!= NULL
&& comp
->remote_candidates
!= NULL
)
1175 agent_signal_component_state_change (agent
,
1177 (c
+ 1), /* component-id */
1178 NICE_COMPONENT_STATE_FAILED
);
1183 * Updates the check list state for a stream component.
1185 * Implements the algorithm described in ICE sect 8.1.2
1186 * "Updating States" (ID-19) as it applies to checks of
1187 * a certain component. If there are any nominated pairs,
1188 * ICE processing may be concluded, and component state is
1191 * Sends a component state changesignal via 'agent'.
1193 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
)
1196 guint succeeded
= 0, nominated
= 0;
1198 g_assert (component
);
1200 /* step: search for at least one nominated pair */
1201 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1202 CandidateCheckPair
*p
= i
->data
;
1203 if (p
->component_id
== component
->id
) {
1204 if (p
->state
== NICE_CHECK_SUCCEEDED
||
1205 p
->state
== NICE_CHECK_DISCOVERED
) {
1207 if (p
->nominated
== TRUE
) {
1214 if (nominated
> 0) {
1215 /* Only go to READY if no checks are left in progress. If there are
1216 * any that are kept, then this function will be called again when the
1217 * conncheck tick timer finishes them all */
1218 if (priv_prune_pending_checks (stream
, component
->id
) == 0) {
1219 agent_signal_component_state_change (agent
, stream
->id
,
1220 component
->id
, NICE_COMPONENT_STATE_READY
);
1223 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent
, nominated
, succeeded
, component
->id
);
1227 * The remote party has signalled that the candidate pair
1228 * described by 'component' and 'remotecand' is nominated
1231 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
)
1235 g_assert (component
);
1237 /* step: search for at least one nominated pair */
1238 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1239 CandidateCheckPair
*pair
= i
->data
;
1240 /* XXX: hmm, how to figure out to which local candidate the
1241 * check was sent to? let's mark all matching pairs
1242 * as nominated instead */
1243 if (pair
->remote
== remotecand
) {
1244 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent
, pair
, pair
->foundation
);
1245 pair
->nominated
= TRUE
;
1246 if (pair
->state
== NICE_CHECK_SUCCEEDED
||
1247 pair
->state
== NICE_CHECK_DISCOVERED
)
1248 priv_update_selected_pair (agent
, component
, pair
);
1249 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1255 * Creates a new connectivity check pair and adds it to
1256 * the agent's list of checks.
1258 static void priv_add_new_check_pair (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*local
, NiceCandidate
*remote
, NiceCheckState initial_state
, gboolean use_candidate
)
1260 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1261 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1263 stream
->conncheck_list
= g_slist_insert_sorted (stream
->conncheck_list
, pair
,
1264 (GCompareFunc
)conn_check_compare
);
1266 pair
->agent
= agent
;
1267 pair
->stream_id
= stream_id
;
1268 pair
->component_id
= component
->id
;;
1269 pair
->local
= local
;
1270 pair
->remote
= remote
;
1271 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s", local
->foundation
, remote
->foundation
);
1273 pair
->priority
= agent_candidate_pair_priority (agent
, local
, remote
);
1274 pair
->state
= initial_state
;
1275 nice_debug ("Agent %p : creating new pair %p state %d", agent
, pair
, initial_state
);
1276 pair
->nominated
= use_candidate
;
1277 pair
->controlling
= agent
->controlling_mode
;
1279 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent
, pair
, pair
->foundation
, stream_id
);
1281 /* implement the hard upper limit for number of
1282 checks (see sect 5.7.3 ICE ID-19): */
1283 if (agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
) {
1284 stream
->conncheck_list
=
1285 priv_limit_conn_check_list_size (stream
->conncheck_list
, agent
->max_conn_checks
);
1289 static gboolean
priv_conn_check_add_for_candidate_pair (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*local
, NiceCandidate
*remote
)
1291 gboolean ret
= FALSE
;
1292 /* note: do not create pairs where the local candidate is
1293 * a srv-reflexive (ICE 5.7.3. "Pruning the pairs" ID-9) */
1294 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
1295 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1296 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1297 local
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
) {
1301 /* note: match pairs only if transport and address family are the same */
1302 if (local
->transport
== remote
->transport
&&
1303 local
->addr
.s
.addr
.sa_family
== remote
->addr
.s
.addr
.sa_family
) {
1305 priv_add_new_check_pair (agent
, stream_id
, component
, local
, remote
, NICE_CHECK_FROZEN
, FALSE
);
1307 if (component
->state
< NICE_COMPONENT_STATE_CONNECTED
) {
1308 agent_signal_component_state_change (agent
,
1311 NICE_COMPONENT_STATE_CONNECTING
);
1313 agent_signal_component_state_change (agent
,
1316 NICE_COMPONENT_STATE_CONNECTED
);
1324 * Forms new candidate pairs by matching the new remote candidate
1325 * 'remote_cand' with all existing local candidates of 'component'.
1326 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1329 * @param agent context
1330 * @param component pointer to the component
1331 * @param remote remote candidate to match with
1333 * @return number of checks added, negative on fatal errors
1335 int conn_check_add_for_candidate (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*remote
)
1341 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1343 NiceCandidate
*local
= i
->data
;
1344 ret
= priv_conn_check_add_for_candidate_pair (agent
, stream_id
, component
, local
, remote
);
1355 * Forms new candidate pairs by matching the new local candidate
1356 * 'local_cand' with all existing remote candidates of 'component'.
1358 * @param agent context
1359 * @param component pointer to the component
1360 * @param local local candidate to match with
1362 * @return number of checks added, negative on fatal errors
1364 int conn_check_add_for_local_candidate (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*local
)
1370 for (i
= component
->remote_candidates
; i
; i
= i
->next
) {
1372 NiceCandidate
*remote
= i
->data
;
1373 ret
= priv_conn_check_add_for_candidate_pair (agent
, stream_id
, component
, local
, remote
);
1384 * Frees the CandidateCheckPair structure pointer to
1385 * by 'user data'. Compatible with g_slist_foreach().
1387 void conn_check_free_item (gpointer data
, gpointer user_data
)
1389 CandidateCheckPair
*pair
= data
;
1390 g_assert (user_data
== NULL
);
1391 pair
->stun_message
.buffer
= NULL
;
1392 pair
->stun_message
.buffer_len
= 0;
1393 g_slice_free (CandidateCheckPair
, pair
);
1397 * Frees all resources of all connectivity checks.
1399 void conn_check_free (NiceAgent
*agent
)
1402 for (i
= agent
->streams
; i
; i
= i
->next
) {
1403 Stream
*stream
= i
->data
;
1405 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent
, stream
);
1406 if (stream
->conncheck_list
) {
1407 g_slist_foreach (stream
->conncheck_list
, conn_check_free_item
, NULL
);
1408 g_slist_free (stream
->conncheck_list
),
1409 stream
->conncheck_list
= NULL
;
1413 if (agent
->conncheck_timer_source
!= NULL
) {
1414 g_source_destroy (agent
->conncheck_timer_source
);
1415 g_source_unref (agent
->conncheck_timer_source
);
1416 agent
->conncheck_timer_source
= NULL
;
1421 * Prunes the list of connectivity checks for items related
1422 * to stream 'stream_id'.
1424 * @return TRUE on success, FALSE on a fatal error
1426 gboolean
conn_check_prune_stream (NiceAgent
*agent
, Stream
*stream
)
1428 CandidateCheckPair
*pair
;
1431 for (i
= stream
->conncheck_list
; i
; ) {
1432 GSList
*next
= i
->next
;
1435 g_assert (pair
->stream_id
== stream
->id
);
1437 stream
->conncheck_list
=
1438 g_slist_remove (stream
->conncheck_list
, pair
);
1439 conn_check_free_item (pair
, NULL
);
1441 if (!stream
->conncheck_list
)
1445 if (!stream
->conncheck_list
)
1446 conn_check_free (agent
);
1448 /* return FALSE if there was a memory allocation failure */
1449 if (stream
->conncheck_list
== NULL
&& i
!= NULL
)
1456 * Fills 'dest' with a username string for use in an outbound connectivity
1457 * checks. No more than 'dest_len' characters (including terminating
1458 * NULL) is ever written to the 'dest'.
1461 size_t priv_gen_username (NiceAgent
*agent
, guint component_id
,
1462 gchar
*remote
, gchar
*local
, uint8_t *dest
, guint dest_len
)
1465 gsize remote_len
= strlen (remote
);
1466 gsize local_len
= strlen (local
);
1468 if (remote_len
> 0 && local_len
> 0) {
1469 if (agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
&&
1470 dest_len
>= remote_len
+ local_len
+ 1) {
1471 memcpy (dest
, remote
, remote_len
);
1473 memcpy (dest
+ len
, ":", 1);
1475 memcpy (dest
+ len
, local
, local_len
);
1477 } else if ((agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1478 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1479 dest_len
>= remote_len
+ local_len
+ 4 ) {
1480 memcpy (dest
, remote
, remote_len
);
1482 memcpy (dest
+ len
, ":", 1);
1484 memcpy (dest
+ len
, local
, local_len
);
1487 memset (dest
+ len
, 0, 4 - (len
% 4));
1488 len
+= 4 - (len
% 4);
1490 } else if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1491 dest_len
>= remote_len
+ local_len
) {
1492 memcpy (dest
, remote
, remote_len
);
1494 memcpy (dest
+ len
, local
, local_len
);
1496 } else if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1497 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1498 gchar component_str
[10];
1499 guchar
*local_decoded
= NULL
;
1500 guchar
*remote_decoded
= NULL
;
1501 gsize local_decoded_len
;
1502 gsize remote_decoded_len
;
1506 g_snprintf (component_str
, sizeof(component_str
), "%d", component_id
);
1507 local_decoded
= g_base64_decode (local
, &local_decoded_len
);
1508 remote_decoded
= g_base64_decode (remote
, &remote_decoded_len
);
1510 total_len
= remote_decoded_len
+ local_decoded_len
+ 3 + 2*strlen (component_str
);
1511 padding
= 4 - (total_len
% 4);
1513 if (dest_len
>= total_len
+ padding
) {
1514 guchar pad_char
[1] = {0};
1517 memcpy (dest
, remote_decoded
, remote_decoded_len
);
1518 len
+= remote_decoded_len
;
1519 memcpy (dest
+ len
, ":", 1);
1521 memcpy (dest
+ len
, component_str
, strlen (component_str
));
1522 len
+= strlen (component_str
);
1524 memcpy (dest
+ len
, ":", 1);
1527 memcpy (dest
+ len
, local_decoded
, local_decoded_len
);
1528 len
+= local_decoded_len
;
1529 memcpy (dest
+ len
, ":", 1);
1531 memcpy (dest
+ len
, component_str
, strlen (component_str
));;
1532 len
+= strlen (component_str
);
1534 for (i
= 0; i
< padding
; i
++) {
1535 memcpy (dest
+ len
, pad_char
, 1);
1541 g_free (local_decoded
);
1542 g_free (remote_decoded
);
1550 * Fills 'dest' with a username string for use in an outbound connectivity
1551 * checks. No more than 'dest_len' characters (including terminating
1552 * NULL) is ever written to the 'dest'.
1555 size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
1556 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
1557 uint8_t *dest
, guint dest_len
, gboolean inbound
)
1559 gchar
*local_username
= NULL
;
1560 gchar
*remote_username
= NULL
;
1563 if (remote
&& remote
->username
) {
1564 remote_username
= remote
->username
;
1567 if (local
&& local
->username
) {
1568 local_username
= local
->username
;
1572 if (remote_username
== NULL
) {
1573 remote_username
= stream
->remote_ufrag
;
1575 if (local_username
== NULL
) {
1576 local_username
= stream
->local_ufrag
;
1580 if (local_username
&& remote_username
) {
1582 return priv_gen_username (agent
, component_id
,
1583 local_username
, remote_username
, dest
, dest_len
);
1585 return priv_gen_username (agent
, component_id
,
1586 remote_username
, local_username
, dest
, dest_len
);
1594 * Returns a password string for use in an outbound connectivity
1598 size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
1599 NiceCandidate
*remote
, uint8_t **password
)
1601 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
)
1604 if (remote
&& remote
->password
) {
1605 *password
= (uint8_t *)remote
->password
;
1606 return strlen (remote
->password
);
1610 *password
= (uint8_t *)stream
->remote_password
;
1611 return strlen (stream
->remote_password
);
1618 * Sends a connectivity check over candidate pair 'pair'.
1620 * @return zero on success, non-zero on error
1622 int conn_check_send (NiceAgent
*agent
, CandidateCheckPair
*pair
)
1625 /* note: following information is supplied:
1626 * - username (for USERNAME attribute)
1627 * - password (for MESSAGE-INTEGRITY)
1628 * - priority (for PRIORITY)
1629 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1630 * - USE-CANDIDATE (if sent by the controlling agent)
1634 nice_candidate_ice_priority_full (
1635 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
,
1637 pair
->local
->component_id
);
1639 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
1641 priv_create_username (agent
, agent_find_stream (agent
, pair
->stream_id
),
1642 pair
->component_id
, pair
->remote
, pair
->local
, uname
, sizeof (uname
), FALSE
);
1643 uint8_t *password
= NULL
;
1644 size_t password_len
= priv_get_password (agent
,
1645 agent_find_stream (agent
, pair
->stream_id
), pair
->remote
, &password
);
1647 bool controlling
= agent
->controlling_mode
;
1648 /* XXX: add API to support different nomination modes: */
1649 bool cand_use
= controlling
;
1651 unsigned int timeout
;
1653 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1654 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1655 password
= g_base64_decode ((gchar
*) password
, &password_len
);
1659 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1660 nice_address_to_string (&pair
->remote
->addr
, tmpbuf
);
1661 nice_debug ("Agent %p : STUN-CC REQ to '%s:%u', socket=%u, pair=%s (c-id:%u), tie=%llu, username='%s' (%d), password='%s' (%d), priority=%u.", agent
,
1663 nice_address_get_port (&pair
->remote
->addr
),
1664 g_socket_get_fd(((NiceSocket
*)pair
->local
->sockptr
)->fileno
),
1665 pair
->foundation
, pair
->component_id
,
1666 (unsigned long long)agent
->tie_breaker
,
1667 uname
, uname_len
, password
, password_len
, priority
);
1672 pair
->nominated
= controlling
;
1674 if (uname_len
> 0) {
1676 buffer_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
1677 &pair
->stun_message
, pair
->stun_buffer
, sizeof(pair
->stun_buffer
),
1678 uname
, uname_len
, password
, password_len
,
1679 cand_use
, controlling
, priority
,
1682 agent_to_ice_compatibility (agent
));
1684 nice_debug ("Agent %p: conncheck created %d - %p", agent
, buffer_len
, pair
->stun_message
.buffer
);
1686 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1687 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1691 if (buffer_len
> 0) {
1692 stun_timer_start (&pair
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
1693 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
1695 /* send the conncheck */
1696 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
1697 buffer_len
, (gchar
*)pair
->stun_buffer
);
1699 timeout
= stun_timer_remainder (&pair
->timer
);
1700 /* note: convert from milli to microseconds for g_time_val_add() */
1701 g_get_current_time (&pair
->next_tick
);
1702 g_time_val_add (&pair
->next_tick
, timeout
* 1000);
1704 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent
);
1705 pair
->stun_message
.buffer
= NULL
;
1706 pair
->stun_message
.buffer_len
= 0;
1710 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent
);
1711 pair
->stun_message
.buffer
= NULL
;
1712 pair
->stun_message
.buffer_len
= 0;
1720 * Implemented the pruning steps described in ICE sect 8.1.2
1721 * "Updating States" (ID-19) after a pair has been nominated.
1723 * @see priv_update_check_list_state_failed_components()
1725 static guint
priv_prune_pending_checks (Stream
*stream
, guint component_id
)
1728 guint64 highest_nominated_priority
= 0;
1729 guint in_progress
= 0;
1731 nice_debug ("Agent XXX: Finding highest priority for component %d",
1734 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1735 CandidateCheckPair
*p
= i
->data
;
1736 if (p
->component_id
== component_id
&&
1737 (p
->state
== NICE_CHECK_SUCCEEDED
||
1738 p
->state
== NICE_CHECK_DISCOVERED
) &&
1739 p
->nominated
== TRUE
){
1740 if (p
->priority
> highest_nominated_priority
) {
1741 highest_nominated_priority
= p
->priority
;
1746 nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
1747 "is %" G_GUINT64_FORMAT
, highest_nominated_priority
);
1749 /* step: cancel all FROZEN and WAITING pairs for the component */
1750 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1751 CandidateCheckPair
*p
= i
->data
;
1752 if (p
->component_id
== component_id
) {
1753 if (p
->state
== NICE_CHECK_FROZEN
||
1754 p
->state
== NICE_CHECK_WAITING
) {
1755 p
->state
= NICE_CHECK_CANCELLED
;
1756 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1759 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1760 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1761 if (highest_nominated_priority
!= 0 &&
1762 p
->priority
< highest_nominated_priority
) {
1763 p
->stun_message
.buffer
= NULL
;
1764 p
->stun_message
.buffer_len
= 0;
1765 p
->state
= NICE_CHECK_CANCELLED
;
1766 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1768 /* We must keep the higher priority pairs running because if a udp
1769 * packet was lost, we might end up using a bad candidate */
1770 nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %"
1771 G_GUINT64_FORMAT
" is higher than currently nominated pair %"
1772 G_GUINT64_FORMAT
, p
, p
->priority
, highest_nominated_priority
);
1783 * Schedules a triggered check after a successfully inbound
1784 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1786 * @param agent self pointer
1787 * @param component the check is related to
1788 * @param local_socket socket from which the inbound check was received
1789 * @param remote_cand remote candidate from which the inbound check was sent
1790 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1792 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
)
1795 NiceCandidate
*local
= NULL
;
1797 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1798 CandidateCheckPair
*p
= i
->data
;
1799 if (p
->component_id
== component
->id
&&
1800 p
->remote
== remote_cand
&&
1801 p
->local
->sockptr
== local_socket
) {
1803 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent
, p
);
1805 if (p
->state
== NICE_CHECK_WAITING
||
1806 p
->state
== NICE_CHECK_FROZEN
)
1807 priv_conn_check_initiate (agent
, p
);
1808 else if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1809 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1810 * we should cancel the existing one, instead we reset our timer, so
1811 * we'll resend the exiting transactions faster if needed...? :P
1813 nice_debug ("Agent %p : check already in progress, "
1814 "restarting the timer again?: %s ..", agent
,
1815 p
->timer_restarted
? "no" : "yes");
1816 if (!p
->timer_restarted
) {
1817 stun_timer_start (&p
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
1818 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
1819 p
->timer_restarted
= TRUE
;
1822 else if (p
->state
== NICE_CHECK_SUCCEEDED
||
1823 p
->state
== NICE_CHECK_DISCOVERED
) {
1824 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent
);
1825 /* note: this is a bit unsure corner-case -- let's do the
1826 same state update as for processing responses to our own checks */
1827 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1829 /* note: to take care of the controlling-controlling case in
1830 * aggressive nomination mode, send a new triggered
1831 * check to nominate the pair */
1832 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
1833 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1834 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1835 agent
->controlling_mode
)
1836 priv_conn_check_initiate (agent
, p
);
1837 } else if (p
->state
== NICE_CHECK_FAILED
) {
1838 /* 7.2.1.4 Triggered Checks
1839 * If the state of the pair is Failed, it is changed to Waiting
1840 and the agent MUST create a new connectivity check for that
1841 pair (representing a new STUN Binding request transaction), by
1842 enqueueing the pair in the triggered check queue. */
1843 priv_conn_check_initiate (agent
, p
);
1846 /* note: the spec says the we SHOULD retransmit in-progress
1847 * checks immediately, but we won't do that now */
1853 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1855 if (local
->sockptr
== local_socket
)
1860 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent
, local
);
1861 priv_add_new_check_pair (agent
, stream
->id
, component
, local
, remote_cand
, NICE_CHECK_WAITING
, use_candidate
);
1865 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent
, remote_cand
);
1872 * Sends a reply to an successfully received STUN connectivity
1873 * check request. Implements parts of the ICE spec section 7.2 (STUN
1874 * Server Procedures).
1876 * @param agent context pointer
1877 * @param stream which stream (of the agent)
1878 * @param component which component (of the stream)
1879 * @param rcand remote candidate from which the request came, if NULL,
1880 * the response is sent immediately but no other processing is done
1881 * @param toaddr address to which reply is sent
1882 * @param socket the socket over which the request came
1883 * @param rbuf_len length of STUN message to send
1884 * @param rbuf buffer containing the STUN message to send
1885 * @param use_candidate whether the request had USE_CANDIDATE attribute
1887 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1889 static void priv_reply_to_conn_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*rcand
, const NiceAddress
*toaddr
, NiceSocket
*socket
, size_t rbuf_len
, uint8_t *rbuf
, gboolean use_candidate
)
1891 g_assert (rcand
== NULL
|| nice_address_equal(&rcand
->addr
, toaddr
) == TRUE
);
1894 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1895 nice_address_to_string (toaddr
, tmpbuf
);
1896 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent
,
1898 nice_address_get_port (toaddr
),
1899 g_socket_get_fd(socket
->fileno
),
1901 rcand
, component
->id
,
1902 (int)use_candidate
);
1905 nice_socket_send (socket
, toaddr
, rbuf_len
, (const gchar
*)rbuf
);
1908 /* note: upon successful check, make the reserve check immediately */
1909 priv_schedule_triggered_check (agent
, stream
, component
, socket
, rcand
, use_candidate
);
1912 priv_mark_pair_nominated (agent
, stream
, component
, rcand
);
1917 * Stores information of an incoming STUN connectivity check
1918 * for later use. This is only needed when a check is received
1919 * before we get information about the remote candidates (via
1920 * SDP or other signaling means).
1922 * @return non-zero on error, zero on success
1924 static int priv_store_pending_check (NiceAgent
*agent
, Component
*component
,
1925 const NiceAddress
*from
, NiceSocket
*socket
, uint8_t *username
,
1926 uint16_t username_len
, uint32_t priority
, gboolean use_candidate
)
1928 IncomingCheck
*icheck
;
1929 nice_debug ("Agent %p : Storing pending check.", agent
);
1931 if (component
->incoming_checks
&&
1932 g_slist_length (component
->incoming_checks
) >=
1933 NICE_AGENT_MAX_REMOTE_CANDIDATES
) {
1934 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent
);
1938 icheck
= g_slice_new0 (IncomingCheck
);
1939 component
->incoming_checks
= g_slist_append (component
->incoming_checks
, icheck
);
1940 icheck
->from
= *from
;
1941 icheck
->local_socket
= socket
;
1942 icheck
->priority
= priority
;
1943 icheck
->use_candidate
= use_candidate
;
1944 icheck
->username_len
= username_len
;
1945 icheck
->username
= NULL
;
1946 if (username_len
> 0)
1947 icheck
->username
= g_memdup (username
, username_len
);
1953 * Adds a new pair, discovered from an incoming STUN response, to
1954 * the connectivity check list.
1956 * @return created pair, or NULL on fatal (memory allocation) errors
1958 static CandidateCheckPair
*priv_add_peer_reflexive_pair (NiceAgent
*agent
, guint stream_id
, guint component_id
, NiceCandidate
*local_cand
, CandidateCheckPair
*parent_pair
)
1960 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1961 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1963 stream
->conncheck_list
= g_slist_append (stream
->conncheck_list
, pair
);
1964 pair
->agent
= agent
;
1965 pair
->stream_id
= stream_id
;
1966 pair
->component_id
= component_id
;;
1967 pair
->local
= local_cand
;
1968 pair
->remote
= parent_pair
->remote
;
1969 pair
->state
= NICE_CHECK_DISCOVERED
;
1970 nice_debug ("Agent %p : pair %p state DISCOVERED", agent
, pair
);
1971 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s",
1972 local_cand
->foundation
, parent_pair
->remote
->foundation
);
1973 if (agent
->controlling_mode
== TRUE
)
1974 pair
->priority
= nice_candidate_pair_priority (pair
->local
->priority
,
1975 pair
->remote
->priority
);
1977 pair
->priority
= nice_candidate_pair_priority (pair
->remote
->priority
,
1978 pair
->local
->priority
);
1979 pair
->nominated
= FALSE
;
1980 pair
->controlling
= agent
->controlling_mode
;
1981 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent
, pair
->foundation
);
1987 * Recalculates priorities of all candidate pairs. This
1988 * is required after a conflict in ICE roles.
1990 static void priv_recalculate_pair_priorities (NiceAgent
*agent
)
1994 for (i
= agent
->streams
; i
; i
= i
->next
) {
1995 Stream
*stream
= i
->data
;
1996 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
1997 CandidateCheckPair
*p
= j
->data
;
1998 p
->priority
= agent_candidate_pair_priority (agent
, p
->local
, p
->remote
);
2004 * Change the agent role if different from 'control'. Can be
2005 * initiated both by handling of incoming connectivity checks,
2006 * and by processing the responses to checks sent by us.
2008 static void priv_check_for_role_conflict (NiceAgent
*agent
, gboolean control
)
2010 /* role conflict, change mode; wait for a new conn. check */
2011 if (control
!= agent
->controlling_mode
) {
2012 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent
, control
);
2013 agent
->controlling_mode
= control
;
2014 /* the pair priorities depend on the roles, so recalculation
2016 priv_recalculate_pair_priorities (agent
);
2019 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent
, control
);
2023 * Checks whether the mapped address in connectivity check response
2024 * matches any of the known local candidates. If not, apply the
2025 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
2027 * @param agent context pointer
2028 * @param stream which stream (of the agent)
2029 * @param component which component (of the stream)
2030 * @param p the connectivity check pair for which we got a response
2031 * @param socketptr socket used to send the reply
2032 * @param mapped_sockaddr mapped address in the response
2034 * @return pointer to a new pair if one was created, otherwise NULL
2036 static CandidateCheckPair
*priv_process_response_check_for_peer_reflexive(NiceAgent
*agent
, Stream
*stream
, Component
*component
, CandidateCheckPair
*p
, NiceSocket
*sockptr
, struct sockaddr
*mapped_sockaddr
, NiceCandidate
*local_candidate
, NiceCandidate
*remote_candidate
)
2038 CandidateCheckPair
*new_pair
= NULL
;
2041 gboolean local_cand_matches
= FALSE
;
2043 nice_address_set_from_sockaddr (&mapped
, mapped_sockaddr
);
2045 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
2046 NiceCandidate
*cand
= j
->data
;
2047 if (nice_address_equal (&mapped
, &cand
->addr
)) {
2048 local_cand_matches
= TRUE
;
2053 if (local_cand_matches
== TRUE
) {
2054 /* note: this is same as "adding to VALID LIST" in the spec
2056 p
->state
= NICE_CHECK_SUCCEEDED
;
2057 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent
, p
);
2058 priv_conn_check_unfreeze_related (agent
, stream
, p
);
2061 NiceCandidate
*cand
=
2062 discovery_add_peer_reflexive_candidate (agent
,
2069 p
->state
= NICE_CHECK_FAILED
;
2070 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
2072 /* step: add a new discovered pair (see ICE 7.1.2.2.2
2073 "Constructing a Valid Pair" (ID-19)) */
2074 new_pair
= priv_add_peer_reflexive_pair (agent
, stream
->id
, component
->id
, cand
, p
);
2075 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent
, p
, new_pair
);
2082 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
2083 * check transaction. If found, the reply is processed. Implements
2084 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
2086 * @return TRUE if a matching transaction is found
2088 static gboolean
priv_map_reply_to_conn_check_request (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*sockptr
, const NiceAddress
*from
, NiceCandidate
*local_candidate
, NiceCandidate
*remote_candidate
, StunMessage
*resp
)
2090 struct sockaddr_storage sockaddr
;
2091 socklen_t socklen
= sizeof (sockaddr
);
2093 StunUsageIceReturn res
;
2094 gboolean trans_found
= FALSE
;
2095 StunTransactionId discovery_id
;
2096 StunTransactionId response_id
;
2097 stun_message_id (resp
, response_id
);
2099 for (i
= stream
->conncheck_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2100 CandidateCheckPair
*p
= i
->data
;
2102 if (p
->stun_message
.buffer
) {
2103 stun_message_id (&p
->stun_message
, discovery_id
);
2105 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2106 res
= stun_usage_ice_conncheck_process (resp
,
2107 (struct sockaddr
*) &sockaddr
, &socklen
,
2108 agent_to_ice_compatibility (agent
));
2109 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
2110 "(controlling=%d).", agent
, p
, (int)res
, agent
->controlling_mode
);
2112 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2113 res
== STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS
) {
2114 /* case: found a matching connectivity check request */
2116 CandidateCheckPair
*ok_pair
= NULL
;
2118 nice_debug ("Agent %p : conncheck %p MATCHED.", agent
, p
);
2119 p
->stun_message
.buffer
= NULL
;
2120 p
->stun_message
.buffer_len
= 0;
2122 /* step: verify that response came from the same IP address we
2123 * sent the original request to (see 7.1.2.1. "Failure
2125 if (nice_address_equal (from
, &p
->remote
->addr
) != TRUE
) {
2126 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2127 gchar tmpbuf2
[INET6_ADDRSTRLEN
];
2129 p
->state
= NICE_CHECK_FAILED
;
2130 nice_debug ("Agent %p : conncheck %p FAILED"
2131 " (mismatch of source address).", agent
, p
);
2132 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
2133 nice_address_to_string (from
, tmpbuf2
);
2134 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent
,
2135 tmpbuf
, nice_address_get_port (&p
->remote
->addr
),
2136 tmpbuf2
, nice_address_get_port (from
));
2142 /* note: CONNECTED but not yet READY, see docs */
2144 /* step: handle the possible case of a peer-reflexive
2145 * candidate where the mapped-address in response does
2146 * not match any local candidate, see 7.1.2.2.1
2147 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
2149 if (res
== STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS
) {
2150 /* note: this is same as "adding to VALID LIST" in the spec
2152 p
->state
= NICE_CHECK_SUCCEEDED
;
2153 nice_debug ("Agent %p : Mapped address not found."
2154 " conncheck %p SUCCEEDED.", agent
, p
);
2155 priv_conn_check_unfreeze_related (agent
, stream
, p
);
2157 ok_pair
= priv_process_response_check_for_peer_reflexive(agent
,
2158 stream
, component
, p
, sockptr
, (struct sockaddr
*) &sockaddr
,
2159 local_candidate
, remote_candidate
);
2166 /* Do not step down to CONNECTED if we're already at state READY*/
2167 if (component
->state
!= NICE_COMPONENT_STATE_READY
) {
2168 /* step: notify the client of a new component state (must be done
2169 * before the possible check list state update step */
2170 agent_signal_component_state_change (agent
,
2171 stream
->id
, component
->id
, NICE_COMPONENT_STATE_CONNECTED
);
2175 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
2176 Nominated Flag" (ID-19) */
2177 if (ok_pair
->nominated
== TRUE
)
2178 priv_update_selected_pair (agent
, component
, ok_pair
);
2180 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
2181 states" and 8.1.2 "Updating States", ID-19) */
2182 priv_update_check_list_state_for_ready (agent
, stream
, component
);
2185 } else if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2186 /* case: role conflict error, need to restart with new role */
2187 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent
, p
);
2188 /* note: our role might already have changed due to an
2189 * incoming request, but if not, change role now;
2190 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
2191 priv_check_for_role_conflict (agent
, !p
->controlling
);
2193 p
->stun_message
.buffer
= NULL
;
2194 p
->stun_message
.buffer_len
= 0;
2195 p
->state
= NICE_CHECK_WAITING
;
2196 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
2199 /* case: STUN error, the check STUN context was freed */
2200 nice_debug ("Agent %p : conncheck %p FAILED.", agent
, p
);
2201 p
->stun_message
.buffer
= NULL
;
2202 p
->stun_message
.buffer_len
= 0;
2213 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2214 * transaction. If found, a reply is sent.
2216 * @return TRUE if a matching transaction is found
2218 static gboolean
priv_map_reply_to_discovery_request (NiceAgent
*agent
, StunMessage
*resp
)
2220 struct sockaddr_storage sockaddr
;
2221 socklen_t socklen
= sizeof (sockaddr
);
2222 struct sockaddr_storage alternate
;
2223 socklen_t alternatelen
= sizeof (sockaddr
);
2225 StunUsageBindReturn res
;
2226 gboolean trans_found
= FALSE
;
2227 StunTransactionId discovery_id
;
2228 StunTransactionId response_id
;
2229 stun_message_id (resp
, response_id
);
2231 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2232 CandidateDiscovery
*d
= i
->data
;
2234 if (d
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
&&
2235 d
->stun_message
.buffer
) {
2236 stun_message_id (&d
->stun_message
, discovery_id
);
2238 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2239 res
= stun_usage_bind_process (resp
, (struct sockaddr
*) &sockaddr
,
2240 &socklen
, (struct sockaddr
*) &alternate
, &alternatelen
);
2241 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
2242 agent
, d
, (int)res
);
2244 if (res
== STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER
) {
2245 /* handle alternate server */
2246 NiceAddress niceaddr
;
2247 nice_address_set_from_sockaddr (&niceaddr
,
2248 (struct sockaddr
*) &alternate
);
2249 d
->server
= niceaddr
;
2252 } else if (res
== STUN_USAGE_BIND_RETURN_SUCCESS
) {
2253 /* case: successful binding discovery, create a new local candidate */
2254 NiceAddress niceaddr
;
2255 nice_address_set_from_sockaddr (&niceaddr
,
2256 (struct sockaddr
*) &sockaddr
);
2258 discovery_add_server_reflexive_candidate (
2265 d
->stun_message
.buffer
= NULL
;
2266 d
->stun_message
.buffer_len
= 0;
2269 } else if (res
== STUN_USAGE_BIND_RETURN_ERROR
) {
2270 /* case: STUN error, the check STUN context was freed */
2271 d
->stun_message
.buffer
= NULL
;
2272 d
->stun_message
.buffer_len
= 0;
2284 static CandidateRefresh
*
2285 priv_add_new_turn_refresh (CandidateDiscovery
*cdisco
, NiceCandidate
*relay_cand
,
2288 CandidateRefresh
*cand
;
2289 NiceAgent
*agent
= cdisco
->agent
;
2291 cand
= g_slice_new0 (CandidateRefresh
);
2292 agent
->refresh_list
= g_slist_append (agent
->refresh_list
, cand
);
2294 cand
->nicesock
= cdisco
->nicesock
;
2295 cand
->relay_socket
= relay_cand
->sockptr
;
2296 cand
->server
= cdisco
->server
;
2297 cand
->turn
= cdisco
->turn
;
2298 cand
->stream
= cdisco
->stream
;
2299 cand
->component
= cdisco
->component
;
2300 cand
->agent
= cdisco
->agent
;
2301 memcpy (&cand
->stun_agent
, &cdisco
->stun_agent
, sizeof(StunAgent
));
2303 /* Use previous stun response for authentication credentials */
2304 if (cdisco
->stun_resp_msg
.buffer
!= NULL
) {
2305 memcpy(cand
->stun_resp_buffer
, cdisco
->stun_resp_buffer
,
2306 sizeof(cand
->stun_resp_buffer
));
2307 memcpy(&cand
->stun_resp_msg
, &cdisco
->stun_resp_msg
, sizeof(StunMessage
));
2308 cand
->stun_resp_msg
.buffer
= cand
->stun_resp_buffer
;
2309 cand
->stun_resp_msg
.agent
= NULL
;
2310 cand
->stun_resp_msg
.key
= NULL
;
2313 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2314 agent
, cand
, (lifetime
- 60) * 1000);
2316 /* step: also start the refresh timer */
2317 /* refresh should be sent 1 minute before it expires */
2318 cand
->timer_source
=
2319 agent_timeout_add_with_context (agent
, (lifetime
- 60) * 1000,
2320 priv_turn_allocate_refresh_tick
, cand
);
2322 nice_debug ("timer source is : %d", cand
->timer_source
);
2328 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2329 * transaction. If found, a reply is sent.
2331 * @return TRUE if a matching transaction is found
2333 static gboolean
priv_map_reply_to_relay_request (NiceAgent
*agent
, StunMessage
*resp
)
2335 struct sockaddr_storage sockaddr
;
2336 socklen_t socklen
= sizeof (sockaddr
);
2337 struct sockaddr_storage alternate
;
2338 socklen_t alternatelen
= sizeof (alternate
);
2339 struct sockaddr_storage relayaddr
;
2340 socklen_t relayaddrlen
= sizeof (relayaddr
);
2344 StunUsageTurnReturn res
;
2345 gboolean trans_found
= FALSE
;
2346 StunTransactionId discovery_id
;
2347 StunTransactionId response_id
;
2348 stun_message_id (resp
, response_id
);
2350 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2351 CandidateDiscovery
*d
= i
->data
;
2353 if (d
->type
== NICE_CANDIDATE_TYPE_RELAYED
&&
2354 d
->stun_message
.buffer
) {
2355 stun_message_id (&d
->stun_message
, discovery_id
);
2357 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2358 res
= stun_usage_turn_process (resp
,
2359 (struct sockaddr
*) &relayaddr
, &relayaddrlen
,
2360 (struct sockaddr
*) &sockaddr
, &socklen
,
2361 (struct sockaddr
*) &alternate
, &alternatelen
,
2362 &bandwidth
, &lifetime
, agent_to_turn_compatibility (agent
));
2363 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2364 agent
, d
, (int)res
);
2366 if (res
== STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER
) {
2367 /* handle alternate server */
2368 nice_address_set_from_sockaddr (&d
->server
,
2369 (struct sockaddr
*) &alternate
);
2370 nice_address_set_from_sockaddr (&d
->turn
->server
,
2371 (struct sockaddr
*) &alternate
);
2374 } else if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
||
2375 res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
) {
2376 /* case: successful allocate, create a new local candidate */
2377 NiceAddress niceaddr
;
2378 NiceCandidate
*relay_cand
;
2380 /* Server reflexive candidates are only valid for UDP sockets */
2381 if (res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
&&
2382 !nice_socket_is_reliable (d
->nicesock
)) {
2383 /* We also received our mapped address */
2384 nice_address_set_from_sockaddr (&niceaddr
,
2385 (struct sockaddr
*) &sockaddr
);
2387 discovery_add_server_reflexive_candidate (
2395 nice_address_set_from_sockaddr (&niceaddr
,
2396 (struct sockaddr
*) &relayaddr
);
2397 relay_cand
= discovery_add_relay_candidate (
2406 priv_add_new_turn_refresh (d
, relay_cand
, lifetime
);
2407 if (agent
->compatibility
== NICE_COMPATIBILITY_OC2007
||
2408 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) {
2409 /* These data are needed on TURN socket when sending requests,
2410 * but never reach nice_turn_socket_parse_recv() where it could
2411 * be read directly, as the socket does not exist when allocate
2412 * response arrives to _nice_agent_recv(). We must set them right
2413 * after socket gets created in discovery_add_relay_candidate(),
2414 * so we are doing it here. */
2415 nice_turn_socket_set_ms_realm(relay_cand
->sockptr
, &d
->stun_message
);
2416 nice_turn_socket_set_ms_connection_id(relay_cand
->sockptr
, resp
);
2420 d
->stun_message
.buffer
= NULL
;
2421 d
->stun_message
.buffer_len
= 0;
2424 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2426 uint8_t *sent_realm
= NULL
;
2427 uint8_t *recv_realm
= NULL
;
2428 uint16_t sent_realm_len
= 0;
2429 uint16_t recv_realm_len
= 0;
2431 sent_realm
= (uint8_t *) stun_message_find (&d
->stun_message
,
2432 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2433 recv_realm
= (uint8_t *) stun_message_find (resp
,
2434 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2436 /* check for unauthorized error response */
2437 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
2438 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
||
2439 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
2440 stun_message_get_class (resp
) == STUN_ERROR
&&
2441 stun_message_find_error (resp
, &code
) ==
2442 STUN_MESSAGE_RETURN_SUCCESS
&&
2443 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2447 !(recv_realm_len
== sent_realm_len
&&
2448 sent_realm
!= NULL
&&
2449 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2450 d
->stun_resp_msg
= *resp
;
2451 memcpy (d
->stun_resp_buffer
, resp
->buffer
,
2452 stun_message_length (resp
));
2453 d
->stun_resp_msg
.buffer
= d
->stun_resp_buffer
;
2454 d
->stun_resp_msg
.buffer_len
= sizeof(d
->stun_resp_buffer
);
2457 /* case: a real unauthorized error */
2458 d
->stun_message
.buffer
= NULL
;
2459 d
->stun_message
.buffer_len
= 0;
2463 /* case: STUN error, the check STUN context was freed */
2464 d
->stun_message
.buffer
= NULL
;
2465 d
->stun_message
.buffer_len
= 0;
2479 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2480 * transaction. If found, a reply is sent.
2482 * @return TRUE if a matching transaction is found
2484 static gboolean
priv_map_reply_to_relay_refresh (NiceAgent
*agent
, StunMessage
*resp
)
2488 StunUsageTurnReturn res
;
2489 gboolean trans_found
= FALSE
;
2490 StunTransactionId refresh_id
;
2491 StunTransactionId response_id
;
2492 stun_message_id (resp
, response_id
);
2494 for (i
= agent
->refresh_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2495 CandidateRefresh
*cand
= i
->data
;
2497 if (cand
->stun_message
.buffer
) {
2498 stun_message_id (&cand
->stun_message
, refresh_id
);
2500 if (memcmp (refresh_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2501 res
= stun_usage_turn_refresh_process (resp
,
2502 &lifetime
, agent_to_turn_compatibility (cand
->agent
));
2503 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2504 agent
, cand
, (int)res
);
2505 if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
) {
2506 /* refresh should be sent 1 minute before it expires */
2507 cand
->timer_source
=
2508 agent_timeout_add_with_context (cand
->agent
, (lifetime
- 60) * 1000,
2509 priv_turn_allocate_refresh_tick
, cand
);
2511 g_source_destroy (cand
->tick_source
);
2512 g_source_unref (cand
->tick_source
);
2513 cand
->tick_source
= NULL
;
2514 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2516 uint8_t *sent_realm
= NULL
;
2517 uint8_t *recv_realm
= NULL
;
2518 uint16_t sent_realm_len
= 0;
2519 uint16_t recv_realm_len
= 0;
2521 sent_realm
= (uint8_t *) stun_message_find (&cand
->stun_message
,
2522 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2523 recv_realm
= (uint8_t *) stun_message_find (resp
,
2524 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2526 /* check for unauthorized error response */
2527 if (cand
->agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
&&
2528 stun_message_get_class (resp
) == STUN_ERROR
&&
2529 stun_message_find_error (resp
, &code
) ==
2530 STUN_MESSAGE_RETURN_SUCCESS
&&
2531 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2535 !(recv_realm_len
== sent_realm_len
&&
2536 sent_realm
!= NULL
&&
2537 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2538 cand
->stun_resp_msg
= *resp
;
2539 memcpy (cand
->stun_resp_buffer
, resp
->buffer
,
2540 stun_message_length (resp
));
2541 cand
->stun_resp_msg
.buffer
= cand
->stun_resp_buffer
;
2542 cand
->stun_resp_msg
.buffer_len
= sizeof(cand
->stun_resp_buffer
);
2543 priv_turn_allocate_refresh_tick_unlocked (cand
);
2545 /* case: a real unauthorized error */
2546 refresh_cancel (cand
);
2549 /* case: STUN error, the check STUN context was freed */
2550 refresh_cancel (cand
);
2562 static gboolean
priv_map_reply_to_keepalive_conncheck (NiceAgent
*agent
,
2563 Component
*component
, StunMessage
*resp
)
2565 StunTransactionId conncheck_id
;
2566 StunTransactionId response_id
;
2567 stun_message_id (resp
, response_id
);
2569 if (component
->selected_pair
.keepalive
.stun_message
.buffer
) {
2570 stun_message_id (&component
->selected_pair
.keepalive
.stun_message
,
2572 if (memcmp (conncheck_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2573 nice_debug ("Agent %p : Keepalive for selected pair received.",
2575 if (component
->selected_pair
.keepalive
.tick_source
) {
2576 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
2577 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
2578 component
->selected_pair
.keepalive
.tick_source
= NULL
;
2580 component
->selected_pair
.keepalive
.stun_message
.buffer
= NULL
;
2592 Component
*component
;
2594 } conncheck_validater_data
;
2596 static bool conncheck_stun_validater (StunAgent
*agent
,
2597 StunMessage
*message
, uint8_t *username
, uint16_t username_len
,
2598 uint8_t **password
, size_t *password_len
, void *user_data
)
2600 conncheck_validater_data
*data
= (conncheck_validater_data
*) user_data
;
2602 gchar
*ufrag
= NULL
;
2605 gboolean msn_msoc_nice_compatibility
=
2606 data
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2607 data
->agent
->compatibility
== NICE_COMPATIBILITY_OC2007
;
2609 if (data
->agent
->compatibility
== NICE_COMPATIBILITY_OC2007
&&
2610 stun_message_get_class (message
) == STUN_RESPONSE
)
2611 i
= data
->component
->remote_candidates
;
2613 i
= data
->component
->local_candidates
;
2615 for (; i
; i
= i
->next
) {
2616 NiceCandidate
*cand
= i
->data
;
2620 ufrag
= cand
->username
;
2621 else if (data
->stream
)
2622 ufrag
= data
->stream
->local_ufrag
;
2623 ufrag_len
= ufrag
? strlen (ufrag
) : 0;
2625 if (ufrag
&& msn_msoc_nice_compatibility
)
2626 ufrag
= (gchar
*)g_base64_decode (ufrag
, &ufrag_len
);
2631 stun_debug ("Comparing username '");
2632 stun_debug_bytes (username
, username_len
);
2633 stun_debug ("' (%d) with '", username_len
);
2634 stun_debug_bytes (ufrag
, ufrag_len
);
2635 stun_debug ("' (%d) : %d\n",
2636 ufrag_len
, memcmp (username
, ufrag
, ufrag_len
));
2637 if (ufrag_len
> 0 && username_len
>= ufrag_len
&&
2638 memcmp (username
, ufrag
, ufrag_len
) == 0) {
2642 pass
= cand
->password
;
2643 else if(data
->stream
->local_password
[0])
2644 pass
= data
->stream
->local_password
;
2647 *password
= (uint8_t *) pass
;
2648 *password_len
= strlen (pass
);
2650 if (msn_msoc_nice_compatibility
) {
2651 data
->password
= g_base64_decode (pass
, password_len
);
2652 *password
= data
->password
;
2656 if (msn_msoc_nice_compatibility
)
2659 stun_debug ("Found valid username, returning password: '%s'\n", *password
);
2663 if (msn_msoc_nice_compatibility
)
2672 * Processing an incoming STUN message.
2674 * @param agent self pointer
2675 * @param stream stream the packet is related to
2676 * @param component component the packet is related to
2677 * @param socket socket from which the packet was received
2678 * @param from address of the sender
2679 * @param buf message contents
2680 * @param buf message length
2682 * @pre contents of 'buf' is a STUN message
2684 * @return XXX (what FALSE means exactly?)
2686 gboolean
conn_check_handle_inbound_stun (NiceAgent
*agent
, Stream
*stream
,
2687 Component
*component
, NiceSocket
*socket
, const NiceAddress
*from
,
2688 gchar
*buf
, guint len
)
2690 struct sockaddr_storage sockaddr
;
2691 uint8_t rbuf
[MAX_STUN_DATAGRAM_PAYLOAD
];
2693 size_t rbuf_len
= sizeof (rbuf
);
2694 bool control
= agent
->controlling_mode
;
2695 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
2698 uint16_t username_len
;
2701 StunValidationStatus valid
;
2702 conncheck_validater_data validater_data
= {agent
, stream
, component
, NULL
};
2704 NiceCandidate
*remote_candidate
= NULL
;
2705 NiceCandidate
*remote_candidate2
= NULL
;
2706 NiceCandidate
*local_candidate
= NULL
;
2707 gboolean discovery_msg
= FALSE
;
2709 nice_address_copy_to_sockaddr (from
, (struct sockaddr
*) &sockaddr
);
2711 /* note: contents of 'buf' already validated, so it is
2712 * a valid and fully received STUN message */
2716 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2717 nice_address_to_string (from
, tmpbuf
);
2718 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2719 agent
, stream
->id
, component
->id
, tmpbuf
, nice_address_get_port (from
), len
);
2723 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2725 valid
= stun_agent_validate (&agent
->stun_agent
, &req
,
2726 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2728 /* Check for discovery candidates stun agents */
2729 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2730 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2731 for (i
= agent
->discovery_list
; i
; i
= i
->next
) {
2732 CandidateDiscovery
*d
= i
->data
;
2733 if (d
->stream
== stream
&& d
->component
== component
&&
2734 d
->nicesock
== socket
) {
2735 valid
= stun_agent_validate (&d
->stun_agent
, &req
,
2736 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2738 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2741 discovery_msg
= TRUE
;
2746 /* Check for relay refresh stun agents */
2747 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2748 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2749 for (i
= agent
->refresh_list
; i
; i
= i
->next
) {
2750 CandidateRefresh
*r
= i
->data
;
2751 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r
->stream
,
2752 stream
, r
->component
, component
, r
->nicesock
, r
->relay_socket
, socket
);
2753 if (r
->stream
== stream
&& r
->component
== component
&&
2754 (r
->nicesock
== socket
|| r
->relay_socket
== socket
)) {
2755 valid
= stun_agent_validate (&r
->stun_agent
, &req
,
2756 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2757 nice_debug ("Validating gave %d", valid
);
2758 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2760 discovery_msg
= TRUE
;
2766 g_free (validater_data
.password
);
2768 if (valid
== STUN_VALIDATION_NOT_STUN
||
2769 valid
== STUN_VALIDATION_INCOMPLETE_STUN
||
2770 valid
== STUN_VALIDATION_BAD_REQUEST
)
2772 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2777 if (valid
== STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
) {
2778 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent
);
2780 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2781 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
) {
2782 rbuf_len
= stun_agent_build_unknown_attributes_error (&agent
->stun_agent
,
2783 &msg
, rbuf
, rbuf_len
, &req
);
2785 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2790 if (valid
== STUN_VALIDATION_UNAUTHORIZED
) {
2791 nice_debug ("Agent %p : Integrity check failed.", agent
);
2793 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2794 &req
, STUN_ERROR_UNAUTHORIZED
)) {
2795 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2796 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2797 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
)
2798 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2802 if (valid
== STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST
) {
2803 nice_debug ("Agent %p : Integrity check failed.", agent
);
2804 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2805 &req
, STUN_ERROR_BAD_REQUEST
)) {
2806 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2807 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2808 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
)
2809 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2814 username
= (uint8_t *) stun_message_find (&req
, STUN_ATTRIBUTE_USERNAME
,
2817 for (i
= component
->remote_candidates
; i
; i
= i
->next
) {
2818 NiceCandidate
*cand
= i
->data
;
2819 if (nice_address_equal (from
, &cand
->addr
)) {
2820 remote_candidate
= cand
;
2825 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2826 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2827 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2828 /* We need to find which local candidate was used */
2829 for (i
= component
->remote_candidates
;
2830 i
!= NULL
&& remote_candidate2
== NULL
; i
= i
->next
) {
2831 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
2832 gboolean inbound
= TRUE
;
2833 NiceCandidate
*rcand
= i
->data
;
2834 NiceCandidate
*lcand
= j
->data
;
2836 /* If we receive a response, then the username is local:remote */
2837 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2838 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
) {
2839 if (stun_message_get_class (&req
) == STUN_REQUEST
||
2840 stun_message_get_class (&req
) == STUN_INDICATION
) {
2847 uname_len
= priv_create_username (agent
, stream
,
2848 component
->id
, rcand
, lcand
,
2849 uname
, sizeof (uname
), inbound
);
2851 stun_debug ("Comparing username '");
2852 stun_debug_bytes (username
, username
? username_len
: 0);
2853 stun_debug ("' (%d) with '", username_len
);
2854 stun_debug_bytes (uname
, uname_len
);
2855 stun_debug ("' (%d) : %d\n",
2856 uname_len
, username
&& uname_len
== username_len
&&
2857 memcmp (username
, uname
, uname_len
) == 0);
2860 uname_len
== username_len
&&
2861 memcmp (uname
, username
, username_len
) == 0) {
2862 local_candidate
= lcand
;
2863 remote_candidate2
= rcand
;
2870 if (component
->remote_candidates
&&
2871 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
2872 local_candidate
== NULL
&&
2873 discovery_msg
== FALSE
) {
2874 /* if we couldn't match the username and the stun agent has
2875 IGNORE_CREDENTIALS then we have an integrity check failing.
2876 This could happen with the race condition of receiving connchecks
2877 before the remote candidates are added. Just drop the message, and let
2878 the retransmissions make it work. */
2879 nice_debug ("Agent %p : Username check failed.", agent
);
2883 if (valid
!= STUN_VALIDATION_SUCCESS
) {
2884 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent
, valid
);
2889 if (stun_message_get_class (&req
) == STUN_REQUEST
) {
2890 if ( agent
->compatibility
== NICE_COMPATIBILITY_MSN
2891 || agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2892 if (local_candidate
&& remote_candidate2
) {
2893 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2894 username
= (uint8_t *) stun_message_find (&req
,
2895 STUN_ATTRIBUTE_USERNAME
, &username_len
);
2896 uname_len
= priv_create_username (agent
, stream
,
2897 component
->id
, remote_candidate2
, local_candidate
,
2898 uname
, sizeof (uname
), FALSE
);
2899 memcpy (username
, uname
, username_len
);
2901 req
.key
= g_base64_decode ((gchar
*) remote_candidate2
->password
,
2903 } else if (agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2904 req
.key
= g_base64_decode ((gchar
*) local_candidate
->password
,
2908 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
2909 "Ignoring request", agent
);
2914 rbuf_len
= sizeof (rbuf
);
2915 res
= stun_usage_ice_conncheck_create_reply (&agent
->stun_agent
, &req
,
2916 &msg
, rbuf
, &rbuf_len
, (struct sockaddr
*) &sockaddr
, sizeof (sockaddr
),
2917 &control
, agent
->tie_breaker
,
2918 agent_to_ice_compatibility (agent
));
2920 if ( agent
->compatibility
== NICE_COMPATIBILITY_MSN
2921 || agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2925 if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
)
2926 priv_check_for_role_conflict (agent
, control
);
2928 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2929 res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2930 /* case 1: valid incoming request, send a reply/error */
2931 bool use_candidate
=
2932 stun_usage_ice_conncheck_use_candidate (&req
);
2933 uint32_t priority
= stun_usage_ice_conncheck_priority (&req
);
2935 if (agent
->controlling_mode
||
2936 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2937 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2938 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
)
2939 use_candidate
= TRUE
;
2941 if (stream
->initial_binding_request_received
!= TRUE
)
2942 agent_signal_initial_binding_request_received (agent
, stream
);
2944 if (component
->remote_candidates
&& remote_candidate
== NULL
) {
2945 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2946 "peer-reflexive candidate.", agent
);
2947 remote_candidate
= discovery_learn_remote_peer_reflexive_candidate (
2948 agent
, stream
, component
, priority
, from
, socket
,
2950 remote_candidate2
? remote_candidate2
: remote_candidate
);
2951 if(remote_candidate
)
2952 conn_check_add_for_candidate (agent
, stream
->id
, component
, remote_candidate
);
2955 priv_reply_to_conn_check (agent
, stream
, component
, remote_candidate
,
2956 from
, socket
, rbuf_len
, rbuf
, use_candidate
);
2958 if (component
->remote_candidates
== NULL
) {
2959 /* case: We've got a valid binding request to a local candidate
2960 * but we do not yet know remote credentials nor
2961 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2962 * immediately but postpone all other processing until
2963 * we get information about the remote candidates */
2965 /* step: send a reply immediately but postpone other processing */
2966 priv_store_pending_check (agent
, component
, from
, socket
,
2967 username
, username_len
, priority
, use_candidate
);
2970 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2971 agent
, strerror(errno
));
2975 /* case 2: not a new request, might be a reply... */
2976 gboolean trans_found
= FALSE
;
2978 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2980 /* step: let's try to match the response to an existing check context */
2981 if (trans_found
!= TRUE
)
2982 trans_found
= priv_map_reply_to_conn_check_request (agent
, stream
,
2983 component
, socket
, from
, local_candidate
, remote_candidate
, &req
);
2985 /* step: let's try to match the response to an existing discovery */
2986 if (trans_found
!= TRUE
)
2987 trans_found
= priv_map_reply_to_discovery_request (agent
, &req
);
2989 /* step: let's try to match the response to an existing turn allocate */
2990 if (trans_found
!= TRUE
)
2991 trans_found
= priv_map_reply_to_relay_request (agent
, &req
);
2993 /* step: let's try to match the response to an existing turn refresh */
2994 if (trans_found
!= TRUE
)
2995 trans_found
= priv_map_reply_to_relay_refresh (agent
, &req
);
2997 /* step: let's try to match the response to an existing keepalive conncheck */
2998 if (trans_found
!= TRUE
)
2999 trans_found
= priv_map_reply_to_keepalive_conncheck (agent
, component
,
3002 if (trans_found
!= TRUE
)
3003 nice_debug ("Agent %p : Unable to match to an existing transaction, "
3004 "probably a keepalive.", agent
);