discovery: Remove useless checks from priv_add_local_candidate_pruned
[sipe-libnice.git] / agent / conncheck.c
blob30d13d32bc880e3d963d25fd53d1d4abf05b2ad4
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2006-2009 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
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 * Youness Alaoui, Collabora Ltd.
27 * Dafydd Harries, Collabora Ltd.
29 * Alternatively, the contents of this file may be used under the terms of the
30 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
31 * case the provisions of LGPL are applicable instead of those above. If you
32 * wish to allow use of your version of this file only under the terms of the
33 * LGPL and not to allow others to use your version of this file under the
34 * MPL, indicate your decision by deleting the provisions above and replace
35 * them with the notice and other provisions required by the LGPL. If you do
36 * not delete the provisions above, a recipient may use your version of this
37 * file under either the MPL or the LGPL.
41 * @file conncheck.c
42 * @brief ICE connectivity checks
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif
49 #include <errno.h>
50 #include <string.h>
52 #include <glib.h>
54 #include "debug.h"
56 #include "agent.h"
57 #include "agent-priv.h"
58 #include "conncheck.h"
59 #include "discovery.h"
60 #include "stun/usages/ice.h"
61 #include "stun/usages/bind.h"
62 #include "stun/usages/turn.h"
64 static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream);
65 static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component);
66 static guint priv_prune_pending_checks (Stream *stream, guint component_id);
67 static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate);
68 static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand);
69 static size_t priv_create_username (NiceAgent *agent, Stream *stream,
70 guint component_id, NiceCandidate *remote, NiceCandidate *local,
71 uint8_t *dest, guint dest_len, gboolean inbound);
72 static size_t priv_get_password (NiceAgent *agent, Stream *stream,
73 NiceCandidate *remote, uint8_t **password);
75 static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
77 return (now->tv_sec == timer->tv_sec) ?
78 now->tv_usec >= timer->tv_usec :
79 now->tv_sec >= timer->tv_sec;
83 * Finds the next connectivity check in WAITING state.
85 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
87 GSList *i;
89 /* note: list is sorted in priority order to first waiting check has
90 * the highest priority */
92 for (i = conn_check_list; i ; i = i->next) {
93 CandidateCheckPair *p = i->data;
94 if (p->state == NICE_CHECK_WAITING)
95 return p;
98 return NULL;
102 * Initiates a new connectivity check for a ICE candidate pair.
104 * @return TRUE on success, FALSE on error
106 static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
108 /* XXX: from ID-16 onwards, the checks should not be sent
109 * immediately, but be put into the "triggered queue",
110 * see "7.2.1.4 Triggered Checks"
112 g_get_current_time (&pair->next_tick);
113 g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
114 pair->state = NICE_CHECK_IN_PROGRESS;
115 nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
116 conn_check_send (agent, pair);
117 return TRUE;
121 * Unfreezes the next connectivity check in the list. Follows the
122 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
123 * (ID-19), with some exceptions (see comments in code).
125 * See also sect 7.1.2.2.3 (Updating Pair States), and
126 * priv_conn_check_unfreeze_related().
128 * @return TRUE on success, and FALSE if no frozen candidates were found.
130 static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
132 CandidateCheckPair *pair = NULL;
133 GSList *i, *j;
135 /* XXX: the unfreezing is implemented a bit differently than in the
136 * current ICE spec, but should still be interoperate:
137 * - checks are not grouped by foundation
138 * - one frozen check is unfrozen (lowest component-id, highest
139 * priority)
142 for (i = agent->streams; i; i = i->next) {
143 Stream *stream = i->data;
144 guint64 max_frozen_priority = 0;
147 for (j = stream->conncheck_list; j ; j = j->next) {
148 CandidateCheckPair *p = j->data;
150 /* XXX: the prio check could be removed as the pairs are sorted
151 * already */
153 if (p->state == NICE_CHECK_FROZEN) {
154 if (p->priority > max_frozen_priority) {
155 max_frozen_priority = p->priority;
156 pair = p;
161 if (pair)
162 break;
165 if (pair) {
166 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation);
167 pair->state = NICE_CHECK_WAITING;
168 nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
169 return TRUE;
172 return FALSE;
176 * Unfreezes the next next connectivity check in the list after
177 * check 'success_check' has succesfully completed.
179 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
181 * @param agent context
182 * @param ok_check a connectivity check that has just completed
184 * @return TRUE on success, and FALSE if no frozen candidates were found.
186 static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check)
188 GSList *i, *j;
189 guint unfrozen = 0;
191 g_assert (ok_check);
192 g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
193 g_assert (stream);
194 g_assert (stream->id == ok_check->stream_id);
196 /* step: perform the step (1) of 'Updating Pair States' */
197 for (i = stream->conncheck_list; i ; i = i->next) {
198 CandidateCheckPair *p = i->data;
200 if (p->stream_id == ok_check->stream_id) {
201 if (p->state == NICE_CHECK_FROZEN &&
202 strcmp (p->foundation, ok_check->foundation) == 0) {
203 nice_debug ("Agent %p : Unfreezing check %p (after succesful check %p).", agent, p, ok_check);
204 p->state = NICE_CHECK_WAITING;
205 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
206 ++unfrozen;
211 /* step: perform the step (2) of 'Updating Pair States' */
212 stream = agent_find_stream (agent, ok_check->stream_id);
213 if (stream_all_components_ready (stream)) {
214 /* step: unfreeze checks from other streams */
215 for (i = agent->streams; i ; i = i->next) {
216 Stream *s = i->data;
217 for (j = stream->conncheck_list; j ; j = j->next) {
218 CandidateCheckPair *p = j->data;
220 if (p->stream_id == s->id &&
221 p->stream_id != ok_check->stream_id) {
222 if (p->state == NICE_CHECK_FROZEN &&
223 strcmp (p->foundation, ok_check->foundation) == 0) {
224 nice_debug ("Agent %p : Unfreezing check %p from stream %u (after succesful check %p).", agent, p, s->id, ok_check);
225 p->state = NICE_CHECK_WAITING;
226 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
227 ++unfrozen;
232 /* note: only unfreeze check from one stream at a time */
233 if (unfrozen)
234 break;
238 if (unfrozen == 0)
239 priv_conn_check_unfreeze_next (agent);
243 * Helper function for connectivity check timer callback that
244 * runs through the stream specific part of the state machine.
246 * @param schedule if TRUE, schedule a new check
248 * @return will return FALSE when no more pending timers.
250 static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now)
252 gboolean keep_timer_going = FALSE;
253 guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
254 s_nominated = 0, s_waiting_for_nomination = 0;
255 guint frozen = 0, waiting = 0;
256 GSList *i, *k;
258 for (i = stream->conncheck_list; i ; i = i->next) {
259 CandidateCheckPair *p = i->data;
261 if (p->state == NICE_CHECK_IN_PROGRESS) {
262 if (p->stun_message.buffer == NULL) {
263 nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent);
264 p->state = NICE_CHECK_FAILED;
265 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
266 } else if (priv_timer_expired (&p->next_tick, now)) {
267 switch (stun_timer_refresh (&p->timer)) {
268 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
270 /* case: error, abort processing */
271 StunTransactionId id;
273 nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p);
274 p->state = NICE_CHECK_FAILED;
275 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
277 stun_message_id (&p->stun_message, id);
278 stun_agent_forget_transaction (&agent->stun_agent, id);
280 p->stun_message.buffer = NULL;
281 p->stun_message.buffer_len = 0;
284 break;
286 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
288 /* case: not ready, so schedule a new timeout */
289 unsigned int timeout = stun_timer_remainder (&p->timer);
290 nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
291 agent, timeout);
293 nice_socket_send (p->local->sockptr, &p->remote->addr,
294 stun_message_length (&p->stun_message),
295 (gchar *)p->stun_buffer);
298 /* note: convert from milli to microseconds for g_time_val_add() */
299 p->next_tick = *now;
300 g_time_val_add (&p->next_tick, timeout * 1000);
302 keep_timer_going = TRUE;
303 break;
305 case STUN_USAGE_TIMER_RETURN_SUCCESS:
307 unsigned int timeout = stun_timer_remainder (&p->timer);
309 /* note: convert from milli to microseconds for g_time_val_add() */
310 p->next_tick = *now;
311 g_time_val_add (&p->next_tick, timeout * 1000);
313 keep_timer_going = TRUE;
314 break;
320 if (p->state == NICE_CHECK_FROZEN)
321 ++frozen;
322 else if (p->state == NICE_CHECK_IN_PROGRESS)
323 ++s_inprogress;
324 else if (p->state == NICE_CHECK_WAITING)
325 ++waiting;
326 else if (p->state == NICE_CHECK_SUCCEEDED)
327 ++s_succeeded;
328 else if (p->state == NICE_CHECK_DISCOVERED)
329 ++s_discovered;
331 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
332 && p->nominated)
333 ++s_nominated;
334 else if ((p->state == NICE_CHECK_SUCCEEDED ||
335 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
336 ++s_waiting_for_nomination;
339 /* note: keep the timer going as long as there is work to be done */
340 if (s_inprogress)
341 keep_timer_going = TRUE;
343 /* note: if some components have established connectivity,
344 * but yet no nominated pair, keep timer going */
345 if (s_nominated < stream->n_components &&
346 s_waiting_for_nomination) {
347 keep_timer_going = TRUE;
348 if (agent->controlling_mode) {
349 guint n;
350 for (n = 0; n < stream->n_components; n++) {
351 for (k = stream->conncheck_list; k ; k = k->next) {
352 CandidateCheckPair *p = k->data;
353 /* note: highest priority item selected (list always sorted) */
354 if (p->state == NICE_CHECK_SUCCEEDED ||
355 p->state == NICE_CHECK_DISCOVERED) {
356 nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
357 p->nominated = TRUE;
358 priv_conn_check_initiate (agent, p);
359 break; /* move to the next component */
366 static int tick_counter = 0;
367 if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
368 nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
369 "%u waiting, %u succeeded, %u discovered, %u nominated, "
370 "%u waiting-for-nom.", agent,
371 tick_counter, frozen, s_inprogress, waiting, s_succeeded,
372 s_discovered, s_nominated, s_waiting_for_nomination);
375 return keep_timer_going;
381 * Timer callback that handles initiating and managing connectivity
382 * checks (paced by the Ta timer).
384 * This function is designed for the g_timeout_add() interface.
386 * @return will return FALSE when no more pending timers.
388 static gboolean priv_conn_check_tick_unlocked (gpointer pointer)
390 CandidateCheckPair *pair = NULL;
391 NiceAgent *agent = pointer;
392 gboolean keep_timer_going = FALSE;
393 GSList *i, *j;
394 GTimeVal now;
396 /* step: process ongoing STUN transactions */
397 g_get_current_time (&now);
399 /* step: find the highest priority waiting check and send it */
400 for (i = agent->streams; i ; i = i->next) {
401 Stream *stream = i->data;
403 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
404 if (pair)
405 break;
408 if (pair) {
409 priv_conn_check_initiate (agent, pair);
410 keep_timer_going = TRUE;
411 } else {
412 keep_timer_going = priv_conn_check_unfreeze_next (agent);
415 for (j = agent->streams; j; j = j->next) {
416 Stream *stream = j->data;
417 gboolean res =
418 priv_conn_check_tick_stream (stream, agent, &now);
419 if (res)
420 keep_timer_going = res;
423 /* step: stop timer if no work left */
424 if (keep_timer_going != TRUE) {
425 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
426 for (i = agent->streams; i; i = i->next) {
427 Stream *stream = i->data;
428 priv_update_check_list_failed_components (agent, stream);
429 for (j = stream->components; j; j = j->next) {
430 Component *component = j->data;
431 priv_update_check_list_state_for_ready (agent, stream, component);
433 stream->conncheck_state = NICE_CHECKLIST_COMPLETED;
436 /* Stopping the timer so destroy the source.. this will allow
437 the timer to be reset if we get a set_remote_candidates after this
438 point */
439 if (agent->conncheck_timer_source != NULL) {
440 g_source_destroy (agent->conncheck_timer_source);
441 g_source_unref (agent->conncheck_timer_source);
442 agent->conncheck_timer_source = NULL;
445 /* XXX: what to signal, is all processing now really done? */
446 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
449 return keep_timer_going;
452 static gboolean priv_conn_check_tick (gpointer pointer)
454 gboolean ret;
456 agent_lock();
457 if (g_source_is_destroyed (g_main_current_source ())) {
458 nice_debug ("Source was destroyed. "
459 "Avoided race condition in priv_conn_check_tick");
460 agent_unlock ();
461 return FALSE;
463 ret = priv_conn_check_tick_unlocked (pointer);
464 agent_unlock();
466 return ret;
469 static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer)
471 CandidatePair *pair = (CandidatePair *) pointer;
473 agent_lock();
475 /* A race condition might happen where the mutex above waits for the lock
476 * and in the meantime another thread destroys the source.
477 * In that case, we don't need to run our retransmission tick since it should
478 * have been cancelled */
479 if (g_source_is_destroyed (g_main_current_source ())) {
480 nice_debug ("Source was destroyed. "
481 "Avoided race condition in priv_conn_keepalive_retransmissions_tick");
482 agent_unlock ();
483 return FALSE;
486 g_source_destroy (pair->keepalive.tick_source);
487 g_source_unref (pair->keepalive.tick_source);
488 pair->keepalive.tick_source = NULL;
490 switch (stun_timer_refresh (&pair->keepalive.timer)) {
491 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
493 /* Time out */
494 StunTransactionId id;
497 stun_message_id (&pair->keepalive.stun_message, id);
498 stun_agent_forget_transaction (&pair->keepalive.agent->stun_agent, id);
500 if (pair->keepalive.agent->media_after_tick) {
501 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
502 "but media was received. Suspecting keepalive lost because of "
503 "network bottleneck", pair->keepalive.agent);
505 if (pair->keepalive.tick_source) {
506 g_source_destroy (pair->keepalive.tick_source);
507 g_source_unref (pair->keepalive.tick_source);
508 pair->keepalive.tick_source = NULL;
510 pair->keepalive.stun_message.buffer = NULL;
512 } else {
513 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
514 "peer probably lost connection", pair->keepalive.agent);
515 agent_signal_component_state_change (pair->keepalive.agent,
516 pair->keepalive.stream_id, pair->keepalive.component_id,
517 NICE_COMPONENT_STATE_FAILED);
519 break;
521 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
522 /* Retransmit */
523 nice_socket_send (pair->local->sockptr, &pair->remote->addr,
524 stun_message_length (&pair->keepalive.stun_message),
525 (gchar *)pair->keepalive.stun_buffer);
527 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
528 pair->keepalive.agent);
529 pair->keepalive.tick_source =
530 agent_timeout_add_with_context (pair->keepalive.agent,
531 stun_timer_remainder (&pair->keepalive.timer),
532 priv_conn_keepalive_retransmissions_tick, pair);
533 break;
534 case STUN_USAGE_TIMER_RETURN_SUCCESS:
535 pair->keepalive.tick_source =
536 agent_timeout_add_with_context (pair->keepalive.agent,
537 stun_timer_remainder (&pair->keepalive.timer),
538 priv_conn_keepalive_retransmissions_tick, pair);
539 break;
543 agent_unlock ();
544 return FALSE;
549 * Timer callback that handles initiating and managing connectivity
550 * checks (paced by the Ta timer).
552 * This function is designed for the g_timeout_add() interface.
554 * @return will return FALSE when no more pending timers.
556 static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
558 GSList *i, *j, *k;
559 int errors = 0;
560 gboolean ret = FALSE;
561 size_t buf_len = 0;
563 /* case 1: session established and media flowing
564 * (ref ICE sect 10 "Keepalives" ID-19) */
565 for (i = agent->streams; i; i = i->next) {
567 Stream *stream = i->data;
568 for (j = stream->components; j; j = j->next) {
569 Component *component = j->data;
570 if (component->selected_pair.local != NULL) {
571 CandidatePair *p = &component->selected_pair;
573 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
574 guint32 priority = nice_candidate_ice_priority_full (
575 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 1,
576 p->local->component_id);
577 uint8_t uname[NICE_STREAM_MAX_UNAME];
578 size_t uname_len =
579 priv_create_username (agent, agent_find_stream (agent, stream->id),
580 component->id, p->remote, p->local, uname, sizeof (uname),
581 FALSE);
582 uint8_t *password = NULL;
583 size_t password_len = priv_get_password (agent,
584 agent_find_stream (agent, stream->id), p->remote, &password);
585 gchar tmpbuf[INET6_ADDRSTRLEN];
587 nice_address_to_string (&p->remote->addr, tmpbuf);
588 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
589 "socket=%u (c-id:%u), username='%s' (%d), "
590 "password='%s' (%d), priority=%u.", agent,
591 tmpbuf, nice_address_get_port (&p->remote->addr),
592 ((NiceSocket *)p->local->sockptr)->fileno, component->id,
593 uname, uname_len, password, password_len, priority);
595 if (uname_len > 0) {
596 buf_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
597 &p->keepalive.stun_message, p->keepalive.stun_buffer,
598 sizeof(p->keepalive.stun_buffer),
599 uname, uname_len, password, password_len,
600 agent->controlling_mode, agent->controlling_mode, priority,
601 agent->tie_breaker,
602 agent_to_ice_compatibility (agent));
604 nice_debug ("Agent %p: conncheck created %d - %p",
605 agent, buf_len, p->keepalive.stun_message.buffer);
607 if (buf_len > 0) {
608 stun_timer_start (&p->keepalive.timer);
610 agent->media_after_tick = FALSE;
612 /* send the conncheck */
613 nice_socket_send (p->local->sockptr, &p->remote->addr,
614 buf_len, (gchar *)p->keepalive.stun_buffer);
616 if (p->keepalive.tick_source != NULL) {
617 g_source_destroy (p->keepalive.tick_source);
618 g_source_unref (p->keepalive.tick_source);
619 p->keepalive.tick_source = NULL;
622 p->keepalive.stream_id = stream->id;
623 p->keepalive.component_id = component->id;
624 p->keepalive.agent = agent;
626 p->keepalive.tick_source =
627 agent_timeout_add_with_context (p->keepalive.agent,
628 stun_timer_remainder (&p->keepalive.timer),
629 priv_conn_keepalive_retransmissions_tick, p);
630 } else {
631 ++errors;
634 } else {
635 buf_len = stun_usage_bind_keepalive (&agent->stun_agent,
636 &p->keepalive.stun_message, p->keepalive.stun_buffer,
637 sizeof(p->keepalive.stun_buffer));
639 if (buf_len > 0) {
640 nice_socket_send (p->local->sockptr, &p->remote->addr, buf_len,
641 (gchar *)p->keepalive.stun_buffer);
643 nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
644 agent, p, (int) buf_len);
645 } else {
646 ++errors;
653 /* case 2: connectivity establishment ongoing
654 * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */
655 for (i = agent->streams; i; i = i->next) {
656 Stream *stream = i->data;
657 for (j = stream->components; j; j = j->next) {
658 Component *component = j->data;
659 if (component->state < NICE_COMPONENT_STATE_READY &&
660 agent->stun_server_ip) {
661 NiceAddress stun_server;
662 if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
663 StunAgent stun_agent;
664 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
665 StunMessage stun_message;
666 size_t buffer_len = 0;
668 nice_address_set_port (&stun_server, agent->stun_server_port);
670 stun_agent_init (&stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
671 STUN_COMPATIBILITY_RFC3489, 0);
673 buffer_len = stun_usage_bind_create (&stun_agent,
674 &stun_message, stun_buffer, sizeof(stun_buffer));
676 for (k = component->local_candidates; k; k = k->next) {
677 NiceCandidate *candidate = (NiceCandidate *) k->data;
678 if (candidate->type == NICE_CANDIDATE_TYPE_HOST) {
679 /* send the conncheck */
680 nice_debug ("Agent %p : resending STUN on %s to keep the "
681 "candidate alive.", agent, candidate->foundation);
682 nice_socket_send (candidate->sockptr, &stun_server,
683 buffer_len, (gchar *)stun_buffer);
691 if (errors) {
692 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
693 goto done;
696 ret = TRUE;
698 done:
699 return ret;
702 static gboolean priv_conn_keepalive_tick (gpointer pointer)
704 NiceAgent *agent = pointer;
705 gboolean ret;
707 agent_lock();
708 if (g_source_is_destroyed (g_main_current_source ())) {
709 nice_debug ("Source was destroyed. "
710 "Avoided race condition in priv_conn_keepalive_tick");
711 agent_unlock ();
712 return FALSE;
715 ret = priv_conn_keepalive_tick_unlocked (agent);
716 if (ret == FALSE) {
717 if (agent->keepalive_timer_source) {
718 g_source_destroy (agent->keepalive_timer_source);
719 g_source_unref (agent->keepalive_timer_source);
720 agent->keepalive_timer_source = NULL;
723 agent_unlock();
724 return ret;
728 static gboolean priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer)
730 CandidateRefresh *cand = (CandidateRefresh *) pointer;
732 agent_lock();
734 /* A race condition might happen where the mutex above waits for the lock
735 * and in the meantime another thread destroys the source.
736 * In that case, we don't need to run our retransmission tick since it should
737 * have been cancelled */
738 if (g_source_is_destroyed (g_main_current_source ())) {
739 nice_debug ("Source was destroyed. "
740 "Avoided race condition in priv_turn_allocate_refresh_retransmissions_tick");
741 agent_unlock ();
742 return FALSE;
746 g_source_destroy (cand->tick_source);
747 g_source_unref (cand->tick_source);
748 cand->tick_source = NULL;
750 switch (stun_timer_refresh (&cand->timer)) {
751 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
753 /* Time out */
754 StunTransactionId id;
756 stun_message_id (&cand->stun_message, id);
757 stun_agent_forget_transaction (&cand->stun_agent, id);
759 refresh_cancel (cand);
760 break;
762 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
763 /* Retransmit */
764 nice_socket_send (cand->nicesock, &cand->server,
765 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
767 cand->tick_source = agent_timeout_add_with_context (cand->agent,
768 stun_timer_remainder (&cand->timer),
769 priv_turn_allocate_refresh_retransmissions_tick, cand);
770 break;
771 case STUN_USAGE_TIMER_RETURN_SUCCESS:
772 cand->tick_source = agent_timeout_add_with_context (cand->agent,
773 stun_timer_remainder (&cand->timer),
774 priv_turn_allocate_refresh_retransmissions_tick, cand);
775 break;
779 agent_unlock ();
780 return FALSE;
783 static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand)
785 uint8_t *username;
786 size_t username_len;
787 uint8_t *password;
788 size_t password_len;
789 size_t buffer_len = 0;
791 username = (uint8_t *)cand->turn->username;
792 username_len = (size_t) strlen (cand->turn->username);
793 password = (uint8_t *)cand->turn->password;
794 password_len = (size_t) strlen (cand->turn->password);
796 if (agent_to_turn_compatibility (cand->agent) ==
797 STUN_USAGE_TURN_COMPATIBILITY_MSN) {
798 username = g_base64_decode ((gchar *)username, &username_len);
799 password = g_base64_decode ((gchar *)password, &password_len);
802 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
803 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
804 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, -1,
805 username, username_len,
806 password, password_len,
807 agent_to_turn_compatibility (cand->agent));
809 if (agent_to_turn_compatibility (cand->agent) ==
810 STUN_USAGE_TURN_COMPATIBILITY_MSN) {
811 g_free (cand->msn_turn_username);
812 g_free (cand->msn_turn_password);
813 cand->msn_turn_username = username;
814 cand->msn_turn_password = password;
817 nice_debug ("Agent %p : Sending allocate Refresh %d", cand->agent, buffer_len);
819 if (cand->tick_source != NULL) {
820 g_source_destroy (cand->tick_source);
821 g_source_unref (cand->tick_source);
822 cand->tick_source = NULL;
825 if (buffer_len > 0) {
826 stun_timer_start (&cand->timer);
828 /* send the refresh */
829 nice_socket_send (cand->nicesock, &cand->server,
830 buffer_len, (gchar *)cand->stun_buffer);
832 cand->tick_source = agent_timeout_add_with_context (cand->agent,
833 stun_timer_remainder (&cand->timer),
834 priv_turn_allocate_refresh_retransmissions_tick, cand);
841 * Timer callback that handles refreshing TURN allocations
843 * This function is designed for the g_timeout_add() interface.
845 * @return will return FALSE when no more pending timers.
847 static gboolean priv_turn_allocate_refresh_tick (gpointer pointer)
849 CandidateRefresh *cand = (CandidateRefresh *) pointer;
851 agent_lock();
852 if (g_source_is_destroyed (g_main_current_source ())) {
853 nice_debug ("Source was destroyed. "
854 "Avoided race condition in priv_turn_allocate_refresh_tick");
855 agent_unlock ();
856 return FALSE;
859 priv_turn_allocate_refresh_tick_unlocked (cand);
860 agent_unlock ();
862 return FALSE;
867 * Initiates the next pending connectivity check.
869 * @return TRUE if a pending check was scheduled
871 gboolean conn_check_schedule_next (NiceAgent *agent)
873 gboolean res = priv_conn_check_unfreeze_next (agent);
874 nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res);
876 if (agent->discovery_unsched_items > 0)
877 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
879 /* step: call once imediately */
880 res = priv_conn_check_tick_unlocked ((gpointer) agent);
881 nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res);
883 /* step: schedule timer if not running yet */
884 if (res && agent->conncheck_timer_source == NULL) {
885 agent->conncheck_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_conn_check_tick, agent);
888 /* step: also start the keepalive timer */
889 if (agent->keepalive_timer_source == NULL) {
890 agent->keepalive_timer_source = agent_timeout_add_with_context (agent, NICE_AGENT_TIMER_TR_DEFAULT, priv_conn_keepalive_tick, agent);
893 nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res);
894 return res;
898 * Compares two connectivity check items. Checkpairs are sorted
899 * in descending priority order, with highest priority item at
900 * the start of the list.
902 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
904 if (a->priority > b->priority)
905 return -1;
906 else if (a->priority < b->priority)
907 return 1;
908 return 0;
912 * Preprocesses a new connectivity check by going through list
913 * of a any stored early incoming connectivity checks from
914 * the remote peer. If a matching incoming check has been already
915 * received, update the state of the new outgoing check 'pair'.
917 * @param agent context pointer
918 * @param stream which stream (of the agent)
919 * @param component pointer to component object to which 'pair'has been added
920 * @param pair newly added connectivity check
922 static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *pair)
924 GSList *i;
925 for (i = component->incoming_checks; i; i = i->next) {
926 IncomingCheck *icheck = i->data;
927 if (nice_address_equal (&icheck->from, &pair->remote->addr) &&
928 icheck->local_socket == pair->local->sockptr) {
929 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);
930 if (icheck->use_candidate)
931 priv_mark_pair_nominated (agent, stream, component, pair->remote);
932 priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate);
938 * Handle any processing steps for connectivity checks after
939 * remote candidates have been set. This function handles
940 * the special case where answerer has sent us connectivity
941 * checks before the answer (containing candidate information),
942 * reaches us. The special case is documented in sect 7.2
943 * if ICE spec (ID-19).
945 void conn_check_remote_candidates_set(NiceAgent *agent)
947 GSList *i, *j, *k, *l, *m, *n;
948 for (i = agent->streams; i ; i = i->next) {
949 Stream *stream = i->data;
950 for (j = stream->conncheck_list; j ; j = j->next) {
951 CandidateCheckPair *pair = j->data;
952 Component *component = stream_find_component_by_id (stream, pair->component_id);
953 gboolean match = FALSE;
955 /* performn delayed processing of spec steps section 7.2.1.4,
956 and section 7.2.1.5 */
957 priv_preprocess_conn_check_pending_data (agent, stream, component, pair);
959 for (k = component->incoming_checks; k; k = k->next) {
960 IncomingCheck *icheck = k->data;
961 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
962 * be handled separately */
963 for (l = component->remote_candidates; l; l = l->next) {
964 NiceCandidate *cand = l->data;
965 if (nice_address_equal (&icheck->from, &cand->addr)) {
966 match = TRUE;
967 break;
970 if (match != TRUE) {
971 /* note: we have gotten an incoming connectivity check from
972 * an address that is not a known remote candidate */
974 NiceCandidate *local_candidate = NULL;
975 NiceCandidate *remote_candidate = NULL;
977 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
978 agent->compatibility == NICE_COMPATIBILITY_MSN) {
979 /* We need to find which local candidate was used */
980 uint8_t uname[NICE_STREAM_MAX_UNAME];
981 guint uname_len;
983 nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
984 "stored pending check", agent);
986 for (m = component->remote_candidates;
987 m != NULL && remote_candidate == NULL; m = m->next) {
988 for (n = component->local_candidates; n; n = n->next) {
989 NiceCandidate *rcand = m->data;
990 NiceCandidate *lcand = n->data;
992 uname_len = priv_create_username (agent, stream,
993 component->id, rcand, lcand,
994 uname, sizeof (uname), TRUE);
996 stun_debug ("pending check, comparing username '");
997 stun_debug_bytes (icheck->username,
998 icheck->username? icheck->username_len : 0);
999 stun_debug ("' (%d) with '", icheck->username_len);
1000 stun_debug_bytes (uname, uname_len);
1001 stun_debug ("' (%d) : %d\n",
1002 uname_len, icheck->username &&
1003 uname_len == icheck->username_len &&
1004 memcmp (icheck->username, uname, uname_len) == 0);
1006 if (icheck->username &&
1007 uname_len == icheck->username_len &&
1008 memcmp (uname, icheck->username, icheck->username_len) == 0) {
1009 local_candidate = lcand;
1010 remote_candidate = rcand;
1011 break;
1017 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
1018 local_candidate == NULL) {
1019 /* if we couldn't match the username, then the matching remote
1020 * candidate hasn't been received yet.. we must wait */
1021 nice_debug ("Agent %p : Username check failed. pending check has "
1022 "to wait to be processed", agent);
1023 } else {
1024 NiceCandidate *candidate =
1025 discovery_learn_remote_peer_reflexive_candidate (agent,
1026 stream,
1027 component,
1028 icheck->priority,
1029 &icheck->from,
1030 icheck->local_socket,
1031 local_candidate, remote_candidate);
1032 if (candidate) {
1033 conn_check_add_for_candidate (agent, stream->id, component, candidate);
1035 priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate);
1041 /* Once we process the pending checks, we should free them to avoid
1042 * reprocessing them again if a dribble-mode set_remote_candidates
1043 * is called */
1044 for (m = component->incoming_checks; m; m = m->next) {
1045 IncomingCheck *icheck = m->data;
1046 g_free (icheck->username);
1047 g_slice_free (IncomingCheck, icheck);
1049 g_slist_free (component->incoming_checks);
1050 component->incoming_checks = NULL;
1056 * Enforces the upper limit for connectivity checks as described
1057 * in ICE spec section 5.7.3 (ID-19). See also
1058 * conn_check_add_for_candidate().
1060 static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
1062 guint list_len = g_slist_length (conncheck_list);
1063 guint c = 0;
1064 GSList *result = conncheck_list;
1066 if (list_len > upper_limit) {
1067 nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
1068 "Maximum connchecks allowed : %d", list_len, upper_limit);
1069 c = list_len - upper_limit;
1070 if (c == list_len) {
1071 /* case: delete whole list */
1072 g_slist_foreach (conncheck_list, conn_check_free_item, NULL);
1073 g_slist_free (conncheck_list),
1074 result = NULL;
1076 else {
1077 /* case: remove 'c' items from list end (lowest priority) */
1078 GSList *i, *tmp;
1080 g_assert (c > 0);
1081 i = g_slist_nth (conncheck_list, c - 1);
1083 tmp = i->next;
1084 i->next = NULL;
1086 if (tmp) {
1087 /* delete the rest of the connectivity check list */
1088 g_slist_foreach (tmp, conn_check_free_item, NULL);
1089 g_slist_free (tmp);
1094 return result;
1098 * Changes the selected pair for the component if 'pair' is nominated
1099 * and has higher priority than the currently selected pair. See
1100 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
1102 static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
1104 g_assert (component);
1105 g_assert (pair);
1106 if (pair->priority > component->selected_pair.priority) {
1107 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1108 "(prio:%" G_GUINT64_FORMAT ").", agent, component->id, pair->local->foundation,
1109 pair->remote->foundation, pair->priority);
1111 if (component->selected_pair.keepalive.tick_source != NULL) {
1112 g_source_destroy (component->selected_pair.keepalive.tick_source);
1113 g_source_unref (component->selected_pair.keepalive.tick_source);
1114 component->selected_pair.keepalive.tick_source = NULL;
1117 memset (&component->selected_pair, 0, sizeof(CandidatePair));
1118 component->selected_pair.local = pair->local;
1119 component->selected_pair.remote = pair->remote;
1120 component->selected_pair.priority = pair->priority;
1122 priv_conn_keepalive_tick_unlocked (agent);
1124 agent_signal_new_selected_pair (agent, pair->stream_id, component->id, pair->local->foundation, pair->remote->foundation);
1128 return TRUE;
1132 * Updates the check list state.
1134 * Implements parts of the algorithm described in
1135 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
1136 * component, all checks have been completed and have
1137 * failed, mark that component's state to NICE_CHECK_FAILED.
1139 * Sends a component state changesignal via 'agent'.
1141 static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream)
1143 GSList *i;
1144 /* note: emitting a signal might cause the client
1145 * to remove the stream, thus the component count
1146 * must be fetched before entering the loop*/
1147 guint c, components = stream->n_components;
1149 /* note: iterate the conncheck list for each component separately */
1150 for (c = 0; c < components; c++) {
1151 Component *comp = NULL;
1152 if (!agent_find_component (agent, stream->id, c+1, NULL, &comp))
1153 continue;
1155 for (i = stream->conncheck_list; i; i = i->next) {
1156 CandidateCheckPair *p = i->data;
1158 if (p->stream_id == stream->id &&
1159 p->component_id == (c + 1)) {
1160 if (p->state != NICE_CHECK_FAILED)
1161 break;
1165 /* note: all checks have failed
1166 * Set the component to FAILED only if it actually had remote candidates
1167 * that failed.. */
1168 if (i == NULL && comp != NULL && comp->remote_candidates != NULL)
1169 agent_signal_component_state_change (agent,
1170 stream->id,
1171 (c + 1), /* component-id */
1172 NICE_COMPONENT_STATE_FAILED);
1177 * Updates the check list state for a stream component.
1179 * Implements the algorithm described in ICE sect 8.1.2
1180 * "Updating States" (ID-19) as it applies to checks of
1181 * a certain component. If there are any nominated pairs,
1182 * ICE processing may be concluded, and component state is
1183 * changed to READY.
1185 * Sends a component state changesignal via 'agent'.
1187 static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component)
1189 GSList *i;
1190 guint succeeded = 0, nominated = 0;
1192 g_assert (component);
1194 /* step: search for at least one nominated pair */
1195 for (i = stream->conncheck_list; i; i = i->next) {
1196 CandidateCheckPair *p = i->data;
1197 if (p->component_id == component->id) {
1198 if (p->state == NICE_CHECK_SUCCEEDED ||
1199 p->state == NICE_CHECK_DISCOVERED) {
1200 ++succeeded;
1201 if (p->nominated == TRUE) {
1202 ++nominated;
1208 if (nominated > 0) {
1209 /* Only go to READY if no checks are left in progress. If there are
1210 * any that are kept, then this function will be called again when the
1211 * conncheck tick timer finishes them all */
1212 if (priv_prune_pending_checks (stream, component->id) == 0) {
1213 agent_signal_component_state_change (agent, stream->id,
1214 component->id, NICE_COMPONENT_STATE_READY);
1217 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id);
1221 * The remote party has signalled that the candidate pair
1222 * described by 'component' and 'remotecand' is nominated
1223 * for use.
1225 static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand)
1227 GSList *i;
1229 g_assert (component);
1231 /* step: search for at least one nominated pair */
1232 for (i = stream->conncheck_list; i; i = i->next) {
1233 CandidateCheckPair *pair = i->data;
1234 /* XXX: hmm, how to figure out to which local candidate the
1235 * check was sent to? let's mark all matching pairs
1236 * as nominated instead */
1237 if (pair->remote == remotecand) {
1238 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
1239 pair->nominated = TRUE;
1240 if (pair->state == NICE_CHECK_SUCCEEDED ||
1241 pair->state == NICE_CHECK_DISCOVERED)
1242 priv_update_selected_pair (agent, component, pair);
1243 priv_update_check_list_state_for_ready (agent, stream, component);
1249 * Creates a new connectivity check pair and adds it to
1250 * the agent's list of checks.
1252 static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
1254 gboolean result = FALSE;
1255 Stream *stream = agent_find_stream (agent, stream_id);
1256 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1257 if (pair) {
1258 GSList *modified_list =
1259 g_slist_insert_sorted (stream->conncheck_list, pair, (GCompareFunc)conn_check_compare);
1260 if (modified_list) {
1261 /* step: allocation and addition succesful, do rest of the work */
1263 pair->agent = agent;
1264 pair->stream_id = stream_id;
1265 pair->component_id = component->id;;
1266 pair->local = local;
1267 pair->remote = remote;
1268 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
1270 pair->priority = agent_candidate_pair_priority (agent, local, remote);
1271 pair->state = initial_state;
1272 nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state);
1273 pair->nominated = use_candidate;
1274 pair->controlling = agent->controlling_mode;
1276 /* note: for the first added check */
1277 if (!stream->conncheck_list)
1278 stream->conncheck_state = NICE_CHECKLIST_RUNNING;
1279 stream->conncheck_list = modified_list;
1281 result = TRUE;
1282 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id);
1284 /* implement the hard upper limit for number of
1285 checks (see sect 5.7.3 ICE ID-19): */
1286 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
1287 stream->conncheck_list =
1288 priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
1290 if (!stream->conncheck_list) {
1291 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1292 result = FALSE;
1295 else {
1296 /* memory alloc failed: list insert */
1297 conn_check_free_item (pair, NULL);
1298 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1301 else { /* memory alloc failed: new pair */
1302 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1305 return result;
1309 * Forms new candidate pairs by matching the new remote candidate
1310 * 'remote_cand' with all existing local candidates of 'component'.
1311 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1312 * Pairs" (ID-19).
1314 * @param agent context
1315 * @param component pointer to the component
1316 * @param remote remote candidate to match with
1318 * @return number of checks added, negative on fatal errors
1320 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
1322 GSList *i;
1323 int added = 0;
1325 for (i = component->local_candidates; i ; i = i->next) {
1327 NiceCandidate *local = i->data;
1329 /* note: match pairs only if transport and address family are the same */
1330 if (local->transport == remote->transport &&
1331 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
1333 gboolean result;
1335 /* note: do not create pairs where local candidate is
1336 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1337 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
1338 agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1339 local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE)
1340 continue;
1342 result = priv_add_new_check_pair (agent, stream_id, component, local, remote, NICE_CHECK_FROZEN, FALSE);
1343 if (result) {
1344 ++added;
1345 if (component->state < NICE_COMPONENT_STATE_CONNECTED) {
1346 agent_signal_component_state_change (agent,
1347 stream_id,
1348 component->id,
1349 NICE_COMPONENT_STATE_CONNECTING);
1350 } else {
1351 agent_signal_component_state_change (agent,
1352 stream_id,
1353 component->id,
1354 NICE_COMPONENT_STATE_CONNECTED);
1357 else {
1358 added = -1;
1359 break;
1364 return added;
1368 * Frees the CandidateCheckPair structure pointer to
1369 * by 'user data'. Compatible with g_slist_foreach().
1371 void conn_check_free_item (gpointer data, gpointer user_data)
1373 CandidateCheckPair *pair = data;
1374 g_assert (user_data == NULL);
1375 pair->stun_message.buffer = NULL;
1376 pair->stun_message.buffer_len = 0;
1377 g_slice_free (CandidateCheckPair, pair);
1381 * Frees all resources of all connectivity checks.
1383 void conn_check_free (NiceAgent *agent)
1385 GSList *i;
1386 for (i = agent->streams; i; i = i->next) {
1387 Stream *stream = i->data;
1389 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
1390 if (stream->conncheck_list) {
1391 g_slist_foreach (stream->conncheck_list, conn_check_free_item, NULL);
1392 g_slist_free (stream->conncheck_list),
1393 stream->conncheck_list = NULL;
1394 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1398 if (agent->conncheck_timer_source != NULL) {
1399 g_source_destroy (agent->conncheck_timer_source);
1400 g_source_unref (agent->conncheck_timer_source);
1401 agent->conncheck_timer_source = NULL;
1406 * Prunes the list of connectivity checks for items related
1407 * to stream 'stream_id'.
1409 * @return TRUE on success, FALSE on a fatal error
1411 gboolean conn_check_prune_stream (NiceAgent *agent, Stream *stream)
1413 CandidateCheckPair *pair;
1414 GSList *i;
1416 for (i = stream->conncheck_list; i ; ) {
1417 GSList *next = i->next;
1418 pair = i->data;
1420 g_assert (pair->stream_id == stream->id);
1422 stream->conncheck_list =
1423 g_slist_remove (stream->conncheck_list, pair);
1424 conn_check_free_item (pair, NULL);
1425 i = next;
1426 if (!stream->conncheck_list)
1427 break;
1430 if (!stream->conncheck_list) {
1431 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1432 conn_check_free (agent);
1435 /* return FALSE if there was a memory allocation failure */
1436 if (stream->conncheck_list == NULL && i != NULL)
1437 return FALSE;
1439 return TRUE;
1443 * Fills 'dest' with a username string for use in an outbound connectivity
1444 * checks. No more than 'dest_len' characters (including terminating
1445 * NULL) is ever written to the 'dest'.
1447 static
1448 size_t priv_gen_username (NiceAgent *agent, guint component_id,
1449 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
1451 guint len = 0;
1452 gsize remote_len = strlen (remote);
1453 gsize local_len = strlen (local);
1455 if (remote_len > 0 && local_len > 0) {
1456 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
1457 dest_len >= remote_len + local_len + 1) {
1458 memcpy (dest, remote, remote_len);
1459 len += remote_len;
1460 memcpy (dest + len, ":", 1);
1461 len++;
1462 memcpy (dest + len, local, local_len);
1463 len += local_len;
1464 } else if (agent->compatibility == NICE_COMPATIBILITY_WLM2009 &&
1465 dest_len >= remote_len + local_len + 4 ) {
1466 memcpy (dest, remote, remote_len);
1467 len += remote_len;
1468 memcpy (dest + len, ":", 1);
1469 len++;
1470 memcpy (dest + len, local, local_len);
1471 len += local_len;
1472 if (len % 4 != 0) {
1473 memset (dest + len, 0, 4 - (len % 4));
1474 len += 4 - (len % 4);
1476 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
1477 dest_len >= remote_len + local_len) {
1478 memcpy (dest, remote, remote_len);
1479 len += remote_len;
1480 memcpy (dest + len, local, local_len);
1481 len += local_len;
1482 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1483 gchar component_str[10];
1484 guchar *local_decoded = NULL;
1485 guchar *remote_decoded = NULL;
1486 gsize local_decoded_len;
1487 gsize remote_decoded_len;
1488 gsize total_len;
1489 int padding;
1491 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
1492 local_decoded = g_base64_decode (local, &local_decoded_len);
1493 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
1495 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
1496 padding = 4 - (total_len % 4);
1498 if (dest_len >= total_len + padding) {
1499 guchar pad_char[1] = {0};
1500 int i;
1502 memcpy (dest, remote_decoded, remote_decoded_len);
1503 len += remote_decoded_len;
1504 memcpy (dest + len, ":", 1);
1505 len++;
1506 memcpy (dest + len, component_str, strlen (component_str));
1507 len += strlen (component_str);
1509 memcpy (dest + len, ":", 1);
1510 len++;
1512 memcpy (dest + len, local_decoded, local_decoded_len);
1513 len += local_decoded_len;
1514 memcpy (dest + len, ":", 1);
1515 len++;
1516 memcpy (dest + len, component_str, strlen (component_str));;
1517 len += strlen (component_str);
1519 for (i = 0; i < padding; i++) {
1520 memcpy (dest + len, pad_char, 1);
1521 len++;
1526 g_free (local_decoded);
1527 g_free (remote_decoded);
1531 return len;
1535 * Fills 'dest' with a username string for use in an outbound connectivity
1536 * checks. No more than 'dest_len' characters (including terminating
1537 * NULL) is ever written to the 'dest'.
1539 static
1540 size_t priv_create_username (NiceAgent *agent, Stream *stream,
1541 guint component_id, NiceCandidate *remote, NiceCandidate *local,
1542 uint8_t *dest, guint dest_len, gboolean inbound)
1544 gchar *local_username = NULL;
1545 gchar *remote_username = NULL;
1548 if (remote && remote->username) {
1549 remote_username = remote->username;
1552 if (local && local->username) {
1553 local_username = local->username;
1556 if (stream) {
1557 if (remote_username == NULL) {
1558 remote_username = stream->remote_ufrag;
1560 if (local_username == NULL) {
1561 local_username = stream->local_ufrag;
1565 if (local_username && remote_username) {
1566 if (inbound) {
1567 return priv_gen_username (agent, component_id,
1568 local_username, remote_username, dest, dest_len);
1569 } else {
1570 return priv_gen_username (agent, component_id,
1571 remote_username, local_username, dest, dest_len);
1575 return 0;
1579 * Returns a password string for use in an outbound connectivity
1580 * check.
1582 static
1583 size_t priv_get_password (NiceAgent *agent, Stream *stream,
1584 NiceCandidate *remote, uint8_t **password)
1586 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
1587 return 0;
1589 if (remote && remote->password) {
1590 *password = (uint8_t *)remote->password;
1591 return strlen (remote->password);
1594 if (stream) {
1595 *password = (uint8_t *)stream->remote_password;
1596 return strlen (stream->remote_password);
1599 return 0;
1603 * Sends a connectivity check over candidate pair 'pair'.
1605 * @return zero on success, non-zero on error
1607 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
1610 /* note: following information is supplied:
1611 * - username (for USERNAME attribute)
1612 * - password (for MESSAGE-INTEGRITY)
1613 * - priority (for PRIORITY)
1614 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1615 * - USE-CANDIDATE (if sent by the controlling agent)
1618 guint32 priority =
1619 nice_candidate_ice_priority_full (
1620 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE,
1622 pair->local->component_id);
1624 uint8_t uname[NICE_STREAM_MAX_UNAME];
1625 size_t uname_len =
1626 priv_create_username (agent, agent_find_stream (agent, pair->stream_id),
1627 pair->component_id, pair->remote, pair->local, uname, sizeof (uname), FALSE);
1628 uint8_t *password = NULL;
1629 size_t password_len = priv_get_password (agent,
1630 agent_find_stream (agent, pair->stream_id), pair->remote, &password);
1632 bool controlling = agent->controlling_mode;
1633 /* XXX: add API to support different nomination modes: */
1634 bool cand_use = controlling;
1635 size_t buffer_len;
1636 unsigned int timeout;
1638 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1639 password = g_base64_decode ((gchar *) password, &password_len);
1643 gchar tmpbuf[INET6_ADDRSTRLEN];
1644 nice_address_to_string (&pair->remote->addr, tmpbuf);
1645 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,
1646 tmpbuf,
1647 ntohs(nice_address_get_port (&pair->remote->addr)),
1648 ((NiceSocket *)pair->local->sockptr)->fileno,
1649 pair->foundation, pair->component_id,
1650 (unsigned long long)agent->tie_breaker,
1651 uname, uname_len, password, password_len, priority);
1655 if (cand_use)
1656 pair->nominated = controlling;
1658 if (uname_len > 0) {
1660 buffer_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
1661 &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer),
1662 uname, uname_len, password, password_len,
1663 cand_use, controlling, priority,
1664 agent->tie_breaker,
1665 agent_to_ice_compatibility (agent));
1667 nice_debug ("Agent %p: conncheck created %d - %p", agent, buffer_len, pair->stun_message.buffer);
1669 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1670 g_free (password);
1673 if (buffer_len > 0) {
1674 stun_timer_start (&pair->timer);
1676 /* send the conncheck */
1677 nice_socket_send (pair->local->sockptr, &pair->remote->addr,
1678 buffer_len, (gchar *)pair->stun_buffer);
1680 timeout = stun_timer_remainder (&pair->timer);
1681 /* note: convert from milli to microseconds for g_time_val_add() */
1682 g_get_current_time (&pair->next_tick);
1683 g_time_val_add (&pair->next_tick, timeout * 1000);
1684 } else {
1685 pair->stun_message.buffer = NULL;
1686 pair->stun_message.buffer_len = 0;
1687 return -1;
1691 return 0;
1695 * Implemented the pruning steps described in ICE sect 8.1.2
1696 * "Updating States" (ID-19) after a pair has been nominated.
1698 * @see priv_update_check_list_state_failed_components()
1700 static guint priv_prune_pending_checks (Stream *stream, guint component_id)
1702 GSList *i;
1703 guint64 highest_nominated_priority = 0;
1704 guint in_progress = 0;
1706 nice_debug ("Finding highest priority for component %d", component_id);
1708 for (i = stream->conncheck_list; i; i = i->next) {
1709 CandidateCheckPair *p = i->data;
1710 if (p->component_id == component_id &&
1711 (p->state == NICE_CHECK_SUCCEEDED ||
1712 p->state == NICE_CHECK_DISCOVERED) &&
1713 p->nominated == TRUE){
1714 if (p->priority > highest_nominated_priority) {
1715 highest_nominated_priority = p->priority;
1720 nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
1721 "is %" G_GUINT64_FORMAT, highest_nominated_priority);
1723 /* step: cancel all FROZEN and WAITING pairs for the component */
1724 for (i = stream->conncheck_list; i; i = i->next) {
1725 CandidateCheckPair *p = i->data;
1726 if (p->component_id == component_id) {
1727 if (p->state == NICE_CHECK_FROZEN ||
1728 p->state == NICE_CHECK_WAITING) {
1729 p->state = NICE_CHECK_CANCELLED;
1730 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1733 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1734 if (p->state == NICE_CHECK_IN_PROGRESS) {
1735 if (highest_nominated_priority != 0 &&
1736 p->priority < highest_nominated_priority) {
1737 p->stun_message.buffer = NULL;
1738 p->stun_message.buffer_len = 0;
1739 p->state = NICE_CHECK_CANCELLED;
1740 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1741 } else {
1742 /* We must keep the higher priority pairs running because if a udp
1743 * packet was lost, we might end up using a bad candidate */
1744 nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %"
1745 G_GUINT64_FORMAT " is higher than currently nominated pair %"
1746 G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority);
1747 in_progress++;
1753 return in_progress;
1757 * Schedules a triggered check after a succesfully inbound
1758 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1760 * @param agent self pointer
1761 * @param component the check is related to
1762 * @param local_socket socket from which the inbound check was received
1763 * @param remote_cand remote candidate from which the inbound check was sent
1764 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1766 static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate)
1768 GSList *i;
1769 gboolean result = FALSE;
1771 for (i = stream->conncheck_list; i ; i = i->next) {
1772 CandidateCheckPair *p = i->data;
1773 if (p->component_id == component->id &&
1774 p->remote == remote_cand &&
1775 p->local->sockptr == local_socket) {
1777 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p);
1779 if (p->state == NICE_CHECK_WAITING ||
1780 p->state == NICE_CHECK_FROZEN)
1781 priv_conn_check_initiate (agent, p);
1782 else if (p->state == NICE_CHECK_IN_PROGRESS) {
1783 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1784 * we should cancel the existing one, and send a new one...? :P */
1785 nice_debug ("Agent %p : Skipping triggered check, already in progress..", agent);
1787 else if (p->state == NICE_CHECK_SUCCEEDED ||
1788 p->state == NICE_CHECK_DISCOVERED) {
1789 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent);
1790 /* note: this is a bit unsure corner-case -- let's do the
1791 same state update as for processing responses to our own checks */
1792 priv_update_check_list_state_for_ready (agent, stream, component);
1794 /* note: to take care of the controlling-controlling case in
1795 * aggressive nomination mode, send a new triggered
1796 * check to nominate the pair */
1797 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
1798 agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1799 agent->controlling_mode)
1800 priv_conn_check_initiate (agent, p);
1803 /* note: the spec says the we SHOULD retransmit in-progress
1804 * checks immediately, but we won't do that now */
1806 return TRUE;
1811 NiceCandidate *local = NULL;
1813 for (i = component->local_candidates; i ; i = i->next) {
1814 local = i->data;
1815 if (local->sockptr == local_socket)
1816 break;
1818 if (i) {
1819 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
1820 result = priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate);
1822 else
1823 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
1826 return result;
1831 * Sends a reply to an succesfully received STUN connectivity
1832 * check request. Implements parts of the ICE spec section 7.2 (STUN
1833 * Server Procedures).
1835 * @param agent context pointer
1836 * @param stream which stream (of the agent)
1837 * @param component which component (of the stream)
1838 * @param rcand remote candidate from which the request came, if NULL,
1839 * the response is sent immediately but no other processing is done
1840 * @param toaddr address to which reply is sent
1841 * @param socket the socket over which the request came
1842 * @param rbuf_len length of STUN message to send
1843 * @param rbuf buffer containing the STUN message to send
1844 * @param use_candidate whether the request had USE_CANDIDATE attribute
1846 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1848 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)
1850 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
1853 gchar tmpbuf[INET6_ADDRSTRLEN];
1854 nice_address_to_string (toaddr, tmpbuf);
1855 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent,
1856 tmpbuf,
1857 nice_address_get_port (toaddr),
1858 socket->fileno,
1859 (unsigned)rbuf_len,
1860 rcand, component->id,
1861 (int)use_candidate);
1864 nice_socket_send (socket, toaddr, rbuf_len, (const gchar*)rbuf);
1866 if (rcand) {
1867 /* note: upon succesful check, make the reserve check immediately */
1868 priv_schedule_triggered_check (agent, stream, component, socket, rcand, use_candidate);
1870 if (use_candidate)
1871 priv_mark_pair_nominated (agent, stream, component, rcand);
1876 * Stores information of an incoming STUN connectivity check
1877 * for later use. This is only needed when a check is received
1878 * before we get information about the remote candidates (via
1879 * SDP or other signaling means).
1881 * @return non-zero on error, zero on success
1883 static int priv_store_pending_check (NiceAgent *agent, Component *component,
1884 const NiceAddress *from, NiceSocket *socket, uint8_t *username,
1885 uint16_t username_len, uint32_t priority, gboolean use_candidate)
1887 IncomingCheck *icheck;
1888 nice_debug ("Agent %p : Storing pending check.", agent);
1890 if (component->incoming_checks &&
1891 g_slist_length (component->incoming_checks) >=
1892 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
1893 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
1894 return -1;
1897 icheck = g_slice_new0 (IncomingCheck);
1898 if (icheck) {
1899 GSList *pending = g_slist_append (component->incoming_checks, icheck);
1900 if (pending) {
1901 component->incoming_checks = pending;
1902 icheck->from = *from;
1903 icheck->local_socket = socket;
1904 icheck->priority = priority;
1905 icheck->use_candidate = use_candidate;
1906 icheck->username_len = username_len;
1907 icheck->username = NULL;
1908 if (username_len > 0)
1909 icheck->username = g_memdup (username, username_len);
1910 return 0;
1914 return -1;
1918 * Adds a new pair, discovered from an incoming STUN response, to
1919 * the connectivity check list.
1921 * @return created pair, or NULL on fatal (memory allocation) errors
1923 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
1925 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1926 if (pair) {
1927 Stream *stream = agent_find_stream (agent, stream_id);
1928 GSList *modified_list = g_slist_append (stream->conncheck_list, pair);
1929 if (modified_list) {
1930 stream->conncheck_list = modified_list;
1931 pair->agent = agent;
1932 pair->stream_id = stream_id;
1933 pair->component_id = component_id;;
1934 pair->local = local_cand;
1935 pair->remote = parent_pair->remote;
1936 pair->state = NICE_CHECK_DISCOVERED;
1937 nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair);
1938 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local_cand->foundation, parent_pair->remote->foundation);
1939 if (agent->controlling_mode == TRUE)
1940 pair->priority = nice_candidate_pair_priority (local_cand->priority, parent_pair->priority);
1941 else
1942 pair->priority = nice_candidate_pair_priority (parent_pair->priority, local_cand->priority);
1943 pair->nominated = FALSE;
1944 pair->controlling = agent->controlling_mode;
1945 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation);
1946 return pair;
1950 return NULL;
1954 * Recalculates priorities of all candidate pairs. This
1955 * is required after a conflict in ICE roles.
1957 static void priv_recalculate_pair_priorities (NiceAgent *agent)
1959 GSList *i, *j;
1961 for (i = agent->streams; i; i = i->next) {
1962 Stream *stream = i->data;
1963 for (j = stream->conncheck_list; j; j = j->next) {
1964 CandidateCheckPair *p = j->data;
1965 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
1971 * Change the agent role if different from 'control'. Can be
1972 * initiated both by handling of incoming connectivity checks,
1973 * and by processing the responses to checks sent by us.
1975 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
1977 /* role conflict, change mode; wait for a new conn. check */
1978 if (control != agent->controlling_mode) {
1979 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control);
1980 agent->controlling_mode = control;
1981 /* the pair priorities depend on the roles, so recalculation
1982 * is needed */
1983 priv_recalculate_pair_priorities (agent);
1985 else
1986 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control);
1990 * Checks whether the mapped address in connectivity check response
1991 * matches any of the known local candidates. If not, apply the
1992 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
1994 * @param agent context pointer
1995 * @param stream which stream (of the agent)
1996 * @param component which component (of the stream)
1997 * @param p the connectivity check pair for which we got a response
1998 * @param socketptr socket used to send the reply
1999 * @param mapped_sockaddr mapped address in the response
2001 * @return pointer to a new pair if one was created, otherwise NULL
2003 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)
2005 CandidateCheckPair *new_pair = NULL;
2006 NiceAddress mapped;
2007 GSList *j;
2008 gboolean local_cand_matches = FALSE;
2010 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
2012 for (j = component->local_candidates; j; j = j->next) {
2013 NiceCandidate *cand = j->data;
2014 if (nice_address_equal (&mapped, &cand->addr)) {
2015 local_cand_matches = TRUE;
2016 break;
2020 if (local_cand_matches == TRUE) {
2021 /* note: this is same as "adding to VALID LIST" in the spec
2022 text */
2023 p->state = NICE_CHECK_SUCCEEDED;
2024 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
2025 priv_conn_check_unfreeze_related (agent, stream, p);
2027 else {
2028 NiceCandidate *cand =
2029 discovery_add_peer_reflexive_candidate (agent,
2030 stream->id,
2031 component->id,
2032 &mapped,
2033 sockptr,
2034 local_candidate,
2035 remote_candidate);
2036 p->state = NICE_CHECK_FAILED;
2037 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
2039 /* step: add a new discovered pair (see ICE 7.1.2.2.2
2040 "Constructing a Valid Pair" (ID-19)) */
2041 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p);
2042 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair);
2045 return new_pair;
2049 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
2050 * check transaction. If found, the reply is processed. Implements
2051 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
2053 * @return TRUE if a matching transaction is found
2055 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)
2057 struct sockaddr_storage sockaddr;
2058 socklen_t socklen = sizeof (sockaddr);
2059 GSList *i;
2060 StunUsageIceReturn res;
2061 gboolean trans_found = FALSE;
2062 StunTransactionId discovery_id;
2063 StunTransactionId response_id;
2064 stun_message_id (resp, response_id);
2066 for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) {
2067 CandidateCheckPair *p = i->data;
2069 if (p->stun_message.buffer) {
2070 stun_message_id (&p->stun_message, discovery_id);
2072 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2073 res = stun_usage_ice_conncheck_process (resp,
2074 (struct sockaddr *) &sockaddr, &socklen,
2075 agent_to_ice_compatibility (agent));
2076 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
2077 "(controlling=%d).", agent, p, (int)res, agent->controlling_mode);
2079 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2080 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
2081 /* case: found a matching connectivity check request */
2083 CandidateCheckPair *ok_pair = NULL;
2085 nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p);
2086 p->stun_message.buffer = NULL;
2087 p->stun_message.buffer_len = 0;
2089 /* step: verify that response came from the same IP address we
2090 * sent the original request to (see 7.1.2.1. "Failure
2091 * Cases") */
2092 if (nice_address_equal (from, &p->remote->addr) != TRUE) {
2093 gchar tmpbuf[INET6_ADDRSTRLEN];
2094 gchar tmpbuf2[INET6_ADDRSTRLEN];
2096 p->state = NICE_CHECK_FAILED;
2097 nice_debug ("Agent %p : conncheck %p FAILED"
2098 " (mismatch of source address).", agent, p);
2099 nice_address_to_string (&p->remote->addr, tmpbuf);
2100 nice_address_to_string (from, tmpbuf2);
2101 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
2102 tmpbuf, nice_address_get_port (&p->remote->addr),
2103 tmpbuf2, nice_address_get_port (from));
2105 trans_found = TRUE;
2106 break;
2109 /* note: CONNECTED but not yet READY, see docs */
2111 /* step: handle the possible case of a peer-reflexive
2112 * candidate where the mapped-address in response does
2113 * not match any local candidate, see 7.1.2.2.1
2114 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
2116 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
2117 /* note: this is same as "adding to VALID LIST" in the spec
2118 text */
2119 p->state = NICE_CHECK_SUCCEEDED;
2120 nice_debug ("Agent %p : Mapped address not found."
2121 " conncheck %p SUCCEEDED.", agent, p);
2122 priv_conn_check_unfreeze_related (agent, stream, p);
2123 } else {
2124 ok_pair = priv_process_response_check_for_peer_reflexive(agent,
2125 stream, component, p, sockptr, (struct sockaddr *) &sockaddr,
2126 local_candidate, remote_candidate);
2130 if (!ok_pair)
2131 ok_pair = p;
2133 /* Do not step down to CONNECTED if we're already at state READY*/
2134 if (component->state != NICE_COMPONENT_STATE_READY) {
2135 /* step: notify the client of a new component state (must be done
2136 * before the possible check list state update step */
2137 agent_signal_component_state_change (agent,
2138 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2142 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
2143 Nominated Flag" (ID-19) */
2144 if (ok_pair->nominated == TRUE)
2145 priv_update_selected_pair (agent, component, ok_pair);
2147 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
2148 states" and 8.1.2 "Updating States", ID-19) */
2149 priv_update_check_list_state_for_ready (agent, stream, component);
2151 trans_found = TRUE;
2152 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2153 /* case: role conflict error, need to restart with new role */
2154 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p);
2155 /* note: our role might already have changed due to an
2156 * incoming request, but if not, change role now;
2157 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
2158 priv_check_for_role_conflict (agent, !p->controlling);
2160 p->stun_message.buffer = NULL;
2161 p->stun_message.buffer_len = 0;
2162 p->state = NICE_CHECK_WAITING;
2163 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
2164 trans_found = TRUE;
2165 } else if (res == STUN_USAGE_ICE_RETURN_ERROR) {
2166 /* case: STUN error, the check STUN context was freed */
2167 nice_debug ("Agent %p : conncheck %p FAILED.", agent, p);
2168 p->stun_message.buffer = NULL;
2169 p->stun_message.buffer_len = 0;
2170 trans_found = TRUE;
2176 return trans_found;
2180 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2181 * transaction. If found, a reply is sent.
2183 * @return TRUE if a matching transaction is found
2185 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp)
2187 struct sockaddr_storage sockaddr;
2188 socklen_t socklen = sizeof (sockaddr);
2189 struct sockaddr_storage alternate;
2190 socklen_t alternatelen = sizeof (sockaddr);
2191 GSList *i;
2192 StunUsageBindReturn res;
2193 gboolean trans_found = FALSE;
2194 StunTransactionId discovery_id;
2195 StunTransactionId response_id;
2196 stun_message_id (resp, response_id);
2198 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
2199 CandidateDiscovery *d = i->data;
2201 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
2202 d->stun_message.buffer) {
2203 stun_message_id (&d->stun_message, discovery_id);
2205 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2206 res = stun_usage_bind_process (resp, (struct sockaddr *) &sockaddr,
2207 &socklen, (struct sockaddr *) &alternate, &alternatelen);
2208 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
2209 agent, d, (int)res);
2211 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
2212 /* handle alternate server */
2213 NiceAddress niceaddr;
2214 nice_address_set_from_sockaddr (&niceaddr,
2215 (struct sockaddr *) &alternate);
2216 d->server = niceaddr;
2218 d->pending = FALSE;
2219 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
2220 /* case: succesful binding discovery, create a new local candidate */
2221 NiceAddress niceaddr;
2222 nice_address_set_from_sockaddr (&niceaddr,
2223 (struct sockaddr *) &sockaddr);
2225 discovery_add_server_reflexive_candidate (
2226 d->agent,
2227 d->stream->id,
2228 d->component->id,
2229 &niceaddr,
2230 d->nicesock);
2232 d->stun_message.buffer = NULL;
2233 d->stun_message.buffer_len = 0;
2234 d->done = TRUE;
2235 trans_found = TRUE;
2236 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
2237 /* case: STUN error, the check STUN context was freed */
2238 d->stun_message.buffer = NULL;
2239 d->stun_message.buffer_len = 0;
2240 d->done = TRUE;
2241 trans_found = TRUE;
2247 return trans_found;
2251 static CandidateRefresh *
2252 priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand,
2253 guint lifetime)
2255 CandidateRefresh *cand;
2256 NiceAgent *agent = cdisco->agent;
2257 GSList *modified_list;
2259 cand = g_slice_new0 (CandidateRefresh);
2260 if (cand) {
2261 modified_list = g_slist_append (agent->refresh_list, cand);
2263 if (modified_list) {
2264 cand->nicesock = cdisco->nicesock;
2265 cand->relay_socket = relay_cand->sockptr;
2266 cand->server = cdisco->server;
2267 cand->turn = cdisco->turn;
2268 cand->stream = cdisco->stream;
2269 cand->component = cdisco->component;
2270 cand->agent = cdisco->agent;
2271 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
2272 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2273 agent, cand, (lifetime - 60) * 1000);
2274 agent->refresh_list = modified_list;
2276 /* step: also start the refresh timer */
2277 /* refresh should be sent 1 minute before it expires */
2278 cand->timer_source =
2279 agent_timeout_add_with_context (agent, (lifetime - 60) * 1000,
2280 priv_turn_allocate_refresh_tick, cand);
2282 nice_debug ("timer source is : %d", cand->timer_source);
2286 return cand;
2290 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2291 * transaction. If found, a reply is sent.
2293 * @return TRUE if a matching transaction is found
2295 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
2297 struct sockaddr_storage sockaddr;
2298 socklen_t socklen = sizeof (sockaddr);
2299 struct sockaddr_storage alternate;
2300 socklen_t alternatelen = sizeof (alternate);
2301 struct sockaddr_storage relayaddr;
2302 socklen_t relayaddrlen = sizeof (relayaddr);
2303 uint32_t lifetime;
2304 uint32_t bandwidth;
2305 GSList *i;
2306 StunUsageTurnReturn res;
2307 gboolean trans_found = FALSE;
2308 StunTransactionId discovery_id;
2309 StunTransactionId response_id;
2310 stun_message_id (resp, response_id);
2312 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
2313 CandidateDiscovery *d = i->data;
2315 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
2316 d->stun_message.buffer) {
2317 stun_message_id (&d->stun_message, discovery_id);
2319 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2320 res = stun_usage_turn_process (resp,
2321 (struct sockaddr *) &relayaddr, &relayaddrlen,
2322 (struct sockaddr *) &sockaddr, &socklen,
2323 (struct sockaddr *) &alternate, &alternatelen,
2324 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
2325 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2326 agent, d, (int)res);
2328 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
2329 /* handle alternate server */
2330 nice_address_set_from_sockaddr (&d->server,
2331 (struct sockaddr *) &alternate);
2332 nice_address_set_from_sockaddr (&d->turn->server,
2333 (struct sockaddr *) &alternate);
2335 d->pending = FALSE;
2336 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
2337 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
2338 /* case: succesful allocate, create a new local candidate */
2339 NiceAddress niceaddr;
2340 NiceCandidate *relay_cand;
2342 /* We also received our mapped address */
2343 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
2344 nice_address_set_from_sockaddr (&niceaddr,
2345 (struct sockaddr *) &sockaddr);
2347 discovery_add_server_reflexive_candidate (
2348 d->agent,
2349 d->stream->id,
2350 d->component->id,
2351 &niceaddr,
2352 d->nicesock);
2355 nice_address_set_from_sockaddr (&niceaddr,
2356 (struct sockaddr *) &relayaddr);
2357 relay_cand = discovery_add_relay_candidate (
2358 d->agent,
2359 d->stream->id,
2360 d->component->id,
2361 &niceaddr,
2362 d->nicesock,
2363 d->turn);
2365 if (relay_cand) {
2366 priv_add_new_turn_refresh (d, relay_cand, lifetime);
2369 d->stun_message.buffer = NULL;
2370 d->stun_message.buffer_len = 0;
2371 d->done = TRUE;
2372 trans_found = TRUE;
2373 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2374 int code = -1;
2375 uint8_t *sent_realm = NULL;
2376 uint8_t *recv_realm = NULL;
2377 uint16_t sent_realm_len = 0;
2378 uint16_t recv_realm_len = 0;
2380 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
2381 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2382 recv_realm = (uint8_t *) stun_message_find (resp,
2383 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2385 /* check for unauthorized error response */
2386 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
2387 stun_message_get_class (resp) == STUN_ERROR &&
2388 stun_message_find_error (resp, &code) ==
2389 STUN_MESSAGE_RETURN_SUCCESS &&
2390 recv_realm != NULL && recv_realm_len > 0) {
2392 if (code == 438 ||
2393 (code == 401 &&
2394 !(recv_realm_len == sent_realm_len &&
2395 sent_realm != NULL &&
2396 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2397 d->stun_resp_msg = *resp;
2398 memcpy (d->stun_resp_buffer, resp->buffer,
2399 stun_message_length (resp));
2400 d->stun_resp_msg.buffer = d->stun_resp_buffer;
2401 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
2402 d->pending = FALSE;
2403 } else {
2404 /* case: a real unauthorized error */
2405 d->stun_message.buffer = NULL;
2406 d->stun_message.buffer_len = 0;
2407 d->done = TRUE;
2409 } else {
2410 /* case: STUN error, the check STUN context was freed */
2411 d->stun_message.buffer = NULL;
2412 d->stun_message.buffer_len = 0;
2413 d->done = TRUE;
2415 trans_found = TRUE;
2421 return trans_found;
2426 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2427 * transaction. If found, a reply is sent.
2429 * @return TRUE if a matching transaction is found
2431 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
2433 uint32_t lifetime;
2434 GSList *i;
2435 StunUsageTurnReturn res;
2436 gboolean trans_found = FALSE;
2437 StunTransactionId refresh_id;
2438 StunTransactionId response_id;
2439 stun_message_id (resp, response_id);
2441 for (i = agent->refresh_list; i && trans_found != TRUE; i = i->next) {
2442 CandidateRefresh *cand = i->data;
2444 if (cand->stun_message.buffer) {
2445 stun_message_id (&cand->stun_message, refresh_id);
2447 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
2448 res = stun_usage_turn_refresh_process (resp,
2449 &lifetime, agent_to_turn_compatibility (cand->agent));
2450 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2451 agent, cand, (int)res);
2452 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
2453 /* refresh should be sent 1 minute before it expires */
2454 cand->timer_source =
2455 agent_timeout_add_with_context (cand->agent, (lifetime - 60) * 1000,
2456 priv_turn_allocate_refresh_tick, cand);
2458 g_source_destroy (cand->tick_source);
2459 g_source_unref (cand->tick_source);
2460 cand->tick_source = NULL;
2461 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2462 int code = -1;
2463 uint8_t *sent_realm = NULL;
2464 uint8_t *recv_realm = NULL;
2465 uint16_t sent_realm_len = 0;
2466 uint16_t recv_realm_len = 0;
2468 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
2469 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2470 recv_realm = (uint8_t *) stun_message_find (resp,
2471 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2473 /* check for unauthorized error response */
2474 if (cand->agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
2475 stun_message_get_class (resp) == STUN_ERROR &&
2476 stun_message_find_error (resp, &code) ==
2477 STUN_MESSAGE_RETURN_SUCCESS &&
2478 recv_realm != NULL && recv_realm_len > 0) {
2480 if (code == 438 ||
2481 (code == 401 &&
2482 !(recv_realm_len == sent_realm_len &&
2483 sent_realm != NULL &&
2484 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2485 cand->stun_resp_msg = *resp;
2486 memcpy (cand->stun_resp_buffer, resp->buffer,
2487 stun_message_length (resp));
2488 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
2489 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
2490 priv_turn_allocate_refresh_tick_unlocked (cand);
2491 } else {
2492 /* case: a real unauthorized error */
2493 refresh_cancel (cand);
2495 } else {
2496 /* case: STUN error, the check STUN context was freed */
2497 refresh_cancel (cand);
2499 trans_found = TRUE;
2505 return trans_found;
2509 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
2510 Component *component, StunMessage *resp)
2512 StunTransactionId conncheck_id;
2513 StunTransactionId response_id;
2514 stun_message_id (resp, response_id);
2516 if (component->selected_pair.keepalive.stun_message.buffer) {
2517 stun_message_id (&component->selected_pair.keepalive.stun_message,
2518 conncheck_id);
2519 if (memcmp (conncheck_id, response_id, sizeof(StunTransactionId)) == 0) {
2520 nice_debug ("Agent %p : Keepalive for selected pair received.",
2521 agent);
2522 if (component->selected_pair.keepalive.tick_source) {
2523 g_source_destroy (component->selected_pair.keepalive.tick_source);
2524 g_source_unref (component->selected_pair.keepalive.tick_source);
2525 component->selected_pair.keepalive.tick_source = NULL;
2527 component->selected_pair.keepalive.stun_message.buffer = NULL;
2528 return TRUE;
2532 return FALSE;
2536 typedef struct {
2537 NiceAgent *agent;
2538 Stream *stream;
2539 Component *component;
2540 uint8_t *password;
2541 } conncheck_validater_data;
2543 static bool conncheck_stun_validater (StunAgent *agent,
2544 StunMessage *message, uint8_t *username, uint16_t username_len,
2545 uint8_t **password, size_t *password_len, void *user_data)
2547 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
2548 GSList *i;
2549 gchar *ufrag = NULL;
2550 gsize ufrag_len;
2552 for (i = data->component->local_candidates; i; i = i->next) {
2553 NiceCandidate *cand = i->data;
2555 ufrag = NULL;
2556 if (cand->username)
2557 ufrag = cand->username;
2558 else if (data->stream)
2559 ufrag = data->stream->local_ufrag;
2560 ufrag_len = ufrag? strlen (ufrag) : 0;
2562 if (ufrag && data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2563 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
2565 if (ufrag == NULL)
2566 continue;
2568 stun_debug ("Comparing username '");
2569 stun_debug_bytes (username, username_len);
2570 stun_debug ("' (%d) with '", username_len);
2571 stun_debug_bytes (ufrag, ufrag_len);
2572 stun_debug ("' (%d) : %d\n",
2573 ufrag_len, memcmp (username, ufrag, ufrag_len));
2574 if (ufrag_len > 0 && username_len >= ufrag_len &&
2575 memcmp (username, ufrag, ufrag_len) == 0) {
2576 gchar *pass = NULL;
2578 if (cand->password)
2579 pass = cand->password;
2580 else if(data->stream->local_password)
2581 pass = data->stream->local_password;
2583 if (pass) {
2584 *password = (uint8_t *) pass;
2585 *password_len = strlen (pass);
2587 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN) {
2588 data->password = g_base64_decode (pass, password_len);
2589 *password = data->password;
2593 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2594 g_free (ufrag);
2596 stun_debug ("Found valid username, returning password: '%s'\n", *password);
2597 return TRUE;
2600 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2601 g_free (ufrag);
2604 return FALSE;
2609 * Processing an incoming STUN message.
2611 * @param agent self pointer
2612 * @param stream stream the packet is related to
2613 * @param component component the packet is related to
2614 * @param socket socket from which the packet was received
2615 * @param from address of the sender
2616 * @param buf message contents
2617 * @param buf message length
2619 * @pre contents of 'buf' is a STUN message
2621 * @return XXX (what FALSE means exactly?)
2623 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
2624 Component *component, NiceSocket *socket, const NiceAddress *from,
2625 gchar *buf, guint len)
2627 struct sockaddr_storage sockaddr;
2628 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
2629 ssize_t res;
2630 size_t rbuf_len = sizeof (rbuf);
2631 bool control = agent->controlling_mode;
2632 uint8_t uname[NICE_STREAM_MAX_UNAME];
2633 guint uname_len;
2634 uint8_t *username;
2635 uint16_t username_len;
2636 StunMessage req;
2637 StunMessage msg;
2638 StunValidationStatus valid;
2639 conncheck_validater_data validater_data = {agent, stream, component, NULL};
2640 GSList *i, *j;
2641 NiceCandidate *remote_candidate = NULL;
2642 NiceCandidate *remote_candidate2 = NULL;
2643 NiceCandidate *local_candidate = NULL;
2644 gboolean discovery_msg = FALSE;
2646 nice_address_copy_to_sockaddr (from, (struct sockaddr *) &sockaddr);
2648 /* note: contents of 'buf' already validated, so it is
2649 * a valid and fully received STUN message */
2651 #ifndef NDEBUG
2653 gchar tmpbuf[INET6_ADDRSTRLEN];
2654 nice_address_to_string (from, tmpbuf);
2655 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2656 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
2658 #endif
2660 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2662 valid = stun_agent_validate (&agent->stun_agent, &req,
2663 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2665 /* Check for discovery candidates stun agents */
2666 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2667 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2668 for (i = agent->discovery_list; i; i = i->next) {
2669 CandidateDiscovery *d = i->data;
2670 if (d->stream == stream && d->component == component &&
2671 d->nicesock == socket) {
2672 valid = stun_agent_validate (&d->stun_agent, &req,
2673 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2675 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2676 continue;
2678 discovery_msg = TRUE;
2679 break;
2683 /* Check for relay refresh stun agents */
2684 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2685 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2686 for (i = agent->refresh_list; i; i = i->next) {
2687 CandidateRefresh *r = i->data;
2688 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream,
2689 stream, r->component, component, r->nicesock, r->relay_socket, socket);
2690 if (r->stream == stream && r->component == component &&
2691 (r->nicesock == socket || r->relay_socket == socket)) {
2692 valid = stun_agent_validate (&r->stun_agent, &req,
2693 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2694 nice_debug ("Validating gave %d", valid);
2695 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2696 continue;
2697 discovery_msg = TRUE;
2698 break;
2704 if (validater_data.password)
2705 g_free (validater_data.password);
2707 if (valid == STUN_VALIDATION_NOT_STUN ||
2708 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
2709 valid == STUN_VALIDATION_BAD_REQUEST)
2711 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2712 agent);
2713 return FALSE;
2716 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
2717 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
2718 rbuf_len = stun_agent_build_unknown_attributes_error (&agent->stun_agent,
2719 &msg, rbuf, rbuf_len, &req);
2720 if (len == 0)
2721 return FALSE;
2723 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2724 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2726 return TRUE;
2729 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
2730 nice_debug ("Agent %p : Integrity check failed.", agent);
2732 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2733 &req, STUN_ERROR_UNAUTHORIZED)) {
2734 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2735 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2736 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2738 return TRUE;
2740 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
2741 nice_debug ("Agent %p : Integrity check failed.", agent);
2742 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2743 &req, STUN_ERROR_BAD_REQUEST)) {
2744 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2745 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2746 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2748 return TRUE;
2751 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
2752 &username_len);
2754 for (i = component->remote_candidates; i; i = i->next) {
2755 NiceCandidate *cand = i->data;
2756 if (nice_address_equal (from, &cand->addr)) {
2757 remote_candidate = cand;
2758 break;
2762 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2763 agent->compatibility == NICE_COMPATIBILITY_MSN) {
2764 /* We need to find which local candidate was used */
2765 for (i = component->remote_candidates;
2766 i != NULL && remote_candidate2 == NULL; i = i->next) {
2767 for (j = component->local_candidates; j; j = j->next) {
2768 gboolean inbound = TRUE;
2769 NiceCandidate *rcand = i->data;
2770 NiceCandidate *lcand = j->data;
2772 /* If we receive a response, then the username is local:remote */
2773 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2774 if (stun_message_get_class (&req) == STUN_REQUEST ||
2775 stun_message_get_class (&req) == STUN_INDICATION) {
2776 inbound = TRUE;
2777 } else {
2778 inbound = FALSE;
2782 uname_len = priv_create_username (agent, stream,
2783 component->id, rcand, lcand,
2784 uname, sizeof (uname), inbound);
2786 stun_debug ("Comparing username '");
2787 stun_debug_bytes (username, username? username_len : 0);
2788 stun_debug ("' (%d) with '", username_len);
2789 stun_debug_bytes (uname, uname_len);
2790 stun_debug ("' (%d) : %d\n",
2791 uname_len, username && uname_len == username_len &&
2792 memcmp (username, uname, uname_len) == 0);
2794 if (username &&
2795 uname_len == username_len &&
2796 memcmp (uname, username, username_len) == 0) {
2797 local_candidate = lcand;
2798 remote_candidate2 = rcand;
2799 break;
2805 if (component->remote_candidates &&
2806 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2807 local_candidate == NULL &&
2808 discovery_msg == FALSE) {
2809 /* if we couldn't match the username and the stun agent has
2810 IGNORE_CREDENTIALS then we have an integrity check failing.
2811 This could happen with the race condition of receiving connchecks
2812 before the remote candidates are added. Just drop the message, and let
2813 the retransmissions make it work. */
2814 nice_debug ("Agent %p : Username check failed.", agent);
2815 return TRUE;
2818 if (valid != STUN_VALIDATION_SUCCESS) {
2819 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent, valid);
2820 return FALSE;
2824 if (stun_message_get_class (&req) == STUN_REQUEST) {
2825 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2826 if (local_candidate && remote_candidate2) {
2827 username = (uint8_t *) stun_message_find (&req,
2828 STUN_ATTRIBUTE_USERNAME, &username_len);
2829 uname_len = priv_create_username (agent, stream,
2830 component->id, remote_candidate2, local_candidate,
2831 uname, sizeof (uname), FALSE);
2832 memcpy (username, uname, username_len);
2833 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
2834 &req.key_len);
2835 } else {
2836 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
2837 "Ignoring request", agent);
2838 return TRUE;
2842 rbuf_len = sizeof (rbuf);
2843 res = stun_usage_ice_conncheck_create_reply (&agent->stun_agent, &req,
2844 &msg, rbuf, &rbuf_len, (struct sockaddr *) &sockaddr, sizeof (sockaddr),
2845 &control, agent->tie_breaker,
2846 agent_to_ice_compatibility (agent));
2848 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2849 g_free (req.key);
2852 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
2853 priv_check_for_role_conflict (agent, control);
2855 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2856 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2857 /* case 1: valid incoming request, send a reply/error */
2858 bool use_candidate =
2859 stun_usage_ice_conncheck_use_candidate (&req);
2860 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
2862 if (agent->controlling_mode ||
2863 agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2864 agent->compatibility == NICE_COMPATIBILITY_MSN)
2865 use_candidate = TRUE;
2867 if (stream->initial_binding_request_received != TRUE)
2868 agent_signal_initial_binding_request_received (agent, stream);
2870 if (component->remote_candidates && remote_candidate == NULL) {
2871 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2872 "peer-reflexive candidate.", agent);
2873 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
2874 agent, stream, component, priority, from, socket,
2875 local_candidate,
2876 remote_candidate2 ? remote_candidate2 : remote_candidate);
2877 if(remote_candidate)
2878 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
2881 priv_reply_to_conn_check (agent, stream, component, remote_candidate,
2882 from, socket, rbuf_len, rbuf, use_candidate);
2884 if (component->remote_candidates == NULL) {
2885 /* case: We've got a valid binding request to a local candidate
2886 * but we do not yet know remote credentials nor
2887 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2888 * immediately but postpone all other processing until
2889 * we get information about the remote candidates */
2891 /* step: send a reply immediately but postpone other processing */
2892 priv_store_pending_check (agent, component, from, socket,
2893 username, username_len, priority, use_candidate);
2895 } else {
2896 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2897 agent, strerror(errno));
2898 return FALSE;
2900 } else {
2901 /* case 2: not a new request, might be a reply... */
2902 gboolean trans_found = FALSE;
2904 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2906 /* step: let's try to match the response to an existing check context */
2907 if (trans_found != TRUE)
2908 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
2909 component, socket, from, local_candidate, remote_candidate, &req);
2911 /* step: let's try to match the response to an existing discovery */
2912 if (trans_found != TRUE)
2913 trans_found = priv_map_reply_to_discovery_request (agent, &req);
2915 /* step: let's try to match the response to an existing turn allocate */
2916 if (trans_found != TRUE)
2917 trans_found = priv_map_reply_to_relay_request (agent, &req);
2919 /* step: let's try to match the response to an existing turn refresh */
2920 if (trans_found != TRUE)
2921 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
2923 /* step: let's try to match the response to an existing keepalive conncheck */
2924 if (trans_found != TRUE)
2925 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
2926 &req);
2928 if (trans_found != TRUE)
2929 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2930 "probably a keepalive.", agent);
2933 return TRUE;