2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006, 2007 Collabora Ltd.
5 * Contact: Dafydd Harries
6 * (C) 2006, 2007 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 * Dafydd Harries, Collabora Ltd.
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
41 * @brief ICE connectivity checks
56 #include "agent-priv.h"
57 #include "conncheck.h"
58 #include "discovery.h"
59 #include "stun/usages/ice.h"
60 #include "stun/usages/bind.h"
61 #include "stun/usages/turn.h"
63 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
);
64 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
);
65 static void priv_prune_pending_checks (Stream
*stream
, guint component_id
);
66 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
);
67 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
);
68 static size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
69 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
70 uint8_t *dest
, guint dest_len
, gboolean inbound
);
71 static size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
72 NiceCandidate
*remote
, uint8_t **password
);
74 static int priv_timer_expired (GTimeVal
*timer
, GTimeVal
*now
)
76 return (now
->tv_sec
== timer
->tv_sec
) ?
77 now
->tv_usec
>= timer
->tv_usec
:
78 now
->tv_sec
>= timer
->tv_sec
;
82 * Finds the next connectivity check in WAITING state.
84 static CandidateCheckPair
*priv_conn_check_find_next_waiting (GSList
*conn_check_list
)
88 /* note: list is sorted in priority order to first waiting check has
89 * the highest priority */
91 for (i
= conn_check_list
; i
; i
= i
->next
) {
92 CandidateCheckPair
*p
= i
->data
;
93 if (p
->state
== NICE_CHECK_WAITING
)
101 * Initiates a new connectivity check for a ICE candidate pair.
103 * @return TRUE on success, FALSE on error
105 static gboolean
priv_conn_check_initiate (NiceAgent
*agent
, CandidateCheckPair
*pair
)
107 /* XXX: from ID-16 onwards, the checks should not be sent
108 * immediately, but be put into the "triggered queue",
109 * see "7.2.1.4 Triggered Checks"
111 g_get_current_time (&pair
->next_tick
);
112 g_time_val_add (&pair
->next_tick
, agent
->timer_ta
* 1000);
113 pair
->state
= NICE_CHECK_IN_PROGRESS
;
114 nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent
, pair
);
115 conn_check_send (agent
, pair
);
120 * Unfreezes the next connectivity check in the list. Follows the
121 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
122 * (ID-19), with some exceptions (see comments in code).
124 * See also sect 7.1.2.2.3 (Updating Pair States), and
125 * priv_conn_check_unfreeze_related().
127 * @return TRUE on success, and FALSE if no frozen candidates were found.
129 static gboolean
priv_conn_check_unfreeze_next (NiceAgent
*agent
)
131 CandidateCheckPair
*pair
= NULL
;
134 /* XXX: the unfreezing is implemented a bit differently than in the
135 * current ICE spec, but should still be interoperate:
136 * - checks are not grouped by foundation
137 * - one frozen check is unfrozen (lowest component-id, highest
141 for (i
= agent
->streams
; i
; i
= i
->next
) {
142 Stream
*stream
= i
->data
;
143 guint64 max_frozen_priority
= 0;
146 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
147 CandidateCheckPair
*p
= j
->data
;
149 /* XXX: the prio check could be removed as the pairs are sorted
152 if (p
->state
== NICE_CHECK_FROZEN
) {
153 if (p
->priority
> max_frozen_priority
) {
154 max_frozen_priority
= p
->priority
;
165 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent
, pair
, pair
->stream_id
, pair
->component_id
, pair
->foundation
);
166 pair
->state
= NICE_CHECK_WAITING
;
167 nice_debug ("Agent %p : pair %p state WAITING", agent
, pair
);
175 * Unfreezes the next next connectivity check in the list after
176 * check 'success_check' has succesfully completed.
178 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
180 * @param agent context
181 * @param ok_check a connectivity check that has just completed
183 * @return TRUE on success, and FALSE if no frozen candidates were found.
185 static void priv_conn_check_unfreeze_related (NiceAgent
*agent
, Stream
*stream
, CandidateCheckPair
*ok_check
)
191 g_assert (ok_check
->state
== NICE_CHECK_SUCCEEDED
);
193 g_assert (stream
->id
== ok_check
->stream_id
);
195 /* step: perform the step (1) of 'Updating Pair States' */
196 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
197 CandidateCheckPair
*p
= i
->data
;
199 if (p
->stream_id
== ok_check
->stream_id
) {
200 if (p
->state
== NICE_CHECK_FROZEN
&&
201 strcmp (p
->foundation
, ok_check
->foundation
) == 0) {
202 nice_debug ("Agent %p : Unfreezing check %p (after succesful check %p).", agent
, p
, ok_check
);
203 p
->state
= NICE_CHECK_WAITING
;
204 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
210 /* step: perform the step (2) of 'Updating Pair States' */
211 stream
= agent_find_stream (agent
, ok_check
->stream_id
);
212 if (stream_all_components_ready (stream
)) {
213 /* step: unfreeze checks from other streams */
214 for (i
= agent
->streams
; i
; i
= i
->next
) {
216 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
217 CandidateCheckPair
*p
= j
->data
;
219 if (p
->stream_id
== s
->id
&&
220 p
->stream_id
!= ok_check
->stream_id
) {
221 if (p
->state
== NICE_CHECK_FROZEN
&&
222 strcmp (p
->foundation
, ok_check
->foundation
) == 0) {
223 nice_debug ("Agent %p : Unfreezing check %p from stream %u (after succesful check %p).", agent
, p
, s
->id
, ok_check
);
224 p
->state
= NICE_CHECK_WAITING
;
225 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
231 /* note: only unfreeze check from one stream at a time */
238 priv_conn_check_unfreeze_next (agent
);
242 * Helper function for connectivity check timer callback that
243 * runs through the stream specific part of the state machine.
245 * @param schedule if TRUE, schedule a new check
247 * @return will return FALSE when no more pending timers.
249 static gboolean
priv_conn_check_tick_stream (Stream
*stream
, NiceAgent
*agent
, GTimeVal
*now
)
251 gboolean keep_timer_going
= FALSE
;
252 guint s_inprogress
= 0, s_succeeded
= 0, s_discovered
= 0,
253 s_nominated
= 0, s_waiting_for_nomination
= 0;
254 guint frozen
= 0, waiting
= 0;
257 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
258 CandidateCheckPair
*p
= i
->data
;
260 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
261 if (p
->stun_message
.buffer
== NULL
) {
262 nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent
);
263 p
->state
= NICE_CHECK_FAILED
;
264 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
265 } else if (priv_timer_expired (&p
->next_tick
, now
)) {
266 switch (stun_timer_refresh (&p
->timer
)) {
267 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
268 /* case: error, abort processing */
269 nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent
, p
);
270 p
->state
= NICE_CHECK_FAILED
;
271 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
272 p
->stun_message
.buffer
= NULL
;
273 p
->stun_message
.buffer_len
= 0;
275 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
277 /* case: not ready, so schedule a new timeout */
278 unsigned int timeout
= stun_timer_remainder (&p
->timer
);
279 nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
282 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
,
283 stun_message_length (&p
->stun_message
),
284 (gchar
*)p
->stun_buffer
);
287 /* note: convert from milli to microseconds for g_time_val_add() */
289 g_time_val_add (&p
->next_tick
, timeout
* 1000);
291 keep_timer_going
= TRUE
;
294 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
296 unsigned int timeout
= stun_timer_remainder (&p
->timer
);
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
;
309 if (p
->state
== NICE_CHECK_FROZEN
)
311 else if (p
->state
== NICE_CHECK_IN_PROGRESS
)
313 else if (p
->state
== NICE_CHECK_WAITING
)
315 else if (p
->state
== NICE_CHECK_SUCCEEDED
)
317 else if (p
->state
== NICE_CHECK_DISCOVERED
)
320 if ((p
->state
== NICE_CHECK_SUCCEEDED
|| p
->state
== NICE_CHECK_DISCOVERED
)
323 else if ((p
->state
== NICE_CHECK_SUCCEEDED
||
324 p
->state
== NICE_CHECK_DISCOVERED
) && !p
->nominated
)
325 ++s_waiting_for_nomination
;
328 /* note: keep the timer going as long as there is work to be done */
330 keep_timer_going
= TRUE
;
332 /* note: if some components have established connectivity,
333 * but yet no nominated pair, keep timer going */
334 if (s_nominated
< stream
->n_components
&&
335 s_waiting_for_nomination
) {
336 keep_timer_going
= TRUE
;
337 if (agent
->controlling_mode
) {
339 for (n
= 0; n
< stream
->n_components
; n
++) {
340 for (k
= stream
->conncheck_list
; k
; k
= k
->next
) {
341 CandidateCheckPair
*p
= k
->data
;
342 /* note: highest priority item selected (list always sorted) */
343 if (p
->state
== NICE_CHECK_SUCCEEDED
||
344 p
->state
== NICE_CHECK_DISCOVERED
) {
345 nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent
, p
);
347 priv_conn_check_initiate (agent
, p
);
348 break; /* move to the next component */
355 static int tick_counter
= 0;
356 if (tick_counter
++ % 50 == 0 || keep_timer_going
!= TRUE
)
357 nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
358 "%u waiting, %u succeeded, %u discovered, %u nominated, "
359 "%u waiting-for-nom.", agent
,
360 tick_counter
, frozen
, s_inprogress
, waiting
, s_succeeded
,
361 s_discovered
, s_nominated
, s_waiting_for_nomination
);
364 return keep_timer_going
;
370 * Timer callback that handles initiating and managing connectivity
371 * checks (paced by the Ta timer).
373 * This function is designed for the g_timeout_add() interface.
375 * @return will return FALSE when no more pending timers.
377 static gboolean
priv_conn_check_tick_unlocked (gpointer pointer
)
379 CandidateCheckPair
*pair
= NULL
;
380 NiceAgent
*agent
= pointer
;
381 gboolean keep_timer_going
= FALSE
;
385 /* step: process ongoing STUN transactions */
386 g_get_current_time (&now
);
388 /* step: find the highest priority waiting check and send it */
389 for (i
= agent
->streams
; i
; i
= i
->next
) {
390 Stream
*stream
= i
->data
;
392 pair
= priv_conn_check_find_next_waiting (stream
->conncheck_list
);
398 priv_conn_check_initiate (agent
, pair
);
399 keep_timer_going
= TRUE
;
401 keep_timer_going
= priv_conn_check_unfreeze_next (agent
);
404 for (j
= agent
->streams
; j
; j
= j
->next
) {
405 Stream
*stream
= j
->data
;
407 priv_conn_check_tick_stream (stream
, agent
, &now
);
409 keep_timer_going
= res
;
412 /* step: stop timer if no work left */
413 if (keep_timer_going
!= TRUE
) {
414 nice_debug ("Agent %p : %s: stopping conncheck timer", agent
, G_STRFUNC
);
415 for (i
= agent
->streams
; i
; i
= i
->next
) {
416 Stream
*stream
= i
->data
;
417 priv_update_check_list_failed_components (agent
, stream
);
418 for (j
= stream
->components
; j
; j
= j
->next
) {
419 Component
*component
= j
->data
;
420 priv_update_check_list_state_for_ready (agent
, stream
, component
);
422 stream
->conncheck_state
= NICE_CHECKLIST_COMPLETED
;
425 /* Stopping the timer so destroy the source.. this will allow
426 the timer to be reset if we get a set_remote_candidates after this
428 if (agent
->conncheck_timer_source
!= NULL
) {
429 g_source_destroy (agent
->conncheck_timer_source
);
430 g_source_unref (agent
->conncheck_timer_source
);
431 agent
->conncheck_timer_source
= NULL
;
434 /* XXX: what to signal, is all processing now really done? */
435 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent
);
438 return keep_timer_going
;
441 static gboolean
priv_conn_check_tick (gpointer pointer
)
443 NiceAgent
*agent
= pointer
;
446 g_static_rec_mutex_lock (&agent
->mutex
);
447 ret
= priv_conn_check_tick_unlocked (pointer
);
448 g_static_rec_mutex_unlock (&agent
->mutex
);
453 static gboolean
priv_conn_keepalive_retransmissions_tick (gpointer pointer
)
455 CandidatePair
*pair
= (CandidatePair
*) pointer
;
457 g_static_rec_mutex_lock (&pair
->keepalive
.agent
->mutex
);
459 g_source_destroy (pair
->keepalive
.tick_source
);
460 g_source_unref (pair
->keepalive
.tick_source
);
461 pair
->keepalive
.tick_source
= NULL
;
463 switch (stun_timer_refresh (&pair
->keepalive
.timer
)) {
464 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
466 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
467 "peer probably lost connection", pair
->keepalive
.agent
);
468 agent_signal_component_state_change (pair
->keepalive
.agent
,
469 pair
->keepalive
.stream_id
, pair
->keepalive
.component_id
,
470 NICE_COMPONENT_STATE_FAILED
);
472 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
474 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
475 stun_message_length (&pair
->keepalive
.stun_message
),
476 (gchar
*)pair
->keepalive
.stun_buffer
);
478 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
479 pair
->keepalive
.agent
);
480 pair
->keepalive
.tick_source
=
481 agent_timeout_add_with_context (pair
->keepalive
.agent
,
482 stun_timer_remainder (&pair
->keepalive
.timer
),
483 priv_conn_keepalive_retransmissions_tick
, pair
);
485 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
486 pair
->keepalive
.tick_source
=
487 agent_timeout_add_with_context (pair
->keepalive
.agent
,
488 stun_timer_remainder (&pair
->keepalive
.timer
),
489 priv_conn_keepalive_retransmissions_tick
, pair
);
494 g_static_rec_mutex_unlock (&pair
->keepalive
.agent
->mutex
);
500 * Timer callback that handles initiating and managing connectivity
501 * checks (paced by the Ta timer).
503 * This function is designed for the g_timeout_add() interface.
505 * @return will return FALSE when no more pending timers.
507 static gboolean
priv_conn_keepalive_tick (gpointer pointer
)
509 NiceAgent
*agent
= pointer
;
512 gboolean ret
= FALSE
;
515 g_static_rec_mutex_lock (&agent
->mutex
);
517 /* case 1: session established and media flowing
518 * (ref ICE sect 10 "Keepalives" ID-19) */
519 for (i
= agent
->streams
; i
; i
= i
->next
) {
521 Stream
*stream
= i
->data
;
522 for (j
= stream
->components
; j
; j
= j
->next
) {
523 Component
*component
= j
->data
;
524 if (component
->selected_pair
.local
!= NULL
&&
525 component
->media_after_tick
!= TRUE
) {
526 CandidatePair
*p
= &component
->selected_pair
;
527 struct sockaddr sockaddr
;
529 memset (&sockaddr
, 0, sizeof (sockaddr
));
530 nice_address_copy_to_sockaddr (&p
->remote
->addr
, &sockaddr
);
532 if (agent
->compatibility
!= NICE_COMPATIBILITY_DRAFT19
) {
533 guint32 priority
= nice_candidate_ice_priority_full (
534 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
, 1,
535 p
->local
->component_id
);
536 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
538 priv_create_username (agent
, agent_find_stream (agent
, stream
->id
),
539 component
->id
, p
->remote
, p
->local
, uname
, sizeof (uname
),
541 uint8_t *password
= NULL
;
542 size_t password_len
= priv_get_password (agent
,
543 agent_find_stream (agent
, stream
->id
), p
->remote
, &password
);
544 gchar tmpbuf
[INET6_ADDRSTRLEN
];
546 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
547 password
= g_base64_decode ((gchar
*) password
, &password_len
);
550 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
551 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
552 "socket=%u (c-id:%u), username='%s' (%d), "
553 "password='%s' (%d), priority=%u.", agent
,
554 tmpbuf
, ntohs(((struct sockaddr_in
*)(&sockaddr
))->sin_port
),
555 p
->local
->sockptr
->fileno
, component
->id
,
556 uname
, uname_len
, password
, password_len
, priority
);
559 buf_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
560 &p
->keepalive
.stun_message
, p
->keepalive
.stun_buffer
,
561 sizeof(p
->keepalive
.stun_buffer
),
562 uname
, uname_len
, password
, password_len
,
563 agent
->controlling_mode
, agent
->controlling_mode
, priority
,
565 agent_to_ice_compatibility (agent
));
567 nice_debug ("Agent %p: conncheck created %d - %p",
568 agent
, buf_len
, p
->keepalive
.stun_message
.buffer
);
570 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
574 stun_timer_start (&p
->keepalive
.timer
);
576 /* send the conncheck */
577 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
,
578 buf_len
, (gchar
*)p
->keepalive
.stun_buffer
);
580 if (p
->keepalive
.tick_source
!= NULL
) {
581 g_source_destroy (p
->keepalive
.tick_source
);
582 g_source_unref (p
->keepalive
.tick_source
);
583 p
->keepalive
.tick_source
= NULL
;
586 p
->keepalive
.stream_id
= stream
->id
;
587 p
->keepalive
.component_id
= component
->id
;
588 p
->keepalive
.agent
= agent
;
590 p
->keepalive
.tick_source
=
591 agent_timeout_add_with_context (p
->keepalive
.agent
,
592 stun_timer_remainder (&p
->keepalive
.timer
),
593 priv_conn_keepalive_retransmissions_tick
, p
);
599 buf_len
= stun_usage_bind_keepalive (&agent
->stun_agent
,
600 &p
->keepalive
.stun_message
, p
->keepalive
.stun_buffer
,
601 sizeof(p
->keepalive
.stun_buffer
));
603 nice_socket_send (p
->local
->sockptr
, &p
->remote
->addr
, buf_len
,
604 (gchar
*)p
->keepalive
.stun_buffer
);
606 nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
607 agent
, p
, (int) buf_len
);
612 component
->media_after_tick
= FALSE
;
616 /* case 2: connectivity establishment ongoing
617 * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */
618 for (i
= agent
->streams
; i
; i
= i
->next
) {
619 Stream
*stream
= i
->data
;
620 if (stream
->conncheck_state
== NICE_CHECKLIST_RUNNING
) {
621 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
622 CandidateCheckPair
*p
= j
->data
;
624 nice_debug ("Agent %p : resending STUN-CC to keep the candidate alive (pair %p).", agent
, p
);
625 conn_check_send (agent
, p
);
631 nice_debug ("Agent %p : %s: stopping keepalive timer", agent
, G_STRFUNC
);
638 g_static_rec_mutex_unlock (&agent
->mutex
);
642 static gboolean
priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer
)
644 CandidateRefresh
*cand
= (CandidateRefresh
*) pointer
;
646 g_static_rec_mutex_lock (&cand
->agent
->mutex
);
648 g_source_destroy (cand
->tick_source
);
649 g_source_unref (cand
->tick_source
);
650 cand
->tick_source
= NULL
;
652 switch (stun_timer_refresh (&cand
->timer
)) {
653 case STUN_USAGE_TIMER_RETURN_TIMEOUT
:
655 refresh_cancel (cand
);
657 case STUN_USAGE_TIMER_RETURN_RETRANSMIT
:
659 nice_socket_send (cand
->nicesock
, &cand
->server
,
660 stun_message_length (&cand
->stun_message
), (gchar
*)cand
->stun_buffer
);
662 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
663 stun_timer_remainder (&cand
->timer
),
664 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
666 case STUN_USAGE_TIMER_RETURN_SUCCESS
:
667 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
668 stun_timer_remainder (&cand
->timer
),
669 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
674 g_static_rec_mutex_unlock (&cand
->agent
->mutex
);
678 static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh
*cand
)
684 size_t buffer_len
= 0;
686 username
= (uint8_t *)cand
->turn
->username
;
687 username_len
= (size_t) strlen (cand
->turn
->username
);
688 password
= (uint8_t *)cand
->turn
->password
;
689 password_len
= (size_t) strlen (cand
->turn
->password
);
691 if (cand
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
692 username
= g_base64_decode ((gchar
*)username
, &username_len
);
693 password
= g_base64_decode ((gchar
*)password
, &password_len
);
696 buffer_len
= stun_usage_turn_create_refresh (&cand
->stun_agent
,
697 &cand
->stun_message
, cand
->stun_buffer
, sizeof(cand
->stun_buffer
),
698 cand
->stun_resp_msg
.buffer
== NULL
? NULL
: &cand
->stun_resp_msg
, -1,
699 username
, username_len
,
700 password
, password_len
,
701 agent_to_turn_compatibility (cand
->agent
));
703 if (cand
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
704 g_free (cand
->msn_turn_username
);
705 g_free (cand
->msn_turn_password
);
706 cand
->msn_turn_username
= username
;
707 cand
->msn_turn_password
= password
;
710 nice_debug ("Agent %p : Sending allocate Refresh %d", cand
->agent
, buffer_len
);
712 if (buffer_len
> 0) {
713 stun_timer_start (&cand
->timer
);
715 /* send the refresh */
716 nice_socket_send (cand
->nicesock
, &cand
->server
,
717 buffer_len
, (gchar
*)cand
->stun_buffer
);
719 if (cand
->tick_source
!= NULL
) {
720 g_source_destroy (cand
->tick_source
);
721 g_source_unref (cand
->tick_source
);
722 cand
->tick_source
= NULL
;
725 cand
->tick_source
= agent_timeout_add_with_context (cand
->agent
,
726 stun_timer_remainder (&cand
->timer
),
727 priv_turn_allocate_refresh_retransmissions_tick
, cand
);
734 * Timer callback that handles refreshing TURN allocations
736 * This function is designed for the g_timeout_add() interface.
738 * @return will return FALSE when no more pending timers.
740 static gboolean
priv_turn_allocate_refresh_tick (gpointer pointer
)
742 CandidateRefresh
*cand
= (CandidateRefresh
*) pointer
;
744 g_static_rec_mutex_lock (&cand
->agent
->mutex
);
745 priv_turn_allocate_refresh_tick_unlocked (cand
);
746 g_static_rec_mutex_unlock (&cand
->agent
->mutex
);
753 * Initiates the next pending connectivity check.
755 * @return TRUE if a pending check was scheduled
757 gboolean
conn_check_schedule_next (NiceAgent
*agent
)
759 gboolean res
= priv_conn_check_unfreeze_next (agent
);
760 nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent
, res
);
762 if (agent
->discovery_unsched_items
> 0)
763 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent
);
766 /* step: call once imediately */
767 res
= priv_conn_check_tick_unlocked ((gpointer
) agent
);
768 nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent
, res
);
770 /* step: schedule timer if not running yet */
771 if (res
&& agent
->conncheck_timer_source
== NULL
) {
772 agent
->conncheck_timer_source
= agent_timeout_add_with_context (agent
, agent
->timer_ta
, priv_conn_check_tick
, agent
);
775 /* step: also start the keepalive timer */
776 if (agent
->keepalive_timer_source
== NULL
) {
777 agent
->keepalive_timer_source
= agent_timeout_add_with_context (agent
, NICE_AGENT_TIMER_TR_DEFAULT
, priv_conn_keepalive_tick
, agent
);
782 nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent
, res
);
787 * Compares two connectivity check items. Checkpairs are sorted
788 * in descending priority order, with highest priority item at
789 * the start of the list.
791 gint
conn_check_compare (const CandidateCheckPair
*a
, const CandidateCheckPair
*b
)
793 if (a
->priority
> b
->priority
)
795 else if (a
->priority
< b
->priority
)
801 * Preprocesses a new connectivity check by going through list
802 * of a any stored early incoming connectivity checks from
803 * the remote peer. If a matching incoming check has been already
804 * received, update the state of the new outgoing check 'pair'.
806 * @param agent context pointer
807 * @param stream which stream (of the agent)
808 * @param component pointer to component object to which 'pair'has been added
809 * @param pair newly added connectivity check
811 static void priv_preprocess_conn_check_pending_data (NiceAgent
*agent
, Stream
*stream
, Component
*component
, CandidateCheckPair
*pair
)
814 for (i
= component
->incoming_checks
; i
; i
= i
->next
) {
815 IncomingCheck
*icheck
= i
->data
;
816 if (nice_address_equal (&icheck
->from
, &pair
->remote
->addr
) &&
817 icheck
->local_socket
== pair
->local
->sockptr
) {
818 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
);
819 if (icheck
->use_candidate
)
820 priv_mark_pair_nominated (agent
, stream
, component
, pair
->remote
);
821 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, pair
->remote
, icheck
->use_candidate
);
827 * Handle any processing steps for connectivity checks after
828 * remote candidates have been set. This function handles
829 * the special case where answerer has sent us connectivity
830 * checks before the answer (containing candidate information),
831 * reaches us. The special case is documented in sect 7.2
832 * if ICE spec (ID-19).
834 void conn_check_remote_candidates_set(NiceAgent
*agent
)
836 GSList
*i
, *j
, *k
, *l
;
837 for (i
= agent
->streams
; i
; i
= i
->next
) {
838 Stream
*stream
= i
->data
;
839 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
840 CandidateCheckPair
*pair
= j
->data
;
841 Component
*component
= stream_find_component_by_id (stream
, pair
->component_id
);
842 gboolean match
= FALSE
;
844 /* performn delayed processing of spec steps section 7.2.1.4,
845 and section 7.2.1.5 */
846 priv_preprocess_conn_check_pending_data (agent
, stream
, component
, pair
);
848 for (k
= component
->incoming_checks
; k
; k
= k
->next
) {
849 IncomingCheck
*icheck
= k
->data
;
850 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
851 * be handled separately */
852 for (l
= component
->remote_candidates
; l
; l
= l
->next
) {
853 NiceCandidate
*cand
= l
->data
;
854 if (nice_address_equal (&icheck
->from
, &cand
->addr
)) {
860 /* note: we have gotten an incoming connectivity check from
861 * an address that is not a known remote candidate */
862 NiceCandidate
*candidate
=
863 discovery_learn_remote_peer_reflexive_candidate (agent
,
868 icheck
->local_socket
,
871 priv_schedule_triggered_check (agent
, stream
, component
, icheck
->local_socket
, candidate
, icheck
->use_candidate
);
880 * Enforces the upper limit for connectivity checks as described
881 * in ICE spec section 5.7.3 (ID-19). See also
882 * conn_check_add_for_candidate().
884 static GSList
*priv_limit_conn_check_list_size (GSList
*conncheck_list
, guint upper_limit
)
886 guint list_len
= g_slist_length (conncheck_list
);
888 GSList
*result
= conncheck_list
;
890 if (list_len
> upper_limit
) {
891 c
= list_len
- upper_limit
;
893 /* case: delete whole list */
894 g_slist_foreach (conncheck_list
, conn_check_free_item
, NULL
);
895 g_slist_free (conncheck_list
),
899 /* case: remove 'c' items from list end (lowest priority) */
903 i
= g_slist_nth (conncheck_list
, c
- 1);
909 /* delete the rest of the connectivity check list */
910 g_slist_foreach (tmp
, conn_check_free_item
, NULL
);
920 * Changes the selected pair for the component if 'pair' is nominated
921 * and has higher priority than the currently selected pair. See
922 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
924 static gboolean
priv_update_selected_pair (NiceAgent
*agent
, Component
*component
, CandidateCheckPair
*pair
)
926 g_assert (component
);
928 if (pair
->priority
> component
->selected_pair
.priority
) {
929 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
930 "(prio:%lu).", agent
, component
->id
, pair
->local
->foundation
,
931 pair
->remote
->foundation
, (long unsigned)pair
->priority
);
933 if (component
->selected_pair
.keepalive
.tick_source
!= NULL
) {
934 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
935 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
936 component
->selected_pair
.keepalive
.tick_source
= NULL
;
939 memset (&component
->selected_pair
, 0, sizeof(CandidatePair
));
940 component
->selected_pair
.local
= pair
->local
;
941 component
->selected_pair
.remote
= pair
->remote
;
942 component
->selected_pair
.priority
= pair
->priority
;
944 agent_signal_new_selected_pair (agent
, pair
->stream_id
, component
->id
, pair
->local
->foundation
, pair
->remote
->foundation
);
952 * Updates the check list state.
954 * Implements parts of the algorithm described in
955 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
956 * component, all checks have been completed and have
957 * failed, mark that component's state to NICE_CHECK_FAILED.
959 * Sends a component state changesignal via 'agent'.
961 static void priv_update_check_list_failed_components (NiceAgent
*agent
, Stream
*stream
)
964 /* note: emitting a signal might cause the client
965 * to remove the stream, thus the component count
966 * must be fetched before entering the loop*/
967 guint c
, components
= stream
->n_components
;
969 /* note: iterate the conncheck list for each component separately */
970 for (c
= 0; c
< components
; c
++) {
971 Component
*comp
= NULL
;
972 agent_find_component (agent
, stream
->id
, c
+1, NULL
, &comp
);
974 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
975 CandidateCheckPair
*p
= i
->data
;
977 if (p
->stream_id
== stream
->id
&&
978 p
->component_id
== (c
+ 1)) {
979 if (p
->state
!= NICE_CHECK_FAILED
)
984 /* note: all checks have failed
985 * Set the component to FAILED only if it actually had remote candidates
987 if (i
== NULL
&& comp
!= NULL
&& comp
->remote_candidates
!= NULL
)
988 agent_signal_component_state_change (agent
,
990 (c
+ 1), /* component-id */
991 NICE_COMPONENT_STATE_FAILED
);
996 * Updates the check list state for a stream component.
998 * Implements the algorithm described in ICE sect 8.1.2
999 * "Updating States" (ID-19) as it applies to checks of
1000 * a certain component. If there are any nominated pairs,
1001 * ICE processing may be concluded, and component state is
1004 * Sends a component state changesignal via 'agent'.
1006 static void priv_update_check_list_state_for_ready (NiceAgent
*agent
, Stream
*stream
, Component
*component
)
1009 guint succeeded
= 0, nominated
= 0;
1011 g_assert (component
);
1013 /* step: search for at least one nominated pair */
1014 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1015 CandidateCheckPair
*p
= i
->data
;
1016 if (p
->component_id
== component
->id
) {
1017 if (p
->state
== NICE_CHECK_SUCCEEDED
||
1018 p
->state
== NICE_CHECK_DISCOVERED
) {
1020 if (p
->nominated
== TRUE
) {
1022 priv_prune_pending_checks (stream
, p
->component_id
);
1023 agent_signal_component_state_change (agent
,
1026 NICE_COMPONENT_STATE_READY
);
1032 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent
, nominated
, succeeded
, component
->id
);
1036 * The remote party has signalled that the candidate pair
1037 * described by 'component' and 'remotecand' is nominated
1040 static void priv_mark_pair_nominated (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceCandidate
*remotecand
)
1044 g_assert (component
);
1046 /* step: search for at least one nominated pair */
1047 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1048 CandidateCheckPair
*pair
= i
->data
;
1049 /* XXX: hmm, how to figure out to which local candidate the
1050 * check was sent to? let's mark all matching pairs
1051 * as nominated instead */
1052 if (pair
->remote
== remotecand
) {
1053 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent
, pair
, pair
->foundation
);
1054 pair
->nominated
= TRUE
;
1055 if (pair
->state
== NICE_CHECK_SUCCEEDED
||
1056 pair
->state
== NICE_CHECK_DISCOVERED
)
1057 priv_update_selected_pair (agent
, component
, pair
);
1058 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1064 * Creates a new connectivity check pair and adds it to
1065 * the agent's list of checks.
1067 static gboolean
priv_add_new_check_pair (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*local
, NiceCandidate
*remote
, NiceCheckState initial_state
, gboolean use_candidate
)
1069 gboolean result
= FALSE
;
1070 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1071 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1073 GSList
*modified_list
=
1074 g_slist_insert_sorted (stream
->conncheck_list
, pair
, (GCompareFunc
)conn_check_compare
);
1075 if (modified_list
) {
1076 /* step: allocation and addition succesful, do rest of the work */
1078 pair
->agent
= agent
;
1079 pair
->stream_id
= stream_id
;
1080 pair
->component_id
= component
->id
;;
1081 pair
->local
= local
;
1082 pair
->remote
= remote
;
1083 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s", local
->foundation
, remote
->foundation
);
1085 pair
->priority
= agent_candidate_pair_priority (agent
, local
, remote
);
1086 pair
->state
= initial_state
;
1087 nice_debug ("Agent %p : creating new pair %p state %d", agent
, pair
, initial_state
);
1088 pair
->nominated
= use_candidate
;
1089 pair
->controlling
= agent
->controlling_mode
;
1091 /* note: for the first added check */
1092 if (!stream
->conncheck_list
)
1093 stream
->conncheck_state
= NICE_CHECKLIST_RUNNING
;
1094 stream
->conncheck_list
= modified_list
;
1097 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent
, pair
, pair
->foundation
, stream_id
);
1099 /* implement the hard upper limit for number of
1100 checks (see sect 5.7.3 ICE ID-19): */
1101 stream
->conncheck_list
=
1102 priv_limit_conn_check_list_size (stream
->conncheck_list
, agent
->max_conn_checks
);
1103 if (!stream
->conncheck_list
) {
1104 stream
->conncheck_state
= NICE_CHECKLIST_FAILED
;
1109 /* memory alloc failed: list insert */
1110 conn_check_free_item (pair
, NULL
);
1111 stream
->conncheck_state
= NICE_CHECKLIST_FAILED
;
1114 else { /* memory alloc failed: new pair */
1115 stream
->conncheck_state
= NICE_CHECKLIST_FAILED
;
1122 * Forms new candidate pairs by matching the new remote candidate
1123 * 'remote_cand' with all existing local candidates of 'component'.
1124 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1127 * @param agent context
1128 * @param component pointer to the component
1129 * @param remote remote candidate to match with
1131 * @return number of checks added, negative on fatal errors
1133 int conn_check_add_for_candidate (NiceAgent
*agent
, guint stream_id
, Component
*component
, NiceCandidate
*remote
)
1138 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1140 NiceCandidate
*local
= i
->data
;
1142 /* note: match pairs only if transport and address family are the same */
1143 if (local
->transport
== remote
->transport
&&
1144 local
->addr
.s
.addr
.sa_family
== remote
->addr
.s
.addr
.sa_family
) {
1148 /* note: do not create pairs where local candidate is
1149 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1150 if ((agent
->compatibility
== NICE_COMPATIBILITY_DRAFT19
||
1151 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
) &&
1152 local
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
)
1155 result
= priv_add_new_check_pair (agent
, stream_id
, component
, local
, remote
, NICE_CHECK_FROZEN
, FALSE
);
1158 if (component
->state
< NICE_COMPONENT_STATE_CONNECTED
) {
1159 agent_signal_component_state_change (agent
,
1162 NICE_COMPONENT_STATE_CONNECTING
);
1164 agent_signal_component_state_change (agent
,
1167 NICE_COMPONENT_STATE_CONNECTED
);
1181 * Frees the CandidateCheckPair structure pointer to
1182 * by 'user data'. Compatible with g_slist_foreach().
1184 void conn_check_free_item (gpointer data
, gpointer user_data
)
1186 CandidateCheckPair
*pair
= data
;
1187 g_assert (user_data
== NULL
);
1188 pair
->stun_message
.buffer
= NULL
;
1189 pair
->stun_message
.buffer_len
= 0;
1190 g_slice_free (CandidateCheckPair
, pair
);
1194 * Frees all resources of all connectivity checks.
1196 void conn_check_free (NiceAgent
*agent
)
1199 for (i
= agent
->streams
; i
; i
= i
->next
) {
1200 Stream
*stream
= i
->data
;
1202 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent
, stream
);
1203 if (stream
->conncheck_list
) {
1204 g_slist_foreach (stream
->conncheck_list
, conn_check_free_item
, NULL
);
1205 g_slist_free (stream
->conncheck_list
),
1206 stream
->conncheck_list
= NULL
;
1207 stream
->conncheck_state
= NICE_CHECKLIST_NOT_STARTED
;
1211 if (agent
->conncheck_timer_source
!= NULL
) {
1212 g_source_destroy (agent
->conncheck_timer_source
);
1213 g_source_unref (agent
->conncheck_timer_source
);
1214 agent
->conncheck_timer_source
= NULL
;
1219 * Prunes the list of connectivity checks for items related
1220 * to stream 'stream_id'.
1222 * @return TRUE on success, FALSE on a fatal error
1224 gboolean
conn_check_prune_stream (NiceAgent
*agent
, Stream
*stream
)
1226 CandidateCheckPair
*pair
;
1229 for (i
= stream
->conncheck_list
; i
; ) {
1230 GSList
*next
= i
->next
;
1233 g_assert (pair
->stream_id
== stream
->id
);
1235 stream
->conncheck_list
=
1236 g_slist_remove (stream
->conncheck_list
, pair
);
1237 conn_check_free_item (pair
, NULL
);
1239 if (!stream
->conncheck_list
)
1243 if (!stream
->conncheck_list
) {
1244 stream
->conncheck_state
= NICE_CHECKLIST_NOT_STARTED
;
1245 conn_check_free (agent
);
1248 /* return FALSE if there was a memory allocation failure */
1249 if (stream
->conncheck_list
== NULL
&& i
!= NULL
)
1256 * Fills 'dest' with a username string for use in an outbound connectivity
1257 * checks. No more than 'dest_len' characters (including terminating
1258 * NULL) is ever written to the 'dest'.
1261 size_t priv_gen_username (NiceAgent
*agent
, guint component_id
,
1262 gchar
*remote
, gchar
*local
, uint8_t *dest
, guint dest_len
)
1265 gsize remote_len
= strlen (remote
);
1266 gsize local_len
= strlen (local
);
1268 if (remote_len
> 0 && local_len
> 0) {
1269 if (agent
->compatibility
== NICE_COMPATIBILITY_DRAFT19
&&
1270 dest_len
>= remote_len
+ local_len
+ 1) {
1271 memcpy (dest
, remote
, remote_len
);
1273 memcpy (dest
+ len
, ":", 1);
1275 memcpy (dest
+ len
, local
, local_len
);
1277 } else if (agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
&&
1278 dest_len
>= remote_len
+ local_len
+ 4 ) {
1279 memcpy (dest
, remote
, remote_len
);
1281 memcpy (dest
+ len
, ":", 1);
1283 memcpy (dest
+ len
, local
, local_len
);
1286 memset (dest
+ len
, 0, 4 - (len
% 4));
1287 len
+= 4 - (len
% 4);
1289 } else if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
1290 dest_len
>= remote_len
+ local_len
) {
1291 memcpy (dest
, remote
, remote_len
);
1293 memcpy (dest
+ len
, local
, local_len
);
1295 } else if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
1296 gchar component_str
[10];
1297 guchar
*local_decoded
= NULL
;
1298 guchar
*remote_decoded
= NULL
;
1299 gsize local_decoded_len
;
1300 gsize remote_decoded_len
;
1304 g_snprintf (component_str
, sizeof(component_str
), "%d", component_id
);
1305 local_decoded
= g_base64_decode (local
, &local_decoded_len
);
1306 remote_decoded
= g_base64_decode (remote
, &remote_decoded_len
);
1308 total_len
= remote_decoded_len
+ local_decoded_len
+ 3 + 2*strlen (component_str
);
1309 padding
= 4 - (total_len
% 4);
1311 if (dest_len
>= total_len
+ padding
) {
1312 guchar pad_char
[1] = {0};
1315 memcpy (dest
, remote_decoded
, remote_decoded_len
);
1316 len
+= remote_decoded_len
;
1317 memcpy (dest
+ len
, ":", 1);
1319 memcpy (dest
+ len
, component_str
, strlen (component_str
));
1320 len
+= strlen (component_str
);
1322 memcpy (dest
+ len
, ":", 1);
1325 memcpy (dest
+ len
, local_decoded
, local_decoded_len
);
1326 len
+= local_decoded_len
;
1327 memcpy (dest
+ len
, ":", 1);
1329 memcpy (dest
+ len
, component_str
, strlen (component_str
));;
1330 len
+= strlen (component_str
);
1332 for (i
= 0; i
< padding
; i
++) {
1333 memcpy (dest
+ len
, pad_char
, 1);
1339 g_free (local_decoded
);
1340 g_free (remote_decoded
);
1348 * Fills 'dest' with a username string for use in an outbound connectivity
1349 * checks. No more than 'dest_len' characters (including terminating
1350 * NULL) is ever written to the 'dest'.
1353 size_t priv_create_username (NiceAgent
*agent
, Stream
*stream
,
1354 guint component_id
, NiceCandidate
*remote
, NiceCandidate
*local
,
1355 uint8_t *dest
, guint dest_len
, gboolean inbound
)
1357 gchar
*local_username
= NULL
;
1358 gchar
*remote_username
= NULL
;
1361 if (remote
&& remote
->username
) {
1362 remote_username
= remote
->username
;
1365 if (local
&& local
->username
) {
1366 local_username
= local
->username
;
1370 if (remote_username
== NULL
) {
1371 remote_username
= stream
->remote_ufrag
;
1373 if (local_username
== NULL
) {
1374 local_username
= stream
->local_ufrag
;
1378 if (local_username
&& remote_username
) {
1380 return priv_gen_username (agent
, component_id
,
1381 local_username
, remote_username
, dest
, dest_len
);
1383 return priv_gen_username (agent
, component_id
,
1384 remote_username
, local_username
, dest
, dest_len
);
1392 * Returns a password string for use in an outbound connectivity
1396 size_t priv_get_password (NiceAgent
*agent
, Stream
*stream
,
1397 NiceCandidate
*remote
, uint8_t **password
)
1399 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
)
1402 if (remote
&& remote
->password
) {
1403 *password
= (uint8_t *)remote
->password
;
1404 return strlen (remote
->password
);
1408 *password
= (uint8_t *)stream
->remote_password
;
1409 return strlen (stream
->remote_password
);
1416 * Sends a connectivity check over candidate pair 'pair'.
1418 * @return zero on success, non-zero on error
1420 int conn_check_send (NiceAgent
*agent
, CandidateCheckPair
*pair
)
1423 /* note: following information is supplied:
1424 * - username (for USERNAME attribute)
1425 * - password (for MESSAGE-INTEGRITY)
1426 * - priority (for PRIORITY)
1427 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1428 * - USE-CANDIDATE (if sent by the controlling agent)
1432 nice_candidate_ice_priority_full (
1433 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE
,
1435 pair
->local
->component_id
);
1437 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
1439 priv_create_username (agent
, agent_find_stream (agent
, pair
->stream_id
),
1440 pair
->component_id
, pair
->remote
, pair
->local
, uname
, sizeof (uname
), FALSE
);
1441 uint8_t *password
= NULL
;
1442 size_t password_len
= priv_get_password (agent
,
1443 agent_find_stream (agent
, pair
->stream_id
), pair
->remote
, &password
);
1445 bool controlling
= agent
->controlling_mode
;
1446 /* XXX: add API to support different nomination modes: */
1447 bool cand_use
= controlling
;
1450 struct sockaddr sockaddr
;
1451 unsigned int timeout
;
1453 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
1454 password
= g_base64_decode ((gchar
*) password
, &password_len
);
1457 memset (&sockaddr
, 0, sizeof (sockaddr
));
1459 nice_address_copy_to_sockaddr (&pair
->remote
->addr
, &sockaddr
);
1462 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1463 nice_address_to_string (&pair
->remote
->addr
, tmpbuf
);
1464 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
,
1466 ntohs(((struct sockaddr_in
*)(&sockaddr
))->sin_port
),
1467 pair
->local
->sockptr
->fileno
,
1468 pair
->foundation
, pair
->component_id
,
1469 (unsigned long long)agent
->tie_breaker
,
1470 uname
, uname_len
, password
, password_len
, priority
);
1475 pair
->nominated
= controlling
;
1477 if (uname_len
> 0) {
1479 buffer_len
= stun_usage_ice_conncheck_create (&agent
->stun_agent
,
1480 &pair
->stun_message
, pair
->stun_buffer
, sizeof(pair
->stun_buffer
),
1481 uname
, uname_len
, password
, password_len
,
1482 cand_use
, controlling
, priority
,
1484 agent_to_ice_compatibility (agent
));
1486 nice_debug ("Agent %p: conncheck created %d - %p", agent
, buffer_len
, pair
->stun_message
.buffer
);
1488 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
1492 stun_timer_start (&pair
->timer
);
1494 /* send the conncheck */
1495 nice_socket_send (pair
->local
->sockptr
, &pair
->remote
->addr
,
1496 buffer_len
, (gchar
*)pair
->stun_buffer
);
1498 timeout
= stun_timer_remainder (&pair
->timer
);
1499 /* note: convert from milli to microseconds for g_time_val_add() */
1500 g_get_current_time (&pair
->next_tick
);
1501 g_time_val_add (&pair
->next_tick
, timeout
* 1000);
1508 * Implemented the pruning steps described in ICE sect 8.1.2
1509 * "Updating States" (ID-19) after a pair has been nominated.
1511 * @see priv_update_check_list_state_failed_components()
1513 static void priv_prune_pending_checks (Stream
*stream
, guint component_id
)
1517 /* step: cancel all FROZEN and WAITING pairs for the component */
1518 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1519 CandidateCheckPair
*p
= i
->data
;
1520 if (p
->component_id
== component_id
) {
1521 if (p
->state
== NICE_CHECK_FROZEN
||
1522 p
->state
== NICE_CHECK_WAITING
) {
1523 p
->state
= NICE_CHECK_CANCELLED
;
1524 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1527 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1528 if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1529 p
->stun_message
.buffer
= NULL
;
1530 p
->stun_message
.buffer_len
= 0;
1531 p
->state
= NICE_CHECK_CANCELLED
;
1532 nice_debug ("Agent XXX : pair %p state CANCELED", p
);
1539 * Schedules a triggered check after a succesfully inbound
1540 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1542 * @param agent self pointer
1543 * @param component the check is related to
1544 * @param local_socket socket from which the inbound check was received
1545 * @param remote_cand remote candidate from which the inbound check was sent
1546 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1548 static gboolean
priv_schedule_triggered_check (NiceAgent
*agent
, Stream
*stream
, Component
*component
, NiceSocket
*local_socket
, NiceCandidate
*remote_cand
, gboolean use_candidate
)
1551 gboolean result
= FALSE
;
1553 for (i
= stream
->conncheck_list
; i
; i
= i
->next
) {
1554 CandidateCheckPair
*p
= i
->data
;
1555 if (p
->component_id
== component
->id
&&
1556 p
->remote
== remote_cand
&&
1557 p
->local
->sockptr
== local_socket
) {
1559 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent
, p
);
1561 if (p
->state
== NICE_CHECK_WAITING
||
1562 p
->state
== NICE_CHECK_FROZEN
)
1563 priv_conn_check_initiate (agent
, p
);
1564 else if (p
->state
== NICE_CHECK_IN_PROGRESS
) {
1565 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1566 * we should cancel the existing one, and send a new one...? :P */
1567 nice_debug ("Agent %p : Skipping triggered check, already in progress..", agent
);
1569 else if (p
->state
== NICE_CHECK_SUCCEEDED
||
1570 p
->state
== NICE_CHECK_DISCOVERED
) {
1571 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent
);
1572 /* note: this is a bit unsure corner-case -- let's do the
1573 same state update as for processing responses to our own checks */
1574 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1576 /* note: to take care of the controlling-controlling case in
1577 * aggressive nomination mode, send a new triggered
1578 * check to nominate the pair */
1579 if ((agent
->compatibility
== NICE_COMPATIBILITY_DRAFT19
||
1580 agent
->compatibility
== NICE_COMPATIBILITY_WLM2009
) &&
1581 agent
->controlling_mode
)
1582 priv_conn_check_initiate (agent
, p
);
1585 /* note: the spec says the we SHOULD retransmit in-progress
1586 * checks immediately, but we won't do that now */
1593 NiceCandidate
*local
= NULL
;
1595 for (i
= component
->local_candidates
; i
; i
= i
->next
) {
1597 if (local
->sockptr
== local_socket
)
1601 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent
, local
);
1602 result
= priv_add_new_check_pair (agent
, stream
->id
, component
, local
, remote_cand
, NICE_CHECK_WAITING
, use_candidate
);
1605 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent
, remote_cand
);
1613 * Sends a reply to an succesfully received STUN connectivity
1614 * check request. Implements parts of the ICE spec section 7.2 (STUN
1615 * Server Procedures).
1617 * @param agent context pointer
1618 * @param stream which stream (of the agent)
1619 * @param component which component (of the stream)
1620 * @param rcand remote candidate from which the request came, if NULL,
1621 * the response is sent immediately but no other processing is done
1622 * @param toaddr address to which reply is sent
1623 * @param socket the socket over which the request came
1624 * @param rbuf_len length of STUN message to send
1625 * @param rbuf buffer containing the STUN message to send
1626 * @param use_candidate whether the request had USE_CANDIDATE attribute
1628 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1630 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
)
1632 g_assert (rcand
== NULL
|| nice_address_equal(&rcand
->addr
, toaddr
) == TRUE
);
1635 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1636 nice_address_to_string (toaddr
, tmpbuf
);
1637 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent
,
1639 nice_address_get_port (toaddr
),
1642 rcand
, component
->id
,
1643 (int)use_candidate
);
1646 nice_socket_send (socket
, toaddr
, rbuf_len
, (const gchar
*)rbuf
);
1649 /* note: upon succesful check, make the reserve check immediately */
1650 priv_schedule_triggered_check (agent
, stream
, component
, socket
, rcand
, use_candidate
);
1653 priv_mark_pair_nominated (agent
, stream
, component
, rcand
);
1658 * Stores information of an incoming STUN connectivity check
1659 * for later use. This is only needed when a check is received
1660 * before we get information about the remote candidates (via
1661 * SDP or other signaling means).
1663 * @return non-zero on error, zero on success
1665 static int priv_store_pending_check (NiceAgent
*agent
, Component
*component
, const NiceAddress
*from
, NiceSocket
*socket
, uint32_t priority
, gboolean use_candidate
)
1667 IncomingCheck
*icheck
;
1668 nice_debug ("Agent %p : Storing pending check.", agent
);
1670 if (component
->incoming_checks
&&
1671 g_slist_length (component
->incoming_checks
) >=
1672 NICE_AGENT_MAX_REMOTE_CANDIDATES
) {
1673 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent
);
1677 icheck
= g_slice_new0 (IncomingCheck
);
1679 GSList
*pending
= g_slist_append (component
->incoming_checks
, icheck
);
1681 component
->incoming_checks
= pending
;
1682 icheck
->from
= *from
;
1683 icheck
->local_socket
= socket
;
1684 icheck
->priority
= priority
;
1685 icheck
->use_candidate
= use_candidate
;
1694 * Adds a new pair, discovered from an incoming STUN response, to
1695 * the connectivity check list.
1697 * @return created pair, or NULL on fatal (memory allocation) errors
1699 static CandidateCheckPair
*priv_add_peer_reflexive_pair (NiceAgent
*agent
, guint stream_id
, guint component_id
, NiceCandidate
*local_cand
, CandidateCheckPair
*parent_pair
)
1701 CandidateCheckPair
*pair
= g_slice_new0 (CandidateCheckPair
);
1703 Stream
*stream
= agent_find_stream (agent
, stream_id
);
1704 GSList
*modified_list
= g_slist_append (stream
->conncheck_list
, pair
);
1705 if (modified_list
) {
1706 stream
->conncheck_list
= modified_list
;
1707 pair
->agent
= agent
;
1708 pair
->stream_id
= stream_id
;
1709 pair
->component_id
= component_id
;;
1710 pair
->local
= local_cand
;
1711 pair
->remote
= parent_pair
->remote
;
1712 pair
->state
= NICE_CHECK_DISCOVERED
;
1713 nice_debug ("Agent %p : pair %p state DISCOVERED", agent
, pair
);
1714 g_snprintf (pair
->foundation
, NICE_CANDIDATE_PAIR_MAX_FOUNDATION
, "%s:%s", local_cand
->foundation
, parent_pair
->remote
->foundation
);
1715 if (agent
->controlling_mode
== TRUE
)
1716 pair
->priority
= nice_candidate_pair_priority (local_cand
->priority
, parent_pair
->priority
);
1718 pair
->priority
= nice_candidate_pair_priority (parent_pair
->priority
, local_cand
->priority
);
1719 pair
->nominated
= FALSE
;
1720 pair
->controlling
= agent
->controlling_mode
;
1721 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent
, pair
->foundation
);
1730 * Recalculates priorities of all candidate pairs. This
1731 * is required after a conflict in ICE roles.
1733 static void priv_recalculate_pair_priorities (NiceAgent
*agent
)
1737 for (i
= agent
->streams
; i
; i
= i
->next
) {
1738 Stream
*stream
= i
->data
;
1739 for (j
= stream
->conncheck_list
; j
; j
= j
->next
) {
1740 CandidateCheckPair
*p
= j
->data
;
1741 p
->priority
= agent_candidate_pair_priority (agent
, p
->local
, p
->remote
);
1747 * Change the agent role if different from 'control'. Can be
1748 * initiated both by handling of incoming connectivity checks,
1749 * and by processing the responses to checks sent by us.
1751 static void priv_check_for_role_conflict (NiceAgent
*agent
, gboolean control
)
1753 /* role conflict, change mode; wait for a new conn. check */
1754 if (control
!= agent
->controlling_mode
) {
1755 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent
, control
);
1756 agent
->controlling_mode
= control
;
1757 /* the pair priorities depend on the roles, so recalculation
1759 priv_recalculate_pair_priorities (agent
);
1762 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent
, control
);
1766 * Checks whether the mapped address in connectivity check response
1767 * matches any of the known local candidates. If not, apply the
1768 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
1770 * @param agent context pointer
1771 * @param stream which stream (of the agent)
1772 * @param component which component (of the stream)
1773 * @param p the connectivity check pair for which we got a response
1774 * @param socketptr socket used to send the reply
1775 * @param mapped_sockaddr mapped address in the response
1777 * @return pointer to a new pair if one was created, otherwise NULL
1779 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
)
1781 CandidateCheckPair
*new_pair
= NULL
;
1784 gboolean local_cand_matches
= FALSE
;
1786 nice_address_set_from_sockaddr (&mapped
, mapped_sockaddr
);
1788 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
1789 NiceCandidate
*cand
= j
->data
;
1790 if (nice_address_equal (&mapped
, &cand
->addr
)) {
1791 local_cand_matches
= TRUE
;
1796 if (local_cand_matches
== TRUE
) {
1797 /* note: this is same as "adding to VALID LIST" in the spec
1799 p
->state
= NICE_CHECK_SUCCEEDED
;
1800 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent
, p
);
1801 priv_conn_check_unfreeze_related (agent
, stream
, p
);
1804 NiceCandidate
*cand
=
1805 discovery_add_peer_reflexive_candidate (agent
,
1812 p
->state
= NICE_CHECK_FAILED
;
1813 nice_debug ("Agent %p : pair %p state FAILED", agent
, p
);
1815 /* step: add a new discovered pair (see ICE 7.1.2.2.2
1816 "Constructing a Valid Pair" (ID-19)) */
1817 new_pair
= priv_add_peer_reflexive_pair (agent
, stream
->id
, component
->id
, cand
, p
);
1818 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent
, p
, new_pair
);
1825 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
1826 * check transaction. If found, the reply is processed. Implements
1827 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
1829 * @return TRUE if a matching transaction is found
1831 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
)
1833 struct sockaddr sockaddr
;
1834 socklen_t socklen
= sizeof (sockaddr
);
1836 StunUsageIceReturn res
;
1837 gboolean trans_found
= FALSE
;
1838 StunTransactionId discovery_id
;
1839 StunTransactionId response_id
;
1840 stun_message_id (resp
, response_id
);
1842 for (i
= stream
->conncheck_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
1843 CandidateCheckPair
*p
= i
->data
;
1845 if (p
->stun_message
.buffer
) {
1846 stun_message_id (&p
->stun_message
, discovery_id
);
1848 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
1849 res
= stun_usage_ice_conncheck_process (resp
, &sockaddr
, &socklen
,
1850 agent_to_ice_compatibility (agent
));
1851 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
1852 "(controlling=%d).", agent
, p
, (int)res
, agent
->controlling_mode
);
1855 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
) {
1856 /* case: found a matching connectivity check request */
1858 CandidateCheckPair
*ok_pair
= NULL
;
1860 nice_debug ("Agent %p : conncheck %p MATCHED.", agent
, p
);
1861 p
->stun_message
.buffer
= NULL
;
1862 p
->stun_message
.buffer_len
= 0;
1864 /* step: verify that response came from the same IP address we
1865 * sent the original request to (see 7.1.2.1. "Failure
1867 if (nice_address_equal (from
, &p
->remote
->addr
) != TRUE
) {
1868 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1869 gchar tmpbuf2
[INET6_ADDRSTRLEN
];
1871 p
->state
= NICE_CHECK_FAILED
;
1872 nice_debug ("Agent %p : conncheck %p FAILED"
1873 " (mismatch of source address).", agent
, p
);
1874 nice_address_to_string (&p
->remote
->addr
, tmpbuf
);
1875 nice_address_to_string (from
, tmpbuf2
);
1876 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent
,
1877 tmpbuf
, nice_address_get_port (&p
->remote
->addr
),
1878 tmpbuf2
, nice_address_get_port (from
));
1884 /* note: CONNECTED but not yet READY, see docs */
1886 /* step: handle the possible case of a peer-reflexive
1887 * candidate where the mapped-address in response does
1888 * not match any local candidate, see 7.1.2.2.1
1889 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
1891 ok_pair
= priv_process_response_check_for_peer_reflexive(agent
, stream
, component
,
1892 p
, sockptr
, &sockaddr
, local_candidate
, remote_candidate
);
1897 /* Do not step down to CONNECTED if we're already at state READY*/
1898 if (component
->state
!= NICE_COMPONENT_STATE_READY
) {
1899 /* step: notify the client of a new component state (must be done
1900 * before the possible check list state update step */
1901 agent_signal_component_state_change (agent
,
1902 stream
->id
, component
->id
, NICE_COMPONENT_STATE_CONNECTED
);
1906 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
1907 Nominated Flag" (ID-19) */
1908 if (ok_pair
->nominated
== TRUE
)
1909 priv_update_selected_pair (agent
, component
, ok_pair
);
1911 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
1912 states" and 8.1.2 "Updating States", ID-19) */
1913 priv_update_check_list_state_for_ready (agent
, stream
, component
);
1916 } else if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
1917 /* case: role conflict error, need to restart with new role */
1918 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent
, p
);
1919 /* note: our role might already have changed due to an
1920 * incoming request, but if not, change role now;
1921 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
1922 priv_check_for_role_conflict (agent
, !p
->controlling
);
1924 p
->stun_message
.buffer
= NULL
;
1925 p
->stun_message
.buffer_len
= 0;
1926 p
->state
= NICE_CHECK_WAITING
;
1927 nice_debug ("Agent %p : pair %p state WAITING", agent
, p
);
1929 } else if (res
== STUN_USAGE_ICE_RETURN_ERROR
) {
1930 /* case: STUN error, the check STUN context was freed */
1931 nice_debug ("Agent %p : conncheck %p FAILED.", agent
, p
);
1932 p
->stun_message
.buffer
= NULL
;
1933 p
->stun_message
.buffer_len
= 0;
1944 * Tries to match STUN reply in 'buf' to an existing STUN discovery
1945 * transaction. If found, a reply is sent.
1947 * @return TRUE if a matching transaction is found
1949 static gboolean
priv_map_reply_to_discovery_request (NiceAgent
*agent
, StunMessage
*resp
)
1951 struct sockaddr sockaddr
;
1952 socklen_t socklen
= sizeof (sockaddr
);
1953 struct sockaddr alternate
;
1954 socklen_t alternatelen
= sizeof (sockaddr
);
1956 StunUsageBindReturn res
;
1957 gboolean trans_found
= FALSE
;
1958 StunTransactionId discovery_id
;
1959 StunTransactionId response_id
;
1960 stun_message_id (resp
, response_id
);
1962 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
1963 CandidateDiscovery
*d
= i
->data
;
1965 if (d
->type
== NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
&&
1966 d
->stun_message
.buffer
) {
1967 stun_message_id (&d
->stun_message
, discovery_id
);
1969 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
1970 res
= stun_usage_bind_process (resp
, &sockaddr
, &socklen
,
1971 &alternate
, &alternatelen
);
1972 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
1973 agent
, d
, (int)res
);
1975 if (res
== STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER
) {
1976 /* handle alternate server */
1977 NiceAddress niceaddr
;
1978 nice_address_set_from_sockaddr (&niceaddr
, &alternate
);
1979 d
->server
= niceaddr
;
1982 } else if (res
== STUN_USAGE_BIND_RETURN_SUCCESS
) {
1983 /* case: succesful binding discovery, create a new local candidate */
1984 NiceAddress niceaddr
;
1985 nice_address_set_from_sockaddr (&niceaddr
, &sockaddr
);
1987 discovery_add_server_reflexive_candidate (
1994 d
->stun_message
.buffer
= NULL
;
1995 d
->stun_message
.buffer_len
= 0;
1998 } else if (res
== STUN_USAGE_BIND_RETURN_ERROR
) {
1999 /* case: STUN error, the check STUN context was freed */
2000 d
->stun_message
.buffer
= NULL
;
2001 d
->stun_message
.buffer_len
= 0;
2013 static CandidateRefresh
*
2014 priv_add_new_turn_refresh (CandidateDiscovery
*cdisco
, NiceCandidate
*relay_cand
,
2017 CandidateRefresh
*cand
;
2018 NiceAgent
*agent
= cdisco
->agent
;
2019 GSList
*modified_list
;
2021 cand
= g_slice_new0 (CandidateRefresh
);
2023 modified_list
= g_slist_append (agent
->refresh_list
, cand
);
2025 if (modified_list
) {
2026 cand
->nicesock
= cdisco
->nicesock
;
2027 cand
->relay_socket
= relay_cand
->sockptr
;
2028 cand
->server
= cdisco
->server
;
2029 cand
->turn
= cdisco
->turn
;
2030 cand
->stream
= cdisco
->stream
;
2031 cand
->component
= cdisco
->component
;
2032 cand
->agent
= cdisco
->agent
;
2033 memcpy (&cand
->stun_agent
, &cdisco
->stun_agent
, sizeof(StunAgent
));
2034 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2035 agent
, cand
, (lifetime
- 60) * 1000);
2036 agent
->refresh_list
= modified_list
;
2038 /* step: also start the refresh timer */
2039 /* refresh should be sent 1 minute before it expires */
2040 cand
->timer_source
=
2041 agent_timeout_add_with_context (agent
, (lifetime
- 60) * 1000,
2042 priv_turn_allocate_refresh_tick
, cand
);
2044 nice_debug ("timer source is : %d", cand
->timer_source
);
2052 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2053 * transaction. If found, a reply is sent.
2055 * @return TRUE if a matching transaction is found
2057 static gboolean
priv_map_reply_to_relay_request (NiceAgent
*agent
, StunMessage
*resp
)
2059 struct sockaddr sockaddr
;
2060 socklen_t socklen
= sizeof (sockaddr
);
2061 struct sockaddr alternate
;
2062 socklen_t alternatelen
= sizeof (alternate
);
2063 struct sockaddr relayaddr
;
2064 socklen_t relayaddrlen
= sizeof (relayaddr
);
2068 StunUsageTurnReturn res
;
2069 gboolean trans_found
= FALSE
;
2070 StunTransactionId discovery_id
;
2071 StunTransactionId response_id
;
2072 stun_message_id (resp
, response_id
);
2074 for (i
= agent
->discovery_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2075 CandidateDiscovery
*d
= i
->data
;
2077 if (d
->type
== NICE_CANDIDATE_TYPE_RELAYED
&&
2078 d
->stun_message
.buffer
) {
2079 stun_message_id (&d
->stun_message
, discovery_id
);
2081 if (memcmp (discovery_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2082 res
= stun_usage_turn_process (resp
,
2083 &relayaddr
, &relayaddrlen
, &sockaddr
, &socklen
, &alternate
, &alternatelen
,
2084 &bandwidth
, &lifetime
, agent_to_turn_compatibility (agent
));
2085 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2086 agent
, d
, (int)res
);
2088 if (res
== STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER
) {
2089 /* handle alternate server */
2090 nice_address_set_from_sockaddr (&d
->server
, &alternate
);
2091 nice_address_set_from_sockaddr (&d
->turn
->server
, &alternate
);
2094 } else if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
||
2095 res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
) {
2096 /* case: succesful allocate, create a new local candidate */
2097 NiceAddress niceaddr
;
2098 NiceCandidate
*relay_cand
;
2100 /* We also received our mapped address */
2101 if (res
== STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS
) {
2102 nice_address_set_from_sockaddr (&niceaddr
, &sockaddr
);
2104 discovery_add_server_reflexive_candidate (
2112 nice_address_set_from_sockaddr (&niceaddr
, &relayaddr
);
2113 relay_cand
= discovery_add_relay_candidate (
2121 priv_add_new_turn_refresh (d
, relay_cand
, lifetime
);
2124 d
->stun_message
.buffer
= NULL
;
2125 d
->stun_message
.buffer_len
= 0;
2128 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2130 uint8_t *sent_realm
= NULL
;
2131 uint8_t *recv_realm
= NULL
;
2132 uint16_t sent_realm_len
= 0;
2133 uint16_t recv_realm_len
= 0;
2135 sent_realm
= (uint8_t *) stun_message_find (&d
->stun_message
,
2136 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2137 recv_realm
= (uint8_t *) stun_message_find (resp
,
2138 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2140 /* check for unauthorized error response */
2141 if (agent
->compatibility
== NICE_COMPATIBILITY_DRAFT19
&&
2142 stun_message_get_class (resp
) == STUN_ERROR
&&
2143 stun_message_find_error (resp
, &code
) ==
2144 STUN_MESSAGE_RETURN_SUCCESS
&&
2145 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2149 !(recv_realm_len
== sent_realm_len
&&
2150 sent_realm
!= NULL
&&
2151 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2152 d
->stun_resp_msg
= *resp
;
2153 memcpy (d
->stun_resp_buffer
, resp
->buffer
,
2154 stun_message_length (resp
));
2155 d
->stun_resp_msg
.buffer
= d
->stun_resp_buffer
;
2156 d
->stun_resp_msg
.buffer_len
= sizeof(d
->stun_resp_buffer
);
2159 /* case: a real unauthorized error */
2160 d
->stun_message
.buffer
= NULL
;
2161 d
->stun_message
.buffer_len
= 0;
2165 /* case: STUN error, the check STUN context was freed */
2166 d
->stun_message
.buffer
= NULL
;
2167 d
->stun_message
.buffer_len
= 0;
2181 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2182 * transaction. If found, a reply is sent.
2184 * @return TRUE if a matching transaction is found
2186 static gboolean
priv_map_reply_to_relay_refresh (NiceAgent
*agent
, StunMessage
*resp
)
2190 StunUsageTurnReturn res
;
2191 gboolean trans_found
= FALSE
;
2192 StunTransactionId refresh_id
;
2193 StunTransactionId response_id
;
2194 stun_message_id (resp
, response_id
);
2196 for (i
= agent
->refresh_list
; i
&& trans_found
!= TRUE
; i
= i
->next
) {
2197 CandidateRefresh
*cand
= i
->data
;
2199 if (cand
->stun_message
.buffer
) {
2200 stun_message_id (&cand
->stun_message
, refresh_id
);
2202 if (memcmp (refresh_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2203 res
= stun_usage_turn_refresh_process (resp
,
2204 &lifetime
, agent_to_turn_compatibility (cand
->agent
));
2205 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2206 agent
, cand
, (int)res
);
2207 if (res
== STUN_USAGE_TURN_RETURN_RELAY_SUCCESS
) {
2208 /* refresh should be sent 1 minute before it expires */
2209 cand
->timer_source
=
2210 agent_timeout_add_with_context (cand
->agent
, (lifetime
- 60) * 1000,
2211 priv_turn_allocate_refresh_tick
, cand
);
2213 g_source_destroy (cand
->tick_source
);
2214 g_source_unref (cand
->tick_source
);
2215 cand
->tick_source
= NULL
;
2216 } else if (res
== STUN_USAGE_TURN_RETURN_ERROR
) {
2218 uint8_t *sent_realm
= NULL
;
2219 uint8_t *recv_realm
= NULL
;
2220 uint16_t sent_realm_len
= 0;
2221 uint16_t recv_realm_len
= 0;
2223 sent_realm
= (uint8_t *) stun_message_find (&cand
->stun_message
,
2224 STUN_ATTRIBUTE_REALM
, &sent_realm_len
);
2225 recv_realm
= (uint8_t *) stun_message_find (resp
,
2226 STUN_ATTRIBUTE_REALM
, &recv_realm_len
);
2228 /* check for unauthorized error response */
2229 if (cand
->agent
->compatibility
== NICE_COMPATIBILITY_DRAFT19
&&
2230 stun_message_get_class (resp
) == STUN_ERROR
&&
2231 stun_message_find_error (resp
, &code
) ==
2232 STUN_MESSAGE_RETURN_SUCCESS
&&
2233 recv_realm
!= NULL
&& recv_realm_len
> 0) {
2237 !(recv_realm_len
== sent_realm_len
&&
2238 sent_realm
!= NULL
&&
2239 memcmp (sent_realm
, recv_realm
, sent_realm_len
) == 0))) {
2240 cand
->stun_resp_msg
= *resp
;
2241 memcpy (cand
->stun_resp_buffer
, resp
->buffer
,
2242 stun_message_length (resp
));
2243 cand
->stun_resp_msg
.buffer
= cand
->stun_resp_buffer
;
2244 cand
->stun_resp_msg
.buffer_len
= sizeof(cand
->stun_resp_buffer
);
2245 priv_turn_allocate_refresh_tick_unlocked (cand
);
2247 /* case: a real unauthorized error */
2248 refresh_cancel (cand
);
2251 /* case: STUN error, the check STUN context was freed */
2252 refresh_cancel (cand
);
2264 static gboolean
priv_map_reply_to_keepalive_conncheck (NiceAgent
*agent
,
2265 Component
*component
, StunMessage
*resp
)
2267 StunTransactionId conncheck_id
;
2268 StunTransactionId response_id
;
2269 stun_message_id (resp
, response_id
);
2271 if (component
->selected_pair
.keepalive
.stun_message
.buffer
) {
2272 stun_message_id (&component
->selected_pair
.keepalive
.stun_message
,
2274 if (memcmp (conncheck_id
, response_id
, sizeof(StunTransactionId
)) == 0) {
2275 nice_debug ("Agent %p : Keepalive for selected pair received.",
2277 if (component
->selected_pair
.keepalive
.tick_source
) {
2278 g_source_destroy (component
->selected_pair
.keepalive
.tick_source
);
2279 g_source_unref (component
->selected_pair
.keepalive
.tick_source
);
2280 component
->selected_pair
.keepalive
.tick_source
= NULL
;
2282 component
->selected_pair
.keepalive
.stun_message
.buffer
= NULL
;
2294 Component
*component
;
2296 } conncheck_validater_data
;
2298 static bool conncheck_stun_validater (StunAgent
*agent
,
2299 StunMessage
*message
, uint8_t *username
, uint16_t username_len
,
2300 uint8_t **password
, size_t *password_len
, void *user_data
)
2302 conncheck_validater_data
*data
= (conncheck_validater_data
*) user_data
;
2304 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
2305 guint uname_len
= 0;
2307 for (i
= data
->component
->local_candidates
; i
; i
= i
->next
) {
2308 NiceCandidate
*cand
= i
->data
;
2309 gchar
*ufrag
= NULL
;
2313 ufrag
= cand
->username
;
2314 else if (data
->stream
)
2315 ufrag
= data
->stream
->local_ufrag
;
2316 ufrag_len
= strlen (ufrag
);
2318 if (data
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
)
2319 ufrag
= (gchar
*)g_base64_decode (ufrag
, &ufrag_len
);
2321 if (ufrag_len
<= NICE_STREAM_MAX_UNAME
) {
2322 memcpy (uname
, ufrag
, ufrag_len
);
2323 uname_len
= ufrag_len
;
2326 if (data
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
)
2329 stun_debug ("Comparing username '");
2330 stun_debug_bytes (username
, username_len
);
2331 stun_debug ("' (%d) with '", username_len
);
2332 stun_debug_bytes (uname
, uname_len
);
2333 stun_debug ("' (%d) : %d\n",
2334 uname_len
, memcmp (username
, uname
, uname_len
));
2335 if (uname_len
> 0 && username_len
>= uname_len
&&
2336 memcmp (username
, uname
, uname_len
) == 0) {
2340 pass
= cand
->password
;
2342 pass
= data
->stream
->local_password
;
2344 *password
= (uint8_t *) pass
;
2345 *password_len
= strlen (pass
);
2347 if (data
->agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2348 data
->password
= g_base64_decode (pass
, password_len
);
2349 *password
= data
->password
;
2352 stun_debug ("Found valid username, returning password: '%s'\n", *password
);
2362 * Processing an incoming STUN message.
2364 * @param agent self pointer
2365 * @param stream stream the packet is related to
2366 * @param component component the packet is related to
2367 * @param socket socket from which the packet was received
2368 * @param from address of the sender
2369 * @param buf message contents
2370 * @param buf message length
2372 * @pre contents of 'buf' is a STUN message
2374 * @return XXX (what FALSE means exactly?)
2376 gboolean
conn_check_handle_inbound_stun (NiceAgent
*agent
, Stream
*stream
,
2377 Component
*component
, NiceSocket
*socket
, const NiceAddress
*from
,
2378 gchar
*buf
, guint len
)
2380 struct sockaddr sockaddr
;
2381 uint8_t rbuf
[MAX_STUN_DATAGRAM_PAYLOAD
];
2383 size_t rbuf_len
= sizeof (rbuf
);
2384 bool control
= agent
->controlling_mode
;
2385 uint8_t uname
[NICE_STREAM_MAX_UNAME
];
2388 uint16_t username_len
;
2391 StunValidationStatus valid
;
2392 conncheck_validater_data validater_data
= {agent
, stream
, component
, NULL
};
2394 NiceCandidate
*remote_candidate
= NULL
;
2395 NiceCandidate
*remote_candidate2
= NULL
;
2396 NiceCandidate
*local_candidate
= NULL
;
2397 gboolean discovery_msg
= FALSE
;
2399 nice_address_copy_to_sockaddr (from
, &sockaddr
);
2401 /* note: contents of 'buf' already validated, so it is
2402 * a valid and fully received STUN message */
2406 gchar tmpbuf
[INET6_ADDRSTRLEN
];
2407 nice_address_to_string (from
, tmpbuf
);
2408 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2409 agent
, stream
->id
, component
->id
, tmpbuf
, nice_address_get_port (from
), len
);
2413 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2415 valid
= stun_agent_validate (&agent
->stun_agent
, &req
,
2416 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2418 /* Check for discovery candidates stun agents */
2419 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2420 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2421 for (i
= agent
->discovery_list
; i
; i
= i
->next
) {
2422 CandidateDiscovery
*d
= i
->data
;
2423 if (d
->stream
== stream
&& d
->component
== component
&&
2424 d
->nicesock
== socket
) {
2425 valid
= stun_agent_validate (&d
->stun_agent
, &req
,
2426 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2428 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2431 discovery_msg
= TRUE
;
2436 /* Check for relay refresh stun agents */
2437 if (valid
== STUN_VALIDATION_BAD_REQUEST
||
2438 valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
) {
2439 for (i
= agent
->refresh_list
; i
; i
= i
->next
) {
2440 CandidateRefresh
*r
= i
->data
;
2441 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r
->stream
,
2442 stream
, r
->component
, component
, r
->nicesock
, r
->relay_socket
, socket
);
2443 if (r
->stream
== stream
&& r
->component
== component
&&
2444 (r
->nicesock
== socket
|| r
->relay_socket
== socket
)) {
2445 valid
= stun_agent_validate (&r
->stun_agent
, &req
,
2446 (uint8_t *) buf
, len
, conncheck_stun_validater
, &validater_data
);
2447 nice_debug ("Validating gave %d", valid
);
2448 if (valid
== STUN_VALIDATION_UNMATCHED_RESPONSE
)
2450 discovery_msg
= TRUE
;
2457 if (validater_data
.password
)
2458 g_free (validater_data
.password
);
2460 if (valid
== STUN_VALIDATION_NOT_STUN
||
2461 valid
== STUN_VALIDATION_INCOMPLETE_STUN
||
2462 valid
== STUN_VALIDATION_BAD_REQUEST
)
2464 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2469 if (valid
== STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
) {
2470 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent
);
2471 rbuf_len
= stun_agent_build_unknown_attributes_error (&agent
->stun_agent
,
2472 &msg
, rbuf
, rbuf_len
, &req
);
2476 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
) {
2477 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2482 if (valid
== STUN_VALIDATION_UNAUTHORIZED
) {
2483 nice_debug ("Agent %p : Integrity check failed.", agent
);
2485 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2486 &req
, STUN_ERROR_UNAUTHORIZED
)) {
2487 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2488 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
)
2489 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2493 if (valid
== STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST
) {
2494 nice_debug ("Agent %p : Integrity check failed.", agent
);
2495 if (stun_agent_init_error (&agent
->stun_agent
, &msg
, rbuf
, rbuf_len
,
2496 &req
, STUN_ERROR_BAD_REQUEST
)) {
2497 rbuf_len
= stun_agent_finish_message (&agent
->stun_agent
, &msg
, NULL
, 0);
2498 if (rbuf_len
> 0 && agent
->compatibility
!= NICE_COMPATIBILITY_MSN
)
2499 nice_socket_send (socket
, from
, rbuf_len
, (const gchar
*)rbuf
);
2504 username
= (uint8_t *) stun_message_find (&req
, STUN_ATTRIBUTE_USERNAME
,
2507 for (i
= component
->remote_candidates
; i
; i
= i
->next
) {
2508 NiceCandidate
*cand
= i
->data
;
2509 if (nice_address_equal (from
, &cand
->addr
)) {
2510 remote_candidate
= cand
;
2515 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2516 agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2517 /* We need to find which local candidate was used */
2518 for (i
= component
->remote_candidates
; i
; i
= i
->next
) {
2519 for (j
= component
->local_candidates
; j
; j
= j
->next
) {
2520 gboolean inbound
= TRUE
;
2521 NiceCandidate
*rcand
= i
->data
;
2522 NiceCandidate
*lcand
= j
->data
;
2524 /* If we receive a response, then the username is local:remote */
2525 if (agent
->compatibility
!= NICE_COMPATIBILITY_MSN
) {
2526 if (stun_message_get_class (&req
) == STUN_REQUEST
||
2527 stun_message_get_class (&req
) == STUN_INDICATION
) {
2534 uname_len
= priv_create_username (agent
, stream
,
2535 component
->id
, rcand
, lcand
,
2536 uname
, sizeof (uname
), inbound
);
2538 stun_debug ("Comparing username '");
2539 stun_debug_bytes (username
, username
? username_len
: 0);
2540 stun_debug ("' (%d) with '", username_len
);
2541 stun_debug_bytes (uname
, uname_len
);
2542 stun_debug ("' (%d) : %d\n",
2543 uname_len
, username
&& uname_len
== username_len
&&
2544 memcmp (username
, uname
, uname_len
) == 0);
2547 uname_len
== username_len
&&
2548 memcmp (uname
, username
, username_len
) == 0) {
2549 local_candidate
= lcand
;
2550 remote_candidate2
= rcand
;
2557 if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
&&
2558 local_candidate
== NULL
&&
2559 discovery_msg
== FALSE
) {
2560 /* if we couldn't match the username and the stun agent has
2561 IGNORE_CREDENTIALS then we have an integrity check failing.
2562 This could happen with the race condition of receiving connchecks
2563 before the remote candidates are added. Just drop the message, and let
2564 the retransmissions make it work. */
2565 nice_debug ("Agent %p : Username check failed.", agent
);
2569 if (valid
!= STUN_VALIDATION_SUCCESS
) {
2570 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent
, valid
);
2575 if (stun_message_get_class (&req
) == STUN_REQUEST
) {
2576 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2577 username
= (uint8_t *) stun_message_find (&req
,
2578 STUN_ATTRIBUTE_USERNAME
, &username_len
);
2579 uname_len
= priv_create_username (agent
, stream
,
2580 component
->id
, remote_candidate2
, local_candidate
,
2581 uname
, sizeof (uname
), FALSE
);
2582 memcpy (username
, uname
, username_len
);
2583 if (remote_candidate2
) {
2584 req
.key
= g_base64_decode ((gchar
*) remote_candidate2
->password
,
2592 rbuf_len
= sizeof (rbuf
);
2593 res
= stun_usage_ice_conncheck_create_reply (&agent
->stun_agent
, &req
,
2594 &msg
, rbuf
, &rbuf_len
, &sockaddr
, sizeof (sockaddr
),
2595 &control
, agent
->tie_breaker
,
2596 agent_to_ice_compatibility (agent
));
2598 if (agent
->compatibility
== NICE_COMPATIBILITY_MSN
) {
2602 if (res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
)
2603 priv_check_for_role_conflict (agent
, control
);
2605 if (res
== STUN_USAGE_ICE_RETURN_SUCCESS
||
2606 res
== STUN_USAGE_ICE_RETURN_ROLE_CONFLICT
) {
2607 /* case 1: valid incoming request, send a reply/error */
2608 bool use_candidate
=
2609 stun_usage_ice_conncheck_use_candidate (&req
);
2610 uint32_t priority
= stun_usage_ice_conncheck_priority (&req
);
2612 if (agent
->controlling_mode
||
2613 agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
||
2614 agent
->compatibility
== NICE_COMPATIBILITY_MSN
)
2615 use_candidate
= TRUE
;
2617 if (stream
->initial_binding_request_received
!= TRUE
)
2618 agent_signal_initial_binding_request_received (agent
, stream
);
2620 if (component
->remote_candidates
&& remote_candidate
== NULL
) {
2621 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2622 "peer-reflexive candidate.", agent
);
2623 remote_candidate
= discovery_learn_remote_peer_reflexive_candidate (
2624 agent
, stream
, component
, priority
, from
, socket
,
2626 remote_candidate2
? remote_candidate2
: remote_candidate
);
2629 priv_reply_to_conn_check (agent
, stream
, component
, remote_candidate
,
2630 from
, socket
, rbuf_len
, rbuf
, use_candidate
);
2632 if (component
->remote_candidates
== NULL
) {
2633 /* case: We've got a valid binding request to a local candidate
2634 * but we do not yet know remote credentials nor
2635 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2636 * immediately but postpone all other processing until
2637 * we get information about the remote candidates */
2639 /* step: send a reply immediately but postpone other processing */
2640 priv_store_pending_check (agent
, component
, from
, socket
,
2641 priority
, use_candidate
);
2644 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2645 agent
, strerror(errno
));
2649 /* case 2: not a new request, might be a reply... */
2650 gboolean trans_found
= FALSE
;
2652 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2654 /* step: let's try to match the response to an existing check context */
2655 if (trans_found
!= TRUE
)
2656 trans_found
= priv_map_reply_to_conn_check_request (agent
, stream
,
2657 component
, socket
, from
, local_candidate
, remote_candidate
, &req
);
2659 /* step: let's try to match the response to an existing discovery */
2660 if (trans_found
!= TRUE
)
2661 trans_found
= priv_map_reply_to_discovery_request (agent
, &req
);
2663 /* step: let's try to match the response to an existing turn allocate */
2664 if (trans_found
!= TRUE
)
2665 trans_found
= priv_map_reply_to_relay_request (agent
, &req
);
2667 /* step: let's try to match the response to an existing turn refresh */
2668 if (trans_found
!= TRUE
)
2669 trans_found
= priv_map_reply_to_relay_refresh (agent
, &req
);
2671 /* step: let's try to match the response to an existing keepalive conncheck */
2672 if (trans_found
!= TRUE
)
2673 trans_found
= priv_map_reply_to_keepalive_conncheck (agent
, component
,
2676 if (trans_found
!= TRUE
)
2677 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2678 "probably a keepalive.", agent
);