2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2006-2009 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
26 * Youness Alaoui, Collabora Ltd.
27 * Dafydd Harries, Collabora Ltd.
29 * Alternatively, the contents of this file may be used under the terms of the
30 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
31 * case the provisions of LGPL are applicable instead of those above. If you
32 * wish to allow use of your version of this file only under the terms of the
33 * LGPL and not to allow others to use your version of this file under the
34 * MPL, indicate your decision by deleting the provisions above and replace
35 * them with the notice and other provisions required by the LGPL. If you do
36 * not delete the provisions above, a recipient may use your version of this
37 * file under either the MPL or the LGPL.
42 * @brief ICE connectivity checks
57 #include "agent-priv.h"
58 #include "conncheck.h"
59 #include "discovery.h"
60 #include "stun/usages/ice.h"
61 #include "stun/usages/bind.h"
62 #include "stun/usages/turn.h"
64 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
);
65 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
);
66 static guint
priv_prune_pending_checks (Stream
*stream
, guint component_id
);
67 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
);
68 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
);
69 static size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
70 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
71 uint8_t *dest
, guint dest_len
, gboolean inbound
);
72 static size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
73 NiceCandidate
*remote
, uint8_t **password
);
75 static int priv_timer_expired (GTimeVal
*timer
, GTimeVal
*now
)
77 return (now
->tv_sec
== timer
->tv_sec
) ?
78 now
->tv_usec
>= timer
->tv_usec
:
79 now
->tv_sec
>= timer
->tv_sec
;
83 * Finds the next connectivity check in WAITING state.
85 static CandidateCheckPair
*priv_conn_check_find_next_waiting (GSList
*conn_check_list
)
89 /* note: list is sorted in priority order to first waiting check has
90 * the highest priority */
92 for (i
= conn_check_list
; i
; i
= i
->next
) {
93 CandidateCheckPair
*p
= i
->data
;
94 if (p
->state
== NICE_CHECK_WAITING
)
102 * Initiates a new connectivity check for a ICE candidate pair.
104 * @return TRUE on success, FALSE on error
106 static gboolean
priv_conn_check_initiate (NiceAgent
*agent
, CandidateCheckPair
*pair
)
108 /* XXX: from ID-16 onwards, the checks should not be sent
109 * immediately, but be put into the "triggered queue",
110 * see "7.2.1.4 Triggered Checks"
112 g_get_current_time (&pair
->next_tick
);
113 g_time_val_add (&pair
->next_tick
, agent
->timer_ta
* 1000);
114 pair
->state
= NICE_CHECK_IN_PROGRESS
;
115 nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent
, pair
);
116 conn_check_send (agent
, pair
);
121 * Unfreezes the next connectivity check in the list. Follows the
122 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
123 * (ID-19), with some exceptions (see comments in code).
125 * See also sect 7.1.2.2.3 (Updating Pair States), and
126 * priv_conn_check_unfreeze_related().
128 * @return TRUE on success, and FALSE if no frozen candidates were found.
130 static gboolean
priv_conn_check_unfreeze_next (NiceAgent
*agent
)
132 CandidateCheckPair
*pair
= NULL
;
135 /* XXX: the unfreezing is implemented a bit differently than in the
136 * current ICE spec, but should still be interoperate:
137 * - checks are not grouped by foundation
138 * - one frozen check is unfrozen (lowest component-id, highest
142 for (i
= agent
->streams
; i
; i
= i
->next
) {
143 Stream
*stream
= i
->data
;
144 guint64 max_frozen_priority
= 0;
147 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
148 CandidateCheckPair
*p
= j
->data
;
150 /* XXX: the prio check could be removed as the pairs are sorted
153 if (p
->state
== NICE_CHECK_FROZEN
) {
154 if (p
->priority
> max_frozen_priority
) {
155 max_frozen_priority
= p
->priority
;
166 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent
, pair
, pair
->stream_id
, pair
->component_id
, pair
->foundation
);
167 pair
->state
= NICE_CHECK_WAITING
;
168 nice_debug ("Agent %p : pair %p state WAITING", agent
, pair
);
176 * Unfreezes the next next connectivity check in the list after
177 * check 'success_check' has successfully completed.
179 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
181 * @param agent context
182 * @param ok_check a connectivity check that has just completed
184 * @return TRUE on success, and FALSE if no frozen candidates were found.
186 static void priv_conn_check_unfreeze_related (NiceAgent
*agent
, Stream
*stream
, CandidateCheckPair
*ok_check
)
192 g_assert (ok_check
->state
== NICE_CHECK_SUCCEEDED
);
194 g_assert (stream
->id
== ok_check
->stream_id
);
196 /* step: perform the step (1) of 'Updating Pair States' */
197 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
198 CandidateCheckPair
*p
= i
->data
;
200 if (p
->stream_id
== ok_check
->stream_id
) {
201 if (p
->state
== NICE_CHECK_FROZEN
&&
202 strcmp (p
->foundation
, ok_check
->foundation
) == 0) {
203 nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent
, p
, ok_check
);
204 p
->state
= NICE_CHECK_WAITING
;
205 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
211 /* step: perform the step (2) of 'Updating Pair States' */
212 stream
= agent_find_stream (agent
, ok_check
->stream_id
);
213 if (stream_all_components_ready (stream
)) {
214 /* step: unfreeze checks from other streams */
215 for (i
= agent
->streams
; i
; i
= i
->next
) {
217 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
218 CandidateCheckPair
*p
= j
->data
;
220 if (p
->stream_id
== s
->id
&&
221 p
->stream_id
!= ok_check
->stream_id
) {
222 if (p
->state
== NICE_CHECK_FROZEN
&&
223 strcmp (p
->foundation
, ok_check
->foundation
) == 0) {
224 nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent
, p
, s
->id
, ok_check
);
225 p
->state
= NICE_CHECK_WAITING
;
226 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
232 /* note: only unfreeze check from one stream at a time */
239 priv_conn_check_unfreeze_next (agent
);
243 * Helper function for connectivity check timer callback that
244 * runs through the stream specific part of the state machine.
246 * @param schedule if TRUE, schedule a new check
248 * @return will return FALSE when no more pending timers.
250 static gboolean
priv_conn_check_tick_stream (Stream
*stream
, NiceAgent
*agent
, GTimeVal
*now
)
252 gboolean keep_timer_going
= FALSE
;
253 guint s_inprogress
= 0, s_succeeded
= 0, s_discovered
= 0,
254 s_nominated
= 0, s_waiting_for_nomination
= 0;
255 guint frozen
= 0, waiting
= 0;
258 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
259 CandidateCheckPair
*p
= i
->data
;
261 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
262 if (p
->stun_message
.buffer
== NULL
) {
263 nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent
);
264 p
->state
= NICE_CHECK_FAILED
;
265 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
266 } else if (priv_timer_expired (&p
->next_tick
, now
)) {
267 switch (stun_timer_refresh (&p
->timer
)) {
268 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
270 /* case: error, abort processing */
271 StunTransactionId id
;
273 nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent
, p
);
274 p
->state
= NICE_CHECK_FAILED
;
275 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
277 stun_message_id (&p
->stun_message
, id
);
278 stun_agent_forget_transaction (&agent
->stun_agent
, id
);
280 p
->stun_message
.buffer
= NULL
;
281 p
->stun_message
.buffer_len
= 0;
286 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
288 /* case: not ready, so schedule a new timeout */
289 unsigned int timeout
= stun_timer_remainder (&p
->timer
);
290 nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
293 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
,
294 stun_message_length (&p
->stun_message
),
295 (gchar
*)p
->stun_buffer
);
298 /* note: convert from milli to microseconds for g_time_val_add() */
300 g_time_val_add (&p
->next_tick
, timeout
* 1000);
302 keep_timer_going
= TRUE
;
305 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
307 unsigned int timeout
= stun_timer_remainder (&p
->timer
);
309 /* note: convert from milli to microseconds for g_time_val_add() */
311 g_time_val_add (&p
->next_tick
, timeout
* 1000);
313 keep_timer_going
= TRUE
;
320 if (p
->state
== NICE_CHECK_FROZEN
)
322 else if (p
->state
== NICE_CHECK_IN_PROGRESS
)
324 else if (p
->state
== NICE_CHECK_WAITING
)
326 else if (p
->state
== NICE_CHECK_SUCCEEDED
)
328 else if (p
->state
== NICE_CHECK_DISCOVERED
)
331 if ((p
->state
== NICE_CHECK_SUCCEEDED
|| p
->state
== NICE_CHECK_DISCOVERED
)
334 else if ((p
->state
== NICE_CHECK_SUCCEEDED
||
335 p
->state
== NICE_CHECK_DISCOVERED
) && !p
->nominated
)
336 ++s_waiting_for_nomination
;
339 /* note: keep the timer going as long as there is work to be done */
341 keep_timer_going
= TRUE
;
343 /* note: if some components have established connectivity,
344 * but yet no nominated pair, keep timer going */
345 if (s_nominated
< stream
->n_components
&&
346 s_waiting_for_nomination
) {
347 keep_timer_going
= TRUE
;
348 if (agent
->controlling_mode
) {
350 for (n
= 0; n
< stream
->n_components
; n
++) {
351 for (k
= stream
->conncheck_list
; k
; k
= k
->next
) {
352 CandidateCheckPair
*p
= k
->data
;
353 /* note: highest priority item selected (list always sorted) */
354 if (p
->state
== NICE_CHECK_SUCCEEDED
||
355 p
->state
== NICE_CHECK_DISCOVERED
) {
356 nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent
, p
);
358 priv_conn_check_initiate (agent
, p
);
359 break; /* move to the next component */
366 static int tick_counter
= 0;
367 if (tick_counter
++ % 50 == 0 || keep_timer_going
!= TRUE
)
368 nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
369 "%u waiting, %u succeeded, %u discovered, %u nominated, "
370 "%u waiting-for-nom.", agent
,
371 tick_counter
, frozen
, s_inprogress
, waiting
, s_succeeded
,
372 s_discovered
, s_nominated
, s_waiting_for_nomination
);
375 return keep_timer_going
;
381 * Timer callback that handles initiating and managing connectivity
382 * checks (paced by the Ta timer).
384 * This function is designed for the g_timeout_add() interface.
386 * @return will return FALSE when no more pending timers.
388 static gboolean
priv_conn_check_tick_unlocked (gpointer pointer
)
390 CandidateCheckPair
*pair
= NULL
;
391 NiceAgent
*agent
= pointer
;
392 gboolean keep_timer_going
= FALSE
;
396 /* step: process ongoing STUN transactions */
397 g_get_current_time (&now
);
399 /* step: find the highest priority waiting check and send it */
400 for (i
= agent
->streams
; i
; i
= i
->next
) {
401 Stream
*stream
= i
->data
;
403 pair
= priv_conn_check_find_next_waiting (stream
->conncheck_list
);
409 priv_conn_check_initiate (agent
, pair
);
410 keep_timer_going
= TRUE
;
412 keep_timer_going
= priv_conn_check_unfreeze_next (agent
);
415 for (j
= agent
->streams
; j
; j
= j
->next
) {
416 Stream
*stream
= j
->data
;
418 priv_conn_check_tick_stream (stream
, agent
, &now
);
420 keep_timer_going
= res
;
423 /* step: stop timer if no work left */
424 if (keep_timer_going
!= TRUE
) {
425 nice_debug ("Agent %p : %s: stopping conncheck timer", agent
, G_STRFUNC
);
426 for (i
= agent
->streams
; i
; i
= i
->next
) {
427 Stream
*stream
= i
->data
;
428 priv_update_check_list_failed_components (agent
, stream
);
429 for (j
= stream
->components
; j
; j
= j
->next
) {
430 Component
*component
= j
->data
;
431 priv_update_check_list_state_for_ready (agent
, stream
, component
);
435 /* Stopping the timer so destroy the source.. this will allow
436 the timer to be reset if we get a set_remote_candidates after this
438 if (agent
->conncheck_timer_source
!= NULL
) {
439 g_source_destroy (agent
->conncheck_timer_source
);
440 g_source_unref (agent
->conncheck_timer_source
);
441 agent
->conncheck_timer_source
= NULL
;
444 /* XXX: what to signal, is all processing now really done? */
445 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent
);
448 return keep_timer_going
;
451 static gboolean
priv_conn_check_tick (gpointer pointer
)
456 if (g_source_is_destroyed (g_main_current_source ())) {
457 nice_debug ("Source was destroyed. "
458 "Avoided race condition in priv_conn_check_tick");
462 ret
= priv_conn_check_tick_unlocked (pointer
);
468 static gboolean
priv_conn_keepalive_retransmissions_tick (gpointer pointer
)
470 CandidatePair
*pair
= (CandidatePair
*) pointer
;
474 /* A race condition might happen where the mutex above waits for the lock
475 * and in the meantime another thread destroys the source.
476 * In that case, we don't need to run our retransmission tick since it should
477 * have been cancelled */
478 if (g_source_is_destroyed (g_main_current_source ())) {
479 nice_debug ("Source was destroyed. "
480 "Avoided race condition in priv_conn_keepalive_retransmissions_tick");
485 g_source_destroy (pair
->keepalive
.tick_source
);
486 g_source_unref (pair
->keepalive
.tick_source
);
487 pair
->keepalive
.tick_source
= NULL
;
489 switch (stun_timer_refresh (&pair
->keepalive
.timer
)) {
490 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
493 StunTransactionId id
;
496 stun_message_id (&pair
->keepalive
.stun_message
, id
);
497 stun_agent_forget_transaction (&pair
->keepalive
.agent
->stun_agent
, id
);
499 if (pair
->keepalive
.agent
->media_after_tick
) {
500 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
501 "but media was received. Suspecting keepalive lost because of "
502 "network bottleneck", pair
->keepalive
.agent
);
504 pair
->keepalive
.stun_message
.buffer
= NULL
;
506 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
507 "peer probably lost connection", pair
->keepalive
.agent
);
508 agent_signal_component_state_change (pair
->keepalive
.agent
,
509 pair
->keepalive
.stream_id
, pair
->keepalive
.component_id
,
510 NICE_COMPONENT_STATE_FAILED
);
514 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
516 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
517 stun_message_length (&pair
->keepalive
.stun_message
),
518 (gchar
*)pair
->keepalive
.stun_buffer
);
520 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
521 pair
->keepalive
.agent
);
522 pair
->keepalive
.tick_source
=
523 agent_timeout_add_with_context (pair
->keepalive
.agent
,
524 stun_timer_remainder (&pair
->keepalive
.timer
),
525 priv_conn_keepalive_retransmissions_tick
, pair
);
527 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
528 pair
->keepalive
.tick_source
=
529 agent_timeout_add_with_context (pair
->keepalive
.agent
,
530 stun_timer_remainder (&pair
->keepalive
.timer
),
531 priv_conn_keepalive_retransmissions_tick
, pair
);
542 * Timer callback that handles initiating and managing connectivity
543 * checks (paced by the Ta timer).
545 * This function is designed for the g_timeout_add() interface.
547 * @return will return FALSE when no more pending timers.
549 static gboolean
priv_conn_keepalive_tick_unlocked (NiceAgent
*agent
)
553 gboolean ret
= FALSE
;
556 /* case 1: session established and media flowing
557 * (ref ICE sect 10 "Keepalives" ID-19) */
558 for (i
= agent
->streams
; i
; i
= i
->next
) {
560 Stream
*stream
= i
->data
;
561 for (j
= stream
->components
; j
; j
= j
->next
) {
562 Component
*component
= j
->data
;
563 if (component
->selected_pair
.local
!= NULL
) {
564 CandidatePair
*p
= &component
->selected_pair
;
566 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
) {
567 guint32 priority
= nice_candidate_ice_priority_full (
568 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
, 1,
569 p
->local
->component_id
);
570 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
572 priv_create_username (agent
, agent_find_stream (agent
, stream
->id
),
573 component
->id
, p
->remote
, p
->local
, uname
, sizeof (uname
),
575 uint8_t *password
= NULL
;
576 size_t password_len
= priv_get_password (agent
,
577 agent_find_stream (agent
, stream
->id
), p
->remote
, &password
);
578 gchar tmpbuf
[INET6_ADDRSTRLEN
];
580 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
581 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
582 "socket=%u (c-id:%u), username='%s' (%d), "
583 "password='%s' (%d), priority=%u.", agent
,
584 tmpbuf
, nice_address_get_port (&p
->remote
->addr
),
585 g_socket_get_fd(((NiceSocket
*)p
->local
->sockptr
)->fileno
), component
->id
,
586 uname
, uname_len
, password
, password_len
, priority
);
589 buf_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
590 &p
->keepalive
.stun_message
, p
->keepalive
.stun_buffer
,
591 sizeof(p
->keepalive
.stun_buffer
),
592 uname
, uname_len
, password
, password_len
,
593 agent
->controlling_mode
, agent
->controlling_mode
, priority
,
596 agent_to_ice_compatibility (agent
));
598 nice_debug ("Agent %p: conncheck created %d - %p",
599 agent
, buf_len
, p
->keepalive
.stun_message
.buffer
);
602 stun_timer_start (&p
->keepalive
.timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
603 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
605 agent
->media_after_tick
= FALSE
;
607 /* send the conncheck */
608 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
,
609 buf_len
, (gchar
*)p
->keepalive
.stun_buffer
);
611 if (p
->keepalive
.tick_source
!= NULL
) {
612 g_source_destroy (p
->keepalive
.tick_source
);
613 g_source_unref (p
->keepalive
.tick_source
);
614 p
->keepalive
.tick_source
= NULL
;
617 p
->keepalive
.stream_id
= stream
->id
;
618 p
->keepalive
.component_id
= component
->id
;
619 p
->keepalive
.agent
= agent
;
621 p
->keepalive
.tick_source
=
622 agent_timeout_add_with_context (p
->keepalive
.agent
,
623 stun_timer_remainder (&p
->keepalive
.timer
),
624 priv_conn_keepalive_retransmissions_tick
, p
);
630 buf_len
= stun_usage_bind_keepalive (&agent
->stun_agent
,
631 &p
->keepalive
.stun_message
, p
->keepalive
.stun_buffer
,
632 sizeof(p
->keepalive
.stun_buffer
));
635 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
, buf_len
,
636 (gchar
*)p
->keepalive
.stun_buffer
);
638 nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
639 agent
, p
, (int) buf_len
);
648 /* case 2: connectivity establishment ongoing
649 * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */
650 for (i
= agent
->streams
; i
; i
= i
->next
) {
651 Stream
*stream
= i
->data
;
652 for (j
= stream
->components
; j
; j
= j
->next
) {
653 Component
*component
= j
->data
;
654 if (component
->state
< NICE_COMPONENT_STATE_READY
&&
655 agent
->stun_server_ip
) {
656 NiceAddress stun_server
;
657 if (nice_address_set_from_string (&stun_server
, agent
->stun_server_ip
)) {
658 StunAgent stun_agent
;
659 uint8_t stun_buffer
[STUN_MAX_MESSAGE_SIZE
];
660 StunMessage stun_message
;
661 size_t buffer_len
= 0;
663 nice_address_set_port (&stun_server
, agent
->stun_server_port
);
665 stun_agent_init (&stun_agent
, STUN_ALL_KNOWN_ATTRIBUTES
,
666 STUN_COMPATIBILITY_RFC3489
, 0);
668 buffer_len
= stun_usage_bind_create (&stun_agent
,
669 &stun_message
, stun_buffer
, sizeof(stun_buffer
));
671 for (k
= component
->local_candidates
; k
; k
= k
->next
) {
672 NiceCandidate
*candidate
= (NiceCandidate
*) k
->data
;
673 if (candidate
->type
== NICE_CANDIDATE_TYPE_HOST
) {
674 /* send the conncheck */
675 nice_debug ("Agent %p : resending STUN on %s to keep the "
676 "candidate alive.", agent
, candidate
->foundation
);
677 nice_socket_send (candidate
->sockptr
, &stun_server
,
678 buffer_len
, (gchar
*)stun_buffer
);
687 nice_debug ("Agent %p : %s: stopping keepalive timer", agent
, G_STRFUNC
);
697 static gboolean
priv_conn_keepalive_tick (gpointer pointer
)
699 NiceAgent
*agent
= pointer
;
703 if (g_source_is_destroyed (g_main_current_source ())) {
704 nice_debug ("Source was destroyed. "
705 "Avoided race condition in priv_conn_keepalive_tick");
710 ret
= priv_conn_keepalive_tick_unlocked (agent
);
712 if (agent
->keepalive_timer_source
) {
713 g_source_destroy (agent
->keepalive_timer_source
);
714 g_source_unref (agent
->keepalive_timer_source
);
715 agent
->keepalive_timer_source
= NULL
;
723 static gboolean
priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer
)
725 CandidateRefresh
*cand
= (CandidateRefresh
*) pointer
;
729 /* A race condition might happen where the mutex above waits for the lock
730 * and in the meantime another thread destroys the source.
731 * In that case, we don't need to run our retransmission tick since it should
732 * have been cancelled */
733 if (g_source_is_destroyed (g_main_current_source ())) {
734 nice_debug ("Source was destroyed. "
735 "Avoided race condition in priv_turn_allocate_refresh_retransmissions_tick");
741 g_source_destroy (cand
->tick_source
);
742 g_source_unref (cand
->tick_source
);
743 cand
->tick_source
= NULL
;
745 switch (stun_timer_refresh (&cand
->timer
)) {
746 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
749 StunTransactionId id
;
751 stun_message_id (&cand
->stun_message
, id
);
752 stun_agent_forget_transaction (&cand
->stun_agent
, id
);
754 refresh_cancel (cand
);
757 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
759 nice_socket_send (cand
->nicesock
, &cand
->server
,
760 stun_message_length (&cand
->stun_message
), (gchar
*)cand
->stun_buffer
);
762 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
763 stun_timer_remainder (&cand
->timer
),
764 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
766 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
767 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
768 stun_timer_remainder (&cand
->timer
),
769 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
778 static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh
*cand
)
784 size_t buffer_len
= 0;
785 StunUsageTurnCompatibility turn_compat
=
786 agent_to_turn_compatibility (cand
->agent
);
788 username
= (uint8_t *)cand
->turn
->username
;
789 username_len
= (size_t) strlen (cand
->turn
->username
);
790 password
= (uint8_t *)cand
->turn
->password
;
791 password_len
= (size_t) strlen (cand
->turn
->password
);
793 if (turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_MSN
||
794 turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_OC2007
) {
795 username
= g_base64_decode ((gchar
*)username
, &username_len
);
796 password
= g_base64_decode ((gchar
*)password
, &password_len
);
799 buffer_len
= stun_usage_turn_create_refresh (&cand
->stun_agent
,
800 &cand
->stun_message
, cand
->stun_buffer
, sizeof(cand
->stun_buffer
),
801 cand
->stun_resp_msg
.buffer
== NULL
? NULL
: &cand
->stun_resp_msg
, -1,
802 username
, username_len
,
803 password
, password_len
,
806 if (turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_MSN
||
807 turn_compat
== STUN_USAGE_TURN_COMPATIBILITY_OC2007
) {
808 g_free (cand
->msn_turn_username
);
809 g_free (cand
->msn_turn_password
);
810 cand
->msn_turn_username
= username
;
811 cand
->msn_turn_password
= password
;
814 nice_debug ("Agent %p : Sending allocate Refresh %d", cand
->agent
, buffer_len
);
816 if (cand
->tick_source
!= NULL
) {
817 g_source_destroy (cand
->tick_source
);
818 g_source_unref (cand
->tick_source
);
819 cand
->tick_source
= NULL
;
822 if (buffer_len
> 0) {
823 stun_timer_start (&cand
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
824 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
826 /* send the refresh */
827 nice_socket_send (cand
->nicesock
, &cand
->server
,
828 buffer_len
, (gchar
*)cand
->stun_buffer
);
830 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
831 stun_timer_remainder (&cand
->timer
),
832 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
839 * Timer callback that handles refreshing TURN allocations
841 * This function is designed for the g_timeout_add() interface.
843 * @return will return FALSE when no more pending timers.
845 static gboolean
priv_turn_allocate_refresh_tick (gpointer pointer
)
847 CandidateRefresh
*cand
= (CandidateRefresh
*) pointer
;
850 if (g_source_is_destroyed (g_main_current_source ())) {
851 nice_debug ("Source was destroyed. "
852 "Avoided race condition in priv_turn_allocate_refresh_tick");
857 priv_turn_allocate_refresh_tick_unlocked (cand
);
865 * Initiates the next pending connectivity check.
867 * @return TRUE if a pending check was scheduled
869 gboolean
conn_check_schedule_next (NiceAgent
*agent
)
871 gboolean res
= priv_conn_check_unfreeze_next (agent
);
872 nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent
, res
);
874 if (agent
->discovery_unsched_items
> 0)
875 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent
);
877 /* step: call once imediately */
878 res
= priv_conn_check_tick_unlocked ((gpointer
) agent
);
879 nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent
, res
);
881 /* step: schedule timer if not running yet */
882 if (res
&& agent
->conncheck_timer_source
== NULL
) {
883 agent
->conncheck_timer_source
= agent_timeout_add_with_context (agent
, agent
->timer_ta
, priv_conn_check_tick
, agent
);
886 /* step: also start the keepalive timer */
887 if (agent
->keepalive_timer_source
== NULL
) {
888 agent
->keepalive_timer_source
= agent_timeout_add_with_context (agent
, NICE_AGENT_TIMER_TR_DEFAULT
, priv_conn_keepalive_tick
, agent
);
891 nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent
, res
);
896 * Compares two connectivity check items. Checkpairs are sorted
897 * in descending priority order, with highest priority item at
898 * the start of the list.
900 gint
conn_check_compare (const CandidateCheckPair
*a
, const CandidateCheckPair
*b
)
902 if (a
->priority
> b
->priority
)
904 else if (a
->priority
< b
->priority
)
910 * Preprocesses a new connectivity check by going through list
911 * of a any stored early incoming connectivity checks from
912 * the remote peer. If a matching incoming check has been already
913 * received, update the state of the new outgoing check 'pair'.
915 * @param agent context pointer
916 * @param stream which stream (of the agent)
917 * @param component pointer to component object to which 'pair'has been added
918 * @param pair newly added connectivity check
920 static void priv_preprocess_conn_check_pending_data (NiceAgent
*agent
, Stream
*stream
, Component
*component
, CandidateCheckPair
*pair
)
923 for (i
= component
->incoming_checks
; i
; i
= i
->next
) {
924 IncomingCheck
*icheck
= i
->data
;
925 if (nice_address_equal (&icheck
->from
, &pair
->remote
->addr
) &&
926 icheck
->local_socket
== pair
->local
->sockptr
) {
927 nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent
, pair
, icheck
, agent
, stream
->id
, component
->id
);
928 if (icheck
->use_candidate
)
929 priv_mark_pair_nominated (agent
, stream
, component
, pair
->remote
);
930 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, pair
->remote
, icheck
->use_candidate
);
936 * Handle any processing steps for connectivity checks after
937 * remote candidates have been set. This function handles
938 * the special case where answerer has sent us connectivity
939 * checks before the answer (containing candidate information),
940 * reaches us. The special case is documented in sect 7.2
941 * if ICE spec (ID-19).
943 void conn_check_remote_candidates_set(NiceAgent
*agent
)
945 GSList
*i
, *j
, *k
, *l
, *m
, *n
;
947 for (i
= agent
->streams
; i
; i
= i
->next
) {
948 Stream
*stream
= i
->data
;
949 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
950 CandidateCheckPair
*pair
= j
->data
;
951 Component
*component
= stream_find_component_by_id (stream
, pair
->component_id
);
952 gboolean match
= FALSE
;
954 /* performn delayed processing of spec steps section 7.2.1.4,
955 and section 7.2.1.5 */
956 priv_preprocess_conn_check_pending_data (agent
, stream
, component
, pair
);
958 for (k
= component
->incoming_checks
; k
; k
= k
->next
) {
959 IncomingCheck
*icheck
= k
->data
;
960 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
961 * be handled separately */
962 for (l
= component
->remote_candidates
; l
; l
= l
->next
) {
963 NiceCandidate
*cand
= l
->data
;
964 if (nice_address_equal (&icheck
->from
, &cand
->addr
)) {
970 /* note: we have gotten an incoming connectivity check from
971 * an address that is not a known remote candidate */
973 NiceCandidate
*local_candidate
= NULL
;
974 NiceCandidate
*remote_candidate
= NULL
;
976 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
977 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
978 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
979 /* We need to find which local candidate was used */
980 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
983 nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
984 "stored pending check", agent
);
986 for (m
= component
->remote_candidates
;
987 m
!= NULL
&& remote_candidate
== NULL
; m
= m
->next
) {
988 for (n
= component
->local_candidates
; n
; n
= n
->next
) {
989 NiceCandidate
*rcand
= m
->data
;
990 NiceCandidate
*lcand
= n
->data
;
992 uname_len
= priv_create_username (agent
, stream
,
993 component
->id
, rcand
, lcand
,
994 uname
, sizeof (uname
), TRUE
);
996 stun_debug ("pending check, comparing username '");
997 stun_debug_bytes (icheck
->username
,
998 icheck
->username
? icheck
->username_len
: 0);
999 stun_debug ("' (%d) with '", icheck
->username_len
);
1000 stun_debug_bytes (uname
, uname_len
);
1001 stun_debug ("' (%d) : %d\n",
1002 uname_len
, icheck
->username
&&
1003 uname_len
== icheck
->username_len
&&
1004 memcmp (icheck
->username
, uname
, uname_len
) == 0);
1006 if (icheck
->username
&&
1007 uname_len
== icheck
->username_len
&&
1008 memcmp (uname
, icheck
->username
, icheck
->username_len
) == 0) {
1009 local_candidate
= lcand
;
1010 remote_candidate
= rcand
;
1017 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1018 local_candidate
== NULL
) {
1019 /* if we couldn't match the username, then the matching remote
1020 * candidate hasn't been received yet.. we must wait */
1021 nice_debug ("Agent %p : Username check failed. pending check has "
1022 "to wait to be processed", agent
);
1024 NiceCandidate
*candidate
;
1026 nice_debug ("Agent %p : Discovered peer reflexive from early i-check",
1029 discovery_learn_remote_peer_reflexive_candidate (agent
,
1034 icheck
->local_socket
,
1035 local_candidate
, remote_candidate
);
1037 conn_check_add_for_candidate (agent
, stream
->id
, component
, candidate
);
1039 if (icheck
->use_candidate
)
1040 priv_mark_pair_nominated (agent
, stream
, component
, candidate
);
1041 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, candidate
, icheck
->use_candidate
);
1047 /* Once we process the pending checks, we should free them to avoid
1048 * reprocessing them again if a dribble-mode set_remote_candidates
1050 for (m
= component
->incoming_checks
; m
; m
= m
->next
) {
1051 IncomingCheck
*icheck
= m
->data
;
1052 g_free (icheck
->username
);
1053 g_slice_free (IncomingCheck
, icheck
);
1055 g_slist_free (component
->incoming_checks
);
1056 component
->incoming_checks
= NULL
;
1062 * Enforces the upper limit for connectivity checks as described
1063 * in ICE spec section 5.7.3 (ID-19). See also
1064 * conn_check_add_for_candidate().
1066 static GSList
*priv_limit_conn_check_list_size (GSList
*conncheck_list
, guint upper_limit
)
1068 guint list_len
= g_slist_length (conncheck_list
);
1070 GSList
*result
= conncheck_list
;
1072 if (list_len
> upper_limit
) {
1073 nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
1074 "Maximum connchecks allowed : %d", list_len
, upper_limit
);
1075 c
= list_len
- upper_limit
;
1076 if (c
== list_len
) {
1077 /* case: delete whole list */
1078 g_slist_foreach (conncheck_list
, conn_check_free_item
, NULL
);
1079 g_slist_free (conncheck_list
),
1083 /* case: remove 'c' items from list end (lowest priority) */
1087 i
= g_slist_nth (conncheck_list
, c
- 1);
1093 /* delete the rest of the connectivity check list */
1094 g_slist_foreach (tmp
, conn_check_free_item
, NULL
);
1104 * Changes the selected pair for the component if 'pair' is nominated
1105 * and has higher priority than the currently selected pair. See
1106 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
1108 static gboolean
priv_update_selected_pair (NiceAgent
*agent
, Component
*component
, CandidateCheckPair
*pair
)
1110 g_assert (component
);
1112 if (pair
->priority
> component
->selected_pair
.priority
) {
1113 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1114 "(prio:%" G_GUINT64_FORMAT
").", agent
, component
->id
, pair
->local
->foundation
,
1115 pair
->remote
->foundation
, pair
->priority
);
1117 if (component
->selected_pair
.keepalive
.tick_source
!= NULL
) {
1118 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
1119 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
1120 component
->selected_pair
.keepalive
.tick_source
= NULL
;
1123 memset (&component
->selected_pair
, 0, sizeof(CandidatePair
));
1124 component
->selected_pair
.local
= pair
->local
;
1125 component
->selected_pair
.remote
= pair
->remote
;
1126 component
->selected_pair
.priority
= pair
->priority
;
1128 priv_conn_keepalive_tick_unlocked (agent
);
1130 agent_signal_new_selected_pair (agent
, pair
->stream_id
, component
->id
, pair
->local
->foundation
, pair
->remote
->foundation
);
1138 * Updates the check list state.
1140 * Implements parts of the algorithm described in
1141 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
1142 * component, all checks have been completed and have
1143 * failed, mark that component's state to NICE_CHECK_FAILED.
1145 * Sends a component state changesignal via 'agent'.
1147 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
)
1150 /* note: emitting a signal might cause the client
1151 * to remove the stream, thus the component count
1152 * must be fetched before entering the loop*/
1153 guint c
, components
= stream
->n_components
;
1155 /* note: iterate the conncheck list for each component separately */
1156 for (c
= 0; c
< components
; c
++) {
1157 Component
*comp
= NULL
;
1158 if (!agent_find_component (agent
, stream
->id
, c
+1, NULL
, &comp
))
1161 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1162 CandidateCheckPair
*p
= i
->data
;
1164 if (p
->stream_id
== stream
->id
&&
1165 p
->component_id
== (c
+ 1)) {
1166 if (p
->state
!= NICE_CHECK_FAILED
)
1171 /* note: all checks have failed
1172 * Set the component to FAILED only if it actually had remote candidates
1174 if (i
== NULL
&& comp
!= NULL
&& comp
->remote_candidates
!= NULL
)
1175 agent_signal_component_state_change (agent
,
1177 (c
+ 1), /* component-id */
1178 NICE_COMPONENT_STATE_FAILED
);
1183 * Updates the check list state for a stream component.
1185 * Implements the algorithm described in ICE sect 8.1.2
1186 * "Updating States" (ID-19) as it applies to checks of
1187 * a certain component. If there are any nominated pairs,
1188 * ICE processing may be concluded, and component state is
1191 * Sends a component state changesignal via 'agent'.
1193 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
)
1196 guint succeeded
= 0, nominated
= 0;
1198 g_assert (component
);
1200 /* step: search for at least one nominated pair */
1201 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1202 CandidateCheckPair
*p
= i
->data
;
1203 if (p
->component_id
== component
->id
) {
1204 if (p
->state
== NICE_CHECK_SUCCEEDED
||
1205 p
->state
== NICE_CHECK_DISCOVERED
) {
1207 if (p
->nominated
== TRUE
) {
1214 if (nominated
> 0) {
1215 /* Only go to READY if no checks are left in progress. If there are
1216 * any that are kept, then this function will be called again when the
1217 * conncheck tick timer finishes them all */
1218 if (priv_prune_pending_checks (stream
, component
->id
) == 0) {
1219 agent_signal_component_state_change (agent
, stream
->id
,
1220 component
->id
, NICE_COMPONENT_STATE_READY
);
1223 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent
, nominated
, succeeded
, component
->id
);
1227 * The remote party has signalled that the candidate pair
1228 * described by 'component' and 'remotecand' is nominated
1231 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
)
1235 g_assert (component
);
1237 /* step: search for at least one nominated pair */
1238 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1239 CandidateCheckPair
*pair
= i
->data
;
1240 /* XXX: hmm, how to figure out to which local candidate the
1241 * check was sent to? let's mark all matching pairs
1242 * as nominated instead */
1243 if (pair
->remote
== remotecand
) {
1244 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent
, pair
, pair
->foundation
);
1245 pair
->nominated
= TRUE
;
1246 if (pair
->state
== NICE_CHECK_SUCCEEDED
||
1247 pair
->state
== NICE_CHECK_DISCOVERED
)
1248 priv_update_selected_pair (agent
, component
, pair
);
1249 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1255 * Creates a new connectivity check pair and adds it to
1256 * the agent's list of checks.
1258 static void priv_add_new_check_pair (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*local
, NiceCandidate
*remote
, NiceCheckState initial_state
, gboolean use_candidate
)
1260 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1261 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1263 stream
->conncheck_list
= g_slist_insert_sorted (stream
->conncheck_list
, pair
,
1264 (GCompareFunc
)conn_check_compare
);
1266 pair
->agent
= agent
;
1267 pair
->stream_id
= stream_id
;
1268 pair
->component_id
= component
->id
;;
1269 pair
->local
= local
;
1270 pair
->remote
= remote
;
1271 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s", local
->foundation
, remote
->foundation
);
1273 pair
->priority
= agent_candidate_pair_priority (agent
, local
, remote
);
1274 pair
->state
= initial_state
;
1275 nice_debug ("Agent %p : creating new pair %p state %d", agent
, pair
, initial_state
);
1276 pair
->nominated
= use_candidate
;
1277 pair
->controlling
= agent
->controlling_mode
;
1279 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent
, pair
, pair
->foundation
, stream_id
);
1281 /* implement the hard upper limit for number of
1282 checks (see sect 5.7.3 ICE ID-19): */
1283 if (agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
) {
1284 stream
->conncheck_list
=
1285 priv_limit_conn_check_list_size (stream
->conncheck_list
, agent
->max_conn_checks
);
1290 * Forms new candidate pairs by matching the new remote candidate
1291 * 'remote_cand' with all existing local candidates of 'component'.
1292 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1295 * @param agent context
1296 * @param component pointer to the component
1297 * @param remote remote candidate to match with
1299 * @return number of checks added, negative on fatal errors
1301 int conn_check_add_for_candidate (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*remote
)
1306 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1308 NiceCandidate
*local
= i
->data
;
1310 /* note: match pairs only if transport and address family are the same */
1311 if (local
->transport
== remote
->transport
&&
1312 local
->addr
.s
.addr
.sa_family
== remote
->addr
.s
.addr
.sa_family
) {
1314 /* note: do not create pairs where local candidate is
1315 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1316 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
1317 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1318 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1319 local
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
)
1322 priv_add_new_check_pair (agent
, stream_id
, component
, local
, remote
, NICE_CHECK_FROZEN
, FALSE
);
1324 if (component
->state
< NICE_COMPONENT_STATE_CONNECTED
) {
1325 agent_signal_component_state_change (agent
,
1328 NICE_COMPONENT_STATE_CONNECTING
);
1330 agent_signal_component_state_change (agent
,
1333 NICE_COMPONENT_STATE_CONNECTED
);
1342 * Frees the CandidateCheckPair structure pointer to
1343 * by 'user data'. Compatible with g_slist_foreach().
1345 void conn_check_free_item (gpointer data
, gpointer user_data
)
1347 CandidateCheckPair
*pair
= data
;
1348 g_assert (user_data
== NULL
);
1349 pair
->stun_message
.buffer
= NULL
;
1350 pair
->stun_message
.buffer_len
= 0;
1351 g_slice_free (CandidateCheckPair
, pair
);
1355 * Frees all resources of all connectivity checks.
1357 void conn_check_free (NiceAgent
*agent
)
1360 for (i
= agent
->streams
; i
; i
= i
->next
) {
1361 Stream
*stream
= i
->data
;
1363 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent
, stream
);
1364 if (stream
->conncheck_list
) {
1365 g_slist_foreach (stream
->conncheck_list
, conn_check_free_item
, NULL
);
1366 g_slist_free (stream
->conncheck_list
),
1367 stream
->conncheck_list
= NULL
;
1371 if (agent
->conncheck_timer_source
!= NULL
) {
1372 g_source_destroy (agent
->conncheck_timer_source
);
1373 g_source_unref (agent
->conncheck_timer_source
);
1374 agent
->conncheck_timer_source
= NULL
;
1379 * Prunes the list of connectivity checks for items related
1380 * to stream 'stream_id'.
1382 * @return TRUE on success, FALSE on a fatal error
1384 gboolean
conn_check_prune_stream (NiceAgent
*agent
, Stream
*stream
)
1386 CandidateCheckPair
*pair
;
1389 for (i
= stream
->conncheck_list
; i
; ) {
1390 GSList
*next
= i
->next
;
1393 g_assert (pair
->stream_id
== stream
->id
);
1395 stream
->conncheck_list
=
1396 g_slist_remove (stream
->conncheck_list
, pair
);
1397 conn_check_free_item (pair
, NULL
);
1399 if (!stream
->conncheck_list
)
1403 if (!stream
->conncheck_list
)
1404 conn_check_free (agent
);
1406 /* return FALSE if there was a memory allocation failure */
1407 if (stream
->conncheck_list
== NULL
&& i
!= NULL
)
1414 * Fills 'dest' with a username string for use in an outbound connectivity
1415 * checks. No more than 'dest_len' characters (including terminating
1416 * NULL) is ever written to the 'dest'.
1419 size_t priv_gen_username (NiceAgent
*agent
, guint component_id
,
1420 gchar
*remote
, gchar
*local
, uint8_t *dest
, guint dest_len
)
1423 gsize remote_len
= strlen (remote
);
1424 gsize local_len
= strlen (local
);
1426 if (remote_len
> 0 && local_len
> 0) {
1427 if (agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
&&
1428 dest_len
>= remote_len
+ local_len
+ 1) {
1429 memcpy (dest
, remote
, remote_len
);
1431 memcpy (dest
+ len
, ":", 1);
1433 memcpy (dest
+ len
, local
, local_len
);
1435 } else if ((agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1436 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1437 dest_len
>= remote_len
+ local_len
+ 4 ) {
1438 memcpy (dest
, remote
, remote_len
);
1440 memcpy (dest
+ len
, ":", 1);
1442 memcpy (dest
+ len
, local
, local_len
);
1445 memset (dest
+ len
, 0, 4 - (len
% 4));
1446 len
+= 4 - (len
% 4);
1448 } else if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1449 dest_len
>= remote_len
+ local_len
) {
1450 memcpy (dest
, remote
, remote_len
);
1452 memcpy (dest
+ len
, local
, local_len
);
1454 } else if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1455 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1456 gchar component_str
[10];
1457 guchar
*local_decoded
= NULL
;
1458 guchar
*remote_decoded
= NULL
;
1459 gsize local_decoded_len
;
1460 gsize remote_decoded_len
;
1464 g_snprintf (component_str
, sizeof(component_str
), "%d", component_id
);
1465 local_decoded
= g_base64_decode (local
, &local_decoded_len
);
1466 remote_decoded
= g_base64_decode (remote
, &remote_decoded_len
);
1468 total_len
= remote_decoded_len
+ local_decoded_len
+ 3 + 2*strlen (component_str
);
1469 padding
= 4 - (total_len
% 4);
1471 if (dest_len
>= total_len
+ padding
) {
1472 guchar pad_char
[1] = {0};
1475 memcpy (dest
, remote_decoded
, remote_decoded_len
);
1476 len
+= remote_decoded_len
;
1477 memcpy (dest
+ len
, ":", 1);
1479 memcpy (dest
+ len
, component_str
, strlen (component_str
));
1480 len
+= strlen (component_str
);
1482 memcpy (dest
+ len
, ":", 1);
1485 memcpy (dest
+ len
, local_decoded
, local_decoded_len
);
1486 len
+= local_decoded_len
;
1487 memcpy (dest
+ len
, ":", 1);
1489 memcpy (dest
+ len
, component_str
, strlen (component_str
));;
1490 len
+= strlen (component_str
);
1492 for (i
= 0; i
< padding
; i
++) {
1493 memcpy (dest
+ len
, pad_char
, 1);
1499 g_free (local_decoded
);
1500 g_free (remote_decoded
);
1508 * Fills 'dest' with a username string for use in an outbound connectivity
1509 * checks. No more than 'dest_len' characters (including terminating
1510 * NULL) is ever written to the 'dest'.
1513 size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
1514 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
1515 uint8_t *dest
, guint dest_len
, gboolean inbound
)
1517 gchar
*local_username
= NULL
;
1518 gchar
*remote_username
= NULL
;
1521 if (remote
&& remote
->username
) {
1522 remote_username
= remote
->username
;
1525 if (local
&& local
->username
) {
1526 local_username
= local
->username
;
1530 if (remote_username
== NULL
) {
1531 remote_username
= stream
->remote_ufrag
;
1533 if (local_username
== NULL
) {
1534 local_username
= stream
->local_ufrag
;
1538 if (local_username
&& remote_username
) {
1540 return priv_gen_username (agent
, component_id
,
1541 local_username
, remote_username
, dest
, dest_len
);
1543 return priv_gen_username (agent
, component_id
,
1544 remote_username
, local_username
, dest
, dest_len
);
1552 * Returns a password string for use in an outbound connectivity
1556 size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
1557 NiceCandidate
*remote
, uint8_t **password
)
1559 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
)
1562 if (remote
&& remote
->password
) {
1563 *password
= (uint8_t *)remote
->password
;
1564 return strlen (remote
->password
);
1568 *password
= (uint8_t *)stream
->remote_password
;
1569 return strlen (stream
->remote_password
);
1576 * Sends a connectivity check over candidate pair 'pair'.
1578 * @return zero on success, non-zero on error
1580 int conn_check_send (NiceAgent
*agent
, CandidateCheckPair
*pair
)
1583 /* note: following information is supplied:
1584 * - username (for USERNAME attribute)
1585 * - password (for MESSAGE-INTEGRITY)
1586 * - priority (for PRIORITY)
1587 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1588 * - USE-CANDIDATE (if sent by the controlling agent)
1592 nice_candidate_ice_priority_full (
1593 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
,
1595 pair
->local
->component_id
);
1597 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
1599 priv_create_username (agent
, agent_find_stream (agent
, pair
->stream_id
),
1600 pair
->component_id
, pair
->remote
, pair
->local
, uname
, sizeof (uname
), FALSE
);
1601 uint8_t *password
= NULL
;
1602 size_t password_len
= priv_get_password (agent
,
1603 agent_find_stream (agent
, pair
->stream_id
), pair
->remote
, &password
);
1605 bool controlling
= agent
->controlling_mode
;
1606 /* XXX: add API to support different nomination modes: */
1607 bool cand_use
= controlling
;
1609 unsigned int timeout
;
1611 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1612 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1613 password
= g_base64_decode ((gchar
*) password
, &password_len
);
1617 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1618 nice_address_to_string (&pair
->remote
->addr
, tmpbuf
);
1619 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
,
1621 nice_address_get_port (&pair
->remote
->addr
),
1622 g_socket_get_fd(((NiceSocket
*)pair
->local
->sockptr
)->fileno
),
1623 pair
->foundation
, pair
->component_id
,
1624 (unsigned long long)agent
->tie_breaker
,
1625 uname
, uname_len
, password
, password_len
, priority
);
1630 pair
->nominated
= controlling
;
1632 if (uname_len
> 0) {
1634 buffer_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
1635 &pair
->stun_message
, pair
->stun_buffer
, sizeof(pair
->stun_buffer
),
1636 uname
, uname_len
, password
, password_len
,
1637 cand_use
, controlling
, priority
,
1640 agent_to_ice_compatibility (agent
));
1642 nice_debug ("Agent %p: conncheck created %d - %p", agent
, buffer_len
, pair
->stun_message
.buffer
);
1644 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
1645 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
1649 if (buffer_len
> 0) {
1650 stun_timer_start (&pair
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
1651 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
1653 /* send the conncheck */
1654 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
1655 buffer_len
, (gchar
*)pair
->stun_buffer
);
1657 timeout
= stun_timer_remainder (&pair
->timer
);
1658 /* note: convert from milli to microseconds for g_time_val_add() */
1659 g_get_current_time (&pair
->next_tick
);
1660 g_time_val_add (&pair
->next_tick
, timeout
* 1000);
1662 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent
);
1663 pair
->stun_message
.buffer
= NULL
;
1664 pair
->stun_message
.buffer_len
= 0;
1668 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent
);
1669 pair
->stun_message
.buffer
= NULL
;
1670 pair
->stun_message
.buffer_len
= 0;
1678 * Implemented the pruning steps described in ICE sect 8.1.2
1679 * "Updating States" (ID-19) after a pair has been nominated.
1681 * @see priv_update_check_list_state_failed_components()
1683 static guint
priv_prune_pending_checks (Stream
*stream
, guint component_id
)
1686 guint64 highest_nominated_priority
= 0;
1687 guint in_progress
= 0;
1689 nice_debug ("Agent XXX: Finding highest priority for component %d",
1692 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1693 CandidateCheckPair
*p
= i
->data
;
1694 if (p
->component_id
== component_id
&&
1695 (p
->state
== NICE_CHECK_SUCCEEDED
||
1696 p
->state
== NICE_CHECK_DISCOVERED
) &&
1697 p
->nominated
== TRUE
){
1698 if (p
->priority
> highest_nominated_priority
) {
1699 highest_nominated_priority
= p
->priority
;
1704 nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
1705 "is %" G_GUINT64_FORMAT
, highest_nominated_priority
);
1707 /* step: cancel all FROZEN and WAITING pairs for the component */
1708 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1709 CandidateCheckPair
*p
= i
->data
;
1710 if (p
->component_id
== component_id
) {
1711 if (p
->state
== NICE_CHECK_FROZEN
||
1712 p
->state
== NICE_CHECK_WAITING
) {
1713 p
->state
= NICE_CHECK_CANCELLED
;
1714 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1717 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1718 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1719 if (highest_nominated_priority
!= 0 &&
1720 p
->priority
< highest_nominated_priority
) {
1721 p
->stun_message
.buffer
= NULL
;
1722 p
->stun_message
.buffer_len
= 0;
1723 p
->state
= NICE_CHECK_CANCELLED
;
1724 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1726 /* We must keep the higher priority pairs running because if a udp
1727 * packet was lost, we might end up using a bad candidate */
1728 nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %"
1729 G_GUINT64_FORMAT
" is higher than currently nominated pair %"
1730 G_GUINT64_FORMAT
, p
, p
->priority
, highest_nominated_priority
);
1741 * Schedules a triggered check after a successfully inbound
1742 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1744 * @param agent self pointer
1745 * @param component the check is related to
1746 * @param local_socket socket from which the inbound check was received
1747 * @param remote_cand remote candidate from which the inbound check was sent
1748 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1750 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
)
1753 NiceCandidate
*local
= NULL
;
1755 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1756 CandidateCheckPair
*p
= i
->data
;
1757 if (p
->component_id
== component
->id
&&
1758 p
->remote
== remote_cand
&&
1759 p
->local
->sockptr
== local_socket
) {
1761 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent
, p
);
1763 if (p
->state
== NICE_CHECK_WAITING
||
1764 p
->state
== NICE_CHECK_FROZEN
)
1765 priv_conn_check_initiate (agent
, p
);
1766 else if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1767 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1768 * we should cancel the existing one, instead we reset our timer, so
1769 * we'll resend the exiting transactions faster if needed...? :P
1771 nice_debug ("Agent %p : check already in progress, "
1772 "restarting the timer again?: %s ..", agent
,
1773 p
->timer_restarted
? "no" : "yes");
1774 if (!p
->timer_restarted
) {
1775 stun_timer_start (&p
->timer
, STUN_TIMER_DEFAULT_TIMEOUT
,
1776 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
);
1777 p
->timer_restarted
= TRUE
;
1780 else if (p
->state
== NICE_CHECK_SUCCEEDED
||
1781 p
->state
== NICE_CHECK_DISCOVERED
) {
1782 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent
);
1783 /* note: this is a bit unsure corner-case -- let's do the
1784 same state update as for processing responses to our own checks */
1785 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1787 /* note: to take care of the controlling-controlling case in
1788 * aggressive nomination mode, send a new triggered
1789 * check to nominate the pair */
1790 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
1791 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
||
1792 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
1793 agent
->controlling_mode
)
1794 priv_conn_check_initiate (agent
, p
);
1795 } else if (p
->state
== NICE_CHECK_FAILED
) {
1796 /* 7.2.1.4 Triggered Checks
1797 * If the state of the pair is Failed, it is changed to Waiting
1798 and the agent MUST create a new connectivity check for that
1799 pair (representing a new STUN Binding request transaction), by
1800 enqueueing the pair in the triggered check queue. */
1801 priv_conn_check_initiate (agent
, p
);
1804 /* note: the spec says the we SHOULD retransmit in-progress
1805 * checks immediately, but we won't do that now */
1811 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1813 if (local
->sockptr
== local_socket
)
1818 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent
, local
);
1819 priv_add_new_check_pair (agent
, stream
->id
, component
, local
, remote_cand
, NICE_CHECK_WAITING
, use_candidate
);
1823 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent
, remote_cand
);
1830 * Sends a reply to an successfully received STUN connectivity
1831 * check request. Implements parts of the ICE spec section 7.2 (STUN
1832 * Server Procedures).
1834 * @param agent context pointer
1835 * @param stream which stream (of the agent)
1836 * @param component which component (of the stream)
1837 * @param rcand remote candidate from which the request came, if NULL,
1838 * the response is sent immediately but no other processing is done
1839 * @param toaddr address to which reply is sent
1840 * @param socket the socket over which the request came
1841 * @param rbuf_len length of STUN message to send
1842 * @param rbuf buffer containing the STUN message to send
1843 * @param use_candidate whether the request had USE_CANDIDATE attribute
1845 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1847 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
)
1849 g_assert (rcand
== NULL
|| nice_address_equal(&rcand
->addr
, toaddr
) == TRUE
);
1852 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1853 nice_address_to_string (toaddr
, tmpbuf
);
1854 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent
,
1856 nice_address_get_port (toaddr
),
1857 g_socket_get_fd(socket
->fileno
),
1859 rcand
, component
->id
,
1860 (int)use_candidate
);
1863 nice_socket_send (socket
, toaddr
, rbuf_len
, (const gchar
*)rbuf
);
1866 /* note: upon successful check, make the reserve check immediately */
1867 priv_schedule_triggered_check (agent
, stream
, component
, socket
, rcand
, use_candidate
);
1870 priv_mark_pair_nominated (agent
, stream
, component
, rcand
);
1875 * Stores information of an incoming STUN connectivity check
1876 * for later use. This is only needed when a check is received
1877 * before we get information about the remote candidates (via
1878 * SDP or other signaling means).
1880 * @return non-zero on error, zero on success
1882 static int priv_store_pending_check (NiceAgent
*agent
, Component
*component
,
1883 const NiceAddress
*from
, NiceSocket
*socket
, uint8_t *username
,
1884 uint16_t username_len
, uint32_t priority
, gboolean use_candidate
)
1886 IncomingCheck
*icheck
;
1887 nice_debug ("Agent %p : Storing pending check.", agent
);
1889 if (component
->incoming_checks
&&
1890 g_slist_length (component
->incoming_checks
) >=
1891 NICE_AGENT_MAX_REMOTE_CANDIDATES
) {
1892 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent
);
1896 icheck
= g_slice_new0 (IncomingCheck
);
1897 component
->incoming_checks
= g_slist_append (component
->incoming_checks
, icheck
);
1898 icheck
->from
= *from
;
1899 icheck
->local_socket
= socket
;
1900 icheck
->priority
= priority
;
1901 icheck
->use_candidate
= use_candidate
;
1902 icheck
->username_len
= username_len
;
1903 icheck
->username
= NULL
;
1904 if (username_len
> 0)
1905 icheck
->username
= g_memdup (username
, username_len
);
1911 * Adds a new pair, discovered from an incoming STUN response, to
1912 * the connectivity check list.
1914 * @return created pair, or NULL on fatal (memory allocation) errors
1916 static CandidateCheckPair
*priv_add_peer_reflexive_pair (NiceAgent
*agent
, guint stream_id
, guint component_id
, NiceCandidate
*local_cand
, CandidateCheckPair
*parent_pair
)
1918 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1919 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1921 stream
->conncheck_list
= g_slist_append (stream
->conncheck_list
, pair
);
1922 pair
->agent
= agent
;
1923 pair
->stream_id
= stream_id
;
1924 pair
->component_id
= component_id
;;
1925 pair
->local
= local_cand
;
1926 pair
->remote
= parent_pair
->remote
;
1927 pair
->state
= NICE_CHECK_DISCOVERED
;
1928 nice_debug ("Agent %p : pair %p state DISCOVERED", agent
, pair
);
1929 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s",
1930 local_cand
->foundation
, parent_pair
->remote
->foundation
);
1931 if (agent
->controlling_mode
== TRUE
)
1932 pair
->priority
= nice_candidate_pair_priority (pair
->local
->priority
,
1933 pair
->remote
->priority
);
1935 pair
->priority
= nice_candidate_pair_priority (pair
->remote
->priority
,
1936 pair
->local
->priority
);
1937 pair
->nominated
= FALSE
;
1938 pair
->controlling
= agent
->controlling_mode
;
1939 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent
, pair
->foundation
);
1945 * Recalculates priorities of all candidate pairs. This
1946 * is required after a conflict in ICE roles.
1948 static void priv_recalculate_pair_priorities (NiceAgent
*agent
)
1952 for (i
= agent
->streams
; i
; i
= i
->next
) {
1953 Stream
*stream
= i
->data
;
1954 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
1955 CandidateCheckPair
*p
= j
->data
;
1956 p
->priority
= agent_candidate_pair_priority (agent
, p
->local
, p
->remote
);
1962 * Change the agent role if different from 'control'. Can be
1963 * initiated both by handling of incoming connectivity checks,
1964 * and by processing the responses to checks sent by us.
1966 static void priv_check_for_role_conflict (NiceAgent
*agent
, gboolean control
)
1968 /* role conflict, change mode; wait for a new conn. check */
1969 if (control
!= agent
->controlling_mode
) {
1970 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent
, control
);
1971 agent
->controlling_mode
= control
;
1972 /* the pair priorities depend on the roles, so recalculation
1974 priv_recalculate_pair_priorities (agent
);
1977 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent
, control
);
1981 * Checks whether the mapped address in connectivity check response
1982 * matches any of the known local candidates. If not, apply the
1983 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
1985 * @param agent context pointer
1986 * @param stream which stream (of the agent)
1987 * @param component which component (of the stream)
1988 * @param p the connectivity check pair for which we got a response
1989 * @param socketptr socket used to send the reply
1990 * @param mapped_sockaddr mapped address in the response
1992 * @return pointer to a new pair if one was created, otherwise NULL
1994 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
)
1996 CandidateCheckPair
*new_pair
= NULL
;
1999 gboolean local_cand_matches
= FALSE
;
2001 nice_address_set_from_sockaddr (&mapped
, mapped_sockaddr
);
2003 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
2004 NiceCandidate
*cand
= j
->data
;
2005 if (nice_address_equal (&mapped
, &cand
->addr
)) {
2006 local_cand_matches
= TRUE
;
2011 if (local_cand_matches
== TRUE
) {
2012 /* note: this is same as "adding to VALID LIST" in the spec
2014 p
->state
= NICE_CHECK_SUCCEEDED
;
2015 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent
, p
);
2016 priv_conn_check_unfreeze_related (agent
, stream
, p
);
2019 NiceCandidate
*cand
=
2020 discovery_add_peer_reflexive_candidate (agent
,
2027 p
->state
= NICE_CHECK_FAILED
;
2028 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
2030 /* step: add a new discovered pair (see ICE 7.1.2.2.2
2031 "Constructing a Valid Pair" (ID-19)) */
2032 new_pair
= priv_add_peer_reflexive_pair (agent
, stream
->id
, component
->id
, cand
, p
);
2033 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent
, p
, new_pair
);
2040 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
2041 * check transaction. If found, the reply is processed. Implements
2042 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
2044 * @return TRUE if a matching transaction is found
2046 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
)
2048 struct sockaddr_storage sockaddr
;
2049 socklen_t socklen
= sizeof (sockaddr
);
2051 StunUsageIceReturn res
;
2052 gboolean trans_found
= FALSE
;
2053 StunTransactionId discovery_id
;
2054 StunTransactionId response_id
;
2055 stun_message_id (resp
, response_id
);
2057 for (i
= stream
->conncheck_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2058 CandidateCheckPair
*p
= i
->data
;
2060 if (p
->stun_message
.buffer
) {
2061 stun_message_id (&p
->stun_message
, discovery_id
);
2063 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2064 res
= stun_usage_ice_conncheck_process (resp
,
2065 (struct sockaddr
*) &sockaddr
, &socklen
,
2066 agent_to_ice_compatibility (agent
));
2067 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
2068 "(controlling=%d).", agent
, p
, (int)res
, agent
->controlling_mode
);
2070 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2071 res
== STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS
) {
2072 /* case: found a matching connectivity check request */
2074 CandidateCheckPair
*ok_pair
= NULL
;
2076 nice_debug ("Agent %p : conncheck %p MATCHED.", agent
, p
);
2077 p
->stun_message
.buffer
= NULL
;
2078 p
->stun_message
.buffer_len
= 0;
2080 /* step: verify that response came from the same IP address we
2081 * sent the original request to (see 7.1.2.1. "Failure
2083 if (nice_address_equal (from
, &p
->remote
->addr
) != TRUE
) {
2084 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2085 gchar tmpbuf2
[INET6_ADDRSTRLEN
];
2087 p
->state
= NICE_CHECK_FAILED
;
2088 nice_debug ("Agent %p : conncheck %p FAILED"
2089 " (mismatch of source address).", agent
, p
);
2090 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
2091 nice_address_to_string (from
, tmpbuf2
);
2092 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent
,
2093 tmpbuf
, nice_address_get_port (&p
->remote
->addr
),
2094 tmpbuf2
, nice_address_get_port (from
));
2100 /* note: CONNECTED but not yet READY, see docs */
2102 /* step: handle the possible case of a peer-reflexive
2103 * candidate where the mapped-address in response does
2104 * not match any local candidate, see 7.1.2.2.1
2105 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
2107 if (res
== STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS
) {
2108 /* note: this is same as "adding to VALID LIST" in the spec
2110 p
->state
= NICE_CHECK_SUCCEEDED
;
2111 nice_debug ("Agent %p : Mapped address not found."
2112 " conncheck %p SUCCEEDED.", agent
, p
);
2113 priv_conn_check_unfreeze_related (agent
, stream
, p
);
2115 ok_pair
= priv_process_response_check_for_peer_reflexive(agent
,
2116 stream
, component
, p
, sockptr
, (struct sockaddr
*) &sockaddr
,
2117 local_candidate
, remote_candidate
);
2124 /* Do not step down to CONNECTED if we're already at state READY*/
2125 if (component
->state
!= NICE_COMPONENT_STATE_READY
) {
2126 /* step: notify the client of a new component state (must be done
2127 * before the possible check list state update step */
2128 agent_signal_component_state_change (agent
,
2129 stream
->id
, component
->id
, NICE_COMPONENT_STATE_CONNECTED
);
2133 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
2134 Nominated Flag" (ID-19) */
2135 if (ok_pair
->nominated
== TRUE
)
2136 priv_update_selected_pair (agent
, component
, ok_pair
);
2138 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
2139 states" and 8.1.2 "Updating States", ID-19) */
2140 priv_update_check_list_state_for_ready (agent
, stream
, component
);
2143 } else if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2144 /* case: role conflict error, need to restart with new role */
2145 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent
, p
);
2146 /* note: our role might already have changed due to an
2147 * incoming request, but if not, change role now;
2148 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
2149 priv_check_for_role_conflict (agent
, !p
->controlling
);
2151 p
->stun_message
.buffer
= NULL
;
2152 p
->stun_message
.buffer_len
= 0;
2153 p
->state
= NICE_CHECK_WAITING
;
2154 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
2157 /* case: STUN error, the check STUN context was freed */
2158 nice_debug ("Agent %p : conncheck %p FAILED.", agent
, p
);
2159 p
->stun_message
.buffer
= NULL
;
2160 p
->stun_message
.buffer_len
= 0;
2171 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2172 * transaction. If found, a reply is sent.
2174 * @return TRUE if a matching transaction is found
2176 static gboolean
priv_map_reply_to_discovery_request (NiceAgent
*agent
, StunMessage
*resp
)
2178 struct sockaddr_storage sockaddr
;
2179 socklen_t socklen
= sizeof (sockaddr
);
2180 struct sockaddr_storage alternate
;
2181 socklen_t alternatelen
= sizeof (sockaddr
);
2183 StunUsageBindReturn res
;
2184 gboolean trans_found
= FALSE
;
2185 StunTransactionId discovery_id
;
2186 StunTransactionId response_id
;
2187 stun_message_id (resp
, response_id
);
2189 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2190 CandidateDiscovery
*d
= i
->data
;
2192 if (d
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
&&
2193 d
->stun_message
.buffer
) {
2194 stun_message_id (&d
->stun_message
, discovery_id
);
2196 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2197 res
= stun_usage_bind_process (resp
, (struct sockaddr
*) &sockaddr
,
2198 &socklen
, (struct sockaddr
*) &alternate
, &alternatelen
);
2199 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
2200 agent
, d
, (int)res
);
2202 if (res
== STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER
) {
2203 /* handle alternate server */
2204 NiceAddress niceaddr
;
2205 nice_address_set_from_sockaddr (&niceaddr
,
2206 (struct sockaddr
*) &alternate
);
2207 d
->server
= niceaddr
;
2210 } else if (res
== STUN_USAGE_BIND_RETURN_SUCCESS
) {
2211 /* case: successful binding discovery, create a new local candidate */
2212 NiceAddress niceaddr
;
2213 nice_address_set_from_sockaddr (&niceaddr
,
2214 (struct sockaddr
*) &sockaddr
);
2216 discovery_add_server_reflexive_candidate (
2223 d
->stun_message
.buffer
= NULL
;
2224 d
->stun_message
.buffer_len
= 0;
2227 } else if (res
== STUN_USAGE_BIND_RETURN_ERROR
) {
2228 /* case: STUN error, the check STUN context was freed */
2229 d
->stun_message
.buffer
= NULL
;
2230 d
->stun_message
.buffer_len
= 0;
2242 static CandidateRefresh
*
2243 priv_add_new_turn_refresh (CandidateDiscovery
*cdisco
, NiceCandidate
*relay_cand
,
2246 CandidateRefresh
*cand
;
2247 NiceAgent
*agent
= cdisco
->agent
;
2249 cand
= g_slice_new0 (CandidateRefresh
);
2250 agent
->refresh_list
= g_slist_append (agent
->refresh_list
, cand
);
2252 cand
->nicesock
= cdisco
->nicesock
;
2253 cand
->relay_socket
= relay_cand
->sockptr
;
2254 cand
->server
= cdisco
->server
;
2255 cand
->turn
= cdisco
->turn
;
2256 cand
->stream
= cdisco
->stream
;
2257 cand
->component
= cdisco
->component
;
2258 cand
->agent
= cdisco
->agent
;
2259 memcpy (&cand
->stun_agent
, &cdisco
->stun_agent
, sizeof(StunAgent
));
2261 /* Use previous stun response for authentication credentials */
2262 if (cdisco
->stun_resp_msg
.buffer
!= NULL
) {
2263 memcpy(cand
->stun_resp_buffer
, cdisco
->stun_resp_buffer
,
2264 sizeof(cand
->stun_resp_buffer
));
2265 memcpy(&cand
->stun_resp_msg
, &cdisco
->stun_resp_msg
, sizeof(StunMessage
));
2266 cand
->stun_resp_msg
.buffer
= cand
->stun_resp_buffer
;
2267 cand
->stun_resp_msg
.agent
= NULL
;
2268 cand
->stun_resp_msg
.key
= NULL
;
2271 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2272 agent
, cand
, (lifetime
- 60) * 1000);
2274 /* step: also start the refresh timer */
2275 /* refresh should be sent 1 minute before it expires */
2276 cand
->timer_source
=
2277 agent_timeout_add_with_context (agent
, (lifetime
- 60) * 1000,
2278 priv_turn_allocate_refresh_tick
, cand
);
2280 nice_debug ("timer source is : %d", cand
->timer_source
);
2286 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2287 * transaction. If found, a reply is sent.
2289 * @return TRUE if a matching transaction is found
2291 static gboolean
priv_map_reply_to_relay_request (NiceAgent
*agent
, StunMessage
*resp
)
2293 struct sockaddr_storage sockaddr
;
2294 socklen_t socklen
= sizeof (sockaddr
);
2295 struct sockaddr_storage alternate
;
2296 socklen_t alternatelen
= sizeof (alternate
);
2297 struct sockaddr_storage relayaddr
;
2298 socklen_t relayaddrlen
= sizeof (relayaddr
);
2302 StunUsageTurnReturn res
;
2303 gboolean trans_found
= FALSE
;
2304 StunTransactionId discovery_id
;
2305 StunTransactionId response_id
;
2306 stun_message_id (resp
, response_id
);
2308 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2309 CandidateDiscovery
*d
= i
->data
;
2311 if (d
->type
== NICE_CANDIDATE_TYPE_RELAYED
&&
2312 d
->stun_message
.buffer
) {
2313 stun_message_id (&d
->stun_message
, discovery_id
);
2315 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2316 res
= stun_usage_turn_process (resp
,
2317 (struct sockaddr
*) &relayaddr
, &relayaddrlen
,
2318 (struct sockaddr
*) &sockaddr
, &socklen
,
2319 (struct sockaddr
*) &alternate
, &alternatelen
,
2320 &bandwidth
, &lifetime
, agent_to_turn_compatibility (agent
));
2321 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2322 agent
, d
, (int)res
);
2324 if (res
== STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER
) {
2325 /* handle alternate server */
2326 nice_address_set_from_sockaddr (&d
->server
,
2327 (struct sockaddr
*) &alternate
);
2328 nice_address_set_from_sockaddr (&d
->turn
->server
,
2329 (struct sockaddr
*) &alternate
);
2332 } else if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
||
2333 res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
) {
2334 /* case: successful allocate, create a new local candidate */
2335 NiceAddress niceaddr
;
2336 NiceCandidate
*relay_cand
;
2338 /* Server reflexive candidates are only valid for UDP sockets */
2339 if (res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
&&
2340 !nice_socket_is_reliable (d
->nicesock
)) {
2341 /* We also received our mapped address */
2342 nice_address_set_from_sockaddr (&niceaddr
,
2343 (struct sockaddr
*) &sockaddr
);
2345 discovery_add_server_reflexive_candidate (
2353 nice_address_set_from_sockaddr (&niceaddr
,
2354 (struct sockaddr
*) &relayaddr
);
2355 relay_cand
= discovery_add_relay_candidate (
2364 priv_add_new_turn_refresh (d
, relay_cand
, lifetime
);
2365 if (agent
->compatibility
== NICE_COMPATIBILITY_OC2007
||
2366 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) {
2367 /* These data are needed on TURN socket when sending requests,
2368 * but never reach nice_turn_socket_parse_recv() where it could
2369 * be read directly, as the socket does not exist when allocate
2370 * response arrives to _nice_agent_recv(). We must set them right
2371 * after socket gets created in discovery_add_relay_candidate(),
2372 * so we are doing it here. */
2373 nice_turn_socket_set_ms_realm(relay_cand
->sockptr
, &d
->stun_message
);
2374 nice_turn_socket_set_ms_connection_id(relay_cand
->sockptr
, resp
);
2378 d
->stun_message
.buffer
= NULL
;
2379 d
->stun_message
.buffer_len
= 0;
2382 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2384 uint8_t *sent_realm
= NULL
;
2385 uint8_t *recv_realm
= NULL
;
2386 uint16_t sent_realm_len
= 0;
2387 uint16_t recv_realm_len
= 0;
2389 sent_realm
= (uint8_t *) stun_message_find (&d
->stun_message
,
2390 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2391 recv_realm
= (uint8_t *) stun_message_find (resp
,
2392 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2394 /* check for unauthorized error response */
2395 if ((agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
||
2396 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
||
2397 agent
->compatibility
== NICE_COMPATIBILITY_OC2007R2
) &&
2398 stun_message_get_class (resp
) == STUN_ERROR
&&
2399 stun_message_find_error (resp
, &code
) ==
2400 STUN_MESSAGE_RETURN_SUCCESS
&&
2401 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2405 !(recv_realm_len
== sent_realm_len
&&
2406 sent_realm
!= NULL
&&
2407 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2408 d
->stun_resp_msg
= *resp
;
2409 memcpy (d
->stun_resp_buffer
, resp
->buffer
,
2410 stun_message_length (resp
));
2411 d
->stun_resp_msg
.buffer
= d
->stun_resp_buffer
;
2412 d
->stun_resp_msg
.buffer_len
= sizeof(d
->stun_resp_buffer
);
2415 /* case: a real unauthorized error */
2416 d
->stun_message
.buffer
= NULL
;
2417 d
->stun_message
.buffer_len
= 0;
2421 /* case: STUN error, the check STUN context was freed */
2422 d
->stun_message
.buffer
= NULL
;
2423 d
->stun_message
.buffer_len
= 0;
2437 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2438 * transaction. If found, a reply is sent.
2440 * @return TRUE if a matching transaction is found
2442 static gboolean
priv_map_reply_to_relay_refresh (NiceAgent
*agent
, StunMessage
*resp
)
2446 StunUsageTurnReturn res
;
2447 gboolean trans_found
= FALSE
;
2448 StunTransactionId refresh_id
;
2449 StunTransactionId response_id
;
2450 stun_message_id (resp
, response_id
);
2452 for (i
= agent
->refresh_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2453 CandidateRefresh
*cand
= i
->data
;
2455 if (cand
->stun_message
.buffer
) {
2456 stun_message_id (&cand
->stun_message
, refresh_id
);
2458 if (memcmp (refresh_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2459 res
= stun_usage_turn_refresh_process (resp
,
2460 &lifetime
, agent_to_turn_compatibility (cand
->agent
));
2461 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2462 agent
, cand
, (int)res
);
2463 if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
) {
2464 /* refresh should be sent 1 minute before it expires */
2465 cand
->timer_source
=
2466 agent_timeout_add_with_context (cand
->agent
, (lifetime
- 60) * 1000,
2467 priv_turn_allocate_refresh_tick
, cand
);
2469 g_source_destroy (cand
->tick_source
);
2470 g_source_unref (cand
->tick_source
);
2471 cand
->tick_source
= NULL
;
2472 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2474 uint8_t *sent_realm
= NULL
;
2475 uint8_t *recv_realm
= NULL
;
2476 uint16_t sent_realm_len
= 0;
2477 uint16_t recv_realm_len
= 0;
2479 sent_realm
= (uint8_t *) stun_message_find (&cand
->stun_message
,
2480 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2481 recv_realm
= (uint8_t *) stun_message_find (resp
,
2482 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2484 /* check for unauthorized error response */
2485 if (cand
->agent
->compatibility
== NICE_COMPATIBILITY_RFC5245
&&
2486 stun_message_get_class (resp
) == STUN_ERROR
&&
2487 stun_message_find_error (resp
, &code
) ==
2488 STUN_MESSAGE_RETURN_SUCCESS
&&
2489 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2493 !(recv_realm_len
== sent_realm_len
&&
2494 sent_realm
!= NULL
&&
2495 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2496 cand
->stun_resp_msg
= *resp
;
2497 memcpy (cand
->stun_resp_buffer
, resp
->buffer
,
2498 stun_message_length (resp
));
2499 cand
->stun_resp_msg
.buffer
= cand
->stun_resp_buffer
;
2500 cand
->stun_resp_msg
.buffer_len
= sizeof(cand
->stun_resp_buffer
);
2501 priv_turn_allocate_refresh_tick_unlocked (cand
);
2503 /* case: a real unauthorized error */
2504 refresh_cancel (cand
);
2507 /* case: STUN error, the check STUN context was freed */
2508 refresh_cancel (cand
);
2520 static gboolean
priv_map_reply_to_keepalive_conncheck (NiceAgent
*agent
,
2521 Component
*component
, StunMessage
*resp
)
2523 StunTransactionId conncheck_id
;
2524 StunTransactionId response_id
;
2525 stun_message_id (resp
, response_id
);
2527 if (component
->selected_pair
.keepalive
.stun_message
.buffer
) {
2528 stun_message_id (&component
->selected_pair
.keepalive
.stun_message
,
2530 if (memcmp (conncheck_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2531 nice_debug ("Agent %p : Keepalive for selected pair received.",
2533 if (component
->selected_pair
.keepalive
.tick_source
) {
2534 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
2535 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
2536 component
->selected_pair
.keepalive
.tick_source
= NULL
;
2538 component
->selected_pair
.keepalive
.stun_message
.buffer
= NULL
;
2550 Component
*component
;
2552 } conncheck_validater_data
;
2554 static bool conncheck_stun_validater (StunAgent
*agent
,
2555 StunMessage
*message
, uint8_t *username
, uint16_t username_len
,
2556 uint8_t **password
, size_t *password_len
, void *user_data
)
2558 conncheck_validater_data
*data
= (conncheck_validater_data
*) user_data
;
2560 gchar
*ufrag
= NULL
;
2563 gboolean msn_msoc_nice_compatibility
=
2564 data
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2565 data
->agent
->compatibility
== NICE_COMPATIBILITY_OC2007
;
2567 if (data
->agent
->compatibility
== NICE_COMPATIBILITY_OC2007
&&
2568 stun_message_get_class (message
) == STUN_RESPONSE
)
2569 i
= data
->component
->remote_candidates
;
2571 i
= data
->component
->local_candidates
;
2573 for (; i
; i
= i
->next
) {
2574 NiceCandidate
*cand
= i
->data
;
2578 ufrag
= cand
->username
;
2579 else if (data
->stream
)
2580 ufrag
= data
->stream
->local_ufrag
;
2581 ufrag_len
= ufrag
? strlen (ufrag
) : 0;
2583 if (ufrag
&& msn_msoc_nice_compatibility
)
2584 ufrag
= (gchar
*)g_base64_decode (ufrag
, &ufrag_len
);
2589 stun_debug ("Comparing username '");
2590 stun_debug_bytes (username
, username_len
);
2591 stun_debug ("' (%d) with '", username_len
);
2592 stun_debug_bytes (ufrag
, ufrag_len
);
2593 stun_debug ("' (%d) : %d\n",
2594 ufrag_len
, memcmp (username
, ufrag
, ufrag_len
));
2595 if (ufrag_len
> 0 && username_len
>= ufrag_len
&&
2596 memcmp (username
, ufrag
, ufrag_len
) == 0) {
2600 pass
= cand
->password
;
2601 else if(data
->stream
->local_password
[0])
2602 pass
= data
->stream
->local_password
;
2605 *password
= (uint8_t *) pass
;
2606 *password_len
= strlen (pass
);
2608 if (msn_msoc_nice_compatibility
) {
2609 data
->password
= g_base64_decode (pass
, password_len
);
2610 *password
= data
->password
;
2614 if (msn_msoc_nice_compatibility
)
2617 stun_debug ("Found valid username, returning password: '%s'\n", *password
);
2621 if (msn_msoc_nice_compatibility
)
2630 * Processing an incoming STUN message.
2632 * @param agent self pointer
2633 * @param stream stream the packet is related to
2634 * @param component component the packet is related to
2635 * @param socket socket from which the packet was received
2636 * @param from address of the sender
2637 * @param buf message contents
2638 * @param buf message length
2640 * @pre contents of 'buf' is a STUN message
2642 * @return XXX (what FALSE means exactly?)
2644 gboolean
conn_check_handle_inbound_stun (NiceAgent
*agent
, Stream
*stream
,
2645 Component
*component
, NiceSocket
*socket
, const NiceAddress
*from
,
2646 gchar
*buf
, guint len
)
2648 struct sockaddr_storage sockaddr
;
2649 uint8_t rbuf
[MAX_STUN_DATAGRAM_PAYLOAD
];
2651 size_t rbuf_len
= sizeof (rbuf
);
2652 bool control
= agent
->controlling_mode
;
2653 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
2656 uint16_t username_len
;
2659 StunValidationStatus valid
;
2660 conncheck_validater_data validater_data
= {agent
, stream
, component
, NULL
};
2662 NiceCandidate
*remote_candidate
= NULL
;
2663 NiceCandidate
*remote_candidate2
= NULL
;
2664 NiceCandidate
*local_candidate
= NULL
;
2665 gboolean discovery_msg
= FALSE
;
2667 nice_address_copy_to_sockaddr (from
, (struct sockaddr
*) &sockaddr
);
2669 /* note: contents of 'buf' already validated, so it is
2670 * a valid and fully received STUN message */
2674 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2675 nice_address_to_string (from
, tmpbuf
);
2676 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2677 agent
, stream
->id
, component
->id
, tmpbuf
, nice_address_get_port (from
), len
);
2681 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2683 valid
= stun_agent_validate (&agent
->stun_agent
, &req
,
2684 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2686 /* Check for discovery candidates stun agents */
2687 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2688 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2689 for (i
= agent
->discovery_list
; i
; i
= i
->next
) {
2690 CandidateDiscovery
*d
= i
->data
;
2691 if (d
->stream
== stream
&& d
->component
== component
&&
2692 d
->nicesock
== socket
) {
2693 valid
= stun_agent_validate (&d
->stun_agent
, &req
,
2694 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2696 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2699 discovery_msg
= TRUE
;
2704 /* Check for relay refresh stun agents */
2705 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2706 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2707 for (i
= agent
->refresh_list
; i
; i
= i
->next
) {
2708 CandidateRefresh
*r
= i
->data
;
2709 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r
->stream
,
2710 stream
, r
->component
, component
, r
->nicesock
, r
->relay_socket
, socket
);
2711 if (r
->stream
== stream
&& r
->component
== component
&&
2712 (r
->nicesock
== socket
|| r
->relay_socket
== socket
)) {
2713 valid
= stun_agent_validate (&r
->stun_agent
, &req
,
2714 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2715 nice_debug ("Validating gave %d", valid
);
2716 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2718 discovery_msg
= TRUE
;
2724 g_free (validater_data
.password
);
2726 if (valid
== STUN_VALIDATION_NOT_STUN
||
2727 valid
== STUN_VALIDATION_INCOMPLETE_STUN
||
2728 valid
== STUN_VALIDATION_BAD_REQUEST
)
2730 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2735 if (valid
== STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
) {
2736 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent
);
2738 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2739 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
) {
2740 rbuf_len
= stun_agent_build_unknown_attributes_error (&agent
->stun_agent
,
2741 &msg
, rbuf
, rbuf_len
, &req
);
2743 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2748 if (valid
== STUN_VALIDATION_UNAUTHORIZED
) {
2749 nice_debug ("Agent %p : Integrity check failed.", agent
);
2751 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2752 &req
, STUN_ERROR_UNAUTHORIZED
)) {
2753 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2754 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2755 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
)
2756 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2760 if (valid
== STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST
) {
2761 nice_debug ("Agent %p : Integrity check failed.", agent
);
2762 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2763 &req
, STUN_ERROR_BAD_REQUEST
)) {
2764 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2765 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2766 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
)
2767 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2772 username
= (uint8_t *) stun_message_find (&req
, STUN_ATTRIBUTE_USERNAME
,
2775 for (i
= component
->remote_candidates
; i
; i
= i
->next
) {
2776 NiceCandidate
*cand
= i
->data
;
2777 if (nice_address_equal (from
, &cand
->addr
)) {
2778 remote_candidate
= cand
;
2783 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2784 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2785 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2786 /* We need to find which local candidate was used */
2787 for (i
= component
->remote_candidates
;
2788 i
!= NULL
&& remote_candidate2
== NULL
; i
= i
->next
) {
2789 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
2790 gboolean inbound
= TRUE
;
2791 NiceCandidate
*rcand
= i
->data
;
2792 NiceCandidate
*lcand
= j
->data
;
2794 /* If we receive a response, then the username is local:remote */
2795 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
&&
2796 agent
->compatibility
!= NICE_COMPATIBILITY_OC2007
) {
2797 if (stun_message_get_class (&req
) == STUN_REQUEST
||
2798 stun_message_get_class (&req
) == STUN_INDICATION
) {
2805 uname_len
= priv_create_username (agent
, stream
,
2806 component
->id
, rcand
, lcand
,
2807 uname
, sizeof (uname
), inbound
);
2809 stun_debug ("Comparing username '");
2810 stun_debug_bytes (username
, username
? username_len
: 0);
2811 stun_debug ("' (%d) with '", username_len
);
2812 stun_debug_bytes (uname
, uname_len
);
2813 stun_debug ("' (%d) : %d\n",
2814 uname_len
, username
&& uname_len
== username_len
&&
2815 memcmp (username
, uname
, uname_len
) == 0);
2818 uname_len
== username_len
&&
2819 memcmp (uname
, username
, username_len
) == 0) {
2820 local_candidate
= lcand
;
2821 remote_candidate2
= rcand
;
2828 if (component
->remote_candidates
&&
2829 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
2830 local_candidate
== NULL
&&
2831 discovery_msg
== FALSE
) {
2832 /* if we couldn't match the username and the stun agent has
2833 IGNORE_CREDENTIALS then we have an integrity check failing.
2834 This could happen with the race condition of receiving connchecks
2835 before the remote candidates are added. Just drop the message, and let
2836 the retransmissions make it work. */
2837 nice_debug ("Agent %p : Username check failed.", agent
);
2841 if (valid
!= STUN_VALIDATION_SUCCESS
) {
2842 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent
, valid
);
2847 if (stun_message_get_class (&req
) == STUN_REQUEST
) {
2848 if ( agent
->compatibility
== NICE_COMPATIBILITY_MSN
2849 || agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2850 if (local_candidate
&& remote_candidate2
) {
2851 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2852 username
= (uint8_t *) stun_message_find (&req
,
2853 STUN_ATTRIBUTE_USERNAME
, &username_len
);
2854 uname_len
= priv_create_username (agent
, stream
,
2855 component
->id
, remote_candidate2
, local_candidate
,
2856 uname
, sizeof (uname
), FALSE
);
2857 memcpy (username
, uname
, username_len
);
2859 req
.key
= g_base64_decode ((gchar
*) remote_candidate2
->password
,
2861 } else if (agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2862 req
.key
= g_base64_decode ((gchar
*) local_candidate
->password
,
2866 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
2867 "Ignoring request", agent
);
2872 rbuf_len
= sizeof (rbuf
);
2873 res
= stun_usage_ice_conncheck_create_reply (&agent
->stun_agent
, &req
,
2874 &msg
, rbuf
, &rbuf_len
, (struct sockaddr
*) &sockaddr
, sizeof (sockaddr
),
2875 &control
, agent
->tie_breaker
,
2876 agent_to_ice_compatibility (agent
));
2878 if ( agent
->compatibility
== NICE_COMPATIBILITY_MSN
2879 || agent
->compatibility
== NICE_COMPATIBILITY_OC2007
) {
2883 if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
)
2884 priv_check_for_role_conflict (agent
, control
);
2886 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2887 res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2888 /* case 1: valid incoming request, send a reply/error */
2889 bool use_candidate
=
2890 stun_usage_ice_conncheck_use_candidate (&req
);
2891 uint32_t priority
= stun_usage_ice_conncheck_priority (&req
);
2893 if (agent
->controlling_mode
||
2894 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2895 agent
->compatibility
== NICE_COMPATIBILITY_MSN
||
2896 agent
->compatibility
== NICE_COMPATIBILITY_OC2007
)
2897 use_candidate
= TRUE
;
2899 if (stream
->initial_binding_request_received
!= TRUE
)
2900 agent_signal_initial_binding_request_received (agent
, stream
);
2902 if (component
->remote_candidates
&& remote_candidate
== NULL
) {
2903 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2904 "peer-reflexive candidate.", agent
);
2905 remote_candidate
= discovery_learn_remote_peer_reflexive_candidate (
2906 agent
, stream
, component
, priority
, from
, socket
,
2908 remote_candidate2
? remote_candidate2
: remote_candidate
);
2909 if(remote_candidate
)
2910 conn_check_add_for_candidate (agent
, stream
->id
, component
, remote_candidate
);
2913 priv_reply_to_conn_check (agent
, stream
, component
, remote_candidate
,
2914 from
, socket
, rbuf_len
, rbuf
, use_candidate
);
2916 if (component
->remote_candidates
== NULL
) {
2917 /* case: We've got a valid binding request to a local candidate
2918 * but we do not yet know remote credentials nor
2919 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2920 * immediately but postpone all other processing until
2921 * we get information about the remote candidates */
2923 /* step: send a reply immediately but postpone other processing */
2924 priv_store_pending_check (agent
, component
, from
, socket
,
2925 username
, username_len
, priority
, use_candidate
);
2928 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2929 agent
, strerror(errno
));
2933 /* case 2: not a new request, might be a reply... */
2934 gboolean trans_found
= FALSE
;
2936 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2938 /* step: let's try to match the response to an existing check context */
2939 if (trans_found
!= TRUE
)
2940 trans_found
= priv_map_reply_to_conn_check_request (agent
, stream
,
2941 component
, socket
, from
, local_candidate
, remote_candidate
, &req
);
2943 /* step: let's try to match the response to an existing discovery */
2944 if (trans_found
!= TRUE
)
2945 trans_found
= priv_map_reply_to_discovery_request (agent
, &req
);
2947 /* step: let's try to match the response to an existing turn allocate */
2948 if (trans_found
!= TRUE
)
2949 trans_found
= priv_map_reply_to_relay_request (agent
, &req
);
2951 /* step: let's try to match the response to an existing turn refresh */
2952 if (trans_found
!= TRUE
)
2953 trans_found
= priv_map_reply_to_relay_refresh (agent
, &req
);
2955 /* step: let's try to match the response to an existing keepalive conncheck */
2956 if (trans_found
!= TRUE
)
2957 trans_found
= priv_map_reply_to_keepalive_conncheck (agent
, component
,
2960 if (trans_found
!= TRUE
)
2961 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2962 "probably a keepalive.", agent
);