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 ((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
;
946 for (i
= agent
->streams
; i
; i
= i
->next
) {
947 Stream
*stream
= i
->data
;
948 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
949 CandidateCheckPair
*pair
= j
->data
;
950 Component
*component
= stream_find_component_by_id (stream
, pair
->component_id
);
951 gboolean match
= FALSE
;
953 /* performn delayed processing of spec steps section 7.2.1.4,
954 and section 7.2.1.5 */
955 priv_preprocess_conn_check_pending_data (agent
, stream
, component
, pair
);
957 for (k
= component
->incoming_checks
; k
; k
= k
->next
) {
958 IncomingCheck
*icheck
= k
->data
;
959 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
960 * be handled separately */
961 for (l
= component
->remote_candidates
; l
; l
= l
->next
) {
962 NiceCandidate
*cand
= l
->data
;
963 if (nice_address_equal (&icheck
->from
, &cand
->addr
)) {
969 /* note: we have gotten an incoming connectivity check from
970 * an address that is not a known remote candidate */
972 NiceCandidate
*local_candidate
= NULL
;
973 NiceCandidate
*remote_candidate
= NULL
;
975 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
976 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
977 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
978 /* We need to find which local candidate was used */
979 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
982 nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
983 "stored pending check", agent
);
985 for (m
= component
->remote_candidates
;
986 m
!= NULL
&& remote_candidate
== NULL
; m
= m
->next
) {
987 for (n
= component
->local_candidates
; n
; n
= n
->next
) {
988 NiceCandidate
*rcand
= m
->data
;
989 NiceCandidate
*lcand
= n
->data
;
991 uname_len
= priv_create_username (agent
, stream
,
992 component
->id
, rcand
, lcand
,
993 uname
, sizeof (uname
), TRUE
);
995 stun_debug ("pending check, comparing username '");
996 stun_debug_bytes (icheck
->username
,
997 icheck
->username
? icheck
->username_len
: 0);
998 stun_debug ("' (%d) with '", icheck
->username_len
);
999 stun_debug_bytes (uname
, uname_len
);
1000 stun_debug ("' (%d) : %d\n",
1001 uname_len
, icheck
->username
&&
1002 uname_len
== icheck
->username_len
&&
1003 memcmp (icheck
->username
, uname
, uname_len
) == 0);
1005 if (icheck
->username
&&
1006 uname_len
== icheck
->username_len
&&
1007 memcmp (uname
, icheck
->username
, icheck
->username_len
) == 0) {
1008 local_candidate
= lcand
;
1009 remote_candidate
= rcand
;
1016 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1017 local_candidate
== NULL
) {
1018 /* if we couldn't match the username, then the matching remote
1019 * candidate hasn't been received yet.. we must wait */
1020 nice_debug ("Agent %p : Username check failed. pending check has "
1021 "to wait to be processed", agent
);
1023 NiceCandidate
*candidate
=
1024 discovery_learn_remote_peer_reflexive_candidate (agent
,
1029 icheck
->local_socket
,
1030 local_candidate
, remote_candidate
);
1032 conn_check_add_for_candidate (agent
, stream
->id
, component
, candidate
);
1034 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, candidate
, icheck
->use_candidate
);
1040 /* Once we process the pending checks, we should free them to avoid
1041 * reprocessing them again if a dribble-mode set_remote_candidates
1043 for (m
= component
->incoming_checks
; m
; m
= m
->next
) {
1044 IncomingCheck
*icheck
= m
->data
;
1045 g_free (icheck
->username
);
1046 g_slice_free (IncomingCheck
, icheck
);
1048 g_slist_free (component
->incoming_checks
);
1049 component
->incoming_checks
= NULL
;
1055 * Enforces the upper limit for connectivity checks as described
1056 * in ICE spec section 5.7.3 (ID-19). See also
1057 * conn_check_add_for_candidate().
1059 static GSList
*priv_limit_conn_check_list_size (GSList
*conncheck_list
, guint upper_limit
)
1061 guint list_len
= g_slist_length (conncheck_list
);
1063 GSList
*result
= conncheck_list
;
1065 if (list_len
> upper_limit
) {
1066 nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
1067 "Maximum connchecks allowed : %d", list_len
, upper_limit
);
1068 c
= list_len
- upper_limit
;
1069 if (c
== list_len
) {
1070 /* case: delete whole list */
1071 g_slist_foreach (conncheck_list
, conn_check_free_item
, NULL
);
1072 g_slist_free (conncheck_list
),
1076 /* case: remove 'c' items from list end (lowest priority) */
1080 i
= g_slist_nth (conncheck_list
, c
- 1);
1086 /* delete the rest of the connectivity check list */
1087 g_slist_foreach (tmp
, conn_check_free_item
, NULL
);
1097 * Changes the selected pair for the component if 'pair' is nominated
1098 * and has higher priority than the currently selected pair. See
1099 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
1101 static gboolean
priv_update_selected_pair (NiceAgent
*agent
, Component
*component
, CandidateCheckPair
*pair
)
1103 g_assert (component
);
1105 if (pair
->priority
> component
->selected_pair
.priority
) {
1106 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1107 "(prio:%" G_GUINT64_FORMAT
").", agent
, component
->id
, pair
->local
->foundation
,
1108 pair
->remote
->foundation
, pair
->priority
);
1110 if (component
->selected_pair
.keepalive
.tick_source
!= NULL
) {
1111 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
1112 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
1113 component
->selected_pair
.keepalive
.tick_source
= NULL
;
1116 memset (&component
->selected_pair
, 0, sizeof(CandidatePair
));
1117 component
->selected_pair
.local
= pair
->local
;
1118 component
->selected_pair
.remote
= pair
->remote
;
1119 component
->selected_pair
.priority
= pair
->priority
;
1121 priv_conn_keepalive_tick_unlocked (agent
);
1123 agent_signal_new_selected_pair (agent
, pair
->stream_id
, component
->id
, pair
->local
->foundation
, pair
->remote
->foundation
);
1131 * Updates the check list state.
1133 * Implements parts of the algorithm described in
1134 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
1135 * component, all checks have been completed and have
1136 * failed, mark that component's state to NICE_CHECK_FAILED.
1138 * Sends a component state changesignal via 'agent'.
1140 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
)
1143 /* note: emitting a signal might cause the client
1144 * to remove the stream, thus the component count
1145 * must be fetched before entering the loop*/
1146 guint c
, components
= stream
->n_components
;
1148 /* note: iterate the conncheck list for each component separately */
1149 for (c
= 0; c
< components
; c
++) {
1150 Component
*comp
= NULL
;
1151 if (!agent_find_component (agent
, stream
->id
, c
+1, NULL
, &comp
))
1154 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1155 CandidateCheckPair
*p
= i
->data
;
1157 if (p
->stream_id
== stream
->id
&&
1158 p
->component_id
== (c
+ 1)) {
1159 if (p
->state
!= NICE_CHECK_FAILED
)
1164 /* note: all checks have failed
1165 * Set the component to FAILED only if it actually had remote candidates
1167 if (i
== NULL
&& comp
!= NULL
&& comp
->remote_candidates
!= NULL
)
1168 agent_signal_component_state_change (agent
,
1170 (c
+ 1), /* component-id */
1171 NICE_COMPONENT_STATE_FAILED
);
1176 * Updates the check list state for a stream component.
1178 * Implements the algorithm described in ICE sect 8.1.2
1179 * "Updating States" (ID-19) as it applies to checks of
1180 * a certain component. If there are any nominated pairs,
1181 * ICE processing may be concluded, and component state is
1184 * Sends a component state changesignal via 'agent'.
1186 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
)
1189 guint succeeded
= 0, nominated
= 0;
1191 g_assert (component
);
1193 /* step: search for at least one nominated pair */
1194 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1195 CandidateCheckPair
*p
= i
->data
;
1196 if (p
->component_id
== component
->id
) {
1197 if (p
->state
== NICE_CHECK_SUCCEEDED
||
1198 p
->state
== NICE_CHECK_DISCOVERED
) {
1200 if (p
->nominated
== TRUE
) {
1207 if (nominated
> 0) {
1208 /* Only go to READY if no checks are left in progress. If there are
1209 * any that are kept, then this function will be called again when the
1210 * conncheck tick timer finishes them all */
1211 if (priv_prune_pending_checks (stream
, component
->id
) == 0) {
1212 agent_signal_component_state_change (agent
, stream
->id
,
1213 component
->id
, NICE_COMPONENT_STATE_READY
);
1216 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent
, nominated
, succeeded
, component
->id
);
1220 * The remote party has signalled that the candidate pair
1221 * described by 'component' and 'remotecand' is nominated
1224 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
)
1228 g_assert (component
);
1230 /* step: search for at least one nominated pair */
1231 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1232 CandidateCheckPair
*pair
= i
->data
;
1233 /* XXX: hmm, how to figure out to which local candidate the
1234 * check was sent to? let's mark all matching pairs
1235 * as nominated instead */
1236 if (pair
->remote
== remotecand
) {
1237 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent
, pair
, pair
->foundation
);
1238 pair
->nominated
= TRUE
;
1239 if (pair
->state
== NICE_CHECK_SUCCEEDED
||
1240 pair
->state
== NICE_CHECK_DISCOVERED
)
1241 priv_update_selected_pair (agent
, component
, pair
);
1242 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1248 * Creates a new connectivity check pair and adds it to
1249 * the agent's list of checks.
1251 static void priv_add_new_check_pair (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*local
, NiceCandidate
*remote
, NiceCheckState initial_state
, gboolean use_candidate
)
1253 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1254 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1256 stream
->conncheck_list
= g_slist_insert_sorted (stream
->conncheck_list
, pair
,
1257 (GCompareFunc
)conn_check_compare
);
1259 pair
->agent
= agent
;
1260 pair
->stream_id
= stream_id
;
1261 pair
->component_id
= component
->id
;;
1262 pair
->local
= local
;
1263 pair
->remote
= remote
;
1264 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s", local
->foundation
, remote
->foundation
);
1266 pair
->priority
= agent_candidate_pair_priority (agent
, local
, remote
);
1267 pair
->state
= initial_state
;
1268 nice_debug ("Agent %p : creating new pair %p state %d", agent
, pair
, initial_state
);
1269 pair
->nominated
= use_candidate
;
1270 pair
->controlling
= agent
->controlling_mode
;
1272 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent
, pair
, pair
->foundation
, stream_id
);
1274 /* implement the hard upper limit for number of
1275 checks (see sect 5.7.3 ICE ID-19): */
1276 if (agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
) {
1277 stream
->conncheck_list
=
1278 priv_limit_conn_check_list_size (stream
->conncheck_list
, agent
->max_conn_checks
);
1283 * Forms new candidate pairs by matching the new remote candidate
1284 * 'remote_cand' with all existing local candidates of 'component'.
1285 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1288 * @param agent context
1289 * @param component pointer to the component
1290 * @param remote remote candidate to match with
1292 * @return number of checks added, negative on fatal errors
1294 int conn_check_add_for_candidate (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*remote
)
1299 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1301 NiceCandidate
*local
= i
->data
;
1303 /* note: match pairs only if transport and address family are the same */
1304 if (local
->transport
== remote
->transport
&&
1305 local
->addr
.s
.addr
.sa_family
== remote
->addr
.s
.addr
.sa_family
) {
1307 /* note: do not create pairs where local candidate is
1308 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1309 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
1310 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1311 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1312 local
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
)
1315 priv_add_new_check_pair (agent
, stream_id
, component
, local
, remote
, NICE_CHECK_FROZEN
, FALSE
);
1317 if (component
->state
< NICE_COMPONENT_STATE_CONNECTED
) {
1318 agent_signal_component_state_change (agent
,
1321 NICE_COMPONENT_STATE_CONNECTING
);
1323 agent_signal_component_state_change (agent
,
1326 NICE_COMPONENT_STATE_CONNECTED
);
1335 * Frees the CandidateCheckPair structure pointer to
1336 * by 'user data'. Compatible with g_slist_foreach().
1338 void conn_check_free_item (gpointer data
, gpointer user_data
)
1340 CandidateCheckPair
*pair
= data
;
1341 g_assert (user_data
== NULL
);
1342 pair
->stun_message
.buffer
= NULL
;
1343 pair
->stun_message
.buffer_len
= 0;
1344 g_slice_free (CandidateCheckPair
, pair
);
1348 * Frees all resources of all connectivity checks.
1350 void conn_check_free (NiceAgent
*agent
)
1353 for (i
= agent
->streams
; i
; i
= i
->next
) {
1354 Stream
*stream
= i
->data
;
1356 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent
, stream
);
1357 if (stream
->conncheck_list
) {
1358 g_slist_foreach (stream
->conncheck_list
, conn_check_free_item
, NULL
);
1359 g_slist_free (stream
->conncheck_list
),
1360 stream
->conncheck_list
= NULL
;
1364 if (agent
->conncheck_timer_source
!= NULL
) {
1365 g_source_destroy (agent
->conncheck_timer_source
);
1366 g_source_unref (agent
->conncheck_timer_source
);
1367 agent
->conncheck_timer_source
= NULL
;
1372 * Prunes the list of connectivity checks for items related
1373 * to stream 'stream_id'.
1375 * @return TRUE on success, FALSE on a fatal error
1377 gboolean
conn_check_prune_stream (NiceAgent
*agent
, Stream
*stream
)
1379 CandidateCheckPair
*pair
;
1382 for (i
= stream
->conncheck_list
; i
; ) {
1383 GSList
*next
= i
->next
;
1386 g_assert (pair
->stream_id
== stream
->id
);
1388 stream
->conncheck_list
=
1389 g_slist_remove (stream
->conncheck_list
, pair
);
1390 conn_check_free_item (pair
, NULL
);
1392 if (!stream
->conncheck_list
)
1396 if (!stream
->conncheck_list
)
1397 conn_check_free (agent
);
1399 /* return FALSE if there was a memory allocation failure */
1400 if (stream
->conncheck_list
== NULL
&& i
!= NULL
)
1407 * Fills 'dest' with a username string for use in an outbound connectivity
1408 * checks. No more than 'dest_len' characters (including terminating
1409 * NULL) is ever written to the 'dest'.
1412 size_t priv_gen_username (NiceAgent
*agent
, guint component_id
,
1413 gchar
*remote
, gchar
*local
, uint8_t *dest
, guint dest_len
)
1416 gsize remote_len
= strlen (remote
);
1417 gsize local_len
= strlen (local
);
1419 if (remote_len
> 0 && local_len
> 0) {
1420 if (agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
&&
1421 dest_len
>= remote_len
+ local_len
+ 1) {
1422 memcpy (dest
, remote
, remote_len
);
1424 memcpy (dest
+ len
, ":", 1);
1426 memcpy (dest
+ len
, local
, local_len
);
1428 } else if ((agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1429 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1430 dest_len
>= remote_len
+ local_len
+ 4 ) {
1431 memcpy (dest
, remote
, remote_len
);
1433 memcpy (dest
+ len
, ":", 1);
1435 memcpy (dest
+ len
, local
, local_len
);
1438 memset (dest
+ len
, 0, 4 - (len
% 4));
1439 len
+= 4 - (len
% 4);
1441 } else if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1442 dest_len
>= remote_len
+ local_len
) {
1443 memcpy (dest
, remote
, remote_len
);
1445 memcpy (dest
+ len
, local
, local_len
);
1447 } else if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1448 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1449 gchar component_str
[10];
1450 guchar
*local_decoded
= NULL
;
1451 guchar
*remote_decoded
= NULL
;
1452 gsize local_decoded_len
;
1453 gsize remote_decoded_len
;
1457 g_snprintf (component_str
, sizeof(component_str
), "%d", component_id
);
1458 local_decoded
= g_base64_decode (local
, &local_decoded_len
);
1459 remote_decoded
= g_base64_decode (remote
, &remote_decoded_len
);
1461 total_len
= remote_decoded_len
+ local_decoded_len
+ 3 + 2*strlen (component_str
);
1462 padding
= 4 - (total_len
% 4);
1464 if (dest_len
>= total_len
+ padding
) {
1465 guchar pad_char
[1] = {0};
1468 memcpy (dest
, remote_decoded
, remote_decoded_len
);
1469 len
+= remote_decoded_len
;
1470 memcpy (dest
+ len
, ":", 1);
1472 memcpy (dest
+ len
, component_str
, strlen (component_str
));
1473 len
+= strlen (component_str
);
1475 memcpy (dest
+ len
, ":", 1);
1478 memcpy (dest
+ len
, local_decoded
, local_decoded_len
);
1479 len
+= local_decoded_len
;
1480 memcpy (dest
+ len
, ":", 1);
1482 memcpy (dest
+ len
, component_str
, strlen (component_str
));;
1483 len
+= strlen (component_str
);
1485 for (i
= 0; i
< padding
; i
++) {
1486 memcpy (dest
+ len
, pad_char
, 1);
1492 g_free (local_decoded
);
1493 g_free (remote_decoded
);
1501 * Fills 'dest' with a username string for use in an outbound connectivity
1502 * checks. No more than 'dest_len' characters (including terminating
1503 * NULL) is ever written to the 'dest'.
1506 size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
1507 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
1508 uint8_t *dest
, guint dest_len
, gboolean inbound
)
1510 gchar
*local_username
= NULL
;
1511 gchar
*remote_username
= NULL
;
1514 if (remote
&& remote
->username
) {
1515 remote_username
= remote
->username
;
1518 if (local
&& local
->username
) {
1519 local_username
= local
->username
;
1523 if (remote_username
== NULL
) {
1524 remote_username
= stream
->remote_ufrag
;
1526 if (local_username
== NULL
) {
1527 local_username
= stream
->local_ufrag
;
1531 if (local_username
&& remote_username
) {
1533 return priv_gen_username (agent
, component_id
,
1534 local_username
, remote_username
, dest
, dest_len
);
1536 return priv_gen_username (agent
, component_id
,
1537 remote_username
, local_username
, dest
, dest_len
);
1545 * Returns a password string for use in an outbound connectivity
1549 size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
1550 NiceCandidate
*remote
, uint8_t **password
)
1552 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
)
1555 if (remote
&& remote
->password
) {
1556 *password
= (uint8_t *)remote
->password
;
1557 return strlen (remote
->password
);
1561 *password
= (uint8_t *)stream
->remote_password
;
1562 return strlen (stream
->remote_password
);
1569 * Sends a connectivity check over candidate pair 'pair'.
1571 * @return zero on success, non-zero on error
1573 int conn_check_send (NiceAgent
*agent
, CandidateCheckPair
*pair
)
1576 /* note: following information is supplied:
1577 * - username (for USERNAME attribute)
1578 * - password (for MESSAGE-INTEGRITY)
1579 * - priority (for PRIORITY)
1580 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1581 * - USE-CANDIDATE (if sent by the controlling agent)
1585 nice_candidate_ice_priority_full (
1586 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
,
1588 pair
->local
->component_id
);
1590 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
1592 priv_create_username (agent
, agent_find_stream (agent
, pair
->stream_id
),
1593 pair
->component_id
, pair
->remote
, pair
->local
, uname
, sizeof (uname
), FALSE
);
1594 uint8_t *password
= NULL
;
1595 size_t password_len
= priv_get_password (agent
,
1596 agent_find_stream (agent
, pair
->stream_id
), pair
->remote
, &password
);
1598 bool controlling
= agent
->controlling_mode
;
1599 /* XXX: add API to support different nomination modes: */
1600 bool cand_use
= controlling
;
1602 unsigned int timeout
;
1604 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1605 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1606 password
= g_base64_decode ((gchar
*) password
, &password_len
);
1610 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1611 nice_address_to_string (&pair
->remote
->addr
, tmpbuf
);
1612 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
,
1614 nice_address_get_port (&pair
->remote
->addr
),
1615 ((NiceSocket
*)pair
->local
->sockptr
)->fileno
,
1616 pair
->foundation
, pair
->component_id
,
1617 (unsigned long long)agent
->tie_breaker
,
1618 uname
, uname_len
, password
, password_len
, priority
);
1623 pair
->nominated
= controlling
;
1625 if (uname_len
> 0) {
1627 buffer_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
1628 &pair
->stun_message
, pair
->stun_buffer
, sizeof(pair
->stun_buffer
),
1629 uname
, uname_len
, password
, password_len
,
1630 cand_use
, controlling
, priority
,
1633 agent_to_ice_compatibility (agent
));
1635 nice_debug ("Agent %p: conncheck created %d - %p", agent
, buffer_len
, pair
->stun_message
.buffer
);
1637 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1638 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1642 if (buffer_len
> 0) {
1643 stun_timer_start (&pair
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
1644 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
1646 /* send the conncheck */
1647 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
1648 buffer_len
, (gchar
*)pair
->stun_buffer
);
1650 timeout
= stun_timer_remainder (&pair
->timer
);
1651 /* note: convert from milli to microseconds for g_time_val_add() */
1652 g_get_current_time (&pair
->next_tick
);
1653 g_time_val_add (&pair
->next_tick
, timeout
* 1000);
1655 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent
);
1656 pair
->stun_message
.buffer
= NULL
;
1657 pair
->stun_message
.buffer_len
= 0;
1661 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent
);
1662 pair
->stun_message
.buffer
= NULL
;
1663 pair
->stun_message
.buffer_len
= 0;
1671 * Implemented the pruning steps described in ICE sect 8.1.2
1672 * "Updating States" (ID-19) after a pair has been nominated.
1674 * @see priv_update_check_list_state_failed_components()
1676 static guint
priv_prune_pending_checks (Stream
*stream
, guint component_id
)
1679 guint64 highest_nominated_priority
= 0;
1680 guint in_progress
= 0;
1682 nice_debug ("Finding highest priority for component %d", component_id
);
1684 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1685 CandidateCheckPair
*p
= i
->data
;
1686 if (p
->component_id
== component_id
&&
1687 (p
->state
== NICE_CHECK_SUCCEEDED
||
1688 p
->state
== NICE_CHECK_DISCOVERED
) &&
1689 p
->nominated
== TRUE
){
1690 if (p
->priority
> highest_nominated_priority
) {
1691 highest_nominated_priority
= p
->priority
;
1696 nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
1697 "is %" G_GUINT64_FORMAT
, highest_nominated_priority
);
1699 /* step: cancel all FROZEN and WAITING pairs for the component */
1700 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1701 CandidateCheckPair
*p
= i
->data
;
1702 if (p
->component_id
== component_id
) {
1703 if (p
->state
== NICE_CHECK_FROZEN
||
1704 p
->state
== NICE_CHECK_WAITING
) {
1705 p
->state
= NICE_CHECK_CANCELLED
;
1706 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1709 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1710 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1711 if (highest_nominated_priority
!= 0 &&
1712 p
->priority
< highest_nominated_priority
) {
1713 p
->stun_message
.buffer
= NULL
;
1714 p
->stun_message
.buffer_len
= 0;
1715 p
->state
= NICE_CHECK_CANCELLED
;
1716 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1718 /* We must keep the higher priority pairs running because if a udp
1719 * packet was lost, we might end up using a bad candidate */
1720 nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %"
1721 G_GUINT64_FORMAT
" is higher than currently nominated pair %"
1722 G_GUINT64_FORMAT
, p
, p
->priority
, highest_nominated_priority
);
1733 * Schedules a triggered check after a successfully inbound
1734 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1736 * @param agent self pointer
1737 * @param component the check is related to
1738 * @param local_socket socket from which the inbound check was received
1739 * @param remote_cand remote candidate from which the inbound check was sent
1740 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1742 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
)
1745 NiceCandidate
*local
= NULL
;
1747 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1748 CandidateCheckPair
*p
= i
->data
;
1749 if (p
->component_id
== component
->id
&&
1750 p
->remote
== remote_cand
&&
1751 p
->local
->sockptr
== local_socket
) {
1753 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent
, p
);
1755 if (p
->state
== NICE_CHECK_WAITING
||
1756 p
->state
== NICE_CHECK_FROZEN
)
1757 priv_conn_check_initiate (agent
, p
);
1758 else if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1759 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1760 * we should cancel the existing one, instead we reset our timer, so
1761 * we'll resend the exiting transactions faster if needed...? :P
1763 nice_debug ("Agent %p : check already in progress, "
1764 "restarting the timer again?: %s ..", agent
,
1765 p
->timer_restarted
? "no" : "yes");
1766 if (!p
->timer_restarted
) {
1767 stun_timer_start (&p
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
1768 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
1769 p
->timer_restarted
= TRUE
;
1772 else if (p
->state
== NICE_CHECK_SUCCEEDED
||
1773 p
->state
== NICE_CHECK_DISCOVERED
) {
1774 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent
);
1775 /* note: this is a bit unsure corner-case -- let's do the
1776 same state update as for processing responses to our own checks */
1777 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1779 /* note: to take care of the controlling-controlling case in
1780 * aggressive nomination mode, send a new triggered
1781 * check to nominate the pair */
1782 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
1783 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1784 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1785 agent
->controlling_mode
)
1786 priv_conn_check_initiate (agent
, p
);
1789 /* note: the spec says the we SHOULD retransmit in-progress
1790 * checks immediately, but we won't do that now */
1796 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1798 if (local
->sockptr
== local_socket
)
1803 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent
, local
);
1804 priv_add_new_check_pair (agent
, stream
->id
, component
, local
, remote_cand
, NICE_CHECK_WAITING
, use_candidate
);
1808 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent
, remote_cand
);
1815 * Sends a reply to an successfully received STUN connectivity
1816 * check request. Implements parts of the ICE spec section 7.2 (STUN
1817 * Server Procedures).
1819 * @param agent context pointer
1820 * @param stream which stream (of the agent)
1821 * @param component which component (of the stream)
1822 * @param rcand remote candidate from which the request came, if NULL,
1823 * the response is sent immediately but no other processing is done
1824 * @param toaddr address to which reply is sent
1825 * @param socket the socket over which the request came
1826 * @param rbuf_len length of STUN message to send
1827 * @param rbuf buffer containing the STUN message to send
1828 * @param use_candidate whether the request had USE_CANDIDATE attribute
1830 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1832 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
)
1834 g_assert (rcand
== NULL
|| nice_address_equal(&rcand
->addr
, toaddr
) == TRUE
);
1837 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1838 nice_address_to_string (toaddr
, tmpbuf
);
1839 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent
,
1841 nice_address_get_port (toaddr
),
1844 rcand
, component
->id
,
1845 (int)use_candidate
);
1848 nice_socket_send (socket
, toaddr
, rbuf_len
, (const gchar
*)rbuf
);
1851 /* note: upon successful check, make the reserve check immediately */
1852 priv_schedule_triggered_check (agent
, stream
, component
, socket
, rcand
, use_candidate
);
1855 priv_mark_pair_nominated (agent
, stream
, component
, rcand
);
1860 * Stores information of an incoming STUN connectivity check
1861 * for later use. This is only needed when a check is received
1862 * before we get information about the remote candidates (via
1863 * SDP or other signaling means).
1865 * @return non-zero on error, zero on success
1867 static int priv_store_pending_check (NiceAgent
*agent
, Component
*component
,
1868 const NiceAddress
*from
, NiceSocket
*socket
, uint8_t *username
,
1869 uint16_t username_len
, uint32_t priority
, gboolean use_candidate
)
1871 IncomingCheck
*icheck
;
1872 nice_debug ("Agent %p : Storing pending check.", agent
);
1874 if (component
->incoming_checks
&&
1875 g_slist_length (component
->incoming_checks
) >=
1876 NICE_AGENT_MAX_REMOTE_CANDIDATES
) {
1877 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent
);
1881 icheck
= g_slice_new0 (IncomingCheck
);
1882 component
->incoming_checks
= g_slist_append (component
->incoming_checks
, icheck
);
1883 icheck
->from
= *from
;
1884 icheck
->local_socket
= socket
;
1885 icheck
->priority
= priority
;
1886 icheck
->use_candidate
= use_candidate
;
1887 icheck
->username_len
= username_len
;
1888 icheck
->username
= NULL
;
1889 if (username_len
> 0)
1890 icheck
->username
= g_memdup (username
, username_len
);
1896 * Adds a new pair, discovered from an incoming STUN response, to
1897 * the connectivity check list.
1899 * @return created pair, or NULL on fatal (memory allocation) errors
1901 static CandidateCheckPair
*priv_add_peer_reflexive_pair (NiceAgent
*agent
, guint stream_id
, guint component_id
, NiceCandidate
*local_cand
, CandidateCheckPair
*parent_pair
)
1903 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1904 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1906 stream
->conncheck_list
= g_slist_append (stream
->conncheck_list
, pair
);
1907 pair
->agent
= agent
;
1908 pair
->stream_id
= stream_id
;
1909 pair
->component_id
= component_id
;;
1910 pair
->local
= local_cand
;
1911 pair
->remote
= parent_pair
->remote
;
1912 pair
->state
= NICE_CHECK_DISCOVERED
;
1913 nice_debug ("Agent %p : pair %p state DISCOVERED", agent
, pair
);
1914 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s",
1915 local_cand
->foundation
, parent_pair
->remote
->foundation
);
1916 if (agent
->controlling_mode
== TRUE
)
1917 pair
->priority
= nice_candidate_pair_priority (local_cand
->priority
,
1918 parent_pair
->priority
);
1920 pair
->priority
= nice_candidate_pair_priority (parent_pair
->priority
,
1921 local_cand
->priority
);
1922 pair
->nominated
= FALSE
;
1923 pair
->controlling
= agent
->controlling_mode
;
1924 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent
, pair
->foundation
);
1930 * Recalculates priorities of all candidate pairs. This
1931 * is required after a conflict in ICE roles.
1933 static void priv_recalculate_pair_priorities (NiceAgent
*agent
)
1937 for (i
= agent
->streams
; i
; i
= i
->next
) {
1938 Stream
*stream
= i
->data
;
1939 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
1940 CandidateCheckPair
*p
= j
->data
;
1941 p
->priority
= agent_candidate_pair_priority (agent
, p
->local
, p
->remote
);
1947 * Change the agent role if different from 'control'. Can be
1948 * initiated both by handling of incoming connectivity checks,
1949 * and by processing the responses to checks sent by us.
1951 static void priv_check_for_role_conflict (NiceAgent
*agent
, gboolean control
)
1953 /* role conflict, change mode; wait for a new conn. check */
1954 if (control
!= agent
->controlling_mode
) {
1955 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent
, control
);
1956 agent
->controlling_mode
= control
;
1957 /* the pair priorities depend on the roles, so recalculation
1959 priv_recalculate_pair_priorities (agent
);
1962 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent
, control
);
1966 * Checks whether the mapped address in connectivity check response
1967 * matches any of the known local candidates. If not, apply the
1968 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
1970 * @param agent context pointer
1971 * @param stream which stream (of the agent)
1972 * @param component which component (of the stream)
1973 * @param p the connectivity check pair for which we got a response
1974 * @param socketptr socket used to send the reply
1975 * @param mapped_sockaddr mapped address in the response
1977 * @return pointer to a new pair if one was created, otherwise NULL
1979 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
)
1981 CandidateCheckPair
*new_pair
= NULL
;
1984 gboolean local_cand_matches
= FALSE
;
1986 nice_address_set_from_sockaddr (&mapped
, mapped_sockaddr
);
1988 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
1989 NiceCandidate
*cand
= j
->data
;
1990 if (nice_address_equal (&mapped
, &cand
->addr
)) {
1991 local_cand_matches
= TRUE
;
1996 if (local_cand_matches
== TRUE
) {
1997 /* note: this is same as "adding to VALID LIST" in the spec
1999 p
->state
= NICE_CHECK_SUCCEEDED
;
2000 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent
, p
);
2001 priv_conn_check_unfreeze_related (agent
, stream
, p
);
2004 NiceCandidate
*cand
=
2005 discovery_add_peer_reflexive_candidate (agent
,
2012 p
->state
= NICE_CHECK_FAILED
;
2013 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
2015 /* step: add a new discovered pair (see ICE 7.1.2.2.2
2016 "Constructing a Valid Pair" (ID-19)) */
2017 new_pair
= priv_add_peer_reflexive_pair (agent
, stream
->id
, component
->id
, cand
, p
);
2018 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent
, p
, new_pair
);
2025 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
2026 * check transaction. If found, the reply is processed. Implements
2027 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
2029 * @return TRUE if a matching transaction is found
2031 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
)
2033 struct sockaddr_storage sockaddr
;
2034 socklen_t socklen
= sizeof (sockaddr
);
2036 StunUsageIceReturn res
;
2037 gboolean trans_found
= FALSE
;
2038 StunTransactionId discovery_id
;
2039 StunTransactionId response_id
;
2040 stun_message_id (resp
, response_id
);
2042 for (i
= stream
->conncheck_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2043 CandidateCheckPair
*p
= i
->data
;
2045 if (p
->stun_message
.buffer
) {
2046 stun_message_id (&p
->stun_message
, discovery_id
);
2048 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2049 res
= stun_usage_ice_conncheck_process (resp
,
2050 (struct sockaddr
*) &sockaddr
, &socklen
,
2051 agent_to_ice_compatibility (agent
));
2052 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
2053 "(controlling=%d).", agent
, p
, (int)res
, agent
->controlling_mode
);
2055 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2056 res
== STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS
) {
2057 /* case: found a matching connectivity check request */
2059 CandidateCheckPair
*ok_pair
= NULL
;
2061 nice_debug ("Agent %p : conncheck %p MATCHED.", agent
, p
);
2062 p
->stun_message
.buffer
= NULL
;
2063 p
->stun_message
.buffer_len
= 0;
2065 /* step: verify that response came from the same IP address we
2066 * sent the original request to (see 7.1.2.1. "Failure
2068 if (nice_address_equal (from
, &p
->remote
->addr
) != TRUE
) {
2069 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2070 gchar tmpbuf2
[INET6_ADDRSTRLEN
];
2072 p
->state
= NICE_CHECK_FAILED
;
2073 nice_debug ("Agent %p : conncheck %p FAILED"
2074 " (mismatch of source address).", agent
, p
);
2075 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
2076 nice_address_to_string (from
, tmpbuf2
);
2077 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent
,
2078 tmpbuf
, nice_address_get_port (&p
->remote
->addr
),
2079 tmpbuf2
, nice_address_get_port (from
));
2085 /* note: CONNECTED but not yet READY, see docs */
2087 /* step: handle the possible case of a peer-reflexive
2088 * candidate where the mapped-address in response does
2089 * not match any local candidate, see 7.1.2.2.1
2090 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
2092 if (res
== STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS
) {
2093 /* note: this is same as "adding to VALID LIST" in the spec
2095 p
->state
= NICE_CHECK_SUCCEEDED
;
2096 nice_debug ("Agent %p : Mapped address not found."
2097 " conncheck %p SUCCEEDED.", agent
, p
);
2098 priv_conn_check_unfreeze_related (agent
, stream
, p
);
2100 ok_pair
= priv_process_response_check_for_peer_reflexive(agent
,
2101 stream
, component
, p
, sockptr
, (struct sockaddr
*) &sockaddr
,
2102 local_candidate
, remote_candidate
);
2109 /* Do not step down to CONNECTED if we're already at state READY*/
2110 if (component
->state
!= NICE_COMPONENT_STATE_READY
) {
2111 /* step: notify the client of a new component state (must be done
2112 * before the possible check list state update step */
2113 agent_signal_component_state_change (agent
,
2114 stream
->id
, component
->id
, NICE_COMPONENT_STATE_CONNECTED
);
2118 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
2119 Nominated Flag" (ID-19) */
2120 if (ok_pair
->nominated
== TRUE
)
2121 priv_update_selected_pair (agent
, component
, ok_pair
);
2123 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
2124 states" and 8.1.2 "Updating States", ID-19) */
2125 priv_update_check_list_state_for_ready (agent
, stream
, component
);
2128 } else if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2129 /* case: role conflict error, need to restart with new role */
2130 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent
, p
);
2131 /* note: our role might already have changed due to an
2132 * incoming request, but if not, change role now;
2133 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
2134 priv_check_for_role_conflict (agent
, !p
->controlling
);
2136 p
->stun_message
.buffer
= NULL
;
2137 p
->stun_message
.buffer_len
= 0;
2138 p
->state
= NICE_CHECK_WAITING
;
2139 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
2141 } else if (res
== STUN_USAGE_ICE_RETURN_ERROR
) {
2142 /* case: STUN error, the check STUN context was freed */
2143 nice_debug ("Agent %p : conncheck %p FAILED.", agent
, p
);
2144 p
->stun_message
.buffer
= NULL
;
2145 p
->stun_message
.buffer_len
= 0;
2156 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2157 * transaction. If found, a reply is sent.
2159 * @return TRUE if a matching transaction is found
2161 static gboolean
priv_map_reply_to_discovery_request (NiceAgent
*agent
, StunMessage
*resp
)
2163 struct sockaddr_storage sockaddr
;
2164 socklen_t socklen
= sizeof (sockaddr
);
2165 struct sockaddr_storage alternate
;
2166 socklen_t alternatelen
= sizeof (sockaddr
);
2168 StunUsageBindReturn res
;
2169 gboolean trans_found
= FALSE
;
2170 StunTransactionId discovery_id
;
2171 StunTransactionId response_id
;
2172 stun_message_id (resp
, response_id
);
2174 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2175 CandidateDiscovery
*d
= i
->data
;
2177 if (d
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
&&
2178 d
->stun_message
.buffer
) {
2179 stun_message_id (&d
->stun_message
, discovery_id
);
2181 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2182 res
= stun_usage_bind_process (resp
, (struct sockaddr
*) &sockaddr
,
2183 &socklen
, (struct sockaddr
*) &alternate
, &alternatelen
);
2184 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
2185 agent
, d
, (int)res
);
2187 if (res
== STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER
) {
2188 /* handle alternate server */
2189 NiceAddress niceaddr
;
2190 nice_address_set_from_sockaddr (&niceaddr
,
2191 (struct sockaddr
*) &alternate
);
2192 d
->server
= niceaddr
;
2195 } else if (res
== STUN_USAGE_BIND_RETURN_SUCCESS
) {
2196 /* case: successful binding discovery, create a new local candidate */
2197 NiceAddress niceaddr
;
2198 nice_address_set_from_sockaddr (&niceaddr
,
2199 (struct sockaddr
*) &sockaddr
);
2201 discovery_add_server_reflexive_candidate (
2208 d
->stun_message
.buffer
= NULL
;
2209 d
->stun_message
.buffer_len
= 0;
2212 } else if (res
== STUN_USAGE_BIND_RETURN_ERROR
) {
2213 /* case: STUN error, the check STUN context was freed */
2214 d
->stun_message
.buffer
= NULL
;
2215 d
->stun_message
.buffer_len
= 0;
2227 static CandidateRefresh
*
2228 priv_add_new_turn_refresh (CandidateDiscovery
*cdisco
, NiceCandidate
*relay_cand
,
2231 CandidateRefresh
*cand
;
2232 NiceAgent
*agent
= cdisco
->agent
;
2234 cand
= g_slice_new0 (CandidateRefresh
);
2235 agent
->refresh_list
= g_slist_append (agent
->refresh_list
, cand
);
2237 cand
->nicesock
= cdisco
->nicesock
;
2238 cand
->relay_socket
= relay_cand
->sockptr
;
2239 cand
->server
= cdisco
->server
;
2240 cand
->turn
= cdisco
->turn
;
2241 cand
->stream
= cdisco
->stream
;
2242 cand
->component
= cdisco
->component
;
2243 cand
->agent
= cdisco
->agent
;
2244 memcpy (&cand
->stun_agent
, &cdisco
->stun_agent
, sizeof(StunAgent
));
2245 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2246 agent
, cand
, (lifetime
- 60) * 1000);
2248 /* step: also start the refresh timer */
2249 /* refresh should be sent 1 minute before it expires */
2250 cand
->timer_source
=
2251 agent_timeout_add_with_context (agent
, (lifetime
- 60) * 1000,
2252 priv_turn_allocate_refresh_tick
, cand
);
2254 nice_debug ("timer source is : %d", cand
->timer_source
);
2260 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2261 * transaction. If found, a reply is sent.
2263 * @return TRUE if a matching transaction is found
2265 static gboolean
priv_map_reply_to_relay_request (NiceAgent
*agent
, StunMessage
*resp
)
2267 struct sockaddr_storage sockaddr
;
2268 socklen_t socklen
= sizeof (sockaddr
);
2269 struct sockaddr_storage alternate
;
2270 socklen_t alternatelen
= sizeof (alternate
);
2271 struct sockaddr_storage relayaddr
;
2272 socklen_t relayaddrlen
= sizeof (relayaddr
);
2276 StunUsageTurnReturn res
;
2277 gboolean trans_found
= FALSE
;
2278 StunTransactionId discovery_id
;
2279 StunTransactionId response_id
;
2280 stun_message_id (resp
, response_id
);
2282 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2283 CandidateDiscovery
*d
= i
->data
;
2285 if (d
->type
== NICE_CANDIDATE_TYPE_RELAYED
&&
2286 d
->stun_message
.buffer
) {
2287 stun_message_id (&d
->stun_message
, discovery_id
);
2289 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2290 res
= stun_usage_turn_process (resp
,
2291 (struct sockaddr
*) &relayaddr
, &relayaddrlen
,
2292 (struct sockaddr
*) &sockaddr
, &socklen
,
2293 (struct sockaddr
*) &alternate
, &alternatelen
,
2294 &bandwidth
, &lifetime
, agent_to_turn_compatibility (agent
));
2295 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2296 agent
, d
, (int)res
);
2298 if (res
== STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER
) {
2299 /* handle alternate server */
2300 nice_address_set_from_sockaddr (&d
->server
,
2301 (struct sockaddr
*) &alternate
);
2302 nice_address_set_from_sockaddr (&d
->turn
->server
,
2303 (struct sockaddr
*) &alternate
);
2306 } else if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
||
2307 res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
) {
2308 /* case: successful allocate, create a new local candidate */
2309 NiceAddress niceaddr
;
2310 NiceCandidate
*relay_cand
;
2312 /* We also received our mapped address */
2313 if (res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
) {
2314 nice_address_set_from_sockaddr (&niceaddr
,
2315 (struct sockaddr
*) &sockaddr
);
2317 discovery_add_server_reflexive_candidate (
2325 nice_address_set_from_sockaddr (&niceaddr
,
2326 (struct sockaddr
*) &relayaddr
);
2327 relay_cand
= discovery_add_relay_candidate (
2336 priv_add_new_turn_refresh (d
, relay_cand
, lifetime
);
2337 if (agent
->compatibility
== NICE_COMPATIBILITY_OC2007
||
2338 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) {
2339 /* These data are needed on TURN socket when sending requests,
2340 * but never reach nice_turn_socket_parse_recv() where it could
2341 * be read directly, as the socket does not exist when allocate
2342 * response arrives to _nice_agent_recv(). We must set them right
2343 * after socket gets created in discovery_add_relay_candidate(),
2344 * so we are doing it here. */
2345 nice_turn_socket_set_ms_realm(relay_cand
->sockptr
, &d
->stun_message
);
2346 nice_turn_socket_set_ms_connection_id(relay_cand
->sockptr
, resp
);
2350 d
->stun_message
.buffer
= NULL
;
2351 d
->stun_message
.buffer_len
= 0;
2354 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2356 uint8_t *sent_realm
= NULL
;
2357 uint8_t *recv_realm
= NULL
;
2358 uint16_t sent_realm_len
= 0;
2359 uint16_t recv_realm_len
= 0;
2361 sent_realm
= (uint8_t *) stun_message_find (&d
->stun_message
,
2362 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2363 recv_realm
= (uint8_t *) stun_message_find (resp
,
2364 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2366 /* check for unauthorized error response */
2367 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
2368 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
||
2369 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
2370 stun_message_get_class (resp
) == STUN_ERROR
&&
2371 stun_message_find_error (resp
, &code
) ==
2372 STUN_MESSAGE_RETURN_SUCCESS
&&
2373 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2377 !(recv_realm_len
== sent_realm_len
&&
2378 sent_realm
!= NULL
&&
2379 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2380 d
->stun_resp_msg
= *resp
;
2381 memcpy (d
->stun_resp_buffer
, resp
->buffer
,
2382 stun_message_length (resp
));
2383 d
->stun_resp_msg
.buffer
= d
->stun_resp_buffer
;
2384 d
->stun_resp_msg
.buffer_len
= sizeof(d
->stun_resp_buffer
);
2387 /* case: a real unauthorized error */
2388 d
->stun_message
.buffer
= NULL
;
2389 d
->stun_message
.buffer_len
= 0;
2393 /* case: STUN error, the check STUN context was freed */
2394 d
->stun_message
.buffer
= NULL
;
2395 d
->stun_message
.buffer_len
= 0;
2409 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2410 * transaction. If found, a reply is sent.
2412 * @return TRUE if a matching transaction is found
2414 static gboolean
priv_map_reply_to_relay_refresh (NiceAgent
*agent
, StunMessage
*resp
)
2418 StunUsageTurnReturn res
;
2419 gboolean trans_found
= FALSE
;
2420 StunTransactionId refresh_id
;
2421 StunTransactionId response_id
;
2422 stun_message_id (resp
, response_id
);
2424 for (i
= agent
->refresh_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2425 CandidateRefresh
*cand
= i
->data
;
2427 if (cand
->stun_message
.buffer
) {
2428 stun_message_id (&cand
->stun_message
, refresh_id
);
2430 if (memcmp (refresh_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2431 res
= stun_usage_turn_refresh_process (resp
,
2432 &lifetime
, agent_to_turn_compatibility (cand
->agent
));
2433 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2434 agent
, cand
, (int)res
);
2435 if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
) {
2436 /* refresh should be sent 1 minute before it expires */
2437 cand
->timer_source
=
2438 agent_timeout_add_with_context (cand
->agent
, (lifetime
- 60) * 1000,
2439 priv_turn_allocate_refresh_tick
, cand
);
2441 g_source_destroy (cand
->tick_source
);
2442 g_source_unref (cand
->tick_source
);
2443 cand
->tick_source
= NULL
;
2444 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2446 uint8_t *sent_realm
= NULL
;
2447 uint8_t *recv_realm
= NULL
;
2448 uint16_t sent_realm_len
= 0;
2449 uint16_t recv_realm_len
= 0;
2451 sent_realm
= (uint8_t *) stun_message_find (&cand
->stun_message
,
2452 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2453 recv_realm
= (uint8_t *) stun_message_find (resp
,
2454 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2456 /* check for unauthorized error response */
2457 if (cand
->agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
&&
2458 stun_message_get_class (resp
) == STUN_ERROR
&&
2459 stun_message_find_error (resp
, &code
) ==
2460 STUN_MESSAGE_RETURN_SUCCESS
&&
2461 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2465 !(recv_realm_len
== sent_realm_len
&&
2466 sent_realm
!= NULL
&&
2467 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2468 cand
->stun_resp_msg
= *resp
;
2469 memcpy (cand
->stun_resp_buffer
, resp
->buffer
,
2470 stun_message_length (resp
));
2471 cand
->stun_resp_msg
.buffer
= cand
->stun_resp_buffer
;
2472 cand
->stun_resp_msg
.buffer_len
= sizeof(cand
->stun_resp_buffer
);
2473 priv_turn_allocate_refresh_tick_unlocked (cand
);
2475 /* case: a real unauthorized error */
2476 refresh_cancel (cand
);
2479 /* case: STUN error, the check STUN context was freed */
2480 refresh_cancel (cand
);
2492 static gboolean
priv_map_reply_to_keepalive_conncheck (NiceAgent
*agent
,
2493 Component
*component
, StunMessage
*resp
)
2495 StunTransactionId conncheck_id
;
2496 StunTransactionId response_id
;
2497 stun_message_id (resp
, response_id
);
2499 if (component
->selected_pair
.keepalive
.stun_message
.buffer
) {
2500 stun_message_id (&component
->selected_pair
.keepalive
.stun_message
,
2502 if (memcmp (conncheck_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2503 nice_debug ("Agent %p : Keepalive for selected pair received.",
2505 if (component
->selected_pair
.keepalive
.tick_source
) {
2506 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
2507 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
2508 component
->selected_pair
.keepalive
.tick_source
= NULL
;
2510 component
->selected_pair
.keepalive
.stun_message
.buffer
= NULL
;
2522 Component
*component
;
2524 } conncheck_validater_data
;
2526 static bool conncheck_stun_validater (StunAgent
*agent
,
2527 StunMessage
*message
, uint8_t *username
, uint16_t username_len
,
2528 uint8_t **password
, size_t *password_len
, void *user_data
)
2530 conncheck_validater_data
*data
= (conncheck_validater_data
*) user_data
;
2532 gchar
*ufrag
= NULL
;
2535 gboolean msn_msoc_nice_compatibility
=
2536 data
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2537 data
->agent
->compatibility
== NICE_COMPATIBILITY_OC2007
;
2539 if (data
->agent
->compatibility
== NICE_COMPATIBILITY_OC2007
&&
2540 stun_message_get_class (message
) == STUN_RESPONSE
)
2541 i
= data
->component
->remote_candidates
;
2543 i
= data
->component
->local_candidates
;
2545 for (; i
; i
= i
->next
) {
2546 NiceCandidate
*cand
= i
->data
;
2550 ufrag
= cand
->username
;
2551 else if (data
->stream
)
2552 ufrag
= data
->stream
->local_ufrag
;
2553 ufrag_len
= ufrag
? strlen (ufrag
) : 0;
2555 if (ufrag
&& msn_msoc_nice_compatibility
)
2556 ufrag
= (gchar
*)g_base64_decode (ufrag
, &ufrag_len
);
2561 stun_debug ("Comparing username '");
2562 stun_debug_bytes (username
, username_len
);
2563 stun_debug ("' (%d) with '", username_len
);
2564 stun_debug_bytes (ufrag
, ufrag_len
);
2565 stun_debug ("' (%d) : %d\n",
2566 ufrag_len
, memcmp (username
, ufrag
, ufrag_len
));
2567 if (ufrag_len
> 0 && username_len
>= ufrag_len
&&
2568 memcmp (username
, ufrag
, ufrag_len
) == 0) {
2572 pass
= cand
->password
;
2573 else if(data
->stream
->local_password
[0])
2574 pass
= data
->stream
->local_password
;
2577 *password
= (uint8_t *) pass
;
2578 *password_len
= strlen (pass
);
2580 if (msn_msoc_nice_compatibility
) {
2581 data
->password
= g_base64_decode (pass
, password_len
);
2582 *password
= data
->password
;
2586 if (msn_msoc_nice_compatibility
)
2589 stun_debug ("Found valid username, returning password: '%s'\n", *password
);
2593 if (msn_msoc_nice_compatibility
)
2602 * Processing an incoming STUN message.
2604 * @param agent self pointer
2605 * @param stream stream the packet is related to
2606 * @param component component the packet is related to
2607 * @param socket socket from which the packet was received
2608 * @param from address of the sender
2609 * @param buf message contents
2610 * @param buf message length
2612 * @pre contents of 'buf' is a STUN message
2614 * @return XXX (what FALSE means exactly?)
2616 gboolean
conn_check_handle_inbound_stun (NiceAgent
*agent
, Stream
*stream
,
2617 Component
*component
, NiceSocket
*socket
, const NiceAddress
*from
,
2618 gchar
*buf
, guint len
)
2620 struct sockaddr_storage sockaddr
;
2621 uint8_t rbuf
[MAX_STUN_DATAGRAM_PAYLOAD
];
2623 size_t rbuf_len
= sizeof (rbuf
);
2624 bool control
= agent
->controlling_mode
;
2625 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
2628 uint16_t username_len
;
2631 StunValidationStatus valid
;
2632 conncheck_validater_data validater_data
= {agent
, stream
, component
, NULL
};
2634 NiceCandidate
*remote_candidate
= NULL
;
2635 NiceCandidate
*remote_candidate2
= NULL
;
2636 NiceCandidate
*local_candidate
= NULL
;
2637 gboolean discovery_msg
= FALSE
;
2639 nice_address_copy_to_sockaddr (from
, (struct sockaddr
*) &sockaddr
);
2641 /* note: contents of 'buf' already validated, so it is
2642 * a valid and fully received STUN message */
2646 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2647 nice_address_to_string (from
, tmpbuf
);
2648 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2649 agent
, stream
->id
, component
->id
, tmpbuf
, nice_address_get_port (from
), len
);
2653 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2655 valid
= stun_agent_validate (&agent
->stun_agent
, &req
,
2656 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2658 /* Check for discovery candidates stun agents */
2659 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2660 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2661 for (i
= agent
->discovery_list
; i
; i
= i
->next
) {
2662 CandidateDiscovery
*d
= i
->data
;
2663 if (d
->stream
== stream
&& d
->component
== component
&&
2664 d
->nicesock
== socket
) {
2665 valid
= stun_agent_validate (&d
->stun_agent
, &req
,
2666 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2668 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2671 discovery_msg
= TRUE
;
2676 /* Check for relay refresh stun agents */
2677 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2678 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2679 for (i
= agent
->refresh_list
; i
; i
= i
->next
) {
2680 CandidateRefresh
*r
= i
->data
;
2681 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r
->stream
,
2682 stream
, r
->component
, component
, r
->nicesock
, r
->relay_socket
, socket
);
2683 if (r
->stream
== stream
&& r
->component
== component
&&
2684 (r
->nicesock
== socket
|| r
->relay_socket
== socket
)) {
2685 valid
= stun_agent_validate (&r
->stun_agent
, &req
,
2686 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2687 nice_debug ("Validating gave %d", valid
);
2688 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2690 discovery_msg
= TRUE
;
2696 g_free (validater_data
.password
);
2698 if (valid
== STUN_VALIDATION_NOT_STUN
||
2699 valid
== STUN_VALIDATION_INCOMPLETE_STUN
||
2700 valid
== STUN_VALIDATION_BAD_REQUEST
)
2702 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2707 if (valid
== STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
) {
2708 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent
);
2709 rbuf_len
= stun_agent_build_unknown_attributes_error (&agent
->stun_agent
,
2710 &msg
, rbuf
, rbuf_len
, &req
);
2714 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2715 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
) {
2716 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2721 if (valid
== STUN_VALIDATION_UNAUTHORIZED
) {
2722 nice_debug ("Agent %p : Integrity check failed.", agent
);
2724 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2725 &req
, STUN_ERROR_UNAUTHORIZED
)) {
2726 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2727 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2728 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
)
2729 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2733 if (valid
== STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST
) {
2734 nice_debug ("Agent %p : Integrity check failed.", agent
);
2735 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2736 &req
, STUN_ERROR_BAD_REQUEST
)) {
2737 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2738 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2739 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
)
2740 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2745 username
= (uint8_t *) stun_message_find (&req
, STUN_ATTRIBUTE_USERNAME
,
2748 for (i
= component
->remote_candidates
; i
; i
= i
->next
) {
2749 NiceCandidate
*cand
= i
->data
;
2750 if (nice_address_equal (from
, &cand
->addr
)) {
2751 remote_candidate
= cand
;
2756 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2757 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2758 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2759 /* We need to find which local candidate was used */
2760 for (i
= component
->remote_candidates
;
2761 i
!= NULL
&& remote_candidate2
== NULL
; i
= i
->next
) {
2762 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
2763 gboolean inbound
= TRUE
;
2764 NiceCandidate
*rcand
= i
->data
;
2765 NiceCandidate
*lcand
= j
->data
;
2767 /* If we receive a response, then the username is local:remote */
2768 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2769 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
) {
2770 if (stun_message_get_class (&req
) == STUN_REQUEST
||
2771 stun_message_get_class (&req
) == STUN_INDICATION
) {
2778 uname_len
= priv_create_username (agent
, stream
,
2779 component
->id
, rcand
, lcand
,
2780 uname
, sizeof (uname
), inbound
);
2782 stun_debug ("Comparing username '");
2783 stun_debug_bytes (username
, username
? username_len
: 0);
2784 stun_debug ("' (%d) with '", username_len
);
2785 stun_debug_bytes (uname
, uname_len
);
2786 stun_debug ("' (%d) : %d\n",
2787 uname_len
, username
&& uname_len
== username_len
&&
2788 memcmp (username
, uname
, uname_len
) == 0);
2791 uname_len
== username_len
&&
2792 memcmp (uname
, username
, username_len
) == 0) {
2793 local_candidate
= lcand
;
2794 remote_candidate2
= rcand
;
2801 if (component
->remote_candidates
&&
2802 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
2803 local_candidate
== NULL
&&
2804 discovery_msg
== FALSE
) {
2805 /* if we couldn't match the username and the stun agent has
2806 IGNORE_CREDENTIALS then we have an integrity check failing.
2807 This could happen with the race condition of receiving connchecks
2808 before the remote candidates are added. Just drop the message, and let
2809 the retransmissions make it work. */
2810 nice_debug ("Agent %p : Username check failed.", agent
);
2814 if (valid
!= STUN_VALIDATION_SUCCESS
) {
2815 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent
, valid
);
2820 if (stun_message_get_class (&req
) == STUN_REQUEST
) {
2821 if ( agent
->compatibility
== NICE_COMPATIBILITY_MSN
2822 || agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2823 if (local_candidate
&& remote_candidate2
) {
2824 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2825 username
= (uint8_t *) stun_message_find (&req
,
2826 STUN_ATTRIBUTE_USERNAME
, &username_len
);
2827 uname_len
= priv_create_username (agent
, stream
,
2828 component
->id
, remote_candidate2
, local_candidate
,
2829 uname
, sizeof (uname
), FALSE
);
2830 memcpy (username
, uname
, username_len
);
2832 req
.key
= g_base64_decode ((gchar
*) remote_candidate2
->password
,
2834 } else if (agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2835 req
.key
= g_base64_decode ((gchar
*) local_candidate
->password
,
2839 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
2840 "Ignoring request", agent
);
2845 rbuf_len
= sizeof (rbuf
);
2846 res
= stun_usage_ice_conncheck_create_reply (&agent
->stun_agent
, &req
,
2847 &msg
, rbuf
, &rbuf_len
, (struct sockaddr
*) &sockaddr
, sizeof (sockaddr
),
2848 &control
, agent
->tie_breaker
,
2849 agent_to_ice_compatibility (agent
));
2851 if ( agent
->compatibility
== NICE_COMPATIBILITY_MSN
2852 || agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2856 if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
)
2857 priv_check_for_role_conflict (agent
, control
);
2859 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2860 res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2861 /* case 1: valid incoming request, send a reply/error */
2862 bool use_candidate
=
2863 stun_usage_ice_conncheck_use_candidate (&req
);
2864 uint32_t priority
= stun_usage_ice_conncheck_priority (&req
);
2866 if (agent
->controlling_mode
||
2867 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2868 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2869 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
)
2870 use_candidate
= TRUE
;
2872 if (stream
->initial_binding_request_received
!= TRUE
)
2873 agent_signal_initial_binding_request_received (agent
, stream
);
2875 if (component
->remote_candidates
&& remote_candidate
== NULL
) {
2876 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2877 "peer-reflexive candidate.", agent
);
2878 remote_candidate
= discovery_learn_remote_peer_reflexive_candidate (
2879 agent
, stream
, component
, priority
, from
, socket
,
2881 remote_candidate2
? remote_candidate2
: remote_candidate
);
2882 if(remote_candidate
)
2883 conn_check_add_for_candidate (agent
, stream
->id
, component
, remote_candidate
);
2886 priv_reply_to_conn_check (agent
, stream
, component
, remote_candidate
,
2887 from
, socket
, rbuf_len
, rbuf
, use_candidate
);
2889 if (component
->remote_candidates
== NULL
) {
2890 /* case: We've got a valid binding request to a local candidate
2891 * but we do not yet know remote credentials nor
2892 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2893 * immediately but postpone all other processing until
2894 * we get information about the remote candidates */
2896 /* step: send a reply immediately but postpone other processing */
2897 priv_store_pending_check (agent
, component
, from
, socket
,
2898 username
, username_len
, priority
, use_candidate
);
2901 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2902 agent
, strerror(errno
));
2906 /* case 2: not a new request, might be a reply... */
2907 gboolean trans_found
= FALSE
;
2909 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2911 /* step: let's try to match the response to an existing check context */
2912 if (trans_found
!= TRUE
)
2913 trans_found
= priv_map_reply_to_conn_check_request (agent
, stream
,
2914 component
, socket
, from
, local_candidate
, remote_candidate
, &req
);
2916 /* step: let's try to match the response to an existing discovery */
2917 if (trans_found
!= TRUE
)
2918 trans_found
= priv_map_reply_to_discovery_request (agent
, &req
);
2920 /* step: let's try to match the response to an existing turn allocate */
2921 if (trans_found
!= TRUE
)
2922 trans_found
= priv_map_reply_to_relay_request (agent
, &req
);
2924 /* step: let's try to match the response to an existing turn refresh */
2925 if (trans_found
!= TRUE
)
2926 trans_found
= priv_map_reply_to_relay_refresh (agent
, &req
);
2928 /* step: let's try to match the response to an existing keepalive conncheck */
2929 if (trans_found
!= TRUE
)
2930 trans_found
= priv_map_reply_to_keepalive_conncheck (agent
, component
,
2933 if (trans_found
!= TRUE
)
2934 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2935 "probably a keepalive.", agent
);