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_fail (NiceStream *stream,
75 NiceAgent *agent, CandidateCheckPair *p);
76 static void candidate_check_pair_free (NiceAgent *agent,
77 CandidateCheckPair *pair);
78 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
79 NiceAgent *agent, guint stream_id, NiceComponent *component,
80 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state);
81 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
83 static void priv_schedule_next (NiceAgent *agent);
85 static gint64 priv_timer_remainder (gint64 timer, gint64 now)
90 return (timer - now) / 1000;
94 priv_state_to_gchar (NiceCheckState state)
97 case NICE_CHECK_WAITING:
99 case NICE_CHECK_IN_PROGRESS:
101 case NICE_CHECK_SUCCEEDED:
103 case NICE_CHECK_FAILED:
105 case NICE_CHECK_FROZEN:
107 case NICE_CHECK_DISCOVERED:
110 g_assert_not_reached ();
115 priv_state_to_string (NiceCheckState state)
118 case NICE_CHECK_WAITING:
120 case NICE_CHECK_IN_PROGRESS:
121 return "IN_PROGRESS";
122 case NICE_CHECK_SUCCEEDED:
124 case NICE_CHECK_FAILED:
126 case NICE_CHECK_FROZEN:
128 case NICE_CHECK_DISCOVERED:
131 g_assert_not_reached ();
135 #define SET_PAIR_STATE( a, p, s ) G_STMT_START{\
138 nice_debug ("Agent %p : pair %p state %s (%s)", \
139 a, p, priv_state_to_string (s), G_STRFUNC); \
143 priv_ice_return_to_string (StunUsageIceReturn ice_return)
145 switch (ice_return) {
146 case STUN_USAGE_ICE_RETURN_SUCCESS:
148 case STUN_USAGE_ICE_RETURN_ERROR:
150 case STUN_USAGE_ICE_RETURN_INVALID:
152 case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT:
153 return "role conflict";
154 case STUN_USAGE_ICE_RETURN_INVALID_REQUEST:
155 return "invalid request";
156 case STUN_USAGE_ICE_RETURN_INVALID_METHOD:
157 return "invalid method";
158 case STUN_USAGE_ICE_RETURN_MEMORY_ERROR:
159 return "memory error";
160 case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS:
161 return "invalid address";
162 case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS:
163 return "no mapped address";
165 g_assert_not_reached ();
170 priv_socket_type_to_string (NiceSocketType type)
173 case NICE_SOCKET_TYPE_UDP_BSD:
175 case NICE_SOCKET_TYPE_TCP_BSD:
177 case NICE_SOCKET_TYPE_PSEUDOSSL:
179 case NICE_SOCKET_TYPE_HTTP:
181 case NICE_SOCKET_TYPE_SOCKS5:
183 case NICE_SOCKET_TYPE_UDP_TURN:
185 case NICE_SOCKET_TYPE_UDP_TURN_OVER_TCP:
187 case NICE_SOCKET_TYPE_TCP_ACTIVE:
189 case NICE_SOCKET_TYPE_TCP_PASSIVE:
191 case NICE_SOCKET_TYPE_TCP_SO:
194 g_assert_not_reached ();
199 * Dump the component list of incoming checks
202 print_component_incoming_checks (NiceAgent *agent, NiceStream *stream,
203 NiceComponent *component)
207 for (i = component->incoming_checks.head; i; i = i->next) {
208 IncomingCheck *icheck = i->data;
209 gchar tmpbuf1[INET6_ADDRSTRLEN] = {0};
210 gchar tmpbuf2[INET6_ADDRSTRLEN] = {0};
212 nice_address_to_string (&icheck->local_socket->addr, tmpbuf1);
213 nice_address_to_string (&icheck->from, tmpbuf2);
214 nice_debug ("Agent %p : *** sc=%d/%d : icheck %p : "
215 "sock %s [%s]:%u > [%s]:%u",
216 agent, stream->id, component->id, icheck,
217 priv_socket_type_to_string (icheck->local_socket->type),
218 tmpbuf1, nice_address_get_port (&icheck->local_socket->addr),
219 tmpbuf2, nice_address_get_port (&icheck->from));
224 * Dump the conncheck lists of the agent
227 priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail)
233 if (!nice_debug_is_verbose ())
236 now = g_get_monotonic_time ();
238 #define PRIORITY_LEN 32
240 nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
241 agent, where, detail ? detail : "");
242 nice_debug ("Agent %p : *** agent nomination mode %s, %s",
243 agent, agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
244 "aggressive" : "regular",
245 agent->controlling_mode ? "controlling" : "controlled");
246 for (i = agent->streams; i ; i = i->next) {
247 NiceStream *stream = i->data;
248 for (j = 1; j <= stream->n_components; j++) {
249 NiceComponent *component;
250 for (k = stream->conncheck_list; k ; k = k->next) {
251 CandidateCheckPair *pair = k->data;
252 if (pair->component_id == j) {
253 gchar local_addr[INET6_ADDRSTRLEN];
254 gchar remote_addr[INET6_ADDRSTRLEN];
255 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
257 nice_address_to_string (&pair->local->addr, local_addr);
258 nice_address_to_string (&pair->remote->addr, remote_addr);
259 nice_candidate_pair_priority_to_string (pair->priority, priority);
261 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
262 "f=%s t=%s:%s sock=%s "
263 "%s:[%s]:%u > %s:[%s]:%u prio=%s/%08x state=%c%s%s%s%s",
264 agent, pair->stream_id, pair->component_id, pair,
266 nice_candidate_type_to_string (pair->local->type),
267 nice_candidate_type_to_string (pair->remote->type),
268 priv_socket_type_to_string (pair->sockptr->type),
269 nice_candidate_transport_to_string (pair->local->transport),
270 local_addr, nice_address_get_port (&pair->local->addr),
271 nice_candidate_transport_to_string (pair->remote->transport),
272 remote_addr, nice_address_get_port (&pair->remote->addr),
273 priority, pair->stun_priority,
274 priv_state_to_gchar (pair->state),
275 pair->valid ? "V" : "",
276 pair->nominated ? "N" : "",
277 pair->use_candidate_on_next_check ? "C" : "",
278 g_slist_find (agent->triggered_check_queue, pair) ? "T" : "");
280 for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) {
281 StunTransaction *stun = l->data;
282 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
283 "stun#=%d timer=%d/%d %" G_GINT64_FORMAT "/%dms buf=%p %s",
284 agent, pair->stream_id, pair->component_id, pair, m,
285 stun->timer.retransmissions, stun->timer.max_retransmissions,
286 stun->timer.delay - priv_timer_remainder (stun->next_tick, now),
288 stun->message.buffer,
289 (m == 0 && pair->retransmit) ? "(R)" : "");
293 if (agent_find_component (agent, stream->id, j, NULL, &component))
294 print_component_incoming_checks (agent, stream, component);
299 /* Add the pair to the triggered checks list, if not already present
302 priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
306 if (agent->triggered_check_queue == NULL ||
307 g_slist_find (agent->triggered_check_queue, pair) == NULL) {
308 agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair);
309 priv_schedule_next (agent);
313 /* Remove the pair from the triggered checks list
316 priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
319 agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair);
322 /* Get the pair from the triggered checks list
324 static CandidateCheckPair *
325 priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
327 CandidateCheckPair *pair = NULL;
329 if (agent->triggered_check_queue) {
330 pair = (CandidateCheckPair *)agent->triggered_check_queue->data;
331 priv_remove_pair_from_triggered_check_queue (agent, pair);
337 * Finds the next connectivity check in WAITING state.
339 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
343 /* note: list is sorted in priority order to first waiting check has
344 * the highest priority */
345 for (i = conn_check_list; i ; i = i->next) {
346 CandidateCheckPair *p = i->data;
347 if (p->state == NICE_CHECK_WAITING)
355 * Initiates a new connectivity check for a ICE candidate pair.
357 * @return TRUE on success, FALSE on error
360 priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
362 SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS);
363 if (conn_check_send (agent, pair)) {
365 NiceComponent *component;
367 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
368 &stream, &component)) {
369 nice_debug ("Could not find stream or component in conn_check_initiate");
370 SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED);
373 candidate_check_pair_fail (stream, agent, pair);
374 conn_check_update_check_list_state_for_ready (agent, stream, component);
381 * Unfreezes the next connectivity check in the list. Follows the
382 * algorithm defined in sect 6.1.2.6 (Computing Candidate Pair States)
383 * and sect 6.1.4.2 (Performing Connectivity Checks) of the ICE spec
386 * Note that this algorithm is slightly simplified compared to previous
387 * version of the spec (RFC5245), and this new version is now
390 * @return TRUE on success, and FALSE if no frozen candidates were found.
393 priv_conn_check_unfreeze_next (NiceAgent *agent)
396 GSList *foundation_list = NULL;
397 gboolean result = FALSE;
399 /* While a pair in state waiting exists, we do nothing */
400 for (i = agent->streams; i ; i = i->next) {
401 NiceStream *s = i->data;
402 for (j = s->conncheck_list; j ; j = j->next) {
403 CandidateCheckPair *p = j->data;
405 if (p->state == NICE_CHECK_WAITING)
410 /* When there are no more pairs in waiting state, we unfreeze some
411 * pairs, so that we get a single waiting pair per foundation.
413 for (i = agent->streams; i ; i = i->next) {
414 NiceStream *s = i->data;
415 for (j = s->conncheck_list; j ; j = j->next) {
416 CandidateCheckPair *p = j->data;
418 if (g_slist_find_custom (foundation_list, p->foundation,
419 (GCompareFunc)strcmp))
422 if (p->state == NICE_CHECK_FROZEN) {
423 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
424 agent, p, p->stream_id, p->component_id, p->foundation);
425 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
426 foundation_list = g_slist_prepend (foundation_list, p->foundation);
431 g_slist_free (foundation_list);
433 /* We dump the conncheck list when something interesting happened, ie
434 * when we unfroze some pairs.
437 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
443 * Unfreezes the related connectivity check in the list after
444 * check 'success_check' has successfully completed.
446 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
448 * Note that this algorithm is slightly simplified compared to previous
449 * version of the spec (RFC5245)
451 * @param agent context
452 * @param pair a pair, whose connectivity check has just succeeded
456 conn_check_unfreeze_related (NiceAgent *agent, CandidateCheckPair *pair)
459 gboolean result = FALSE;
462 g_assert (pair->state == NICE_CHECK_SUCCEEDED);
464 for (i = agent->streams; i ; i = i->next) {
465 NiceStream *s = i->data;
466 for (j = s->conncheck_list; j ; j = j->next) {
467 CandidateCheckPair *p = j->data;
469 /* The states for all other Frozen candidates pairs in all
470 * checklists with the same foundation is set to waiting
472 if (p->state == NICE_CHECK_FROZEN &&
473 strncmp (p->foundation, pair->foundation,
474 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
475 nice_debug ("Agent %p : Unfreezing check %p "
476 "(after successful check %p).", agent, p, pair);
477 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
482 /* We dump the conncheck list when something interesting happened, ie
483 * when we unfroze some pairs.
486 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
490 * Unfreezes this connectivity check if its foundation is the same than
491 * the foundation of an already succeeded pair.
493 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
495 * @param agent context
496 * @param pair a pair, whose state is frozen
500 priv_conn_check_unfreeze_maybe (NiceAgent *agent, CandidateCheckPair *pair)
503 gboolean result = FALSE;
506 g_assert (pair->state == NICE_CHECK_FROZEN);
508 for (i = agent->streams; i ; i = i->next) {
509 NiceStream *s = i->data;
510 for (j = s->conncheck_list; j ; j = j->next) {
511 CandidateCheckPair *p = j->data;
513 if (p->state == NICE_CHECK_SUCCEEDED &&
514 strncmp (p->foundation, pair->foundation,
515 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
516 nice_debug ("Agent %p : Unfreezing check %p "
517 "(after successful check %p).", agent, pair, p);
518 SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
523 /* We dump the conncheck list when something interesting happened, ie
524 * when we unfroze some pairs.
527 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
531 conn_check_stun_transactions_count (NiceAgent *agent)
536 for (i = agent->streams; i ; i = i->next) {
537 NiceStream *s = i->data;
538 for (j = s->conncheck_list; j ; j = j->next) {
539 CandidateCheckPair *p = j->data;
541 if (p->stun_transactions)
542 count += g_slist_length (p->stun_transactions);
549 * Create a new STUN transaction and add it to the list
550 * of ongoing stun transactions of a pair.
552 * @pair the pair the new stun transaction should be added to.
553 * @return the created stun transaction.
555 static StunTransaction *
556 priv_add_stun_transaction (CandidateCheckPair *pair)
558 StunTransaction *stun = g_slice_new0 (StunTransaction);
559 pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun);
560 pair->retransmit = TRUE;
565 * Forget a STUN transaction.
567 * @data the stun transaction to be forgotten.
568 * @user_data the component contained the concerned stun agent.
571 priv_forget_stun_transaction (gpointer data, gpointer user_data)
573 StunTransaction *stun = data;
574 NiceComponent *component = user_data;
575 StunTransactionId id;
577 if (stun->message.buffer != NULL) {
578 stun_message_id (&stun->message, id);
579 stun_agent_forget_transaction (&component->stun_agent, id);
584 priv_free_stun_transaction (gpointer data)
586 g_slice_free (StunTransaction, data);
590 * Remove a STUN transaction from a pair, and forget it
591 * from the related component stun agent.
593 * @pair the pair the stun transaction should be removed from.
594 * @stun the stun transaction to be removed.
595 * @component the component containing the stun agent used to
596 * forget the stun transaction.
599 priv_remove_stun_transaction (CandidateCheckPair *pair,
600 StunTransaction *stun, NiceComponent *component)
602 priv_forget_stun_transaction (stun, component);
603 pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun);
604 priv_free_stun_transaction (stun);
605 if (pair->stun_transactions == NULL)
606 pair->retransmit = FALSE;
610 * Remove all STUN transactions from a pair, and forget them
611 * from the related component stun agent.
613 * @pair the pair the stun list should be cleared.
614 * @component the component containing the stun agent used to
615 * forget the stun transactions.
618 priv_free_all_stun_transactions (CandidateCheckPair *pair,
619 NiceComponent *component)
622 g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component);
623 g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction);
624 pair->stun_transactions = NULL;
625 pair->retransmit = FALSE;
629 candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
631 NiceComponent *component;
633 component = nice_stream_find_component_by_id (stream, p->component_id);
634 SET_PAIR_STATE (agent, p, NICE_CHECK_FAILED);
635 priv_free_all_stun_transactions (p, component);
637 /* Ensure related succeeded-discovered pairs change to state failed
638 * simultaneously, to avoid leaving dangling pointers if one is freeed
639 * while the other is still in the conncheck list. Such transition is
640 * very rare, and only occurs in regular nomination mode, when the
641 * network conditions change between the time the pair is initially
642 * discovered and the time it is rechecked with the use-candidate
645 if (p->discovered_pair != NULL) {
646 nice_debug ("Agent %p : related discovered pair %p of pair %p "
647 "will fail too.", agent, p->discovered_pair, p);
648 SET_PAIR_STATE (agent, p->discovered_pair, NICE_CHECK_FAILED);
653 * Helper function for connectivity check timer callback that
654 * runs through the stream specific part of the state machine.
656 * @param agent context pointer
657 * @param stream which stream (of the agent)
658 * @return will return TRUE if a new stun request has been sent
661 priv_conn_check_tick_stream (NiceAgent *agent, NiceStream *stream)
663 gboolean pair_failed = FALSE;
665 unsigned int timeout;
668 now = g_get_monotonic_time ();
670 /* step: process ongoing STUN transactions */
671 for (i = stream->conncheck_list; i ; i = i->next) {
672 CandidateCheckPair *p = i->data;
673 gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
674 NiceComponent *component;
675 guint index = 0, remaining = 0;
677 if (p->stun_transactions == NULL)
680 if (!agent_find_component (agent, p->stream_id, p->component_id,
684 j = p->stun_transactions;
686 StunTransaction *stun = j->data;
687 GSList *next = j->next;
689 if (now < stun->next_tick)
692 switch (stun_timer_refresh (&stun->timer)) {
693 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
694 timer_return_timeout:
695 priv_remove_stun_transaction (p, stun, component);
697 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
698 /* case: retransmission stopped, due to the nomination of
699 * a pair with a higher priority than this in-progress pair,
700 * ICE spec, sect 8.1.2 "Updating States", item 2.2
702 if (!p->retransmit || index > 0)
703 goto timer_return_timeout;
705 /* case: not ready, so schedule a new timeout */
706 timeout = stun_timer_remainder (&stun->timer);
708 nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
709 "(timer=%d/%d %d/%dms).",
711 stun->timer.retransmissions, stun->timer.max_retransmissions,
712 stun->timer.delay - timeout, stun->timer.delay);
714 agent_socket_send (p->sockptr, &p->remote->addr,
715 stun_message_length (&stun->message),
716 (gchar *)stun->buffer);
718 /* note: convert from milli to microseconds for g_time_val_add() */
719 stun->next_tick = now + timeout * 1000;
722 case STUN_USAGE_TIMER_RETURN_SUCCESS:
723 timeout = stun_timer_remainder (&stun->timer);
724 /* note: convert from milli to microseconds for g_time_val_add() */
725 stun->next_tick = now + timeout * 1000;
729 g_assert_not_reached();
736 if (remaining == 0) {
737 nice_address_to_string (&p->local->addr, tmpbuf1);
738 nice_address_to_string (&p->remote->addr, tmpbuf2);
739 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
741 nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
742 tmpbuf1, nice_address_get_port (&p->local->addr),
743 tmpbuf2, nice_address_get_port (&p->remote->addr));
744 candidate_check_pair_fail (stream, agent, p);
747 /* perform a check if a transition state from connected to
748 * ready can be performed. This may happen here, when the last
749 * in-progress pair has expired its retransmission count
750 * in priv_conn_check_tick_stream(), which is a condition to
751 * make the transition connected to ready.
753 conn_check_update_check_list_state_for_ready (agent, stream, component);
758 priv_print_conn_check_lists (agent, G_STRFUNC, ", retransmission failed");
764 priv_conn_check_ordinary_check (NiceAgent *agent, NiceStream *stream)
766 CandidateCheckPair *pair;
767 gboolean stun_sent = FALSE;
769 /* step: perform an ordinary check, sec 6.1.4.2 point 3. (Performing
770 * Connectivity Checks) of ICE spec (RFC8445)
771 * note: This code is executed when the triggered checks list is
772 * empty, and when no STUN message has been sent (pacing constraint)
774 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
776 /* step: there is no candidate in waiting state, try to unfreeze
777 * some pairs and retry, sect 6.1.4.2 point 2. (Performing Connectivity
778 * Checks) of ICE spec (RFC8445)
780 priv_conn_check_unfreeze_next (agent);
781 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
785 stun_sent = priv_conn_check_initiate (agent, pair);
786 priv_print_conn_check_lists (agent, G_STRFUNC,
787 ", initiated an ordinary connection check");
793 priv_conn_check_triggered_check (NiceAgent *agent, NiceStream *stream)
795 CandidateCheckPair *pair;
796 gboolean stun_sent = FALSE;
798 /* step: perform a test from the triggered checks list,
799 * sect 6.1.4.2 point 1. (Performing Connectivity Checks) of ICE
802 pair = priv_get_pair_from_triggered_check_queue (agent);
805 stun_sent = priv_conn_check_initiate (agent, pair);
806 priv_print_conn_check_lists (agent, G_STRFUNC,
807 ", initiated a connection check from triggered check list");
814 priv_conn_check_tick_stream_nominate (NiceAgent *agent, NiceStream *stream)
816 gboolean keep_timer_going = FALSE;
817 /* s_xxx counters are stream-wide */
818 guint s_inprogress = 0;
819 guint s_succeeded = 0;
820 guint s_discovered = 0;
821 guint s_nominated = 0;
822 guint s_waiting_for_nomination = 0;
826 CandidateCheckPair *other_stream_pair = NULL;
829 /* Search for a nominated pair (or selected to be nominated pair)
830 * from another stream.
832 for (i = agent->streams; i ; i = i->next) {
833 NiceStream *s = i->data;
834 if (s->id == stream->id)
836 for (j = s->conncheck_list; j ; j = j->next) {
837 CandidateCheckPair *p = j->data;
838 if (p->nominated || (p->use_candidate_on_next_check &&
839 p->state != NICE_CHECK_FAILED)) {
840 other_stream_pair = p;
844 if (other_stream_pair)
848 /* we compute some stream-wide counter values */
849 for (i = stream->conncheck_list; i ; i = i->next) {
850 CandidateCheckPair *p = i->data;
851 if (p->state == NICE_CHECK_FROZEN)
853 else if (p->state == NICE_CHECK_IN_PROGRESS)
855 else if (p->state == NICE_CHECK_WAITING)
857 else if (p->state == NICE_CHECK_SUCCEEDED)
859 else if (p->state == NICE_CHECK_DISCOVERED)
864 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
867 else if ((p->state == NICE_CHECK_SUCCEEDED ||
868 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
869 s_waiting_for_nomination++;
872 /* note: keep the timer going as long as there is work to be done */
874 keep_timer_going = TRUE;
876 if (s_nominated < stream->n_components &&
877 s_waiting_for_nomination) {
878 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
879 if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
880 agent->controlling_mode) {
881 #define NICE_MIN_NUMBER_OF_VALID_PAIRS 2
882 /* ICE 8.1.1.1 Regular nomination
883 * we choose to nominate the valid pair of a component if
884 * - there is no pair left frozen, waiting or in-progress, or
885 * - if there are at least two valid pairs, or
886 * - if there is at least one valid pair of type HOST-HOST
888 * This is the "stopping criterion" described in 8.1.1.1, and is
889 * a "local optimization" between accumulating more valid pairs,
890 * and limiting the time spent waiting for in-progress connections
891 * checks until they finally fail.
893 for (i = stream->components; i; i = i->next) {
894 NiceComponent *component = i->data;
895 CandidateCheckPair *other_component_pair = NULL;
896 CandidateCheckPair *this_component_pair = NULL;
897 NiceCandidate *lcand1 = NULL;
898 NiceCandidate *rcand1 = NULL;
899 NiceCandidate *lcand2, *rcand2;
900 gboolean already_done = FALSE;
901 gboolean found_other_component_pair = FALSE;
902 gboolean found_other_stream_pair = FALSE;
903 gboolean first_nomination = FALSE;
904 gboolean stopping_criterion;
905 /* p_xxx counters are component-wide */
909 guint p_inprogress = 0;
910 guint p_host_host_valid = 0;
912 /* we compute some component-wide counter values */
913 for (j = stream->conncheck_list; j ; j = j->next) {
914 CandidateCheckPair *p = j->data;
915 if (p->component_id == component->id) {
916 /* verify that the choice of the pair to be nominated
917 * has not already been done
919 if (p->use_candidate_on_next_check)
921 if (p->state == NICE_CHECK_FROZEN)
923 else if (p->state == NICE_CHECK_WAITING)
925 else if (p->state == NICE_CHECK_IN_PROGRESS)
930 p->local->type == NICE_CANDIDATE_TYPE_HOST &&
931 p->remote->type == NICE_CANDIDATE_TYPE_HOST)
939 /* Search for a nominated pair (or selected to be nominated pair)
940 * from another component of this stream.
942 for (j = stream->conncheck_list; j ; j = j->next) {
943 CandidateCheckPair *p = j->data;
944 if (p->component_id == component->id)
946 if (p->nominated || (p->use_candidate_on_next_check &&
947 p->state != NICE_CHECK_FAILED)) {
948 other_component_pair = p;
953 if (other_stream_pair == NULL && other_component_pair == NULL)
954 first_nomination = TRUE;
956 /* We choose a pair to be nominated in the list of valid
959 * this pair will be the one with the highest priority,
960 * when we don't have other nominated pairs in other
961 * components and in other streams
963 * this pair will be a pair compatible with another nominated
964 * pair from another component if we found one.
966 * else this pair will be a pair compatible with another
967 * nominated pair from another stream if we found one.
970 for (j = stream->conncheck_list; j ; j = j->next) {
971 CandidateCheckPair *p = j->data;
972 /* note: highest priority item selected (list always sorted) */
973 if (p->component_id == component->id &&
975 !p->use_candidate_on_next_check &&
977 /* According a ICE spec, sect 8.1.1.1. "Regular
978 * Nomination", we enqueue the check that produced this
979 * valid pair. When this pair has been discovered, we want
980 * to test its parent pair instead.
982 if (p->succeeded_pair != NULL) {
983 g_assert (p->state == NICE_CHECK_DISCOVERED);
984 p = p->succeeded_pair;
986 g_assert (p->state == NICE_CHECK_SUCCEEDED);
988 if (this_component_pair == NULL)
989 /* highest priority pair */
990 this_component_pair = p;
995 if (first_nomination)
996 /* use the highest priority pair */
999 if (other_component_pair) {
1000 lcand2 = other_component_pair->local;
1001 rcand2 = other_component_pair->remote;
1003 if (other_component_pair &&
1004 lcand1->transport == lcand2->transport &&
1005 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1006 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1007 /* else continue the research with lower priority
1008 * pairs, compatible with a nominated pair of
1011 this_component_pair = p;
1012 found_other_component_pair = TRUE;
1016 if (other_stream_pair) {
1017 lcand2 = other_stream_pair->local;
1018 rcand2 = other_stream_pair->remote;
1020 if (other_stream_pair &&
1021 other_component_pair == NULL &&
1022 lcand1->transport == lcand2->transport &&
1023 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1024 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1025 /* else continue the research with lower priority
1026 * pairs, compatible with a nominated pair of
1029 this_component_pair = p;
1030 found_other_stream_pair = TRUE;
1036 /* No valid pair for this component */
1037 if (this_component_pair == NULL)
1040 /* The stopping criterion tries to select a set of pairs of
1041 * the same kind (transport/type) for all components of a
1042 * stream, and for all streams, when possible (see last
1045 * When no stream has nominated a pair yet, we apply the
1046 * following criterion :
1047 * - stop if we have a valid host-host pair
1048 * - or stop if we have at least "some* (2 in the current
1049 * implementation) valid pairs, and select the best one
1050 * - or stop if the conncheck cannot evolve more
1052 * Else when the stream has a nominated pair in another
1053 * component we apply this criterion:
1054 * - stop if we have a valid pair of the same kind than this
1055 * other nominated pair.
1056 * - or stop if the conncheck cannot evolve more
1058 * Else when another stream has a nominated pair we apply the
1059 * following criterion:
1060 * - stop if we have a valid pair of the same kind than the
1061 * other nominated pair.
1062 * - or stop if the conncheck cannot evolve more
1064 * When no further evolution of the conncheck is possible, we
1065 * prefer to select the best valid pair we have, *even* if it
1066 * is not compatible with the transport of another stream of
1067 * component. We think it's still a better choice than marking
1068 * this component 'failed'.
1070 stopping_criterion = FALSE;
1071 if (first_nomination && p_host_host_valid > 0) {
1072 stopping_criterion = TRUE;
1073 nice_debug ("Agent %p : stopping criterion: "
1074 "valid host-host pair", agent);
1075 } else if (first_nomination &&
1076 p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS) {
1077 stopping_criterion = TRUE;
1078 nice_debug ("Agent %p : stopping criterion: "
1079 "*some* valid pairs", agent);
1080 } else if (found_other_component_pair) {
1081 stopping_criterion = TRUE;
1082 nice_debug ("Agent %p : stopping criterion: "
1083 "matching pair in another component", agent);
1084 } else if (found_other_stream_pair) {
1085 stopping_criterion = TRUE;
1086 nice_debug ("Agent %p : stopping criterion: "
1087 "matching pair in another stream", agent);
1088 } else if (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0) {
1089 stopping_criterion = TRUE;
1090 nice_debug ("Agent %p : stopping criterion: "
1091 "no more pairs to check", agent);
1094 if (!stopping_criterion)
1097 /* when the stopping criterion is reached, we add the
1098 * selected pair for this component to the triggered checks
1101 nice_debug ("Agent %p : restarting check of %s:%s pair %p with "
1102 "USE-CANDIDATE attrib (regular nomination) for "
1103 "stream %d component %d", agent,
1104 nice_candidate_transport_to_string (
1105 this_component_pair->local->transport),
1106 nice_candidate_transport_to_string (
1107 this_component_pair->remote->transport),
1108 this_component_pair, stream->id, component->id);
1109 this_component_pair->use_candidate_on_next_check = TRUE;
1110 priv_add_pair_to_triggered_check_queue (agent, this_component_pair);
1111 keep_timer_going = TRUE;
1114 } else if (agent->controlling_mode) {
1115 for (i = stream->components; i; i = i->next) {
1116 NiceComponent *component = i->data;
1118 for (j = stream->conncheck_list; j ; j = j->next) {
1119 CandidateCheckPair *p = j->data;
1120 /* note: highest priority item selected (list always sorted) */
1121 if (p->component_id == component->id &&
1122 (p->state == NICE_CHECK_SUCCEEDED ||
1123 p->state == NICE_CHECK_DISCOVERED)) {
1124 nice_debug ("Agent %p : restarting check of pair %p as the "
1125 "nominated pair.", agent, p);
1126 p->nominated = TRUE;
1127 conn_check_update_selected_pair (agent, component, p);
1128 priv_add_pair_to_triggered_check_queue (agent, p);
1129 keep_timer_going = TRUE;
1130 break; /* move to the next component */
1136 if (stream->tick_counter++ % 50 == 0)
1137 nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, "
1138 "%u in-progress, %u waiting, %u succeeded, %u discovered, "
1139 "%u nominated, %u waiting-for-nom, %u valid",
1140 agent, stream->id, stream->tick_counter,
1141 s_frozen, s_inprogress, s_waiting, s_succeeded, s_discovered,
1142 s_nominated, s_waiting_for_nomination, s_valid);
1144 return keep_timer_going;
1149 conn_check_stop (NiceAgent *agent)
1151 if (agent->conncheck_timer_source == NULL)
1154 g_source_destroy (agent->conncheck_timer_source);
1155 g_source_unref (agent->conncheck_timer_source);
1156 agent->conncheck_timer_source = NULL;
1157 agent->conncheck_ongoing_idle_delay = 0;
1162 * Timer callback that handles initiating and managing connectivity
1163 * checks (paced by the Ta timer).
1165 * This function is designed for the g_timeout_add() interface.
1167 * @return will return FALSE when no more pending timers.
1169 static gboolean priv_conn_check_tick_agent_locked (NiceAgent *agent,
1172 gboolean keep_timer_going = FALSE;
1173 gboolean stun_sent = FALSE;
1176 /* step: process triggered checks
1177 * these steps are ordered by priority, since a single stun request
1178 * is sent per callback, we process the important steps first.
1180 * perform a single stun request per timer callback,
1181 * to respect stun pacing
1183 for (i = agent->streams; i && !stun_sent; i = i->next) {
1184 NiceStream *stream = i->data;
1186 stun_sent = priv_conn_check_triggered_check (agent, stream);
1189 /* step: process ongoing STUN transactions */
1190 for (i = agent->streams; i && !stun_sent; i = i->next) {
1191 NiceStream *stream = i->data;
1193 stun_sent = priv_conn_check_tick_stream (agent, stream);
1196 /* step: process ordinary checks */
1197 for (i = agent->streams; i && !stun_sent; i = i->next) {
1198 NiceStream *stream = i->data;
1200 stun_sent = priv_conn_check_ordinary_check (agent, stream);
1204 keep_timer_going = TRUE;
1206 /* step: try to nominate a pair
1208 for (i = agent->streams; i; i = i->next) {
1209 NiceStream *stream = i->data;
1211 if (priv_conn_check_tick_stream_nominate (agent, stream))
1212 keep_timer_going = TRUE;
1215 /* note: we provide a grace period before declaring a component as
1216 * failed. Components marked connected, and then ready follow another
1217 * code path, and are not concerned by this grace period.
1219 if (!keep_timer_going && agent->conncheck_ongoing_idle_delay == 0)
1220 nice_debug ("Agent %p : waiting %d msecs before checking "
1221 "for failed components.", agent, agent->idle_timeout);
1223 if (keep_timer_going)
1224 agent->conncheck_ongoing_idle_delay = 0;
1226 agent->conncheck_ongoing_idle_delay += agent->timer_ta;
1228 /* step: stop timer if no work left */
1229 if (!keep_timer_going &&
1230 agent->conncheck_ongoing_idle_delay >= agent->idle_timeout) {
1231 nice_debug ("Agent %p : checking for failed components now.", agent);
1232 for (i = agent->streams; i; i = i->next) {
1233 NiceStream *stream = i->data;
1234 priv_update_check_list_failed_components (agent, stream);
1237 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
1238 priv_print_conn_check_lists (agent, G_STRFUNC,
1239 ", conncheck timer stopped");
1241 /* Stopping the timer so destroy the source.. this will allow
1242 the timer to be reset if we get a set_remote_candidates after this
1244 conn_check_stop (agent);
1246 /* XXX: what to signal, is all processing now really done? */
1247 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
1254 static gboolean priv_conn_remote_consent_tick_agent_locked (
1255 NiceAgent *agent, gpointer pointer)
1257 CandidatePair *pair = (CandidatePair *) pointer;
1258 guint64 consent_timeout = 0;
1261 if (pair->remote_consent.tick_source) {
1262 g_source_destroy (pair->remote_consent.tick_source);
1263 g_source_unref (pair->remote_consent.tick_source);
1265 pair->remote_consent.tick_source = NULL;
1267 if (agent->consent_freshness) {
1268 consent_timeout = NICE_AGENT_TIMER_CONSENT_TIMEOUT * 1000;
1270 consent_timeout = NICE_AGENT_TIMER_KEEPALIVE_TIMEOUT* 1000;
1273 now = g_get_monotonic_time();
1274 if (now - pair->remote_consent.last_received > consent_timeout) {
1275 guint64 time_since = now - pair->remote_consent.last_received;
1276 pair->remote_consent.have = FALSE;
1277 nice_debug ("Agent %p : pair %p consent for stream/component %u/%u timed "
1278 "out! -> FAILED. Last consent received: %" G_GUINT64_FORMAT ".%" G_GUINT64_FORMAT "s ago",
1279 agent, pair, pair->keepalive.stream_id, pair->keepalive.component_id,
1280 time_since / G_USEC_PER_SEC, time_since % G_USEC_PER_SEC);
1281 agent_signal_component_state_change (agent, pair->keepalive.stream_id,
1282 pair->keepalive.component_id, NICE_COMPONENT_STATE_FAILED);
1284 guint64 delay = (consent_timeout - (now - pair->remote_consent.last_received)) / 1000;
1285 nice_debug ("Agent %p : pair %p rechecking consent in %" G_GUINT64_FORMAT ".%03" G_GUINT64_FORMAT "s",
1286 agent, pair, delay / 1000, delay % 1000);
1287 agent_timeout_add_with_context (agent,
1288 &pair->remote_consent.tick_source,
1289 "Pair remote consent", delay,
1290 priv_conn_remote_consent_tick_agent_locked, pair);
1296 static guint32 peer_reflexive_candidate_priority (NiceAgent *agent,
1297 NiceCandidate *local_candidate)
1299 NiceCandidate *candidate_priority =
1300 nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
1303 candidate_priority->transport = local_candidate->transport;
1304 candidate_priority->component_id = local_candidate->component_id;
1305 candidate_priority->base_addr = local_candidate->addr;
1306 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1307 priority = nice_candidate_jingle_priority (candidate_priority);
1308 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
1309 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
1310 priority = nice_candidate_msn_priority (candidate_priority);
1311 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1312 priority = nice_candidate_ms_ice_priority (candidate_priority,
1313 agent->reliable, FALSE);
1315 priority = nice_candidate_ice_priority (candidate_priority,
1316 agent->reliable, FALSE);
1318 nice_candidate_free (candidate_priority);
1323 /* Returns the priority of a local candidate of type peer-reflexive that
1324 * would be learned as a consequence of a check from this local
1325 * candidate. See RFC 5245, section 7.1.2.1. "PRIORITY and USE-CANDIDATE".
1326 * RFC 5245 is more explanatory than RFC 8445 on this detail.
1328 * Apply to local candidates of type host only, because candidates of type
1329 * relay are supposed to have a public IP address, that wont generate
1330 * a peer-reflexive address. Server-reflexive candidates are not
1331 * concerned too, because no STUN request is sent with a local candidate
1334 static guint32 stun_request_priority (NiceAgent *agent,
1335 NiceCandidate *local_candidate)
1337 if (local_candidate->type == NICE_CANDIDATE_TYPE_HOST)
1338 return peer_reflexive_candidate_priority (agent, local_candidate);
1340 return local_candidate->priority;
1343 static void ms_ice2_legacy_conncheck_send(StunMessage *msg, NiceSocket *sock,
1344 const NiceAddress *remote_addr)
1346 uint32_t *fingerprint_attr;
1347 uint32_t fingerprint_orig;
1348 uint16_t fingerprint_len;
1351 if (msg->agent->ms_ice2_send_legacy_connchecks == FALSE) {
1355 fingerprint_attr = (uint32_t *)stun_message_find (msg,
1356 STUN_ATTRIBUTE_FINGERPRINT, &fingerprint_len);
1358 if (fingerprint_attr == NULL) {
1359 nice_debug ("FINGERPRINT not found.");
1363 if (fingerprint_len != sizeof (fingerprint_orig)) {
1364 nice_debug ("Unexpected FINGERPRINT length %u.", fingerprint_len);
1368 memcpy (&fingerprint_orig, fingerprint_attr, sizeof (fingerprint_orig));
1370 buffer_len = stun_message_length (msg);
1372 *fingerprint_attr = stun_fingerprint (msg->buffer, buffer_len, TRUE);
1374 agent_socket_send (sock, remote_addr, buffer_len, (gchar *)msg->buffer);
1376 memcpy (fingerprint_attr, &fingerprint_orig, sizeof (fingerprint_orig));
1380 * Timer callback that handles initiating and managing connectivity
1381 * checks (paced by the Ta timer).
1383 * This function is designed for the g_timeout_add() interface.
1385 * @return will return FALSE when no more pending timers.
1387 static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
1393 guint64 min_next_tick;
1394 guint64 next_timer_tick;
1396 now = g_get_monotonic_time ();
1397 if (agent->consent_freshness) {
1398 min_next_tick = now + 1000 * NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL;
1400 min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1403 /* case 1: session established and media flowing
1404 * (ref ICE sect 11 "Keepalives" RFC-8445)
1405 * TODO: without RFC 7675 (consent freshness), keepalives should be sent
1406 * only when no packet has been sent on that pair in the last Tr seconds,
1407 * and not unconditionally.
1409 for (i = agent->streams; i; i = i->next) {
1411 NiceStream *stream = i->data;
1412 for (j = stream->components; j; j = j->next) {
1413 NiceComponent *component = j->data;
1414 if (component->selected_pair.local != NULL) {
1415 CandidatePair *p = &component->selected_pair;
1417 /* Disable keepalive checks on TCP candidates unless explicitly enabled */
1418 if (p->local->c.transport != NICE_CANDIDATE_TRANSPORT_UDP &&
1419 !NICE_AGENT_DO_KEEPALIVE_CONNCHECKS (agent))
1422 if (p->keepalive.next_tick) {
1423 if (p->keepalive.next_tick < min_next_tick)
1424 min_next_tick = p->keepalive.next_tick;
1425 if (now < p->keepalive.next_tick)
1429 if (NICE_AGENT_DO_KEEPALIVE_CONNCHECKS (agent)) {
1430 uint8_t uname[NICE_STREAM_MAX_UNAME];
1432 priv_create_username (agent, agent_find_stream (agent, stream->id),
1433 component->id, (NiceCandidate *) p->remote,
1434 (NiceCandidate *) p->local, uname, sizeof (uname), FALSE);
1435 uint8_t *password = NULL;
1436 size_t password_len = priv_get_password (agent,
1437 agent_find_stream (agent, stream->id),
1438 (NiceCandidate *) p->remote, &password);
1439 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1440 StunMessage stun_message;
1442 if (uname_len > 0) {
1443 if (nice_debug_is_enabled ()) {
1444 gchar tmpbuf[INET6_ADDRSTRLEN];
1445 nice_address_to_string (&p->remote->c.addr, tmpbuf);
1446 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
1447 "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
1448 "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
1449 agent, tmpbuf, nice_address_get_port (&p->remote->c.addr),
1450 component->id, (int) uname_len, uname, uname_len,
1451 (int) password_len, password, password_len,
1455 buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
1456 &stun_message, stun_buffer, sizeof(stun_buffer),
1457 uname, uname_len, password, password_len,
1458 agent->controlling_mode, agent->controlling_mode,
1462 agent_to_ice_compatibility (agent));
1464 nice_debug ("Agent %p: conncheck created %zd - %p",
1465 agent, buf_len, stun_message.buffer);
1468 /* random range over 0.8 -> 1.2 as specified in RFC7675 */
1469 double modifier = g_random_double() * 0.4 + 0.8;
1470 guint64 delay = 1000 * MAX((guint64) ((NICE_AGENT_TIMER_CONSENT_DEFAULT) * modifier),
1471 NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL);
1473 p->keepalive.stream_id = stream->id;
1474 p->keepalive.component_id = component->id;
1475 p->keepalive.next_tick = now + delay;
1477 if (p->remote_consent.have) {
1478 if (p->remote_consent.last_received == 0) {
1479 p->remote_consent.last_received = g_get_monotonic_time();
1482 priv_conn_remote_consent_tick_agent_locked (agent, p);
1485 agent->media_after_tick = FALSE;
1487 /* send the conncheck */
1488 agent_socket_send (p->local->sockptr, &p->remote->c.addr,
1489 buf_len, (gchar *) stun_buffer);
1491 next_timer_tick = now + agent->timer_ta * 1000;
1498 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1499 StunMessage stun_message;
1501 buf_len = stun_usage_bind_keepalive (&component->stun_agent,
1502 &stun_message, stun_buffer, sizeof(stun_buffer));
1505 agent_socket_send (p->local->sockptr, &p->remote->c.addr, buf_len,
1506 (gchar *) stun_buffer);
1508 p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1510 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1511 ms_ice2_legacy_conncheck_send (&stun_message,
1512 p->local->sockptr, &p->remote->c.addr);
1515 if (nice_debug_is_enabled ()) {
1516 gchar tmpbuf[INET6_ADDRSTRLEN];
1517 nice_address_to_string (&p->local->c.base_addr, tmpbuf);
1518 nice_debug ("Agent %p : resending STUN to keep the "
1519 "selected base address %s:%u alive in s%d/c%d.", agent,
1520 tmpbuf, nice_address_get_port (&p->local->c.base_addr),
1521 stream->id, component->id);
1524 next_timer_tick = now + agent->timer_ta * 1000;
1534 /* case 2: connectivity establishment ongoing
1535 * (ref ICE sect 5.1.1.4 "Keeping Candidates Alive" RFC-8445)
1537 for (i = agent->streams; i; i = i->next) {
1538 NiceStream *stream = i->data;
1539 for (j = stream->components; j; j = j->next) {
1540 NiceComponent *component = j->data;
1541 if (component->state < NICE_COMPONENT_STATE_CONNECTED &&
1542 agent->stun_server_ip) {
1543 NiceAddress stun_server;
1544 if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
1545 StunAgent stun_agent;
1546 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1547 StunMessage stun_message;
1548 size_t buffer_len = 0;
1550 nice_address_set_port (&stun_server, agent->stun_server_port);
1552 nice_agent_init_stun_agent (agent, &stun_agent);
1554 buffer_len = stun_usage_bind_create (&stun_agent,
1555 &stun_message, stun_buffer, sizeof(stun_buffer));
1557 for (k = component->local_candidates; k; k = k->next) {
1558 NiceCandidateImpl *candidate = (NiceCandidateImpl *) k->data;
1559 if (candidate->c.type == NICE_CANDIDATE_TYPE_HOST &&
1560 candidate->c.transport == NICE_CANDIDATE_TRANSPORT_UDP &&
1561 nice_address_ip_version (&candidate->c.addr) ==
1562 nice_address_ip_version (&stun_server)) {
1564 if (candidate->keepalive_next_tick) {
1565 if (candidate->keepalive_next_tick < min_next_tick)
1566 min_next_tick = candidate->keepalive_next_tick;
1567 if (now < candidate->keepalive_next_tick)
1571 /* send the conncheck */
1572 if (nice_debug_is_enabled ()) {
1573 gchar tmpbuf[INET6_ADDRSTRLEN];
1574 nice_address_to_string (&candidate->c.addr, tmpbuf);
1575 nice_debug ("Agent %p : resending STUN to keep the local "
1576 "candidate %s:%u alive in s%d/c%d.", agent,
1577 tmpbuf, nice_address_get_port (&candidate->c.addr),
1578 stream->id, component->id);
1580 agent_socket_send (candidate->sockptr, &stun_server,
1581 buffer_len, (gchar *)stun_buffer);
1582 candidate->keepalive_next_tick = now +
1583 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1584 next_timer_tick = now + agent->timer_ta * 1000;
1593 next_timer_tick = min_next_tick;
1597 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
1601 if (agent->keepalive_timer_source) {
1602 g_source_destroy (agent->keepalive_timer_source);
1603 g_source_unref (agent->keepalive_timer_source);
1604 agent->keepalive_timer_source = NULL;
1606 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1607 "Connectivity keepalive timeout", (next_timer_tick - now)/ 1000,
1608 priv_conn_keepalive_tick_agent_locked, NULL);
1612 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
1617 ret = priv_conn_keepalive_tick_unlocked (agent);
1619 if (agent->keepalive_timer_source) {
1620 g_source_destroy (agent->keepalive_timer_source);
1621 g_source_unref (agent->keepalive_timer_source);
1622 agent->keepalive_timer_source = NULL;
1630 static gboolean priv_turn_allocate_refresh_retransmissions_tick_agent_locked (
1631 NiceAgent *agent, gpointer pointer)
1633 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1635 g_source_destroy (cand->tick_source);
1636 g_source_unref (cand->tick_source);
1637 cand->tick_source = NULL;
1639 switch (stun_timer_refresh (&cand->timer)) {
1640 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1643 StunTransactionId id;
1645 stun_message_id (&cand->stun_message, id);
1646 stun_agent_forget_transaction (&cand->stun_agent, id);
1648 refresh_free (agent, cand);
1651 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1653 agent_socket_send (cand->nicesock, &cand->server,
1654 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
1657 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1658 agent_timeout_add_with_context (agent, &cand->tick_source,
1659 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1660 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1663 /* Nothing to do. */
1667 return G_SOURCE_REMOVE;
1670 static void priv_turn_allocate_refresh_tick_unlocked (NiceAgent *agent,
1671 CandidateRefresh *cand)
1677 size_t buffer_len = 0;
1678 StunUsageTurnCompatibility turn_compat =
1679 agent_to_turn_compatibility (agent);
1681 username = (uint8_t *)cand->candidate->turn->username;
1682 username_len = (size_t) strlen (cand->candidate->turn->username);
1683 password = (uint8_t *)cand->candidate->turn->password;
1684 password_len = (size_t) strlen (cand->candidate->turn->password);
1686 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
1687 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
1688 username = cand->candidate->turn->decoded_username;
1689 password = cand->candidate->turn->decoded_password;
1690 username_len = cand->candidate->turn->decoded_username_len;
1691 password_len = cand->candidate->turn->decoded_password_len;
1694 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
1695 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
1696 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
1697 cand->disposing ? 0 : -1,
1698 username, username_len,
1699 password, password_len,
1702 nice_debug ("Agent %p : Sending allocate Refresh %zd", agent,
1705 if (cand->tick_source != NULL) {
1706 g_source_destroy (cand->tick_source);
1707 g_source_unref (cand->tick_source);
1708 cand->tick_source = NULL;
1711 if (buffer_len > 0) {
1712 stun_timer_start (&cand->timer,
1713 agent->stun_initial_timeout,
1714 agent->stun_max_retransmissions);
1716 /* send the refresh */
1717 agent_socket_send (cand->nicesock, &cand->server,
1718 buffer_len, (gchar *)cand->stun_buffer);
1720 agent_timeout_add_with_context (agent, &cand->tick_source,
1721 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1722 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1729 * Timer callback that handles refreshing TURN allocations
1731 * This function is designed for the g_timeout_add() interface.
1733 * @return will return FALSE when no more pending timers.
1735 static gboolean priv_turn_allocate_refresh_tick_agent_locked (NiceAgent *agent,
1738 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1740 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
1742 return G_SOURCE_REMOVE;
1747 * Initiates the next pending connectivity check.
1749 static void priv_schedule_next (NiceAgent *agent)
1751 if (agent->discovery_unsched_items > 0)
1752 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
1754 /* step: schedule timer if not running yet */
1755 if (agent->conncheck_timer_source == NULL) {
1756 agent_timeout_add_with_context (agent, &agent->conncheck_timer_source,
1757 "Connectivity check schedule", agent->timer_ta,
1758 priv_conn_check_tick_agent_locked, NULL);
1761 /* step: also start the keepalive timer */
1762 if (agent->keepalive_timer_source == NULL) {
1763 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1764 "Connectivity keepalive timeout", agent->timer_ta,
1765 priv_conn_keepalive_tick_agent_locked, NULL);
1770 * Compares two connectivity check items. Checkpairs are sorted
1771 * in descending priority order, with highest priority item at
1772 * the start of the list.
1774 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
1776 if (a->priority > b->priority)
1778 else if (a->priority < b->priority)
1783 /* Find a transport compatible with a given socket.
1785 * Returns TRUE when a matching transport can be guessed from
1786 * the type of the socket in an unambiguous way.
1789 nice_socket_has_compatible_transport (NiceSocket *socket,
1790 NiceCandidateTransport *transport)
1792 gboolean found = TRUE;
1795 g_assert (transport);
1797 switch (socket->type) {
1798 case NICE_SOCKET_TYPE_TCP_BSD:
1799 if (nice_tcp_bsd_socket_get_passive_parent (socket))
1800 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1802 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1804 case NICE_SOCKET_TYPE_TCP_PASSIVE:
1805 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1807 case NICE_SOCKET_TYPE_TCP_ACTIVE:
1808 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1810 case NICE_SOCKET_TYPE_UDP_BSD:
1811 *transport = NICE_CANDIDATE_TRANSPORT_UDP;
1820 /* Test if a local socket and a local candidate are compatible. This
1821 * function does supplementary tests when the address and port are not
1822 * sufficient to give a unique candidate. We try to avoid comparing
1823 * directly the sockptr value, when possible, to rely on objective
1824 * properties of the candidate and the socket instead, and we also
1825 * choose to ignore the conncheck list for the same reason.
1828 local_candidate_and_socket_compatible (NiceAgent *agent,
1829 NiceCandidate *lcand, NiceSocket *socket)
1831 gboolean ret = TRUE;
1832 NiceCandidateTransport transport;
1833 NiceCandidateImpl *lc = (NiceCandidateImpl *) lcand;
1838 if (nice_socket_has_compatible_transport (socket, &transport)) {
1839 ret = (lcand->transport == transport);
1840 /* tcp-active discovered peer-reflexive local candidate, where
1841 * socket is the tcp connect related socket */
1842 if (ret && transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1843 nice_address_get_port (&lcand->addr) > 0)
1844 ret = (lc->sockptr == socket);
1845 } else if (socket->type == NICE_SOCKET_TYPE_UDP_TURN)
1846 /* Socket of type udp-turn will match a unique local candidate
1847 * by its sockptr value. An an udp-turn socket doesn't carry enough
1848 * information when base socket is udp-turn-over-tcp to disambiguate
1849 * between a tcp-act and a tcp-pass local candidate.
1851 ret = (lc->sockptr == socket);
1856 /* Test if a local socket and a remote candidate are compatible.
1857 * This function is very close to its local candidate counterpart,
1858 * the difference is that we also use information from the local
1859 * candidate we may have identified previously. This is needed
1860 * to disambiguate the transport of the candidate with a socket
1865 remote_candidate_and_socket_compatible (NiceAgent *agent,
1866 NiceCandidate *lcand, NiceCandidate *rcand, NiceSocket *socket)
1868 gboolean ret = TRUE;
1869 NiceCandidateTransport transport;
1874 if (nice_socket_has_compatible_transport (socket, &transport))
1875 ret = (conn_check_match_transport (rcand->transport) == transport);
1877 /* This supplementary test with the local candidate is needed with
1878 * socket of type udp-turn, the type doesn't allow to disambiguate
1879 * between a tcp-pass and a tcp-act remote candidate
1882 ret = (conn_check_match_transport (lcand->transport) == rcand->transport);
1888 conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream,
1889 NiceComponent *component)
1893 NiceCandidate *lcand = NULL, *rcand = NULL;
1895 nice_debug ("Agent %p : conn_check_remote_candidates_set %u %u",
1896 agent, stream->id, component->id);
1898 if (stream->remote_ufrag[0] == 0)
1901 if (component->incoming_checks.head)
1902 nice_debug ("Agent %p : credentials have been set, "
1903 "we can process incoming checks", agent);
1905 for (i = component->incoming_checks.head; i;) {
1906 IncomingCheck *icheck = i->data;
1907 GList *i_next = i->next;
1909 nice_debug ("Agent %p : replaying icheck=%p (sock=%p)",
1910 agent, icheck, icheck->local_socket);
1912 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
1913 * be handled separately */
1914 for (j = component->local_candidates; j; j = j->next) {
1915 NiceCandidate *cand = j->data;
1918 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
1921 addr = &cand->base_addr;
1923 if (nice_address_equal (&icheck->local_socket->addr, addr) &&
1924 local_candidate_and_socket_compatible (agent, cand,
1925 icheck->local_socket)) {
1931 if (lcand == NULL) {
1932 for (j = component->local_candidates; j; j = j->next) {
1933 NiceCandidate *cand = j->data;
1934 NiceAddress *addr = &cand->base_addr;
1936 /* tcp-active (not peer-reflexive discovered) local candidate, where
1937 * socket is the tcp connect related socket */
1938 if (nice_address_equal_no_port (&icheck->local_socket->addr, addr) &&
1939 nice_address_get_port (&cand->addr) == 0 &&
1940 cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1941 local_candidate_and_socket_compatible (agent, cand,
1942 icheck->local_socket)) {
1949 g_assert (lcand != NULL);
1951 for (j = component->remote_candidates; j; j = j->next) {
1952 NiceCandidate *cand = j->data;
1953 if (nice_address_equal (&cand->addr, &icheck->from) &&
1954 remote_candidate_and_socket_compatible (agent, lcand, cand,
1955 icheck->local_socket)) {
1961 if (lcand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
1962 CandidateCheckPair *pair = NULL;
1964 for (j = stream->conncheck_list; j; j = j->next) {
1965 CandidateCheckPair *p = j->data;
1966 if (lcand == p->local && rcand == p->remote) {
1972 priv_conn_check_add_for_candidate_pair_matched (agent,
1973 stream->id, component, lcand, rcand, NICE_CHECK_WAITING);
1976 priv_schedule_triggered_check (agent, stream, component,
1977 icheck->local_socket, rcand);
1978 if (icheck->use_candidate)
1979 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
1981 if (icheck->username)
1982 g_free (icheck->username);
1983 g_slice_free (IncomingCheck, icheck);
1984 g_queue_delete_link (&component->incoming_checks, i);
1990 * Handle any processing steps for connectivity checks after
1991 * remote credentials have been set. This function handles
1992 * the special case where answerer has sent us connectivity
1993 * checks before the answer (containing credentials information),
1994 * reaches us. The special case is documented in RFC 5245 sect 7.2.
1997 void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream)
2001 for (j = stream->components; j ; j = j->next) {
2002 NiceComponent *component = j->data;
2004 conn_check_remote_candidates_set(agent, stream, component);
2009 * Enforces the upper limit for connectivity checks by dropping
2010 * lower-priority pairs as described RFC 8445 section 6.1.2.5. See also
2011 * conn_check_add_for_candidate().
2012 * Returns TRUE if the pair in argument is one of the deleted pairs.
2014 static gboolean priv_limit_conn_check_list_size (NiceAgent *agent,
2015 NiceStream *stream, CandidateCheckPair *pair)
2018 guint cancelled = 0;
2019 gboolean deleted = FALSE;
2020 GSList *item = stream->conncheck_list;
2023 CandidateCheckPair *p = item->data;
2024 GSList *next = item->next;
2027 /* We remove lower-priority pairs, but only the ones that can be
2028 * safely discarded without breaking an ongoing conncheck process.
2029 * This only includes pairs that are in the frozen state (those
2030 * initially added when remote candidates are received) or in failed
2031 * state. Pairs in any other state play a role in the conncheck, and
2032 * there removal may lead to a failing conncheck that would succeed
2035 * We also remove failed pairs from the list unconditionally.
2037 if ((valid > agent->max_conn_checks && p->state == NICE_CHECK_FROZEN) ||
2038 p->state == NICE_CHECK_FAILED) {
2041 nice_debug ("Agent %p : pair %p removed.", agent, p);
2042 candidate_check_pair_free (agent, p);
2043 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list,
2051 nice_debug ("Agent %p : Pruned %d pairs. "
2052 "Conncheck list has %d elements left. "
2053 "Maximum connchecks allowed : %d", agent, cancelled,
2054 valid - cancelled, agent->max_conn_checks);
2060 * Changes the selected pair for the component if 'pair'
2061 * has higher priority than the currently selected pair. See
2062 * RFC 8445 sect 8.1.1. "Nominating Pairs"
2065 conn_check_update_selected_pair (NiceAgent *agent, NiceComponent *component,
2066 CandidateCheckPair *pair)
2068 CandidatePair cpair = { 0, };
2070 g_assert (component);
2072 /* pair is expected to have the nominated flag */
2073 g_assert (pair->nominated);
2074 if (pair->priority > component->selected_pair.priority) {
2075 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2076 nice_candidate_pair_priority_to_string (pair->priority, priority);
2077 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
2078 "(prio:%s).", agent, component->id,
2079 pair->local->foundation, pair->remote->foundation, priority);
2081 cpair.local = (NiceCandidateImpl *) pair->local;
2082 cpair.remote = (NiceCandidateImpl *) pair->remote;
2083 cpair.priority = pair->priority;
2084 cpair.stun_priority = pair->stun_priority;
2085 cpair.remote_consent.have = TRUE;
2087 nice_component_update_selected_pair (agent, component, &cpair);
2089 priv_conn_keepalive_tick_unlocked (agent);
2091 agent_signal_new_selected_pair (agent, pair->stream_id, component->id,
2092 pair->local, pair->remote);
2097 * Updates the check list state.
2099 * Implements parts of the algorithm described in
2100 * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any
2101 * component, all checks have been completed and have
2102 * failed to produce a nominated pair, mark that component's
2103 * state to NICE_CHECK_FAILED.
2105 * Sends a component state changesignal via 'agent'.
2107 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream)
2112 /* note: emitting a signal might cause the client
2113 * to remove the stream, thus the component count
2114 * must be fetched before entering the loop*/
2115 guint c, components = stream->n_components;
2117 if (stream->conncheck_list == NULL)
2120 for (i = agent->discovery_list; i; i = i->next) {
2121 CandidateDiscovery *d = i->data;
2123 /* There is still discovery ogoing for this stream,
2124 * so don't fail any of it's candidates.
2126 if (d->stream_id == stream->id && !d->done)
2129 if (agent->discovery_list != NULL)
2132 /* note: iterate the conncheck list for each component separately */
2133 for (c = 0; c < components; c++) {
2134 NiceComponent *component = NULL;
2135 if (!agent_find_component (agent, stream->id, c+1, NULL, &component))
2140 for (i = stream->conncheck_list; i; i = i->next) {
2141 CandidateCheckPair *p = i->data;
2143 g_assert (p->stream_id == stream->id);
2145 if (p->component_id == (c + 1)) {
2148 if (p->state != NICE_CHECK_FAILED &&
2149 p->state != NICE_CHECK_SUCCEEDED &&
2150 p->state != NICE_CHECK_DISCOVERED)
2155 /* note: all pairs are either failed or succeeded, and the component
2156 * has not produced a nominated pair.
2157 * Set the component to FAILED only if it actually had remote candidates
2159 if (completed && nominated == 0 &&
2160 component != NULL && component->remote_candidates != NULL)
2161 agent_signal_component_state_change (agent,
2163 (c + 1), /* component-id */
2164 NICE_COMPONENT_STATE_FAILED);
2169 * Updates the check list state for a stream component.
2171 * Implements the algorithm described in ICE sect 8.1.2
2172 * "Updating States" (ID-19) as it applies to checks of
2173 * a certain component. If there are any nominated pairs,
2174 * ICE processing may be concluded, and component state is
2177 * Sends a component state changesignal via 'agent'.
2179 void conn_check_update_check_list_state_for_ready (NiceAgent *agent,
2180 NiceStream *stream, NiceComponent *component)
2183 guint valid = 0, nominated = 0;
2185 g_assert (component);
2187 /* step: search for at least one nominated pair */
2188 for (i = stream->conncheck_list; i; i = i->next) {
2189 CandidateCheckPair *p = i->data;
2190 if (p->component_id == component->id) {
2193 if (p->nominated == TRUE) {
2200 if (nominated > 0) {
2201 /* Only go to READY if no checks are left in progress. If there are
2202 * any that are kept, then this function will be called again when the
2203 * conncheck tick timer finishes them all */
2204 if (priv_prune_pending_checks (agent, stream, component) == 0) {
2205 /* Continue through the states to give client code a nice
2206 * logical progression. See http://phabricator.freedesktop.org/D218 for
2208 if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
2209 component->state == NICE_COMPONENT_STATE_FAILED)
2210 agent_signal_component_state_change (agent, stream->id, component->id,
2211 NICE_COMPONENT_STATE_CONNECTING);
2212 if (component->state < NICE_COMPONENT_STATE_CONNECTED)
2213 agent_signal_component_state_change (agent, stream->id, component->id,
2214 NICE_COMPONENT_STATE_CONNECTED);
2215 agent_signal_component_state_change (agent, stream->id,
2216 component->id, NICE_COMPONENT_STATE_READY);
2219 nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id);
2223 * The remote party has signalled that the candidate pair
2224 * described by 'component' and 'remotecand' is nominated
2227 static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand)
2231 g_assert (component);
2233 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2234 agent->controlling_mode)
2237 /* step: search for at least one nominated pair */
2238 for (i = stream->conncheck_list; i; i = i->next) {
2239 CandidateCheckPair *pair = i->data;
2240 if (pair->local == localcand && pair->remote == remotecand) {
2241 /* ICE, 7.2.1.5. Updating the Nominated Flag */
2242 /* note: TCP candidates typically produce peer reflexive
2243 * candidate, generating a "discovered" pair that can be
2246 if (pair->state == NICE_CHECK_SUCCEEDED &&
2247 pair->discovered_pair != NULL) {
2248 pair = pair->discovered_pair;
2249 g_assert (pair->state == NICE_CHECK_DISCOVERED);
2252 /* If the received Binding request triggered a new check to be
2253 * enqueued in the triggered-check queue (Section 7.3.1.4), once
2254 * the check is sent and if it generates a successful response,
2255 * and generates a valid pair, the agent sets the nominated flag
2256 * of the pair to true
2258 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2259 if (g_slist_find (agent->triggered_check_queue, pair) ||
2260 pair->state == NICE_CHECK_IN_PROGRESS) {
2262 /* This pair is not always in the triggered check list, for
2263 * example if it is in-progress with a lower priority than an
2264 * already nominated pair. Is that case, it is not rescheduled
2265 * for a connection check, see function
2266 * priv_schedule_triggered_check(), case NICE_CHECK_IN_PROGRESS.
2268 pair->mark_nominated_on_response_arrival = TRUE;
2269 nice_debug ("Agent %p : pair %p (%s) is %s, "
2270 "will be nominated on response receipt.",
2271 agent, pair, pair->foundation,
2272 priv_state_to_string (pair->state));
2277 !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2278 nice_debug ("Agent %p : marking pair %p (%s) as nominated",
2279 agent, pair, pair->foundation);
2280 pair->nominated = TRUE;
2284 /* Do not step down to CONNECTED if we're already at state READY*/
2285 if (component->state == NICE_COMPONENT_STATE_FAILED)
2286 agent_signal_component_state_change (agent,
2287 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
2288 conn_check_update_selected_pair (agent, component, pair);
2289 if (component->state == NICE_COMPONENT_STATE_CONNECTING)
2290 /* step: notify the client of a new component state (must be done
2291 * before the possible check list state update step */
2292 agent_signal_component_state_change (agent,
2293 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2296 if (pair->nominated)
2297 conn_check_update_check_list_state_for_ready (agent, stream, component);
2303 * Creates a new connectivity check pair and adds it to
2304 * the agent's list of checks.
2306 static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
2307 guint stream_id, NiceComponent *component, NiceCandidateImpl *local,
2308 NiceCandidateImpl *remote, NiceCheckState initial_state)
2311 CandidateCheckPair *pair;
2314 g_assert (local != NULL);
2315 g_assert (remote != NULL);
2317 priority = agent_candidate_pair_priority (agent, (NiceCandidate *) local,
2318 (NiceCandidate *) remote);
2320 if (component->selected_pair.priority &&
2321 priority < component->selected_pair.priority) {
2322 gchar prio1[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2323 gchar prio2[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2325 nice_candidate_pair_priority_to_string (priority, prio1);
2326 nice_candidate_pair_priority_to_string (component->selected_pair.priority,
2328 nice_debug ("Agent %p : do not create a pair that would have a priority "
2329 "%s lower than selected pair priority %s.", agent, prio1, prio2);
2333 stream = agent_find_stream (agent, stream_id);
2334 pair = g_slice_new0 (CandidateCheckPair);
2336 pair->stream_id = stream_id;
2337 pair->component_id = component->id;
2338 pair->local = (NiceCandidate *) local;
2339 pair->remote = (NiceCandidate *) remote;
2340 /* note: we use the remote sockptr only in the case
2343 if (local->c.transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
2344 remote->c.type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2345 pair->sockptr = remote->sockptr;
2347 pair->sockptr = local->sockptr;
2348 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
2349 local->c.foundation, remote->c.foundation);
2351 pair->priority = agent_candidate_pair_priority (agent,
2352 (NiceCandidate *) local, (NiceCandidate *) remote);
2353 nice_debug ("Agent %p : creating a new pair", agent);
2354 SET_PAIR_STATE (agent, pair, initial_state);
2356 gchar tmpbuf1[INET6_ADDRSTRLEN];
2357 gchar tmpbuf2[INET6_ADDRSTRLEN];
2358 nice_address_to_string (&pair->local->addr, tmpbuf1);
2359 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2360 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
2361 tmpbuf1, nice_address_get_port (&pair->local->addr),
2362 tmpbuf2, nice_address_get_port (&pair->remote->addr));
2364 pair->stun_priority = stun_request_priority (agent, (NiceCandidate *) local);
2366 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
2367 (GCompareFunc)conn_check_compare);
2369 priv_schedule_next (agent);
2371 nice_debug ("Agent %p : added a new pair %p with foundation '%s' and "
2372 "transport %s:%s to stream %u component %u",
2373 agent, pair, pair->foundation,
2374 nice_candidate_transport_to_string (pair->local->transport),
2375 nice_candidate_transport_to_string (pair->remote->transport),
2376 stream_id, component->id);
2378 if (initial_state == NICE_CHECK_FROZEN)
2379 priv_conn_check_unfreeze_maybe (agent, pair);
2381 /* implement the hard upper limit for number of
2382 checks (see sect 5.7.3 ICE ID-19): */
2383 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
2384 if (priv_limit_conn_check_list_size (agent, stream, pair))
2391 NiceCandidateTransport
2392 conn_check_match_transport (NiceCandidateTransport transport)
2394 switch (transport) {
2395 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
2396 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
2398 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
2399 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
2401 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
2402 case NICE_CANDIDATE_TRANSPORT_UDP:
2409 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
2410 NiceAgent *agent, guint stream_id, NiceComponent *component,
2411 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state)
2413 CandidateCheckPair *pair;
2415 pair = priv_add_new_check_pair (agent, stream_id, component,
2416 (NiceCandidateImpl *) local, (NiceCandidateImpl *) remote, initial_state);
2418 if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
2419 component->state == NICE_COMPONENT_STATE_READY) {
2420 agent_signal_component_state_change (agent,
2423 NICE_COMPONENT_STATE_CONNECTED);
2425 agent_signal_component_state_change (agent,
2428 NICE_COMPONENT_STATE_CONNECTING);
2436 _is_linklocal_to_non_linklocal (NiceAddress *laddr, NiceAddress *raddr)
2438 return nice_address_is_linklocal (laddr) != nice_address_is_linklocal (raddr);
2441 gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
2442 guint stream_id, NiceComponent *component, NiceCandidate *local,
2443 NiceCandidate *remote)
2445 gboolean ret = FALSE;
2447 g_assert (local != NULL);
2448 g_assert (remote != NULL);
2450 /* note: do not create pairs where the local candidate is a srv-reflexive
2451 * or peer-reflexive (ICE 6.1.2.4. "Pruning the pairs" RFC 8445)
2453 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
2454 agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2455 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2456 (local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
2457 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)) {
2461 /* note: do not create pairs where local candidate has TCP passive transport
2462 * (ice-tcp-13 6.2. "Forming the Check Lists") */
2463 if (local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
2467 /* note: match pairs only if transport and address family are the same
2468 * and make sure link-local stay link-local */
2469 if (local->transport == conn_check_match_transport (remote->transport) &&
2470 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family &&
2471 !_is_linklocal_to_non_linklocal (&local->addr, &remote->addr)) {
2472 if (priv_conn_check_add_for_candidate_pair_matched (agent, stream_id,
2473 component, local, remote, NICE_CHECK_FROZEN))
2481 * Forms new candidate pairs by matching the new remote candidate
2482 * 'remote_cand' with all existing local candidates of 'component'.
2483 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
2486 * @param agent context
2487 * @param component pointer to the component
2488 * @param remote remote candidate to match with
2490 * @return number of checks added, negative on fatal errors
2492 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote)
2498 g_assert (remote != NULL);
2500 /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates",
2501 * the agent does not pair this candidate with any local candidates.
2503 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2504 remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2509 for (i = component->local_candidates; i ; i = i->next) {
2510 NiceCandidate *local = i->data;
2512 if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED)
2515 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2526 * Forms new candidate pairs by matching the new local candidate
2527 * 'local_cand' with all existing remote candidates of 'component'.
2529 * @param agent context
2530 * @param component pointer to the component
2531 * @param local local candidate to match with
2533 * @return number of checks added, negative on fatal errors
2535 int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local)
2541 g_assert (local != NULL);
2544 * note: according to 7.1.3.2.1 "Discovering Peer Reflexive
2545 * Candidates", the peer reflexive candidate is not paired
2546 * with other remote candidates
2549 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2550 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2555 for (i = component->remote_candidates; i ; i = i->next) {
2557 NiceCandidate *remote = i->data;
2558 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2569 * Frees the CandidateCheckPair structure pointer to
2570 * by 'user data'. Compatible with GDestroyNotify.
2572 static void candidate_check_pair_free (NiceAgent *agent,
2573 CandidateCheckPair *pair)
2575 priv_remove_pair_from_triggered_check_queue (agent, pair);
2576 priv_free_all_stun_transactions (pair, NULL);
2577 g_slice_free (CandidateCheckPair, pair);
2581 * Frees all resources of all connectivity checks.
2583 void conn_check_free (NiceAgent *agent)
2586 for (i = agent->streams; i; i = i->next) {
2587 NiceStream *stream = i->data;
2589 if (stream->conncheck_list) {
2592 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent,
2594 for (item = stream->conncheck_list; item; item = item->next)
2595 candidate_check_pair_free (agent, item->data);
2596 g_slist_free (stream->conncheck_list);
2597 stream->conncheck_list = NULL;
2601 conn_check_stop (agent);
2605 * Prunes the list of connectivity checks for items related
2606 * to stream 'stream_id'.
2608 * @return TRUE on success, FALSE on a fatal error
2610 void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream)
2613 gboolean keep_going = FALSE;
2615 if (stream->conncheck_list) {
2618 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
2620 for (item = stream->conncheck_list; item; item = item->next)
2621 candidate_check_pair_free (agent, item->data);
2622 g_slist_free (stream->conncheck_list);
2623 stream->conncheck_list = NULL;
2626 for (i = agent->streams; i; i = i->next) {
2627 NiceStream *s = i->data;
2628 if (s->conncheck_list) {
2635 conn_check_stop (agent);
2639 * Fills 'dest' with a username string for use in an outbound connectivity
2640 * checks. No more than 'dest_len' characters (including terminating
2641 * NULL) is ever written to the 'dest'.
2644 size_t priv_gen_username (NiceAgent *agent, guint component_id,
2645 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
2648 gsize remote_len = strlen (remote);
2649 gsize local_len = strlen (local);
2651 if (remote_len > 0 && local_len > 0) {
2652 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
2653 dest_len >= remote_len + local_len + 1) {
2654 memcpy (dest, remote, remote_len);
2656 memcpy (dest + len, ":", 1);
2658 memcpy (dest + len, local, local_len);
2660 } else if ((agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2661 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2662 dest_len >= remote_len + local_len + 4 ) {
2663 memcpy (dest, remote, remote_len);
2665 memcpy (dest + len, ":", 1);
2667 memcpy (dest + len, local, local_len);
2670 memset (dest + len, 0, 4 - (len % 4));
2671 len += 4 - (len % 4);
2673 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2674 dest_len >= remote_len + local_len) {
2675 memcpy (dest, remote, remote_len);
2677 memcpy (dest + len, local, local_len);
2679 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2680 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
2681 gchar component_str[10];
2682 guchar *local_decoded = NULL;
2683 guchar *remote_decoded = NULL;
2684 gsize local_decoded_len;
2685 gsize remote_decoded_len;
2689 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
2690 local_decoded = g_base64_decode (local, &local_decoded_len);
2691 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
2693 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
2694 padding = 4 - (total_len % 4);
2696 if (dest_len >= total_len + padding) {
2697 guchar pad_char[1] = {0};
2700 memcpy (dest, remote_decoded, remote_decoded_len);
2701 len += remote_decoded_len;
2702 memcpy (dest + len, ":", 1);
2704 memcpy (dest + len, component_str, strlen (component_str));
2705 len += strlen (component_str);
2707 memcpy (dest + len, ":", 1);
2710 memcpy (dest + len, local_decoded, local_decoded_len);
2711 len += local_decoded_len;
2712 memcpy (dest + len, ":", 1);
2714 memcpy (dest + len, component_str, strlen (component_str));;
2715 len += strlen (component_str);
2717 for (i = 0; i < padding; i++) {
2718 memcpy (dest + len, pad_char, 1);
2724 g_free (local_decoded);
2725 g_free (remote_decoded);
2733 * Fills 'dest' with a username string for use in an outbound connectivity
2734 * checks. No more than 'dest_len' characters (including terminating
2735 * NULL) is ever written to the 'dest'.
2738 size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
2739 guint component_id, NiceCandidate *remote, NiceCandidate *local,
2740 uint8_t *dest, guint dest_len, gboolean inbound)
2742 gchar *local_username = NULL;
2743 gchar *remote_username = NULL;
2746 if (remote && remote->username) {
2747 remote_username = remote->username;
2750 if (local && local->username) {
2751 local_username = local->username;
2755 if (remote_username == NULL) {
2756 remote_username = stream->remote_ufrag;
2758 if (local_username == NULL) {
2759 local_username = stream->local_ufrag;
2763 if (local_username && remote_username) {
2765 return priv_gen_username (agent, component_id,
2766 local_username, remote_username, dest, dest_len);
2768 return priv_gen_username (agent, component_id,
2769 remote_username, local_username, dest, dest_len);
2777 * Returns a password string for use in an outbound connectivity
2781 size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
2782 NiceCandidate *remote, uint8_t **password)
2784 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
2787 if (remote && remote->password) {
2788 *password = (uint8_t *)remote->password;
2789 return strlen (remote->password);
2793 *password = (uint8_t *)stream->remote_password;
2794 return strlen (stream->remote_password);
2800 /* Implement the computation specific in RFC 8445 section 14 */
2802 static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
2805 guint waiting_and_in_progress = 0;
2806 unsigned int rto = 0;
2808 /* we can compute precisely the number of pairs in-progress or
2809 * waiting for all streams, instead of limiting the value to one
2810 * stream, and multiplying it by the number of active streams.
2811 * Since RFC8445, this number of waiting and in-progress pairs
2812 * if maxed by the number of different foundations in the conncheck
2815 for (i = agent->streams; i ; i = i->next) {
2816 NiceStream *s = i->data;
2817 for (j = s->conncheck_list; j ; j = j->next) {
2818 CandidateCheckPair *p = j->data;
2819 if (p->state == NICE_CHECK_IN_PROGRESS ||
2820 p->state == NICE_CHECK_WAITING)
2821 waiting_and_in_progress++;
2825 rto = agent->timer_ta * waiting_and_in_progress;
2827 nice_debug ("Agent %p : timer set to %dms, "
2828 "waiting+in_progress=%d", agent, MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT),
2829 waiting_and_in_progress);
2830 return MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT);
2834 * Sends a connectivity check over candidate pair 'pair'.
2836 * @return zero on success, non-zero on error
2838 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
2841 /* note: following information is supplied:
2842 * - username (for USERNAME attribute)
2843 * - password (for MESSAGE-INTEGRITY)
2844 * - priority (for PRIORITY)
2845 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
2846 * - USE-CANDIDATE (if sent by the controlling agent)
2849 uint8_t uname[NICE_STREAM_MAX_UNAME];
2851 NiceComponent *component;
2853 uint8_t *password = NULL;
2854 uint8_t *free_password = NULL;
2856 bool controlling = agent->controlling_mode;
2857 /* XXX: add API to support different nomination modes: */
2858 bool cand_use = controlling;
2860 unsigned int timeout;
2861 StunTransaction *stun;
2863 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
2864 &stream, &component))
2867 uname_len = priv_create_username (agent, stream, pair->component_id,
2868 pair->remote, pair->local, uname, sizeof (uname), FALSE);
2869 password_len = priv_get_password (agent, stream, pair->remote, &password);
2871 if (password != NULL &&
2872 (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2873 agent->compatibility == NICE_COMPATIBILITY_OC2007)) {
2874 free_password = password =
2875 g_base64_decode ((gchar *) password, &password_len);
2878 if (nice_debug_is_enabled ()) {
2879 gchar tmpbuf1[INET6_ADDRSTRLEN];
2880 gchar tmpbuf2[INET6_ADDRSTRLEN];
2881 nice_address_to_string (&pair->local->addr, tmpbuf1);
2882 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2883 nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, "
2884 "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
2885 "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%08x, %s.", agent,
2886 tmpbuf1, nice_address_get_port (&pair->local->addr),
2887 tmpbuf2, nice_address_get_port (&pair->remote->addr),
2888 pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1,
2889 pair, pair->component_id,
2890 (unsigned long long)agent->tie_breaker,
2891 (int) uname_len, uname, uname_len,
2892 (int) password_len, password, password_len,
2893 pair->stun_priority,
2894 controlling ? "controlling" : "controlled");
2897 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2898 switch (agent->nomination_mode) {
2899 case NICE_NOMINATION_MODE_REGULAR:
2900 /* We are doing regular nomination, so we set the use-candidate
2901 * attrib, when the controlling agent decided which valid pair to
2902 * resend with this flag in priv_conn_check_tick_stream()
2904 cand_use = pair->use_candidate_on_next_check;
2905 nice_debug ("Agent %p : %s: set cand_use=%d "
2906 "(regular nomination).", agent, G_STRFUNC, cand_use);
2908 case NICE_NOMINATION_MODE_AGGRESSIVE:
2909 /* We are doing aggressive nomination, we set the use-candidate
2910 * attrib in every check we send, when we are the controlling
2911 * agent, RFC 5245, 8.1.1.2
2913 cand_use = controlling;
2914 nice_debug ("Agent %p : %s: set cand_use=%d "
2915 "(aggressive nomination).", agent, G_STRFUNC, cand_use);
2918 /* Nothing to do. */
2921 } else if (cand_use)
2922 pair->nominated = controlling;
2924 if (uname_len == 0) {
2925 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent);
2926 g_free (free_password);
2930 stun = priv_add_stun_transaction (pair);
2932 buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
2933 &stun->message, stun->buffer, sizeof(stun->buffer),
2934 uname, uname_len, password, password_len,
2935 cand_use, controlling, pair->stun_priority,
2937 pair->local->foundation,
2938 agent_to_ice_compatibility (agent));
2940 nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len,
2941 stun->message.buffer);
2943 g_free (free_password);
2945 if (buffer_len == 0) {
2946 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent);
2947 priv_remove_stun_transaction (pair, stun, component);
2951 if (nice_socket_is_reliable(pair->sockptr)) {
2952 timeout = agent->stun_reliable_timeout;
2953 stun_timer_start_reliable(&stun->timer, timeout);
2955 timeout = priv_compute_conncheck_timer (agent, stream);
2956 stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions);
2959 stun->next_tick = g_get_monotonic_time () + timeout * 1000;
2961 /* TCP-ACTIVE candidate must create a new socket before sending
2962 * by connecting to the peer. The new socket is stored in the candidate
2963 * check pair, until we discover a new local peer reflexive */
2964 if (pair->sockptr->fileno == NULL &&
2965 pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN &&
2966 pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
2967 NiceStream *stream2 = NULL;
2968 NiceComponent *component2 = NULL;
2969 NiceSocket *new_socket;
2971 if (agent_find_component (agent, pair->stream_id, pair->component_id,
2972 &stream2, &component2)) {
2973 new_socket = nice_tcp_active_socket_connect (pair->sockptr,
2974 &pair->remote->addr);
2976 nice_debug ("Agent %p: add to tcp-act socket %p a new "
2977 "tcp connect socket %p on pair %p in s/c %d/%d",
2978 agent, pair->sockptr, new_socket, pair, stream->id, component->id);
2979 pair->sockptr = new_socket;
2980 _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
2982 nice_socket_set_writable_callback (pair->sockptr, _tcp_sock_is_writable,
2985 nice_component_attach_socket (component2, new_socket);
2987 priv_remove_stun_transaction (pair, stun, component);
2992 /* send the conncheck */
2993 if (agent_socket_send (pair->sockptr, &pair->remote->addr,
2994 buffer_len, (gchar *)stun->buffer) < 0) {
2995 priv_remove_stun_transaction (pair, stun, component);
2999 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2)
3000 ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr,
3001 &pair->remote->addr);
3007 * Implemented the pruning steps described in ICE sect 8.1.2
3008 * "Updating States" (ID-19) after a pair has been nominated.
3010 * @see conn_check_update_check_list_state_failed_components()
3012 static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
3016 guint in_progress = 0;
3017 guint triggered_check = 0;
3018 gchar prio[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
3020 nice_debug ("Agent %p: Pruning pending checks for s%d/c%d",
3021 agent, stream->id, component->id);
3023 /* Called when we have at least one selected pair */
3024 priority = component->selected_pair.priority;
3025 g_assert (priority > 0);
3027 nice_candidate_pair_priority_to_string (priority, prio);
3028 nice_debug ("Agent %p : selected pair priority is %s", agent, prio);
3030 i = stream->conncheck_list;
3032 CandidateCheckPair *p = i->data;
3033 GSList *next = i->next;
3035 if (p->component_id != component->id) {
3040 /* We do not remove a pair from the conncheck list if it is also in
3041 * the triggered check queue. This is not what suggests the ICE
3042 * spec, but it proved to be more robust in the aggressive
3043 * nomination scenario, precisely because these pairs may have the
3044 * use-candidate flag set, and the peer agent may already have
3045 * selected such one.
3047 if (g_slist_find (agent->triggered_check_queue, p) &&
3048 p->state != NICE_CHECK_IN_PROGRESS) {
3049 if (p->priority < priority) {
3050 nice_debug ("Agent %p : pair %p removed.", agent, p);
3051 candidate_check_pair_free (agent, p);
3052 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3057 /* step: cancel all FROZEN and WAITING pairs for the component */
3058 else if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
3059 nice_debug ("Agent %p : pair %p removed.", agent, p);
3060 candidate_check_pair_free (agent, p);
3061 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3064 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
3065 else if (p->state == NICE_CHECK_IN_PROGRESS) {
3066 if (p->priority < priority) {
3067 priv_remove_pair_from_triggered_check_queue (agent, p);
3068 if (p->retransmit) {
3069 p->retransmit = FALSE;
3070 nice_debug ("Agent %p : pair %p will not be retransmitted.",
3074 /* We must keep the higher priority pairs running because if a udp
3075 * packet was lost, we might end up using a bad candidate */
3076 nice_candidate_pair_priority_to_string (p->priority, prio);
3077 nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority "
3078 "%s is higher than priority of best nominated pair.", agent, p, prio);
3079 /* We may also have to enable the retransmit flag of pairs with
3080 * a higher priority than the first nominated pair
3082 if (!p->retransmit && p->stun_transactions) {
3083 p->retransmit = TRUE;
3084 nice_debug ("Agent %p : pair %p will be retransmitted.", agent, p);
3092 return in_progress + triggered_check;
3096 * Schedules a triggered check after a successfully inbound
3097 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
3099 * @param agent self pointer
3100 * @param component the check is related to
3101 * @param local_socket socket from which the inbound check was received
3102 * @param remote_cand remote candidate from which the inbound check was sent
3104 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand)
3107 NiceCandidate *local = NULL;
3108 CandidateCheckPair *p;
3110 g_assert (remote_cand != NULL);
3112 nice_debug ("Agent %p : scheduling triggered check with socket=%p "
3113 "and remote cand=%p.", agent, local_socket, remote_cand);
3115 for (i = stream->conncheck_list; i ; i = i->next) {
3117 if (p->component_id == component->id &&
3118 p->remote == remote_cand &&
3119 p->sockptr == local_socket) {
3120 /* If we match with a peer-reflexive discovered pair, we
3121 * use the parent succeeded pair instead */
3123 if (p->succeeded_pair != NULL) {
3124 g_assert (p->state == NICE_CHECK_DISCOVERED);
3125 p = p->succeeded_pair;
3128 nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...",
3129 agent, p, p->foundation, priv_state_to_string (p->state));
3132 case NICE_CHECK_WAITING:
3133 case NICE_CHECK_FROZEN:
3134 nice_debug ("Agent %p : pair %p added for a triggered check.",
3136 priv_add_pair_to_triggered_check_queue (agent, p);
3138 case NICE_CHECK_IN_PROGRESS:
3139 /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks"
3140 * we cancel the in-progress transaction, and after the
3141 * retransmission timeout, we create a new connectivity check
3142 * for that pair. The controlling role of this new check may
3143 * be different from the role of this cancelled check.
3145 * When another pair, with a higher priority is already
3146 * nominated, so there's no reason to recheck this pair,
3147 * since it can in no way replace the nominated one.
3149 if (p->priority > component->selected_pair.priority) {
3150 nice_debug ("Agent %p : pair %p added for a triggered check.",
3152 priv_add_pair_to_triggered_check_queue (agent, p);
3155 case NICE_CHECK_FAILED:
3156 if (p->priority > component->selected_pair.priority) {
3157 nice_debug ("Agent %p : pair %p added for a triggered check.",
3159 priv_add_pair_to_triggered_check_queue (agent, p);
3160 /* If the component for this pair is in failed state, move it
3161 * back to connecting, and reinitiate the timers
3163 if (component->state == NICE_COMPONENT_STATE_FAILED)
3164 agent_signal_component_state_change (agent, stream->id,
3165 component->id, NICE_COMPONENT_STATE_CONNECTING);
3166 /* If the component if in ready state, move it back to
3167 * connected as this failed pair with a higher priority
3168 * than the nominated pair requires to pursue the
3171 else if (component->state == NICE_COMPONENT_STATE_READY)
3172 agent_signal_component_state_change (agent, stream->id,
3173 component->id, NICE_COMPONENT_STATE_CONNECTED);
3176 case NICE_CHECK_SUCCEEDED:
3177 nice_debug ("Agent %p : nothing to do for pair %p.", agent, p);
3183 /* note: the spec says the we SHOULD retransmit in-progress
3184 * checks immediately, but we won't do that now */
3190 for (i = component->local_candidates; i ; i = i->next) {
3191 NiceCandidateImpl *lc = i->data;
3193 if (lc->sockptr == local_socket)
3198 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
3199 p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id,
3200 component, local, remote_cand, NICE_CHECK_WAITING);
3202 priv_add_pair_to_triggered_check_queue (agent, p);
3206 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
3213 * Sends a reply to a successfully received STUN connectivity
3214 * check request. Implements parts of the ICE spec section 7.2 (STUN
3215 * Server Procedures).
3217 * @param agent context pointer
3218 * @param stream which stream (of the agent)
3219 * @param component which component (of the stream)
3220 * @param rcand remote candidate from which the request came, if NULL,
3221 * the response is sent immediately but no other processing is done
3222 * @param toaddr address to which reply is sent
3223 * @param socket the socket over which the request came
3224 * @param rbuf_len length of STUN message to send
3225 * @param msg the STUN message to send
3226 * @param use_candidate whether the request had USE_CANDIDATE attribute
3228 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
3230 static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream,
3231 NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand,
3232 const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len,
3233 StunMessage *msg, gboolean use_candidate)
3235 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
3237 if (nice_debug_is_enabled ()) {
3238 gchar tmpbuf[INET6_ADDRSTRLEN];
3239 StunTransactionId id;
3240 nice_address_to_string (toaddr, tmpbuf);
3242 /* get stun message transaction id and convert it to hex. */
3243 stun_message_id(msg, id);
3244 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u),"
3246 "transactionId=%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx",
3249 nice_address_get_port (toaddr),
3250 sockptr->fileno ? g_socket_get_fd(sockptr->fileno) : -1,
3252 rcand, component->id,
3254 id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10],
3255 id[11], id[12], id[13], id[14], id[15]);
3258 agent_socket_send (sockptr, toaddr, rbuf_len, (const gchar*)msg->buffer);
3259 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
3260 ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr);
3263 /* We react to this stun request when we have the remote credentials.
3264 * When credentials are not yet known, this request is stored
3265 * in incoming_checks for later processing when returning from this
3268 if (rcand && stream->remote_ufrag[0]) {
3269 priv_schedule_triggered_check (agent, stream, component, sockptr, rcand);
3271 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
3276 * Stores information of an incoming STUN connectivity check
3277 * for later use. This is only needed when a check is received
3278 * before we get information about the remote candidates (via
3279 * SDP or other signaling means).
3281 * @return non-zero on error, zero on success
3283 static int priv_store_pending_check (NiceAgent *agent, NiceComponent *component,
3284 const NiceAddress *from, NiceSocket *sockptr, uint8_t *username,
3285 uint16_t username_len, uint32_t priority, gboolean use_candidate)
3287 IncomingCheck *icheck;
3288 nice_debug ("Agent %p : Storing pending check.", agent);
3290 if (g_queue_get_length (&component->incoming_checks) >=
3291 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
3292 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
3296 icheck = g_slice_new0 (IncomingCheck);
3297 g_queue_push_tail (&component->incoming_checks, icheck);
3298 icheck->from = *from;
3299 icheck->local_socket = sockptr;
3300 icheck->priority = priority;
3301 icheck->use_candidate = use_candidate;
3302 icheck->username_len = username_len;
3303 icheck->username = NULL;
3304 if (username_len > 0)
3305 icheck->username = g_memdup (username, username_len);
3311 * Adds a new pair, discovered from an incoming STUN response, to
3312 * the connectivity check list.
3314 * @return created pair, or NULL on fatal (memory allocation) errors
3316 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidateImpl *local_cand, CandidateCheckPair *parent_pair)
3318 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
3319 NiceStream *stream = agent_find_stream (agent, stream_id);
3321 pair->stream_id = stream_id;
3322 pair->component_id = component->id;;
3323 pair->local = (NiceCandidate *) local_cand;
3324 pair->remote = parent_pair->remote;
3325 pair->sockptr = local_cand->sockptr;
3326 parent_pair->discovered_pair = pair;
3327 pair->succeeded_pair = parent_pair;
3328 nice_debug ("Agent %p : creating a new pair", agent);
3329 SET_PAIR_STATE (agent, pair, NICE_CHECK_DISCOVERED);
3331 gchar tmpbuf1[INET6_ADDRSTRLEN];
3332 gchar tmpbuf2[INET6_ADDRSTRLEN];
3333 nice_address_to_string (&pair->local->addr, tmpbuf1);
3334 nice_address_to_string (&pair->remote->addr, tmpbuf2);
3335 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
3336 tmpbuf1, nice_address_get_port (&pair->local->addr),
3337 tmpbuf2, nice_address_get_port (&pair->remote->addr));
3339 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
3340 local_cand->c.foundation, parent_pair->remote->foundation);
3342 if (agent->controlling_mode == TRUE)
3343 pair->priority = nice_candidate_pair_priority (pair->local->priority,
3344 pair->remote->priority);
3346 pair->priority = nice_candidate_pair_priority (pair->remote->priority,
3347 pair->local->priority);
3348 pair->nominated = parent_pair->nominated;
3349 /* the peer-reflexive priority used in stun request is copied from
3350 * the parent succeeded pair. This value is not required for discovered
3351 * pair, that won't emit stun requests themselves, but may be used when
3352 * such pair becomes the selected pair, and when keepalive stun are emitted,
3353 * using the sockptr and stun_priority values from the succeeded pair.
3355 pair->stun_priority = parent_pair->stun_priority;
3356 nice_debug ("Agent %p : added a new peer-discovered pair %p with "
3357 "foundation '%s' and transport %s:%s to stream %u component %u",
3358 agent, pair, pair->foundation,
3359 nice_candidate_transport_to_string (pair->local->transport),
3360 nice_candidate_transport_to_string (pair->remote->transport),
3361 stream_id, component->id);
3363 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
3364 (GCompareFunc)conn_check_compare);
3370 * Recalculates priorities of all candidate pairs. This
3371 * is required after a conflict in ICE roles.
3373 void recalculate_pair_priorities (NiceAgent *agent)
3377 for (i = agent->streams; i; i = i->next) {
3378 NiceStream *stream = i->data;
3379 for (j = stream->conncheck_list; j; j = j->next) {
3380 CandidateCheckPair *p = j->data;
3381 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
3383 stream->conncheck_list = g_slist_sort (stream->conncheck_list,
3384 (GCompareFunc)conn_check_compare);
3389 * Change the agent role if different from 'control'. Can be
3390 * initiated both by handling of incoming connectivity checks,
3391 * and by processing the responses to checks sent by us.
3393 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
3395 /* role conflict, change mode; wait for a new conn. check */
3396 if (control != agent->controlling_mode) {
3397 nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".",
3398 agent, control ? "controlling" : "controlled");
3399 agent->controlling_mode = control;
3400 /* the pair priorities depend on the roles, so recalculation
3402 recalculate_pair_priorities (agent);
3405 nice_debug ("Agent %p : Role conflict, staying with role \"%s\".",
3406 agent, control ? "controlling" : "controlled");
3410 * Checks whether the mapped address in connectivity check response
3411 * matches any of the known local candidates. If not, apply the
3412 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
3414 * @param agent context pointer
3415 * @param stream which stream (of the agent)
3416 * @param component which component (of the stream)
3417 * @param p the connectivity check pair for which we got a response
3418 * @param socketptr socket used to send the reply
3419 * @param mapped_sockaddr mapped address in the response
3421 * @return pointer to a candidate pair, found in conncheck list or newly created
3423 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)
3425 CandidateCheckPair *new_pair = NULL;
3428 NiceCandidate *local_cand = NULL;
3430 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
3432 for (i = component->local_candidates; i; i = i->next) {
3433 NiceCandidate *cand = i->data;
3435 if (nice_address_equal (&mapped, &cand->addr) &&
3436 local_candidate_and_socket_compatible (agent, cand, sockptr)) {
3442 /* The mapped address allows to look for a previously discovered
3443 * peer reflexive local candidate, and its related pair. This
3444 * new_pair will be marked 'Valid', while the pair 'p' of the
3445 * initial stun request will be marked 'Succeeded'
3447 * In the case of a tcp-act/tcp-pass pair 'p', where the local
3448 * candidate is of type tcp-act, and its port number is zero, a
3449 * conncheck on this pair *always* leads to the creation of a
3450 * discovered peer-reflexive tcp-act local candidate.
3452 for (i = stream->conncheck_list; i; i = i->next) {
3453 CandidateCheckPair *pair = i->data;
3454 if (local_cand == pair->local && remote_candidate == pair->remote) {
3461 /* note: when new_pair is distinct from p, it means new_pair is a
3462 * previously discovered peer-reflexive candidate pair, so we don't
3463 * set the valid flag on p in this case, because the valid flag is
3464 * already set on the discovered pair.
3469 /* this new_pair distinct from p may also be in state failed (if
3470 * the related succeeded pair p was in state failed previously, but
3471 * retriggered a successful check a bit later), so we force its
3472 * state back to discovered there.
3474 SET_PAIR_STATE (agent, new_pair, NICE_CHECK_DISCOVERED);
3476 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3477 priv_remove_pair_from_triggered_check_queue (agent, p);
3478 priv_free_all_stun_transactions (p, component);
3479 nice_component_add_valid_candidate (agent, component, remote_candidate);
3482 if (local_cand == NULL && !agent->force_relay) {
3483 /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
3484 * "Discovering Peer Reflexive Candidates"
3486 * The priority equal to the value of the PRIORITY attribute
3487 * in the Binding request is taken from the "parent" pair p
3489 local_cand = discovery_add_peer_reflexive_candidate (agent,
3497 nice_debug ("Agent %p : added a new peer-reflexive local candidate %p "
3498 "with transport %s", agent, local_cand,
3499 nice_candidate_transport_to_string (local_cand->transport));
3502 /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
3503 "Constructing a Valid Pair") */
3505 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component,
3506 (NiceCandidateImpl *) local_cand, p);
3507 /* note: this is same as "adding to VALID LIST" in the spec
3510 new_pair->valid = TRUE;
3511 /* step: The agent sets the state of the pair that *generated* the check to
3512 * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States"
3514 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3515 priv_remove_pair_from_triggered_check_queue (agent, p);
3516 priv_free_all_stun_transactions (p, component);
3519 if (new_pair && new_pair->valid)
3520 nice_component_add_valid_candidate (agent, component, remote_candidate);
3527 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
3528 * check transaction. If found, the reply is processed. Implements
3529 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
3531 * @return TRUE if a matching transaction is found
3533 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)
3536 struct sockaddr_storage storage;
3537 struct sockaddr addr;
3539 socklen_t socklen = sizeof (sockaddr);
3542 StunUsageIceReturn res;
3543 StunTransactionId discovery_id;
3544 StunTransactionId response_id;
3545 stun_message_id (resp, response_id);
3547 for (i = stream->conncheck_list; i; i = i->next) {
3548 CandidateCheckPair *p = i->data;
3550 for (j = p->stun_transactions, k = 0; j; j = j->next, k++) {
3551 StunTransaction *stun = j->data;
3553 stun_message_id (&stun->message, discovery_id);
3555 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)))
3558 res = stun_usage_ice_conncheck_process (resp,
3559 &sockaddr.storage, &socklen,
3560 agent_to_ice_compatibility (agent));
3561 nice_debug ("Agent %p : stun_bind_process/conncheck for %p: "
3562 "%s,res=%s,stun#=%d.",
3564 agent->controlling_mode ? "controlling" : "controlled",
3565 priv_ice_return_to_string (res), k);
3567 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
3568 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3569 /* case: found a matching connectivity check request */
3571 CandidateCheckPair *ok_pair = NULL;
3573 nice_debug ("Agent %p : pair %p MATCHED.", agent, p);
3574 priv_remove_stun_transaction (p, stun, component);
3576 /* step: verify that response came from the same IP address we
3577 * sent the original request to (see 7.1.2.1. "Failure
3579 if (nice_address_equal (from, &p->remote->addr) == FALSE) {
3580 candidate_check_pair_fail (stream, agent, p);
3581 if (nice_debug_is_enabled ()) {
3582 gchar tmpbuf[INET6_ADDRSTRLEN];
3583 gchar tmpbuf2[INET6_ADDRSTRLEN];
3584 nice_debug ("Agent %p : pair %p FAILED"
3585 " (mismatch of source address).", agent, p);
3586 nice_address_to_string (&p->remote->addr, tmpbuf);
3587 nice_address_to_string (from, tmpbuf2);
3588 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
3589 tmpbuf, nice_address_get_port (&p->remote->addr),
3590 tmpbuf2, nice_address_get_port (from));
3592 conn_check_update_check_list_state_for_ready (agent,
3597 if (remote_candidate == NULL) {
3598 candidate_check_pair_fail (stream, agent, p);
3599 if (nice_debug_is_enabled ()) {
3600 nice_debug ("Agent %p : pair %p FAILED "
3601 "(got a matching pair without a known remote candidate).", agent, p);
3603 conn_check_update_check_list_state_for_ready (agent,
3608 /* note: CONNECTED but not yet READY, see docs */
3610 /* step: handle the possible case of a peer-reflexive
3611 * candidate where the mapped-address in response does
3612 * not match any local candidate, see 7.1.2.2.1
3613 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
3615 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3616 nice_debug ("Agent %p : Mapped address not found", agent);
3617 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3619 nice_component_add_valid_candidate (agent, component, p->remote);
3621 ok_pair = priv_process_response_check_for_reflexive (agent,
3622 stream, component, p, sockptr, &sockaddr.addr,
3623 local_candidate, remote_candidate);
3625 /* note: The success of this check might also
3626 * cause the state of other checks to change as well
3627 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of
3628 * ICE spec (RFC8445).
3630 conn_check_unfreeze_related (agent, p);
3632 /* Note: this assignment helps to reduce the numbers of cases
3633 * to be tested. If ok_pair and p refer to distinct pairs, it
3634 * means that ok_pair is a discovered peer reflexive one,
3635 * caused by the check made on pair p. In that case, the
3636 * flags to be tested are on p, but the nominated flag will be
3637 * set on ok_pair. When there's no discovered pair, p and
3638 * ok_pair refer to the same pair.
3639 * To summarize : p is a SUCCEEDED pair, ok_pair is a
3640 * DISCOVERED, VALID, and eventually NOMINATED pair.
3645 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
3646 Nominated Flag" (ID-19) */
3647 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
3648 nice_debug ("Agent %p : Updating nominated flag (%s): "
3649 "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)",
3650 agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ?
3652 ok_pair, ok_pair->use_candidate_on_next_check,
3653 ok_pair->mark_nominated_on_response_arrival,
3654 p, p->use_candidate_on_next_check,
3655 p->mark_nominated_on_response_arrival);
3657 if (agent->controlling_mode) {
3658 switch (agent->nomination_mode) {
3659 case NICE_NOMINATION_MODE_REGULAR:
3660 if (p->use_candidate_on_next_check) {
3661 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3662 "(regular nomination, controlling, "
3663 "use_cand_on_next_check=1).",
3664 agent, ok_pair, ok_pair->foundation);
3665 ok_pair->nominated = TRUE;
3668 case NICE_NOMINATION_MODE_AGGRESSIVE:
3669 if (!p->nominated) {
3670 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3671 "(aggressive nomination, controlling).",
3672 agent, ok_pair, ok_pair->foundation);
3673 ok_pair->nominated = TRUE;
3681 if (p->mark_nominated_on_response_arrival) {
3682 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3683 "(%s nomination, controlled, mark_on_response=1).",
3684 agent, ok_pair, ok_pair->foundation,
3685 agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
3686 "aggressive" : "regular");
3687 ok_pair->nominated = TRUE;
3692 if (ok_pair->nominated == TRUE) {
3693 conn_check_update_selected_pair (agent, component, ok_pair);
3694 priv_print_conn_check_lists (agent, G_STRFUNC,
3695 ", got a nominated pair");
3697 /* Do not step down to CONNECTED if we're already at state READY*/
3698 if (component->state != NICE_COMPONENT_STATE_READY)
3699 /* step: notify the client of a new component state (must be done
3700 * before the possible check list state update step */
3701 agent_signal_component_state_change (agent,
3702 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
3705 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
3706 states" and 8.1.2 "Updating States", ID-19) */
3707 conn_check_update_check_list_state_for_ready (agent, stream, component);
3708 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
3710 gboolean controlled_mode;
3712 if (!p->retransmit) {
3713 nice_debug ("Agent %p : Role conflict with pair %p, not restarting",
3718 /* case: role conflict error, need to restart with new role */
3719 nice_debug ("Agent %p : Role conflict with pair %p, restarting",
3722 /* note: this res value indicates that the role of the peer
3723 * agent has not changed after the tie-breaker comparison, so
3724 * this is our role that must change. see ICE sect. 7.1.3.1
3725 * "Failure Cases". Our role might already have changed due to
3726 * an earlier incoming request, but if not, change role now.
3728 * Sect. 7.1.3.1 is not clear on this point, but we choose to
3729 * put the candidate pair in the triggered check list even
3730 * when the agent did not switch its role. The reason for this
3731 * interpretation is that the reception of the stun reply, even
3732 * an error reply, is a good sign that this pair will be
3733 * valid, if we retry the check after the role of both peers
3736 controlled_mode = (stun_message_find64 (&stun->message,
3737 STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) ==
3738 STUN_MESSAGE_RETURN_SUCCESS);
3740 priv_check_for_role_conflict (agent, controlled_mode);
3741 priv_remove_stun_transaction (p, stun, component);
3742 priv_add_pair_to_triggered_check_queue (agent, p);
3744 /* case: STUN error, the check STUN context was freed */
3745 candidate_check_pair_fail (stream, agent, p);
3746 conn_check_update_check_list_state_for_ready (agent, stream, component);
3756 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3757 * transaction. If found, a reply is sent.
3759 * @return TRUE if a matching transaction is found
3761 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp, const NiceAddress *server_address)
3764 struct sockaddr_storage storage;
3765 struct sockaddr addr;
3767 socklen_t socklen = sizeof (sockaddr);
3770 struct sockaddr_storage storage;
3771 struct sockaddr addr;
3773 socklen_t alternatelen = sizeof (sockaddr);
3776 StunUsageBindReturn res;
3777 gboolean trans_found = FALSE;
3778 StunTransactionId discovery_id;
3779 StunTransactionId response_id;
3780 stun_message_id (resp, response_id);
3782 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
3783 CandidateDiscovery *d = i->data;
3785 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
3786 d->stun_message.buffer) {
3787 stun_message_id (&d->stun_message, discovery_id);
3789 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
3790 res = stun_usage_bind_process (resp, &sockaddr.addr,
3791 &socklen, &alternate.addr, &alternatelen);
3792 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
3793 agent, d, (int)res);
3795 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
3796 /* handle alternate server */
3797 NiceAddress niceaddr;
3798 nice_address_set_from_sockaddr (&niceaddr, &alternate.addr);
3799 d->server = niceaddr;
3802 agent->discovery_unsched_items++;
3803 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
3804 /* case: successful binding discovery, create a new local candidate */
3806 if (!agent->force_relay) {
3807 NiceAddress niceaddr;
3809 nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr);
3810 discovery_add_server_reflexive_candidate (
3815 NICE_CANDIDATE_TRANSPORT_UDP,
3820 if (agent->use_ice_tcp)
3821 discovery_discover_tcp_server_reflexive_candidates (
3829 d->stun_message.buffer = NULL;
3830 d->stun_message.buffer_len = 0;
3833 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
3834 /* case: STUN error, the check STUN context was freed */
3835 d->stun_message.buffer = NULL;
3836 d->stun_message.buffer_len = 0;
3848 priv_calc_turn_timeout (guint lifetime)
3851 return lifetime - 60;
3853 return lifetime / 2;
3857 priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
3858 NiceCandidateImpl *relay_cand, guint lifetime)
3860 CandidateRefresh *cand;
3862 if (cdisco->turn->type == NICE_RELAY_TYPE_TURN_TLS &&
3863 (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
3864 agent->compatibility == NICE_COMPATIBILITY_OC2007R2))
3867 cand = g_slice_new0 (CandidateRefresh);
3869 cand->candidate = relay_cand;
3870 cand->nicesock = cdisco->nicesock;
3871 cand->server = cdisco->server;
3872 cand->stream_id = cdisco->stream_id;
3873 cand->component_id = cdisco->component_id;
3874 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
3876 /* Use previous stun response for authentication credentials */
3877 if (cdisco->stun_resp_msg.buffer != NULL) {
3878 memcpy(cand->stun_resp_buffer, cdisco->stun_resp_buffer,
3879 sizeof(cand->stun_resp_buffer));
3880 memcpy(&cand->stun_resp_msg, &cdisco->stun_resp_msg, sizeof(StunMessage));
3881 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
3882 cand->stun_resp_msg.agent = &cand->stun_agent;
3883 cand->stun_resp_msg.key = NULL;
3887 agent->refresh_list = g_slist_append (agent->refresh_list, cand);
3888 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
3889 agent, cand, priv_calc_turn_timeout (lifetime));
3890 /* step: also start the refresh timer */
3891 /* refresh should be sent 1 minute before it expires */
3892 agent_timeout_add_seconds_with_context (agent, &cand->timer_source,
3893 "Candidate TURN refresh",
3894 priv_calc_turn_timeout (lifetime),
3895 priv_turn_allocate_refresh_tick_agent_locked, cand);
3897 nice_debug ("timer source is : %p", cand->timer_source);
3899 agent->pruning_refreshes = g_slist_append (agent->pruning_refreshes, cand);
3900 nice_debug ("Agent %p : Sending request to remove TURN allocation "
3901 "for refresh %p", agent, cand);
3902 cand->disposing = TRUE;
3903 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
3904 if (relay_cand->sockptr)
3905 nice_socket_free (relay_cand->sockptr);
3906 nice_candidate_free ((NiceCandidate *)relay_cand);
3912 static void priv_handle_turn_alternate_server (NiceAgent *agent,
3913 CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate)
3915 /* We need to cancel and reset all candidate discovery turn for the same
3916 stream and type if there is an alternate server. Otherwise, we might end up
3917 with two relay components on different servers, creating candidates with
3918 unique foundations that only contain one component.
3922 for (i = agent->discovery_list; i; i = i->next) {
3923 CandidateDiscovery *d = i->data;
3926 d->type == disco->type &&
3927 d->stream_id == disco->stream_id &&
3928 d->turn->type == disco->turn->type &&
3929 nice_address_equal (&d->server, &server)) {
3930 gchar ip[INET6_ADDRSTRLEN];
3931 // Cancel the pending request to avoid a race condition with another
3932 // component responding with another altenrate-server
3933 d->stun_message.buffer = NULL;
3934 d->stun_message.buffer_len = 0;
3936 nice_address_to_string (&server, ip);
3937 nice_debug ("Agent %p : Cancelling and setting alternate server %s for "
3938 "CandidateDiscovery %p on s%d/c%d", agent, ip, d,
3939 d->stream_id, d->component_id);
3940 d->server = alternate;
3941 d->turn->server = alternate;
3943 agent->discovery_unsched_items++;
3945 if (d->turn->type == NICE_RELAY_TYPE_TURN_TCP ||
3946 d->turn->type == NICE_RELAY_TYPE_TURN_TLS) {
3948 NiceComponent *component;
3950 if (!agent_find_component (agent, d->stream_id, d->component_id,
3951 &stream, &component)) {
3952 nice_debug ("Could not find stream or component in "
3953 "priv_handle_turn_alternate_server");
3956 d->nicesock = agent_create_tcp_turn_socket (agent, stream, component,
3957 d->nicesock, &d->server, d->turn->type,
3958 nice_socket_is_reliable (d->nicesock));
3960 nice_component_attach_socket (component, d->nicesock);
3967 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3968 * transaction. If found, a reply is sent.
3970 * @return TRUE if a matching transaction is found
3972 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
3975 struct sockaddr_storage storage;
3976 struct sockaddr addr;
3978 socklen_t socklen = sizeof (sockaddr);
3981 struct sockaddr_storage storage;
3982 struct sockaddr addr;
3984 socklen_t alternatelen = sizeof (alternate);
3987 struct sockaddr_storage storage;
3988 struct sockaddr addr;
3990 socklen_t relayaddrlen = sizeof (relayaddr);
3995 StunUsageTurnReturn res;
3996 gboolean trans_found = FALSE;
3997 StunTransactionId discovery_id;
3998 StunTransactionId response_id;
3999 stun_message_id (resp, response_id);
4001 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
4002 CandidateDiscovery *d = i->data;
4004 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
4005 d->stun_message.buffer) {
4006 stun_message_id (&d->stun_message, discovery_id);
4008 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
4009 res = stun_usage_turn_process (resp,
4010 &relayaddr.storage, &relayaddrlen,
4011 &sockaddr.storage, &socklen,
4012 &alternate.storage, &alternatelen,
4013 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
4014 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
4015 agent, d, (int)res);
4017 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
4020 /* handle alternate server */
4021 nice_address_set_from_sockaddr (&addr, &alternate.addr);
4022 priv_handle_turn_alternate_server (agent, d, d->server, addr);
4024 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
4025 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
4026 /* case: successful allocate, create a new local candidate */
4027 NiceAddress niceaddr;
4028 NiceCandidateImpl *relay_cand;
4030 nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr);
4032 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
4033 NiceAddress mappedniceaddr;
4035 /* We also received our mapped address */
4036 nice_address_set_from_sockaddr (&mappedniceaddr, &sockaddr.addr);
4038 /* TCP or TLS TURNS means the server-reflexive address was
4039 * on a TCP connection, which cannot be used for server-reflexive
4040 * discovery of candidates.
4042 if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP &&
4043 !agent->force_relay) {
4044 discovery_add_server_reflexive_candidate (
4049 NICE_CANDIDATE_TRANSPORT_UDP,
4054 if (agent->use_ice_tcp) {
4055 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4056 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4057 !nice_address_equal_no_port (&niceaddr, &d->turn->server)) {
4058 nice_debug("TURN port got allocated on an alternate server, "
4059 "ignoring bogus srflx address");
4061 discovery_discover_tcp_server_reflexive_candidates (
4072 if (nice_socket_is_reliable (d->nicesock)) {
4073 relay_cand = discovery_add_relay_candidate (
4078 NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE,
4084 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4085 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4086 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4088 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4091 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4094 relay_cand = discovery_add_relay_candidate (
4099 NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE,
4104 relay_cand = discovery_add_relay_candidate (
4109 NICE_CANDIDATE_TRANSPORT_UDP,
4116 if (d->stun_resp_msg.buffer)
4117 nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr,
4119 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4120 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4121 /* These data are needed on TURN socket when sending requests,
4122 * but never reach nice_turn_socket_parse_recv() where it could
4123 * be read directly, as the socket does not exist when allocate
4124 * response arrives to _nice_agent_recv(). We must set them right
4125 * after socket gets created in discovery_add_relay_candidate(),
4126 * so we are doing it here. */
4127 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4129 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4132 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4135 d->stun_message.buffer = NULL;
4136 d->stun_message.buffer_len = 0;
4139 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4141 uint8_t *sent_realm = NULL;
4142 uint8_t *recv_realm = NULL;
4143 uint16_t sent_realm_len = 0;
4144 uint16_t recv_realm_len = 0;
4146 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
4147 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4148 recv_realm = (uint8_t *) stun_message_find (resp,
4149 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4151 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4152 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4153 alternatelen != sizeof(alternate)) {
4156 nice_address_set_from_sockaddr (&addr, &alternate.addr);
4158 if (!nice_address_equal (&addr, &d->server)) {
4159 priv_handle_turn_alternate_server (agent, d, d->server, addr);
4162 /* check for unauthorized error response */
4163 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
4164 agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4165 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4166 stun_message_get_class (resp) == STUN_ERROR &&
4167 stun_message_find_error (resp, &code) ==
4168 STUN_MESSAGE_RETURN_SUCCESS &&
4169 recv_realm != NULL && recv_realm_len > 0) {
4171 if (code == STUN_ERROR_STALE_NONCE ||
4172 (code == STUN_ERROR_UNAUTHORIZED &&
4173 !(recv_realm_len == sent_realm_len &&
4174 sent_realm != NULL &&
4175 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4176 d->stun_resp_msg = *resp;
4177 memcpy (d->stun_resp_buffer, resp->buffer,
4178 stun_message_length (resp));
4179 d->stun_resp_msg.buffer = d->stun_resp_buffer;
4180 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
4182 agent->discovery_unsched_items++;
4184 /* case: a real unauthorized error */
4185 d->stun_message.buffer = NULL;
4186 d->stun_message.buffer_len = 0;
4189 } else if (d->pending) {
4190 /* case: STUN error, the check STUN context was freed */
4191 d->stun_message.buffer = NULL;
4192 d->stun_message.buffer_len = 0;
4206 * Tries to match STUN reply in 'buf' to an existing STUN discovery
4207 * transaction. If found, a reply is sent.
4209 * @return TRUE if a matching transaction is found
4211 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
4215 StunUsageTurnReturn res;
4216 gboolean trans_found = FALSE;
4217 StunTransactionId refresh_id;
4218 StunTransactionId response_id;
4219 stun_message_id (resp, response_id);
4221 for (i = agent->refresh_list; i && trans_found != TRUE;) {
4222 CandidateRefresh *cand = i->data;
4223 GSList *next = i->next;
4225 if (!cand->disposing && cand->stun_message.buffer) {
4226 stun_message_id (&cand->stun_message, refresh_id);
4228 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
4229 res = stun_usage_turn_refresh_process (resp,
4230 &lifetime, agent_to_turn_compatibility (agent));
4231 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d with lifetime %u.",
4232 agent, cand, (int)res, lifetime);
4233 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
4234 /* refresh should be sent 1 minute before it expires */
4235 agent_timeout_add_seconds_with_context (agent,
4236 &cand->timer_source,
4237 "Candidate TURN refresh", priv_calc_turn_timeout (lifetime),
4238 priv_turn_allocate_refresh_tick_agent_locked, cand);
4240 g_source_destroy (cand->tick_source);
4241 g_source_unref (cand->tick_source);
4242 cand->tick_source = NULL;
4244 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4246 uint8_t *sent_realm = NULL;
4247 uint8_t *recv_realm = NULL;
4248 uint16_t sent_realm_len = 0;
4249 uint16_t recv_realm_len = 0;
4251 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
4252 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4253 recv_realm = (uint8_t *) stun_message_find (resp,
4254 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4256 /* check for unauthorized error response */
4257 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
4258 stun_message_get_class (resp) == STUN_ERROR &&
4259 stun_message_find_error (resp, &code) ==
4260 STUN_MESSAGE_RETURN_SUCCESS &&
4261 recv_realm != NULL && recv_realm_len > 0) {
4263 if (code == STUN_ERROR_STALE_NONCE ||
4264 (code == STUN_ERROR_UNAUTHORIZED &&
4265 !(recv_realm_len == sent_realm_len &&
4266 sent_realm != NULL &&
4267 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4268 cand->stun_resp_msg = *resp;
4269 memcpy (cand->stun_resp_buffer, resp->buffer,
4270 stun_message_length (resp));
4271 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
4272 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
4273 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
4275 /* case: a real unauthorized error */
4276 refresh_free (agent, cand);
4279 /* case: STUN error, the check STUN context was freed */
4280 refresh_free (agent, cand);
4292 static gboolean priv_map_reply_to_relay_remove (NiceAgent *agent,
4295 StunTransactionId response_id;
4298 stun_message_id (resp, response_id);
4300 for (i = agent->refresh_list; i; i = i->next) {
4301 CandidateRefresh *cand = i->data;
4302 StunTransactionId request_id;
4303 StunUsageTurnReturn res;
4306 if (!cand->disposing || !cand->stun_message.buffer) {
4310 stun_message_id (&cand->stun_message, request_id);
4312 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
4313 res = stun_usage_turn_refresh_process (resp, &lifetime,
4314 agent_to_turn_compatibility (agent));
4316 nice_debug ("Agent %p : priv_map_reply_to_relay_remove for %p res %d "
4317 "with lifetime %u.", agent, cand, res, lifetime);
4319 if (res != STUN_USAGE_TURN_RETURN_INVALID) {
4320 refresh_free (agent, cand);
4329 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
4330 NiceComponent *component, StunMessage *resp)
4332 nice_debug ("Agent %p : Keepalive for selected pair %p received.",
4333 agent, &component->selected_pair);
4334 if (agent->consent_freshness) {
4335 guint64 now = g_get_monotonic_time();
4336 component->selected_pair.remote_consent.last_received = now;
4345 NiceComponent *component;
4347 } conncheck_validater_data;
4349 static bool conncheck_stun_validater (StunAgent *agent,
4350 StunMessage *message, uint8_t *username, uint16_t username_len,
4351 uint8_t **password, size_t *password_len, void *user_data)
4353 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
4355 gchar *ufrag = NULL;
4358 gboolean msn_msoc_nice_compatibility =
4359 data->agent->compatibility == NICE_COMPATIBILITY_MSN ||
4360 data->agent->compatibility == NICE_COMPATIBILITY_OC2007;
4362 if (data->agent->compatibility == NICE_COMPATIBILITY_OC2007 &&
4363 stun_message_get_class (message) == STUN_RESPONSE)
4364 i = data->component->remote_candidates;
4366 i = data->component->local_candidates;
4368 for (; i; i = i->next) {
4369 NiceCandidate *cand = i->data;
4373 ufrag = cand->username;
4375 ufrag = data->stream->local_ufrag;
4376 ufrag_len = ufrag? strlen (ufrag) : 0;
4378 if (ufrag && msn_msoc_nice_compatibility)
4379 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
4384 stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d",
4385 username_len, ufrag_len, username_len >= ufrag_len ?
4386 memcmp (username, ufrag, ufrag_len) : 0);
4387 stun_debug_bytes (" username: ", username, username_len);
4388 stun_debug_bytes (" ufrag: ", ufrag, ufrag_len);
4389 if (ufrag_len > 0 && username_len >= ufrag_len &&
4390 memcmp (username, ufrag, ufrag_len) == 0) {
4394 pass = cand->password;
4395 else if (data->stream && data->stream->local_password[0])
4396 pass = data->stream->local_password;
4399 *password = (uint8_t *) pass;
4400 *password_len = strlen (pass);
4402 if (msn_msoc_nice_compatibility) {
4405 data->password = g_base64_decode (pass, &pass_len);
4406 *password = data->password;
4407 *password_len = pass_len;
4411 if (msn_msoc_nice_compatibility)
4414 stun_debug ("Found valid username, returning password: '%s'", *password);
4418 if (msn_msoc_nice_compatibility)
4426 * handle RENOMINATION stun attribute
4427 * @return TRUE if nomination changed. FALSE otherwise
4429 static gboolean conn_check_handle_renomination (NiceAgent *agent, NiceStream *stream,
4430 NiceComponent *component, StunMessage *req,
4431 NiceCandidate *remote_candidate, NiceCandidate *local_candidate)
4434 if (!agent->controlling_mode && NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
4435 agent->support_renomination && remote_candidate && local_candidate)
4437 uint32_t nom_value = 0;
4438 uint16_t nom_len = 0;
4439 const void *value = stun_message_find (req, STUN_ATTRIBUTE_NOMINATION, &nom_len);
4444 memcpy (&nom_value, value, 4);
4445 nom_value = ntohl (nom_value);
4447 nice_debug ("Agent %p : received NOMINATION attr with incorrect octet length %u, expected 4 bytes",
4452 if (nice_debug_is_enabled ()) {
4453 gchar remote_str[INET6_ADDRSTRLEN];
4454 nice_address_to_string(&remote_candidate->addr, remote_str);
4455 nice_debug ("Agent %p : received NOMINATION attr for remote candidate [%s]:%u, value is %u",
4456 agent, remote_str, nice_address_get_port (&remote_candidate->addr), nom_value);
4460 * If another pair is SELECTED, change this pair's priority to be greater than
4461 * selected pair's priority so this pair gets SELECTED!
4463 if (component->selected_pair.priority &&
4464 component->selected_pair.remote && component->selected_pair.remote != (NiceCandidateImpl *) remote_candidate &&
4465 component->selected_pair.local && component->selected_pair.local != (NiceCandidateImpl *) local_candidate) {
4466 for (lst = stream->conncheck_list; lst; lst = lst->next) {
4467 CandidateCheckPair *pair = lst->data;
4468 if (pair->local == local_candidate && pair->remote == remote_candidate) {
4470 pair->priority = component->selected_pair.priority + 1;
4476 priv_mark_pair_nominated (agent, stream, component, local_candidate, remote_candidate);
4483 * Processing an incoming STUN message.
4485 * @param agent self pointer
4486 * @param stream stream the packet is related to
4487 * @param component component the packet is related to
4488 * @param nicesock socket from which the packet was received
4489 * @param from address of the sender
4490 * @param buf message contents
4491 * @param buf message length
4493 * @pre contents of 'buf' is a STUN message
4495 * @return XXX (what FALSE means exactly?)
4497 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
4498 NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from,
4499 gchar *buf, guint len)
4502 struct sockaddr_storage storage;
4503 struct sockaddr addr;
4505 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
4507 size_t rbuf_len = sizeof (rbuf);
4508 bool control = agent->controlling_mode;
4509 uint8_t uname[NICE_STREAM_MAX_UNAME];
4512 uint16_t username_len;
4515 StunValidationStatus valid;
4516 conncheck_validater_data validater_data = {agent, stream, component, NULL};
4518 NiceCandidate *remote_candidate = NULL;
4519 NiceCandidate *remote_candidate2 = NULL;
4520 NiceCandidate *local_candidate = NULL;
4521 gboolean discovery_msg = FALSE;
4523 nice_address_copy_to_sockaddr (from, &sockaddr.addr);
4525 /* note: contents of 'buf' already validated, so it is
4526 * a valid and fully received STUN message */
4528 if (nice_debug_is_enabled ()) {
4529 gchar tmpbuf[INET6_ADDRSTRLEN];
4530 nice_address_to_string (from, tmpbuf);
4531 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
4532 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
4535 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
4537 valid = stun_agent_validate (&component->stun_agent, &req,
4538 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4540 /* Check for discovery candidates stun agents */
4541 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4542 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4543 for (i = agent->discovery_list; i; i = i->next) {
4544 CandidateDiscovery *d = i->data;
4545 if (d->stream_id == stream->id && d->component_id == component->id &&
4546 d->nicesock == nicesock) {
4547 valid = stun_agent_validate (&d->stun_agent, &req,
4548 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4550 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4553 discovery_msg = TRUE;
4558 /* Check for relay refresh stun agents */
4559 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4560 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4561 for (i = agent->refresh_list; i; i = i->next) {
4562 CandidateRefresh *r = i->data;
4564 nice_debug_verbose ("Comparing r.sid=%u to sid=%u, r.cid=%u to cid=%u and %p and %p to %p",
4565 r->stream_id, stream->id, r->component_id, component->id, r->nicesock,
4566 r->candidate->sockptr, nicesock);
4568 if (r->stream_id == stream->id && r->component_id == component->id &&
4569 (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) {
4570 valid = stun_agent_validate (&r->stun_agent, &req,
4571 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4572 nice_debug ("Validating gave %d", valid);
4573 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4575 discovery_msg = TRUE;
4581 g_free (validater_data.password);
4583 if (valid == STUN_VALIDATION_NOT_STUN ||
4584 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
4585 valid == STUN_VALIDATION_BAD_REQUEST)
4587 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
4592 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
4593 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
4595 if (agent->compatibility != NICE_COMPATIBILITY_MSN &&
4596 agent->compatibility != NICE_COMPATIBILITY_OC2007) {
4597 rbuf_len = stun_agent_build_unknown_attributes_error (&component->stun_agent,
4598 &msg, rbuf, rbuf_len, &req);
4600 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4605 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
4606 nice_debug ("Agent %p : Integrity check failed.", agent);
4608 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4609 &req, STUN_ERROR_UNAUTHORIZED)) {
4610 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4611 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4612 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4613 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4617 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
4618 nice_debug ("Agent %p : Integrity check failed - bad request.", agent);
4619 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4620 &req, STUN_ERROR_BAD_REQUEST)) {
4621 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4622 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4623 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4624 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4629 if (valid == STUN_VALIDATION_FORBIDDEN) {
4630 CandidatePair *pair = &component->selected_pair;
4631 gchar tmpbuf[INET6_ADDRSTRLEN];
4632 nice_address_to_string (from, tmpbuf);
4633 nice_debug ("Agent %p : received 403: 'Forbidden' for %u/%u (stream/component) from [%s]:%u",
4634 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from));
4636 for (i = stream->conncheck_list; i; i = i->next) {
4637 CandidateCheckPair *p = i->data;
4639 if (nice_address_equal (from, &p->remote->addr)) {
4640 candidate_check_pair_fail (stream, agent, p);
4644 /* if the pair was selected, it is no longer useful */
4645 if (nice_address_equal (from, &pair->remote->c.addr)) {
4646 pair->remote_consent.have = FALSE;
4647 nice_debug ("Agent %p : pair %p lost consent for %u/%u (stream/component)",
4648 agent, pair, stream->id, component->id);
4650 /* explicit revocation received, we don't need to time out anymore */
4651 if (pair->remote_consent.tick_source) {
4652 g_source_destroy (pair->remote_consent.tick_source);
4653 g_source_unref (pair->remote_consent.tick_source);
4654 pair->remote_consent.tick_source = NULL;
4657 agent_signal_component_state_change (agent, stream->id, component->id,
4658 NICE_COMPONENT_STATE_FAILED);
4664 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
4667 for (i = component->local_candidates; i; i = i->next) {
4668 NiceCandidate *cand = i->data;
4671 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
4674 addr = &cand->base_addr;
4676 if (nice_address_equal (&nicesock->addr, addr) &&
4677 local_candidate_and_socket_compatible (agent, cand, nicesock)) {
4678 local_candidate = cand;
4683 for (i = component->remote_candidates; i; i = i->next) {
4684 NiceCandidate *cand = i->data;
4685 if (nice_address_equal (from, &cand->addr) &&
4686 remote_candidate_and_socket_compatible (agent, local_candidate,
4688 remote_candidate = cand;
4693 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4694 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4695 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4696 /* We need to find which local candidate was used */
4697 for (i = component->remote_candidates;
4698 i != NULL && remote_candidate2 == NULL; i = i->next) {
4699 for (j = component->local_candidates; j; j = j->next) {
4700 gboolean inbound = TRUE;
4701 NiceCandidate *rcand = i->data;
4702 NiceCandidate *lcand = j->data;
4704 /* If we receive a response, then the username is local:remote */
4705 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
4706 if (stun_message_get_class (&req) == STUN_REQUEST ||
4707 stun_message_get_class (&req) == STUN_INDICATION) {
4714 uname_len = priv_create_username (agent, stream,
4715 component->id, rcand, lcand,
4716 uname, sizeof (uname), inbound);
4720 stun_debug ("Comparing usernames of size %d and %d: %d",
4721 username_len, uname_len, username && uname_len == username_len &&
4722 memcmp (username, uname, uname_len) == 0);
4723 stun_debug_bytes (" First username: ", username,
4724 username ? username_len : 0);
4725 stun_debug_bytes (" Second uname: ", uname, uname_len);
4728 uname_len == username_len &&
4729 memcmp (uname, username, username_len) == 0) {
4730 local_candidate = lcand;
4731 remote_candidate2 = rcand;
4738 if (component->remote_candidates &&
4739 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
4740 local_candidate == NULL &&
4741 discovery_msg == FALSE) {
4742 /* if we couldn't match the username and the stun agent has
4743 IGNORE_CREDENTIALS then we have an integrity check failing.
4744 This could happen with the race condition of receiving connchecks
4745 before the remote candidates are added. Just drop the message, and let
4746 the retransmissions make it work. */
4747 nice_debug ("Agent %p : Username check failed.", agent);
4751 /* This is most likely caused by a second response to a request which
4752 * already has received a valid reply.
4754 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4755 nice_debug ("Agent %p : Valid STUN response for which we don't have a request, ignoring", agent);
4759 if (valid != STUN_VALIDATION_SUCCESS) {
4760 nice_debug ("Agent %p : STUN message is unsuccessful %d, ignoring", agent, valid);
4764 agent->media_after_tick = TRUE;
4766 if (stun_message_get_class (&req) == STUN_REQUEST) {
4767 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4768 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4769 if (local_candidate && remote_candidate2) {
4772 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
4773 username = (uint8_t *) stun_message_find (&req,
4774 STUN_ATTRIBUTE_USERNAME, &username_len);
4775 uname_len = priv_create_username (agent, stream,
4776 component->id, remote_candidate2, local_candidate,
4777 uname, sizeof (uname), FALSE);
4778 memcpy (username, uname, MIN (uname_len, username_len));
4780 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
4782 req.key_len = key_len;
4783 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4784 req.key = g_base64_decode ((gchar *) local_candidate->password,
4786 req.key_len = key_len;
4789 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
4790 "Ignoring request", agent);
4795 if (!component->have_local_consent) {
4796 /* RFC 7675: return forbidden to all authenticated requests if we should
4797 * signal lost consent */
4798 nice_debug("Agent %p : returning FORBIDDEN on stream/component %u/%u "
4799 "for lost local consent", agent, stream->id, component->id);
4800 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4801 &req, STUN_ERROR_FORBIDDEN)) {
4802 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4803 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4804 agent->compatibility != NICE_COMPATIBILITY_OC2007) {
4805 agent_socket_send (nicesock, from, rbuf_len, (const gchar*) rbuf);
4811 rbuf_len = sizeof (rbuf);
4812 res = stun_usage_ice_conncheck_create_reply (&component->stun_agent, &req,
4813 &msg, rbuf, &rbuf_len, &sockaddr.storage, sizeof (sockaddr),
4814 &control, agent->tie_breaker,
4815 agent_to_ice_compatibility (agent));
4817 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4818 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4822 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
4823 priv_check_for_role_conflict (agent, control);
4825 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
4826 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
4827 /* case 1: valid incoming request, send a reply/error */
4828 bool use_candidate =
4829 stun_usage_ice_conncheck_use_candidate (&req);
4830 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
4832 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4833 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4834 agent->compatibility == NICE_COMPATIBILITY_OC2007)
4835 use_candidate = TRUE;
4837 if (stream->initial_binding_request_received != TRUE)
4838 agent_signal_initial_binding_request_received (agent, stream);
4840 if (remote_candidate == NULL) {
4841 nice_debug ("Agent %p : No matching remote candidate for incoming "
4842 "check -> peer-reflexive candidate.", agent);
4843 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
4844 agent, stream, component, priority, from, nicesock,
4846 remote_candidate2 ? remote_candidate2 : remote_candidate);
4847 if(remote_candidate && stream->remote_ufrag[0]) {
4848 if (local_candidate &&
4849 local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
4850 priv_conn_check_add_for_candidate_pair_matched (agent,
4851 stream->id, component, local_candidate, remote_candidate,
4852 NICE_CHECK_WAITING);
4854 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
4858 nice_component_add_valid_candidate (agent, component, remote_candidate);
4860 priv_reply_to_conn_check (agent, stream, component, local_candidate,
4861 remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate);
4863 if (stream->remote_ufrag[0] == 0) {
4864 /* case: We've got a valid binding request to a local candidate
4865 * but we do not yet know remote credentials.
4866 * As per sect 7.2 of ICE (ID-19), we send a reply
4867 * immediately but postpone all other processing until
4868 * we get information about the remote candidates */
4870 /* step: send a reply immediately but postpone other processing */
4871 priv_store_pending_check (agent, component, from, nicesock,
4872 username, username_len, priority, use_candidate);
4873 priv_print_conn_check_lists (agent, G_STRFUNC, ", icheck stored");
4876 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
4877 agent, strerror(errno));
4881 /* case 2: not a new request, might be a reply... */
4882 gboolean trans_found = FALSE;
4884 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
4886 /* step: let's try to match the response to an existing check context */
4887 if (trans_found != TRUE)
4888 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
4889 component, nicesock, from, local_candidate, remote_candidate, &req);
4891 /* step: let's try to match the response to an existing discovery */
4892 if (trans_found != TRUE)
4893 trans_found = priv_map_reply_to_discovery_request (agent, &req, from);
4895 /* step: let's try to match the response to an existing turn allocate */
4896 if (trans_found != TRUE)
4897 trans_found = priv_map_reply_to_relay_request (agent, &req);
4899 /* step: let's try to match the response to an existing turn refresh */
4900 if (trans_found != TRUE)
4901 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
4903 if (trans_found != TRUE)
4904 trans_found = priv_map_reply_to_relay_remove (agent, &req);
4906 /* step: let's try to match the response to an existing keepalive conncheck */
4907 if (trans_found != TRUE)
4908 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
4911 if (trans_found != TRUE)
4912 nice_debug ("Agent %p : Unable to match to an existing transaction, "
4913 "probably a keepalive.", agent);
4916 /* RENOMINATION attribute support */
4917 conn_check_handle_renomination(agent, stream, component, &req, remote_candidate, local_candidate);
4922 /* Remove all pointers to the given @sock from the connection checking process.
4923 * These are entirely NiceCandidates pointed to from various places. */
4925 conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
4929 gboolean pair_failed = FALSE;
4930 gboolean selected_pair_failed = FALSE;
4931 guint p_nominated = 0, p_count = 0;
4933 if (component->selected_pair.local &&
4934 component->selected_pair.local->sockptr == sock) {
4935 nice_debug ("Agent %p: Selected pair socket %p has been destroyed, "
4936 "declaring failed", agent, sock);
4937 selected_pair_failed = TRUE;
4938 if (component->state == NICE_COMPONENT_STATE_READY)
4939 agent_signal_component_state_change (agent,
4940 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
4941 else if (component->state == NICE_COMPONENT_STATE_CONNECTED)
4942 agent_signal_component_state_change (agent,
4943 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
4946 /* Prune from the candidate check pairs. */
4947 for (l = stream->conncheck_list; l != NULL;) {
4948 CandidateCheckPair *p = l->data;
4949 GSList *next = l->next;
4951 if (p->component_id != component->id) {
4956 if (selected_pair_failed && !p->retransmit && p->stun_transactions)
4957 p->retransmit = TRUE;
4959 if ((p->local != NULL && ((NiceCandidateImpl*) p->local)->sockptr == sock) ||
4960 (p->remote != NULL && ((NiceCandidateImpl*)p->remote)->sockptr == sock) ||
4961 (p->sockptr == sock)) {
4962 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
4964 if (component->selected_pair.local == ((NiceCandidateImpl *)p->local) &&
4965 component->selected_pair.remote == ((NiceCandidateImpl *)p->remote))
4966 selected_pair_failed = TRUE;
4967 candidate_check_pair_fail (stream, agent, p);
4968 candidate_check_pair_free (agent, p);
4969 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l);
4982 agent_signal_component_state_change (agent,
4983 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
4984 else if (p_nominated == 0) {
4985 if (component->state == NICE_COMPONENT_STATE_READY)
4986 agent_signal_component_state_change (agent,
4987 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
4988 else if (component->state == NICE_COMPONENT_STATE_CONNECTED)
4989 agent_signal_component_state_change (agent,
4990 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
4994 /* outside of the previous loop, because it may
4995 * remove pairs from the conncheck list
4998 conn_check_update_check_list_state_for_ready (agent, stream, component);