2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2006-2009 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
26 * Youness Alaoui, Collabora Ltd.
27 * Dafydd Harries, Collabora Ltd.
29 * Alternatively, the contents of this file may be used under the terms of the
30 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
31 * case the provisions of LGPL are applicable instead of those above. If you
32 * wish to allow use of your version of this file only under the terms of the
33 * LGPL and not to allow others to use your version of this file under the
34 * MPL, indicate your decision by deleting the provisions above and replace
35 * them with the notice and other provisions required by the LGPL. If you do
36 * not delete the provisions above, a recipient may use your version of this
37 * file under either the MPL or the LGPL.
42 * @brief ICE connectivity checks
57 #include "agent-priv.h"
58 #include "conncheck.h"
59 #include "discovery.h"
60 #include "stun/stun5389.h"
61 #include "stun/usages/ice.h"
62 #include "stun/usages/bind.h"
63 #include "stun/usages/turn.h"
65 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream);
66 static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component);
67 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand);
68 static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand);
69 static size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
70 guint component_id, NiceCandidate *remote, NiceCandidate *local,
71 uint8_t *dest, guint dest_len, gboolean inbound);
72 static size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
73 NiceCandidate *remote, uint8_t **password);
74 static void candidate_check_pair_free (NiceAgent *agent,
75 CandidateCheckPair *pair);
76 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
77 NiceAgent *agent, guint stream_id, NiceComponent *component,
78 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state);
79 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
82 static gint64 priv_timer_remainder (gint64 timer, gint64 now)
87 return (timer - now) / 1000;
91 priv_state_to_gchar (NiceCheckState state)
94 case NICE_CHECK_WAITING:
96 case NICE_CHECK_IN_PROGRESS:
98 case NICE_CHECK_SUCCEEDED:
100 case NICE_CHECK_FAILED:
102 case NICE_CHECK_FROZEN:
104 case NICE_CHECK_DISCOVERED:
107 g_assert_not_reached ();
112 priv_state_to_string (NiceCheckState state)
115 case NICE_CHECK_WAITING:
117 case NICE_CHECK_IN_PROGRESS:
118 return "IN_PROGRESS";
119 case NICE_CHECK_SUCCEEDED:
121 case NICE_CHECK_FAILED:
123 case NICE_CHECK_FROZEN:
125 case NICE_CHECK_DISCOVERED:
128 g_assert_not_reached ();
132 #define SET_PAIR_STATE( a, p, s ) G_STMT_START{\
135 nice_debug ("Agent %p : pair %p state %s (%s)", \
136 a, p, priv_state_to_string (s), G_STRFUNC); \
140 priv_ice_return_to_string (StunUsageIceReturn ice_return)
142 switch (ice_return) {
143 case STUN_USAGE_ICE_RETURN_SUCCESS:
145 case STUN_USAGE_ICE_RETURN_ERROR:
147 case STUN_USAGE_ICE_RETURN_INVALID:
149 case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT:
150 return "role conflict";
151 case STUN_USAGE_ICE_RETURN_INVALID_REQUEST:
152 return "invalid request";
153 case STUN_USAGE_ICE_RETURN_INVALID_METHOD:
154 return "invalid method";
155 case STUN_USAGE_ICE_RETURN_MEMORY_ERROR:
156 return "memory error";
157 case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS:
158 return "invalid address";
159 case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS:
160 return "no mapped address";
162 g_assert_not_reached ();
167 priv_candidate_type_to_string (NiceCandidateType type)
170 case NICE_CANDIDATE_TYPE_HOST:
172 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
174 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
176 case NICE_CANDIDATE_TYPE_RELAYED:
179 g_assert_not_reached ();
184 priv_candidate_transport_to_string (NiceCandidateTransport transport)
187 case NICE_CANDIDATE_TRANSPORT_UDP:
189 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
191 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
193 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
196 g_assert_not_reached ();
201 priv_socket_type_to_string (NiceSocketType type)
204 case NICE_SOCKET_TYPE_UDP_BSD:
206 case NICE_SOCKET_TYPE_TCP_BSD:
208 case NICE_SOCKET_TYPE_PSEUDOSSL:
210 case NICE_SOCKET_TYPE_HTTP:
212 case NICE_SOCKET_TYPE_SOCKS5:
214 case NICE_SOCKET_TYPE_UDP_TURN:
216 case NICE_SOCKET_TYPE_UDP_TURN_OVER_TCP:
218 case NICE_SOCKET_TYPE_TCP_ACTIVE:
220 case NICE_SOCKET_TYPE_TCP_PASSIVE:
222 case NICE_SOCKET_TYPE_TCP_SO:
225 g_assert_not_reached ();
230 * Dump the component list of incoming checks
233 print_component_incoming_checks (NiceAgent *agent, NiceStream *stream,
234 NiceComponent *component)
238 for (i = component->incoming_checks.head; i; i = i->next) {
239 IncomingCheck *icheck = i->data;
240 gchar tmpbuf1[INET6_ADDRSTRLEN] = {0};
241 gchar tmpbuf2[INET6_ADDRSTRLEN] = {0};
243 nice_address_to_string (&icheck->local_socket->addr, tmpbuf1);
244 nice_address_to_string (&icheck->from, tmpbuf2);
245 nice_debug ("Agent %p : *** sc=%d/%d : icheck %p : "
246 "sock %s [%s]:%u > [%s]:%u",
247 agent, stream->id, component->id, icheck,
248 priv_socket_type_to_string (icheck->local_socket->type),
249 tmpbuf1, nice_address_get_port (&icheck->local_socket->addr),
250 tmpbuf2, nice_address_get_port (&icheck->from));
255 * Dump the conncheck lists of the agent
258 priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail)
264 if (!nice_debug_is_verbose ())
267 now = g_get_monotonic_time ();
269 #define PRIORITY_LEN 32
271 nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
272 agent, where, detail ? detail : "");
273 nice_debug ("Agent %p : *** agent nomination mode %s, %s",
274 agent, agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
275 "aggressive" : "regular",
276 agent->controlling_mode ? "controlling" : "controlled");
277 for (i = agent->streams; i ; i = i->next) {
278 NiceStream *stream = i->data;
279 for (j = 1; j <= stream->n_components; j++) {
280 NiceComponent *component;
281 for (k = stream->conncheck_list; k ; k = k->next) {
282 CandidateCheckPair *pair = k->data;
283 if (pair->component_id == j) {
284 gchar local_addr[INET6_ADDRSTRLEN];
285 gchar remote_addr[INET6_ADDRSTRLEN];
286 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
288 nice_address_to_string (&pair->local->addr, local_addr);
289 nice_address_to_string (&pair->remote->addr, remote_addr);
290 nice_candidate_pair_priority_to_string (pair->priority, priority);
292 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
293 "f=%s t=%s:%s sock=%s "
294 "%s:[%s]:%u > %s:[%s]:%u prio=%s/%08x state=%c%s%s%s%s",
295 agent, pair->stream_id, pair->component_id, pair,
297 priv_candidate_type_to_string (pair->local->type),
298 priv_candidate_type_to_string (pair->remote->type),
299 priv_socket_type_to_string (pair->sockptr->type),
300 priv_candidate_transport_to_string (pair->local->transport),
301 local_addr, nice_address_get_port (&pair->local->addr),
302 priv_candidate_transport_to_string (pair->remote->transport),
303 remote_addr, nice_address_get_port (&pair->remote->addr),
304 priority, pair->stun_priority,
305 priv_state_to_gchar (pair->state),
306 pair->valid ? "V" : "",
307 pair->nominated ? "N" : "",
308 pair->use_candidate_on_next_check ? "C" : "",
309 g_slist_find (agent->triggered_check_queue, pair) ? "T" : "");
311 for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) {
312 StunTransaction *stun = l->data;
313 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
314 "stun#=%d timer=%d/%d %" G_GINT64_FORMAT "/%dms buf=%p %s",
315 agent, pair->stream_id, pair->component_id, pair, m,
316 stun->timer.retransmissions, stun->timer.max_retransmissions,
317 stun->timer.delay - priv_timer_remainder (stun->next_tick, now),
319 stun->message.buffer,
320 (m == 0 && pair->retransmit) ? "(R)" : "");
324 if (agent_find_component (agent, stream->id, j, NULL, &component))
325 print_component_incoming_checks (agent, stream, component);
330 /* Add the pair to the triggered checks list, if not already present
333 priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
337 if (agent->triggered_check_queue == NULL ||
338 g_slist_find (agent->triggered_check_queue, pair) == NULL)
339 agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair);
342 /* Remove the pair from the triggered checks list
345 priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
348 agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair);
351 /* Get the pair from the triggered checks list
353 static CandidateCheckPair *
354 priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
356 CandidateCheckPair *pair = NULL;
358 if (agent->triggered_check_queue) {
359 pair = (CandidateCheckPair *)agent->triggered_check_queue->data;
360 priv_remove_pair_from_triggered_check_queue (agent, pair);
366 * Finds the next connectivity check in WAITING state.
368 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
372 /* note: list is sorted in priority order to first waiting check has
373 * the highest priority */
374 for (i = conn_check_list; i ; i = i->next) {
375 CandidateCheckPair *p = i->data;
376 if (p->state == NICE_CHECK_WAITING)
384 * Initiates a new connectivity check for a ICE candidate pair.
386 * @return TRUE on success, FALSE on error
389 priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
391 SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS);
392 if (conn_check_send (agent, pair)) {
393 SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED);
400 * Unfreezes the next connectivity check in the list. Follows the
401 * algorithm defined in sect 6.1.2.6 (Computing Candidate Pair States)
402 * and sect 6.1.4.2 (Performing Connectivity Checks) of the ICE spec
405 * Note that this algorithm is slightly simplified compared to previous
406 * version of the spec (RFC5245), and this new version is now
409 * @return TRUE on success, and FALSE if no frozen candidates were found.
412 priv_conn_check_unfreeze_next (NiceAgent *agent)
415 GSList *foundation_list = NULL;
416 gboolean result = FALSE;
418 /* While a pair in state waiting exists, we do nothing */
419 for (i = agent->streams; i ; i = i->next) {
420 NiceStream *s = i->data;
421 for (j = s->conncheck_list; j ; j = j->next) {
422 CandidateCheckPair *p = j->data;
424 if (p->state == NICE_CHECK_WAITING)
429 /* When there are no more pairs in waiting state, we unfreeze some
430 * pairs, so that we get a single waiting pair per foundation.
432 for (i = agent->streams; i ; i = i->next) {
433 NiceStream *s = i->data;
434 for (j = s->conncheck_list; j ; j = j->next) {
435 CandidateCheckPair *p = j->data;
437 if (g_slist_find_custom (foundation_list, p->foundation,
438 (GCompareFunc)strcmp))
441 if (p->state == NICE_CHECK_FROZEN) {
442 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
443 agent, p, p->stream_id, p->component_id, p->foundation);
444 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
445 foundation_list = g_slist_prepend (foundation_list, p->foundation);
450 g_slist_free (foundation_list);
452 /* We dump the conncheck list when something interesting happened, ie
453 * when we unfroze some pairs.
456 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
462 * Unfreezes the related connectivity check in the list after
463 * check 'success_check' has successfully completed.
465 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
467 * Note that this algorithm is slightly simplified compared to previous
468 * version of the spec (RFC5245)
470 * @param agent context
471 * @param pair a pair, whose connectivity check has just succeeded
475 conn_check_unfreeze_related (NiceAgent *agent, CandidateCheckPair *pair)
478 gboolean result = FALSE;
481 g_assert (pair->state == NICE_CHECK_SUCCEEDED);
483 for (i = agent->streams; i ; i = i->next) {
484 NiceStream *s = i->data;
485 for (j = s->conncheck_list; j ; j = j->next) {
486 CandidateCheckPair *p = j->data;
488 /* The states for all other Frozen candidates pairs in all
489 * checklists with the same foundation is set to waiting
491 if (p->state == NICE_CHECK_FROZEN &&
492 strncmp (p->foundation, pair->foundation,
493 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
494 nice_debug ("Agent %p : Unfreezing check %p "
495 "(after successful check %p).", agent, p, pair);
496 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
501 /* We dump the conncheck list when something interesting happened, ie
502 * when we unfroze some pairs.
505 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
509 * Unfreezes this connectivity check if its foundation is the same than
510 * the foundation of an already succeeded pair.
512 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
514 * @param agent context
515 * @param pair a pair, whose state is frozen
519 priv_conn_check_unfreeze_maybe (NiceAgent *agent, CandidateCheckPair *pair)
522 gboolean result = FALSE;
525 g_assert (pair->state == NICE_CHECK_FROZEN);
527 for (i = agent->streams; i ; i = i->next) {
528 NiceStream *s = i->data;
529 for (j = s->conncheck_list; j ; j = j->next) {
530 CandidateCheckPair *p = j->data;
532 if (p->state == NICE_CHECK_SUCCEEDED &&
533 strncmp (p->foundation, pair->foundation,
534 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
535 nice_debug ("Agent %p : Unfreezing check %p "
536 "(after successful check %p).", agent, pair, p);
537 SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
542 /* We dump the conncheck list when something interesting happened, ie
543 * when we unfroze some pairs.
546 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
550 conn_check_stun_transactions_count (NiceAgent *agent)
555 for (i = agent->streams; i ; i = i->next) {
556 NiceStream *s = i->data;
557 for (j = s->conncheck_list; j ; j = j->next) {
558 CandidateCheckPair *p = j->data;
560 if (p->stun_transactions)
561 count += g_slist_length (p->stun_transactions);
568 * Create a new STUN transaction and add it to the list
569 * of ongoing stun transactions of a pair.
571 * @pair the pair the new stun transaction should be added to.
572 * @return the created stun transaction.
574 static StunTransaction *
575 priv_add_stun_transaction (CandidateCheckPair *pair)
577 StunTransaction *stun = g_slice_new0 (StunTransaction);
578 pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun);
579 pair->retransmit = TRUE;
584 * Forget a STUN transaction.
586 * @data the stun transaction to be forgotten.
587 * @user_data the component contained the concerned stun agent.
590 priv_forget_stun_transaction (gpointer data, gpointer user_data)
592 StunTransaction *stun = data;
593 NiceComponent *component = user_data;
594 StunTransactionId id;
596 if (stun->message.buffer != NULL) {
597 stun_message_id (&stun->message, id);
598 stun_agent_forget_transaction (&component->stun_agent, id);
603 priv_free_stun_transaction (gpointer data)
605 g_slice_free (StunTransaction, data);
609 * Remove a STUN transaction from a pair, and forget it
610 * from the related component stun agent.
612 * @pair the pair the stun transaction should be removed from.
613 * @stun the stun transaction to be removed.
614 * @component the component containing the stun agent used to
615 * forget the stun transaction.
618 priv_remove_stun_transaction (CandidateCheckPair *pair,
619 StunTransaction *stun, NiceComponent *component)
621 priv_forget_stun_transaction (stun, component);
622 pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun);
623 priv_free_stun_transaction (stun);
624 if (pair->stun_transactions == NULL)
625 pair->retransmit = FALSE;
629 * Remove all STUN transactions from a pair, and forget them
630 * from the related component stun agent.
632 * @pair the pair the stun list should be cleared.
633 * @component the component containing the stun agent used to
634 * forget the stun transactions.
637 priv_free_all_stun_transactions (CandidateCheckPair *pair,
638 NiceComponent *component)
641 g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component);
642 g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction);
643 pair->stun_transactions = NULL;
644 pair->retransmit = FALSE;
648 candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
650 NiceComponent *component;
652 component = nice_stream_find_component_by_id (stream, p->component_id);
653 SET_PAIR_STATE (agent, p, NICE_CHECK_FAILED);
654 priv_free_all_stun_transactions (p, component);
658 * Helper function for connectivity check timer callback that
659 * runs through the stream specific part of the state machine.
661 * @param agent context pointer
662 * @param stream which stream (of the agent)
663 * @return will return TRUE if a new stun request has been sent
666 priv_conn_check_tick_stream (NiceAgent *agent, NiceStream *stream)
668 gboolean pair_failed = FALSE;
670 unsigned int timeout;
673 now = g_get_monotonic_time ();
675 /* step: process ongoing STUN transactions */
676 for (i = stream->conncheck_list; i ; i = i->next) {
677 CandidateCheckPair *p = i->data;
678 gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
679 NiceComponent *component;
680 guint index = 0, remaining = 0;
682 if (p->stun_transactions == NULL)
685 if (!agent_find_component (agent, p->stream_id, p->component_id,
689 j = p->stun_transactions;
691 StunTransaction *stun = j->data;
692 GSList *next = j->next;
694 if (now < stun->next_tick)
697 switch (stun_timer_refresh (&stun->timer)) {
698 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
699 timer_return_timeout:
700 priv_remove_stun_transaction (p, stun, component);
702 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
703 /* case: retransmission stopped, due to the nomination of
704 * a pair with a higher priority than this in-progress pair,
705 * ICE spec, sect 8.1.2 "Updating States", item 2.2
707 if (!p->retransmit || index > 0)
708 goto timer_return_timeout;
710 /* case: not ready, so schedule a new timeout */
711 timeout = stun_timer_remainder (&stun->timer);
713 nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
714 "(timer=%d/%d %d/%dms).",
716 stun->timer.retransmissions, stun->timer.max_retransmissions,
717 stun->timer.delay - timeout, stun->timer.delay);
719 agent_socket_send (p->sockptr, &p->remote->addr,
720 stun_message_length (&stun->message),
721 (gchar *)stun->buffer);
723 /* note: convert from milli to microseconds for g_time_val_add() */
724 stun->next_tick = now + timeout * 1000;
727 case STUN_USAGE_TIMER_RETURN_SUCCESS:
728 timeout = stun_timer_remainder (&stun->timer);
729 /* note: convert from milli to microseconds for g_time_val_add() */
730 stun->next_tick = now + timeout * 1000;
734 g_assert_not_reached();
741 if (remaining == 0) {
742 nice_address_to_string (&p->local->addr, tmpbuf1);
743 nice_address_to_string (&p->remote->addr, tmpbuf2);
744 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
746 nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
747 tmpbuf1, nice_address_get_port (&p->local->addr),
748 tmpbuf2, nice_address_get_port (&p->remote->addr));
749 candidate_check_pair_fail (stream, agent, p);
752 /* perform a check if a transition state from connected to
753 * ready can be performed. This may happen here, when the last
754 * in-progress pair has expired its retransmission count
755 * in priv_conn_check_tick_stream(), which is a condition to
756 * make the transition connected to ready.
758 conn_check_update_check_list_state_for_ready (agent, stream, component);
763 priv_print_conn_check_lists (agent, G_STRFUNC, ", retransmission failed");
769 priv_conn_check_ordinary_check (NiceAgent *agent, NiceStream *stream)
771 CandidateCheckPair *pair;
772 gboolean stun_sent = FALSE;
774 /* step: perform an ordinary check, sec 6.1.4.2 point 3. (Performing
775 * Connectivity Checks) of ICE spec (RFC8445)
776 * note: This code is executed when the triggered checks list is
777 * empty, and when no STUN message has been sent (pacing constraint)
779 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
781 /* step: there is no candidate in waiting state, try to unfreeze
782 * some pairs and retry, sect 6.1.4.2 point 2. (Performing Connectivity
783 * Checks) of ICE spec (RFC8445)
785 priv_conn_check_unfreeze_next (agent);
786 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
790 stun_sent = priv_conn_check_initiate (agent, pair);
791 priv_print_conn_check_lists (agent, G_STRFUNC,
792 ", initiated an ordinary connection check");
798 priv_conn_check_triggered_check (NiceAgent *agent, NiceStream *stream)
800 CandidateCheckPair *pair;
801 gboolean stun_sent = FALSE;
803 /* step: perform a test from the triggered checks list,
804 * sect 6.1.4.2 point 1. (Performing Connectivity Checks) of ICE
807 pair = priv_get_pair_from_triggered_check_queue (agent);
810 stun_sent = priv_conn_check_initiate (agent, pair);
811 priv_print_conn_check_lists (agent, G_STRFUNC,
812 ", initiated a connection check from triggered check list");
819 priv_conn_check_tick_stream_nominate (NiceAgent *agent, NiceStream *stream)
821 gboolean keep_timer_going = FALSE;
822 /* s_xxx counters are stream-wide */
823 guint s_inprogress = 0;
824 guint s_succeeded = 0;
825 guint s_discovered = 0;
826 guint s_nominated = 0;
827 guint s_waiting_for_nomination = 0;
831 CandidateCheckPair *other_stream_pair = NULL;
834 /* Search for a nominated pair (or selected to be nominated pair)
835 * from another stream.
837 for (i = agent->streams; i ; i = i->next) {
838 NiceStream *s = i->data;
839 if (s->id == stream->id)
841 for (j = s->conncheck_list; j ; j = j->next) {
842 CandidateCheckPair *p = j->data;
843 if (p->nominated || (p->use_candidate_on_next_check &&
844 p->state != NICE_CHECK_FAILED)) {
845 other_stream_pair = p;
849 if (other_stream_pair)
853 /* we compute some stream-wide counter values */
854 for (i = stream->conncheck_list; i ; i = i->next) {
855 CandidateCheckPair *p = i->data;
856 if (p->state == NICE_CHECK_FROZEN)
858 else if (p->state == NICE_CHECK_IN_PROGRESS)
860 else if (p->state == NICE_CHECK_WAITING)
862 else if (p->state == NICE_CHECK_SUCCEEDED)
864 else if (p->state == NICE_CHECK_DISCOVERED)
869 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
872 else if ((p->state == NICE_CHECK_SUCCEEDED ||
873 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
874 s_waiting_for_nomination++;
877 /* note: keep the timer going as long as there is work to be done */
879 keep_timer_going = TRUE;
881 if (s_nominated < stream->n_components &&
882 s_waiting_for_nomination) {
883 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
884 if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
885 agent->controlling_mode) {
886 #define NICE_MIN_NUMBER_OF_VALID_PAIRS 2
887 /* ICE 8.1.1.1 Regular nomination
888 * we choose to nominate the valid pair of a component if
889 * - there is no pair left frozen, waiting or in-progress, or
890 * - if there are at least two valid pairs, or
891 * - if there is at least one valid pair of type HOST-HOST
893 * This is the "stopping criterion" described in 8.1.1.1, and is
894 * a "local optimization" between accumulating more valid pairs,
895 * and limiting the time spent waiting for in-progress connections
896 * checks until they finally fail.
898 for (i = stream->components; i; i = i->next) {
899 NiceComponent *component = i->data;
900 CandidateCheckPair *other_component_pair = NULL;
901 CandidateCheckPair *this_component_pair = NULL;
902 NiceCandidate *lcand1 = NULL;
903 NiceCandidate *rcand1 = NULL;
904 NiceCandidate *lcand2, *rcand2;
905 gboolean already_done = FALSE;
906 gboolean found_other_component_pair = FALSE;
907 gboolean found_other_stream_pair = FALSE;
908 gboolean first_nomination = FALSE;
909 gboolean stopping_criterion;
910 /* p_xxx counters are component-wide */
914 guint p_inprogress = 0;
915 guint p_host_host_valid = 0;
917 /* we compute some component-wide counter values */
918 for (j = stream->conncheck_list; j ; j = j->next) {
919 CandidateCheckPair *p = j->data;
920 if (p->component_id == component->id) {
921 /* verify that the choice of the pair to be nominated
922 * has not already been done
924 if (p->use_candidate_on_next_check)
926 if (p->state == NICE_CHECK_FROZEN)
928 else if (p->state == NICE_CHECK_WAITING)
930 else if (p->state == NICE_CHECK_IN_PROGRESS)
935 p->local->type == NICE_CANDIDATE_TYPE_HOST &&
936 p->remote->type == NICE_CANDIDATE_TYPE_HOST)
944 /* Search for a nominated pair (or selected to be nominated pair)
945 * from another component of this stream.
947 for (j = stream->conncheck_list; j ; j = j->next) {
948 CandidateCheckPair *p = j->data;
949 if (p->component_id == component->id)
951 if (p->nominated || (p->use_candidate_on_next_check &&
952 p->state != NICE_CHECK_FAILED)) {
953 other_component_pair = p;
958 if (other_stream_pair == NULL && other_component_pair == NULL)
959 first_nomination = TRUE;
961 /* We choose a pair to be nominated in the list of valid
964 * this pair will be the one with the highest priority,
965 * when we don't have other nominated pairs in other
966 * components and in other streams
968 * this pair will be a pair compatible with another nominated
969 * pair from another component if we found one.
971 * else this pair will be a pair compatible with another
972 * nominated pair from another stream if we found one.
975 for (j = stream->conncheck_list; j ; j = j->next) {
976 CandidateCheckPair *p = j->data;
977 /* note: highest priority item selected (list always sorted) */
978 if (p->component_id == component->id &&
980 !p->use_candidate_on_next_check &&
982 /* According a ICE spec, sect 8.1.1.1. "Regular
983 * Nomination", we enqueue the check that produced this
984 * valid pair. When this pair has been discovered, we want
985 * to test its parent pair instead.
987 if (p->succeeded_pair != NULL) {
988 g_assert_cmpint (p->state, ==, NICE_CHECK_DISCOVERED);
989 p = p->succeeded_pair;
991 g_assert_cmpint (p->state, ==, NICE_CHECK_SUCCEEDED);
993 if (this_component_pair == NULL)
994 /* highest priority pair */
995 this_component_pair = p;
1000 if (first_nomination)
1001 /* use the highest priority pair */
1004 if (other_component_pair) {
1005 lcand2 = other_component_pair->local;
1006 rcand2 = other_component_pair->remote;
1008 if (other_component_pair &&
1009 lcand1->transport == lcand2->transport &&
1010 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1011 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1012 /* else continue the research with lower priority
1013 * pairs, compatible with a nominated pair of
1016 this_component_pair = p;
1017 found_other_component_pair = TRUE;
1021 if (other_stream_pair) {
1022 lcand2 = other_stream_pair->local;
1023 rcand2 = other_stream_pair->remote;
1025 if (other_stream_pair &&
1026 other_component_pair == NULL &&
1027 lcand1->transport == lcand2->transport &&
1028 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1029 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1030 /* else continue the research with lower priority
1031 * pairs, compatible with a nominated pair of
1034 this_component_pair = p;
1035 found_other_stream_pair = TRUE;
1041 /* No valid pair for this component */
1042 if (this_component_pair == NULL)
1045 /* The stopping criterion tries to select a set of pairs of
1046 * the same kind (transport/type) for all components of a
1047 * stream, and for all streams, when possible (see last
1050 * When no stream has nominated a pair yet, we apply the
1051 * following criterion :
1052 * - stop if we have a valid host-host pair
1053 * - or stop if we have at least "some* (2 in the current
1054 * implementation) valid pairs, and select the best one
1055 * - or stop if the conncheck cannot evolve more
1057 * Else when the stream has a nominated pair in another
1058 * component we apply this criterion:
1059 * - stop if we have a valid pair of the same kind than this
1060 * other nominated pair.
1061 * - or stop if the conncheck cannot evolve more
1063 * Else when another stream has a nominated pair we apply the
1064 * following criterion:
1065 * - stop if we have a valid pair of the same kind than the
1066 * other nominated pair.
1067 * - or stop if the conncheck cannot evolve more
1069 * When no further evolution of the conncheck is possible, we
1070 * prefer to select the best valid pair we have, *even* if it
1071 * is not compatible with the transport of another stream of
1072 * component. We think it's still a better choice than marking
1073 * this component 'failed'.
1075 stopping_criterion = FALSE;
1076 if (first_nomination && p_host_host_valid > 0) {
1077 stopping_criterion = TRUE;
1078 nice_debug ("Agent %p : stopping criterion: "
1079 "valid host-host pair", agent);
1080 } else if (first_nomination &&
1081 p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS) {
1082 stopping_criterion = TRUE;
1083 nice_debug ("Agent %p : stopping criterion: "
1084 "*some* valid pairs", agent);
1085 } else if (found_other_component_pair) {
1086 stopping_criterion = TRUE;
1087 nice_debug ("Agent %p : stopping criterion: "
1088 "matching pair in another component", agent);
1089 } else if (found_other_stream_pair) {
1090 stopping_criterion = TRUE;
1091 nice_debug ("Agent %p : stopping criterion: "
1092 "matching pair in another stream", agent);
1093 } else if (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0) {
1094 stopping_criterion = TRUE;
1095 nice_debug ("Agent %p : stopping criterion: "
1096 "no more pairs to check", agent);
1099 if (!stopping_criterion)
1102 /* when the stopping criterion is reached, we add the
1103 * selected pair for this component to the triggered checks
1106 nice_debug ("Agent %p : restarting check of %s:%s pair %p with "
1107 "USE-CANDIDATE attrib (regular nomination) for "
1108 "stream %d component %d", agent,
1109 priv_candidate_transport_to_string (
1110 this_component_pair->local->transport),
1111 priv_candidate_transport_to_string (
1112 this_component_pair->remote->transport),
1113 this_component_pair, stream->id, component->id);
1114 this_component_pair->use_candidate_on_next_check = TRUE;
1115 priv_add_pair_to_triggered_check_queue (agent, this_component_pair);
1116 keep_timer_going = TRUE;
1119 } else if (agent->controlling_mode) {
1120 for (i = stream->components; i; i = i->next) {
1121 NiceComponent *component = i->data;
1123 for (j = stream->conncheck_list; j ; j = j->next) {
1124 CandidateCheckPair *p = j->data;
1125 /* note: highest priority item selected (list always sorted) */
1126 if (p->component_id == component->id &&
1127 (p->state == NICE_CHECK_SUCCEEDED ||
1128 p->state == NICE_CHECK_DISCOVERED)) {
1129 nice_debug ("Agent %p : restarting check of pair %p as the "
1130 "nominated pair.", agent, p);
1131 p->nominated = TRUE;
1132 conn_check_update_selected_pair (agent, component, p);
1133 priv_add_pair_to_triggered_check_queue (agent, p);
1134 keep_timer_going = TRUE;
1135 break; /* move to the next component */
1141 if (stream->tick_counter++ % 50 == 0)
1142 nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, "
1143 "%u in-progress, %u waiting, %u succeeded, %u discovered, "
1144 "%u nominated, %u waiting-for-nom, %u valid",
1145 agent, stream->id, stream->tick_counter,
1146 s_frozen, s_inprogress, s_waiting, s_succeeded, s_discovered,
1147 s_nominated, s_waiting_for_nomination, s_valid);
1149 return keep_timer_going;
1154 conn_check_stop (NiceAgent *agent)
1156 if (agent->conncheck_timer_source == NULL)
1159 g_source_destroy (agent->conncheck_timer_source);
1160 g_source_unref (agent->conncheck_timer_source);
1161 agent->conncheck_timer_source = NULL;
1162 agent->conncheck_ongoing_idle_delay = 0;
1167 * Timer callback that handles initiating and managing connectivity
1168 * checks (paced by the Ta timer).
1170 * This function is designed for the g_timeout_add() interface.
1172 * @return will return FALSE when no more pending timers.
1174 static gboolean priv_conn_check_tick_agent_locked (NiceAgent *agent,
1177 gboolean keep_timer_going = FALSE;
1178 gboolean stun_sent = FALSE;
1181 /* step: process triggered checks
1182 * these steps are ordered by priority, since a single stun request
1183 * is sent per callback, we process the important steps first.
1185 * perform a single stun request per timer callback,
1186 * to respect stun pacing
1188 for (i = agent->streams; i && !stun_sent; i = i->next) {
1189 NiceStream *stream = i->data;
1191 stun_sent = priv_conn_check_triggered_check (agent, stream);
1194 /* step: process ongoing STUN transactions */
1195 for (i = agent->streams; i && !stun_sent; i = i->next) {
1196 NiceStream *stream = i->data;
1198 stun_sent = priv_conn_check_tick_stream (agent, stream);
1201 /* step: process ordinary checks */
1202 for (i = agent->streams; i && !stun_sent; i = i->next) {
1203 NiceStream *stream = i->data;
1205 stun_sent = priv_conn_check_ordinary_check (agent, stream);
1209 keep_timer_going = TRUE;
1211 /* step: try to nominate a pair
1213 for (i = agent->streams; i; i = i->next) {
1214 NiceStream *stream = i->data;
1216 if (priv_conn_check_tick_stream_nominate (agent, stream))
1217 keep_timer_going = TRUE;
1220 /* note: we provide a grace period before declaring a component as
1221 * failed. Components marked connected, and then ready follow another
1222 * code path, and are not concerned by this grace period.
1224 if (!keep_timer_going && agent->conncheck_ongoing_idle_delay == 0)
1225 nice_debug ("Agent %p : waiting %d msecs before checking "
1226 "for failed components.", agent, agent->idle_timeout);
1228 if (keep_timer_going)
1229 agent->conncheck_ongoing_idle_delay = 0;
1231 agent->conncheck_ongoing_idle_delay += agent->timer_ta;
1233 /* step: stop timer if no work left */
1234 if (!keep_timer_going &&
1235 agent->conncheck_ongoing_idle_delay >= agent->idle_timeout) {
1236 nice_debug ("Agent %p : checking for failed components now.", agent);
1237 for (i = agent->streams; i; i = i->next) {
1238 NiceStream *stream = i->data;
1239 priv_update_check_list_failed_components (agent, stream);
1240 for (j = stream->components; j; j = j->next) {
1241 NiceComponent *component = j->data;
1242 conn_check_update_check_list_state_for_ready (agent, stream, component);
1246 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
1247 priv_print_conn_check_lists (agent, G_STRFUNC,
1248 ", conncheck timer stopped");
1250 /* Stopping the timer so destroy the source.. this will allow
1251 the timer to be reset if we get a set_remote_candidates after this
1253 conn_check_stop (agent);
1255 /* XXX: what to signal, is all processing now really done? */
1256 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
1263 static gboolean priv_conn_keepalive_retransmissions_tick_agent_locked (
1264 NiceAgent *agent, gpointer pointer)
1266 CandidatePair *pair = (CandidatePair *) pointer;
1268 g_source_destroy (pair->keepalive.tick_source);
1269 g_source_unref (pair->keepalive.tick_source);
1270 pair->keepalive.tick_source = NULL;
1272 switch (stun_timer_refresh (&pair->keepalive.timer)) {
1273 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1276 StunTransactionId id;
1277 NiceComponent *component;
1279 if (!agent_find_component (agent,
1280 pair->keepalive.stream_id, pair->keepalive.component_id,
1281 NULL, &component)) {
1282 nice_debug ("Could not find stream or component in"
1283 " priv_conn_keepalive_retransmissions_tick");
1287 stun_message_id (&pair->keepalive.stun_message, id);
1288 stun_agent_forget_transaction (&component->stun_agent, id);
1289 pair->keepalive.stun_message.buffer = NULL;
1291 if (agent->media_after_tick) {
1292 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
1293 "but media was received. Suspecting keepalive lost because of "
1294 "network bottleneck", agent);
1296 nice_debug ("Agent %p : Keepalive conncheck timed out!! "
1297 "peer probably lost connection", agent);
1298 agent_signal_component_state_change (agent,
1299 pair->keepalive.stream_id, pair->keepalive.component_id,
1300 NICE_COMPONENT_STATE_FAILED);
1304 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1306 agent_socket_send (pair->local->sockptr, &pair->remote->addr,
1307 stun_message_length (&pair->keepalive.stun_message),
1308 (gchar *)pair->keepalive.stun_buffer);
1310 nice_debug ("Agent %p : Retransmitting keepalive conncheck",
1314 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1315 agent_timeout_add_with_context (agent,
1316 &pair->keepalive.tick_source,
1317 "Pair keepalive", stun_timer_remainder (&pair->keepalive.timer),
1318 priv_conn_keepalive_retransmissions_tick_agent_locked, pair);
1321 g_assert_not_reached();
1328 static guint32 peer_reflexive_candidate_priority (NiceAgent *agent,
1329 NiceCandidate *local_candidate)
1331 NiceCandidate *candidate_priority =
1332 nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
1335 candidate_priority->transport = local_candidate->transport;
1336 candidate_priority->component_id = local_candidate->component_id;
1337 candidate_priority->base_addr = local_candidate->addr;
1338 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1339 priority = nice_candidate_jingle_priority (candidate_priority);
1340 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
1341 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
1342 priority = nice_candidate_msn_priority (candidate_priority);
1343 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1344 priority = nice_candidate_ms_ice_priority (candidate_priority,
1345 agent->reliable, FALSE);
1347 priority = nice_candidate_ice_priority (candidate_priority,
1348 agent->reliable, FALSE);
1350 nice_candidate_free (candidate_priority);
1355 /* Returns the priority of a local candidate of type peer-reflexive that
1356 * would be learned as a consequence of a check from this local
1357 * candidate. See RFC 5245, section 7.1.2.1. "PRIORITY and USE-CANDIDATE".
1358 * RFC 5245 is more explanatory than RFC 8445 on this detail.
1360 * Apply to local candidates of type host only, because candidates of type
1361 * relay are supposed to have a public IP address, that wont generate
1362 * a peer-reflexive address. Server-reflexive candidates are not
1363 * concerned too, because no STUN request is sent with a local candidate
1366 static guint32 stun_request_priority (NiceAgent *agent,
1367 NiceCandidate *local_candidate)
1369 if (local_candidate->type == NICE_CANDIDATE_TYPE_HOST)
1370 return peer_reflexive_candidate_priority (agent, local_candidate);
1372 return local_candidate->priority;
1375 static void ms_ice2_legacy_conncheck_send(StunMessage *msg, NiceSocket *sock,
1376 const NiceAddress *remote_addr)
1378 uint32_t *fingerprint_attr;
1379 uint32_t fingerprint_orig;
1380 uint16_t fingerprint_len;
1383 if (msg->agent->ms_ice2_send_legacy_connchecks == FALSE) {
1387 fingerprint_attr = (uint32_t *)stun_message_find (msg,
1388 STUN_ATTRIBUTE_FINGERPRINT, &fingerprint_len);
1390 if (fingerprint_attr == NULL) {
1391 nice_debug ("FINGERPRINT not found.");
1395 if (fingerprint_len != sizeof (fingerprint_orig)) {
1396 nice_debug ("Unexpected FINGERPRINT length %u.", fingerprint_len);
1400 memcpy (&fingerprint_orig, fingerprint_attr, sizeof (fingerprint_orig));
1402 buffer_len = stun_message_length (msg);
1404 *fingerprint_attr = stun_fingerprint (msg->buffer, buffer_len, TRUE);
1406 agent_socket_send (sock, remote_addr, buffer_len, (gchar *)msg->buffer);
1408 memcpy (fingerprint_attr, &fingerprint_orig, sizeof (fingerprint_orig));
1412 * Timer callback that handles initiating and managing connectivity
1413 * checks (paced by the Ta timer).
1415 * This function is designed for the g_timeout_add() interface.
1417 * @return will return FALSE when no more pending timers.
1419 static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
1425 guint64 min_next_tick;
1426 guint64 next_timer_tick;
1428 now = g_get_monotonic_time ();
1429 min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1431 /* case 1: session established and media flowing
1432 * (ref ICE sect 11 "Keepalives" RFC-8445)
1433 * TODO: keepalives should be send only when no packet has been sent
1434 * on that pair in the last Tr seconds, and not unconditionally.
1436 for (i = agent->streams; i; i = i->next) {
1438 NiceStream *stream = i->data;
1439 for (j = stream->components; j; j = j->next) {
1440 NiceComponent *component = j->data;
1441 if (component->selected_pair.local != NULL) {
1442 CandidatePair *p = &component->selected_pair;
1444 /* Disable keepalive checks on TCP candidates unless explicitly enabled */
1445 if (p->local->transport != NICE_CANDIDATE_TRANSPORT_UDP &&
1446 !agent->keepalive_conncheck)
1449 if (p->keepalive.next_tick) {
1450 if (p->keepalive.next_tick < min_next_tick)
1451 min_next_tick = p->keepalive.next_tick;
1452 if (now < p->keepalive.next_tick)
1456 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
1457 agent->keepalive_conncheck) {
1458 uint8_t uname[NICE_STREAM_MAX_UNAME];
1460 priv_create_username (agent, agent_find_stream (agent, stream->id),
1461 component->id, p->remote, p->local, uname, sizeof (uname),
1463 uint8_t *password = NULL;
1464 size_t password_len = priv_get_password (agent,
1465 agent_find_stream (agent, stream->id), p->remote, &password);
1467 if (p->keepalive.stun_message.buffer != NULL) {
1468 nice_debug ("Agent %p: Keepalive for s%u:c%u still"
1469 " retransmitting, not restarting", agent, stream->id,
1474 if (nice_debug_is_enabled ()) {
1475 gchar tmpbuf[INET6_ADDRSTRLEN];
1476 nice_address_to_string (&p->remote->addr, tmpbuf);
1477 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
1478 "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
1479 "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
1480 agent, tmpbuf, nice_address_get_port (&p->remote->addr),
1481 component->id, (int) uname_len, uname, uname_len,
1482 (int) password_len, password, password_len,
1485 if (uname_len > 0) {
1486 buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
1487 &p->keepalive.stun_message, p->keepalive.stun_buffer,
1488 sizeof(p->keepalive.stun_buffer),
1489 uname, uname_len, password, password_len,
1490 agent->controlling_mode, agent->controlling_mode,
1494 agent_to_ice_compatibility (agent));
1496 nice_debug ("Agent %p: conncheck created %zd - %p",
1497 agent, buf_len, p->keepalive.stun_message.buffer);
1500 stun_timer_start (&p->keepalive.timer,
1501 agent->stun_initial_timeout,
1502 agent->stun_max_retransmissions);
1504 agent->media_after_tick = FALSE;
1506 /* send the conncheck */
1507 agent_socket_send (p->local->sockptr, &p->remote->addr,
1508 buf_len, (gchar *)p->keepalive.stun_buffer);
1510 p->keepalive.stream_id = stream->id;
1511 p->keepalive.component_id = component->id;
1512 p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1514 agent_timeout_add_with_context (agent,
1515 &p->keepalive.tick_source, "Pair keepalive",
1516 stun_timer_remainder (&p->keepalive.timer),
1517 priv_conn_keepalive_retransmissions_tick_agent_locked, p);
1519 next_timer_tick = now + agent->timer_ta * 1000;
1526 buf_len = stun_usage_bind_keepalive (&component->stun_agent,
1527 &p->keepalive.stun_message, p->keepalive.stun_buffer,
1528 sizeof(p->keepalive.stun_buffer));
1531 agent_socket_send (p->local->sockptr, &p->remote->addr, buf_len,
1532 (gchar *)p->keepalive.stun_buffer);
1534 p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1536 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1537 ms_ice2_legacy_conncheck_send (&p->keepalive.stun_message,
1538 p->local->sockptr, &p->remote->addr);
1541 if (nice_debug_is_enabled ()) {
1542 gchar tmpbuf[INET6_ADDRSTRLEN];
1543 nice_address_to_string (&p->local->base_addr, tmpbuf);
1544 nice_debug ("Agent %p : resending STUN to keep the "
1545 "selected base address %s:%u alive in s%d/c%d.", agent,
1546 tmpbuf, nice_address_get_port (&p->local->base_addr),
1547 stream->id, component->id);
1550 next_timer_tick = now + agent->timer_ta * 1000;
1560 /* case 2: connectivity establishment ongoing
1561 * (ref ICE sect 5.1.1.4 "Keeping Candidates Alive" RFC-8445)
1563 for (i = agent->streams; i; i = i->next) {
1564 NiceStream *stream = i->data;
1565 for (j = stream->components; j; j = j->next) {
1566 NiceComponent *component = j->data;
1567 if (component->state < NICE_COMPONENT_STATE_CONNECTED &&
1568 agent->stun_server_ip) {
1569 NiceAddress stun_server;
1570 if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
1571 StunAgent stun_agent;
1572 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1573 StunMessage stun_message;
1574 size_t buffer_len = 0;
1576 nice_address_set_port (&stun_server, agent->stun_server_port);
1578 nice_agent_init_stun_agent (agent, &stun_agent);
1580 buffer_len = stun_usage_bind_create (&stun_agent,
1581 &stun_message, stun_buffer, sizeof(stun_buffer));
1583 for (k = component->local_candidates; k; k = k->next) {
1584 NiceCandidate *candidate = (NiceCandidate *) k->data;
1585 if (candidate->type == NICE_CANDIDATE_TYPE_HOST &&
1586 candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP &&
1587 nice_address_ip_version (&candidate->addr) ==
1588 nice_address_ip_version (&stun_server)) {
1590 if (candidate->keepalive_next_tick) {
1591 if (candidate->keepalive_next_tick < min_next_tick)
1592 min_next_tick = candidate->keepalive_next_tick;
1593 if (now < candidate->keepalive_next_tick)
1597 /* send the conncheck */
1598 if (nice_debug_is_enabled ()) {
1599 gchar tmpbuf[INET6_ADDRSTRLEN];
1600 nice_address_to_string (&candidate->addr, tmpbuf);
1601 nice_debug ("Agent %p : resending STUN to keep the local "
1602 "candidate %s:%u alive in s%d/c%d.", agent,
1603 tmpbuf, nice_address_get_port (&candidate->addr),
1604 stream->id, component->id);
1606 agent_socket_send (candidate->sockptr, &stun_server,
1607 buffer_len, (gchar *)stun_buffer);
1608 candidate->keepalive_next_tick = now +
1609 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1610 next_timer_tick = now + agent->timer_ta * 1000;
1619 next_timer_tick = min_next_tick;
1623 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
1627 if (agent->keepalive_timer_source) {
1628 g_source_destroy (agent->keepalive_timer_source);
1629 g_source_unref (agent->keepalive_timer_source);
1630 agent->keepalive_timer_source = NULL;
1632 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1633 "Connectivity keepalive timeout", (next_timer_tick - now)/ 1000,
1634 priv_conn_keepalive_tick_agent_locked, NULL);
1638 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
1643 ret = priv_conn_keepalive_tick_unlocked (agent);
1645 if (agent->keepalive_timer_source) {
1646 g_source_destroy (agent->keepalive_timer_source);
1647 g_source_unref (agent->keepalive_timer_source);
1648 agent->keepalive_timer_source = NULL;
1656 static gboolean priv_turn_allocate_refresh_retransmissions_tick_agent_locked (
1657 NiceAgent *agent, gpointer pointer)
1659 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1661 g_source_destroy (cand->tick_source);
1662 g_source_unref (cand->tick_source);
1663 cand->tick_source = NULL;
1665 switch (stun_timer_refresh (&cand->timer)) {
1666 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1669 StunTransactionId id;
1671 stun_message_id (&cand->stun_message, id);
1672 stun_agent_forget_transaction (&cand->stun_agent, id);
1674 refresh_free (agent, cand);
1677 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1679 agent_socket_send (cand->nicesock, &cand->server,
1680 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
1683 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1684 agent_timeout_add_with_context (agent, &cand->tick_source,
1685 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1686 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1689 /* Nothing to do. */
1693 return G_SOURCE_REMOVE;
1696 static void priv_turn_allocate_refresh_tick_unlocked (NiceAgent *agent,
1697 CandidateRefresh *cand)
1703 size_t buffer_len = 0;
1704 StunUsageTurnCompatibility turn_compat =
1705 agent_to_turn_compatibility (agent);
1707 username = (uint8_t *)cand->candidate->turn->username;
1708 username_len = (size_t) strlen (cand->candidate->turn->username);
1709 password = (uint8_t *)cand->candidate->turn->password;
1710 password_len = (size_t) strlen (cand->candidate->turn->password);
1712 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
1713 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
1714 username = cand->candidate->turn->decoded_username;
1715 password = cand->candidate->turn->decoded_password;
1716 username_len = cand->candidate->turn->decoded_username_len;
1717 password_len = cand->candidate->turn->decoded_password_len;
1720 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
1721 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
1722 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, -1,
1723 username, username_len,
1724 password, password_len,
1727 nice_debug ("Agent %p : Sending allocate Refresh %zd", agent,
1730 if (cand->tick_source != NULL) {
1731 g_source_destroy (cand->tick_source);
1732 g_source_unref (cand->tick_source);
1733 cand->tick_source = NULL;
1736 if (buffer_len > 0) {
1737 stun_timer_start (&cand->timer,
1738 agent->stun_initial_timeout,
1739 agent->stun_max_retransmissions);
1741 /* send the refresh */
1742 agent_socket_send (cand->nicesock, &cand->server,
1743 buffer_len, (gchar *)cand->stun_buffer);
1745 agent_timeout_add_with_context (agent, &cand->tick_source,
1746 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1747 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1754 * Timer callback that handles refreshing TURN allocations
1756 * This function is designed for the g_timeout_add() interface.
1758 * @return will return FALSE when no more pending timers.
1760 static gboolean priv_turn_allocate_refresh_tick_agent_locked (NiceAgent *agent,
1763 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1765 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
1767 return G_SOURCE_REMOVE;
1772 * Initiates the next pending connectivity check.
1774 void conn_check_schedule_next (NiceAgent *agent)
1776 if (agent->discovery_unsched_items > 0)
1777 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
1779 /* step: schedule timer if not running yet */
1780 if (agent->conncheck_timer_source == NULL) {
1781 agent_timeout_add_with_context (agent, &agent->conncheck_timer_source,
1782 "Connectivity check schedule", agent->timer_ta,
1783 priv_conn_check_tick_agent_locked, NULL);
1786 /* step: also start the keepalive timer */
1787 if (agent->keepalive_timer_source == NULL) {
1788 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1789 "Connectivity keepalive timeout", agent->timer_ta,
1790 priv_conn_keepalive_tick_agent_locked, NULL);
1795 * Compares two connectivity check items. Checkpairs are sorted
1796 * in descending priority order, with highest priority item at
1797 * the start of the list.
1799 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
1801 if (a->priority > b->priority)
1803 else if (a->priority < b->priority)
1808 /* Find a transport compatible with a given socket.
1810 * Returns TRUE when a matching transport can be guessed from
1811 * the type of the socket in an unambiguous way.
1814 nice_socket_has_compatible_transport (NiceSocket *socket,
1815 NiceCandidateTransport *transport)
1817 gboolean found = TRUE;
1820 g_assert (transport);
1822 switch (socket->type) {
1823 case NICE_SOCKET_TYPE_TCP_BSD:
1824 if (nice_tcp_bsd_socket_get_passive_parent (socket))
1825 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1827 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1829 case NICE_SOCKET_TYPE_TCP_PASSIVE:
1830 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1832 case NICE_SOCKET_TYPE_TCP_ACTIVE:
1833 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1835 case NICE_SOCKET_TYPE_UDP_BSD:
1836 *transport = NICE_CANDIDATE_TRANSPORT_UDP;
1845 /* Test if a local socket and a local candidate are compatible. This
1846 * function does supplementary tests when the address and port are not
1847 * sufficient to give a unique candidate. We try to avoid comparing
1848 * directly the sockptr value, when possible, to rely on objective
1849 * properties of the candidate and the socket instead, and we also
1850 * choose to ignore the conncheck list for the same reason.
1853 local_candidate_and_socket_compatible (NiceAgent *agent,
1854 NiceCandidate *lcand, NiceSocket *socket)
1856 gboolean ret = TRUE;
1857 NiceCandidateTransport transport;
1862 if (nice_socket_has_compatible_transport (socket, &transport)) {
1863 ret = (lcand->transport == transport);
1864 /* tcp-active discovered peer-reflexive local candidate, where
1865 * socket is the tcp connect related socket */
1866 if (ret && transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1867 nice_address_get_port (&lcand->addr) > 0)
1868 ret = (lcand->sockptr == socket);
1869 } else if (socket->type == NICE_SOCKET_TYPE_UDP_TURN)
1870 /* Socket of type udp-turn will match a unique local candidate
1871 * by its sockptr value. An an udp-turn socket doesn't carry enough
1872 * information when base socket is udp-turn-over-tcp to disambiguate
1873 * between a tcp-act and a tcp-pass local candidate.
1875 ret = (lcand->sockptr == socket);
1880 /* Test if a local socket and a remote candidate are compatible.
1881 * This function is very close to its local candidate counterpart,
1882 * the difference is that we also use information from the local
1883 * candidate we may have identified previously. This is needed
1884 * to disambiguate the transport of the candidate with a socket
1889 remote_candidate_and_socket_compatible (NiceAgent *agent,
1890 NiceCandidate *lcand, NiceCandidate *rcand, NiceSocket *socket)
1892 gboolean ret = TRUE;
1893 NiceCandidateTransport transport;
1898 if (nice_socket_has_compatible_transport (socket, &transport))
1899 ret = (conn_check_match_transport (rcand->transport) == transport);
1901 /* This supplementary test with the local candidate is needed with
1902 * socket of type udp-turn, the type doesn't allow to disambiguate
1903 * between a tcp-pass and a tcp-act remote candidate
1906 ret = (conn_check_match_transport (lcand->transport) == rcand->transport);
1912 conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream,
1913 NiceComponent *component)
1917 NiceCandidate *lcand = NULL, *rcand = NULL;
1919 nice_debug ("Agent %p : conn_check_remote_candidates_set %u %u",
1920 agent, stream->id, component->id);
1922 if (stream->remote_ufrag[0] == 0)
1925 if (component->incoming_checks.head)
1926 nice_debug ("Agent %p : credentials have been set, "
1927 "we can process incoming checks", agent);
1929 for (i = component->incoming_checks.head; i;) {
1930 IncomingCheck *icheck = i->data;
1931 GList *i_next = i->next;
1933 nice_debug ("Agent %p : replaying icheck=%p (sock=%p)",
1934 agent, icheck, icheck->local_socket);
1936 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
1937 * be handled separately */
1938 for (j = component->local_candidates; j; j = j->next) {
1939 NiceCandidate *cand = j->data;
1942 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
1945 addr = &cand->base_addr;
1947 if (nice_address_equal (&icheck->local_socket->addr, addr) &&
1948 local_candidate_and_socket_compatible (agent, cand,
1949 icheck->local_socket)) {
1955 if (lcand == NULL) {
1956 for (j = component->local_candidates; j; j = j->next) {
1957 NiceCandidate *cand = j->data;
1958 NiceAddress *addr = &cand->base_addr;
1960 /* tcp-active (not peer-reflexive discovered) local candidate, where
1961 * socket is the tcp connect related socket */
1962 if (nice_address_equal_no_port (&icheck->local_socket->addr, addr) &&
1963 nice_address_get_port (&cand->addr) == 0 &&
1964 cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1965 local_candidate_and_socket_compatible (agent, cand,
1966 icheck->local_socket)) {
1973 g_assert (lcand != NULL);
1975 for (j = component->remote_candidates; j; j = j->next) {
1976 NiceCandidate *cand = j->data;
1977 if (nice_address_equal (&cand->addr, &icheck->from) &&
1978 remote_candidate_and_socket_compatible (agent, lcand, cand,
1979 icheck->local_socket)) {
1985 if (lcand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
1986 CandidateCheckPair *pair = NULL;
1988 for (j = stream->conncheck_list; j; j = j->next) {
1989 CandidateCheckPair *p = j->data;
1990 if (lcand == p->local && rcand == p->remote) {
1996 priv_conn_check_add_for_candidate_pair_matched (agent,
1997 stream->id, component, lcand, rcand, NICE_CHECK_WAITING);
2000 priv_schedule_triggered_check (agent, stream, component,
2001 icheck->local_socket, rcand);
2002 if (icheck->use_candidate)
2003 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
2005 if (icheck->username)
2006 g_free (icheck->username);
2007 g_slice_free (IncomingCheck, icheck);
2008 g_queue_delete_link (&component->incoming_checks, i);
2014 * Handle any processing steps for connectivity checks after
2015 * remote credentials have been set. This function handles
2016 * the special case where answerer has sent us connectivity
2017 * checks before the answer (containing credentials information),
2018 * reaches us. The special case is documented in RFC 5245 sect 7.2.
2021 void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream)
2025 for (j = stream->components; j ; j = j->next) {
2026 NiceComponent *component = j->data;
2028 conn_check_remote_candidates_set(agent, stream, component);
2033 * Enforces the upper limit for connectivity checks by dropping
2034 * lower-priority pairs as described RFC 8445 section 6.1.2.5. See also
2035 * conn_check_add_for_candidate().
2036 * Returns TRUE if the pair in argument is one of the deleted pairs.
2038 static gboolean priv_limit_conn_check_list_size (NiceAgent *agent,
2039 NiceStream *stream, CandidateCheckPair *pair)
2042 guint cancelled = 0;
2043 gboolean deleted = FALSE;
2044 GSList *item = stream->conncheck_list;
2047 CandidateCheckPair *p = item->data;
2048 GSList *next = item->next;
2051 /* We remove lower-priority pairs, but only the ones that can be
2052 * safely discarded without breaking an ongoing conncheck process.
2053 * This only includes pairs that are in the frozen state (those
2054 * initially added when remote candidates are received) or in failed
2055 * state. Pairs in any other state play a role in the conncheck, and
2056 * there removal may lead to a failing conncheck that would succeed
2059 * We also remove failed pairs from the list unconditionally.
2061 if ((valid > agent->max_conn_checks && p->state == NICE_CHECK_FROZEN) ||
2062 p->state == NICE_CHECK_FAILED) {
2065 nice_debug ("Agent %p : pair %p removed.", agent, p);
2066 candidate_check_pair_free (agent, p);
2067 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list,
2075 nice_debug ("Agent %p : Pruned %d pairs. "
2076 "Conncheck list has %d elements left. "
2077 "Maximum connchecks allowed : %d", agent, cancelled,
2078 valid - cancelled, agent->max_conn_checks);
2084 * Changes the selected pair for the component if 'pair'
2085 * has higher priority than the currently selected pair. See
2086 * RFC 8445 sect 8.1.1. "Nominating Pairs"
2089 conn_check_update_selected_pair (NiceAgent *agent, NiceComponent *component,
2090 CandidateCheckPair *pair)
2092 CandidatePair cpair = { 0, };
2094 g_assert (component);
2096 /* pair is expected to have the nominated flag */
2097 g_assert (pair->nominated);
2098 if (pair->priority > component->selected_pair.priority) {
2099 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2100 nice_candidate_pair_priority_to_string (pair->priority, priority);
2101 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
2102 "(prio:%s).", agent, component->id,
2103 pair->local->foundation, pair->remote->foundation, priority);
2105 cpair.local = pair->local;
2106 cpair.remote = pair->remote;
2107 cpair.priority = pair->priority;
2108 cpair.stun_priority = pair->stun_priority;
2110 nice_component_update_selected_pair (agent, component, &cpair);
2112 priv_conn_keepalive_tick_unlocked (agent);
2114 agent_signal_new_selected_pair (agent, pair->stream_id, component->id,
2115 pair->local, pair->remote);
2120 * Updates the check list state.
2122 * Implements parts of the algorithm described in
2123 * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any
2124 * component, all checks have been completed and have
2125 * failed to produce a nominated pair, mark that component's
2126 * state to NICE_CHECK_FAILED.
2128 * Sends a component state changesignal via 'agent'.
2130 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream)
2135 /* note: emitting a signal might cause the client
2136 * to remove the stream, thus the component count
2137 * must be fetched before entering the loop*/
2138 guint c, components = stream->n_components;
2140 if (stream->conncheck_list == NULL)
2143 for (i = agent->discovery_list; i; i = i->next) {
2144 CandidateDiscovery *d = i->data;
2146 /* There is still discovery ogoing for this stream,
2147 * so don't fail any of it's candidates.
2149 if (d->stream_id == stream->id && !d->done)
2152 if (agent->discovery_list != NULL)
2155 /* note: iterate the conncheck list for each component separately */
2156 for (c = 0; c < components; c++) {
2157 NiceComponent *component = NULL;
2158 if (!agent_find_component (agent, stream->id, c+1, NULL, &component))
2163 for (i = stream->conncheck_list; i; i = i->next) {
2164 CandidateCheckPair *p = i->data;
2166 g_assert_cmpuint (p->stream_id, ==, stream->id);
2168 if (p->component_id == (c + 1)) {
2171 if (p->state != NICE_CHECK_FAILED &&
2172 p->state != NICE_CHECK_SUCCEEDED &&
2173 p->state != NICE_CHECK_DISCOVERED)
2178 /* note: all pairs are either failed or succeeded, and the component
2179 * has not produced a nominated pair.
2180 * Set the component to FAILED only if it actually had remote candidates
2182 if (completed && nominated == 0 &&
2183 component != NULL && component->remote_candidates != NULL)
2184 agent_signal_component_state_change (agent,
2186 (c + 1), /* component-id */
2187 NICE_COMPONENT_STATE_FAILED);
2192 * Updates the check list state for a stream component.
2194 * Implements the algorithm described in ICE sect 8.1.2
2195 * "Updating States" (ID-19) as it applies to checks of
2196 * a certain component. If there are any nominated pairs,
2197 * ICE processing may be concluded, and component state is
2200 * Sends a component state changesignal via 'agent'.
2202 void conn_check_update_check_list_state_for_ready (NiceAgent *agent,
2203 NiceStream *stream, NiceComponent *component)
2206 guint valid = 0, nominated = 0;
2208 g_assert (component);
2210 /* step: search for at least one nominated pair */
2211 for (i = stream->conncheck_list; i; i = i->next) {
2212 CandidateCheckPair *p = i->data;
2213 if (p->component_id == component->id) {
2216 if (p->nominated == TRUE) {
2223 if (nominated > 0) {
2224 /* Only go to READY if no checks are left in progress. If there are
2225 * any that are kept, then this function will be called again when the
2226 * conncheck tick timer finishes them all */
2227 if (priv_prune_pending_checks (agent, stream, component) == 0) {
2228 /* Continue through the states to give client code a nice
2229 * logical progression. See http://phabricator.freedesktop.org/D218 for
2231 if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
2232 component->state == NICE_COMPONENT_STATE_FAILED)
2233 agent_signal_component_state_change (agent, stream->id, component->id,
2234 NICE_COMPONENT_STATE_CONNECTING);
2235 if (component->state < NICE_COMPONENT_STATE_CONNECTED)
2236 agent_signal_component_state_change (agent, stream->id, component->id,
2237 NICE_COMPONENT_STATE_CONNECTED);
2238 agent_signal_component_state_change (agent, stream->id,
2239 component->id, NICE_COMPONENT_STATE_READY);
2242 nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id);
2246 * The remote party has signalled that the candidate pair
2247 * described by 'component' and 'remotecand' is nominated
2250 static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand)
2254 g_assert (component);
2256 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2257 agent->controlling_mode)
2260 /* step: search for at least one nominated pair */
2261 for (i = stream->conncheck_list; i; i = i->next) {
2262 CandidateCheckPair *pair = i->data;
2263 if (pair->local == localcand && pair->remote == remotecand) {
2264 /* ICE, 7.2.1.5. Updating the Nominated Flag */
2265 /* note: TCP candidates typically produce peer reflexive
2266 * candidate, generating a "discovered" pair that can be
2269 if (pair->state == NICE_CHECK_SUCCEEDED &&
2270 pair->discovered_pair != NULL) {
2271 pair = pair->discovered_pair;
2272 g_assert_cmpint (pair->state, ==, NICE_CHECK_DISCOVERED);
2275 /* If the received Binding request triggered a new check to be
2276 * enqueued in the triggered-check queue (Section 7.3.1.4), once
2277 * the check is sent and if it generates a successful response,
2278 * and generates a valid pair, the agent sets the nominated flag
2279 * of the pair to true
2281 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2282 if (g_slist_find (agent->triggered_check_queue, pair) ||
2283 pair->state == NICE_CHECK_IN_PROGRESS) {
2285 /* This pair is not always in the triggered check list, for
2286 * example if it is in-progress with a lower priority than an
2287 * already nominated pair. Is that case, it is not rescheduled
2288 * for a connection check, see function
2289 * priv_schedule_triggered_check(), case NICE_CHECK_IN_PROGRESS.
2291 pair->mark_nominated_on_response_arrival = TRUE;
2292 nice_debug ("Agent %p : pair %p (%s) is %s, "
2293 "will be nominated on response receipt.",
2294 agent, pair, pair->foundation,
2295 priv_state_to_string (pair->state));
2300 !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2301 nice_debug ("Agent %p : marking pair %p (%s) as nominated",
2302 agent, pair, pair->foundation);
2303 pair->nominated = TRUE;
2307 /* Do not step down to CONNECTED if we're already at state READY*/
2308 if (component->state == NICE_COMPONENT_STATE_FAILED)
2309 agent_signal_component_state_change (agent,
2310 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
2311 conn_check_update_selected_pair (agent, component, pair);
2312 if (component->state == NICE_COMPONENT_STATE_CONNECTING)
2313 /* step: notify the client of a new component state (must be done
2314 * before the possible check list state update step */
2315 agent_signal_component_state_change (agent,
2316 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2319 if (pair->nominated)
2320 conn_check_update_check_list_state_for_ready (agent, stream, component);
2326 * Creates a new connectivity check pair and adds it to
2327 * the agent's list of checks.
2329 static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
2330 guint stream_id, NiceComponent *component, NiceCandidate *local,
2331 NiceCandidate *remote, NiceCheckState initial_state)
2334 CandidateCheckPair *pair;
2337 g_assert (local != NULL);
2338 g_assert (remote != NULL);
2340 priority = agent_candidate_pair_priority (agent, local, remote);
2342 if (component->selected_pair.priority &&
2343 priority < component->selected_pair.priority) {
2344 gchar prio1[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2345 gchar prio2[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2347 nice_candidate_pair_priority_to_string (priority, prio1);
2348 nice_candidate_pair_priority_to_string (component->selected_pair.priority,
2350 nice_debug ("Agent %p : do not create a pair that would have a priority "
2351 "%s lower than selected pair priority %s.", agent, prio1, prio2);
2355 stream = agent_find_stream (agent, stream_id);
2356 pair = g_slice_new0 (CandidateCheckPair);
2358 pair->stream_id = stream_id;
2359 pair->component_id = component->id;;
2360 pair->local = local;
2361 pair->remote = remote;
2362 /* note: we use the remote sockptr only in the case
2365 if (local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
2366 remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2367 pair->sockptr = (NiceSocket *) remote->sockptr;
2369 pair->sockptr = (NiceSocket *) local->sockptr;
2370 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
2372 pair->priority = agent_candidate_pair_priority (agent, local, remote);
2373 nice_debug ("Agent %p : creating a new pair", agent);
2374 SET_PAIR_STATE (agent, pair, initial_state);
2376 gchar tmpbuf1[INET6_ADDRSTRLEN];
2377 gchar tmpbuf2[INET6_ADDRSTRLEN];
2378 nice_address_to_string (&pair->local->addr, tmpbuf1);
2379 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2380 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
2381 tmpbuf1, nice_address_get_port (&pair->local->addr),
2382 tmpbuf2, nice_address_get_port (&pair->remote->addr));
2384 pair->stun_priority = stun_request_priority (agent, local);
2386 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
2387 (GCompareFunc)conn_check_compare);
2389 nice_debug ("Agent %p : added a new pair %p with foundation '%s' and "
2390 "transport %s:%s to stream %u component %u",
2391 agent, pair, pair->foundation,
2392 priv_candidate_transport_to_string (pair->local->transport),
2393 priv_candidate_transport_to_string (pair->remote->transport),
2394 stream_id, component->id);
2396 if (initial_state == NICE_CHECK_FROZEN)
2397 priv_conn_check_unfreeze_maybe (agent, pair);
2399 /* implement the hard upper limit for number of
2400 checks (see sect 5.7.3 ICE ID-19): */
2401 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
2402 if (priv_limit_conn_check_list_size (agent, stream, pair))
2409 NiceCandidateTransport
2410 conn_check_match_transport (NiceCandidateTransport transport)
2412 switch (transport) {
2413 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
2414 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
2416 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
2417 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
2419 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
2420 case NICE_CANDIDATE_TRANSPORT_UDP:
2427 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
2428 NiceAgent *agent, guint stream_id, NiceComponent *component,
2429 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state)
2431 CandidateCheckPair *pair;
2433 pair = priv_add_new_check_pair (agent, stream_id, component, local, remote,
2436 if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
2437 component->state == NICE_COMPONENT_STATE_READY) {
2438 agent_signal_component_state_change (agent,
2441 NICE_COMPONENT_STATE_CONNECTED);
2443 agent_signal_component_state_change (agent,
2446 NICE_COMPONENT_STATE_CONNECTING);
2453 gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
2454 guint stream_id, NiceComponent *component, NiceCandidate *local,
2455 NiceCandidate *remote)
2457 gboolean ret = FALSE;
2459 g_assert (local != NULL);
2460 g_assert (remote != NULL);
2462 /* note: do not create pairs where the local candidate is a srv-reflexive
2463 * or peer-reflexive (ICE 6.1.2.4. "Pruning the pairs" RFC 8445)
2465 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
2466 agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2467 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2468 (local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
2469 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)) {
2473 /* note: do not create pairs where local candidate has TCP passive transport
2474 * (ice-tcp-13 6.2. "Forming the Check Lists") */
2475 if (local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
2479 /* note: match pairs only if transport and address family are the same */
2480 if (local->transport == conn_check_match_transport (remote->transport) &&
2481 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family) {
2482 if (priv_conn_check_add_for_candidate_pair_matched (agent, stream_id,
2483 component, local, remote, NICE_CHECK_FROZEN))
2491 * Forms new candidate pairs by matching the new remote candidate
2492 * 'remote_cand' with all existing local candidates of 'component'.
2493 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
2496 * @param agent context
2497 * @param component pointer to the component
2498 * @param remote remote candidate to match with
2500 * @return number of checks added, negative on fatal errors
2502 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote)
2508 g_assert (remote != NULL);
2510 /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates",
2511 * the agent does not pair this candidate with any local candidates.
2513 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2514 remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2519 for (i = component->local_candidates; i ; i = i->next) {
2520 NiceCandidate *local = i->data;
2522 if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED)
2525 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2536 * Forms new candidate pairs by matching the new local candidate
2537 * 'local_cand' with all existing remote candidates of 'component'.
2539 * @param agent context
2540 * @param component pointer to the component
2541 * @param local local candidate to match with
2543 * @return number of checks added, negative on fatal errors
2545 int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local)
2551 g_assert (local != NULL);
2554 * note: according to 7.1.3.2.1 "Discovering Peer Reflexive
2555 * Candidates", the peer reflexive candidate is not paired
2556 * with other remote candidates
2559 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2560 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2565 for (i = component->remote_candidates; i ; i = i->next) {
2567 NiceCandidate *remote = i->data;
2568 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2579 * Frees the CandidateCheckPair structure pointer to
2580 * by 'user data'. Compatible with GDestroyNotify.
2582 static void candidate_check_pair_free (NiceAgent *agent,
2583 CandidateCheckPair *pair)
2585 priv_remove_pair_from_triggered_check_queue (agent, pair);
2586 priv_free_all_stun_transactions (pair, NULL);
2587 g_slice_free (CandidateCheckPair, pair);
2591 * Frees all resources of all connectivity checks.
2593 void conn_check_free (NiceAgent *agent)
2596 for (i = agent->streams; i; i = i->next) {
2597 NiceStream *stream = i->data;
2599 if (stream->conncheck_list) {
2602 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent,
2604 for (item = stream->conncheck_list; item; item = item->next)
2605 candidate_check_pair_free (agent, item->data);
2606 g_slist_free (stream->conncheck_list);
2607 stream->conncheck_list = NULL;
2611 conn_check_stop (agent);
2615 * Prunes the list of connectivity checks for items related
2616 * to stream 'stream_id'.
2618 * @return TRUE on success, FALSE on a fatal error
2620 void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream)
2623 gboolean keep_going = FALSE;
2625 if (stream->conncheck_list) {
2628 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
2630 for (item = stream->conncheck_list; item; item = item->next)
2631 candidate_check_pair_free (agent, item->data);
2632 g_slist_free (stream->conncheck_list);
2633 stream->conncheck_list = NULL;
2636 for (i = agent->streams; i; i = i->next) {
2637 NiceStream *s = i->data;
2638 if (s->conncheck_list) {
2645 conn_check_stop (agent);
2649 * Fills 'dest' with a username string for use in an outbound connectivity
2650 * checks. No more than 'dest_len' characters (including terminating
2651 * NULL) is ever written to the 'dest'.
2654 size_t priv_gen_username (NiceAgent *agent, guint component_id,
2655 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
2658 gsize remote_len = strlen (remote);
2659 gsize local_len = strlen (local);
2661 if (remote_len > 0 && local_len > 0) {
2662 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
2663 dest_len >= remote_len + local_len + 1) {
2664 memcpy (dest, remote, remote_len);
2666 memcpy (dest + len, ":", 1);
2668 memcpy (dest + len, local, local_len);
2670 } else if ((agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2671 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2672 dest_len >= remote_len + local_len + 4 ) {
2673 memcpy (dest, remote, remote_len);
2675 memcpy (dest + len, ":", 1);
2677 memcpy (dest + len, local, local_len);
2680 memset (dest + len, 0, 4 - (len % 4));
2681 len += 4 - (len % 4);
2683 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2684 dest_len >= remote_len + local_len) {
2685 memcpy (dest, remote, remote_len);
2687 memcpy (dest + len, local, local_len);
2689 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2690 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
2691 gchar component_str[10];
2692 guchar *local_decoded = NULL;
2693 guchar *remote_decoded = NULL;
2694 gsize local_decoded_len;
2695 gsize remote_decoded_len;
2699 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
2700 local_decoded = g_base64_decode (local, &local_decoded_len);
2701 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
2703 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
2704 padding = 4 - (total_len % 4);
2706 if (dest_len >= total_len + padding) {
2707 guchar pad_char[1] = {0};
2710 memcpy (dest, remote_decoded, remote_decoded_len);
2711 len += remote_decoded_len;
2712 memcpy (dest + len, ":", 1);
2714 memcpy (dest + len, component_str, strlen (component_str));
2715 len += strlen (component_str);
2717 memcpy (dest + len, ":", 1);
2720 memcpy (dest + len, local_decoded, local_decoded_len);
2721 len += local_decoded_len;
2722 memcpy (dest + len, ":", 1);
2724 memcpy (dest + len, component_str, strlen (component_str));;
2725 len += strlen (component_str);
2727 for (i = 0; i < padding; i++) {
2728 memcpy (dest + len, pad_char, 1);
2734 g_free (local_decoded);
2735 g_free (remote_decoded);
2743 * Fills 'dest' with a username string for use in an outbound connectivity
2744 * checks. No more than 'dest_len' characters (including terminating
2745 * NULL) is ever written to the 'dest'.
2748 size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
2749 guint component_id, NiceCandidate *remote, NiceCandidate *local,
2750 uint8_t *dest, guint dest_len, gboolean inbound)
2752 gchar *local_username = NULL;
2753 gchar *remote_username = NULL;
2756 if (remote && remote->username) {
2757 remote_username = remote->username;
2760 if (local && local->username) {
2761 local_username = local->username;
2765 if (remote_username == NULL) {
2766 remote_username = stream->remote_ufrag;
2768 if (local_username == NULL) {
2769 local_username = stream->local_ufrag;
2773 if (local_username && remote_username) {
2775 return priv_gen_username (agent, component_id,
2776 local_username, remote_username, dest, dest_len);
2778 return priv_gen_username (agent, component_id,
2779 remote_username, local_username, dest, dest_len);
2787 * Returns a password string for use in an outbound connectivity
2791 size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
2792 NiceCandidate *remote, uint8_t **password)
2794 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
2797 if (remote && remote->password) {
2798 *password = (uint8_t *)remote->password;
2799 return strlen (remote->password);
2803 *password = (uint8_t *)stream->remote_password;
2804 return strlen (stream->remote_password);
2810 /* Implement the computation specific in RFC 8445 section 14 */
2812 static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
2815 guint waiting_and_in_progress = 0;
2816 unsigned int rto = 0;
2818 /* we can compute precisely the number of pairs in-progress or
2819 * waiting for all streams, instead of limiting the value to one
2820 * stream, and multiplying it by the number of active streams.
2821 * Since RFC8445, this number of waiting and in-progress pairs
2822 * if maxed by the number of different foundations in the conncheck
2825 for (i = agent->streams; i ; i = i->next) {
2826 NiceStream *s = i->data;
2827 for (j = s->conncheck_list; j ; j = j->next) {
2828 CandidateCheckPair *p = j->data;
2829 if (p->state == NICE_CHECK_IN_PROGRESS ||
2830 p->state == NICE_CHECK_WAITING)
2831 waiting_and_in_progress++;
2835 rto = agent->timer_ta * waiting_and_in_progress;
2837 nice_debug ("Agent %p : timer set to %dms, "
2838 "waiting+in_progress=%d", agent, MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT),
2839 waiting_and_in_progress);
2840 return MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT);
2844 * Sends a connectivity check over candidate pair 'pair'.
2846 * @return zero on success, non-zero on error
2848 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
2851 /* note: following information is supplied:
2852 * - username (for USERNAME attribute)
2853 * - password (for MESSAGE-INTEGRITY)
2854 * - priority (for PRIORITY)
2855 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
2856 * - USE-CANDIDATE (if sent by the controlling agent)
2859 uint8_t uname[NICE_STREAM_MAX_UNAME];
2861 NiceComponent *component;
2863 uint8_t *password = NULL;
2865 bool controlling = agent->controlling_mode;
2866 /* XXX: add API to support different nomination modes: */
2867 bool cand_use = controlling;
2869 unsigned int timeout;
2870 StunTransaction *stun;
2872 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
2873 &stream, &component))
2876 uname_len = priv_create_username (agent, stream, pair->component_id,
2877 pair->remote, pair->local, uname, sizeof (uname), FALSE);
2878 password_len = priv_get_password (agent, stream, pair->remote, &password);
2880 if (password != NULL &&
2881 (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2882 agent->compatibility == NICE_COMPATIBILITY_OC2007)) {
2883 password = g_base64_decode ((gchar *) password, &password_len);
2886 if (nice_debug_is_enabled ()) {
2887 gchar tmpbuf1[INET6_ADDRSTRLEN];
2888 gchar tmpbuf2[INET6_ADDRSTRLEN];
2889 nice_address_to_string (&pair->local->addr, tmpbuf1);
2890 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2891 nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, "
2892 "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
2893 "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%08x, %s.", agent,
2894 tmpbuf1, nice_address_get_port (&pair->local->addr),
2895 tmpbuf2, nice_address_get_port (&pair->remote->addr),
2896 pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1,
2897 pair, pair->component_id,
2898 (unsigned long long)agent->tie_breaker,
2899 (int) uname_len, uname, uname_len,
2900 (int) password_len, password, password_len,
2901 pair->stun_priority,
2902 controlling ? "controlling" : "controlled");
2905 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2906 switch (agent->nomination_mode) {
2907 case NICE_NOMINATION_MODE_REGULAR:
2908 /* We are doing regular nomination, so we set the use-candidate
2909 * attrib, when the controlling agent decided which valid pair to
2910 * resend with this flag in priv_conn_check_tick_stream()
2912 cand_use = pair->use_candidate_on_next_check;
2913 nice_debug ("Agent %p : %s: set cand_use=%d "
2914 "(regular nomination).", agent, G_STRFUNC, cand_use);
2916 case NICE_NOMINATION_MODE_AGGRESSIVE:
2917 /* We are doing aggressive nomination, we set the use-candidate
2918 * attrib in every check we send, when we are the controlling
2919 * agent, RFC 5245, 8.1.1.2
2921 cand_use = controlling;
2922 nice_debug ("Agent %p : %s: set cand_use=%d "
2923 "(aggressive nomination).", agent, G_STRFUNC, cand_use);
2926 /* Nothing to do. */
2929 } else if (cand_use)
2930 pair->nominated = controlling;
2932 if (uname_len == 0) {
2933 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent);
2937 stun = priv_add_stun_transaction (pair);
2939 buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
2940 &stun->message, stun->buffer, sizeof(stun->buffer),
2941 uname, uname_len, password, password_len,
2942 cand_use, controlling, pair->stun_priority,
2944 pair->local->foundation,
2945 agent_to_ice_compatibility (agent));
2947 nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len,
2948 stun->message.buffer);
2950 if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2951 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
2955 if (buffer_len == 0) {
2956 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent);
2957 priv_remove_stun_transaction (pair, stun, component);
2961 if (nice_socket_is_reliable(pair->sockptr)) {
2962 timeout = agent->stun_reliable_timeout;
2963 stun_timer_start_reliable(&stun->timer, timeout);
2965 timeout = priv_compute_conncheck_timer (agent, stream);
2966 stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions);
2969 stun->next_tick = g_get_monotonic_time () + timeout * 1000;
2971 /* TCP-ACTIVE candidate must create a new socket before sending
2972 * by connecting to the peer. The new socket is stored in the candidate
2973 * check pair, until we discover a new local peer reflexive */
2974 if (pair->sockptr->fileno == NULL &&
2975 pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN &&
2976 pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
2977 NiceStream *stream2 = NULL;
2978 NiceComponent *component2 = NULL;
2979 NiceSocket *new_socket;
2981 if (agent_find_component (agent, pair->stream_id, pair->component_id,
2982 &stream2, &component2)) {
2983 new_socket = nice_tcp_active_socket_connect (pair->sockptr,
2984 &pair->remote->addr);
2986 nice_debug ("Agent %p: add to tcp-act socket %p a new "
2987 "tcp connect socket %p on pair %p in s/c %d/%d",
2988 agent, pair->sockptr, new_socket, pair, stream->id, component->id);
2989 pair->sockptr = new_socket;
2990 _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
2992 nice_socket_set_writable_callback (pair->sockptr, _tcp_sock_is_writable,
2995 nice_component_attach_socket (component2, new_socket);
2999 /* send the conncheck */
3000 agent_socket_send (pair->sockptr, &pair->remote->addr,
3001 buffer_len, (gchar *)stun->buffer);
3003 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2)
3004 ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr,
3005 &pair->remote->addr);
3011 * Implemented the pruning steps described in ICE sect 8.1.2
3012 * "Updating States" (ID-19) after a pair has been nominated.
3014 * @see conn_check_update_check_list_state_failed_components()
3016 static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
3020 guint in_progress = 0;
3021 guint triggered_check = 0;
3022 gchar prio[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
3024 nice_debug ("Agent %p: Pruning pending checks for s%d/c%d",
3025 agent, stream->id, component->id);
3027 /* Called when we have at least one selected pair */
3028 priority = component->selected_pair.priority;
3029 g_assert (priority > 0);
3031 nice_candidate_pair_priority_to_string (priority, prio);
3032 nice_debug ("Agent %p : selected pair priority is %s", agent, prio);
3034 i = stream->conncheck_list;
3036 CandidateCheckPair *p = i->data;
3037 GSList *next = i->next;
3039 if (p->component_id != component->id) {
3044 /* We do not remove a pair from the conncheck list if it is also in
3045 * the triggered check queue. This is not what suggests the ICE
3046 * spec, but it proved to be more robust in the aggressive
3047 * nomination scenario, precisely because these pairs may have the
3048 * use-candidate flag set, and the peer agent may already have
3049 * selected such one.
3051 if (g_slist_find (agent->triggered_check_queue, p) &&
3052 p->state != NICE_CHECK_IN_PROGRESS) {
3053 if (p->priority < priority) {
3054 nice_debug ("Agent %p : pair %p removed.", agent, p);
3055 candidate_check_pair_free (agent, p);
3056 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3061 /* step: cancel all FROZEN and WAITING pairs for the component */
3062 else if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
3063 nice_debug ("Agent %p : pair %p removed.", agent, p);
3064 candidate_check_pair_free (agent, p);
3065 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3068 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
3069 else if (p->state == NICE_CHECK_IN_PROGRESS) {
3070 if (p->priority < priority) {
3071 priv_remove_pair_from_triggered_check_queue (agent, p);
3072 if (p->retransmit) {
3073 p->retransmit = FALSE;
3074 nice_debug ("Agent %p : pair %p will not be retransmitted.",
3078 /* We must keep the higher priority pairs running because if a udp
3079 * packet was lost, we might end up using a bad candidate */
3080 nice_candidate_pair_priority_to_string (p->priority, prio);
3081 nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority "
3082 "%s is higher than priority of best nominated pair.", agent, p, prio);
3083 /* We may also have to enable the retransmit flag of pairs with
3084 * a higher priority than the first nominated pair
3086 if (!p->retransmit && p->stun_transactions) {
3087 p->retransmit = TRUE;
3088 nice_debug ("Agent %p : pair %p will be retransmitted.", agent, p);
3096 return in_progress + triggered_check;
3100 * Schedules a triggered check after a successfully inbound
3101 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
3103 * @param agent self pointer
3104 * @param component the check is related to
3105 * @param local_socket socket from which the inbound check was received
3106 * @param remote_cand remote candidate from which the inbound check was sent
3108 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand)
3111 NiceCandidate *local = NULL;
3112 CandidateCheckPair *p;
3114 g_assert (remote_cand != NULL);
3116 nice_debug ("Agent %p : scheduling triggered check with socket=%p "
3117 "and remote cand=%p.", agent, local_socket, remote_cand);
3119 for (i = stream->conncheck_list; i ; i = i->next) {
3121 if (p->component_id == component->id &&
3122 p->remote == remote_cand &&
3123 p->sockptr == local_socket) {
3124 /* If we match with a peer-reflexive discovered pair, we
3125 * use the parent succeeded pair instead */
3127 if (p->succeeded_pair != NULL) {
3128 g_assert_cmpint (p->state, ==, NICE_CHECK_DISCOVERED);
3129 p = p->succeeded_pair;
3132 nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...",
3133 agent, p, p->foundation, priv_state_to_string (p->state));
3136 case NICE_CHECK_WAITING:
3137 case NICE_CHECK_FROZEN:
3138 nice_debug ("Agent %p : pair %p added for a triggered check.",
3140 priv_add_pair_to_triggered_check_queue (agent, p);
3142 case NICE_CHECK_IN_PROGRESS:
3143 /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks"
3144 * we cancel the in-progress transaction, and after the
3145 * retransmission timeout, we create a new connectivity check
3146 * for that pair. The controlling role of this new check may
3147 * be different from the role of this cancelled check.
3149 * When another pair, with a higher priority is already
3150 * nominated, so there's no reason to recheck this pair,
3151 * since it can in no way replace the nominated one.
3153 if (p->priority > component->selected_pair.priority) {
3154 nice_debug ("Agent %p : pair %p added for a triggered check.",
3156 priv_add_pair_to_triggered_check_queue (agent, p);
3159 case NICE_CHECK_FAILED:
3160 if (p->priority > component->selected_pair.priority) {
3161 nice_debug ("Agent %p : pair %p added for a triggered check.",
3163 priv_add_pair_to_triggered_check_queue (agent, p);
3164 /* If the component for this pair is in failed state, move it
3165 * back to connecting, and reinitiate the timers
3167 if (component->state == NICE_COMPONENT_STATE_FAILED) {
3168 agent_signal_component_state_change (agent, stream->id,
3169 component->id, NICE_COMPONENT_STATE_CONNECTING);
3170 conn_check_schedule_next (agent);
3171 /* If the component if in ready state, move it back to
3172 * connected as this failed pair with a higher priority
3173 * than the nominated pair requires to pursue the
3176 } else if (component->state == NICE_COMPONENT_STATE_READY) {
3177 agent_signal_component_state_change (agent, stream->id,
3178 component->id, NICE_COMPONENT_STATE_CONNECTED);
3179 conn_check_schedule_next (agent);
3183 case NICE_CHECK_SUCCEEDED:
3184 nice_debug ("Agent %p : nothing to do for pair %p.", agent, p);
3190 /* note: the spec says the we SHOULD retransmit in-progress
3191 * checks immediately, but we won't do that now */
3197 for (i = component->local_candidates; i ; i = i->next) {
3199 if (local->sockptr == local_socket)
3204 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
3205 p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id,
3206 component, local, remote_cand, NICE_CHECK_WAITING);
3208 priv_add_pair_to_triggered_check_queue (agent, p);
3212 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
3219 * Sends a reply to an successfully received STUN connectivity
3220 * check request. Implements parts of the ICE spec section 7.2 (STUN
3221 * Server Procedures).
3223 * @param agent context pointer
3224 * @param stream which stream (of the agent)
3225 * @param component which component (of the stream)
3226 * @param rcand remote candidate from which the request came, if NULL,
3227 * the response is sent immediately but no other processing is done
3228 * @param toaddr address to which reply is sent
3229 * @param socket the socket over which the request came
3230 * @param rbuf_len length of STUN message to send
3231 * @param msg the STUN message to send
3232 * @param use_candidate whether the request had USE_CANDIDATE attribute
3234 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
3236 static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream,
3237 NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand,
3238 const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len,
3239 StunMessage *msg, gboolean use_candidate)
3241 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
3243 if (nice_debug_is_enabled ()) {
3244 gchar tmpbuf[INET6_ADDRSTRLEN];
3245 nice_address_to_string (toaddr, tmpbuf);
3246 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u), use-cand=%d.", agent,
3248 nice_address_get_port (toaddr),
3249 sockptr->fileno ? g_socket_get_fd(sockptr->fileno) : -1,
3251 rcand, component->id,
3252 (int)use_candidate);
3255 agent_socket_send (sockptr, toaddr, rbuf_len, (const gchar*)msg->buffer);
3256 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
3257 ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr);
3260 /* We react to this stun request when we have the remote credentials.
3261 * When credentials are not yet known, this request is stored
3262 * in incoming_checks for later processing when returning from this
3265 if (rcand && stream->remote_ufrag[0]) {
3266 priv_schedule_triggered_check (agent, stream, component, sockptr, rcand);
3268 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
3273 * Stores information of an incoming STUN connectivity check
3274 * for later use. This is only needed when a check is received
3275 * before we get information about the remote candidates (via
3276 * SDP or other signaling means).
3278 * @return non-zero on error, zero on success
3280 static int priv_store_pending_check (NiceAgent *agent, NiceComponent *component,
3281 const NiceAddress *from, NiceSocket *sockptr, uint8_t *username,
3282 uint16_t username_len, uint32_t priority, gboolean use_candidate)
3284 IncomingCheck *icheck;
3285 nice_debug ("Agent %p : Storing pending check.", agent);
3287 if (g_queue_get_length (&component->incoming_checks) >=
3288 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
3289 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
3293 icheck = g_slice_new0 (IncomingCheck);
3294 g_queue_push_tail (&component->incoming_checks, icheck);
3295 icheck->from = *from;
3296 icheck->local_socket = sockptr;
3297 icheck->priority = priority;
3298 icheck->use_candidate = use_candidate;
3299 icheck->username_len = username_len;
3300 icheck->username = NULL;
3301 if (username_len > 0)
3302 icheck->username = g_memdup (username, username_len);
3308 * Adds a new pair, discovered from an incoming STUN response, to
3309 * the connectivity check list.
3311 * @return created pair, or NULL on fatal (memory allocation) errors
3313 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
3315 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
3316 NiceStream *stream = agent_find_stream (agent, stream_id);
3318 pair->stream_id = stream_id;
3319 pair->component_id = component->id;;
3320 pair->local = local_cand;
3321 pair->remote = parent_pair->remote;
3322 pair->sockptr = local_cand->sockptr;
3323 parent_pair->discovered_pair = pair;
3324 pair->succeeded_pair = parent_pair;
3325 nice_debug ("Agent %p : creating a new pair", agent);
3326 SET_PAIR_STATE (agent, pair, NICE_CHECK_DISCOVERED);
3328 gchar tmpbuf1[INET6_ADDRSTRLEN];
3329 gchar tmpbuf2[INET6_ADDRSTRLEN];
3330 nice_address_to_string (&pair->local->addr, tmpbuf1);
3331 nice_address_to_string (&pair->remote->addr, tmpbuf2);
3332 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
3333 tmpbuf1, nice_address_get_port (&pair->local->addr),
3334 tmpbuf2, nice_address_get_port (&pair->remote->addr));
3336 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
3337 local_cand->foundation, parent_pair->remote->foundation);
3339 if (agent->controlling_mode == TRUE)
3340 pair->priority = nice_candidate_pair_priority (pair->local->priority,
3341 pair->remote->priority);
3343 pair->priority = nice_candidate_pair_priority (pair->remote->priority,
3344 pair->local->priority);
3345 pair->nominated = parent_pair->nominated;
3346 /* the peer-reflexive priority used in stun request is copied from
3347 * the parent succeeded pair. This value is not required for discovered
3348 * pair, that won't emit stun requests themselves, but may be used when
3349 * such pair becomes the selected pair, and when keepalive stun are emitted,
3350 * using the sockptr and stun_priority values from the succeeded pair.
3352 pair->stun_priority = parent_pair->stun_priority;
3353 nice_debug ("Agent %p : added a new peer-discovered pair %p with "
3354 "foundation '%s' and transport %s:%s to stream %u component %u",
3355 agent, pair, pair->foundation,
3356 priv_candidate_transport_to_string (pair->local->transport),
3357 priv_candidate_transport_to_string (pair->remote->transport),
3358 stream_id, component->id);
3360 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
3361 (GCompareFunc)conn_check_compare);
3367 * Recalculates priorities of all candidate pairs. This
3368 * is required after a conflict in ICE roles.
3370 void recalculate_pair_priorities (NiceAgent *agent)
3374 for (i = agent->streams; i; i = i->next) {
3375 NiceStream *stream = i->data;
3376 for (j = stream->conncheck_list; j; j = j->next) {
3377 CandidateCheckPair *p = j->data;
3378 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
3380 stream->conncheck_list = g_slist_sort (stream->conncheck_list,
3381 (GCompareFunc)conn_check_compare);
3386 * Change the agent role if different from 'control'. Can be
3387 * initiated both by handling of incoming connectivity checks,
3388 * and by processing the responses to checks sent by us.
3390 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
3392 /* role conflict, change mode; wait for a new conn. check */
3393 if (control != agent->controlling_mode) {
3394 nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".",
3395 agent, control ? "controlling" : "controlled");
3396 agent->controlling_mode = control;
3397 /* the pair priorities depend on the roles, so recalculation
3399 recalculate_pair_priorities (agent);
3402 nice_debug ("Agent %p : Role conflict, staying with role \"%s\".",
3403 agent, control ? "controlling" : "controlled");
3407 * Checks whether the mapped address in connectivity check response
3408 * matches any of the known local candidates. If not, apply the
3409 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
3411 * @param agent context pointer
3412 * @param stream which stream (of the agent)
3413 * @param component which component (of the stream)
3414 * @param p the connectivity check pair for which we got a response
3415 * @param socketptr socket used to send the reply
3416 * @param mapped_sockaddr mapped address in the response
3418 * @return pointer to a candidate pair, found in conncheck list or newly created
3420 static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate)
3422 CandidateCheckPair *new_pair = NULL;
3425 NiceCandidate *local_cand = NULL;
3427 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
3429 for (i = component->local_candidates; i; i = i->next) {
3430 NiceCandidate *cand = i->data;
3432 if (nice_address_equal (&mapped, &cand->addr) &&
3433 local_candidate_and_socket_compatible (agent, cand, sockptr)) {
3439 /* The mapped address allows to look for a previously discovered
3440 * peer reflexive local candidate, and its related pair. This
3441 * new_pair will be marked 'Valid', while the pair 'p' of the
3442 * initial stun request will be marked 'Succeeded'
3444 * In the case of a tcp-act/tcp-pass pair 'p', where the local
3445 * candidate is of type tcp-act, and its port number is zero, a
3446 * conncheck on this pair *always* leads to the creation of a
3447 * discovered peer-reflexive tcp-act local candidate.
3449 for (i = stream->conncheck_list; i; i = i->next) {
3450 CandidateCheckPair *pair = i->data;
3451 if (local_cand == pair->local && remote_candidate == pair->remote) {
3458 /* note: when new_pair is distinct from p, it means new_pair is a
3459 * previously discovered peer-reflexive candidate pair, so we don't
3460 * set the valid flag on p in this case, because the valid flag is
3461 * already set on the discovered pair.
3465 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3466 priv_remove_pair_from_triggered_check_queue (agent, p);
3467 priv_free_all_stun_transactions (p, component);
3468 nice_component_add_valid_candidate (agent, component, remote_candidate);
3471 if (local_cand == NULL && !agent->force_relay) {
3472 /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
3473 * "Discovering Peer Reflexive Candidates"
3475 * The priority equal to the value of the PRIORITY attribute
3476 * in the Binding request is taken from the "parent" pair p
3478 local_cand = discovery_add_peer_reflexive_candidate (agent,
3486 nice_debug ("Agent %p : added a new peer-reflexive local candidate %p "
3487 "with transport %s", agent, local_cand,
3488 priv_candidate_transport_to_string (local_cand->transport));
3491 /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
3492 "Constructing a Valid Pair") */
3494 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component,
3496 /* note: this is same as "adding to VALID LIST" in the spec
3499 new_pair->valid = TRUE;
3500 /* step: The agent sets the state of the pair that *generated* the check to
3501 * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States"
3503 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3504 priv_remove_pair_from_triggered_check_queue (agent, p);
3505 priv_free_all_stun_transactions (p, component);
3508 if (new_pair && new_pair->valid)
3509 nice_component_add_valid_candidate (agent, component, remote_candidate);
3516 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
3517 * check transaction. If found, the reply is processed. Implements
3518 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
3520 * @return TRUE if a matching transaction is found
3522 static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp)
3525 struct sockaddr_storage storage;
3526 struct sockaddr addr;
3528 socklen_t socklen = sizeof (sockaddr);
3531 StunUsageIceReturn res;
3532 StunTransactionId discovery_id;
3533 StunTransactionId response_id;
3534 stun_message_id (resp, response_id);
3536 for (i = stream->conncheck_list; i; i = i->next) {
3537 CandidateCheckPair *p = i->data;
3539 for (j = p->stun_transactions, k = 0; j; j = j->next, k++) {
3540 StunTransaction *stun = j->data;
3542 stun_message_id (&stun->message, discovery_id);
3544 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)))
3547 res = stun_usage_ice_conncheck_process (resp,
3548 &sockaddr.storage, &socklen,
3549 agent_to_ice_compatibility (agent));
3550 nice_debug ("Agent %p : stun_bind_process/conncheck for %p: "
3551 "%s,res=%s,stun#=%d.",
3553 agent->controlling_mode ? "controlling" : "controlled",
3554 priv_ice_return_to_string (res), k);
3556 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
3557 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3558 /* case: found a matching connectivity check request */
3560 CandidateCheckPair *ok_pair = NULL;
3562 nice_debug ("Agent %p : pair %p MATCHED.", agent, p);
3563 priv_remove_stun_transaction (p, stun, component);
3565 /* step: verify that response came from the same IP address we
3566 * sent the original request to (see 7.1.2.1. "Failure
3568 if (nice_address_equal (from, &p->remote->addr) == FALSE) {
3569 candidate_check_pair_fail (stream, agent, p);
3570 if (nice_debug_is_enabled ()) {
3571 gchar tmpbuf[INET6_ADDRSTRLEN];
3572 gchar tmpbuf2[INET6_ADDRSTRLEN];
3573 nice_debug ("Agent %p : pair %p FAILED"
3574 " (mismatch of source address).", agent, p);
3575 nice_address_to_string (&p->remote->addr, tmpbuf);
3576 nice_address_to_string (from, tmpbuf2);
3577 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
3578 tmpbuf, nice_address_get_port (&p->remote->addr),
3579 tmpbuf2, nice_address_get_port (from));
3584 if (remote_candidate == NULL) {
3585 candidate_check_pair_fail (stream, agent, p);
3586 if (nice_debug_is_enabled ()) {
3587 nice_debug ("Agent %p : pair %p FAILED "
3588 "(got a matching pair without a known remote candidate).", agent, p);
3593 /* note: CONNECTED but not yet READY, see docs */
3595 /* step: handle the possible case of a peer-reflexive
3596 * candidate where the mapped-address in response does
3597 * not match any local candidate, see 7.1.2.2.1
3598 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
3600 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3601 nice_debug ("Agent %p : Mapped address not found", agent);
3602 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3604 nice_component_add_valid_candidate (agent, component, p->remote);
3606 ok_pair = priv_process_response_check_for_reflexive (agent,
3607 stream, component, p, sockptr, &sockaddr.addr,
3608 local_candidate, remote_candidate);
3610 /* note: The success of this check might also
3611 * cause the state of other checks to change as well
3612 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of
3613 * ICE spec (RFC8445).
3615 conn_check_unfreeze_related (agent, p);
3617 /* Note: this assignment helps to reduce the numbers of cases
3618 * to be tested. If ok_pair and p refer to distinct pairs, it
3619 * means that ok_pair is a discovered peer reflexive one,
3620 * caused by the check made on pair p. In that case, the
3621 * flags to be tested are on p, but the nominated flag will be
3622 * set on ok_pair. When there's no discovered pair, p and
3623 * ok_pair refer to the same pair.
3624 * To summarize : p is a SUCCEEDED pair, ok_pair is a
3625 * DISCOVERED, VALID, and eventually NOMINATED pair.
3630 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
3631 Nominated Flag" (ID-19) */
3632 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
3633 nice_debug ("Agent %p : Updating nominated flag (%s): "
3634 "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)",
3635 agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ?
3637 ok_pair, ok_pair->use_candidate_on_next_check,
3638 ok_pair->mark_nominated_on_response_arrival,
3639 p, p->use_candidate_on_next_check,
3640 p->mark_nominated_on_response_arrival);
3642 if (agent->controlling_mode) {
3643 switch (agent->nomination_mode) {
3644 case NICE_NOMINATION_MODE_REGULAR:
3645 if (p->use_candidate_on_next_check) {
3646 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3647 "(regular nomination, controlling, "
3648 "use_cand_on_next_check=1).",
3649 agent, ok_pair, ok_pair->foundation);
3650 ok_pair->nominated = TRUE;
3653 case NICE_NOMINATION_MODE_AGGRESSIVE:
3654 if (!p->nominated) {
3655 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3656 "(aggressive nomination, controlling).",
3657 agent, ok_pair, ok_pair->foundation);
3658 ok_pair->nominated = TRUE;
3666 if (p->mark_nominated_on_response_arrival) {
3667 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3668 "(%s nomination, controlled, mark_on_response=1).",
3669 agent, ok_pair, ok_pair->foundation,
3670 agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
3671 "aggressive" : "regular");
3672 ok_pair->nominated = TRUE;
3677 if (ok_pair->nominated == TRUE) {
3678 conn_check_update_selected_pair (agent, component, ok_pair);
3679 priv_print_conn_check_lists (agent, G_STRFUNC,
3680 ", got a nominated pair");
3682 /* Do not step down to CONNECTED if we're already at state READY*/
3683 if (component->state != NICE_COMPONENT_STATE_READY)
3684 /* step: notify the client of a new component state (must be done
3685 * before the possible check list state update step */
3686 agent_signal_component_state_change (agent,
3687 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
3690 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
3691 states" and 8.1.2 "Updating States", ID-19) */
3692 conn_check_update_check_list_state_for_ready (agent, stream, component);
3693 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
3695 gboolean controlled_mode;
3697 if (!p->retransmit) {
3698 nice_debug ("Agent %p : Role conflict with pair %p, not restarting",
3703 /* case: role conflict error, need to restart with new role */
3704 nice_debug ("Agent %p : Role conflict with pair %p, restarting",
3707 /* note: this res value indicates that the role of the peer
3708 * agent has not changed after the tie-breaker comparison, so
3709 * this is our role that must change. see ICE sect. 7.1.3.1
3710 * "Failure Cases". Our role might already have changed due to
3711 * an earlier incoming request, but if not, change role now.
3713 * Sect. 7.1.3.1 is not clear on this point, but we choose to
3714 * put the candidate pair in the triggered check list even
3715 * when the agent did not switch its role. The reason for this
3716 * interpretation is that the reception of the stun reply, even
3717 * an error reply, is a good sign that this pair will be
3718 * valid, if we retry the check after the role of both peers
3721 controlled_mode = (stun_message_find64 (&stun->message,
3722 STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) ==
3723 STUN_MESSAGE_RETURN_SUCCESS);
3725 priv_check_for_role_conflict (agent, controlled_mode);
3726 priv_remove_stun_transaction (p, stun, component);
3727 priv_add_pair_to_triggered_check_queue (agent, p);
3729 /* case: STUN error, the check STUN context was freed */
3730 candidate_check_pair_fail (stream, agent, p);
3740 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3741 * transaction. If found, a reply is sent.
3743 * @return TRUE if a matching transaction is found
3745 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp)
3748 struct sockaddr_storage storage;
3749 struct sockaddr addr;
3751 socklen_t socklen = sizeof (sockaddr);
3754 struct sockaddr_storage storage;
3755 struct sockaddr addr;
3757 socklen_t alternatelen = sizeof (sockaddr);
3760 StunUsageBindReturn res;
3761 gboolean trans_found = FALSE;
3762 StunTransactionId discovery_id;
3763 StunTransactionId response_id;
3764 stun_message_id (resp, response_id);
3766 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
3767 CandidateDiscovery *d = i->data;
3769 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
3770 d->stun_message.buffer) {
3771 stun_message_id (&d->stun_message, discovery_id);
3773 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
3774 res = stun_usage_bind_process (resp, &sockaddr.addr,
3775 &socklen, &alternate.addr, &alternatelen);
3776 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
3777 agent, d, (int)res);
3779 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
3780 /* handle alternate server */
3781 NiceAddress niceaddr;
3782 nice_address_set_from_sockaddr (&niceaddr, &alternate.addr);
3783 d->server = niceaddr;
3786 agent->discovery_unsched_items++;
3787 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
3788 /* case: successful binding discovery, create a new local candidate */
3790 if (!agent->force_relay) {
3791 NiceAddress niceaddr;
3793 nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr);
3794 discovery_add_server_reflexive_candidate (
3799 NICE_CANDIDATE_TRANSPORT_UDP,
3802 if (agent->use_ice_tcp)
3803 discovery_discover_tcp_server_reflexive_candidates (
3810 d->stun_message.buffer = NULL;
3811 d->stun_message.buffer_len = 0;
3814 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
3815 /* case: STUN error, the check STUN context was freed */
3816 d->stun_message.buffer = NULL;
3817 d->stun_message.buffer_len = 0;
3829 priv_calc_turn_timeout (guint lifetime)
3832 return lifetime - 60;
3834 return lifetime / 2;
3838 priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
3839 NiceCandidate *relay_cand, guint lifetime)
3841 CandidateRefresh *cand;
3843 if (cdisco->turn->type == NICE_RELAY_TYPE_TURN_TLS &&
3844 (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
3845 agent->compatibility == NICE_COMPATIBILITY_OC2007R2))
3848 cand = g_slice_new0 (CandidateRefresh);
3849 agent->refresh_list = g_slist_append (agent->refresh_list, cand);
3851 cand->candidate = relay_cand;
3852 cand->nicesock = cdisco->nicesock;
3853 cand->server = cdisco->server;
3854 cand->stream_id = cdisco->stream_id;
3855 cand->component_id = cdisco->component_id;
3856 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
3858 /* Use previous stun response for authentication credentials */
3859 if (cdisco->stun_resp_msg.buffer != NULL) {
3860 memcpy(cand->stun_resp_buffer, cdisco->stun_resp_buffer,
3861 sizeof(cand->stun_resp_buffer));
3862 memcpy(&cand->stun_resp_msg, &cdisco->stun_resp_msg, sizeof(StunMessage));
3863 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
3864 cand->stun_resp_msg.agent = &cand->stun_agent;
3865 cand->stun_resp_msg.key = NULL;
3868 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
3869 agent, cand, priv_calc_turn_timeout (lifetime));
3870 /* step: also start the refresh timer */
3871 /* refresh should be sent 1 minute before it expires */
3872 agent_timeout_add_seconds_with_context (agent, &cand->timer_source,
3873 "Candidate TURN refresh",
3874 priv_calc_turn_timeout (lifetime),
3875 priv_turn_allocate_refresh_tick_agent_locked, cand);
3877 nice_debug ("timer source is : %p", cand->timer_source);
3882 static void priv_handle_turn_alternate_server (NiceAgent *agent,
3883 CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate)
3885 /* We need to cancel and reset all candidate discovery turn for the same
3886 stream and type if there is an alternate server. Otherwise, we might end up
3887 with two relay components on different servers, creating candidates with
3888 unique foundations that only contain one component.
3892 for (i = agent->discovery_list; i; i = i->next) {
3893 CandidateDiscovery *d = i->data;
3896 d->type == disco->type &&
3897 d->stream_id == disco->stream_id &&
3898 d->turn->type == disco->turn->type &&
3899 nice_address_equal (&d->server, &server)) {
3900 gchar ip[INET6_ADDRSTRLEN];
3901 // Cancel the pending request to avoid a race condition with another
3902 // component responding with another altenrate-server
3903 d->stun_message.buffer = NULL;
3904 d->stun_message.buffer_len = 0;
3906 nice_address_to_string (&server, ip);
3907 nice_debug ("Agent %p : Cancelling and setting alternate server %s for "
3908 "CandidateDiscovery %p on s%d/c%d", agent, ip, d,
3909 d->stream_id, d->component_id);
3910 d->server = alternate;
3911 d->turn->server = alternate;
3913 agent->discovery_unsched_items++;
3915 if (d->turn->type == NICE_RELAY_TYPE_TURN_TCP ||
3916 d->turn->type == NICE_RELAY_TYPE_TURN_TLS) {
3918 NiceComponent *component;
3920 if (!agent_find_component (agent, d->stream_id, d->component_id,
3921 &stream, &component)) {
3922 nice_debug ("Could not find stream or component in "
3923 "priv_handle_turn_alternate_server");
3926 d->nicesock = agent_create_tcp_turn_socket (agent, stream, component,
3927 d->nicesock, &d->server, d->turn->type,
3928 nice_socket_is_reliable (d->nicesock));
3930 nice_component_attach_socket (component, d->nicesock);
3937 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3938 * transaction. If found, a reply is sent.
3940 * @return TRUE if a matching transaction is found
3942 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
3945 struct sockaddr_storage storage;
3946 struct sockaddr addr;
3948 socklen_t socklen = sizeof (sockaddr);
3951 struct sockaddr_storage storage;
3952 struct sockaddr addr;
3954 socklen_t alternatelen = sizeof (alternate);
3957 struct sockaddr_storage storage;
3958 struct sockaddr addr;
3960 socklen_t relayaddrlen = sizeof (relayaddr);
3965 StunUsageTurnReturn res;
3966 gboolean trans_found = FALSE;
3967 StunTransactionId discovery_id;
3968 StunTransactionId response_id;
3969 stun_message_id (resp, response_id);
3971 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
3972 CandidateDiscovery *d = i->data;
3974 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
3975 d->stun_message.buffer) {
3976 stun_message_id (&d->stun_message, discovery_id);
3978 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
3979 res = stun_usage_turn_process (resp,
3980 &relayaddr.storage, &relayaddrlen,
3981 &sockaddr.storage, &socklen,
3982 &alternate.storage, &alternatelen,
3983 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
3984 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
3985 agent, d, (int)res);
3987 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
3990 /* handle alternate server */
3991 nice_address_set_from_sockaddr (&addr, &alternate.addr);
3992 priv_handle_turn_alternate_server (agent, d, d->server, addr);
3994 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
3995 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
3996 /* case: successful allocate, create a new local candidate */
3997 NiceAddress niceaddr;
3998 NiceCandidate *relay_cand;
4000 nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr);
4002 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
4003 NiceAddress mappedniceaddr;
4005 /* We also received our mapped address */
4006 nice_address_set_from_sockaddr (&mappedniceaddr, &sockaddr.addr);
4008 /* TCP or TLS TURNS means the server-reflexive address was
4009 * on a TCP connection, which cannot be used for server-reflexive
4010 * discovery of candidates.
4012 if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP &&
4013 !agent->force_relay) {
4014 discovery_add_server_reflexive_candidate (
4019 NICE_CANDIDATE_TRANSPORT_UDP,
4023 if (agent->use_ice_tcp) {
4024 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4025 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4026 !nice_address_equal_no_port (&niceaddr, &d->turn->server)) {
4027 nice_debug("TURN port got allocated on an alternate server, "
4028 "ignoring bogus srflx address");
4030 discovery_discover_tcp_server_reflexive_candidates (
4040 if (nice_socket_is_reliable (d->nicesock)) {
4041 relay_cand = discovery_add_relay_candidate (
4046 NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE,
4051 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4052 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4053 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4055 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4058 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4061 relay_cand = discovery_add_relay_candidate (
4066 NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE,
4070 relay_cand = discovery_add_relay_candidate (
4075 NICE_CANDIDATE_TRANSPORT_UDP,
4081 if (d->stun_resp_msg.buffer)
4082 nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr,
4084 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4085 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4086 /* These data are needed on TURN socket when sending requests,
4087 * but never reach nice_turn_socket_parse_recv() where it could
4088 * be read directly, as the socket does not exist when allocate
4089 * response arrives to _nice_agent_recv(). We must set them right
4090 * after socket gets created in discovery_add_relay_candidate(),
4091 * so we are doing it here. */
4092 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4094 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4097 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4099 /* In case a new candidate has been added */
4100 conn_check_schedule_next (agent);
4103 d->stun_message.buffer = NULL;
4104 d->stun_message.buffer_len = 0;
4107 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4109 uint8_t *sent_realm = NULL;
4110 uint8_t *recv_realm = NULL;
4111 uint16_t sent_realm_len = 0;
4112 uint16_t recv_realm_len = 0;
4114 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
4115 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4116 recv_realm = (uint8_t *) stun_message_find (resp,
4117 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4119 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4120 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4121 alternatelen != sizeof(alternate)) {
4124 nice_address_set_from_sockaddr (&addr, &alternate.addr);
4126 if (!nice_address_equal (&addr, &d->server)) {
4127 priv_handle_turn_alternate_server (agent, d, d->server, addr);
4130 /* check for unauthorized error response */
4131 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
4132 agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4133 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4134 stun_message_get_class (resp) == STUN_ERROR &&
4135 stun_message_find_error (resp, &code) ==
4136 STUN_MESSAGE_RETURN_SUCCESS &&
4137 recv_realm != NULL && recv_realm_len > 0) {
4139 if (code == STUN_ERROR_STALE_NONCE ||
4140 (code == STUN_ERROR_UNAUTHORIZED &&
4141 !(recv_realm_len == sent_realm_len &&
4142 sent_realm != NULL &&
4143 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4144 d->stun_resp_msg = *resp;
4145 memcpy (d->stun_resp_buffer, resp->buffer,
4146 stun_message_length (resp));
4147 d->stun_resp_msg.buffer = d->stun_resp_buffer;
4148 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
4150 agent->discovery_unsched_items++;
4152 /* case: a real unauthorized error */
4153 d->stun_message.buffer = NULL;
4154 d->stun_message.buffer_len = 0;
4157 } else if (d->pending) {
4158 /* case: STUN error, the check STUN context was freed */
4159 d->stun_message.buffer = NULL;
4160 d->stun_message.buffer_len = 0;
4174 * Tries to match STUN reply in 'buf' to an existing STUN discovery
4175 * transaction. If found, a reply is sent.
4177 * @return TRUE if a matching transaction is found
4179 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
4183 StunUsageTurnReturn res;
4184 gboolean trans_found = FALSE;
4185 StunTransactionId refresh_id;
4186 StunTransactionId response_id;
4187 stun_message_id (resp, response_id);
4189 for (i = agent->refresh_list; i && trans_found != TRUE;) {
4190 CandidateRefresh *cand = i->data;
4191 GSList *next = i->next;
4193 if (!cand->disposing && cand->stun_message.buffer) {
4194 stun_message_id (&cand->stun_message, refresh_id);
4196 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
4197 res = stun_usage_turn_refresh_process (resp,
4198 &lifetime, agent_to_turn_compatibility (agent));
4199 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d with lifetime %u.",
4200 agent, cand, (int)res, lifetime);
4201 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
4202 /* refresh should be sent 1 minute before it expires */
4203 agent_timeout_add_seconds_with_context (agent,
4204 &cand->timer_source,
4205 "Candidate TURN refresh", priv_calc_turn_timeout (lifetime),
4206 priv_turn_allocate_refresh_tick_agent_locked, cand);
4208 g_source_destroy (cand->tick_source);
4209 g_source_unref (cand->tick_source);
4210 cand->tick_source = NULL;
4212 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4214 uint8_t *sent_realm = NULL;
4215 uint8_t *recv_realm = NULL;
4216 uint16_t sent_realm_len = 0;
4217 uint16_t recv_realm_len = 0;
4219 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
4220 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4221 recv_realm = (uint8_t *) stun_message_find (resp,
4222 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4224 /* check for unauthorized error response */
4225 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
4226 stun_message_get_class (resp) == STUN_ERROR &&
4227 stun_message_find_error (resp, &code) ==
4228 STUN_MESSAGE_RETURN_SUCCESS &&
4229 recv_realm != NULL && recv_realm_len > 0) {
4231 if (code == STUN_ERROR_STALE_NONCE ||
4232 (code == STUN_ERROR_UNAUTHORIZED &&
4233 !(recv_realm_len == sent_realm_len &&
4234 sent_realm != NULL &&
4235 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4236 cand->stun_resp_msg = *resp;
4237 memcpy (cand->stun_resp_buffer, resp->buffer,
4238 stun_message_length (resp));
4239 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
4240 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
4241 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
4243 /* case: a real unauthorized error */
4244 refresh_free (agent, cand);
4247 /* case: STUN error, the check STUN context was freed */
4248 refresh_free (agent, cand);
4260 static gboolean priv_map_reply_to_relay_remove (NiceAgent *agent,
4263 StunTransactionId response_id;
4266 stun_message_id (resp, response_id);
4268 for (i = agent->refresh_list; i; i = i->next) {
4269 CandidateRefresh *cand = i->data;
4270 StunTransactionId request_id;
4271 StunUsageTurnReturn res;
4274 if (!cand->disposing || !cand->stun_message.buffer) {
4278 stun_message_id (&cand->stun_message, request_id);
4280 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
4281 res = stun_usage_turn_refresh_process (resp, &lifetime,
4282 agent_to_turn_compatibility (agent));
4284 nice_debug ("Agent %p : priv_map_reply_to_relay_remove for %p res %d "
4285 "with lifetime %u.", agent, cand, res, lifetime);
4287 if (res != STUN_USAGE_TURN_RETURN_INVALID) {
4288 refresh_free (agent, cand);
4297 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
4298 NiceComponent *component, StunMessage *resp)
4300 StunTransactionId conncheck_id;
4301 StunTransactionId response_id;
4302 stun_message_id (resp, response_id);
4304 if (component->selected_pair.keepalive.stun_message.buffer) {
4305 stun_message_id (&component->selected_pair.keepalive.stun_message,
4307 if (memcmp (conncheck_id, response_id, sizeof(StunTransactionId)) == 0) {
4308 nice_debug ("Agent %p : Keepalive for selected pair received.",
4310 if (component->selected_pair.keepalive.tick_source) {
4311 g_source_destroy (component->selected_pair.keepalive.tick_source);
4312 g_source_unref (component->selected_pair.keepalive.tick_source);
4313 component->selected_pair.keepalive.tick_source = NULL;
4315 component->selected_pair.keepalive.stun_message.buffer = NULL;
4327 NiceComponent *component;
4329 } conncheck_validater_data;
4331 static bool conncheck_stun_validater (StunAgent *agent,
4332 StunMessage *message, uint8_t *username, uint16_t username_len,
4333 uint8_t **password, size_t *password_len, void *user_data)
4335 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
4337 gchar *ufrag = NULL;
4340 gboolean msn_msoc_nice_compatibility =
4341 data->agent->compatibility == NICE_COMPATIBILITY_MSN ||
4342 data->agent->compatibility == NICE_COMPATIBILITY_OC2007;
4344 if (data->agent->compatibility == NICE_COMPATIBILITY_OC2007 &&
4345 stun_message_get_class (message) == STUN_RESPONSE)
4346 i = data->component->remote_candidates;
4348 i = data->component->local_candidates;
4350 for (; i; i = i->next) {
4351 NiceCandidate *cand = i->data;
4355 ufrag = cand->username;
4357 ufrag = data->stream->local_ufrag;
4358 ufrag_len = ufrag? strlen (ufrag) : 0;
4360 if (ufrag && msn_msoc_nice_compatibility)
4361 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
4366 stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d",
4367 username_len, ufrag_len, username_len >= ufrag_len ?
4368 memcmp (username, ufrag, ufrag_len) : 0);
4369 stun_debug_bytes (" username: ", username, username_len);
4370 stun_debug_bytes (" ufrag: ", ufrag, ufrag_len);
4371 if (ufrag_len > 0 && username_len >= ufrag_len &&
4372 memcmp (username, ufrag, ufrag_len) == 0) {
4376 pass = cand->password;
4377 else if (data->stream && data->stream->local_password[0])
4378 pass = data->stream->local_password;
4381 *password = (uint8_t *) pass;
4382 *password_len = strlen (pass);
4384 if (msn_msoc_nice_compatibility) {
4387 data->password = g_base64_decode (pass, &pass_len);
4388 *password = data->password;
4389 *password_len = pass_len;
4393 if (msn_msoc_nice_compatibility)
4396 stun_debug ("Found valid username, returning password: '%s'", *password);
4400 if (msn_msoc_nice_compatibility)
4408 * handle RENOMINATION stun attribute
4409 * @return TRUE if nomination changed. FALSE otherwise
4411 static gboolean conn_check_handle_renomination (NiceAgent *agent, NiceStream *stream,
4412 NiceComponent *component, StunMessage *req,
4413 NiceCandidate *remote_candidate, NiceCandidate *local_candidate)
4416 if (!agent->controlling_mode && NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
4417 agent->support_renomination && remote_candidate && local_candidate)
4419 uint32_t nom_value = 0;
4420 uint16_t nom_len = 0;
4421 const void *value = stun_message_find (req, STUN_ATTRIBUTE_NOMINATION, &nom_len);
4426 memcpy (&nom_value, value, 4);
4427 nom_value = ntohl (nom_value);
4429 nice_debug ("Agent %p : received NOMINATION attr with incorrect octet length %u, expected 4 bytes",
4434 if (nice_debug_is_enabled ()) {
4435 gchar remote_str[INET6_ADDRSTRLEN];
4436 nice_address_to_string(&remote_candidate->addr, remote_str);
4437 nice_debug ("Agent %p : received NOMINATION attr for remote candidate [%s]:%u, value is %u",
4438 agent, remote_str, nice_address_get_port (&remote_candidate->addr), nom_value);
4442 * If another pair is SELECTED, change this pair's priority to be greater than
4443 * selected pair's priority so this pair gets SELECTED!
4445 if (component->selected_pair.priority &&
4446 component->selected_pair.remote && component->selected_pair.remote != remote_candidate &&
4447 component->selected_pair.local && component->selected_pair.local != local_candidate) {
4448 for (lst = stream->conncheck_list; lst; lst = lst->next) {
4449 CandidateCheckPair *pair = lst->data;
4450 if (pair->local == local_candidate && pair->remote == remote_candidate) {
4452 pair->priority = component->selected_pair.priority + 1;
4458 priv_mark_pair_nominated (agent, stream, component, local_candidate, remote_candidate);
4465 * Processing an incoming STUN message.
4467 * @param agent self pointer
4468 * @param stream stream the packet is related to
4469 * @param component component the packet is related to
4470 * @param nicesock socket from which the packet was received
4471 * @param from address of the sender
4472 * @param buf message contents
4473 * @param buf message length
4475 * @pre contents of 'buf' is a STUN message
4477 * @return XXX (what FALSE means exactly?)
4479 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
4480 NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from,
4481 gchar *buf, guint len)
4484 struct sockaddr_storage storage;
4485 struct sockaddr addr;
4487 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
4489 size_t rbuf_len = sizeof (rbuf);
4490 bool control = agent->controlling_mode;
4491 uint8_t uname[NICE_STREAM_MAX_UNAME];
4494 uint16_t username_len;
4497 StunValidationStatus valid;
4498 conncheck_validater_data validater_data = {agent, stream, component, NULL};
4500 NiceCandidate *remote_candidate = NULL;
4501 NiceCandidate *remote_candidate2 = NULL;
4502 NiceCandidate *local_candidate = NULL;
4503 gboolean discovery_msg = FALSE;
4505 nice_address_copy_to_sockaddr (from, &sockaddr.addr);
4507 /* note: contents of 'buf' already validated, so it is
4508 * a valid and fully received STUN message */
4510 if (nice_debug_is_enabled ()) {
4511 gchar tmpbuf[INET6_ADDRSTRLEN];
4512 nice_address_to_string (from, tmpbuf);
4513 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
4514 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
4517 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
4519 valid = stun_agent_validate (&component->stun_agent, &req,
4520 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4522 /* Check for discovery candidates stun agents */
4523 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4524 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4525 for (i = agent->discovery_list; i; i = i->next) {
4526 CandidateDiscovery *d = i->data;
4527 if (d->stream_id == stream->id && d->component_id == component->id &&
4528 d->nicesock == nicesock) {
4529 valid = stun_agent_validate (&d->stun_agent, &req,
4530 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4532 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4535 discovery_msg = TRUE;
4540 /* Check for relay refresh stun agents */
4541 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4542 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4543 for (i = agent->refresh_list; i; i = i->next) {
4544 CandidateRefresh *r = i->data;
4546 nice_debug_verbose ("Comparing r.sid=%u to sid=%u, r.cid=%u to cid=%u and %p and %p to %p",
4547 r->stream_id, stream->id, r->component_id, component->id, r->nicesock,
4548 r->candidate->sockptr, nicesock);
4550 if (r->stream_id == stream->id && r->component_id == component->id &&
4551 (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) {
4552 valid = stun_agent_validate (&r->stun_agent, &req,
4553 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4554 nice_debug ("Validating gave %d", valid);
4555 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4557 discovery_msg = TRUE;
4563 g_free (validater_data.password);
4565 if (valid == STUN_VALIDATION_NOT_STUN ||
4566 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
4567 valid == STUN_VALIDATION_BAD_REQUEST)
4569 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
4574 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
4575 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
4577 if (agent->compatibility != NICE_COMPATIBILITY_MSN &&
4578 agent->compatibility != NICE_COMPATIBILITY_OC2007) {
4579 rbuf_len = stun_agent_build_unknown_attributes_error (&component->stun_agent,
4580 &msg, rbuf, rbuf_len, &req);
4582 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4587 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
4588 nice_debug ("Agent %p : Integrity check failed.", agent);
4590 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4591 &req, STUN_ERROR_UNAUTHORIZED)) {
4592 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4593 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4594 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4595 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4599 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
4600 nice_debug ("Agent %p : Integrity check failed - bad request.", agent);
4601 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4602 &req, STUN_ERROR_BAD_REQUEST)) {
4603 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4604 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4605 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4606 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4611 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
4614 for (i = component->local_candidates; i; i = i->next) {
4615 NiceCandidate *cand = i->data;
4618 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
4621 addr = &cand->base_addr;
4623 if (nice_address_equal (&nicesock->addr, addr) &&
4624 local_candidate_and_socket_compatible (agent, cand, nicesock)) {
4625 local_candidate = cand;
4630 for (i = component->remote_candidates; i; i = i->next) {
4631 NiceCandidate *cand = i->data;
4632 if (nice_address_equal (from, &cand->addr) &&
4633 remote_candidate_and_socket_compatible (agent, local_candidate,
4635 remote_candidate = cand;
4640 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4641 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4642 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4643 /* We need to find which local candidate was used */
4644 for (i = component->remote_candidates;
4645 i != NULL && remote_candidate2 == NULL; i = i->next) {
4646 for (j = component->local_candidates; j; j = j->next) {
4647 gboolean inbound = TRUE;
4648 NiceCandidate *rcand = i->data;
4649 NiceCandidate *lcand = j->data;
4651 /* If we receive a response, then the username is local:remote */
4652 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
4653 if (stun_message_get_class (&req) == STUN_REQUEST ||
4654 stun_message_get_class (&req) == STUN_INDICATION) {
4661 uname_len = priv_create_username (agent, stream,
4662 component->id, rcand, lcand,
4663 uname, sizeof (uname), inbound);
4667 stun_debug ("Comparing usernames of size %d and %d: %d",
4668 username_len, uname_len, username && uname_len == username_len &&
4669 memcmp (username, uname, uname_len) == 0);
4670 stun_debug_bytes (" First username: ", username,
4671 username ? username_len : 0);
4672 stun_debug_bytes (" Second uname: ", uname, uname_len);
4675 uname_len == username_len &&
4676 memcmp (uname, username, username_len) == 0) {
4677 local_candidate = lcand;
4678 remote_candidate2 = rcand;
4685 if (component->remote_candidates &&
4686 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
4687 local_candidate == NULL &&
4688 discovery_msg == FALSE) {
4689 /* if we couldn't match the username and the stun agent has
4690 IGNORE_CREDENTIALS then we have an integrity check failing.
4691 This could happen with the race condition of receiving connchecks
4692 before the remote candidates are added. Just drop the message, and let
4693 the retransmissions make it work. */
4694 nice_debug ("Agent %p : Username check failed.", agent);
4698 /* This is most likely caused by a second response to a request which
4699 * already has received a valid reply.
4701 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4702 nice_debug ("Agent %p : Valid STUN response for which we don't have a request, ignoring", agent);
4706 if (valid != STUN_VALIDATION_SUCCESS) {
4707 nice_debug ("Agent %p : STUN message is unsuccessful %d, ignoring", agent, valid);
4712 if (stun_message_get_class (&req) == STUN_REQUEST) {
4713 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4714 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4715 if (local_candidate && remote_candidate2) {
4718 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
4719 username = (uint8_t *) stun_message_find (&req,
4720 STUN_ATTRIBUTE_USERNAME, &username_len);
4721 uname_len = priv_create_username (agent, stream,
4722 component->id, remote_candidate2, local_candidate,
4723 uname, sizeof (uname), FALSE);
4724 memcpy (username, uname, MIN (uname_len, username_len));
4726 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
4728 req.key_len = key_len;
4729 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4730 req.key = g_base64_decode ((gchar *) local_candidate->password,
4732 req.key_len = key_len;
4735 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
4736 "Ignoring request", agent);
4741 rbuf_len = sizeof (rbuf);
4742 res = stun_usage_ice_conncheck_create_reply (&component->stun_agent, &req,
4743 &msg, rbuf, &rbuf_len, &sockaddr.storage, sizeof (sockaddr),
4744 &control, agent->tie_breaker,
4745 agent_to_ice_compatibility (agent));
4747 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4748 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4752 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
4753 priv_check_for_role_conflict (agent, control);
4755 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
4756 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
4757 /* case 1: valid incoming request, send a reply/error */
4758 bool use_candidate =
4759 stun_usage_ice_conncheck_use_candidate (&req);
4760 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
4762 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4763 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4764 agent->compatibility == NICE_COMPATIBILITY_OC2007)
4765 use_candidate = TRUE;
4767 if (stream->initial_binding_request_received != TRUE)
4768 agent_signal_initial_binding_request_received (agent, stream);
4770 if (remote_candidate == NULL) {
4771 nice_debug ("Agent %p : No matching remote candidate for incoming "
4772 "check -> peer-reflexive candidate.", agent);
4773 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
4774 agent, stream, component, priority, from, nicesock,
4776 remote_candidate2 ? remote_candidate2 : remote_candidate);
4777 if(remote_candidate && stream->remote_ufrag[0]) {
4778 if (local_candidate &&
4779 local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
4780 priv_conn_check_add_for_candidate_pair_matched (agent,
4781 stream->id, component, local_candidate, remote_candidate,
4782 NICE_CHECK_WAITING);
4784 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
4788 nice_component_add_valid_candidate (agent, component, remote_candidate);
4790 priv_reply_to_conn_check (agent, stream, component, local_candidate,
4791 remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate);
4793 if (stream->remote_ufrag[0] == 0) {
4794 /* case: We've got a valid binding request to a local candidate
4795 * but we do not yet know remote credentials.
4796 * As per sect 7.2 of ICE (ID-19), we send a reply
4797 * immediately but postpone all other processing until
4798 * we get information about the remote candidates */
4800 /* step: send a reply immediately but postpone other processing */
4801 priv_store_pending_check (agent, component, from, nicesock,
4802 username, username_len, priority, use_candidate);
4803 priv_print_conn_check_lists (agent, G_STRFUNC, ", icheck stored");
4806 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
4807 agent, strerror(errno));
4811 /* case 2: not a new request, might be a reply... */
4812 gboolean trans_found = FALSE;
4814 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
4816 /* step: let's try to match the response to an existing check context */
4817 if (trans_found != TRUE)
4818 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
4819 component, nicesock, from, local_candidate, remote_candidate, &req);
4821 /* step: let's try to match the response to an existing discovery */
4822 if (trans_found != TRUE)
4823 trans_found = priv_map_reply_to_discovery_request (agent, &req);
4825 /* step: let's try to match the response to an existing turn allocate */
4826 if (trans_found != TRUE)
4827 trans_found = priv_map_reply_to_relay_request (agent, &req);
4829 /* step: let's try to match the response to an existing turn refresh */
4830 if (trans_found != TRUE)
4831 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
4833 if (trans_found != TRUE)
4834 trans_found = priv_map_reply_to_relay_remove (agent, &req);
4836 /* step: let's try to match the response to an existing keepalive conncheck */
4837 if (trans_found != TRUE)
4838 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
4841 if (trans_found != TRUE)
4842 nice_debug ("Agent %p : Unable to match to an existing transaction, "
4843 "probably a keepalive.", agent);
4846 /* RENOMINATION attribute support */
4847 conn_check_handle_renomination(agent, stream, component, &req, remote_candidate, local_candidate);
4852 /* Remove all pointers to the given @sock from the connection checking process.
4853 * These are entirely NiceCandidates pointed to from various places. */
4855 conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
4860 if (component->selected_pair.local &&
4861 component->selected_pair.local->sockptr == sock &&
4862 component->state == NICE_COMPONENT_STATE_READY) {
4863 nice_debug ("Agent %p: Selected pair socket %p has been destroyed, "
4864 "declaring failed", agent, sock);
4865 agent_signal_component_state_change (agent,
4866 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
4869 /* Prune from the candidate check pairs. */
4870 for (l = stream->conncheck_list; l != NULL;) {
4871 CandidateCheckPair *p = l->data;
4872 GSList *next = l->next;
4874 if ((p->local != NULL && p->local->sockptr == sock) ||
4875 (p->remote != NULL && p->remote->sockptr == sock) ||
4876 (p->sockptr == sock)) {
4877 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
4879 candidate_check_pair_fail (stream, agent, p);
4880 candidate_check_pair_free (agent, p);
4881 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l);