Version 0.0.10
[sipe-libnice.git] / agent / conncheck.c
blobc68650249ff7cdccbeca487d8d089a9602cb9e74
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006, 2007 Collabora Ltd.
5 * Contact: Dafydd Harries
6 * (C) 2006, 2007 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
17 * License.
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
24 * Contributors:
25 * Kai Vehmanen, Nokia
26 * Dafydd Harries, Collabora Ltd.
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
40 * @file conncheck.c
41 * @brief ICE connectivity checks
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif
48 #include <errno.h>
49 #include <string.h>
51 #include <glib.h>
53 #include "debug.h"
55 #include "agent.h"
56 #include "agent-priv.h"
57 #include "conncheck.h"
58 #include "discovery.h"
59 #include "stun/usages/ice.h"
60 #include "stun/usages/bind.h"
61 #include "stun/usages/turn.h"
63 static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream);
64 static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component);
65 static guint priv_prune_pending_checks (Stream *stream, guint component_id);
66 static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate);
67 static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand);
68 static size_t priv_create_username (NiceAgent *agent, Stream *stream,
69 guint component_id, NiceCandidate *remote, NiceCandidate *local,
70 uint8_t *dest, guint dest_len, gboolean inbound);
71 static size_t priv_get_password (NiceAgent *agent, Stream *stream,
72 NiceCandidate *remote, uint8_t **password);
74 static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
76 return (now->tv_sec == timer->tv_sec) ?
77 now->tv_usec >= timer->tv_usec :
78 now->tv_sec >= timer->tv_sec;
82 * Finds the next connectivity check in WAITING state.
84 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
86 GSList *i;
88 /* note: list is sorted in priority order to first waiting check has
89 * the highest priority */
91 for (i = conn_check_list; i ; i = i->next) {
92 CandidateCheckPair *p = i->data;
93 if (p->state == NICE_CHECK_WAITING)
94 return p;
97 return NULL;
101 * Initiates a new connectivity check for a ICE candidate pair.
103 * @return TRUE on success, FALSE on error
105 static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
107 /* XXX: from ID-16 onwards, the checks should not be sent
108 * immediately, but be put into the "triggered queue",
109 * see "7.2.1.4 Triggered Checks"
111 g_get_current_time (&pair->next_tick);
112 g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
113 pair->state = NICE_CHECK_IN_PROGRESS;
114 nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
115 conn_check_send (agent, pair);
116 return TRUE;
120 * Unfreezes the next connectivity check in the list. Follows the
121 * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
122 * (ID-19), with some exceptions (see comments in code).
124 * See also sect 7.1.2.2.3 (Updating Pair States), and
125 * priv_conn_check_unfreeze_related().
127 * @return TRUE on success, and FALSE if no frozen candidates were found.
129 static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
131 CandidateCheckPair *pair = NULL;
132 GSList *i, *j;
134 /* XXX: the unfreezing is implemented a bit differently than in the
135 * current ICE spec, but should still be interoperate:
136 * - checks are not grouped by foundation
137 * - one frozen check is unfrozen (lowest component-id, highest
138 * priority)
141 for (i = agent->streams; i; i = i->next) {
142 Stream *stream = i->data;
143 guint64 max_frozen_priority = 0;
146 for (j = stream->conncheck_list; j ; j = j->next) {
147 CandidateCheckPair *p = j->data;
149 /* XXX: the prio check could be removed as the pairs are sorted
150 * already */
152 if (p->state == NICE_CHECK_FROZEN) {
153 if (p->priority > max_frozen_priority) {
154 max_frozen_priority = p->priority;
155 pair = p;
160 if (pair)
161 break;
164 if (pair) {
165 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation);
166 pair->state = NICE_CHECK_WAITING;
167 nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
168 return TRUE;
171 return FALSE;
175 * Unfreezes the next next connectivity check in the list after
176 * check 'success_check' has succesfully completed.
178 * See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-19).
180 * @param agent context
181 * @param ok_check a connectivity check that has just completed
183 * @return TRUE on success, and FALSE if no frozen candidates were found.
185 static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check)
187 GSList *i, *j;
188 guint unfrozen = 0;
190 g_assert (ok_check);
191 g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
192 g_assert (stream);
193 g_assert (stream->id == ok_check->stream_id);
195 /* step: perform the step (1) of 'Updating Pair States' */
196 for (i = stream->conncheck_list; i ; i = i->next) {
197 CandidateCheckPair *p = i->data;
199 if (p->stream_id == ok_check->stream_id) {
200 if (p->state == NICE_CHECK_FROZEN &&
201 strcmp (p->foundation, ok_check->foundation) == 0) {
202 nice_debug ("Agent %p : Unfreezing check %p (after succesful check %p).", agent, p, ok_check);
203 p->state = NICE_CHECK_WAITING;
204 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
205 ++unfrozen;
210 /* step: perform the step (2) of 'Updating Pair States' */
211 stream = agent_find_stream (agent, ok_check->stream_id);
212 if (stream_all_components_ready (stream)) {
213 /* step: unfreeze checks from other streams */
214 for (i = agent->streams; i ; i = i->next) {
215 Stream *s = i->data;
216 for (j = stream->conncheck_list; j ; j = j->next) {
217 CandidateCheckPair *p = j->data;
219 if (p->stream_id == s->id &&
220 p->stream_id != ok_check->stream_id) {
221 if (p->state == NICE_CHECK_FROZEN &&
222 strcmp (p->foundation, ok_check->foundation) == 0) {
223 nice_debug ("Agent %p : Unfreezing check %p from stream %u (after succesful check %p).", agent, p, s->id, ok_check);
224 p->state = NICE_CHECK_WAITING;
225 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
226 ++unfrozen;
231 /* note: only unfreeze check from one stream at a time */
232 if (unfrozen)
233 break;
237 if (unfrozen == 0)
238 priv_conn_check_unfreeze_next (agent);
242 * Helper function for connectivity check timer callback that
243 * runs through the stream specific part of the state machine.
245 * @param schedule if TRUE, schedule a new check
247 * @return will return FALSE when no more pending timers.
249 static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now)
251 gboolean keep_timer_going = FALSE;
252 guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
253 s_nominated = 0, s_waiting_for_nomination = 0;
254 guint frozen = 0, waiting = 0;
255 GSList *i, *k;
257 for (i = stream->conncheck_list; i ; i = i->next) {
258 CandidateCheckPair *p = i->data;
260 if (p->state == NICE_CHECK_IN_PROGRESS) {
261 if (p->stun_message.buffer == NULL) {
262 nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent);
263 p->state = NICE_CHECK_FAILED;
264 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
265 } else if (priv_timer_expired (&p->next_tick, now)) {
266 switch (stun_timer_refresh (&p->timer)) {
267 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
269 /* case: error, abort processing */
270 StunTransactionId id;
272 nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p);
273 p->state = NICE_CHECK_FAILED;
274 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
276 stun_message_id (&p->stun_message, id);
277 stun_agent_forget_transaction (&agent->stun_agent, id);
279 p->stun_message.buffer = NULL;
280 p->stun_message.buffer_len = 0;
283 break;
285 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
287 /* case: not ready, so schedule a new timeout */
288 unsigned int timeout = stun_timer_remainder (&p->timer);
289 nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
290 agent, timeout);
292 nice_socket_send (p->local->sockptr, &p->remote->addr,
293 stun_message_length (&p->stun_message),
294 (gchar *)p->stun_buffer);
297 /* note: convert from milli to microseconds for g_time_val_add() */
298 p->next_tick = *now;
299 g_time_val_add (&p->next_tick, timeout * 1000);
301 keep_timer_going = TRUE;
302 break;
304 case STUN_USAGE_TIMER_RETURN_SUCCESS:
306 unsigned int timeout = stun_timer_remainder (&p->timer);
308 /* note: convert from milli to microseconds for g_time_val_add() */
309 p->next_tick = *now;
310 g_time_val_add (&p->next_tick, timeout * 1000);
312 keep_timer_going = TRUE;
313 break;
319 if (p->state == NICE_CHECK_FROZEN)
320 ++frozen;
321 else if (p->state == NICE_CHECK_IN_PROGRESS)
322 ++s_inprogress;
323 else if (p->state == NICE_CHECK_WAITING)
324 ++waiting;
325 else if (p->state == NICE_CHECK_SUCCEEDED)
326 ++s_succeeded;
327 else if (p->state == NICE_CHECK_DISCOVERED)
328 ++s_discovered;
330 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
331 && p->nominated)
332 ++s_nominated;
333 else if ((p->state == NICE_CHECK_SUCCEEDED ||
334 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
335 ++s_waiting_for_nomination;
338 /* note: keep the timer going as long as there is work to be done */
339 if (s_inprogress)
340 keep_timer_going = TRUE;
342 /* note: if some components have established connectivity,
343 * but yet no nominated pair, keep timer going */
344 if (s_nominated < stream->n_components &&
345 s_waiting_for_nomination) {
346 keep_timer_going = TRUE;
347 if (agent->controlling_mode) {
348 guint n;
349 for (n = 0; n < stream->n_components; n++) {
350 for (k = stream->conncheck_list; k ; k = k->next) {
351 CandidateCheckPair *p = k->data;
352 /* note: highest priority item selected (list always sorted) */
353 if (p->state == NICE_CHECK_SUCCEEDED ||
354 p->state == NICE_CHECK_DISCOVERED) {
355 nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
356 p->nominated = TRUE;
357 priv_conn_check_initiate (agent, p);
358 break; /* move to the next component */
365 static int tick_counter = 0;
366 if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
367 nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
368 "%u waiting, %u succeeded, %u discovered, %u nominated, "
369 "%u waiting-for-nom.", agent,
370 tick_counter, frozen, s_inprogress, waiting, s_succeeded,
371 s_discovered, s_nominated, s_waiting_for_nomination);
374 return keep_timer_going;
380 * Timer callback that handles initiating and managing connectivity
381 * checks (paced by the Ta timer).
383 * This function is designed for the g_timeout_add() interface.
385 * @return will return FALSE when no more pending timers.
387 static gboolean priv_conn_check_tick_unlocked (gpointer pointer)
389 CandidateCheckPair *pair = NULL;
390 NiceAgent *agent = pointer;
391 gboolean keep_timer_going = FALSE;
392 GSList *i, *j;
393 GTimeVal now;
395 /* step: process ongoing STUN transactions */
396 g_get_current_time (&now);
398 /* step: find the highest priority waiting check and send it */
399 for (i = agent->streams; i ; i = i->next) {
400 Stream *stream = i->data;
402 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
403 if (pair)
404 break;
407 if (pair) {
408 priv_conn_check_initiate (agent, pair);
409 keep_timer_going = TRUE;
410 } else {
411 keep_timer_going = priv_conn_check_unfreeze_next (agent);
414 for (j = agent->streams; j; j = j->next) {
415 Stream *stream = j->data;
416 gboolean res =
417 priv_conn_check_tick_stream (stream, agent, &now);
418 if (res)
419 keep_timer_going = res;
422 /* step: stop timer if no work left */
423 if (keep_timer_going != TRUE) {
424 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
425 for (i = agent->streams; i; i = i->next) {
426 Stream *stream = i->data;
427 priv_update_check_list_failed_components (agent, stream);
428 for (j = stream->components; j; j = j->next) {
429 Component *component = j->data;
430 priv_update_check_list_state_for_ready (agent, stream, component);
432 stream->conncheck_state = NICE_CHECKLIST_COMPLETED;
435 /* Stopping the timer so destroy the source.. this will allow
436 the timer to be reset if we get a set_remote_candidates after this
437 point */
438 if (agent->conncheck_timer_source != NULL) {
439 g_source_destroy (agent->conncheck_timer_source);
440 g_source_unref (agent->conncheck_timer_source);
441 agent->conncheck_timer_source = NULL;
444 /* XXX: what to signal, is all processing now really done? */
445 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
448 return keep_timer_going;
451 static gboolean priv_conn_check_tick (gpointer pointer)
453 gboolean ret;
455 agent_lock();
456 if (g_source_is_destroyed (g_main_current_source ())) {
457 nice_debug ("Source was destroyed. "
458 "Avoided race condition in priv_conn_check_tick");
459 agent_unlock ();
460 return FALSE;
462 ret = priv_conn_check_tick_unlocked (pointer);
463 agent_unlock();
465 return ret;
468 static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer)
470 CandidatePair *pair = (CandidatePair *) pointer;
472 agent_lock();
474 /* A race condition might happen where the mutex above waits for the lock
475 * and in the meantime another thread destroys the source.
476 * In that case, we don't need to run our retransmission tick since it should
477 * have been cancelled */
478 if (g_source_is_destroyed (g_main_current_source ())) {
479 nice_debug ("Source was destroyed. "
480 "Avoided race condition in priv_conn_keepalive_retransmissions_tick");
481 agent_unlock ();
482 return FALSE;
485 g_source_destroy (pair->keepalive.tick_source);
486 g_source_unref (pair->keepalive.tick_source);
487 pair->keepalive.tick_source = NULL;
489 switch (stun_timer_refresh (&pair->keepalive.timer)) {
490 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
492 /* Time out */
493 StunTransactionId id;
496 stun_message_id (&pair->keepalive.stun_message, id);
497 stun_agent_forget_transaction (&pair->keepalive.agent->stun_agent, id);
499 if (pair->keepalive.agent->media_after_tick) {
500 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
501 "but media was received. Suspecting keepalive lost because of "
502 "network bottleneck", pair->keepalive.agent);
504 if (pair->keepalive.tick_source) {
505 g_source_destroy (pair->keepalive.tick_source);
506 g_source_unref (pair->keepalive.tick_source);
507 pair->keepalive.tick_source = NULL;
509 pair->keepalive.stun_message.buffer = NULL;
511 } else {
512 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
513 "peer probably lost connection", pair->keepalive.agent);
514 agent_signal_component_state_change (pair->keepalive.agent,
515 pair->keepalive.stream_id, pair->keepalive.component_id,
516 NICE_COMPONENT_STATE_FAILED);
518 break;
520 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
521 /* Retransmit */
522 nice_socket_send (pair->local->sockptr, &pair->remote->addr,
523 stun_message_length (&pair->keepalive.stun_message),
524 (gchar *)pair->keepalive.stun_buffer);
526 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
527 pair->keepalive.agent);
528 pair->keepalive.tick_source =
529 agent_timeout_add_with_context (pair->keepalive.agent,
530 stun_timer_remainder (&pair->keepalive.timer),
531 priv_conn_keepalive_retransmissions_tick, pair);
532 break;
533 case STUN_USAGE_TIMER_RETURN_SUCCESS:
534 pair->keepalive.tick_source =
535 agent_timeout_add_with_context (pair->keepalive.agent,
536 stun_timer_remainder (&pair->keepalive.timer),
537 priv_conn_keepalive_retransmissions_tick, pair);
538 break;
542 agent_unlock ();
543 return FALSE;
548 * Timer callback that handles initiating and managing connectivity
549 * checks (paced by the Ta timer).
551 * This function is designed for the g_timeout_add() interface.
553 * @return will return FALSE when no more pending timers.
555 static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
557 GSList *i, *j, *k;
558 int errors = 0;
559 gboolean ret = FALSE;
560 size_t buf_len = 0;
562 /* case 1: session established and media flowing
563 * (ref ICE sect 10 "Keepalives" ID-19) */
564 for (i = agent->streams; i; i = i->next) {
566 Stream *stream = i->data;
567 for (j = stream->components; j; j = j->next) {
568 Component *component = j->data;
569 if (component->selected_pair.local != NULL) {
570 CandidatePair *p = &component->selected_pair;
571 struct sockaddr sockaddr;
573 memset (&sockaddr, 0, sizeof (sockaddr));
574 nice_address_copy_to_sockaddr (&p->remote->addr, &sockaddr);
576 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
577 guint32 priority = nice_candidate_ice_priority_full (
578 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 1,
579 p->local->component_id);
580 uint8_t uname[NICE_STREAM_MAX_UNAME];
581 size_t uname_len =
582 priv_create_username (agent, agent_find_stream (agent, stream->id),
583 component->id, p->remote, p->local, uname, sizeof (uname),
584 FALSE);
585 uint8_t *password = NULL;
586 size_t password_len = priv_get_password (agent,
587 agent_find_stream (agent, stream->id), p->remote, &password);
588 gchar tmpbuf[INET6_ADDRSTRLEN];
590 nice_address_to_string (&p->remote->addr, tmpbuf);
591 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
592 "socket=%u (c-id:%u), username='%s' (%d), "
593 "password='%s' (%d), priority=%u.", agent,
594 tmpbuf, ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
595 p->local->sockptr->fileno, component->id,
596 uname, uname_len, password, password_len, priority);
598 if (uname_len > 0) {
599 buf_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
600 &p->keepalive.stun_message, p->keepalive.stun_buffer,
601 sizeof(p->keepalive.stun_buffer),
602 uname, uname_len, password, password_len,
603 agent->controlling_mode, agent->controlling_mode, priority,
604 agent->tie_breaker,
605 agent_to_ice_compatibility (agent));
607 nice_debug ("Agent %p: conncheck created %d - %p",
608 agent, buf_len, p->keepalive.stun_message.buffer);
610 if (buf_len > 0) {
611 stun_timer_start (&p->keepalive.timer);
613 agent->media_after_tick = FALSE;
615 /* send the conncheck */
616 nice_socket_send (p->local->sockptr, &p->remote->addr,
617 buf_len, (gchar *)p->keepalive.stun_buffer);
619 if (p->keepalive.tick_source != NULL) {
620 g_source_destroy (p->keepalive.tick_source);
621 g_source_unref (p->keepalive.tick_source);
622 p->keepalive.tick_source = NULL;
625 p->keepalive.stream_id = stream->id;
626 p->keepalive.component_id = component->id;
627 p->keepalive.agent = agent;
629 p->keepalive.tick_source =
630 agent_timeout_add_with_context (p->keepalive.agent,
631 stun_timer_remainder (&p->keepalive.timer),
632 priv_conn_keepalive_retransmissions_tick, p);
633 } else {
634 ++errors;
637 } else {
638 buf_len = stun_usage_bind_keepalive (&agent->stun_agent,
639 &p->keepalive.stun_message, p->keepalive.stun_buffer,
640 sizeof(p->keepalive.stun_buffer));
642 if (buf_len > 0) {
643 nice_socket_send (p->local->sockptr, &p->remote->addr, buf_len,
644 (gchar *)p->keepalive.stun_buffer);
646 nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.",
647 agent, p, (int) buf_len);
648 } else {
649 ++errors;
656 /* case 2: connectivity establishment ongoing
657 * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */
658 for (i = agent->streams; i; i = i->next) {
659 Stream *stream = i->data;
660 for (j = stream->components; j; j = j->next) {
661 Component *component = j->data;
662 if (component->state < NICE_COMPONENT_STATE_READY &&
663 agent->stun_server_ip) {
664 NiceAddress stun_server;
665 if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
666 StunAgent stun_agent;
667 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
668 StunMessage stun_message;
669 size_t buffer_len = 0;
671 nice_address_set_port (&stun_server, agent->stun_server_port);
673 stun_agent_init (&stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
674 STUN_COMPATIBILITY_RFC3489, 0);
676 buffer_len = stun_usage_bind_create (&stun_agent,
677 &stun_message, stun_buffer, sizeof(stun_buffer));
679 for (k = component->local_candidates; k; k = k->next) {
680 NiceCandidate *candidate = (NiceCandidate *) k->data;
681 if (candidate->type == NICE_CANDIDATE_TYPE_HOST) {
682 /* send the conncheck */
683 nice_debug ("Agent %p : resending STUN on %s to keep the "
684 "candidate alive.", agent, candidate->foundation);
685 nice_socket_send (candidate->sockptr, &stun_server,
686 buffer_len, (gchar *)stun_buffer);
694 if (errors) {
695 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
696 goto done;
699 ret = TRUE;
701 done:
702 return ret;
705 static gboolean priv_conn_keepalive_tick (gpointer pointer)
707 NiceAgent *agent = pointer;
708 gboolean ret;
710 agent_lock();
711 if (g_source_is_destroyed (g_main_current_source ())) {
712 nice_debug ("Source was destroyed. "
713 "Avoided race condition in priv_conn_keepalive_tick");
714 agent_unlock ();
715 return FALSE;
718 ret = priv_conn_keepalive_tick_unlocked (agent);
719 if (ret == FALSE) {
720 if (agent->keepalive_timer_source) {
721 g_source_destroy (agent->keepalive_timer_source);
722 g_source_unref (agent->keepalive_timer_source);
723 agent->keepalive_timer_source = NULL;
726 agent_unlock();
727 return ret;
731 static gboolean priv_turn_allocate_refresh_retransmissions_tick (gpointer pointer)
733 CandidateRefresh *cand = (CandidateRefresh *) pointer;
735 agent_lock();
737 /* A race condition might happen where the mutex above waits for the lock
738 * and in the meantime another thread destroys the source.
739 * In that case, we don't need to run our retransmission tick since it should
740 * have been cancelled */
741 if (g_source_is_destroyed (g_main_current_source ())) {
742 nice_debug ("Source was destroyed. "
743 "Avoided race condition in priv_turn_allocate_refresh_retransmissions_tick");
744 agent_unlock ();
745 return FALSE;
749 g_source_destroy (cand->tick_source);
750 g_source_unref (cand->tick_source);
751 cand->tick_source = NULL;
753 switch (stun_timer_refresh (&cand->timer)) {
754 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
756 /* Time out */
757 StunTransactionId id;
759 stun_message_id (&cand->stun_message, id);
760 stun_agent_forget_transaction (&cand->stun_agent, id);
762 refresh_cancel (cand);
763 break;
765 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
766 /* Retransmit */
767 nice_socket_send (cand->nicesock, &cand->server,
768 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
770 cand->tick_source = agent_timeout_add_with_context (cand->agent,
771 stun_timer_remainder (&cand->timer),
772 priv_turn_allocate_refresh_retransmissions_tick, cand);
773 break;
774 case STUN_USAGE_TIMER_RETURN_SUCCESS:
775 cand->tick_source = agent_timeout_add_with_context (cand->agent,
776 stun_timer_remainder (&cand->timer),
777 priv_turn_allocate_refresh_retransmissions_tick, cand);
778 break;
782 agent_unlock ();
783 return FALSE;
786 static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand)
788 uint8_t *username;
789 size_t username_len;
790 uint8_t *password;
791 size_t password_len;
792 size_t buffer_len = 0;
794 username = (uint8_t *)cand->turn->username;
795 username_len = (size_t) strlen (cand->turn->username);
796 password = (uint8_t *)cand->turn->password;
797 password_len = (size_t) strlen (cand->turn->password);
799 if (agent_to_turn_compatibility (cand->agent) ==
800 STUN_USAGE_TURN_COMPATIBILITY_MSN) {
801 username = g_base64_decode ((gchar *)username, &username_len);
802 password = g_base64_decode ((gchar *)password, &password_len);
805 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
806 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
807 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, -1,
808 username, username_len,
809 password, password_len,
810 agent_to_turn_compatibility (cand->agent));
812 if (agent_to_turn_compatibility (cand->agent) ==
813 STUN_USAGE_TURN_COMPATIBILITY_MSN) {
814 g_free (cand->msn_turn_username);
815 g_free (cand->msn_turn_password);
816 cand->msn_turn_username = username;
817 cand->msn_turn_password = password;
820 nice_debug ("Agent %p : Sending allocate Refresh %d", cand->agent, buffer_len);
822 if (cand->tick_source != NULL) {
823 g_source_destroy (cand->tick_source);
824 g_source_unref (cand->tick_source);
825 cand->tick_source = NULL;
828 if (buffer_len > 0) {
829 stun_timer_start (&cand->timer);
831 /* send the refresh */
832 nice_socket_send (cand->nicesock, &cand->server,
833 buffer_len, (gchar *)cand->stun_buffer);
835 cand->tick_source = agent_timeout_add_with_context (cand->agent,
836 stun_timer_remainder (&cand->timer),
837 priv_turn_allocate_refresh_retransmissions_tick, cand);
844 * Timer callback that handles refreshing TURN allocations
846 * This function is designed for the g_timeout_add() interface.
848 * @return will return FALSE when no more pending timers.
850 static gboolean priv_turn_allocate_refresh_tick (gpointer pointer)
852 CandidateRefresh *cand = (CandidateRefresh *) pointer;
854 agent_lock();
855 if (g_source_is_destroyed (g_main_current_source ())) {
856 nice_debug ("Source was destroyed. "
857 "Avoided race condition in priv_turn_allocate_refresh_tick");
858 agent_unlock ();
859 return FALSE;
862 priv_turn_allocate_refresh_tick_unlocked (cand);
863 agent_unlock ();
865 return FALSE;
870 * Initiates the next pending connectivity check.
872 * @return TRUE if a pending check was scheduled
874 gboolean conn_check_schedule_next (NiceAgent *agent)
876 gboolean res = priv_conn_check_unfreeze_next (agent);
877 nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res);
879 if (agent->discovery_unsched_items > 0)
880 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
882 if (res == TRUE) {
883 /* step: call once imediately */
884 res = priv_conn_check_tick_unlocked ((gpointer) agent);
885 nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res);
887 /* step: schedule timer if not running yet */
888 if (res && agent->conncheck_timer_source == NULL) {
889 agent->conncheck_timer_source = agent_timeout_add_with_context (agent, agent->timer_ta, priv_conn_check_tick, agent);
892 /* step: also start the keepalive timer */
893 if (agent->keepalive_timer_source == NULL) {
894 agent->keepalive_timer_source = agent_timeout_add_with_context (agent, NICE_AGENT_TIMER_TR_DEFAULT, priv_conn_keepalive_tick, agent);
899 nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res);
900 return res;
904 * Compares two connectivity check items. Checkpairs are sorted
905 * in descending priority order, with highest priority item at
906 * the start of the list.
908 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
910 if (a->priority > b->priority)
911 return -1;
912 else if (a->priority < b->priority)
913 return 1;
914 return 0;
918 * Preprocesses a new connectivity check by going through list
919 * of a any stored early incoming connectivity checks from
920 * the remote peer. If a matching incoming check has been already
921 * received, update the state of the new outgoing check 'pair'.
923 * @param agent context pointer
924 * @param stream which stream (of the agent)
925 * @param component pointer to component object to which 'pair'has been added
926 * @param pair newly added connectivity check
928 static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *pair)
930 GSList *i;
931 for (i = component->incoming_checks; i; i = i->next) {
932 IncomingCheck *icheck = i->data;
933 if (nice_address_equal (&icheck->from, &pair->remote->addr) &&
934 icheck->local_socket == pair->local->sockptr) {
935 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);
936 if (icheck->use_candidate)
937 priv_mark_pair_nominated (agent, stream, component, pair->remote);
938 priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate);
944 * Handle any processing steps for connectivity checks after
945 * remote candidates have been set. This function handles
946 * the special case where answerer has sent us connectivity
947 * checks before the answer (containing candidate information),
948 * reaches us. The special case is documented in sect 7.2
949 * if ICE spec (ID-19).
951 void conn_check_remote_candidates_set(NiceAgent *agent)
953 GSList *i, *j, *k, *l, *m, *n;
954 for (i = agent->streams; i ; i = i->next) {
955 Stream *stream = i->data;
956 for (j = stream->conncheck_list; j ; j = j->next) {
957 CandidateCheckPair *pair = j->data;
958 Component *component = stream_find_component_by_id (stream, pair->component_id);
959 gboolean match = FALSE;
961 /* performn delayed processing of spec steps section 7.2.1.4,
962 and section 7.2.1.5 */
963 priv_preprocess_conn_check_pending_data (agent, stream, component, pair);
965 for (k = component->incoming_checks; k; k = k->next) {
966 IncomingCheck *icheck = k->data;
967 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
968 * be handled separately */
969 for (l = component->remote_candidates; l; l = l->next) {
970 NiceCandidate *cand = l->data;
971 if (nice_address_equal (&icheck->from, &cand->addr)) {
972 match = TRUE;
973 break;
976 if (match != TRUE) {
977 /* note: we have gotten an incoming connectivity check from
978 * an address that is not a known remote candidate */
980 NiceCandidate *local_candidate = NULL;
981 NiceCandidate *remote_candidate = NULL;
983 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
984 agent->compatibility == NICE_COMPATIBILITY_MSN) {
985 /* We need to find which local candidate was used */
986 uint8_t uname[NICE_STREAM_MAX_UNAME];
987 guint uname_len;
989 nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
990 "stored pending check", agent);
992 for (m = component->remote_candidates;
993 m != NULL && remote_candidate == NULL; m = m->next) {
994 for (n = component->local_candidates; n; n = n->next) {
995 NiceCandidate *rcand = m->data;
996 NiceCandidate *lcand = n->data;
998 uname_len = priv_create_username (agent, stream,
999 component->id, rcand, lcand,
1000 uname, sizeof (uname), TRUE);
1002 stun_debug ("pending check, comparing username '");
1003 stun_debug_bytes (icheck->username,
1004 icheck->username? icheck->username_len : 0);
1005 stun_debug ("' (%d) with '", icheck->username_len);
1006 stun_debug_bytes (uname, uname_len);
1007 stun_debug ("' (%d) : %d\n",
1008 uname_len, icheck->username &&
1009 uname_len == icheck->username_len &&
1010 memcmp (icheck->username, uname, uname_len) == 0);
1012 if (icheck->username &&
1013 uname_len == icheck->username_len &&
1014 memcmp (uname, icheck->username, icheck->username_len) == 0) {
1015 local_candidate = lcand;
1016 remote_candidate = rcand;
1017 break;
1023 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
1024 local_candidate == NULL) {
1025 /* if we couldn't match the username, then the matching remote
1026 * candidate hasn't been received yet.. we must wait */
1027 nice_debug ("Agent %p : Username check failed. pending check has "
1028 "to wait to be processed", agent);
1029 } else {
1030 NiceCandidate *candidate =
1031 discovery_learn_remote_peer_reflexive_candidate (agent,
1032 stream,
1033 component,
1034 icheck->priority,
1035 &icheck->from,
1036 icheck->local_socket,
1037 local_candidate, remote_candidate);
1038 if (candidate) {
1039 conn_check_add_for_candidate (agent, stream->id, component, candidate);
1041 priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate);
1045 /* Once we process the pending checks, we should free them to avoid
1046 * reprocessing them again if a dribble-mode set_remote_candidates
1047 * is called */
1048 for (m = component->incoming_checks; m; m = m->next) {
1049 IncomingCheck *icheck = m->data;
1050 g_free (icheck->username);
1051 g_slice_free (IncomingCheck, icheck);
1053 g_slist_free (component->incoming_checks);
1054 component->incoming_checks = NULL;
1061 * Enforces the upper limit for connectivity checks as described
1062 * in ICE spec section 5.7.3 (ID-19). See also
1063 * conn_check_add_for_candidate().
1065 static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
1067 guint list_len = g_slist_length (conncheck_list);
1068 guint c = 0;
1069 GSList *result = conncheck_list;
1071 if (list_len > upper_limit) {
1072 nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
1073 "Maximum connchecks allowed : %d", list_len, upper_limit);
1074 c = list_len - upper_limit;
1075 if (c == list_len) {
1076 /* case: delete whole list */
1077 g_slist_foreach (conncheck_list, conn_check_free_item, NULL);
1078 g_slist_free (conncheck_list),
1079 result = NULL;
1081 else {
1082 /* case: remove 'c' items from list end (lowest priority) */
1083 GSList *i, *tmp;
1085 g_assert (c > 0);
1086 i = g_slist_nth (conncheck_list, c - 1);
1088 tmp = i->next;
1089 i->next = NULL;
1091 if (tmp) {
1092 /* delete the rest of the connectivity check list */
1093 g_slist_foreach (tmp, conn_check_free_item, NULL);
1094 g_slist_free (tmp);
1099 return result;
1103 * Changes the selected pair for the component if 'pair' is nominated
1104 * and has higher priority than the currently selected pair. See
1105 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
1107 static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
1109 g_assert (component);
1110 g_assert (pair);
1111 if (pair->priority > component->selected_pair.priority) {
1112 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1113 "(prio:%" G_GUINT64_FORMAT ").", agent, component->id, pair->local->foundation,
1114 pair->remote->foundation, pair->priority);
1116 if (component->selected_pair.keepalive.tick_source != NULL) {
1117 g_source_destroy (component->selected_pair.keepalive.tick_source);
1118 g_source_unref (component->selected_pair.keepalive.tick_source);
1119 component->selected_pair.keepalive.tick_source = NULL;
1122 memset (&component->selected_pair, 0, sizeof(CandidatePair));
1123 component->selected_pair.local = pair->local;
1124 component->selected_pair.remote = pair->remote;
1125 component->selected_pair.priority = pair->priority;
1127 priv_conn_keepalive_tick_unlocked (agent);
1129 agent_signal_new_selected_pair (agent, pair->stream_id, component->id, pair->local->foundation, pair->remote->foundation);
1133 return TRUE;
1137 * Updates the check list state.
1139 * Implements parts of the algorithm described in
1140 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
1141 * component, all checks have been completed and have
1142 * failed, mark that component's state to NICE_CHECK_FAILED.
1144 * Sends a component state changesignal via 'agent'.
1146 static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream)
1148 GSList *i;
1149 /* note: emitting a signal might cause the client
1150 * to remove the stream, thus the component count
1151 * must be fetched before entering the loop*/
1152 guint c, components = stream->n_components;
1154 /* note: iterate the conncheck list for each component separately */
1155 for (c = 0; c < components; c++) {
1156 Component *comp = NULL;
1157 agent_find_component (agent, stream->id, c+1, NULL, &comp);
1159 for (i = stream->conncheck_list; i; i = i->next) {
1160 CandidateCheckPair *p = i->data;
1162 if (p->stream_id == stream->id &&
1163 p->component_id == (c + 1)) {
1164 if (p->state != NICE_CHECK_FAILED)
1165 break;
1169 /* note: all checks have failed
1170 * Set the component to FAILED only if it actually had remote candidates
1171 * that failed.. */
1172 if (i == NULL && comp != NULL && comp->remote_candidates != NULL)
1173 agent_signal_component_state_change (agent,
1174 stream->id,
1175 (c + 1), /* component-id */
1176 NICE_COMPONENT_STATE_FAILED);
1181 * Updates the check list state for a stream component.
1183 * Implements the algorithm described in ICE sect 8.1.2
1184 * "Updating States" (ID-19) as it applies to checks of
1185 * a certain component. If there are any nominated pairs,
1186 * ICE processing may be concluded, and component state is
1187 * changed to READY.
1189 * Sends a component state changesignal via 'agent'.
1191 static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component)
1193 GSList *i;
1194 guint succeeded = 0, nominated = 0;
1196 g_assert (component);
1198 /* step: search for at least one nominated pair */
1199 for (i = stream->conncheck_list; i; i = i->next) {
1200 CandidateCheckPair *p = i->data;
1201 if (p->component_id == component->id) {
1202 if (p->state == NICE_CHECK_SUCCEEDED ||
1203 p->state == NICE_CHECK_DISCOVERED) {
1204 ++succeeded;
1205 if (p->nominated == TRUE) {
1206 ++nominated;
1212 if (nominated > 0) {
1213 /* Only go to READY if no checks are left in progress. If there are
1214 * any that are kept, then this function will be called again when the
1215 * conncheck tick timer finishes them all */
1216 if (priv_prune_pending_checks (stream, component->id) == 0) {
1217 agent_signal_component_state_change (agent, stream->id,
1218 component->id, NICE_COMPONENT_STATE_READY);
1221 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id);
1225 * The remote party has signalled that the candidate pair
1226 * described by 'component' and 'remotecand' is nominated
1227 * for use.
1229 static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand)
1231 GSList *i;
1233 g_assert (component);
1235 /* step: search for at least one nominated pair */
1236 for (i = stream->conncheck_list; i; i = i->next) {
1237 CandidateCheckPair *pair = i->data;
1238 /* XXX: hmm, how to figure out to which local candidate the
1239 * check was sent to? let's mark all matching pairs
1240 * as nominated instead */
1241 if (pair->remote == remotecand) {
1242 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
1243 pair->nominated = TRUE;
1244 if (pair->state == NICE_CHECK_SUCCEEDED ||
1245 pair->state == NICE_CHECK_DISCOVERED)
1246 priv_update_selected_pair (agent, component, pair);
1247 priv_update_check_list_state_for_ready (agent, stream, component);
1253 * Creates a new connectivity check pair and adds it to
1254 * the agent's list of checks.
1256 static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
1258 gboolean result = FALSE;
1259 Stream *stream = agent_find_stream (agent, stream_id);
1260 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1261 if (pair) {
1262 GSList *modified_list =
1263 g_slist_insert_sorted (stream->conncheck_list, pair, (GCompareFunc)conn_check_compare);
1264 if (modified_list) {
1265 /* step: allocation and addition succesful, do rest of the work */
1267 pair->agent = agent;
1268 pair->stream_id = stream_id;
1269 pair->component_id = component->id;;
1270 pair->local = local;
1271 pair->remote = remote;
1272 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
1274 pair->priority = agent_candidate_pair_priority (agent, local, remote);
1275 pair->state = initial_state;
1276 nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state);
1277 pair->nominated = use_candidate;
1278 pair->controlling = agent->controlling_mode;
1280 /* note: for the first added check */
1281 if (!stream->conncheck_list)
1282 stream->conncheck_state = NICE_CHECKLIST_RUNNING;
1283 stream->conncheck_list = modified_list;
1285 result = TRUE;
1286 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id);
1288 /* implement the hard upper limit for number of
1289 checks (see sect 5.7.3 ICE ID-19): */
1290 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19) {
1291 stream->conncheck_list =
1292 priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
1294 if (!stream->conncheck_list) {
1295 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1296 result = FALSE;
1299 else {
1300 /* memory alloc failed: list insert */
1301 conn_check_free_item (pair, NULL);
1302 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1305 else { /* memory alloc failed: new pair */
1306 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1309 return result;
1313 * Forms new candidate pairs by matching the new remote candidate
1314 * 'remote_cand' with all existing local candidates of 'component'.
1315 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1316 * Pairs" (ID-19).
1318 * @param agent context
1319 * @param component pointer to the component
1320 * @param remote remote candidate to match with
1322 * @return number of checks added, negative on fatal errors
1324 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
1326 GSList *i;
1327 int added = 0;
1329 for (i = component->local_candidates; i ; i = i->next) {
1331 NiceCandidate *local = i->data;
1333 /* note: match pairs only if transport and address family are the same */
1334 if (local->transport == remote->transport &&
1335 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
1337 gboolean result;
1339 /* note: do not create pairs where local candidate is
1340 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1341 if ((agent->compatibility == NICE_COMPATIBILITY_DRAFT19 ||
1342 agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1343 local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE)
1344 continue;
1346 result = priv_add_new_check_pair (agent, stream_id, component, local, remote, NICE_CHECK_FROZEN, FALSE);
1347 if (result) {
1348 ++added;
1349 if (component->state < NICE_COMPONENT_STATE_CONNECTED) {
1350 agent_signal_component_state_change (agent,
1351 stream_id,
1352 component->id,
1353 NICE_COMPONENT_STATE_CONNECTING);
1354 } else {
1355 agent_signal_component_state_change (agent,
1356 stream_id,
1357 component->id,
1358 NICE_COMPONENT_STATE_CONNECTED);
1361 else {
1362 added = -1;
1363 break;
1368 return added;
1372 * Frees the CandidateCheckPair structure pointer to
1373 * by 'user data'. Compatible with g_slist_foreach().
1375 void conn_check_free_item (gpointer data, gpointer user_data)
1377 CandidateCheckPair *pair = data;
1378 g_assert (user_data == NULL);
1379 pair->stun_message.buffer = NULL;
1380 pair->stun_message.buffer_len = 0;
1381 g_slice_free (CandidateCheckPair, pair);
1385 * Frees all resources of all connectivity checks.
1387 void conn_check_free (NiceAgent *agent)
1389 GSList *i;
1390 for (i = agent->streams; i; i = i->next) {
1391 Stream *stream = i->data;
1393 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
1394 if (stream->conncheck_list) {
1395 g_slist_foreach (stream->conncheck_list, conn_check_free_item, NULL);
1396 g_slist_free (stream->conncheck_list),
1397 stream->conncheck_list = NULL;
1398 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1402 if (agent->conncheck_timer_source != NULL) {
1403 g_source_destroy (agent->conncheck_timer_source);
1404 g_source_unref (agent->conncheck_timer_source);
1405 agent->conncheck_timer_source = NULL;
1410 * Prunes the list of connectivity checks for items related
1411 * to stream 'stream_id'.
1413 * @return TRUE on success, FALSE on a fatal error
1415 gboolean conn_check_prune_stream (NiceAgent *agent, Stream *stream)
1417 CandidateCheckPair *pair;
1418 GSList *i;
1420 for (i = stream->conncheck_list; i ; ) {
1421 GSList *next = i->next;
1422 pair = i->data;
1424 g_assert (pair->stream_id == stream->id);
1426 stream->conncheck_list =
1427 g_slist_remove (stream->conncheck_list, pair);
1428 conn_check_free_item (pair, NULL);
1429 i = next;
1430 if (!stream->conncheck_list)
1431 break;
1434 if (!stream->conncheck_list) {
1435 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1436 conn_check_free (agent);
1439 /* return FALSE if there was a memory allocation failure */
1440 if (stream->conncheck_list == NULL && i != NULL)
1441 return FALSE;
1443 return TRUE;
1447 * Fills 'dest' with a username string for use in an outbound connectivity
1448 * checks. No more than 'dest_len' characters (including terminating
1449 * NULL) is ever written to the 'dest'.
1451 static
1452 size_t priv_gen_username (NiceAgent *agent, guint component_id,
1453 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
1455 guint len = 0;
1456 gsize remote_len = strlen (remote);
1457 gsize local_len = strlen (local);
1459 if (remote_len > 0 && local_len > 0) {
1460 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
1461 dest_len >= remote_len + local_len + 1) {
1462 memcpy (dest, remote, remote_len);
1463 len += remote_len;
1464 memcpy (dest + len, ":", 1);
1465 len++;
1466 memcpy (dest + len, local, local_len);
1467 len += local_len;
1468 } else if (agent->compatibility == NICE_COMPATIBILITY_WLM2009 &&
1469 dest_len >= remote_len + local_len + 4 ) {
1470 memcpy (dest, remote, remote_len);
1471 len += remote_len;
1472 memcpy (dest + len, ":", 1);
1473 len++;
1474 memcpy (dest + len, local, local_len);
1475 len += local_len;
1476 if (len % 4 != 0) {
1477 memset (dest + len, 0, 4 - (len % 4));
1478 len += 4 - (len % 4);
1480 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
1481 dest_len >= remote_len + local_len) {
1482 memcpy (dest, remote, remote_len);
1483 len += remote_len;
1484 memcpy (dest + len, local, local_len);
1485 len += local_len;
1486 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1487 gchar component_str[10];
1488 guchar *local_decoded = NULL;
1489 guchar *remote_decoded = NULL;
1490 gsize local_decoded_len;
1491 gsize remote_decoded_len;
1492 gsize total_len;
1493 int padding;
1495 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
1496 local_decoded = g_base64_decode (local, &local_decoded_len);
1497 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
1499 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
1500 padding = 4 - (total_len % 4);
1502 if (dest_len >= total_len + padding) {
1503 guchar pad_char[1] = {0};
1504 int i;
1506 memcpy (dest, remote_decoded, remote_decoded_len);
1507 len += remote_decoded_len;
1508 memcpy (dest + len, ":", 1);
1509 len++;
1510 memcpy (dest + len, component_str, strlen (component_str));
1511 len += strlen (component_str);
1513 memcpy (dest + len, ":", 1);
1514 len++;
1516 memcpy (dest + len, local_decoded, local_decoded_len);
1517 len += local_decoded_len;
1518 memcpy (dest + len, ":", 1);
1519 len++;
1520 memcpy (dest + len, component_str, strlen (component_str));;
1521 len += strlen (component_str);
1523 for (i = 0; i < padding; i++) {
1524 memcpy (dest + len, pad_char, 1);
1525 len++;
1530 g_free (local_decoded);
1531 g_free (remote_decoded);
1535 return len;
1539 * Fills 'dest' with a username string for use in an outbound connectivity
1540 * checks. No more than 'dest_len' characters (including terminating
1541 * NULL) is ever written to the 'dest'.
1543 static
1544 size_t priv_create_username (NiceAgent *agent, Stream *stream,
1545 guint component_id, NiceCandidate *remote, NiceCandidate *local,
1546 uint8_t *dest, guint dest_len, gboolean inbound)
1548 gchar *local_username = NULL;
1549 gchar *remote_username = NULL;
1552 if (remote && remote->username) {
1553 remote_username = remote->username;
1556 if (local && local->username) {
1557 local_username = local->username;
1560 if (stream) {
1561 if (remote_username == NULL) {
1562 remote_username = stream->remote_ufrag;
1564 if (local_username == NULL) {
1565 local_username = stream->local_ufrag;
1569 if (local_username && remote_username) {
1570 if (inbound) {
1571 return priv_gen_username (agent, component_id,
1572 local_username, remote_username, dest, dest_len);
1573 } else {
1574 return priv_gen_username (agent, component_id,
1575 remote_username, local_username, dest, dest_len);
1579 return 0;
1583 * Returns a password string for use in an outbound connectivity
1584 * check.
1586 static
1587 size_t priv_get_password (NiceAgent *agent, Stream *stream,
1588 NiceCandidate *remote, uint8_t **password)
1590 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
1591 return 0;
1593 if (remote && remote->password) {
1594 *password = (uint8_t *)remote->password;
1595 return strlen (remote->password);
1598 if (stream) {
1599 *password = (uint8_t *)stream->remote_password;
1600 return strlen (stream->remote_password);
1603 return 0;
1607 * Sends a connectivity check over candidate pair 'pair'.
1609 * @return zero on success, non-zero on error
1611 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
1614 /* note: following information is supplied:
1615 * - username (for USERNAME attribute)
1616 * - password (for MESSAGE-INTEGRITY)
1617 * - priority (for PRIORITY)
1618 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1619 * - USE-CANDIDATE (if sent by the controlling agent)
1622 guint32 priority =
1623 nice_candidate_ice_priority_full (
1624 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE,
1626 pair->local->component_id);
1628 uint8_t uname[NICE_STREAM_MAX_UNAME];
1629 size_t uname_len =
1630 priv_create_username (agent, agent_find_stream (agent, pair->stream_id),
1631 pair->component_id, pair->remote, pair->local, uname, sizeof (uname), FALSE);
1632 uint8_t *password = NULL;
1633 size_t password_len = priv_get_password (agent,
1634 agent_find_stream (agent, pair->stream_id), pair->remote, &password);
1636 bool controlling = agent->controlling_mode;
1637 /* XXX: add API to support different nomination modes: */
1638 bool cand_use = controlling;
1639 size_t buffer_len;
1641 struct sockaddr sockaddr;
1642 unsigned int timeout;
1644 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1645 password = g_base64_decode ((gchar *) password, &password_len);
1648 memset (&sockaddr, 0, sizeof (sockaddr));
1650 nice_address_copy_to_sockaddr (&pair->remote->addr, &sockaddr);
1653 gchar tmpbuf[INET6_ADDRSTRLEN];
1654 nice_address_to_string (&pair->remote->addr, tmpbuf);
1655 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,
1656 tmpbuf,
1657 ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
1658 pair->local->sockptr->fileno,
1659 pair->foundation, pair->component_id,
1660 (unsigned long long)agent->tie_breaker,
1661 uname, uname_len, password, password_len, priority);
1665 if (cand_use)
1666 pair->nominated = controlling;
1668 if (uname_len > 0) {
1670 buffer_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
1671 &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer),
1672 uname, uname_len, password, password_len,
1673 cand_use, controlling, priority,
1674 agent->tie_breaker,
1675 agent_to_ice_compatibility (agent));
1677 nice_debug ("Agent %p: conncheck created %d - %p", agent, buffer_len, pair->stun_message.buffer);
1679 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1680 g_free (password);
1683 if (buffer_len > 0) {
1684 stun_timer_start (&pair->timer);
1686 /* send the conncheck */
1687 nice_socket_send (pair->local->sockptr, &pair->remote->addr,
1688 buffer_len, (gchar *)pair->stun_buffer);
1690 timeout = stun_timer_remainder (&pair->timer);
1691 /* note: convert from milli to microseconds for g_time_val_add() */
1692 g_get_current_time (&pair->next_tick);
1693 g_time_val_add (&pair->next_tick, timeout * 1000);
1694 } else {
1695 pair->stun_message.buffer = NULL;
1696 pair->stun_message.buffer_len = 0;
1697 return -1;
1701 return 0;
1705 * Implemented the pruning steps described in ICE sect 8.1.2
1706 * "Updating States" (ID-19) after a pair has been nominated.
1708 * @see priv_update_check_list_state_failed_components()
1710 static guint priv_prune_pending_checks (Stream *stream, guint component_id)
1712 GSList *i;
1713 guint64 highest_nominated_priority = 0;
1714 guint in_progress = 0;
1716 nice_debug ("Finding highest priority for component %d", component_id);
1718 for (i = stream->conncheck_list; i; i = i->next) {
1719 CandidateCheckPair *p = i->data;
1720 if (p->component_id == component_id &&
1721 (p->state == NICE_CHECK_SUCCEEDED ||
1722 p->state == NICE_CHECK_DISCOVERED) &&
1723 p->nominated == TRUE){
1724 if (p->priority > highest_nominated_priority) {
1725 highest_nominated_priority = p->priority;
1730 nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
1731 "is %" G_GUINT64_FORMAT, highest_nominated_priority);
1733 /* step: cancel all FROZEN and WAITING pairs for the component */
1734 for (i = stream->conncheck_list; i; i = i->next) {
1735 CandidateCheckPair *p = i->data;
1736 if (p->component_id == component_id) {
1737 if (p->state == NICE_CHECK_FROZEN ||
1738 p->state == NICE_CHECK_WAITING) {
1739 p->state = NICE_CHECK_CANCELLED;
1740 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1743 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1744 if (p->state == NICE_CHECK_IN_PROGRESS) {
1745 if (highest_nominated_priority != 0 &&
1746 p->priority < highest_nominated_priority) {
1747 p->stun_message.buffer = NULL;
1748 p->stun_message.buffer_len = 0;
1749 p->state = NICE_CHECK_CANCELLED;
1750 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1751 } else {
1752 /* We must keep the higher priority pairs running because if a udp
1753 * packet was lost, we might end up using a bad candidate */
1754 nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %"
1755 G_GUINT64_FORMAT " is higher than currently nominated pair %"
1756 G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority);
1757 in_progress++;
1763 return in_progress;
1767 * Schedules a triggered check after a succesfully inbound
1768 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1770 * @param agent self pointer
1771 * @param component the check is related to
1772 * @param local_socket socket from which the inbound check was received
1773 * @param remote_cand remote candidate from which the inbound check was sent
1774 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1776 static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate)
1778 GSList *i;
1779 gboolean result = FALSE;
1781 for (i = stream->conncheck_list; i ; i = i->next) {
1782 CandidateCheckPair *p = i->data;
1783 if (p->component_id == component->id &&
1784 p->remote == remote_cand &&
1785 p->local->sockptr == local_socket) {
1787 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p);
1789 if (p->state == NICE_CHECK_WAITING ||
1790 p->state == NICE_CHECK_FROZEN)
1791 priv_conn_check_initiate (agent, p);
1792 else if (p->state == NICE_CHECK_IN_PROGRESS) {
1793 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1794 * we should cancel the existing one, and send a new one...? :P */
1795 nice_debug ("Agent %p : Skipping triggered check, already in progress..", agent);
1797 else if (p->state == NICE_CHECK_SUCCEEDED ||
1798 p->state == NICE_CHECK_DISCOVERED) {
1799 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent);
1800 /* note: this is a bit unsure corner-case -- let's do the
1801 same state update as for processing responses to our own checks */
1802 priv_update_check_list_state_for_ready (agent, stream, component);
1804 /* note: to take care of the controlling-controlling case in
1805 * aggressive nomination mode, send a new triggered
1806 * check to nominate the pair */
1807 if ((agent->compatibility == NICE_COMPATIBILITY_DRAFT19 ||
1808 agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1809 agent->controlling_mode)
1810 priv_conn_check_initiate (agent, p);
1813 /* note: the spec says the we SHOULD retransmit in-progress
1814 * checks immediately, but we won't do that now */
1816 return TRUE;
1821 NiceCandidate *local = NULL;
1823 for (i = component->local_candidates; i ; i = i->next) {
1824 local = i->data;
1825 if (local->sockptr == local_socket)
1826 break;
1828 if (i) {
1829 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
1830 result = priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate);
1832 else
1833 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
1836 return result;
1841 * Sends a reply to an succesfully received STUN connectivity
1842 * check request. Implements parts of the ICE spec section 7.2 (STUN
1843 * Server Procedures).
1845 * @param agent context pointer
1846 * @param stream which stream (of the agent)
1847 * @param component which component (of the stream)
1848 * @param rcand remote candidate from which the request came, if NULL,
1849 * the response is sent immediately but no other processing is done
1850 * @param toaddr address to which reply is sent
1851 * @param socket the socket over which the request came
1852 * @param rbuf_len length of STUN message to send
1853 * @param rbuf buffer containing the STUN message to send
1854 * @param use_candidate whether the request had USE_CANDIDATE attribute
1856 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1858 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)
1860 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
1863 gchar tmpbuf[INET6_ADDRSTRLEN];
1864 nice_address_to_string (toaddr, tmpbuf);
1865 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent,
1866 tmpbuf,
1867 nice_address_get_port (toaddr),
1868 socket->fileno,
1869 (unsigned)rbuf_len,
1870 rcand, component->id,
1871 (int)use_candidate);
1874 nice_socket_send (socket, toaddr, rbuf_len, (const gchar*)rbuf);
1876 if (rcand) {
1877 /* note: upon succesful check, make the reserve check immediately */
1878 priv_schedule_triggered_check (agent, stream, component, socket, rcand, use_candidate);
1880 if (use_candidate)
1881 priv_mark_pair_nominated (agent, stream, component, rcand);
1886 * Stores information of an incoming STUN connectivity check
1887 * for later use. This is only needed when a check is received
1888 * before we get information about the remote candidates (via
1889 * SDP or other signaling means).
1891 * @return non-zero on error, zero on success
1893 static int priv_store_pending_check (NiceAgent *agent, Component *component,
1894 const NiceAddress *from, NiceSocket *socket, uint8_t *username,
1895 uint16_t username_len, uint32_t priority, gboolean use_candidate)
1897 IncomingCheck *icheck;
1898 nice_debug ("Agent %p : Storing pending check.", agent);
1900 if (component->incoming_checks &&
1901 g_slist_length (component->incoming_checks) >=
1902 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
1903 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
1904 return -1;
1907 icheck = g_slice_new0 (IncomingCheck);
1908 if (icheck) {
1909 GSList *pending = g_slist_append (component->incoming_checks, icheck);
1910 if (pending) {
1911 component->incoming_checks = pending;
1912 icheck->from = *from;
1913 icheck->local_socket = socket;
1914 icheck->priority = priority;
1915 icheck->use_candidate = use_candidate;
1916 icheck->username_len = username_len;
1917 icheck->username = NULL;
1918 if (username_len > 0)
1919 icheck->username = g_memdup (username, username_len);
1920 return 0;
1924 return -1;
1928 * Adds a new pair, discovered from an incoming STUN response, to
1929 * the connectivity check list.
1931 * @return created pair, or NULL on fatal (memory allocation) errors
1933 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
1935 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1936 if (pair) {
1937 Stream *stream = agent_find_stream (agent, stream_id);
1938 GSList *modified_list = g_slist_append (stream->conncheck_list, pair);
1939 if (modified_list) {
1940 stream->conncheck_list = modified_list;
1941 pair->agent = agent;
1942 pair->stream_id = stream_id;
1943 pair->component_id = component_id;;
1944 pair->local = local_cand;
1945 pair->remote = parent_pair->remote;
1946 pair->state = NICE_CHECK_DISCOVERED;
1947 nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair);
1948 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local_cand->foundation, parent_pair->remote->foundation);
1949 if (agent->controlling_mode == TRUE)
1950 pair->priority = nice_candidate_pair_priority (local_cand->priority, parent_pair->priority);
1951 else
1952 pair->priority = nice_candidate_pair_priority (parent_pair->priority, local_cand->priority);
1953 pair->nominated = FALSE;
1954 pair->controlling = agent->controlling_mode;
1955 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation);
1956 return pair;
1960 return NULL;
1964 * Recalculates priorities of all candidate pairs. This
1965 * is required after a conflict in ICE roles.
1967 static void priv_recalculate_pair_priorities (NiceAgent *agent)
1969 GSList *i, *j;
1971 for (i = agent->streams; i; i = i->next) {
1972 Stream *stream = i->data;
1973 for (j = stream->conncheck_list; j; j = j->next) {
1974 CandidateCheckPair *p = j->data;
1975 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
1981 * Change the agent role if different from 'control'. Can be
1982 * initiated both by handling of incoming connectivity checks,
1983 * and by processing the responses to checks sent by us.
1985 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
1987 /* role conflict, change mode; wait for a new conn. check */
1988 if (control != agent->controlling_mode) {
1989 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control);
1990 agent->controlling_mode = control;
1991 /* the pair priorities depend on the roles, so recalculation
1992 * is needed */
1993 priv_recalculate_pair_priorities (agent);
1995 else
1996 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control);
2000 * Checks whether the mapped address in connectivity check response
2001 * matches any of the known local candidates. If not, apply the
2002 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
2004 * @param agent context pointer
2005 * @param stream which stream (of the agent)
2006 * @param component which component (of the stream)
2007 * @param p the connectivity check pair for which we got a response
2008 * @param socketptr socket used to send the reply
2009 * @param mapped_sockaddr mapped address in the response
2011 * @return pointer to a new pair if one was created, otherwise NULL
2013 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)
2015 CandidateCheckPair *new_pair = NULL;
2016 NiceAddress mapped;
2017 GSList *j;
2018 gboolean local_cand_matches = FALSE;
2020 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
2022 for (j = component->local_candidates; j; j = j->next) {
2023 NiceCandidate *cand = j->data;
2024 if (nice_address_equal (&mapped, &cand->addr)) {
2025 local_cand_matches = TRUE;
2026 break;
2030 if (local_cand_matches == TRUE) {
2031 /* note: this is same as "adding to VALID LIST" in the spec
2032 text */
2033 p->state = NICE_CHECK_SUCCEEDED;
2034 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
2035 priv_conn_check_unfreeze_related (agent, stream, p);
2037 else {
2038 NiceCandidate *cand =
2039 discovery_add_peer_reflexive_candidate (agent,
2040 stream->id,
2041 component->id,
2042 &mapped,
2043 sockptr,
2044 local_candidate,
2045 remote_candidate);
2046 p->state = NICE_CHECK_FAILED;
2047 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
2049 /* step: add a new discovered pair (see ICE 7.1.2.2.2
2050 "Constructing a Valid Pair" (ID-19)) */
2051 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p);
2052 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair);
2055 return new_pair;
2059 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
2060 * check transaction. If found, the reply is processed. Implements
2061 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
2063 * @return TRUE if a matching transaction is found
2065 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)
2067 struct sockaddr sockaddr;
2068 socklen_t socklen = sizeof (sockaddr);
2069 GSList *i;
2070 StunUsageIceReturn res;
2071 gboolean trans_found = FALSE;
2072 StunTransactionId discovery_id;
2073 StunTransactionId response_id;
2074 stun_message_id (resp, response_id);
2076 for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) {
2077 CandidateCheckPair *p = i->data;
2079 if (p->stun_message.buffer) {
2080 stun_message_id (&p->stun_message, discovery_id);
2082 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2083 res = stun_usage_ice_conncheck_process (resp, &sockaddr, &socklen,
2084 agent_to_ice_compatibility (agent));
2085 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
2086 "(controlling=%d).", agent, p, (int)res, agent->controlling_mode);
2088 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2089 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
2090 /* case: found a matching connectivity check request */
2092 CandidateCheckPair *ok_pair = NULL;
2094 nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p);
2095 p->stun_message.buffer = NULL;
2096 p->stun_message.buffer_len = 0;
2098 /* step: verify that response came from the same IP address we
2099 * sent the original request to (see 7.1.2.1. "Failure
2100 * Cases") */
2101 if (nice_address_equal (from, &p->remote->addr) != TRUE) {
2102 gchar tmpbuf[INET6_ADDRSTRLEN];
2103 gchar tmpbuf2[INET6_ADDRSTRLEN];
2105 p->state = NICE_CHECK_FAILED;
2106 nice_debug ("Agent %p : conncheck %p FAILED"
2107 " (mismatch of source address).", agent, p);
2108 nice_address_to_string (&p->remote->addr, tmpbuf);
2109 nice_address_to_string (from, tmpbuf2);
2110 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
2111 tmpbuf, nice_address_get_port (&p->remote->addr),
2112 tmpbuf2, nice_address_get_port (from));
2114 trans_found = TRUE;
2115 break;
2118 /* note: CONNECTED but not yet READY, see docs */
2120 /* step: handle the possible case of a peer-reflexive
2121 * candidate where the mapped-address in response does
2122 * not match any local candidate, see 7.1.2.2.1
2123 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
2125 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
2126 /* note: this is same as "adding to VALID LIST" in the spec
2127 text */
2128 p->state = NICE_CHECK_SUCCEEDED;
2129 nice_debug ("Agent %p : Mapped address not found."
2130 " conncheck %p SUCCEEDED.", agent, p);
2131 priv_conn_check_unfreeze_related (agent, stream, p);
2132 } else {
2133 ok_pair = priv_process_response_check_for_peer_reflexive(agent,
2134 stream, component, p, sockptr, &sockaddr,
2135 local_candidate, remote_candidate);
2139 if (!ok_pair)
2140 ok_pair = p;
2142 /* Do not step down to CONNECTED if we're already at state READY*/
2143 if (component->state != NICE_COMPONENT_STATE_READY) {
2144 /* step: notify the client of a new component state (must be done
2145 * before the possible check list state update step */
2146 agent_signal_component_state_change (agent,
2147 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2151 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
2152 Nominated Flag" (ID-19) */
2153 if (ok_pair->nominated == TRUE)
2154 priv_update_selected_pair (agent, component, ok_pair);
2156 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
2157 states" and 8.1.2 "Updating States", ID-19) */
2158 priv_update_check_list_state_for_ready (agent, stream, component);
2160 trans_found = TRUE;
2161 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2162 /* case: role conflict error, need to restart with new role */
2163 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p);
2164 /* note: our role might already have changed due to an
2165 * incoming request, but if not, change role now;
2166 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
2167 priv_check_for_role_conflict (agent, !p->controlling);
2169 p->stun_message.buffer = NULL;
2170 p->stun_message.buffer_len = 0;
2171 p->state = NICE_CHECK_WAITING;
2172 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
2173 trans_found = TRUE;
2174 } else if (res == STUN_USAGE_ICE_RETURN_ERROR) {
2175 /* case: STUN error, the check STUN context was freed */
2176 nice_debug ("Agent %p : conncheck %p FAILED.", agent, p);
2177 p->stun_message.buffer = NULL;
2178 p->stun_message.buffer_len = 0;
2179 trans_found = TRUE;
2185 return trans_found;
2189 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2190 * transaction. If found, a reply is sent.
2192 * @return TRUE if a matching transaction is found
2194 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp)
2196 struct sockaddr sockaddr;
2197 socklen_t socklen = sizeof (sockaddr);
2198 struct sockaddr alternate;
2199 socklen_t alternatelen = sizeof (sockaddr);
2200 GSList *i;
2201 StunUsageBindReturn res;
2202 gboolean trans_found = FALSE;
2203 StunTransactionId discovery_id;
2204 StunTransactionId response_id;
2205 stun_message_id (resp, response_id);
2207 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
2208 CandidateDiscovery *d = i->data;
2210 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
2211 d->stun_message.buffer) {
2212 stun_message_id (&d->stun_message, discovery_id);
2214 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2215 res = stun_usage_bind_process (resp, &sockaddr, &socklen,
2216 &alternate, &alternatelen);
2217 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
2218 agent, d, (int)res);
2220 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
2221 /* handle alternate server */
2222 NiceAddress niceaddr;
2223 nice_address_set_from_sockaddr (&niceaddr, &alternate);
2224 d->server = niceaddr;
2226 d->pending = FALSE;
2227 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
2228 /* case: succesful binding discovery, create a new local candidate */
2229 NiceAddress niceaddr;
2230 nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
2232 discovery_add_server_reflexive_candidate (
2233 d->agent,
2234 d->stream->id,
2235 d->component->id,
2236 &niceaddr,
2237 d->nicesock);
2239 d->stun_message.buffer = NULL;
2240 d->stun_message.buffer_len = 0;
2241 d->done = TRUE;
2242 trans_found = TRUE;
2243 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
2244 /* case: STUN error, the check STUN context was freed */
2245 d->stun_message.buffer = NULL;
2246 d->stun_message.buffer_len = 0;
2247 d->done = TRUE;
2248 trans_found = TRUE;
2254 return trans_found;
2258 static CandidateRefresh *
2259 priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand,
2260 guint lifetime)
2262 CandidateRefresh *cand;
2263 NiceAgent *agent = cdisco->agent;
2264 GSList *modified_list;
2266 cand = g_slice_new0 (CandidateRefresh);
2267 if (cand) {
2268 modified_list = g_slist_append (agent->refresh_list, cand);
2270 if (modified_list) {
2271 cand->nicesock = cdisco->nicesock;
2272 cand->relay_socket = relay_cand->sockptr;
2273 cand->server = cdisco->server;
2274 cand->turn = cdisco->turn;
2275 cand->stream = cdisco->stream;
2276 cand->component = cdisco->component;
2277 cand->agent = cdisco->agent;
2278 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
2279 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2280 agent, cand, (lifetime - 60) * 1000);
2281 agent->refresh_list = modified_list;
2283 /* step: also start the refresh timer */
2284 /* refresh should be sent 1 minute before it expires */
2285 cand->timer_source =
2286 agent_timeout_add_with_context (agent, (lifetime - 60) * 1000,
2287 priv_turn_allocate_refresh_tick, cand);
2289 nice_debug ("timer source is : %d", cand->timer_source);
2293 return cand;
2297 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2298 * transaction. If found, a reply is sent.
2300 * @return TRUE if a matching transaction is found
2302 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
2304 struct sockaddr sockaddr;
2305 socklen_t socklen = sizeof (sockaddr);
2306 struct sockaddr alternate;
2307 socklen_t alternatelen = sizeof (alternate);
2308 struct sockaddr relayaddr;
2309 socklen_t relayaddrlen = sizeof (relayaddr);
2310 uint32_t lifetime;
2311 uint32_t bandwidth;
2312 GSList *i;
2313 StunUsageTurnReturn res;
2314 gboolean trans_found = FALSE;
2315 StunTransactionId discovery_id;
2316 StunTransactionId response_id;
2317 stun_message_id (resp, response_id);
2319 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
2320 CandidateDiscovery *d = i->data;
2322 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
2323 d->stun_message.buffer) {
2324 stun_message_id (&d->stun_message, discovery_id);
2326 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2327 res = stun_usage_turn_process (resp,
2328 &relayaddr, &relayaddrlen, &sockaddr, &socklen, &alternate, &alternatelen,
2329 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
2330 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2331 agent, d, (int)res);
2333 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
2334 /* handle alternate server */
2335 nice_address_set_from_sockaddr (&d->server, &alternate);
2336 nice_address_set_from_sockaddr (&d->turn->server, &alternate);
2338 d->pending = FALSE;
2339 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
2340 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
2341 /* case: succesful allocate, create a new local candidate */
2342 NiceAddress niceaddr;
2343 NiceCandidate *relay_cand;
2345 /* We also received our mapped address */
2346 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
2347 nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
2349 discovery_add_server_reflexive_candidate (
2350 d->agent,
2351 d->stream->id,
2352 d->component->id,
2353 &niceaddr,
2354 d->nicesock);
2357 nice_address_set_from_sockaddr (&niceaddr, &relayaddr);
2358 relay_cand = discovery_add_relay_candidate (
2359 d->agent,
2360 d->stream->id,
2361 d->component->id,
2362 &niceaddr,
2363 d->nicesock,
2364 d->turn);
2366 if (relay_cand) {
2367 priv_add_new_turn_refresh (d, relay_cand, lifetime);
2370 d->stun_message.buffer = NULL;
2371 d->stun_message.buffer_len = 0;
2372 d->done = TRUE;
2373 trans_found = TRUE;
2374 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2375 int code = -1;
2376 uint8_t *sent_realm = NULL;
2377 uint8_t *recv_realm = NULL;
2378 uint16_t sent_realm_len = 0;
2379 uint16_t recv_realm_len = 0;
2381 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
2382 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2383 recv_realm = (uint8_t *) stun_message_find (resp,
2384 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2386 /* check for unauthorized error response */
2387 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
2388 stun_message_get_class (resp) == STUN_ERROR &&
2389 stun_message_find_error (resp, &code) ==
2390 STUN_MESSAGE_RETURN_SUCCESS &&
2391 recv_realm != NULL && recv_realm_len > 0) {
2393 if (code == 438 ||
2394 (code == 401 &&
2395 !(recv_realm_len == sent_realm_len &&
2396 sent_realm != NULL &&
2397 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2398 d->stun_resp_msg = *resp;
2399 memcpy (d->stun_resp_buffer, resp->buffer,
2400 stun_message_length (resp));
2401 d->stun_resp_msg.buffer = d->stun_resp_buffer;
2402 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
2403 d->pending = FALSE;
2404 } else {
2405 /* case: a real unauthorized error */
2406 d->stun_message.buffer = NULL;
2407 d->stun_message.buffer_len = 0;
2408 d->done = TRUE;
2410 } else {
2411 /* case: STUN error, the check STUN context was freed */
2412 d->stun_message.buffer = NULL;
2413 d->stun_message.buffer_len = 0;
2414 d->done = TRUE;
2416 trans_found = TRUE;
2422 return trans_found;
2427 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2428 * transaction. If found, a reply is sent.
2430 * @return TRUE if a matching transaction is found
2432 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
2434 uint32_t lifetime;
2435 GSList *i;
2436 StunUsageTurnReturn res;
2437 gboolean trans_found = FALSE;
2438 StunTransactionId refresh_id;
2439 StunTransactionId response_id;
2440 stun_message_id (resp, response_id);
2442 for (i = agent->refresh_list; i && trans_found != TRUE; i = i->next) {
2443 CandidateRefresh *cand = i->data;
2445 if (cand->stun_message.buffer) {
2446 stun_message_id (&cand->stun_message, refresh_id);
2448 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
2449 res = stun_usage_turn_refresh_process (resp,
2450 &lifetime, agent_to_turn_compatibility (cand->agent));
2451 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2452 agent, cand, (int)res);
2453 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
2454 /* refresh should be sent 1 minute before it expires */
2455 cand->timer_source =
2456 agent_timeout_add_with_context (cand->agent, (lifetime - 60) * 1000,
2457 priv_turn_allocate_refresh_tick, cand);
2459 g_source_destroy (cand->tick_source);
2460 g_source_unref (cand->tick_source);
2461 cand->tick_source = NULL;
2462 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2463 int code = -1;
2464 uint8_t *sent_realm = NULL;
2465 uint8_t *recv_realm = NULL;
2466 uint16_t sent_realm_len = 0;
2467 uint16_t recv_realm_len = 0;
2469 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
2470 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2471 recv_realm = (uint8_t *) stun_message_find (resp,
2472 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2474 /* check for unauthorized error response */
2475 if (cand->agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
2476 stun_message_get_class (resp) == STUN_ERROR &&
2477 stun_message_find_error (resp, &code) ==
2478 STUN_MESSAGE_RETURN_SUCCESS &&
2479 recv_realm != NULL && recv_realm_len > 0) {
2481 if (code == 438 ||
2482 (code == 401 &&
2483 !(recv_realm_len == sent_realm_len &&
2484 sent_realm != NULL &&
2485 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2486 cand->stun_resp_msg = *resp;
2487 memcpy (cand->stun_resp_buffer, resp->buffer,
2488 stun_message_length (resp));
2489 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
2490 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
2491 priv_turn_allocate_refresh_tick_unlocked (cand);
2492 } else {
2493 /* case: a real unauthorized error */
2494 refresh_cancel (cand);
2496 } else {
2497 /* case: STUN error, the check STUN context was freed */
2498 refresh_cancel (cand);
2500 trans_found = TRUE;
2506 return trans_found;
2510 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
2511 Component *component, StunMessage *resp)
2513 StunTransactionId conncheck_id;
2514 StunTransactionId response_id;
2515 stun_message_id (resp, response_id);
2517 if (component->selected_pair.keepalive.stun_message.buffer) {
2518 stun_message_id (&component->selected_pair.keepalive.stun_message,
2519 conncheck_id);
2520 if (memcmp (conncheck_id, response_id, sizeof(StunTransactionId)) == 0) {
2521 nice_debug ("Agent %p : Keepalive for selected pair received.",
2522 agent);
2523 if (component->selected_pair.keepalive.tick_source) {
2524 g_source_destroy (component->selected_pair.keepalive.tick_source);
2525 g_source_unref (component->selected_pair.keepalive.tick_source);
2526 component->selected_pair.keepalive.tick_source = NULL;
2528 component->selected_pair.keepalive.stun_message.buffer = NULL;
2529 return TRUE;
2533 return FALSE;
2537 typedef struct {
2538 NiceAgent *agent;
2539 Stream *stream;
2540 Component *component;
2541 uint8_t *password;
2542 } conncheck_validater_data;
2544 static bool conncheck_stun_validater (StunAgent *agent,
2545 StunMessage *message, uint8_t *username, uint16_t username_len,
2546 uint8_t **password, size_t *password_len, void *user_data)
2548 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
2549 GSList *i;
2550 uint8_t uname[NICE_STREAM_MAX_UNAME];
2551 guint uname_len = 0;
2553 for (i = data->component->local_candidates; i; i = i->next) {
2554 NiceCandidate *cand = i->data;
2555 gchar *ufrag = NULL;
2556 gsize ufrag_len;
2558 if (cand->username)
2559 ufrag = cand->username;
2560 else if (data->stream)
2561 ufrag = data->stream->local_ufrag;
2562 ufrag_len = strlen (ufrag);
2564 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2565 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
2567 if (ufrag_len <= NICE_STREAM_MAX_UNAME) {
2568 memcpy (uname, ufrag, ufrag_len);
2569 uname_len = ufrag_len;
2572 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2573 g_free (ufrag);
2575 stun_debug ("Comparing username '");
2576 stun_debug_bytes (username, username_len);
2577 stun_debug ("' (%d) with '", username_len);
2578 stun_debug_bytes (uname, uname_len);
2579 stun_debug ("' (%d) : %d\n",
2580 uname_len, memcmp (username, uname, uname_len));
2581 if (uname_len > 0 && username_len >= uname_len &&
2582 memcmp (username, uname, uname_len) == 0) {
2583 gchar *pass = NULL;
2585 if (cand->password)
2586 pass = cand->password;
2587 else
2588 pass = data->stream->local_password;
2590 *password = (uint8_t *) pass;
2591 *password_len = strlen (pass);
2593 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN) {
2594 data->password = g_base64_decode (pass, password_len);
2595 *password = data->password;
2598 stun_debug ("Found valid username, returning password: '%s'\n", *password);
2599 return TRUE;
2603 return FALSE;
2608 * Processing an incoming STUN message.
2610 * @param agent self pointer
2611 * @param stream stream the packet is related to
2612 * @param component component the packet is related to
2613 * @param socket socket from which the packet was received
2614 * @param from address of the sender
2615 * @param buf message contents
2616 * @param buf message length
2618 * @pre contents of 'buf' is a STUN message
2620 * @return XXX (what FALSE means exactly?)
2622 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
2623 Component *component, NiceSocket *socket, const NiceAddress *from,
2624 gchar *buf, guint len)
2626 struct sockaddr sockaddr;
2627 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
2628 ssize_t res;
2629 size_t rbuf_len = sizeof (rbuf);
2630 bool control = agent->controlling_mode;
2631 uint8_t uname[NICE_STREAM_MAX_UNAME];
2632 guint uname_len;
2633 uint8_t *username;
2634 uint16_t username_len;
2635 StunMessage req;
2636 StunMessage msg;
2637 StunValidationStatus valid;
2638 conncheck_validater_data validater_data = {agent, stream, component, NULL};
2639 GSList *i, *j;
2640 NiceCandidate *remote_candidate = NULL;
2641 NiceCandidate *remote_candidate2 = NULL;
2642 NiceCandidate *local_candidate = NULL;
2643 gboolean discovery_msg = FALSE;
2645 nice_address_copy_to_sockaddr (from, &sockaddr);
2647 /* note: contents of 'buf' already validated, so it is
2648 * a valid and fully received STUN message */
2650 #ifndef NDEBUG
2652 gchar tmpbuf[INET6_ADDRSTRLEN];
2653 nice_address_to_string (from, tmpbuf);
2654 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2655 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
2657 #endif
2659 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2661 valid = stun_agent_validate (&agent->stun_agent, &req,
2662 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2664 /* Check for discovery candidates stun agents */
2665 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2666 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2667 for (i = agent->discovery_list; i; i = i->next) {
2668 CandidateDiscovery *d = i->data;
2669 if (d->stream == stream && d->component == component &&
2670 d->nicesock == socket) {
2671 valid = stun_agent_validate (&d->stun_agent, &req,
2672 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2674 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2675 continue;
2677 discovery_msg = TRUE;
2678 break;
2682 /* Check for relay refresh stun agents */
2683 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2684 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2685 for (i = agent->refresh_list; i; i = i->next) {
2686 CandidateRefresh *r = i->data;
2687 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream,
2688 stream, r->component, component, r->nicesock, r->relay_socket, socket);
2689 if (r->stream == stream && r->component == component &&
2690 (r->nicesock == socket || r->relay_socket == socket)) {
2691 valid = stun_agent_validate (&r->stun_agent, &req,
2692 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2693 nice_debug ("Validating gave %d", valid);
2694 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2695 continue;
2696 discovery_msg = TRUE;
2697 break;
2703 if (validater_data.password)
2704 g_free (validater_data.password);
2706 if (valid == STUN_VALIDATION_NOT_STUN ||
2707 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
2708 valid == STUN_VALIDATION_BAD_REQUEST)
2710 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2711 agent);
2712 return FALSE;
2715 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
2716 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
2717 rbuf_len = stun_agent_build_unknown_attributes_error (&agent->stun_agent,
2718 &msg, rbuf, rbuf_len, &req);
2719 if (len == 0)
2720 return FALSE;
2722 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2723 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2725 return TRUE;
2728 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
2729 nice_debug ("Agent %p : Integrity check failed.", agent);
2731 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2732 &req, STUN_ERROR_UNAUTHORIZED)) {
2733 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2734 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2735 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2737 return TRUE;
2739 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
2740 nice_debug ("Agent %p : Integrity check failed.", agent);
2741 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2742 &req, STUN_ERROR_BAD_REQUEST)) {
2743 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2744 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2745 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2747 return TRUE;
2750 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
2751 &username_len);
2753 for (i = component->remote_candidates; i; i = i->next) {
2754 NiceCandidate *cand = i->data;
2755 if (nice_address_equal (from, &cand->addr)) {
2756 remote_candidate = cand;
2757 break;
2761 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2762 agent->compatibility == NICE_COMPATIBILITY_MSN) {
2763 /* We need to find which local candidate was used */
2764 for (i = component->remote_candidates;
2765 i != NULL && remote_candidate2 == NULL; i = i->next) {
2766 for (j = component->local_candidates; j; j = j->next) {
2767 gboolean inbound = TRUE;
2768 NiceCandidate *rcand = i->data;
2769 NiceCandidate *lcand = j->data;
2771 /* If we receive a response, then the username is local:remote */
2772 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2773 if (stun_message_get_class (&req) == STUN_REQUEST ||
2774 stun_message_get_class (&req) == STUN_INDICATION) {
2775 inbound = TRUE;
2776 } else {
2777 inbound = FALSE;
2781 uname_len = priv_create_username (agent, stream,
2782 component->id, rcand, lcand,
2783 uname, sizeof (uname), inbound);
2785 stun_debug ("Comparing username '");
2786 stun_debug_bytes (username, username? username_len : 0);
2787 stun_debug ("' (%d) with '", username_len);
2788 stun_debug_bytes (uname, uname_len);
2789 stun_debug ("' (%d) : %d\n",
2790 uname_len, username && uname_len == username_len &&
2791 memcmp (username, uname, uname_len) == 0);
2793 if (username &&
2794 uname_len == username_len &&
2795 memcmp (uname, username, username_len) == 0) {
2796 local_candidate = lcand;
2797 remote_candidate2 = rcand;
2798 break;
2804 if (component->remote_candidates &&
2805 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2806 local_candidate == NULL &&
2807 discovery_msg == FALSE) {
2808 /* if we couldn't match the username and the stun agent has
2809 IGNORE_CREDENTIALS then we have an integrity check failing.
2810 This could happen with the race condition of receiving connchecks
2811 before the remote candidates are added. Just drop the message, and let
2812 the retransmissions make it work. */
2813 nice_debug ("Agent %p : Username check failed.", agent);
2814 return TRUE;
2817 if (valid != STUN_VALIDATION_SUCCESS) {
2818 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent, valid);
2819 return FALSE;
2823 if (stun_message_get_class (&req) == STUN_REQUEST) {
2824 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2825 if (local_candidate && remote_candidate2) {
2826 username = (uint8_t *) stun_message_find (&req,
2827 STUN_ATTRIBUTE_USERNAME, &username_len);
2828 uname_len = priv_create_username (agent, stream,
2829 component->id, remote_candidate2, local_candidate,
2830 uname, sizeof (uname), FALSE);
2831 memcpy (username, uname, username_len);
2832 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
2833 &req.key_len);
2834 } else {
2835 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
2836 "Ignoring request", agent);
2837 return TRUE;
2841 rbuf_len = sizeof (rbuf);
2842 res = stun_usage_ice_conncheck_create_reply (&agent->stun_agent, &req,
2843 &msg, rbuf, &rbuf_len, &sockaddr, sizeof (sockaddr),
2844 &control, agent->tie_breaker,
2845 agent_to_ice_compatibility (agent));
2847 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2848 g_free (req.key);
2851 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
2852 priv_check_for_role_conflict (agent, control);
2854 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2855 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2856 /* case 1: valid incoming request, send a reply/error */
2857 bool use_candidate =
2858 stun_usage_ice_conncheck_use_candidate (&req);
2859 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
2861 if (agent->controlling_mode ||
2862 agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2863 agent->compatibility == NICE_COMPATIBILITY_MSN)
2864 use_candidate = TRUE;
2866 if (stream->initial_binding_request_received != TRUE)
2867 agent_signal_initial_binding_request_received (agent, stream);
2869 if (component->remote_candidates && remote_candidate == NULL) {
2870 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2871 "peer-reflexive candidate.", agent);
2872 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
2873 agent, stream, component, priority, from, socket,
2874 local_candidate,
2875 remote_candidate2 ? remote_candidate2 : remote_candidate);
2876 if(remote_candidate)
2877 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
2880 priv_reply_to_conn_check (agent, stream, component, remote_candidate,
2881 from, socket, rbuf_len, rbuf, use_candidate);
2883 if (component->remote_candidates == NULL) {
2884 /* case: We've got a valid binding request to a local candidate
2885 * but we do not yet know remote credentials nor
2886 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2887 * immediately but postpone all other processing until
2888 * we get information about the remote candidates */
2890 /* step: send a reply immediately but postpone other processing */
2891 priv_store_pending_check (agent, component, from, socket,
2892 username, username_len, priority, use_candidate);
2894 } else {
2895 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2896 agent, strerror(errno));
2897 return FALSE;
2899 } else {
2900 /* case 2: not a new request, might be a reply... */
2901 gboolean trans_found = FALSE;
2903 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2905 /* step: let's try to match the response to an existing check context */
2906 if (trans_found != TRUE)
2907 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
2908 component, socket, from, local_candidate, remote_candidate, &req);
2910 /* step: let's try to match the response to an existing discovery */
2911 if (trans_found != TRUE)
2912 trans_found = priv_map_reply_to_discovery_request (agent, &req);
2914 /* step: let's try to match the response to an existing turn allocate */
2915 if (trans_found != TRUE)
2916 trans_found = priv_map_reply_to_relay_request (agent, &req);
2918 /* step: let's try to match the response to an existing turn refresh */
2919 if (trans_found != TRUE)
2920 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
2922 /* step: let's try to match the response to an existing keepalive conncheck */
2923 if (trans_found != TRUE)
2924 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
2925 &req);
2927 if (trans_found != TRUE)
2928 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2929 "probably a keepalive.", agent);
2932 return TRUE;