Fix bug where a discovered peer-reflexive pair does not get nominated
[sipe-libnice.git] / agent / conncheck.c
blob3c57734f261da5a121a1752dd73d099f69afba58
1 /*
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
17 * License.
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.
24 * Contributors:
25 * Kai Vehmanen, Nokia
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.
39 /**
40 * @file conncheck.c
41 * @brief ICE connectivity checks
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif
48 #include <errno.h>
49 #include <string.h>
51 #include <glib.h>
53 #include "debug.h"
55 #include "agent.h"
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);
69 static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
71 return (now->tv_sec == timer->tv_sec) ?
72 now->tv_usec >= timer->tv_usec :
73 now->tv_sec >= timer->tv_sec;
76 /**
77 * Finds the next connectivity check in WAITING state.
79 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
81 GSList *i;
83 /* note: list is sorted in priority order to first waiting check has
84 * the highest priority */
86 for (i = conn_check_list; i ; i = i->next) {
87 CandidateCheckPair *p = i->data;
88 if (p->state == NICE_CHECK_WAITING)
89 return p;
92 return NULL;
95 /**
96 * Initiates a new connectivity check for a ICE candidate pair.
98 * @return TRUE on success, FALSE on error
100 static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
102 /* XXX: from ID-16 onwards, the checks should not be sent
103 * immediately, but be put into the "triggered queue",
104 * see "7.2.1.4 Triggered Checks"
106 g_get_current_time (&pair->next_tick);
107 g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
108 pair->state = NICE_CHECK_IN_PROGRESS;
109 nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
110 conn_check_send (agent, pair);
111 return TRUE;
115 * Unfreezes the next connectivity check in the list. Follows the
116 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
117 * (ID-19), with some exceptions (see comments in code).
119 * See also sect 7.1.2.2.3 (Updating Pair States), and
120 * priv_conn_check_unfreeze_related().
122 * @return TRUE on success, and FALSE if no frozen candidates were found.
124 static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
126 CandidateCheckPair *pair = NULL;
127 GSList *i, *j;
129 /* XXX: the unfreezing is implemented a bit differently than in the
130 * current ICE spec, but should still be interoperate:
131 * - checks are not grouped by foundation
132 * - one frozen check is unfrozen (lowest component-id, highest
133 * priority)
136 for (i = agent->streams; i; i = i->next) {
137 Stream *stream = i->data;
138 guint64 max_frozen_priority = 0;
141 for (j = stream->conncheck_list; j ; j = j->next) {
142 CandidateCheckPair *p = j->data;
144 /* XXX: the prio check could be removed as the pairs are sorted
145 * already */
147 if (p->state == NICE_CHECK_FROZEN) {
148 if (p->priority > max_frozen_priority) {
149 max_frozen_priority = p->priority;
150 pair = p;
155 if (pair)
156 break;
159 if (pair) {
160 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation);
161 pair->state = NICE_CHECK_WAITING;
162 nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
163 return TRUE;
166 return FALSE;
170 * Unfreezes the next next connectivity check in the list after
171 * check 'success_check' has succesfully completed.
173 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
175 * @param agent context
176 * @param ok_check a connectivity check that has just completed
178 * @return TRUE on success, and FALSE if no frozen candidates were found.
180 static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check)
182 GSList *i, *j;
183 guint unfrozen = 0;
185 g_assert (ok_check);
186 g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
187 g_assert (stream);
188 g_assert (stream->id == ok_check->stream_id);
190 /* step: perform the step (1) of 'Updating Pair States' */
191 for (i = stream->conncheck_list; i ; i = i->next) {
192 CandidateCheckPair *p = i->data;
194 if (p->stream_id == ok_check->stream_id) {
195 if (p->state == NICE_CHECK_FROZEN &&
196 strcmp (p->foundation, ok_check->foundation) == 0) {
197 nice_debug ("Agent %p : Unfreezing check %p (after succesful check %p).", agent, p, ok_check);
198 p->state = NICE_CHECK_WAITING;
199 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
200 ++unfrozen;
205 /* step: perform the step (2) of 'Updating Pair States' */
206 stream = agent_find_stream (agent, ok_check->stream_id);
207 if (stream_all_components_ready (stream)) {
208 /* step: unfreeze checks from other streams */
209 for (i = agent->streams; i ; i = i->next) {
210 Stream *s = i->data;
211 for (j = stream->conncheck_list; j ; j = j->next) {
212 CandidateCheckPair *p = j->data;
214 if (p->stream_id == s->id &&
215 p->stream_id != ok_check->stream_id) {
216 if (p->state == NICE_CHECK_FROZEN &&
217 strcmp (p->foundation, ok_check->foundation) == 0) {
218 nice_debug ("Agent %p : Unfreezing check %p from stream %u (after succesful check %p).", agent, p, s->id, ok_check);
219 p->state = NICE_CHECK_WAITING;
220 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
221 ++unfrozen;
226 /* note: only unfreeze check from one stream at a time */
227 if (unfrozen)
228 break;
232 if (unfrozen == 0)
233 priv_conn_check_unfreeze_next (agent);
237 * Helper function for connectivity check timer callback that
238 * runs through the stream specific part of the state machine.
240 * @param schedule if TRUE, schedule a new check
242 * @return will return FALSE when no more pending timers.
244 static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now)
246 gboolean keep_timer_going = FALSE;
247 guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
248 s_nominated = 0, s_waiting_for_nomination = 0;
249 guint frozen = 0, waiting = 0;
250 GSList *i, *k;
252 for (i = stream->conncheck_list; i ; i = i->next) {
253 CandidateCheckPair *p = i->data;
255 if (p->state == NICE_CHECK_IN_PROGRESS) {
256 if (p->stun_message.buffer == NULL) {
257 nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent);
258 p->state = NICE_CHECK_FAILED;
259 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
260 } else if (priv_timer_expired (&p->next_tick, now)) {
261 switch (stun_timer_refresh (&p->timer)) {
262 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
263 /* case: error, abort processing */
264 nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p);
265 p->state = NICE_CHECK_FAILED;
266 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
267 p->stun_message.buffer = NULL;
268 p->stun_message.buffer_len = 0;
269 break;
270 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
272 /* case: not ready, so schedule a new timeout */
273 unsigned int timeout = stun_timer_remainder (&p->timer);
274 nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
275 agent, timeout);
277 nice_socket_send (p->local->sockptr, &p->remote->addr,
278 stun_message_length (&p->stun_message),
279 (gchar *)p->stun_buffer);
282 /* note: convert from milli to microseconds for g_time_val_add() */
283 p->next_tick = *now;
284 g_time_val_add (&p->next_tick, timeout * 1000);
286 keep_timer_going = TRUE;
287 break;
289 case STUN_USAGE_TIMER_RETURN_SUCCESS:
291 unsigned int timeout = stun_timer_remainder (&p->timer);
293 /* note: convert from milli to microseconds for g_time_val_add() */
294 p->next_tick = *now;
295 g_time_val_add (&p->next_tick, timeout * 1000);
297 keep_timer_going = TRUE;
298 break;
304 if (p->state == NICE_CHECK_FROZEN)
305 ++frozen;
306 else if (p->state == NICE_CHECK_IN_PROGRESS)
307 ++s_inprogress;
308 else if (p->state == NICE_CHECK_WAITING)
309 ++waiting;
310 else if (p->state == NICE_CHECK_SUCCEEDED)
311 ++s_succeeded;
312 else if (p->state == NICE_CHECK_DISCOVERED)
313 ++s_discovered;
315 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
316 && p->nominated)
317 ++s_nominated;
318 else if ((p->state == NICE_CHECK_SUCCEEDED ||
319 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
320 ++s_waiting_for_nomination;
323 /* note: keep the timer going as long as there is work to be done */
324 if (s_inprogress)
325 keep_timer_going = TRUE;
327 /* note: if some components have established connectivity,
328 * but yet no nominated pair, keep timer going */
329 if (s_nominated < stream->n_components &&
330 s_waiting_for_nomination) {
331 keep_timer_going = TRUE;
332 if (agent->controlling_mode) {
333 guint n;
334 for (n = 0; n < stream->n_components; n++) {
335 for (k = stream->conncheck_list; k ; k = k->next) {
336 CandidateCheckPair *p = k->data;
337 /* note: highest priority item selected (list always sorted) */
338 if (p->state == NICE_CHECK_SUCCEEDED ||
339 p->state == NICE_CHECK_DISCOVERED) {
340 nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
341 p->nominated = TRUE;
342 priv_conn_check_initiate (agent, p);
343 break; /* move to the next component */
350 static int tick_counter = 0;
351 if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
352 nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
353 "%u waiting, %u succeeded, %u discovered, %u nominated, "
354 "%u waiting-for-nom.", agent,
355 tick_counter, frozen, s_inprogress, waiting, s_succeeded,
356 s_discovered, s_nominated, s_waiting_for_nomination);
359 return keep_timer_going;
365 * Timer callback that handles initiating and managing connectivity
366 * checks (paced by the Ta timer).
368 * This function is designed for the g_timeout_add() interface.
370 * @return will return FALSE when no more pending timers.
372 static gboolean priv_conn_check_tick_unlocked (gpointer pointer)
374 CandidateCheckPair *pair = NULL;
375 NiceAgent *agent = pointer;
376 gboolean keep_timer_going = FALSE;
377 GSList *i, *j;
378 GTimeVal now;
380 /* step: process ongoing STUN transactions */
381 g_get_current_time (&now);
383 /* step: find the highest priority waiting check and send it */
384 for (i = agent->streams; i ; i = i->next) {
385 Stream *stream = i->data;
387 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
388 if (pair)
389 break;
392 if (pair) {
393 priv_conn_check_initiate (agent, pair);
394 keep_timer_going = TRUE;
395 } else {
396 keep_timer_going = priv_conn_check_unfreeze_next (agent);
399 for (j = agent->streams; j; j = j->next) {
400 Stream *stream = j->data;
401 gboolean res =
402 priv_conn_check_tick_stream (stream, agent, &now);
403 if (res)
404 keep_timer_going = res;
407 /* step: stop timer if no work left */
408 if (keep_timer_going != TRUE) {
409 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
410 for (i = agent->streams; i; i = i->next) {
411 Stream *stream = i->data;
412 priv_update_check_list_failed_components (agent, stream);
413 for (j = stream->components; j; j = j->next) {
414 Component *component = j->data;
415 priv_update_check_list_state_for_ready (agent, stream, component);
417 stream->conncheck_state = NICE_CHECKLIST_COMPLETED;
420 /* Stopping the timer so destroy the source.. this will allow
421 the timer to be reset if we get a set_remote_candidates after this
422 point */
423 if (agent->conncheck_timer_source != NULL) {
424 g_source_destroy (agent->conncheck_timer_source);
425 g_source_unref (agent->conncheck_timer_source);
426 agent->conncheck_timer_source = NULL;
429 /* XXX: what to signal, is all processing now really done? */
430 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
433 return keep_timer_going;
436 static gboolean priv_conn_check_tick (gpointer pointer)
438 NiceAgent *agent = pointer;
439 gboolean ret;
441 g_static_rec_mutex_lock (&agent->mutex);
442 ret = priv_conn_check_tick_unlocked (pointer);
443 g_static_rec_mutex_unlock (&agent->mutex);
445 return ret;
449 * Timer callback that handles initiating and managing connectivity
450 * checks (paced by the Ta timer).
452 * This function is designed for the g_timeout_add() interface.
454 * @return will return FALSE when no more pending timers.
456 static gboolean priv_conn_keepalive_tick (gpointer pointer)
458 NiceAgent *agent = pointer;
459 GSList *i, *j;
460 int errors = 0;
461 gboolean ret = FALSE;
462 StunMessage msg;
463 uint8_t buf[STUN_MAX_MESSAGE_SIZE];
464 size_t buf_len;
466 g_static_rec_mutex_lock (&agent->mutex);
468 /* case 1: session established and media flowing
469 * (ref ICE sect 10 "Keepalives" ID-19) */
470 for (i = agent->streams; i; i = i->next) {
472 Stream *stream = i->data;
473 for (j = stream->components; j; j = j->next) {
474 Component *component = j->data;
475 if (component->selected_pair.local != NULL &&
476 component->media_after_tick != TRUE) {
477 CandidatePair *p = &component->selected_pair;
478 struct sockaddr sockaddr;
480 memset (&sockaddr, 0, sizeof (sockaddr));
481 nice_address_copy_to_sockaddr (&p->remote->addr, &sockaddr);
483 buf_len = stun_usage_bind_keepalive (&agent->stun_agent, &msg,
484 buf, sizeof(buf));
486 nice_socket_send (p->local->sockptr, &p->remote->addr, buf_len, (gchar *)buf);
488 nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
489 agent, p, (int) buf_len);
490 if (buf_len == 0)
491 ++errors;
493 component->media_after_tick = FALSE;
497 /* case 2: connectivity establishment ongoing
498 * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */
499 for (i = agent->streams; i; i = i->next) {
500 Stream *stream = i->data;
501 if (stream->conncheck_state == NICE_CHECKLIST_RUNNING) {
502 for (j = stream->conncheck_list; j ; j = j->next) {
503 CandidateCheckPair *p = j->data;
505 nice_debug ("Agent %p : resending STUN-CC to keep the candidate alive (pair %p).", agent, p);
506 conn_check_send (agent, p);
511 if (errors) {
512 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
513 goto done;
516 ret = TRUE;
518 done:
519 g_static_rec_mutex_unlock (&agent->mutex);
520 return ret;
523 static gboolean priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer)
525 CandidateRefresh *cand = (CandidateRefresh *) pointer;
526 guint timeout;
528 g_static_rec_mutex_lock (&cand->agent->mutex);
530 g_source_destroy (cand->tick_source);
531 g_source_unref (cand->tick_source);
532 cand->tick_source = NULL;
534 switch (stun_timer_refresh (&cand->timer)) {
535 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
536 /* Time out */
537 refresh_cancel (cand);
538 break;
539 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
540 /* Retransmit */
541 nice_socket_send (cand->nicesock, &cand->server,
542 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
544 timeout = stun_timer_remainder (&cand->timer);
545 cand->tick_source = agent_timeout_add_with_context (cand->agent, timeout,
546 priv_turn_allocate_refresh_retransmissions_tick, cand);
547 break;
548 case STUN_USAGE_TIMER_RETURN_SUCCESS:
549 timeout = stun_timer_remainder (&cand->timer);
550 cand->tick_source = agent_timeout_add_with_context (cand->agent, timeout,
551 priv_turn_allocate_refresh_retransmissions_tick, cand);
552 break;
556 g_static_rec_mutex_unlock (&cand->agent->mutex);
557 return FALSE;
560 static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand)
562 uint8_t *username;
563 size_t username_len;
564 uint8_t *password;
565 size_t password_len;
566 size_t buffer_len = 0;
568 username = (uint8_t *)cand->turn->username;
569 username_len = (size_t) strlen (cand->turn->username);
570 password = (uint8_t *)cand->turn->password;
571 password_len = (size_t) strlen (cand->turn->password);
573 if (cand->agent->compatibility == NICE_COMPATIBILITY_MSN) {
574 username = g_base64_decode ((gchar *)username, &username_len);
575 password = g_base64_decode ((gchar *)password, &password_len);
578 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
579 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
580 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, -1,
581 username, username_len,
582 password, password_len,
583 agent_to_turn_compatibility (cand->agent));
585 if (cand->agent->compatibility == NICE_COMPATIBILITY_MSN) {
586 g_free (cand->msn_turn_username);
587 g_free (cand->msn_turn_password);
588 cand->msn_turn_username = username;
589 cand->msn_turn_password = password;
592 nice_debug ("Agent %p : Sending allocate Refresh %d", cand->agent, buffer_len);
594 if (buffer_len > 0) {
595 stun_timer_start (&cand->timer);
597 /* send the refresh */
598 nice_socket_send (cand->nicesock, &cand->server,
599 buffer_len, (gchar *)cand->stun_buffer);
601 if (cand->tick_source != NULL) {
602 g_source_destroy (cand->tick_source);
603 g_source_unref (cand->tick_source);
604 cand->tick_source = NULL;
607 cand->tick_source = agent_timeout_add_with_context (cand->agent,
608 stun_timer_remainder (&cand->timer),
609 priv_turn_allocate_refresh_retransmissions_tick, cand);
616 * Timer callback that handles refreshing TURN allocations
618 * This function is designed for the g_timeout_add() interface.
620 * @return will return FALSE when no more pending timers.
622 static gboolean priv_turn_allocate_refresh_tick (gpointer pointer)
624 CandidateRefresh *cand = (CandidateRefresh *) pointer;
626 g_static_rec_mutex_lock (&cand->agent->mutex);
627 priv_turn_allocate_refresh_tick_unlocked (cand);
628 g_static_rec_mutex_unlock (&cand->agent->mutex);
630 return FALSE;
635 * Initiates the next pending connectivity check.
637 * @return TRUE if a pending check was scheduled
639 gboolean conn_check_schedule_next (NiceAgent *agent)
641 gboolean res = priv_conn_check_unfreeze_next (agent);
642 nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res);
644 if (agent->discovery_unsched_items > 0)
645 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
647 if (res == TRUE) {
648 /* step: call once imediately */
649 res = priv_conn_check_tick_unlocked ((gpointer) agent);
650 nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res);
652 /* step: schedule timer if not running yet */
653 if (res && agent->conncheck_timer_source == NULL) {
654 agent->conncheck_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_conn_check_tick, agent);
657 /* step: also start the keepalive timer */
658 if (agent->keepalive_timer_source == NULL) {
659 agent->keepalive_timer_source = agent_timeout_add_with_context (agent, NICE_AGENT_TIMER_TR_DEFAULT, priv_conn_keepalive_tick, agent);
664 nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res);
665 return res;
668 /**
669 * Compares two connectivity check items. Checkpairs are sorted
670 * in descending priority order, with highest priority item at
671 * the start of the list.
673 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
675 if (a->priority > b->priority)
676 return -1;
677 else if (a->priority < b->priority)
678 return 1;
679 return 0;
683 * Preprocesses a new connectivity check by going through list
684 * of a any stored early incoming connectivity checks from
685 * the remote peer. If a matching incoming check has been already
686 * received, update the state of the new outgoing check 'pair'.
688 * @param agent context pointer
689 * @param stream which stream (of the agent)
690 * @param component pointer to component object to which 'pair'has been added
691 * @param pair newly added connectivity check
693 static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *pair)
695 GSList *i;
696 for (i = component->incoming_checks; i; i = i->next) {
697 IncomingCheck *icheck = i->data;
698 if (nice_address_equal (&icheck->from, &pair->remote->addr) &&
699 icheck->local_socket == pair->local->sockptr) {
700 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);
701 if (icheck->use_candidate)
702 priv_mark_pair_nominated (agent, stream, component, pair->remote);
703 priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate);
709 * Handle any processing steps for connectivity checks after
710 * remote candidates have been set. This function handles
711 * the special case where answerer has sent us connectivity
712 * checks before the answer (containing candidate information),
713 * reaches us. The special case is documented in sect 7.2
714 * if ICE spec (ID-19).
716 void conn_check_remote_candidates_set(NiceAgent *agent)
718 GSList *i, *j, *k, *l;
719 for (i = agent->streams; i ; i = i->next) {
720 Stream *stream = i->data;
721 for (j = stream->conncheck_list; j ; j = j->next) {
722 CandidateCheckPair *pair = j->data;
723 Component *component = stream_find_component_by_id (stream, pair->component_id);
724 gboolean match = FALSE;
726 /* performn delayed processing of spec steps section 7.2.1.4,
727 and section 7.2.1.5 */
728 priv_preprocess_conn_check_pending_data (agent, stream, component, pair);
730 for (k = component->incoming_checks; k; k = k->next) {
731 IncomingCheck *icheck = k->data;
732 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
733 * be handled separately */
734 for (l = component->remote_candidates; l; l = l->next) {
735 NiceCandidate *cand = l->data;
736 if (nice_address_equal (&icheck->from, &cand->addr)) {
737 match = TRUE;
738 break;
741 if (match != TRUE) {
742 /* note: we have gotten an incoming connectivity check from
743 * an address that is not a known remote candidate */
744 NiceCandidate *candidate =
745 discovery_learn_remote_peer_reflexive_candidate (agent,
746 stream,
747 component,
748 icheck->priority,
749 &icheck->from,
750 icheck->local_socket,
751 NULL, NULL);
752 if (candidate) {
753 priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate);
761 /**
762 * Enforces the upper limit for connectivity checks as described
763 * in ICE spec section 5.7.3 (ID-19). See also
764 * conn_check_add_for_candidate().
766 static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
768 guint list_len = g_slist_length (conncheck_list);
769 guint c = 0;
770 GSList *result = conncheck_list;
772 if (list_len > upper_limit) {
773 c = list_len - upper_limit;
774 if (c == list_len) {
775 /* case: delete whole list */
776 g_slist_foreach (conncheck_list, conn_check_free_item, NULL);
777 g_slist_free (conncheck_list),
778 result = NULL;
780 else {
781 /* case: remove 'c' items from list end (lowest priority) */
782 GSList *i, *tmp;
784 g_assert (c > 0);
785 i = g_slist_nth (conncheck_list, c - 1);
787 tmp = i->next;
788 i->next = NULL;
790 if (tmp) {
791 /* delete the rest of the connectivity check list */
792 g_slist_foreach (tmp, conn_check_free_item, NULL);
793 g_slist_free (tmp);
798 return result;
802 * Changes the selected pair for the component if 'pair' is nominated
803 * and has higher priority than the currently selected pair. See
804 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
806 static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
808 g_assert (component);
809 g_assert (pair);
810 if (pair->priority > component->selected_pair.priority) {
811 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s (prio:%lu).", agent,
812 component->id, pair->local->foundation, pair->remote->foundation, (long unsigned)pair->priority);
813 component->selected_pair.local = pair->local;
814 component->selected_pair.remote = pair->remote;
815 component->selected_pair.priority = pair->priority;
817 agent_signal_new_selected_pair (agent, pair->stream_id, component->id, pair->local->foundation, pair->remote->foundation);
821 return TRUE;
825 * Updates the check list state.
827 * Implements parts of the algorithm described in
828 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
829 * component, all checks have been completed and have
830 * failed, mark that component's state to NICE_CHECK_FAILED.
832 * Sends a component state changesignal via 'agent'.
834 static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream)
836 GSList *i;
837 /* note: emitting a signal might cause the client
838 * to remove the stream, thus the component count
839 * must be fetched before entering the loop*/
840 guint c, components = stream->n_components;
842 /* note: iterate the conncheck list for each component separately */
843 for (c = 0; c < components; c++) {
844 Component *comp = NULL;
845 agent_find_component (agent, stream->id, c+1, NULL, &comp);
847 for (i = stream->conncheck_list; i; i = i->next) {
848 CandidateCheckPair *p = i->data;
850 if (p->stream_id == stream->id &&
851 p->component_id == (c + 1)) {
852 if (p->state != NICE_CHECK_FAILED)
853 break;
857 /* note: all checks have failed
858 * Set the component to FAILED only if it actually had remote candidates
859 * that failed.. */
860 if (i == NULL && comp != NULL && comp->remote_candidates != NULL)
861 agent_signal_component_state_change (agent,
862 stream->id,
863 (c + 1), /* component-id */
864 NICE_COMPONENT_STATE_FAILED);
869 * Updates the check list state for a stream component.
871 * Implements the algorithm described in ICE sect 8.1.2
872 * "Updating States" (ID-19) as it applies to checks of
873 * a certain component. If there are any nominated pairs,
874 * ICE processing may be concluded, and component state is
875 * changed to READY.
877 * Sends a component state changesignal via 'agent'.
879 static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component)
881 GSList *i;
882 guint succeeded = 0, nominated = 0;
884 g_assert (component);
886 /* step: search for at least one nominated pair */
887 for (i = stream->conncheck_list; i; i = i->next) {
888 CandidateCheckPair *p = i->data;
889 if (p->component_id == component->id) {
890 if (p->state == NICE_CHECK_SUCCEEDED ||
891 p->state == NICE_CHECK_DISCOVERED) {
892 ++succeeded;
893 if (p->nominated == TRUE) {
894 ++nominated;
895 priv_prune_pending_checks (stream, p->component_id);
896 agent_signal_component_state_change (agent,
897 p->stream_id,
898 p->component_id,
899 NICE_COMPONENT_STATE_READY);
905 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id);
909 * The remote party has signalled that the candidate pair
910 * described by 'component' and 'remotecand' is nominated
911 * for use.
913 static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand)
915 GSList *i;
917 g_assert (component);
919 /* step: search for at least one nominated pair */
920 for (i = stream->conncheck_list; i; i = i->next) {
921 CandidateCheckPair *pair = i->data;
922 /* XXX: hmm, how to figure out to which local candidate the
923 * check was sent to? let's mark all matching pairs
924 * as nominated instead */
925 if (pair->remote == remotecand) {
926 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
927 pair->nominated = TRUE;
928 if (pair->state == NICE_CHECK_SUCCEEDED ||
929 pair->state == NICE_CHECK_DISCOVERED)
930 priv_update_selected_pair (agent, component, pair);
931 priv_update_check_list_state_for_ready (agent, stream, component);
937 * Creates a new connectivity check pair and adds it to
938 * the agent's list of checks.
940 static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
942 gboolean result = FALSE;
943 Stream *stream = agent_find_stream (agent, stream_id);
944 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
945 if (pair) {
946 GSList *modified_list =
947 g_slist_insert_sorted (stream->conncheck_list, pair, (GCompareFunc)conn_check_compare);
948 if (modified_list) {
949 /* step: allocation and addition succesful, do rest of the work */
951 pair->agent = agent;
952 pair->stream_id = stream_id;
953 pair->component_id = component->id;;
954 pair->local = local;
955 pair->remote = remote;
956 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
958 pair->priority = agent_candidate_pair_priority (agent, local, remote);
959 pair->state = initial_state;
960 nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state);
961 pair->nominated = use_candidate;
962 pair->controlling = agent->controlling_mode;
964 /* note: for the first added check */
965 if (!stream->conncheck_list)
966 stream->conncheck_state = NICE_CHECKLIST_RUNNING;
967 stream->conncheck_list = modified_list;
969 result = TRUE;
970 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id);
972 /* implement the hard upper limit for number of
973 checks (see sect 5.7.3 ICE ID-19): */
974 stream->conncheck_list =
975 priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
976 if (!stream->conncheck_list) {
977 stream->conncheck_state = NICE_CHECKLIST_FAILED;
978 result = FALSE;
981 else {
982 /* memory alloc failed: list insert */
983 conn_check_free_item (pair, NULL);
984 stream->conncheck_state = NICE_CHECKLIST_FAILED;
987 else { /* memory alloc failed: new pair */
988 stream->conncheck_state = NICE_CHECKLIST_FAILED;
991 return result;
995 * Forms new candidate pairs by matching the new remote candidate
996 * 'remote_cand' with all existing local candidates of 'component'.
997 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
998 * Pairs" (ID-19).
1000 * @param agent context
1001 * @param component pointer to the component
1002 * @param remote remote candidate to match with
1004 * @return number of checks added, negative on fatal errors
1006 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
1008 GSList *i;
1009 int added = 0;
1011 for (i = component->local_candidates; i ; i = i->next) {
1013 NiceCandidate *local = i->data;
1015 /* note: match pairs only if transport and address family are the same */
1016 if (local->transport == remote->transport &&
1017 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
1019 gboolean result;
1021 /* note: do not create pairs where local candidate is
1022 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1023 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
1024 local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE)
1025 continue;
1027 result = priv_add_new_check_pair (agent, stream_id, component, local, remote, NICE_CHECK_FROZEN, FALSE);
1028 if (result) {
1029 ++added;
1030 if (component->state < NICE_COMPONENT_STATE_CONNECTED) {
1031 agent_signal_component_state_change (agent,
1032 stream_id,
1033 component->id,
1034 NICE_COMPONENT_STATE_CONNECTING);
1035 } else {
1036 agent_signal_component_state_change (agent,
1037 stream_id,
1038 component->id,
1039 NICE_COMPONENT_STATE_CONNECTED);
1042 else {
1043 added = -1;
1044 break;
1049 return added;
1053 * Frees the CandidateCheckPair structure pointer to
1054 * by 'user data'. Compatible with g_slist_foreach().
1056 void conn_check_free_item (gpointer data, gpointer user_data)
1058 CandidateCheckPair *pair = data;
1059 g_assert (user_data == NULL);
1060 pair->stun_message.buffer = NULL;
1061 pair->stun_message.buffer_len = 0;
1062 g_slice_free (CandidateCheckPair, pair);
1066 * Frees all resources of all connectivity checks.
1068 void conn_check_free (NiceAgent *agent)
1070 GSList *i;
1071 for (i = agent->streams; i; i = i->next) {
1072 Stream *stream = i->data;
1074 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
1075 if (stream->conncheck_list) {
1076 g_slist_foreach (stream->conncheck_list, conn_check_free_item, NULL);
1077 g_slist_free (stream->conncheck_list),
1078 stream->conncheck_list = NULL;
1079 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1083 if (agent->conncheck_timer_source != NULL) {
1084 g_source_destroy (agent->conncheck_timer_source);
1085 g_source_unref (agent->conncheck_timer_source);
1086 agent->conncheck_timer_source = NULL;
1091 * Prunes the list of connectivity checks for items related
1092 * to stream 'stream_id'.
1094 * @return TRUE on success, FALSE on a fatal error
1096 gboolean conn_check_prune_stream (NiceAgent *agent, Stream *stream)
1098 CandidateCheckPair *pair;
1099 GSList *i;
1101 for (i = stream->conncheck_list; i ; ) {
1102 GSList *next = i->next;
1103 pair = i->data;
1105 g_assert (pair->stream_id == stream->id);
1107 stream->conncheck_list =
1108 g_slist_remove (stream->conncheck_list, pair);
1109 conn_check_free_item (pair, NULL);
1110 i = next;
1111 if (!stream->conncheck_list)
1112 break;
1115 if (!stream->conncheck_list) {
1116 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1117 conn_check_free (agent);
1120 /* return FALSE if there was a memory allocation failure */
1121 if (stream->conncheck_list == NULL && i != NULL)
1122 return FALSE;
1124 return TRUE;
1128 * Fills 'dest' with a username string for use in an outbound connectivity
1129 * checks. No more than 'dest_len' characters (including terminating
1130 * NULL) is ever written to the 'dest'.
1132 static
1133 size_t priv_gen_username (NiceAgent *agent, guint component_id,
1134 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
1136 guint len = 0;
1137 gsize remote_len = strlen (remote);
1138 gsize local_len = strlen (local);
1140 if (remote_len > 0 && local_len > 0) {
1141 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
1142 dest_len >= remote_len + local_len + 1) {
1143 memcpy (dest, remote, remote_len);
1144 len += remote_len;
1145 memcpy (dest + len, ":", 1);
1146 len++;
1147 memcpy (dest + len, local, local_len);
1148 len += local_len;
1149 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
1150 dest_len >= remote_len + local_len) {
1151 memcpy (dest, remote, remote_len);
1152 len += remote_len;
1153 memcpy (dest + len, local, local_len);
1154 len += local_len;
1155 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1156 gchar component_str[10];
1157 guchar *local_decoded = NULL;
1158 guchar *remote_decoded = NULL;
1159 gsize local_decoded_len;
1160 gsize remote_decoded_len;
1161 gsize total_len;
1162 int padding;
1164 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
1165 local_decoded = g_base64_decode (local, &local_decoded_len);
1166 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
1168 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
1169 padding = 4 - (total_len % 4);
1171 if (dest_len >= total_len + padding) {
1172 guchar pad_char[1] = {0};
1173 int i;
1175 memcpy (dest, remote_decoded, remote_decoded_len);
1176 len += remote_decoded_len;
1177 memcpy (dest + len, ":", 1);
1178 len++;
1179 memcpy (dest + len, component_str, strlen (component_str));
1180 len += strlen (component_str);
1182 memcpy (dest + len, ":", 1);
1183 len++;
1185 memcpy (dest + len, local_decoded, local_decoded_len);
1186 len += local_decoded_len;
1187 memcpy (dest + len, ":", 1);
1188 len++;
1189 memcpy (dest + len, component_str, strlen (component_str));;
1190 len += strlen (component_str);
1192 for (i = 0; i < padding; i++) {
1193 memcpy (dest + len, pad_char, 1);
1194 len++;
1199 g_free (local_decoded);
1200 g_free (remote_decoded);
1204 return len;
1208 * Fills 'dest' with a username string for use in an outbound connectivity
1209 * checks. No more than 'dest_len' characters (including terminating
1210 * NULL) is ever written to the 'dest'.
1212 static
1213 size_t priv_create_username (NiceAgent *agent, Stream *stream,
1214 guint component_id, NiceCandidate *remote, NiceCandidate *local,
1215 uint8_t *dest, guint dest_len, gboolean inbound)
1217 gchar *local_username = NULL;
1218 gchar *remote_username = NULL;
1221 if (remote && remote->username) {
1222 remote_username = remote->username;
1225 if (local && local->username) {
1226 local_username = local->username;
1229 if (stream) {
1230 if (remote_username == NULL) {
1231 remote_username = stream->remote_ufrag;
1233 if (local_username == NULL) {
1234 local_username = stream->local_ufrag;
1238 if (local_username && remote_username) {
1239 if (inbound) {
1240 return priv_gen_username (agent, component_id,
1241 local_username, remote_username, dest, dest_len);
1242 } else {
1243 return priv_gen_username (agent, component_id,
1244 remote_username, local_username, dest, dest_len);
1248 return 0;
1252 * Returns a password string for use in an outbound connectivity
1253 * check.
1255 static
1256 size_t priv_get_password (NiceAgent *agent, Stream *stream,
1257 NiceCandidate *remote, uint8_t **password)
1259 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
1260 return 0;
1262 if (remote && remote->password) {
1263 *password = (uint8_t *)remote->password;
1264 return strlen (remote->password);
1267 if (stream) {
1268 *password = (uint8_t *)stream->remote_password;
1269 return strlen (stream->remote_password);
1272 return 0;
1276 * Sends a connectivity check over candidate pair 'pair'.
1278 * @return zero on success, non-zero on error
1280 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
1283 /* note: following information is supplied:
1284 * - username (for USERNAME attribute)
1285 * - password (for MESSAGE-INTEGRITY)
1286 * - priority (for PRIORITY)
1287 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1288 * - USE-CANDIDATE (if sent by the controlling agent)
1291 guint32 priority =
1292 nice_candidate_ice_priority_full (
1293 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE,
1295 pair->local->component_id);
1297 uint8_t uname[NICE_STREAM_MAX_UNAME];
1298 size_t uname_len =
1299 priv_create_username (agent, agent_find_stream (agent, pair->stream_id),
1300 pair->component_id, pair->remote, pair->local, uname, sizeof (uname), FALSE);
1301 uint8_t *password = NULL;
1302 size_t password_len = priv_get_password (agent,
1303 agent_find_stream (agent, pair->stream_id), pair->remote, &password);
1305 bool controlling = agent->controlling_mode;
1306 /* XXX: add API to support different nomination modes: */
1307 bool cand_use = controlling;
1308 size_t buffer_len;
1310 struct sockaddr sockaddr;
1311 unsigned int timeout;
1313 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1314 password = g_base64_decode ((gchar *) password, &password_len);
1317 memset (&sockaddr, 0, sizeof (sockaddr));
1319 nice_address_copy_to_sockaddr (&pair->remote->addr, &sockaddr);
1322 gchar tmpbuf[INET6_ADDRSTRLEN];
1323 nice_address_to_string (&pair->remote->addr, tmpbuf);
1324 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,
1325 tmpbuf,
1326 ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
1327 pair->local->sockptr->fileno,
1328 pair->foundation, pair->component_id,
1329 (unsigned long long)agent->tie_breaker,
1330 uname, uname_len, password, password_len, priority);
1334 if (cand_use)
1335 pair->nominated = controlling;
1337 if (uname_len > 0) {
1339 buffer_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
1340 &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer),
1341 uname, uname_len, password, password_len,
1342 cand_use, controlling, priority,
1343 agent->tie_breaker,
1344 agent_to_ice_compatibility (agent));
1346 nice_debug ("Agent %p: conncheck created %d - %p", agent, buffer_len, pair->stun_message.buffer);
1348 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1349 g_free (password);
1352 stun_timer_start (&pair->timer);
1354 /* send the conncheck */
1355 nice_socket_send (pair->local->sockptr, &pair->remote->addr,
1356 buffer_len, (gchar *)pair->stun_buffer);
1358 timeout = stun_timer_remainder (&pair->timer);
1359 /* note: convert from milli to microseconds for g_time_val_add() */
1360 g_get_current_time (&pair->next_tick);
1361 g_time_val_add (&pair->next_tick, timeout * 1000);
1364 return 0;
1368 * Implemented the pruning steps described in ICE sect 8.1.2
1369 * "Updating States" (ID-19) after a pair has been nominated.
1371 * @see priv_update_check_list_state_failed_components()
1373 static void priv_prune_pending_checks (Stream *stream, guint component_id)
1375 GSList *i;
1377 /* step: cancel all FROZEN and WAITING pairs for the component */
1378 for (i = stream->conncheck_list; i; i = i->next) {
1379 CandidateCheckPair *p = i->data;
1380 if (p->component_id == component_id) {
1381 if (p->state == NICE_CHECK_FROZEN ||
1382 p->state == NICE_CHECK_WAITING) {
1383 p->state = NICE_CHECK_CANCELLED;
1384 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1387 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1388 if (p->state == NICE_CHECK_IN_PROGRESS) {
1389 p->stun_message.buffer = NULL;
1390 p->stun_message.buffer_len = 0;
1391 p->state = NICE_CHECK_CANCELLED;
1392 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1399 * Schedules a triggered check after a succesfully inbound
1400 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1402 * @param agent self pointer
1403 * @param component the check is related to
1404 * @param local_socket socket from which the inbound check was received
1405 * @param remote_cand remote candidate from which the inbound check was sent
1406 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1408 static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate)
1410 GSList *i;
1411 gboolean result = FALSE;
1413 for (i = stream->conncheck_list; i ; i = i->next) {
1414 CandidateCheckPair *p = i->data;
1415 if (p->component_id == component->id &&
1416 p->remote == remote_cand &&
1417 p->local->sockptr == local_socket) {
1419 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p);
1421 if (p->state == NICE_CHECK_WAITING ||
1422 p->state == NICE_CHECK_FROZEN)
1423 priv_conn_check_initiate (agent, p);
1424 else if (p->state == NICE_CHECK_IN_PROGRESS) {
1425 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1426 * we should cancel the existing one, and send a new one...? :P */
1427 nice_debug ("Agent %p : Skipping triggered check, already in progress..", agent);
1429 else if (p->state == NICE_CHECK_SUCCEEDED ||
1430 p->state == NICE_CHECK_DISCOVERED) {
1431 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent);
1432 /* note: this is a bit unsure corner-case -- let's do the
1433 same state update as for processing responses to our own checks */
1434 priv_update_check_list_state_for_ready (agent, stream, component);
1436 /* note: to take care of the controlling-controlling case in
1437 * aggressive nomination mode, send a new triggered
1438 * check to nominate the pair */
1439 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
1440 agent->controlling_mode)
1441 priv_conn_check_initiate (agent, p);
1444 /* note: the spec says the we SHOULD retransmit in-progress
1445 * checks immediately, but we won't do that now */
1447 return TRUE;
1452 NiceCandidate *local = NULL;
1454 for (i = component->local_candidates; i ; i = i->next) {
1455 local = i->data;
1456 if (local->sockptr == local_socket)
1457 break;
1459 if (i) {
1460 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
1461 result = priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate);
1463 else
1464 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
1467 return result;
1472 * Sends a reply to an succesfully received STUN connectivity
1473 * check request. Implements parts of the ICE spec section 7.2 (STUN
1474 * Server Procedures).
1476 * @param agent context pointer
1477 * @param stream which stream (of the agent)
1478 * @param component which component (of the stream)
1479 * @param rcand remote candidate from which the request came, if NULL,
1480 * the response is sent immediately but no other processing is done
1481 * @param toaddr address to which reply is sent
1482 * @param socket the socket over which the request came
1483 * @param rbuf_len length of STUN message to send
1484 * @param rbuf buffer containing the STUN message to send
1485 * @param use_candidate whether the request had USE_CANDIDATE attribute
1487 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1489 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)
1491 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
1494 gchar tmpbuf[INET6_ADDRSTRLEN];
1495 nice_address_to_string (toaddr, tmpbuf);
1496 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent,
1497 tmpbuf,
1498 nice_address_get_port (toaddr),
1499 socket->fileno,
1500 (unsigned)rbuf_len,
1501 rcand, component->id,
1502 (int)use_candidate);
1505 nice_socket_send (socket, toaddr, rbuf_len, (const gchar*)rbuf);
1507 if (rcand) {
1508 /* note: upon succesful check, make the reserve check immediately */
1509 priv_schedule_triggered_check (agent, stream, component, socket, rcand, use_candidate);
1511 if (use_candidate)
1512 priv_mark_pair_nominated (agent, stream, component, rcand);
1517 * Stores information of an incoming STUN connectivity check
1518 * for later use. This is only needed when a check is received
1519 * before we get information about the remote candidates (via
1520 * SDP or other signaling means).
1522 * @return non-zero on error, zero on success
1524 static int priv_store_pending_check (NiceAgent *agent, Component *component, const NiceAddress *from, NiceSocket *socket, uint32_t priority, gboolean use_candidate)
1526 IncomingCheck *icheck;
1527 nice_debug ("Agent %p : Storing pending check.", agent);
1529 if (component->incoming_checks &&
1530 g_slist_length (component->incoming_checks) >=
1531 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
1532 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
1533 return -1;
1536 icheck = g_slice_new0 (IncomingCheck);
1537 if (icheck) {
1538 GSList *pending = g_slist_append (component->incoming_checks, icheck);
1539 if (pending) {
1540 component->incoming_checks = pending;
1541 icheck->from = *from;
1542 icheck->local_socket = socket;
1543 icheck->priority = priority;
1544 icheck->use_candidate = use_candidate;
1545 return 0;
1549 return -1;
1553 * Adds a new pair, discovered from an incoming STUN response, to
1554 * the connectivity check list.
1556 * @return created pair, or NULL on fatal (memory allocation) errors
1558 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
1560 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1561 if (pair) {
1562 Stream *stream = agent_find_stream (agent, stream_id);
1563 GSList *modified_list = g_slist_append (stream->conncheck_list, pair);
1564 if (modified_list) {
1565 stream->conncheck_list = modified_list;
1566 pair->agent = agent;
1567 pair->stream_id = stream_id;
1568 pair->component_id = component_id;;
1569 pair->local = local_cand;
1570 pair->remote = parent_pair->remote;
1571 pair->state = NICE_CHECK_DISCOVERED;
1572 nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair);
1573 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local_cand->foundation, parent_pair->remote->foundation);
1574 if (agent->controlling_mode == TRUE)
1575 pair->priority = nice_candidate_pair_priority (local_cand->priority, parent_pair->priority);
1576 else
1577 pair->priority = nice_candidate_pair_priority (parent_pair->priority, local_cand->priority);
1578 pair->nominated = FALSE;
1579 pair->controlling = agent->controlling_mode;
1580 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation);
1581 return pair;
1585 return NULL;
1589 * Recalculates priorities of all candidate pairs. This
1590 * is required after a conflict in ICE roles.
1592 static void priv_recalculate_pair_priorities (NiceAgent *agent)
1594 GSList *i, *j;
1596 for (i = agent->streams; i; i = i->next) {
1597 Stream *stream = i->data;
1598 for (j = stream->conncheck_list; j; j = j->next) {
1599 CandidateCheckPair *p = j->data;
1600 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
1606 * Change the agent role if different from 'control'. Can be
1607 * initiated both by handling of incoming connectivity checks,
1608 * and by processing the responses to checks sent by us.
1610 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
1612 /* role conflict, change mode; wait for a new conn. check */
1613 if (control != agent->controlling_mode) {
1614 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control);
1615 agent->controlling_mode = control;
1616 /* the pair priorities depend on the roles, so recalculation
1617 * is needed */
1618 priv_recalculate_pair_priorities (agent);
1620 else
1621 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control);
1624 /**
1625 * Checks whether the mapped address in connectivity check response
1626 * matches any of the known local candidates. If not, apply the
1627 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
1629 * @param agent context pointer
1630 * @param stream which stream (of the agent)
1631 * @param component which component (of the stream)
1632 * @param p the connectivity check pair for which we got a response
1633 * @param socketptr socket used to send the reply
1634 * @param mapped_sockaddr mapped address in the response
1636 * @return pointer to a new pair if one was created, otherwise NULL
1638 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)
1640 CandidateCheckPair *new_pair = NULL;
1641 NiceAddress mapped;
1642 GSList *j;
1643 gboolean local_cand_matches = FALSE;
1645 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
1647 for (j = component->local_candidates; j; j = j->next) {
1648 NiceCandidate *cand = j->data;
1649 if (nice_address_equal (&mapped, &cand->addr)) {
1650 local_cand_matches = TRUE;
1651 break;
1655 if (local_cand_matches == TRUE) {
1656 /* note: this is same as "adding to VALID LIST" in the spec
1657 text */
1658 p->state = NICE_CHECK_SUCCEEDED;
1659 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
1660 priv_conn_check_unfreeze_related (agent, stream, p);
1662 else {
1663 NiceCandidate *cand =
1664 discovery_add_peer_reflexive_candidate (agent,
1665 stream->id,
1666 component->id,
1667 &mapped,
1668 sockptr,
1669 local_candidate,
1670 remote_candidate);
1671 p->state = NICE_CHECK_FAILED;
1672 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
1674 /* step: add a new discovered pair (see ICE 7.1.2.2.2
1675 "Constructing a Valid Pair" (ID-19)) */
1676 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p);
1677 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair);
1680 return new_pair;
1684 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
1685 * check transaction. If found, the reply is processed. Implements
1686 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
1688 * @return TRUE if a matching transaction is found
1690 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)
1692 struct sockaddr sockaddr;
1693 socklen_t socklen = sizeof (sockaddr);
1694 GSList *i;
1695 StunUsageIceReturn res;
1696 gboolean trans_found = FALSE;
1697 stun_transid_t discovery_id;
1698 stun_transid_t response_id;
1699 stun_message_id (resp, response_id);
1701 for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) {
1702 CandidateCheckPair *p = i->data;
1704 if (p->stun_message.buffer) {
1705 stun_message_id (&p->stun_message, discovery_id);
1707 if (memcmp (discovery_id, response_id, sizeof(stun_transid_t)) == 0) {
1708 res = stun_usage_ice_conncheck_process (resp, &sockaddr, &socklen,
1709 agent_to_ice_compatibility (agent));
1710 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
1711 "(controlling=%d).", agent, p, (int)res, agent->controlling_mode);
1714 if (res == STUN_USAGE_ICE_RETURN_SUCCESS) {
1715 /* case: found a matching connectivity check request */
1717 CandidateCheckPair *ok_pair = NULL;
1719 nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p);
1720 p->stun_message.buffer = NULL;
1721 p->stun_message.buffer_len = 0;
1723 /* step: verify that response came from the same IP address we
1724 * sent the original request to (see 7.1.2.1. "Failure
1725 * Cases") */
1726 if (nice_address_equal (from, &p->remote->addr) != TRUE) {
1727 gchar tmpbuf[INET6_ADDRSTRLEN];
1728 gchar tmpbuf2[INET6_ADDRSTRLEN];
1730 p->state = NICE_CHECK_FAILED;
1731 nice_debug ("Agent %p : conncheck %p FAILED"
1732 " (mismatch of source address).", agent, p);
1733 nice_address_to_string (&p->remote->addr, tmpbuf);
1734 nice_address_to_string (from, tmpbuf2);
1735 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
1736 tmpbuf, nice_address_get_port (&p->remote->addr),
1737 tmpbuf2, nice_address_get_port (from));
1739 trans_found = TRUE;
1740 break;
1743 /* note: CONNECTED but not yet READY, see docs */
1745 /* step: handle the possible case of a peer-reflexive
1746 * candidate where the mapped-address in response does
1747 * not match any local candidate, see 7.1.2.2.1
1748 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
1750 ok_pair = priv_process_response_check_for_peer_reflexive(agent, stream, component,
1751 p, sockptr, &sockaddr, local_candidate, remote_candidate);
1753 if (!ok_pair)
1754 ok_pair = p;
1756 /* Do not step down to CONNECTED if we're already at state READY*/
1757 if (component->state != NICE_COMPONENT_STATE_READY) {
1758 /* step: notify the client of a new component state (must be done
1759 * before the possible check list state update step */
1760 agent_signal_component_state_change (agent,
1761 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
1765 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
1766 Nominated Flag" (ID-19) */
1767 if (ok_pair->nominated == TRUE)
1768 priv_update_selected_pair (agent, component, ok_pair);
1770 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
1771 states" and 8.1.2 "Updating States", ID-19) */
1772 priv_update_check_list_state_for_ready (agent, stream, component);
1774 trans_found = TRUE;
1775 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
1776 /* case: role conflict error, need to restart with new role */
1777 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p);
1778 /* note: our role might already have changed due to an
1779 * incoming request, but if not, change role now;
1780 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
1781 priv_check_for_role_conflict (agent, !p->controlling);
1783 p->stun_message.buffer = NULL;
1784 p->stun_message.buffer_len = 0;
1785 p->state = NICE_CHECK_WAITING;
1786 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
1787 trans_found = TRUE;
1788 } else if (res == STUN_USAGE_ICE_RETURN_ERROR) {
1789 /* case: STUN error, the check STUN context was freed */
1790 nice_debug ("Agent %p : conncheck %p FAILED.", agent, p);
1791 p->stun_message.buffer = NULL;
1792 p->stun_message.buffer_len = 0;
1793 trans_found = TRUE;
1799 return trans_found;
1803 * Tries to match STUN reply in 'buf' to an existing STUN discovery
1804 * transaction. If found, a reply is sent.
1806 * @return TRUE if a matching transaction is found
1808 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp)
1810 struct sockaddr sockaddr;
1811 socklen_t socklen = sizeof (sockaddr);
1812 struct sockaddr alternate;
1813 socklen_t alternatelen = sizeof (sockaddr);
1814 GSList *i;
1815 StunUsageBindReturn res;
1816 gboolean trans_found = FALSE;
1817 stun_transid_t discovery_id;
1818 stun_transid_t response_id;
1819 stun_message_id (resp, response_id);
1821 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
1822 CandidateDiscovery *d = i->data;
1824 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
1825 d->stun_message.buffer) {
1826 stun_message_id (&d->stun_message, discovery_id);
1828 if (memcmp (discovery_id, response_id, sizeof(stun_transid_t)) == 0) {
1829 res = stun_usage_bind_process (resp, &sockaddr, &socklen,
1830 &alternate, &alternatelen);
1831 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
1832 agent, d, (int)res);
1834 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
1835 /* handle alternate server */
1836 NiceAddress niceaddr;
1837 nice_address_set_from_sockaddr (&niceaddr, &alternate);
1838 d->server = niceaddr;
1840 d->pending = FALSE;
1841 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
1842 /* case: succesful binding discovery, create a new local candidate */
1843 NiceAddress niceaddr;
1844 nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
1846 discovery_add_server_reflexive_candidate (
1847 d->agent,
1848 d->stream->id,
1849 d->component->id,
1850 &niceaddr,
1851 d->nicesock);
1853 d->stun_message.buffer = NULL;
1854 d->stun_message.buffer_len = 0;
1855 d->done = TRUE;
1856 trans_found = TRUE;
1857 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
1858 /* case: STUN error, the check STUN context was freed */
1859 d->stun_message.buffer = NULL;
1860 d->stun_message.buffer_len = 0;
1861 d->done = TRUE;
1862 trans_found = TRUE;
1868 return trans_found;
1872 static CandidateRefresh *
1873 priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand,
1874 guint lifetime)
1876 CandidateRefresh *cand;
1877 NiceAgent *agent = cdisco->agent;
1878 GSList *modified_list;
1880 cand = g_slice_new0 (CandidateRefresh);
1881 if (cand) {
1882 modified_list = g_slist_append (agent->refresh_list, cand);
1884 if (modified_list) {
1885 cand->nicesock = cdisco->nicesock;
1886 cand->relay_socket = relay_cand->sockptr;
1887 cand->server = cdisco->server;
1888 cand->turn = cdisco->turn;
1889 cand->stream = cdisco->stream;
1890 cand->component = cdisco->component;
1891 cand->agent = cdisco->agent;
1892 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
1893 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
1894 agent, cand, (lifetime - 60) * 1000);
1895 agent->refresh_list = modified_list;
1897 /* step: also start the keepalive timer */
1898 /* refresh should be sent 1 minute before it expires */
1899 cand->timer_source =
1900 agent_timeout_add_with_context (agent, (lifetime - 60) * 1000,
1901 priv_turn_allocate_refresh_tick, cand);
1903 nice_debug ("timer source is : %d", cand->timer_source);
1907 return cand;
1911 * Tries to match STUN reply in 'buf' to an existing STUN discovery
1912 * transaction. If found, a reply is sent.
1914 * @return TRUE if a matching transaction is found
1916 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
1918 struct sockaddr sockaddr;
1919 socklen_t socklen = sizeof (sockaddr);
1920 struct sockaddr alternate;
1921 socklen_t alternatelen = sizeof (alternate);
1922 struct sockaddr relayaddr;
1923 socklen_t relayaddrlen = sizeof (relayaddr);
1924 uint32_t lifetime;
1925 uint32_t bandwidth;
1926 GSList *i;
1927 StunUsageTurnReturn res;
1928 gboolean trans_found = FALSE;
1929 stun_transid_t discovery_id;
1930 stun_transid_t response_id;
1931 stun_message_id (resp, response_id);
1933 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
1934 CandidateDiscovery *d = i->data;
1936 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
1937 d->stun_message.buffer) {
1938 stun_message_id (&d->stun_message, discovery_id);
1940 if (memcmp (discovery_id, response_id, sizeof(stun_transid_t)) == 0) {
1941 res = stun_usage_turn_process (resp,
1942 &relayaddr, &relayaddrlen, &sockaddr, &socklen, &alternate, &alternatelen,
1943 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
1944 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
1945 agent, d, (int)res);
1947 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
1948 /* handle alternate server */
1949 nice_address_set_from_sockaddr (&d->server, &alternate);
1950 nice_address_set_from_sockaddr (&d->turn->server, &alternate);
1952 d->pending = FALSE;
1953 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
1954 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
1955 /* case: succesful allocate, create a new local candidate */
1956 NiceAddress niceaddr;
1957 NiceCandidate *relay_cand;
1959 /* We also received our mapped address */
1960 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
1961 nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
1963 discovery_add_server_reflexive_candidate (
1964 d->agent,
1965 d->stream->id,
1966 d->component->id,
1967 &niceaddr,
1968 d->nicesock);
1971 nice_address_set_from_sockaddr (&niceaddr, &relayaddr);
1972 relay_cand = discovery_add_relay_candidate (
1973 d->agent,
1974 d->stream->id,
1975 d->component->id,
1976 &niceaddr,
1977 d->nicesock,
1978 d->turn);
1980 priv_add_new_turn_refresh (d, relay_cand, lifetime);
1983 d->stun_message.buffer = NULL;
1984 d->stun_message.buffer_len = 0;
1985 d->done = TRUE;
1986 trans_found = TRUE;
1987 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
1988 int code = -1;
1989 uint8_t *sent_realm = NULL;
1990 uint8_t *recv_realm = NULL;
1991 uint16_t sent_realm_len = 0;
1992 uint16_t recv_realm_len = 0;
1994 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
1995 STUN_ATTRIBUTE_REALM, &sent_realm_len);
1996 recv_realm = (uint8_t *) stun_message_find (resp,
1997 STUN_ATTRIBUTE_REALM, &recv_realm_len);
1999 /* check for unauthorized error response */
2000 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
2001 stun_message_get_class (resp) == STUN_ERROR &&
2002 stun_message_find_error (resp, &code) ==
2003 STUN_MESSAGE_RETURN_SUCCESS &&
2004 recv_realm != NULL && recv_realm_len > 0) {
2006 if (code == 438 ||
2007 (code == 401 &&
2008 !(recv_realm_len == sent_realm_len &&
2009 sent_realm != NULL &&
2010 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2011 d->stun_resp_msg = *resp;
2012 memcpy (d->stun_resp_buffer, resp->buffer,
2013 stun_message_length (resp));
2014 d->stun_resp_msg.buffer = d->stun_resp_buffer;
2015 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
2016 d->pending = FALSE;
2017 } else {
2018 /* case: a real unauthorized error */
2019 d->stun_message.buffer = NULL;
2020 d->stun_message.buffer_len = 0;
2021 d->done = TRUE;
2023 } else {
2024 /* case: STUN error, the check STUN context was freed */
2025 d->stun_message.buffer = NULL;
2026 d->stun_message.buffer_len = 0;
2027 d->done = TRUE;
2029 trans_found = TRUE;
2035 return trans_found;
2040 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2041 * transaction. If found, a reply is sent.
2043 * @return TRUE if a matching transaction is found
2045 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
2047 uint32_t lifetime;
2048 GSList *i;
2049 StunUsageTurnReturn res;
2050 gboolean trans_found = FALSE;
2051 stun_transid_t refresh_id;
2052 stun_transid_t response_id;
2053 stun_message_id (resp, response_id);
2055 for (i = agent->refresh_list; i && trans_found != TRUE; i = i->next) {
2056 CandidateRefresh *cand = i->data;
2058 if (cand->stun_message.buffer) {
2059 stun_message_id (&cand->stun_message, refresh_id);
2061 if (memcmp (refresh_id, response_id, sizeof(stun_transid_t)) == 0) {
2062 res = stun_usage_turn_refresh_process (resp,
2063 &lifetime, agent_to_turn_compatibility (cand->agent));
2064 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2065 agent, cand, (int)res);
2066 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
2067 /* refresh should be sent 1 minute before it expires */
2068 cand->timer_source =
2069 agent_timeout_add_with_context (cand->agent, (lifetime - 60) * 1000,
2070 priv_turn_allocate_refresh_tick, cand);
2072 g_source_destroy (cand->tick_source);
2073 g_source_unref (cand->tick_source);
2074 cand->tick_source = NULL;
2075 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2076 int code = -1;
2077 uint8_t *sent_realm = NULL;
2078 uint8_t *recv_realm = NULL;
2079 uint16_t sent_realm_len = 0;
2080 uint16_t recv_realm_len = 0;
2082 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
2083 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2084 recv_realm = (uint8_t *) stun_message_find (resp,
2085 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2087 /* check for unauthorized error response */
2088 if (cand->agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
2089 stun_message_get_class (resp) == STUN_ERROR &&
2090 stun_message_find_error (resp, &code) ==
2091 STUN_MESSAGE_RETURN_SUCCESS &&
2092 recv_realm != NULL && recv_realm_len > 0) {
2094 if (code == 438 ||
2095 (code == 401 &&
2096 !(recv_realm_len == sent_realm_len &&
2097 sent_realm != NULL &&
2098 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2099 cand->stun_resp_msg = *resp;
2100 memcpy (cand->stun_resp_buffer, resp->buffer,
2101 stun_message_length (resp));
2102 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
2103 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
2104 priv_turn_allocate_refresh_tick_unlocked (cand);
2105 } else {
2106 /* case: a real unauthorized error */
2107 refresh_cancel (cand);
2109 } else {
2110 /* case: STUN error, the check STUN context was freed */
2111 refresh_cancel (cand);
2113 trans_found = TRUE;
2119 return trans_found;
2123 typedef struct {
2124 NiceAgent *agent;
2125 Stream *stream;
2126 Component *component;
2127 uint8_t *password;
2128 } conncheck_validater_data;
2130 static bool conncheck_stun_validater (StunAgent *agent,
2131 StunMessage *message, uint8_t *username, uint16_t username_len,
2132 uint8_t **password, size_t *password_len, void *user_data)
2134 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
2135 GSList *i;
2136 uint8_t uname[NICE_STREAM_MAX_UNAME];
2137 guint uname_len = 0;
2139 for (i = data->component->local_candidates; i; i = i->next) {
2140 NiceCandidate *cand = i->data;
2141 gchar *ufrag = NULL;
2142 gsize ufrag_len;
2144 if (cand->username)
2145 ufrag = cand->username;
2146 else if (data->stream)
2147 ufrag = data->stream->local_ufrag;
2148 ufrag_len = strlen (ufrag);
2150 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2151 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
2153 if (ufrag_len <= NICE_STREAM_MAX_UNAME) {
2154 memcpy (uname, ufrag, ufrag_len);
2155 uname_len = ufrag_len;
2158 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2159 g_free (ufrag);
2161 stun_debug ("Comparing username '");
2162 stun_debug_bytes (username, username_len);
2163 stun_debug ("' (%d) with '", username_len);
2164 stun_debug_bytes (uname, uname_len);
2165 stun_debug ("' (%d) : %d\n",
2166 uname_len, memcmp (username, uname, uname_len));
2167 if (uname_len > 0 && username_len >= uname_len &&
2168 memcmp (username, uname, uname_len) == 0) {
2169 gchar *pass = NULL;
2171 if (cand->password)
2172 pass = cand->password;
2173 else
2174 pass = data->stream->local_password;
2176 *password = (uint8_t *) pass;
2177 *password_len = strlen (pass);
2179 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN) {
2180 data->password = g_base64_decode (pass, password_len);
2181 *password = data->password;
2184 stun_debug ("Found valid username, returning password: '%s'\n", *password);
2185 return TRUE;
2189 return FALSE;
2194 * Processing an incoming STUN message.
2196 * @param agent self pointer
2197 * @param stream stream the packet is related to
2198 * @param component component the packet is related to
2199 * @param socket socket from which the packet was received
2200 * @param from address of the sender
2201 * @param buf message contents
2202 * @param buf message length
2204 * @pre contents of 'buf' is a STUN message
2206 * @return XXX (what FALSE means exactly?)
2208 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
2209 Component *component, NiceSocket *socket, const NiceAddress *from,
2210 gchar *buf, guint len)
2212 struct sockaddr sockaddr;
2213 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
2214 ssize_t res;
2215 size_t rbuf_len = sizeof (rbuf);
2216 bool control = agent->controlling_mode;
2217 uint8_t uname[NICE_STREAM_MAX_UNAME];
2218 guint uname_len;
2219 uint8_t *username;
2220 uint16_t username_len;
2221 StunMessage req;
2222 StunMessage msg;
2223 StunValidationStatus valid;
2224 conncheck_validater_data validater_data = {agent, stream, component, NULL};
2225 GSList *i, *j;
2226 NiceCandidate *remote_candidate = NULL;
2227 NiceCandidate *remote_candidate2 = NULL;
2228 NiceCandidate *local_candidate = NULL;
2229 gboolean discovery_msg = FALSE;
2231 nice_address_copy_to_sockaddr (from, &sockaddr);
2233 /* note: contents of 'buf' already validated, so it is
2234 * a valid and fully received STUN message */
2236 #ifndef NDEBUG
2238 gchar tmpbuf[INET6_ADDRSTRLEN];
2239 nice_address_to_string (from, tmpbuf);
2240 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2241 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
2243 #endif
2245 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2247 valid = stun_agent_validate (&agent->stun_agent, &req,
2248 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2250 /* Check for discovery candidates stun agents */
2251 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2252 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2253 for (i = agent->discovery_list; i; i = i->next) {
2254 CandidateDiscovery *d = i->data;
2255 if (d->stream == stream && d->component == component &&
2256 d->nicesock == socket) {
2257 valid = stun_agent_validate (&d->stun_agent, &req,
2258 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2260 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2261 continue;
2263 discovery_msg = TRUE;
2264 break;
2268 /* Check for relay refresh stun agents */
2269 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2270 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2271 for (i = agent->refresh_list; i; i = i->next) {
2272 CandidateRefresh *r = i->data;
2273 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream,
2274 stream, r->component, component, r->nicesock, r->relay_socket, socket);
2275 if (r->stream == stream && r->component == component &&
2276 (r->nicesock == socket || r->relay_socket == socket)) {
2277 valid = stun_agent_validate (&r->stun_agent, &req,
2278 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2279 nice_debug ("Validating gave %d", valid);
2280 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2281 continue;
2282 discovery_msg = TRUE;
2283 break;
2289 if (validater_data.password)
2290 g_free (validater_data.password);
2292 if (valid == STUN_VALIDATION_NOT_STUN ||
2293 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
2294 valid == STUN_VALIDATION_BAD_REQUEST)
2296 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2297 agent);
2298 return FALSE;
2301 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
2302 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
2303 rbuf_len = stun_agent_build_unknown_attributes_error (&agent->stun_agent,
2304 &msg, rbuf, rbuf_len, &req);
2305 if (len == 0)
2306 return FALSE;
2308 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2309 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2311 return TRUE;
2314 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
2315 nice_debug ("Agent %p : Integrity check failed.", agent);
2317 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2318 &req, STUN_ERROR_UNAUTHORIZED)) {
2319 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2320 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2321 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2323 return TRUE;
2325 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
2326 nice_debug ("Agent %p : Integrity check failed.", agent);
2327 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2328 &req, STUN_ERROR_BAD_REQUEST)) {
2329 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2330 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2331 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2333 return TRUE;
2336 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
2337 &username_len);
2339 for (i = component->remote_candidates; i; i = i->next) {
2340 NiceCandidate *cand = i->data;
2341 if (nice_address_equal (from, &cand->addr)) {
2342 remote_candidate = cand;
2343 break;
2347 /* We need to find which local candidate was used */
2348 for (i = component->remote_candidates; i; i = i->next) {
2349 for (j = component->local_candidates; j; j = j->next) {
2350 gboolean inbound = TRUE;
2351 NiceCandidate *rcand = i->data;
2352 NiceCandidate *lcand = j->data;
2354 /* If we receive a response, then the username is local:remote */
2355 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2356 if (stun_message_get_class (&req) == STUN_REQUEST ||
2357 stun_message_get_class (&req) == STUN_INDICATION) {
2358 inbound = TRUE;
2359 } else {
2360 inbound = FALSE;
2364 uname_len = priv_create_username (agent, stream,
2365 component->id, rcand, lcand,
2366 uname, sizeof (uname), inbound);
2368 stun_debug ("Comparing username '");
2369 stun_debug_bytes (username, username? username_len : 0);
2370 stun_debug ("' (%d) with '", username_len);
2371 stun_debug_bytes (uname, uname_len);
2372 stun_debug ("' (%d) : %d\n",
2373 uname_len, username && uname_len == username_len &&
2374 memcmp (username, uname, uname_len) == 0);
2376 if (username &&
2377 uname_len == username_len &&
2378 memcmp (uname, username, username_len) == 0) {
2379 local_candidate = lcand;
2380 remote_candidate2 = rcand;
2381 break;
2386 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2387 local_candidate == NULL &&
2388 discovery_msg == FALSE) {
2389 /* if we couldn't match the username and the stun agent has
2390 IGNORE_CREDENTIALS then we have an integrity check failing.
2391 This could happen with the race condition of receiving connchecks
2392 before the remote candidates are added. Just drop the message, and let
2393 the retransmissions make it work. */
2394 nice_debug ("Agent %p : Username check failed.", agent);
2395 return TRUE;
2398 if (valid != STUN_VALIDATION_SUCCESS) {
2399 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent, valid);
2400 return FALSE;
2404 if (stun_message_get_class (&req) == STUN_REQUEST) {
2405 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2406 username = (uint8_t *) stun_message_find (&req,
2407 STUN_ATTRIBUTE_USERNAME, &username_len);
2408 uname_len = priv_create_username (agent, stream,
2409 component->id, remote_candidate2, local_candidate,
2410 uname, sizeof (uname), FALSE);
2411 memcpy (username, uname, username_len);
2412 if (remote_candidate2) {
2413 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
2414 &req.key_len);
2415 } else {
2416 req.key = NULL;
2417 req.key_len = 0;
2421 rbuf_len = sizeof (rbuf);
2422 res = stun_usage_ice_conncheck_create_reply (&agent->stun_agent, &req,
2423 &msg, rbuf, &rbuf_len, &sockaddr, sizeof (sockaddr),
2424 &control, agent->tie_breaker,
2425 agent_to_ice_compatibility (agent));
2427 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2428 g_free (req.key);
2431 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
2432 priv_check_for_role_conflict (agent, control);
2434 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2435 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2436 /* case 1: valid incoming request, send a reply/error */
2437 bool use_candidate =
2438 stun_usage_ice_conncheck_use_candidate (&req);
2439 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
2441 if (agent->controlling_mode ||
2442 agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2443 agent->compatibility == NICE_COMPATIBILITY_MSN)
2444 use_candidate = TRUE;
2446 if (stream->initial_binding_request_received != TRUE)
2447 agent_signal_initial_binding_request_received (agent, stream);
2449 if (component->remote_candidates && remote_candidate == NULL) {
2450 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2451 "peer-reflexive candidate.", agent);
2452 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
2453 agent, stream, component, priority, from, socket,
2454 local_candidate, remote_candidate2);
2457 priv_reply_to_conn_check (agent, stream, component, remote_candidate,
2458 from, socket, rbuf_len, rbuf, use_candidate);
2460 if (component->remote_candidates == NULL) {
2461 /* case: We've got a valid binding request to a local candidate
2462 * but we do not yet know remote credentials nor
2463 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2464 * immediately but postpone all other processing until
2465 * we get information about the remote candidates */
2467 /* step: send a reply immediately but postpone other processing */
2468 priv_store_pending_check (agent, component, from, socket,
2469 priority, use_candidate);
2471 } else {
2472 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2473 agent, strerror(errno));
2474 return FALSE;
2476 } else {
2477 /* case 2: not a new request, might be a reply... */
2478 gboolean trans_found = FALSE;
2480 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2482 /* step: let's try to match the response to an existing check context */
2483 if (trans_found != TRUE)
2484 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
2485 component, socket, from, local_candidate, remote_candidate, &req);
2487 /* step: let's try to match the response to an existing discovery */
2488 if (trans_found != TRUE)
2489 trans_found = priv_map_reply_to_discovery_request (agent, &req);
2491 /* step: let's try to match the response to an existing turn allocate */
2492 if (trans_found != TRUE)
2493 trans_found = priv_map_reply_to_relay_request (agent, &req);
2495 /* step: let's try to match the response to an existing turn refresh */
2496 if (trans_found != TRUE)
2497 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
2499 if (trans_found != TRUE)
2500 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2501 "probably a keepalive.", agent);
2504 return TRUE;