Only keep the srv-reflexive candidates alive by resending our initial STUN discovery...
[sipe-libnice.git] / agent / conncheck.c
blob90a5f5b4b40d92147e8e5754980778f4f86ad6e9
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);
1051 * Enforces the upper limit for connectivity checks as described
1052 * in ICE spec section 5.7.3 (ID-19). See also
1053 * conn_check_add_for_candidate().
1055 static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
1057 guint list_len = g_slist_length (conncheck_list);
1058 guint c = 0;
1059 GSList *result = conncheck_list;
1061 if (list_len > upper_limit) {
1062 nice_debug ("Agent : Pruning candidates. Conncheck list has %d elements. "
1063 "Maximum connchecks allowed : %d", list_len, upper_limit);
1064 c = list_len - upper_limit;
1065 if (c == list_len) {
1066 /* case: delete whole list */
1067 g_slist_foreach (conncheck_list, conn_check_free_item, NULL);
1068 g_slist_free (conncheck_list),
1069 result = NULL;
1071 else {
1072 /* case: remove 'c' items from list end (lowest priority) */
1073 GSList *i, *tmp;
1075 g_assert (c > 0);
1076 i = g_slist_nth (conncheck_list, c - 1);
1078 tmp = i->next;
1079 i->next = NULL;
1081 if (tmp) {
1082 /* delete the rest of the connectivity check list */
1083 g_slist_foreach (tmp, conn_check_free_item, NULL);
1084 g_slist_free (tmp);
1089 return result;
1093 * Changes the selected pair for the component if 'pair' is nominated
1094 * and has higher priority than the currently selected pair. See
1095 * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
1097 static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
1099 g_assert (component);
1100 g_assert (pair);
1101 if (pair->priority > component->selected_pair.priority) {
1102 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
1103 "(prio:%lu).", agent, component->id, pair->local->foundation,
1104 pair->remote->foundation, (long unsigned)pair->priority);
1106 if (component->selected_pair.keepalive.tick_source != NULL) {
1107 g_source_destroy (component->selected_pair.keepalive.tick_source);
1108 g_source_unref (component->selected_pair.keepalive.tick_source);
1109 component->selected_pair.keepalive.tick_source = NULL;
1112 memset (&component->selected_pair, 0, sizeof(CandidatePair));
1113 component->selected_pair.local = pair->local;
1114 component->selected_pair.remote = pair->remote;
1115 component->selected_pair.priority = pair->priority;
1117 priv_conn_keepalive_tick_unlocked (agent);
1119 agent_signal_new_selected_pair (agent, pair->stream_id, component->id, pair->local->foundation, pair->remote->foundation);
1123 return TRUE;
1127 * Updates the check list state.
1129 * Implements parts of the algorithm described in
1130 * ICE sect 8.1.2. "Updating States" (ID-19): if for any
1131 * component, all checks have been completed and have
1132 * failed, mark that component's state to NICE_CHECK_FAILED.
1134 * Sends a component state changesignal via 'agent'.
1136 static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream)
1138 GSList *i;
1139 /* note: emitting a signal might cause the client
1140 * to remove the stream, thus the component count
1141 * must be fetched before entering the loop*/
1142 guint c, components = stream->n_components;
1144 /* note: iterate the conncheck list for each component separately */
1145 for (c = 0; c < components; c++) {
1146 Component *comp = NULL;
1147 agent_find_component (agent, stream->id, c+1, NULL, &comp);
1149 for (i = stream->conncheck_list; i; i = i->next) {
1150 CandidateCheckPair *p = i->data;
1152 if (p->stream_id == stream->id &&
1153 p->component_id == (c + 1)) {
1154 if (p->state != NICE_CHECK_FAILED)
1155 break;
1159 /* note: all checks have failed
1160 * Set the component to FAILED only if it actually had remote candidates
1161 * that failed.. */
1162 if (i == NULL && comp != NULL && comp->remote_candidates != NULL)
1163 agent_signal_component_state_change (agent,
1164 stream->id,
1165 (c + 1), /* component-id */
1166 NICE_COMPONENT_STATE_FAILED);
1171 * Updates the check list state for a stream component.
1173 * Implements the algorithm described in ICE sect 8.1.2
1174 * "Updating States" (ID-19) as it applies to checks of
1175 * a certain component. If there are any nominated pairs,
1176 * ICE processing may be concluded, and component state is
1177 * changed to READY.
1179 * Sends a component state changesignal via 'agent'.
1181 static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component)
1183 GSList *i;
1184 guint succeeded = 0, nominated = 0;
1186 g_assert (component);
1188 /* step: search for at least one nominated pair */
1189 for (i = stream->conncheck_list; i; i = i->next) {
1190 CandidateCheckPair *p = i->data;
1191 if (p->component_id == component->id) {
1192 if (p->state == NICE_CHECK_SUCCEEDED ||
1193 p->state == NICE_CHECK_DISCOVERED) {
1194 ++succeeded;
1195 if (p->nominated == TRUE) {
1196 ++nominated;
1197 /* Only go to READY if no checks are left in progress. If there are
1198 * any that are kept, then this function will be called again when the
1199 * conncheck tick timer finishes them all */
1200 if (priv_prune_pending_checks (stream, p->component_id) == 0) {
1201 agent_signal_component_state_change (agent, p->stream_id,
1202 p->component_id, NICE_COMPONENT_STATE_READY);
1209 nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id);
1213 * The remote party has signalled that the candidate pair
1214 * described by 'component' and 'remotecand' is nominated
1215 * for use.
1217 static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand)
1219 GSList *i;
1221 g_assert (component);
1223 /* step: search for at least one nominated pair */
1224 for (i = stream->conncheck_list; i; i = i->next) {
1225 CandidateCheckPair *pair = i->data;
1226 /* XXX: hmm, how to figure out to which local candidate the
1227 * check was sent to? let's mark all matching pairs
1228 * as nominated instead */
1229 if (pair->remote == remotecand) {
1230 nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
1231 pair->nominated = TRUE;
1232 if (pair->state == NICE_CHECK_SUCCEEDED ||
1233 pair->state == NICE_CHECK_DISCOVERED)
1234 priv_update_selected_pair (agent, component, pair);
1235 priv_update_check_list_state_for_ready (agent, stream, component);
1241 * Creates a new connectivity check pair and adds it to
1242 * the agent's list of checks.
1244 static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
1246 gboolean result = FALSE;
1247 Stream *stream = agent_find_stream (agent, stream_id);
1248 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1249 if (pair) {
1250 GSList *modified_list =
1251 g_slist_insert_sorted (stream->conncheck_list, pair, (GCompareFunc)conn_check_compare);
1252 if (modified_list) {
1253 /* step: allocation and addition succesful, do rest of the work */
1255 pair->agent = agent;
1256 pair->stream_id = stream_id;
1257 pair->component_id = component->id;;
1258 pair->local = local;
1259 pair->remote = remote;
1260 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
1262 pair->priority = agent_candidate_pair_priority (agent, local, remote);
1263 pair->state = initial_state;
1264 nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state);
1265 pair->nominated = use_candidate;
1266 pair->controlling = agent->controlling_mode;
1268 /* note: for the first added check */
1269 if (!stream->conncheck_list)
1270 stream->conncheck_state = NICE_CHECKLIST_RUNNING;
1271 stream->conncheck_list = modified_list;
1273 result = TRUE;
1274 nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id);
1276 /* implement the hard upper limit for number of
1277 checks (see sect 5.7.3 ICE ID-19): */
1278 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19) {
1279 stream->conncheck_list =
1280 priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
1282 if (!stream->conncheck_list) {
1283 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1284 result = FALSE;
1287 else {
1288 /* memory alloc failed: list insert */
1289 conn_check_free_item (pair, NULL);
1290 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1293 else { /* memory alloc failed: new pair */
1294 stream->conncheck_state = NICE_CHECKLIST_FAILED;
1297 return result;
1301 * Forms new candidate pairs by matching the new remote candidate
1302 * 'remote_cand' with all existing local candidates of 'component'.
1303 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
1304 * Pairs" (ID-19).
1306 * @param agent context
1307 * @param component pointer to the component
1308 * @param remote remote candidate to match with
1310 * @return number of checks added, negative on fatal errors
1312 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
1314 GSList *i;
1315 int added = 0;
1317 for (i = component->local_candidates; i ; i = i->next) {
1319 NiceCandidate *local = i->data;
1321 /* note: match pairs only if transport and address family are the same */
1322 if (local->transport == remote->transport &&
1323 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
1325 gboolean result;
1327 /* note: do not create pairs where local candidate is
1328 * a srv-reflexive (ICE 5.7.3. "Pruning the Pairs" ID-19) */
1329 if ((agent->compatibility == NICE_COMPATIBILITY_DRAFT19 ||
1330 agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1331 local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE)
1332 continue;
1334 result = priv_add_new_check_pair (agent, stream_id, component, local, remote, NICE_CHECK_FROZEN, FALSE);
1335 if (result) {
1336 ++added;
1337 if (component->state < NICE_COMPONENT_STATE_CONNECTED) {
1338 agent_signal_component_state_change (agent,
1339 stream_id,
1340 component->id,
1341 NICE_COMPONENT_STATE_CONNECTING);
1342 } else {
1343 agent_signal_component_state_change (agent,
1344 stream_id,
1345 component->id,
1346 NICE_COMPONENT_STATE_CONNECTED);
1349 else {
1350 added = -1;
1351 break;
1356 return added;
1360 * Frees the CandidateCheckPair structure pointer to
1361 * by 'user data'. Compatible with g_slist_foreach().
1363 void conn_check_free_item (gpointer data, gpointer user_data)
1365 CandidateCheckPair *pair = data;
1366 g_assert (user_data == NULL);
1367 pair->stun_message.buffer = NULL;
1368 pair->stun_message.buffer_len = 0;
1369 g_slice_free (CandidateCheckPair, pair);
1373 * Frees all resources of all connectivity checks.
1375 void conn_check_free (NiceAgent *agent)
1377 GSList *i;
1378 for (i = agent->streams; i; i = i->next) {
1379 Stream *stream = i->data;
1381 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
1382 if (stream->conncheck_list) {
1383 g_slist_foreach (stream->conncheck_list, conn_check_free_item, NULL);
1384 g_slist_free (stream->conncheck_list),
1385 stream->conncheck_list = NULL;
1386 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1390 if (agent->conncheck_timer_source != NULL) {
1391 g_source_destroy (agent->conncheck_timer_source);
1392 g_source_unref (agent->conncheck_timer_source);
1393 agent->conncheck_timer_source = NULL;
1398 * Prunes the list of connectivity checks for items related
1399 * to stream 'stream_id'.
1401 * @return TRUE on success, FALSE on a fatal error
1403 gboolean conn_check_prune_stream (NiceAgent *agent, Stream *stream)
1405 CandidateCheckPair *pair;
1406 GSList *i;
1408 for (i = stream->conncheck_list; i ; ) {
1409 GSList *next = i->next;
1410 pair = i->data;
1412 g_assert (pair->stream_id == stream->id);
1414 stream->conncheck_list =
1415 g_slist_remove (stream->conncheck_list, pair);
1416 conn_check_free_item (pair, NULL);
1417 i = next;
1418 if (!stream->conncheck_list)
1419 break;
1422 if (!stream->conncheck_list) {
1423 stream->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
1424 conn_check_free (agent);
1427 /* return FALSE if there was a memory allocation failure */
1428 if (stream->conncheck_list == NULL && i != NULL)
1429 return FALSE;
1431 return TRUE;
1435 * Fills 'dest' with a username string for use in an outbound connectivity
1436 * checks. No more than 'dest_len' characters (including terminating
1437 * NULL) is ever written to the 'dest'.
1439 static
1440 size_t priv_gen_username (NiceAgent *agent, guint component_id,
1441 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
1443 guint len = 0;
1444 gsize remote_len = strlen (remote);
1445 gsize local_len = strlen (local);
1447 if (remote_len > 0 && local_len > 0) {
1448 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
1449 dest_len >= remote_len + local_len + 1) {
1450 memcpy (dest, remote, remote_len);
1451 len += remote_len;
1452 memcpy (dest + len, ":", 1);
1453 len++;
1454 memcpy (dest + len, local, local_len);
1455 len += local_len;
1456 } else if (agent->compatibility == NICE_COMPATIBILITY_WLM2009 &&
1457 dest_len >= remote_len + local_len + 4 ) {
1458 memcpy (dest, remote, remote_len);
1459 len += remote_len;
1460 memcpy (dest + len, ":", 1);
1461 len++;
1462 memcpy (dest + len, local, local_len);
1463 len += local_len;
1464 if (len % 4 != 0) {
1465 memset (dest + len, 0, 4 - (len % 4));
1466 len += 4 - (len % 4);
1468 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
1469 dest_len >= remote_len + local_len) {
1470 memcpy (dest, remote, remote_len);
1471 len += remote_len;
1472 memcpy (dest + len, local, local_len);
1473 len += local_len;
1474 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1475 gchar component_str[10];
1476 guchar *local_decoded = NULL;
1477 guchar *remote_decoded = NULL;
1478 gsize local_decoded_len;
1479 gsize remote_decoded_len;
1480 gsize total_len;
1481 int padding;
1483 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
1484 local_decoded = g_base64_decode (local, &local_decoded_len);
1485 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
1487 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
1488 padding = 4 - (total_len % 4);
1490 if (dest_len >= total_len + padding) {
1491 guchar pad_char[1] = {0};
1492 int i;
1494 memcpy (dest, remote_decoded, remote_decoded_len);
1495 len += remote_decoded_len;
1496 memcpy (dest + len, ":", 1);
1497 len++;
1498 memcpy (dest + len, component_str, strlen (component_str));
1499 len += strlen (component_str);
1501 memcpy (dest + len, ":", 1);
1502 len++;
1504 memcpy (dest + len, local_decoded, local_decoded_len);
1505 len += local_decoded_len;
1506 memcpy (dest + len, ":", 1);
1507 len++;
1508 memcpy (dest + len, component_str, strlen (component_str));;
1509 len += strlen (component_str);
1511 for (i = 0; i < padding; i++) {
1512 memcpy (dest + len, pad_char, 1);
1513 len++;
1518 g_free (local_decoded);
1519 g_free (remote_decoded);
1523 return len;
1527 * Fills 'dest' with a username string for use in an outbound connectivity
1528 * checks. No more than 'dest_len' characters (including terminating
1529 * NULL) is ever written to the 'dest'.
1531 static
1532 size_t priv_create_username (NiceAgent *agent, Stream *stream,
1533 guint component_id, NiceCandidate *remote, NiceCandidate *local,
1534 uint8_t *dest, guint dest_len, gboolean inbound)
1536 gchar *local_username = NULL;
1537 gchar *remote_username = NULL;
1540 if (remote && remote->username) {
1541 remote_username = remote->username;
1544 if (local && local->username) {
1545 local_username = local->username;
1548 if (stream) {
1549 if (remote_username == NULL) {
1550 remote_username = stream->remote_ufrag;
1552 if (local_username == NULL) {
1553 local_username = stream->local_ufrag;
1557 if (local_username && remote_username) {
1558 if (inbound) {
1559 return priv_gen_username (agent, component_id,
1560 local_username, remote_username, dest, dest_len);
1561 } else {
1562 return priv_gen_username (agent, component_id,
1563 remote_username, local_username, dest, dest_len);
1567 return 0;
1571 * Returns a password string for use in an outbound connectivity
1572 * check.
1574 static
1575 size_t priv_get_password (NiceAgent *agent, Stream *stream,
1576 NiceCandidate *remote, uint8_t **password)
1578 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
1579 return 0;
1581 if (remote && remote->password) {
1582 *password = (uint8_t *)remote->password;
1583 return strlen (remote->password);
1586 if (stream) {
1587 *password = (uint8_t *)stream->remote_password;
1588 return strlen (stream->remote_password);
1591 return 0;
1595 * Sends a connectivity check over candidate pair 'pair'.
1597 * @return zero on success, non-zero on error
1599 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
1602 /* note: following information is supplied:
1603 * - username (for USERNAME attribute)
1604 * - password (for MESSAGE-INTEGRITY)
1605 * - priority (for PRIORITY)
1606 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
1607 * - USE-CANDIDATE (if sent by the controlling agent)
1610 guint32 priority =
1611 nice_candidate_ice_priority_full (
1612 NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE,
1614 pair->local->component_id);
1616 uint8_t uname[NICE_STREAM_MAX_UNAME];
1617 size_t uname_len =
1618 priv_create_username (agent, agent_find_stream (agent, pair->stream_id),
1619 pair->component_id, pair->remote, pair->local, uname, sizeof (uname), FALSE);
1620 uint8_t *password = NULL;
1621 size_t password_len = priv_get_password (agent,
1622 agent_find_stream (agent, pair->stream_id), pair->remote, &password);
1624 bool controlling = agent->controlling_mode;
1625 /* XXX: add API to support different nomination modes: */
1626 bool cand_use = controlling;
1627 size_t buffer_len;
1629 struct sockaddr sockaddr;
1630 unsigned int timeout;
1632 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1633 password = g_base64_decode ((gchar *) password, &password_len);
1636 memset (&sockaddr, 0, sizeof (sockaddr));
1638 nice_address_copy_to_sockaddr (&pair->remote->addr, &sockaddr);
1641 gchar tmpbuf[INET6_ADDRSTRLEN];
1642 nice_address_to_string (&pair->remote->addr, tmpbuf);
1643 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,
1644 tmpbuf,
1645 ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
1646 pair->local->sockptr->fileno,
1647 pair->foundation, pair->component_id,
1648 (unsigned long long)agent->tie_breaker,
1649 uname, uname_len, password, password_len, priority);
1653 if (cand_use)
1654 pair->nominated = controlling;
1656 if (uname_len > 0) {
1658 buffer_len = stun_usage_ice_conncheck_create (&agent->stun_agent,
1659 &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer),
1660 uname, uname_len, password, password_len,
1661 cand_use, controlling, priority,
1662 agent->tie_breaker,
1663 agent_to_ice_compatibility (agent));
1665 nice_debug ("Agent %p: conncheck created %d - %p", agent, buffer_len, pair->stun_message.buffer);
1667 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
1668 g_free (password);
1671 if (buffer_len > 0) {
1672 stun_timer_start (&pair->timer);
1674 /* send the conncheck */
1675 nice_socket_send (pair->local->sockptr, &pair->remote->addr,
1676 buffer_len, (gchar *)pair->stun_buffer);
1678 timeout = stun_timer_remainder (&pair->timer);
1679 /* note: convert from milli to microseconds for g_time_val_add() */
1680 g_get_current_time (&pair->next_tick);
1681 g_time_val_add (&pair->next_tick, timeout * 1000);
1682 } else {
1683 pair->stun_message.buffer = NULL;
1684 pair->stun_message.buffer_len = 0;
1685 return -1;
1689 return 0;
1693 * Implemented the pruning steps described in ICE sect 8.1.2
1694 * "Updating States" (ID-19) after a pair has been nominated.
1696 * @see priv_update_check_list_state_failed_components()
1698 static guint priv_prune_pending_checks (Stream *stream, guint component_id)
1700 GSList *i;
1701 guint64 highest_nominated_priority = 0;
1702 guint in_progress = 0;
1704 for (i = stream->conncheck_list; i; i = i->next) {
1705 CandidateCheckPair *p = i->data;
1706 if (p->component_id == component_id &&
1707 (p->state == NICE_CHECK_SUCCEEDED ||
1708 p->state == NICE_CHECK_DISCOVERED) &&
1709 p->nominated == TRUE){
1710 if (p->priority > highest_nominated_priority) {
1711 highest_nominated_priority = p->priority;
1716 nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
1717 "is %lu", highest_nominated_priority);
1719 /* step: cancel all FROZEN and WAITING pairs for the component */
1720 for (i = stream->conncheck_list; i; i = i->next) {
1721 CandidateCheckPair *p = i->data;
1722 if (p->component_id == component_id) {
1723 if (p->state == NICE_CHECK_FROZEN ||
1724 p->state == NICE_CHECK_WAITING) {
1725 p->state = NICE_CHECK_CANCELLED;
1726 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1729 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
1730 if (p->state == NICE_CHECK_IN_PROGRESS) {
1731 if (highest_nominated_priority != 0 &&
1732 p->priority < highest_nominated_priority) {
1733 p->stun_message.buffer = NULL;
1734 p->stun_message.buffer_len = 0;
1735 p->state = NICE_CHECK_CANCELLED;
1736 nice_debug ("Agent XXX : pair %p state CANCELED", p);
1737 } else {
1738 /* We must keep the higher priority pairs running because if a udp
1739 * packet was lost, we might end up using a bad candidate */
1740 nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %d"
1741 " is higher than currently nominated pair %d", p, p->priority,
1742 highest_nominated_priority);
1743 in_progress++;
1749 return in_progress;
1753 * Schedules a triggered check after a succesfully inbound
1754 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
1756 * @param agent self pointer
1757 * @param component the check is related to
1758 * @param local_socket socket from which the inbound check was received
1759 * @param remote_cand remote candidate from which the inbound check was sent
1760 * @param use_candidate whether the original check had USE-CANDIDATE attribute set
1762 static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate)
1764 GSList *i;
1765 gboolean result = FALSE;
1767 for (i = stream->conncheck_list; i ; i = i->next) {
1768 CandidateCheckPair *p = i->data;
1769 if (p->component_id == component->id &&
1770 p->remote == remote_cand &&
1771 p->local->sockptr == local_socket) {
1773 nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p);
1775 if (p->state == NICE_CHECK_WAITING ||
1776 p->state == NICE_CHECK_FROZEN)
1777 priv_conn_check_initiate (agent, p);
1778 else if (p->state == NICE_CHECK_IN_PROGRESS) {
1779 /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
1780 * we should cancel the existing one, and send a new one...? :P */
1781 nice_debug ("Agent %p : Skipping triggered check, already in progress..", agent);
1783 else if (p->state == NICE_CHECK_SUCCEEDED ||
1784 p->state == NICE_CHECK_DISCOVERED) {
1785 nice_debug ("Agent %p : Skipping triggered check, already completed..", agent);
1786 /* note: this is a bit unsure corner-case -- let's do the
1787 same state update as for processing responses to our own checks */
1788 priv_update_check_list_state_for_ready (agent, stream, component);
1790 /* note: to take care of the controlling-controlling case in
1791 * aggressive nomination mode, send a new triggered
1792 * check to nominate the pair */
1793 if ((agent->compatibility == NICE_COMPATIBILITY_DRAFT19 ||
1794 agent->compatibility == NICE_COMPATIBILITY_WLM2009) &&
1795 agent->controlling_mode)
1796 priv_conn_check_initiate (agent, p);
1799 /* note: the spec says the we SHOULD retransmit in-progress
1800 * checks immediately, but we won't do that now */
1802 return TRUE;
1807 NiceCandidate *local = NULL;
1809 for (i = component->local_candidates; i ; i = i->next) {
1810 local = i->data;
1811 if (local->sockptr == local_socket)
1812 break;
1814 if (i) {
1815 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
1816 result = priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate);
1818 else
1819 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
1822 return result;
1827 * Sends a reply to an succesfully received STUN connectivity
1828 * check request. Implements parts of the ICE spec section 7.2 (STUN
1829 * Server Procedures).
1831 * @param agent context pointer
1832 * @param stream which stream (of the agent)
1833 * @param component which component (of the stream)
1834 * @param rcand remote candidate from which the request came, if NULL,
1835 * the response is sent immediately but no other processing is done
1836 * @param toaddr address to which reply is sent
1837 * @param socket the socket over which the request came
1838 * @param rbuf_len length of STUN message to send
1839 * @param rbuf buffer containing the STUN message to send
1840 * @param use_candidate whether the request had USE_CANDIDATE attribute
1842 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
1844 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)
1846 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
1849 gchar tmpbuf[INET6_ADDRSTRLEN];
1850 nice_address_to_string (toaddr, tmpbuf);
1851 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent,
1852 tmpbuf,
1853 nice_address_get_port (toaddr),
1854 socket->fileno,
1855 (unsigned)rbuf_len,
1856 rcand, component->id,
1857 (int)use_candidate);
1860 nice_socket_send (socket, toaddr, rbuf_len, (const gchar*)rbuf);
1862 if (rcand) {
1863 /* note: upon succesful check, make the reserve check immediately */
1864 priv_schedule_triggered_check (agent, stream, component, socket, rcand, use_candidate);
1866 if (use_candidate)
1867 priv_mark_pair_nominated (agent, stream, component, rcand);
1872 * Stores information of an incoming STUN connectivity check
1873 * for later use. This is only needed when a check is received
1874 * before we get information about the remote candidates (via
1875 * SDP or other signaling means).
1877 * @return non-zero on error, zero on success
1879 static int priv_store_pending_check (NiceAgent *agent, Component *component,
1880 const NiceAddress *from, NiceSocket *socket, uint8_t *username,
1881 uint16_t username_len, uint32_t priority, gboolean use_candidate)
1883 IncomingCheck *icheck;
1884 nice_debug ("Agent %p : Storing pending check.", agent);
1886 if (component->incoming_checks &&
1887 g_slist_length (component->incoming_checks) >=
1888 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
1889 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
1890 return -1;
1893 icheck = g_slice_new0 (IncomingCheck);
1894 if (icheck) {
1895 GSList *pending = g_slist_append (component->incoming_checks, icheck);
1896 if (pending) {
1897 component->incoming_checks = pending;
1898 icheck->from = *from;
1899 icheck->local_socket = socket;
1900 icheck->priority = priority;
1901 icheck->use_candidate = use_candidate;
1902 icheck->username_len = username_len;
1903 icheck->username = NULL;
1904 if (username_len > 0)
1905 icheck->username = g_memdup (username, username_len);
1906 return 0;
1910 return -1;
1914 * Adds a new pair, discovered from an incoming STUN response, to
1915 * the connectivity check list.
1917 * @return created pair, or NULL on fatal (memory allocation) errors
1919 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
1921 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
1922 if (pair) {
1923 Stream *stream = agent_find_stream (agent, stream_id);
1924 GSList *modified_list = g_slist_append (stream->conncheck_list, pair);
1925 if (modified_list) {
1926 stream->conncheck_list = modified_list;
1927 pair->agent = agent;
1928 pair->stream_id = stream_id;
1929 pair->component_id = component_id;;
1930 pair->local = local_cand;
1931 pair->remote = parent_pair->remote;
1932 pair->state = NICE_CHECK_DISCOVERED;
1933 nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair);
1934 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local_cand->foundation, parent_pair->remote->foundation);
1935 if (agent->controlling_mode == TRUE)
1936 pair->priority = nice_candidate_pair_priority (local_cand->priority, parent_pair->priority);
1937 else
1938 pair->priority = nice_candidate_pair_priority (parent_pair->priority, local_cand->priority);
1939 pair->nominated = FALSE;
1940 pair->controlling = agent->controlling_mode;
1941 nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation);
1942 return pair;
1946 return NULL;
1950 * Recalculates priorities of all candidate pairs. This
1951 * is required after a conflict in ICE roles.
1953 static void priv_recalculate_pair_priorities (NiceAgent *agent)
1955 GSList *i, *j;
1957 for (i = agent->streams; i; i = i->next) {
1958 Stream *stream = i->data;
1959 for (j = stream->conncheck_list; j; j = j->next) {
1960 CandidateCheckPair *p = j->data;
1961 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
1967 * Change the agent role if different from 'control'. Can be
1968 * initiated both by handling of incoming connectivity checks,
1969 * and by processing the responses to checks sent by us.
1971 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
1973 /* role conflict, change mode; wait for a new conn. check */
1974 if (control != agent->controlling_mode) {
1975 nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control);
1976 agent->controlling_mode = control;
1977 /* the pair priorities depend on the roles, so recalculation
1978 * is needed */
1979 priv_recalculate_pair_priorities (agent);
1981 else
1982 nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control);
1986 * Checks whether the mapped address in connectivity check response
1987 * matches any of the known local candidates. If not, apply the
1988 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
1990 * @param agent context pointer
1991 * @param stream which stream (of the agent)
1992 * @param component which component (of the stream)
1993 * @param p the connectivity check pair for which we got a response
1994 * @param socketptr socket used to send the reply
1995 * @param mapped_sockaddr mapped address in the response
1997 * @return pointer to a new pair if one was created, otherwise NULL
1999 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)
2001 CandidateCheckPair *new_pair = NULL;
2002 NiceAddress mapped;
2003 GSList *j;
2004 gboolean local_cand_matches = FALSE;
2006 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
2008 for (j = component->local_candidates; j; j = j->next) {
2009 NiceCandidate *cand = j->data;
2010 if (nice_address_equal (&mapped, &cand->addr)) {
2011 local_cand_matches = TRUE;
2012 break;
2016 if (local_cand_matches == TRUE) {
2017 /* note: this is same as "adding to VALID LIST" in the spec
2018 text */
2019 p->state = NICE_CHECK_SUCCEEDED;
2020 nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
2021 priv_conn_check_unfreeze_related (agent, stream, p);
2023 else {
2024 NiceCandidate *cand =
2025 discovery_add_peer_reflexive_candidate (agent,
2026 stream->id,
2027 component->id,
2028 &mapped,
2029 sockptr,
2030 local_candidate,
2031 remote_candidate);
2032 p->state = NICE_CHECK_FAILED;
2033 nice_debug ("Agent %p : pair %p state FAILED", agent, p);
2035 /* step: add a new discovered pair (see ICE 7.1.2.2.2
2036 "Constructing a Valid Pair" (ID-19)) */
2037 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p);
2038 nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair);
2041 return new_pair;
2045 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
2046 * check transaction. If found, the reply is processed. Implements
2047 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
2049 * @return TRUE if a matching transaction is found
2051 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)
2053 struct sockaddr sockaddr;
2054 socklen_t socklen = sizeof (sockaddr);
2055 GSList *i;
2056 StunUsageIceReturn res;
2057 gboolean trans_found = FALSE;
2058 StunTransactionId discovery_id;
2059 StunTransactionId response_id;
2060 stun_message_id (resp, response_id);
2062 for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) {
2063 CandidateCheckPair *p = i->data;
2065 if (p->stun_message.buffer) {
2066 stun_message_id (&p->stun_message, discovery_id);
2068 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2069 res = stun_usage_ice_conncheck_process (resp, &sockaddr, &socklen,
2070 agent_to_ice_compatibility (agent));
2071 nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
2072 "(controlling=%d).", agent, p, (int)res, agent->controlling_mode);
2074 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2075 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
2076 /* case: found a matching connectivity check request */
2078 CandidateCheckPair *ok_pair = NULL;
2080 nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p);
2081 p->stun_message.buffer = NULL;
2082 p->stun_message.buffer_len = 0;
2084 /* step: verify that response came from the same IP address we
2085 * sent the original request to (see 7.1.2.1. "Failure
2086 * Cases") */
2087 if (nice_address_equal (from, &p->remote->addr) != TRUE) {
2088 gchar tmpbuf[INET6_ADDRSTRLEN];
2089 gchar tmpbuf2[INET6_ADDRSTRLEN];
2091 p->state = NICE_CHECK_FAILED;
2092 nice_debug ("Agent %p : conncheck %p FAILED"
2093 " (mismatch of source address).", agent, p);
2094 nice_address_to_string (&p->remote->addr, tmpbuf);
2095 nice_address_to_string (from, tmpbuf2);
2096 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
2097 tmpbuf, nice_address_get_port (&p->remote->addr),
2098 tmpbuf2, nice_address_get_port (from));
2100 trans_found = TRUE;
2101 break;
2104 /* note: CONNECTED but not yet READY, see docs */
2106 /* step: handle the possible case of a peer-reflexive
2107 * candidate where the mapped-address in response does
2108 * not match any local candidate, see 7.1.2.2.1
2109 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
2111 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
2112 /* note: this is same as "adding to VALID LIST" in the spec
2113 text */
2114 p->state = NICE_CHECK_SUCCEEDED;
2115 nice_debug ("Agent %p : Mapped address not found."
2116 " conncheck %p SUCCEEDED.", agent, p);
2117 priv_conn_check_unfreeze_related (agent, stream, p);
2118 } else {
2119 ok_pair = priv_process_response_check_for_peer_reflexive(agent,
2120 stream, component, p, sockptr, &sockaddr,
2121 local_candidate, remote_candidate);
2125 if (!ok_pair)
2126 ok_pair = p;
2128 /* Do not step down to CONNECTED if we're already at state READY*/
2129 if (component->state != NICE_COMPONENT_STATE_READY) {
2130 /* step: notify the client of a new component state (must be done
2131 * before the possible check list state update step */
2132 agent_signal_component_state_change (agent,
2133 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2137 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
2138 Nominated Flag" (ID-19) */
2139 if (ok_pair->nominated == TRUE)
2140 priv_update_selected_pair (agent, component, ok_pair);
2142 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
2143 states" and 8.1.2 "Updating States", ID-19) */
2144 priv_update_check_list_state_for_ready (agent, stream, component);
2146 trans_found = TRUE;
2147 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2148 /* case: role conflict error, need to restart with new role */
2149 nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p);
2150 /* note: our role might already have changed due to an
2151 * incoming request, but if not, change role now;
2152 * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
2153 priv_check_for_role_conflict (agent, !p->controlling);
2155 p->stun_message.buffer = NULL;
2156 p->stun_message.buffer_len = 0;
2157 p->state = NICE_CHECK_WAITING;
2158 nice_debug ("Agent %p : pair %p state WAITING", agent, p);
2159 trans_found = TRUE;
2160 } else if (res == STUN_USAGE_ICE_RETURN_ERROR) {
2161 /* case: STUN error, the check STUN context was freed */
2162 nice_debug ("Agent %p : conncheck %p FAILED.", agent, p);
2163 p->stun_message.buffer = NULL;
2164 p->stun_message.buffer_len = 0;
2165 trans_found = TRUE;
2171 return trans_found;
2175 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2176 * transaction. If found, a reply is sent.
2178 * @return TRUE if a matching transaction is found
2180 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp)
2182 struct sockaddr sockaddr;
2183 socklen_t socklen = sizeof (sockaddr);
2184 struct sockaddr alternate;
2185 socklen_t alternatelen = sizeof (sockaddr);
2186 GSList *i;
2187 StunUsageBindReturn res;
2188 gboolean trans_found = FALSE;
2189 StunTransactionId discovery_id;
2190 StunTransactionId response_id;
2191 stun_message_id (resp, response_id);
2193 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
2194 CandidateDiscovery *d = i->data;
2196 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
2197 d->stun_message.buffer) {
2198 stun_message_id (&d->stun_message, discovery_id);
2200 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2201 res = stun_usage_bind_process (resp, &sockaddr, &socklen,
2202 &alternate, &alternatelen);
2203 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
2204 agent, d, (int)res);
2206 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
2207 /* handle alternate server */
2208 NiceAddress niceaddr;
2209 nice_address_set_from_sockaddr (&niceaddr, &alternate);
2210 d->server = niceaddr;
2212 d->pending = FALSE;
2213 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
2214 /* case: succesful binding discovery, create a new local candidate */
2215 NiceAddress niceaddr;
2216 nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
2218 discovery_add_server_reflexive_candidate (
2219 d->agent,
2220 d->stream->id,
2221 d->component->id,
2222 &niceaddr,
2223 d->nicesock);
2225 d->stun_message.buffer = NULL;
2226 d->stun_message.buffer_len = 0;
2227 d->done = TRUE;
2228 trans_found = TRUE;
2229 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
2230 /* case: STUN error, the check STUN context was freed */
2231 d->stun_message.buffer = NULL;
2232 d->stun_message.buffer_len = 0;
2233 d->done = TRUE;
2234 trans_found = TRUE;
2240 return trans_found;
2244 static CandidateRefresh *
2245 priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand,
2246 guint lifetime)
2248 CandidateRefresh *cand;
2249 NiceAgent *agent = cdisco->agent;
2250 GSList *modified_list;
2252 cand = g_slice_new0 (CandidateRefresh);
2253 if (cand) {
2254 modified_list = g_slist_append (agent->refresh_list, cand);
2256 if (modified_list) {
2257 cand->nicesock = cdisco->nicesock;
2258 cand->relay_socket = relay_cand->sockptr;
2259 cand->server = cdisco->server;
2260 cand->turn = cdisco->turn;
2261 cand->stream = cdisco->stream;
2262 cand->component = cdisco->component;
2263 cand->agent = cdisco->agent;
2264 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
2265 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
2266 agent, cand, (lifetime - 60) * 1000);
2267 agent->refresh_list = modified_list;
2269 /* step: also start the refresh timer */
2270 /* refresh should be sent 1 minute before it expires */
2271 cand->timer_source =
2272 agent_timeout_add_with_context (agent, (lifetime - 60) * 1000,
2273 priv_turn_allocate_refresh_tick, cand);
2275 nice_debug ("timer source is : %d", cand->timer_source);
2279 return cand;
2283 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2284 * transaction. If found, a reply is sent.
2286 * @return TRUE if a matching transaction is found
2288 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
2290 struct sockaddr sockaddr;
2291 socklen_t socklen = sizeof (sockaddr);
2292 struct sockaddr alternate;
2293 socklen_t alternatelen = sizeof (alternate);
2294 struct sockaddr relayaddr;
2295 socklen_t relayaddrlen = sizeof (relayaddr);
2296 uint32_t lifetime;
2297 uint32_t bandwidth;
2298 GSList *i;
2299 StunUsageTurnReturn res;
2300 gboolean trans_found = FALSE;
2301 StunTransactionId discovery_id;
2302 StunTransactionId response_id;
2303 stun_message_id (resp, response_id);
2305 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
2306 CandidateDiscovery *d = i->data;
2308 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
2309 d->stun_message.buffer) {
2310 stun_message_id (&d->stun_message, discovery_id);
2312 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
2313 res = stun_usage_turn_process (resp,
2314 &relayaddr, &relayaddrlen, &sockaddr, &socklen, &alternate, &alternatelen,
2315 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
2316 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
2317 agent, d, (int)res);
2319 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
2320 /* handle alternate server */
2321 nice_address_set_from_sockaddr (&d->server, &alternate);
2322 nice_address_set_from_sockaddr (&d->turn->server, &alternate);
2324 d->pending = FALSE;
2325 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
2326 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
2327 /* case: succesful allocate, create a new local candidate */
2328 NiceAddress niceaddr;
2329 NiceCandidate *relay_cand;
2331 /* We also received our mapped address */
2332 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
2333 nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
2335 discovery_add_server_reflexive_candidate (
2336 d->agent,
2337 d->stream->id,
2338 d->component->id,
2339 &niceaddr,
2340 d->nicesock);
2343 nice_address_set_from_sockaddr (&niceaddr, &relayaddr);
2344 relay_cand = discovery_add_relay_candidate (
2345 d->agent,
2346 d->stream->id,
2347 d->component->id,
2348 &niceaddr,
2349 d->nicesock,
2350 d->turn);
2352 priv_add_new_turn_refresh (d, relay_cand, lifetime);
2355 d->stun_message.buffer = NULL;
2356 d->stun_message.buffer_len = 0;
2357 d->done = TRUE;
2358 trans_found = TRUE;
2359 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2360 int code = -1;
2361 uint8_t *sent_realm = NULL;
2362 uint8_t *recv_realm = NULL;
2363 uint16_t sent_realm_len = 0;
2364 uint16_t recv_realm_len = 0;
2366 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
2367 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2368 recv_realm = (uint8_t *) stun_message_find (resp,
2369 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2371 /* check for unauthorized error response */
2372 if (agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
2373 stun_message_get_class (resp) == STUN_ERROR &&
2374 stun_message_find_error (resp, &code) ==
2375 STUN_MESSAGE_RETURN_SUCCESS &&
2376 recv_realm != NULL && recv_realm_len > 0) {
2378 if (code == 438 ||
2379 (code == 401 &&
2380 !(recv_realm_len == sent_realm_len &&
2381 sent_realm != NULL &&
2382 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2383 d->stun_resp_msg = *resp;
2384 memcpy (d->stun_resp_buffer, resp->buffer,
2385 stun_message_length (resp));
2386 d->stun_resp_msg.buffer = d->stun_resp_buffer;
2387 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
2388 d->pending = FALSE;
2389 } else {
2390 /* case: a real unauthorized error */
2391 d->stun_message.buffer = NULL;
2392 d->stun_message.buffer_len = 0;
2393 d->done = TRUE;
2395 } else {
2396 /* case: STUN error, the check STUN context was freed */
2397 d->stun_message.buffer = NULL;
2398 d->stun_message.buffer_len = 0;
2399 d->done = TRUE;
2401 trans_found = TRUE;
2407 return trans_found;
2412 * Tries to match STUN reply in 'buf' to an existing STUN discovery
2413 * transaction. If found, a reply is sent.
2415 * @return TRUE if a matching transaction is found
2417 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
2419 uint32_t lifetime;
2420 GSList *i;
2421 StunUsageTurnReturn res;
2422 gboolean trans_found = FALSE;
2423 StunTransactionId refresh_id;
2424 StunTransactionId response_id;
2425 stun_message_id (resp, response_id);
2427 for (i = agent->refresh_list; i && trans_found != TRUE; i = i->next) {
2428 CandidateRefresh *cand = i->data;
2430 if (cand->stun_message.buffer) {
2431 stun_message_id (&cand->stun_message, refresh_id);
2433 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
2434 res = stun_usage_turn_refresh_process (resp,
2435 &lifetime, agent_to_turn_compatibility (cand->agent));
2436 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d.",
2437 agent, cand, (int)res);
2438 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
2439 /* refresh should be sent 1 minute before it expires */
2440 cand->timer_source =
2441 agent_timeout_add_with_context (cand->agent, (lifetime - 60) * 1000,
2442 priv_turn_allocate_refresh_tick, cand);
2444 g_source_destroy (cand->tick_source);
2445 g_source_unref (cand->tick_source);
2446 cand->tick_source = NULL;
2447 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
2448 int code = -1;
2449 uint8_t *sent_realm = NULL;
2450 uint8_t *recv_realm = NULL;
2451 uint16_t sent_realm_len = 0;
2452 uint16_t recv_realm_len = 0;
2454 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
2455 STUN_ATTRIBUTE_REALM, &sent_realm_len);
2456 recv_realm = (uint8_t *) stun_message_find (resp,
2457 STUN_ATTRIBUTE_REALM, &recv_realm_len);
2459 /* check for unauthorized error response */
2460 if (cand->agent->compatibility == NICE_COMPATIBILITY_DRAFT19 &&
2461 stun_message_get_class (resp) == STUN_ERROR &&
2462 stun_message_find_error (resp, &code) ==
2463 STUN_MESSAGE_RETURN_SUCCESS &&
2464 recv_realm != NULL && recv_realm_len > 0) {
2466 if (code == 438 ||
2467 (code == 401 &&
2468 !(recv_realm_len == sent_realm_len &&
2469 sent_realm != NULL &&
2470 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
2471 cand->stun_resp_msg = *resp;
2472 memcpy (cand->stun_resp_buffer, resp->buffer,
2473 stun_message_length (resp));
2474 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
2475 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
2476 priv_turn_allocate_refresh_tick_unlocked (cand);
2477 } else {
2478 /* case: a real unauthorized error */
2479 refresh_cancel (cand);
2481 } else {
2482 /* case: STUN error, the check STUN context was freed */
2483 refresh_cancel (cand);
2485 trans_found = TRUE;
2491 return trans_found;
2495 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
2496 Component *component, StunMessage *resp)
2498 StunTransactionId conncheck_id;
2499 StunTransactionId response_id;
2500 stun_message_id (resp, response_id);
2502 if (component->selected_pair.keepalive.stun_message.buffer) {
2503 stun_message_id (&component->selected_pair.keepalive.stun_message,
2504 conncheck_id);
2505 if (memcmp (conncheck_id, response_id, sizeof(StunTransactionId)) == 0) {
2506 nice_debug ("Agent %p : Keepalive for selected pair received.",
2507 agent);
2508 if (component->selected_pair.keepalive.tick_source) {
2509 g_source_destroy (component->selected_pair.keepalive.tick_source);
2510 g_source_unref (component->selected_pair.keepalive.tick_source);
2511 component->selected_pair.keepalive.tick_source = NULL;
2513 component->selected_pair.keepalive.stun_message.buffer = NULL;
2514 return TRUE;
2518 return FALSE;
2522 typedef struct {
2523 NiceAgent *agent;
2524 Stream *stream;
2525 Component *component;
2526 uint8_t *password;
2527 } conncheck_validater_data;
2529 static bool conncheck_stun_validater (StunAgent *agent,
2530 StunMessage *message, uint8_t *username, uint16_t username_len,
2531 uint8_t **password, size_t *password_len, void *user_data)
2533 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
2534 GSList *i;
2535 uint8_t uname[NICE_STREAM_MAX_UNAME];
2536 guint uname_len = 0;
2538 for (i = data->component->local_candidates; i; i = i->next) {
2539 NiceCandidate *cand = i->data;
2540 gchar *ufrag = NULL;
2541 gsize ufrag_len;
2543 if (cand->username)
2544 ufrag = cand->username;
2545 else if (data->stream)
2546 ufrag = data->stream->local_ufrag;
2547 ufrag_len = strlen (ufrag);
2549 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2550 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
2552 if (ufrag_len <= NICE_STREAM_MAX_UNAME) {
2553 memcpy (uname, ufrag, ufrag_len);
2554 uname_len = ufrag_len;
2557 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN)
2558 g_free (ufrag);
2560 stun_debug ("Comparing username '");
2561 stun_debug_bytes (username, username_len);
2562 stun_debug ("' (%d) with '", username_len);
2563 stun_debug_bytes (uname, uname_len);
2564 stun_debug ("' (%d) : %d\n",
2565 uname_len, memcmp (username, uname, uname_len));
2566 if (uname_len > 0 && username_len >= uname_len &&
2567 memcmp (username, uname, uname_len) == 0) {
2568 gchar *pass = NULL;
2570 if (cand->password)
2571 pass = cand->password;
2572 else
2573 pass = data->stream->local_password;
2575 *password = (uint8_t *) pass;
2576 *password_len = strlen (pass);
2578 if (data->agent->compatibility == NICE_COMPATIBILITY_MSN) {
2579 data->password = g_base64_decode (pass, password_len);
2580 *password = data->password;
2583 stun_debug ("Found valid username, returning password: '%s'\n", *password);
2584 return TRUE;
2588 return FALSE;
2593 * Processing an incoming STUN message.
2595 * @param agent self pointer
2596 * @param stream stream the packet is related to
2597 * @param component component the packet is related to
2598 * @param socket socket from which the packet was received
2599 * @param from address of the sender
2600 * @param buf message contents
2601 * @param buf message length
2603 * @pre contents of 'buf' is a STUN message
2605 * @return XXX (what FALSE means exactly?)
2607 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
2608 Component *component, NiceSocket *socket, const NiceAddress *from,
2609 gchar *buf, guint len)
2611 struct sockaddr sockaddr;
2612 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
2613 ssize_t res;
2614 size_t rbuf_len = sizeof (rbuf);
2615 bool control = agent->controlling_mode;
2616 uint8_t uname[NICE_STREAM_MAX_UNAME];
2617 guint uname_len;
2618 uint8_t *username;
2619 uint16_t username_len;
2620 StunMessage req;
2621 StunMessage msg;
2622 StunValidationStatus valid;
2623 conncheck_validater_data validater_data = {agent, stream, component, NULL};
2624 GSList *i, *j;
2625 NiceCandidate *remote_candidate = NULL;
2626 NiceCandidate *remote_candidate2 = NULL;
2627 NiceCandidate *local_candidate = NULL;
2628 gboolean discovery_msg = FALSE;
2630 nice_address_copy_to_sockaddr (from, &sockaddr);
2632 /* note: contents of 'buf' already validated, so it is
2633 * a valid and fully received STUN message */
2635 #ifndef NDEBUG
2637 gchar tmpbuf[INET6_ADDRSTRLEN];
2638 nice_address_to_string (from, tmpbuf);
2639 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
2640 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
2642 #endif
2644 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
2646 valid = stun_agent_validate (&agent->stun_agent, &req,
2647 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2649 /* Check for discovery candidates stun agents */
2650 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2651 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2652 for (i = agent->discovery_list; i; i = i->next) {
2653 CandidateDiscovery *d = i->data;
2654 if (d->stream == stream && d->component == component &&
2655 d->nicesock == socket) {
2656 valid = stun_agent_validate (&d->stun_agent, &req,
2657 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2659 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2660 continue;
2662 discovery_msg = TRUE;
2663 break;
2667 /* Check for relay refresh stun agents */
2668 if (valid == STUN_VALIDATION_BAD_REQUEST ||
2669 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
2670 for (i = agent->refresh_list; i; i = i->next) {
2671 CandidateRefresh *r = i->data;
2672 nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream,
2673 stream, r->component, component, r->nicesock, r->relay_socket, socket);
2674 if (r->stream == stream && r->component == component &&
2675 (r->nicesock == socket || r->relay_socket == socket)) {
2676 valid = stun_agent_validate (&r->stun_agent, &req,
2677 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
2678 nice_debug ("Validating gave %d", valid);
2679 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
2680 continue;
2681 discovery_msg = TRUE;
2682 break;
2688 if (validater_data.password)
2689 g_free (validater_data.password);
2691 if (valid == STUN_VALIDATION_NOT_STUN ||
2692 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
2693 valid == STUN_VALIDATION_BAD_REQUEST)
2695 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
2696 agent);
2697 return FALSE;
2700 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
2701 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
2702 rbuf_len = stun_agent_build_unknown_attributes_error (&agent->stun_agent,
2703 &msg, rbuf, rbuf_len, &req);
2704 if (len == 0)
2705 return FALSE;
2707 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2708 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2710 return TRUE;
2713 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
2714 nice_debug ("Agent %p : Integrity check failed.", agent);
2716 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2717 &req, STUN_ERROR_UNAUTHORIZED)) {
2718 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2719 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2720 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2722 return TRUE;
2724 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
2725 nice_debug ("Agent %p : Integrity check failed.", agent);
2726 if (stun_agent_init_error (&agent->stun_agent, &msg, rbuf, rbuf_len,
2727 &req, STUN_ERROR_BAD_REQUEST)) {
2728 rbuf_len = stun_agent_finish_message (&agent->stun_agent, &msg, NULL, 0);
2729 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN)
2730 nice_socket_send (socket, from, rbuf_len, (const gchar*)rbuf);
2732 return TRUE;
2735 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
2736 &username_len);
2738 for (i = component->remote_candidates; i; i = i->next) {
2739 NiceCandidate *cand = i->data;
2740 if (nice_address_equal (from, &cand->addr)) {
2741 remote_candidate = cand;
2742 break;
2746 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2747 agent->compatibility == NICE_COMPATIBILITY_MSN) {
2748 /* We need to find which local candidate was used */
2749 for (i = component->remote_candidates;
2750 i != NULL && remote_candidate2 == NULL; i = i->next) {
2751 for (j = component->local_candidates; j; j = j->next) {
2752 gboolean inbound = TRUE;
2753 NiceCandidate *rcand = i->data;
2754 NiceCandidate *lcand = j->data;
2756 /* If we receive a response, then the username is local:remote */
2757 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
2758 if (stun_message_get_class (&req) == STUN_REQUEST ||
2759 stun_message_get_class (&req) == STUN_INDICATION) {
2760 inbound = TRUE;
2761 } else {
2762 inbound = FALSE;
2766 uname_len = priv_create_username (agent, stream,
2767 component->id, rcand, lcand,
2768 uname, sizeof (uname), inbound);
2770 stun_debug ("Comparing username '");
2771 stun_debug_bytes (username, username? username_len : 0);
2772 stun_debug ("' (%d) with '", username_len);
2773 stun_debug_bytes (uname, uname_len);
2774 stun_debug ("' (%d) : %d\n",
2775 uname_len, username && uname_len == username_len &&
2776 memcmp (username, uname, uname_len) == 0);
2778 if (username &&
2779 uname_len == username_len &&
2780 memcmp (uname, username, username_len) == 0) {
2781 local_candidate = lcand;
2782 remote_candidate2 = rcand;
2783 break;
2789 if (component->remote_candidates &&
2790 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2791 local_candidate == NULL &&
2792 discovery_msg == FALSE) {
2793 /* if we couldn't match the username and the stun agent has
2794 IGNORE_CREDENTIALS then we have an integrity check failing.
2795 This could happen with the race condition of receiving connchecks
2796 before the remote candidates are added. Just drop the message, and let
2797 the retransmissions make it work. */
2798 nice_debug ("Agent %p : Username check failed.", agent);
2799 return TRUE;
2802 if (valid != STUN_VALIDATION_SUCCESS) {
2803 nice_debug ("Agent %p : STUN message is unsuccessfull %d, ignoring", agent, valid);
2804 return FALSE;
2808 if (stun_message_get_class (&req) == STUN_REQUEST) {
2809 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2810 if (local_candidate && remote_candidate2) {
2811 username = (uint8_t *) stun_message_find (&req,
2812 STUN_ATTRIBUTE_USERNAME, &username_len);
2813 uname_len = priv_create_username (agent, stream,
2814 component->id, remote_candidate2, local_candidate,
2815 uname, sizeof (uname), FALSE);
2816 memcpy (username, uname, username_len);
2817 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
2818 &req.key_len);
2819 } else {
2820 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
2821 "Ignoring request", agent);
2822 return TRUE;
2826 rbuf_len = sizeof (rbuf);
2827 res = stun_usage_ice_conncheck_create_reply (&agent->stun_agent, &req,
2828 &msg, rbuf, &rbuf_len, &sockaddr, sizeof (sockaddr),
2829 &control, agent->tie_breaker,
2830 agent_to_ice_compatibility (agent));
2832 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
2833 g_free (req.key);
2836 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
2837 priv_check_for_role_conflict (agent, control);
2839 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
2840 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
2841 /* case 1: valid incoming request, send a reply/error */
2842 bool use_candidate =
2843 stun_usage_ice_conncheck_use_candidate (&req);
2844 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
2846 if (agent->controlling_mode ||
2847 agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
2848 agent->compatibility == NICE_COMPATIBILITY_MSN)
2849 use_candidate = TRUE;
2851 if (stream->initial_binding_request_received != TRUE)
2852 agent_signal_initial_binding_request_received (agent, stream);
2854 if (component->remote_candidates && remote_candidate == NULL) {
2855 nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
2856 "peer-reflexive candidate.", agent);
2857 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
2858 agent, stream, component, priority, from, socket,
2859 local_candidate,
2860 remote_candidate2 ? remote_candidate2 : remote_candidate);
2861 if(remote_candidate)
2862 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
2865 priv_reply_to_conn_check (agent, stream, component, remote_candidate,
2866 from, socket, rbuf_len, rbuf, use_candidate);
2868 if (component->remote_candidates == NULL) {
2869 /* case: We've got a valid binding request to a local candidate
2870 * but we do not yet know remote credentials nor
2871 * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
2872 * immediately but postpone all other processing until
2873 * we get information about the remote candidates */
2875 /* step: send a reply immediately but postpone other processing */
2876 priv_store_pending_check (agent, component, from, socket,
2877 username, username_len, priority, use_candidate);
2879 } else {
2880 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
2881 agent, strerror(errno));
2882 return FALSE;
2884 } else {
2885 /* case 2: not a new request, might be a reply... */
2886 gboolean trans_found = FALSE;
2888 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
2890 /* step: let's try to match the response to an existing check context */
2891 if (trans_found != TRUE)
2892 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
2893 component, socket, from, local_candidate, remote_candidate, &req);
2895 /* step: let's try to match the response to an existing discovery */
2896 if (trans_found != TRUE)
2897 trans_found = priv_map_reply_to_discovery_request (agent, &req);
2899 /* step: let's try to match the response to an existing turn allocate */
2900 if (trans_found != TRUE)
2901 trans_found = priv_map_reply_to_relay_request (agent, &req);
2903 /* step: let's try to match the response to an existing turn refresh */
2904 if (trans_found != TRUE)
2905 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
2907 /* step: let's try to match the response to an existing keepalive conncheck */
2908 if (trans_found != TRUE)
2909 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
2910 &req);
2912 if (trans_found != TRUE)
2913 nice_debug ("Agent %p : Unable to match to an existing transaction, "
2914 "probably a keepalive.", agent);
2917 return TRUE;