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 gboolean 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, use_cand %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),
220 icheck->use_candidate);
225 * Dump the conncheck lists of the agent
228 priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail)
234 if (!nice_debug_is_verbose ())
237 now = g_get_monotonic_time ();
239 #define PRIORITY_LEN 32
241 nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
242 agent, where, detail ? detail : "");
243 nice_debug ("Agent %p : *** agent nomination mode %s, %s",
244 agent, agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
245 "aggressive" : "regular",
246 agent->controlling_mode ? "controlling" : "controlled");
247 for (i = agent->streams; i ; i = i->next) {
248 NiceStream *stream = i->data;
249 for (j = 1; j <= stream->n_components; j++) {
250 NiceComponent *component;
251 for (k = stream->conncheck_list; k ; k = k->next) {
252 CandidateCheckPair *pair = k->data;
253 if (pair->component_id == j) {
254 gchar local_addr[INET6_ADDRSTRLEN];
255 gchar remote_addr[INET6_ADDRSTRLEN];
256 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
258 nice_address_to_string (&pair->local->addr, local_addr);
259 nice_address_to_string (&pair->remote->addr, remote_addr);
260 nice_candidate_pair_priority_to_string (pair->priority, priority);
262 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
263 "f=%s t=%s:%s sock=%s "
264 "%s:[%s]:%u > %s:[%s]:%u prio=%s/%08x state=%c%s%s%s%s",
265 agent, pair->stream_id, pair->component_id, pair,
267 nice_candidate_type_to_string (pair->local->type),
268 nice_candidate_type_to_string (pair->remote->type),
269 priv_socket_type_to_string (pair->sockptr->type),
270 nice_candidate_transport_to_string (pair->local->transport),
271 local_addr, nice_address_get_port (&pair->local->addr),
272 nice_candidate_transport_to_string (pair->remote->transport),
273 remote_addr, nice_address_get_port (&pair->remote->addr),
274 priority, pair->stun_priority,
275 priv_state_to_gchar (pair->state),
276 pair->valid ? "V" : "",
277 pair->nominated ? "N" : "",
278 pair->use_candidate_on_next_check ? "C" : "",
279 g_slist_find (agent->triggered_check_queue, pair) ? "T" : "");
281 for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) {
282 StunTransaction *stun = l->data;
283 nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
284 "stun#=%d timer=%d/%d %" G_GINT64_FORMAT "/%dms buf=%p %s",
285 agent, pair->stream_id, pair->component_id, pair, m,
286 stun->timer.retransmissions, stun->timer.max_retransmissions,
287 stun->timer.delay - priv_timer_remainder (stun->next_tick, now),
289 stun->message.buffer,
290 (m == 0 && pair->retransmit) ? "(R)" : "");
294 if (agent_find_component (agent, stream->id, j, NULL, &component))
295 print_component_incoming_checks (agent, stream, component);
300 /* Add the pair to the triggered checks list, if not already present
303 priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
307 if (agent->triggered_check_queue == NULL ||
308 g_slist_find (agent->triggered_check_queue, pair) == NULL) {
309 agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair);
310 priv_schedule_next (agent);
314 /* Remove the pair from the triggered checks list
317 priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
320 agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair);
323 /* Get the pair from the triggered checks list
325 static CandidateCheckPair *
326 priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
328 CandidateCheckPair *pair = NULL;
330 if (agent->triggered_check_queue) {
331 pair = (CandidateCheckPair *)agent->triggered_check_queue->data;
332 priv_remove_pair_from_triggered_check_queue (agent, pair);
338 * Finds the next connectivity check in WAITING state.
340 static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
344 /* note: list is sorted in priority order to first waiting check has
345 * the highest priority */
346 for (i = conn_check_list; i ; i = i->next) {
347 CandidateCheckPair *p = i->data;
348 if (p->state == NICE_CHECK_WAITING)
356 * Initiates a new connectivity check for a ICE candidate pair.
358 * @return TRUE on success, FALSE on error
361 priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
363 SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS);
364 if (conn_check_send (agent, pair)) {
366 NiceComponent *component;
368 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
369 &stream, &component)) {
370 nice_debug ("Could not find stream or component in conn_check_initiate");
371 SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED);
374 candidate_check_pair_fail (stream, agent, pair);
375 conn_check_update_check_list_state_for_ready (agent, stream, component);
382 * Unfreezes the next connectivity check in the list. Follows the
383 * algorithm defined in sect 6.1.2.6 (Computing Candidate Pair States)
384 * and sect 6.1.4.2 (Performing Connectivity Checks) of the ICE spec
387 * Note that this algorithm is slightly simplified compared to previous
388 * version of the spec (RFC5245), and this new version is now
391 * @return TRUE on success, and FALSE if no frozen candidates were found.
394 priv_conn_check_unfreeze_next (NiceAgent *agent)
397 GSList *foundation_list = NULL;
398 gboolean result = FALSE;
400 /* While a pair in state waiting exists, we do nothing */
401 for (i = agent->streams; i ; i = i->next) {
402 NiceStream *s = i->data;
403 for (j = s->conncheck_list; j ; j = j->next) {
404 CandidateCheckPair *p = j->data;
406 if (p->state == NICE_CHECK_WAITING)
411 /* When there are no more pairs in waiting state, we unfreeze some
412 * pairs, so that we get a single waiting pair per foundation.
414 for (i = agent->streams; i ; i = i->next) {
415 NiceStream *s = i->data;
416 for (j = s->conncheck_list; j ; j = j->next) {
417 CandidateCheckPair *p = j->data;
419 if (g_slist_find_custom (foundation_list, p->foundation,
420 (GCompareFunc)strcmp))
423 if (p->state == NICE_CHECK_FROZEN) {
424 nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
425 agent, p, p->stream_id, p->component_id, p->foundation);
426 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
427 foundation_list = g_slist_prepend (foundation_list, p->foundation);
432 g_slist_free (foundation_list);
434 /* We dump the conncheck list when something interesting happened, ie
435 * when we unfroze some pairs.
438 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
444 * Unfreezes the related connectivity check in the list after
445 * check 'success_check' has successfully completed.
447 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
449 * Note that this algorithm is slightly simplified compared to previous
450 * version of the spec (RFC5245)
452 * @param agent context
453 * @param pair a pair, whose connectivity check has just succeeded
457 conn_check_unfreeze_related (NiceAgent *agent, CandidateCheckPair *pair)
460 gboolean result = FALSE;
463 g_assert (pair->state == NICE_CHECK_SUCCEEDED);
465 for (i = agent->streams; i ; i = i->next) {
466 NiceStream *s = i->data;
467 for (j = s->conncheck_list; j ; j = j->next) {
468 CandidateCheckPair *p = j->data;
470 /* The states for all other Frozen candidates pairs in all
471 * checklists with the same foundation is set to waiting
473 if (p->state == NICE_CHECK_FROZEN &&
474 strncmp (p->foundation, pair->foundation,
475 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
476 nice_debug ("Agent %p : Unfreezing check %p "
477 "(after successful check %p).", agent, p, pair);
478 SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING);
483 /* We dump the conncheck list when something interesting happened, ie
484 * when we unfroze some pairs.
487 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
491 * Unfreezes this connectivity check if its foundation is the same than
492 * the foundation of an already succeeded pair.
494 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of ICE spec (RFC8445).
496 * @param agent context
497 * @param pair a pair, whose state is frozen
501 priv_conn_check_unfreeze_maybe (NiceAgent *agent, CandidateCheckPair *pair)
504 gboolean result = FALSE;
507 g_assert (pair->state == NICE_CHECK_FROZEN);
509 for (i = agent->streams; i ; i = i->next) {
510 NiceStream *s = i->data;
511 for (j = s->conncheck_list; j ; j = j->next) {
512 CandidateCheckPair *p = j->data;
514 if (p->state == NICE_CHECK_SUCCEEDED &&
515 strncmp (p->foundation, pair->foundation,
516 NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
517 nice_debug ("Agent %p : Unfreezing check %p "
518 "(after successful check %p).", agent, pair, p);
519 SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING);
524 /* We dump the conncheck list when something interesting happened, ie
525 * when we unfroze some pairs.
528 priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
532 conn_check_stun_transactions_count (NiceAgent *agent)
537 for (i = agent->streams; i ; i = i->next) {
538 NiceStream *s = i->data;
539 for (j = s->conncheck_list; j ; j = j->next) {
540 CandidateCheckPair *p = j->data;
542 if (p->stun_transactions)
543 count += g_slist_length (p->stun_transactions);
550 * Create a new STUN transaction and add it to the list
551 * of ongoing stun transactions of a pair.
553 * @pair the pair the new stun transaction should be added to.
554 * @return the created stun transaction.
556 static StunTransaction *
557 priv_add_stun_transaction (CandidateCheckPair *pair)
559 StunTransaction *stun = g_slice_new0 (StunTransaction);
560 pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun);
561 pair->retransmit = TRUE;
566 * Forget a STUN transaction.
568 * @data the stun transaction to be forgotten.
569 * @user_data the component contained the concerned stun agent.
572 priv_forget_stun_transaction (gpointer data, gpointer user_data)
574 StunTransaction *stun = data;
575 NiceComponent *component = user_data;
576 StunTransactionId id;
578 if (stun->message.buffer != NULL) {
579 stun_message_id (&stun->message, id);
580 stun_agent_forget_transaction (&component->stun_agent, id);
585 priv_free_stun_transaction (gpointer data)
587 g_slice_free (StunTransaction, data);
591 * Remove a STUN transaction from a pair, and forget it
592 * from the related component stun agent.
594 * @pair the pair the stun transaction should be removed from.
595 * @stun the stun transaction to be removed.
596 * @component the component containing the stun agent used to
597 * forget the stun transaction.
600 priv_remove_stun_transaction (CandidateCheckPair *pair,
601 StunTransaction *stun, NiceComponent *component)
603 priv_forget_stun_transaction (stun, component);
604 pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun);
605 priv_free_stun_transaction (stun);
606 if (pair->stun_transactions == NULL)
607 pair->retransmit = FALSE;
611 * Remove all STUN transactions from a pair, and forget them
612 * from the related component stun agent.
614 * @pair the pair the stun list should be cleared.
615 * @component the component containing the stun agent used to
616 * forget the stun transactions.
619 priv_free_all_stun_transactions (CandidateCheckPair *pair,
620 NiceComponent *component)
623 g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component);
624 g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction);
625 pair->stun_transactions = NULL;
626 pair->retransmit = FALSE;
630 candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
632 NiceComponent *component;
634 component = nice_stream_find_component_by_id (stream, p->component_id);
635 SET_PAIR_STATE (agent, p, NICE_CHECK_FAILED);
636 priv_free_all_stun_transactions (p, component);
638 /* Ensure related succeeded-discovered pairs change to state failed
639 * simultaneously, to avoid leaving dangling pointers if one is freeed
640 * while the other is still in the conncheck list. Such transition is
641 * very rare, and only occurs in regular nomination mode, when the
642 * network conditions change between the time the pair is initially
643 * discovered and the time it is rechecked with the use-candidate
646 if (p->discovered_pair != NULL) {
647 nice_debug ("Agent %p : related discovered pair %p of pair %p "
648 "will fail too.", agent, p->discovered_pair, p);
649 SET_PAIR_STATE (agent, p->discovered_pair, NICE_CHECK_FAILED);
654 * Helper function for connectivity check timer callback that
655 * runs through the stream specific part of the state machine.
657 * @param agent context pointer
658 * @param stream which stream (of the agent)
659 * @return will return TRUE if a new stun request has been sent
662 priv_conn_check_tick_stream (NiceAgent *agent, NiceStream *stream)
664 gboolean pair_failed = FALSE;
666 unsigned int timeout;
669 now = g_get_monotonic_time ();
671 /* step: process ongoing STUN transactions */
672 for (i = stream->conncheck_list; i ; i = i->next) {
673 CandidateCheckPair *p = i->data;
674 gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
675 NiceComponent *component;
676 guint index = 0, remaining = 0;
678 if (p->stun_transactions == NULL)
681 if (!agent_find_component (agent, p->stream_id, p->component_id,
685 j = p->stun_transactions;
687 StunTransaction *stun = j->data;
688 GSList *next = j->next;
690 if (now < stun->next_tick)
693 switch (stun_timer_refresh (&stun->timer)) {
694 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
695 timer_return_timeout:
696 priv_remove_stun_transaction (p, stun, component);
698 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
699 /* case: retransmission stopped, due to the nomination of
700 * a pair with a higher priority than this in-progress pair,
701 * ICE spec, sect 8.1.2 "Updating States", item 2.2
703 if (!p->retransmit || index > 0)
704 goto timer_return_timeout;
706 /* case: not ready, so schedule a new timeout */
707 timeout = stun_timer_remainder (&stun->timer);
709 nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
710 "(timer=%d/%d %d/%dms).",
712 stun->timer.retransmissions, stun->timer.max_retransmissions,
713 stun->timer.delay - timeout, stun->timer.delay);
715 agent_socket_send (p->sockptr, &p->remote->addr,
716 stun_message_length (&stun->message),
717 (gchar *)stun->buffer);
719 /* note: convert from milli to microseconds for g_time_val_add() */
720 stun->next_tick = now + timeout * 1000;
723 case STUN_USAGE_TIMER_RETURN_SUCCESS:
724 timeout = stun_timer_remainder (&stun->timer);
725 /* note: convert from milli to microseconds for g_time_val_add() */
726 stun->next_tick = now + timeout * 1000;
730 g_assert_not_reached();
737 if (remaining == 0) {
738 nice_address_to_string (&p->local->addr, tmpbuf1);
739 nice_address_to_string (&p->remote->addr, tmpbuf2);
740 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
742 nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
743 tmpbuf1, nice_address_get_port (&p->local->addr),
744 tmpbuf2, nice_address_get_port (&p->remote->addr));
745 candidate_check_pair_fail (stream, agent, p);
748 /* perform a check if a transition state from connected to
749 * ready can be performed. This may happen here, when the last
750 * in-progress pair has expired its retransmission count
751 * in priv_conn_check_tick_stream(), which is a condition to
752 * make the transition connected to ready.
754 conn_check_update_check_list_state_for_ready (agent, stream, component);
759 priv_print_conn_check_lists (agent, G_STRFUNC, ", retransmission failed");
765 priv_conn_check_ordinary_check (NiceAgent *agent, NiceStream *stream)
767 CandidateCheckPair *pair;
768 gboolean stun_sent = FALSE;
770 /* step: perform an ordinary check, sec 6.1.4.2 point 3. (Performing
771 * Connectivity Checks) of ICE spec (RFC8445)
772 * note: This code is executed when the triggered checks list is
773 * empty, and when no STUN message has been sent (pacing constraint)
775 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
777 /* step: there is no candidate in waiting state, try to unfreeze
778 * some pairs and retry, sect 6.1.4.2 point 2. (Performing Connectivity
779 * Checks) of ICE spec (RFC8445)
781 priv_conn_check_unfreeze_next (agent);
782 pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
786 stun_sent = priv_conn_check_initiate (agent, pair);
787 priv_print_conn_check_lists (agent, G_STRFUNC,
788 ", initiated an ordinary connection check");
794 priv_conn_check_triggered_check (NiceAgent *agent, NiceStream *stream)
796 CandidateCheckPair *pair;
797 gboolean stun_sent = FALSE;
799 /* step: perform a test from the triggered checks list,
800 * sect 6.1.4.2 point 1. (Performing Connectivity Checks) of ICE
803 pair = priv_get_pair_from_triggered_check_queue (agent);
806 stun_sent = priv_conn_check_initiate (agent, pair);
807 priv_print_conn_check_lists (agent, G_STRFUNC,
808 ", initiated a connection check from triggered check list");
815 priv_conn_check_tick_stream_nominate (NiceAgent *agent, NiceStream *stream)
817 gboolean keep_timer_going = FALSE;
818 /* s_xxx counters are stream-wide */
819 guint s_inprogress = 0;
820 guint s_succeeded = 0;
821 guint s_discovered = 0;
822 guint s_nominated = 0;
823 guint s_waiting_for_nomination = 0;
827 CandidateCheckPair *other_stream_pair = NULL;
830 /* Search for a nominated pair (or selected to be nominated pair)
831 * from another stream.
833 for (i = agent->streams; i ; i = i->next) {
834 NiceStream *s = i->data;
835 if (s->id == stream->id)
837 for (j = s->conncheck_list; j ; j = j->next) {
838 CandidateCheckPair *p = j->data;
839 if (p->nominated || (p->use_candidate_on_next_check &&
840 p->state != NICE_CHECK_FAILED)) {
841 other_stream_pair = p;
845 if (other_stream_pair)
849 /* we compute some stream-wide counter values */
850 for (i = stream->conncheck_list; i ; i = i->next) {
851 CandidateCheckPair *p = i->data;
852 if (p->state == NICE_CHECK_FROZEN)
854 else if (p->state == NICE_CHECK_IN_PROGRESS)
856 else if (p->state == NICE_CHECK_WAITING)
858 else if (p->state == NICE_CHECK_SUCCEEDED)
860 else if (p->state == NICE_CHECK_DISCOVERED)
865 if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
868 else if ((p->state == NICE_CHECK_SUCCEEDED ||
869 p->state == NICE_CHECK_DISCOVERED) && !p->nominated)
870 s_waiting_for_nomination++;
873 /* note: keep the timer going as long as there is work to be done */
875 keep_timer_going = TRUE;
877 if (s_nominated < stream->n_components &&
878 s_waiting_for_nomination) {
879 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
880 if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
881 agent->controlling_mode) {
882 #define NICE_MIN_NUMBER_OF_VALID_PAIRS 2
883 /* ICE 8.1.1.1 Regular nomination
884 * we choose to nominate the valid pair of a component if
885 * - there is no pair left frozen, waiting or in-progress, or
886 * - if there are at least two valid pairs, or
887 * - if there is at least one valid pair of type HOST-HOST
889 * This is the "stopping criterion" described in 8.1.1.1, and is
890 * a "local optimization" between accumulating more valid pairs,
891 * and limiting the time spent waiting for in-progress connections
892 * checks until they finally fail.
894 for (i = stream->components; i; i = i->next) {
895 NiceComponent *component = i->data;
896 CandidateCheckPair *other_component_pair = NULL;
897 CandidateCheckPair *this_component_pair = NULL;
898 NiceCandidate *lcand1 = NULL;
899 NiceCandidate *rcand1 = NULL;
900 NiceCandidate *lcand2, *rcand2;
901 gboolean already_done = FALSE;
902 gboolean found_other_component_pair = FALSE;
903 gboolean found_other_stream_pair = FALSE;
904 gboolean first_nomination = FALSE;
905 gboolean stopping_criterion;
906 /* p_xxx counters are component-wide */
910 guint p_inprogress = 0;
911 guint p_host_host_valid = 0;
913 /* we compute some component-wide counter values */
914 for (j = stream->conncheck_list; j ; j = j->next) {
915 CandidateCheckPair *p = j->data;
916 if (p->component_id == component->id) {
917 /* verify that the choice of the pair to be nominated
918 * has not already been done
920 if (p->use_candidate_on_next_check)
922 if (p->state == NICE_CHECK_FROZEN)
924 else if (p->state == NICE_CHECK_WAITING)
926 else if (p->state == NICE_CHECK_IN_PROGRESS)
931 p->local->type == NICE_CANDIDATE_TYPE_HOST &&
932 p->remote->type == NICE_CANDIDATE_TYPE_HOST)
940 /* Search for a nominated pair (or selected to be nominated pair)
941 * from another component of this stream.
943 for (j = stream->conncheck_list; j ; j = j->next) {
944 CandidateCheckPair *p = j->data;
945 if (p->component_id == component->id)
947 if (p->nominated || (p->use_candidate_on_next_check &&
948 p->state != NICE_CHECK_FAILED)) {
949 other_component_pair = p;
954 if (other_stream_pair == NULL && other_component_pair == NULL)
955 first_nomination = TRUE;
957 /* We choose a pair to be nominated in the list of valid
960 * this pair will be the one with the highest priority,
961 * when we don't have other nominated pairs in other
962 * components and in other streams
964 * this pair will be a pair compatible with another nominated
965 * pair from another component if we found one.
967 * else this pair will be a pair compatible with another
968 * nominated pair from another stream if we found one.
971 for (j = stream->conncheck_list; j ; j = j->next) {
972 CandidateCheckPair *p = j->data;
973 /* note: highest priority item selected (list always sorted) */
974 if (p->component_id == component->id &&
976 !p->use_candidate_on_next_check &&
978 /* According a ICE spec, sect 8.1.1.1. "Regular
979 * Nomination", we enqueue the check that produced this
980 * valid pair. When this pair has been discovered, we want
981 * to test its parent pair instead.
983 if (p->succeeded_pair != NULL) {
984 g_assert (p->state == NICE_CHECK_DISCOVERED);
985 p = p->succeeded_pair;
987 g_assert (p->state == NICE_CHECK_SUCCEEDED);
989 if (this_component_pair == NULL)
990 /* highest priority pair */
991 this_component_pair = p;
996 if (first_nomination)
997 /* use the highest priority pair */
1000 if (other_component_pair) {
1001 lcand2 = other_component_pair->local;
1002 rcand2 = other_component_pair->remote;
1004 if (other_component_pair &&
1005 lcand1->transport == lcand2->transport &&
1006 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1007 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1008 /* else continue the research with lower priority
1009 * pairs, compatible with a nominated pair of
1012 this_component_pair = p;
1013 found_other_component_pair = TRUE;
1017 if (other_stream_pair) {
1018 lcand2 = other_stream_pair->local;
1019 rcand2 = other_stream_pair->remote;
1021 if (other_stream_pair &&
1022 other_component_pair == NULL &&
1023 lcand1->transport == lcand2->transport &&
1024 nice_address_equal_no_port (&lcand1->addr, &lcand2->addr) &&
1025 nice_address_equal_no_port (&rcand1->addr, &rcand2->addr)) {
1026 /* else continue the research with lower priority
1027 * pairs, compatible with a nominated pair of
1030 this_component_pair = p;
1031 found_other_stream_pair = TRUE;
1037 /* No valid pair for this component */
1038 if (this_component_pair == NULL)
1041 /* The stopping criterion tries to select a set of pairs of
1042 * the same kind (transport/type) for all components of a
1043 * stream, and for all streams, when possible (see last
1046 * When no stream has nominated a pair yet, we apply the
1047 * following criterion :
1048 * - stop if we have a valid host-host pair
1049 * - or stop if we have at least "some* (2 in the current
1050 * implementation) valid pairs, and select the best one
1051 * - or stop if the conncheck cannot evolve more
1053 * Else when the stream has a nominated pair in another
1054 * component we apply this criterion:
1055 * - stop if we have a valid pair of the same kind than this
1056 * other nominated pair.
1057 * - or stop if the conncheck cannot evolve more
1059 * Else when another stream has a nominated pair we apply the
1060 * following criterion:
1061 * - stop if we have a valid pair of the same kind than the
1062 * other nominated pair.
1063 * - or stop if the conncheck cannot evolve more
1065 * When no further evolution of the conncheck is possible, we
1066 * prefer to select the best valid pair we have, *even* if it
1067 * is not compatible with the transport of another stream of
1068 * component. We think it's still a better choice than marking
1069 * this component 'failed'.
1071 stopping_criterion = FALSE;
1072 if (first_nomination && p_host_host_valid > 0) {
1073 stopping_criterion = TRUE;
1074 nice_debug ("Agent %p : stopping criterion: "
1075 "valid host-host pair", agent);
1076 } else if (first_nomination &&
1077 p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS) {
1078 stopping_criterion = TRUE;
1079 nice_debug ("Agent %p : stopping criterion: "
1080 "*some* valid pairs", agent);
1081 } else if (found_other_component_pair) {
1082 stopping_criterion = TRUE;
1083 nice_debug ("Agent %p : stopping criterion: "
1084 "matching pair in another component", agent);
1085 } else if (found_other_stream_pair) {
1086 stopping_criterion = TRUE;
1087 nice_debug ("Agent %p : stopping criterion: "
1088 "matching pair in another stream", agent);
1089 } else if (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0) {
1090 stopping_criterion = TRUE;
1091 nice_debug ("Agent %p : stopping criterion: "
1092 "no more pairs to check", agent);
1095 if (!stopping_criterion)
1098 /* when the stopping criterion is reached, we add the
1099 * selected pair for this component to the triggered checks
1102 nice_debug ("Agent %p : restarting check of %s:%s pair %p with "
1103 "USE-CANDIDATE attrib (regular nomination) for "
1104 "stream %d component %d", agent,
1105 nice_candidate_transport_to_string (
1106 this_component_pair->local->transport),
1107 nice_candidate_transport_to_string (
1108 this_component_pair->remote->transport),
1109 this_component_pair, stream->id, component->id);
1110 this_component_pair->use_candidate_on_next_check = TRUE;
1111 priv_add_pair_to_triggered_check_queue (agent, this_component_pair);
1112 keep_timer_going = TRUE;
1115 } else if (agent->controlling_mode) {
1116 for (i = stream->components; i; i = i->next) {
1117 NiceComponent *component = i->data;
1119 for (j = stream->conncheck_list; j ; j = j->next) {
1120 CandidateCheckPair *p = j->data;
1121 /* note: highest priority item selected (list always sorted) */
1122 if (p->component_id == component->id &&
1123 (p->state == NICE_CHECK_SUCCEEDED ||
1124 p->state == NICE_CHECK_DISCOVERED)) {
1125 nice_debug ("Agent %p : restarting check of pair %p as the "
1126 "nominated pair.", agent, p);
1127 p->nominated = TRUE;
1128 conn_check_update_selected_pair (agent, component, p);
1129 priv_add_pair_to_triggered_check_queue (agent, p);
1130 keep_timer_going = TRUE;
1131 break; /* move to the next component */
1137 if (stream->tick_counter++ % 50 == 0)
1138 nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, "
1139 "%u in-progress, %u waiting, %u succeeded, %u discovered, "
1140 "%u nominated, %u waiting-for-nom, %u valid",
1141 agent, stream->id, stream->tick_counter,
1142 s_frozen, s_inprogress, s_waiting, s_succeeded, s_discovered,
1143 s_nominated, s_waiting_for_nomination, s_valid);
1145 return keep_timer_going;
1150 conn_check_stop (NiceAgent *agent)
1152 if (agent->conncheck_timer_source == NULL)
1155 g_source_destroy (agent->conncheck_timer_source);
1156 g_source_unref (agent->conncheck_timer_source);
1157 agent->conncheck_timer_source = NULL;
1158 agent->conncheck_ongoing_idle_delay = 0;
1163 * Timer callback that handles initiating and managing connectivity
1164 * checks (paced by the Ta timer).
1166 * This function is designed for the g_timeout_add() interface.
1168 * @return will return FALSE when no more pending timers.
1170 static gboolean priv_conn_check_tick_agent_locked (NiceAgent *agent,
1173 gboolean keep_timer_going = FALSE;
1174 gboolean stun_sent = FALSE;
1177 /* step: process triggered checks
1178 * these steps are ordered by priority, since a single stun request
1179 * is sent per callback, we process the important steps first.
1181 * perform a single stun request per timer callback,
1182 * to respect stun pacing
1184 for (i = agent->streams; i && !stun_sent; i = i->next) {
1185 NiceStream *stream = i->data;
1187 stun_sent = priv_conn_check_triggered_check (agent, stream);
1190 /* step: process ongoing STUN transactions */
1191 for (i = agent->streams; i && !stun_sent; i = i->next) {
1192 NiceStream *stream = i->data;
1194 stun_sent = priv_conn_check_tick_stream (agent, stream);
1197 /* step: process ordinary checks */
1198 for (i = agent->streams; i && !stun_sent; i = i->next) {
1199 NiceStream *stream = i->data;
1201 stun_sent = priv_conn_check_ordinary_check (agent, stream);
1205 keep_timer_going = TRUE;
1207 /* step: try to nominate a pair
1209 for (i = agent->streams; i; i = i->next) {
1210 NiceStream *stream = i->data;
1212 if (priv_conn_check_tick_stream_nominate (agent, stream))
1213 keep_timer_going = TRUE;
1216 /* note: we provide a grace period before declaring a component as
1217 * failed. Components marked connected, and then ready follow another
1218 * code path, and are not concerned by this grace period.
1220 if (!keep_timer_going && agent->conncheck_ongoing_idle_delay == 0)
1221 nice_debug ("Agent %p : waiting %d msecs before checking "
1222 "for failed components.", agent, agent->idle_timeout);
1224 if (keep_timer_going)
1225 agent->conncheck_ongoing_idle_delay = 0;
1227 agent->conncheck_ongoing_idle_delay += agent->timer_ta;
1229 /* step: stop timer if no work left */
1230 if (!keep_timer_going &&
1231 agent->conncheck_ongoing_idle_delay >= agent->idle_timeout) {
1232 nice_debug ("Agent %p : checking for failed components now.", agent);
1233 for (i = agent->streams; i; i = i->next) {
1234 NiceStream *stream = i->data;
1235 priv_update_check_list_failed_components (agent, stream);
1238 nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
1239 priv_print_conn_check_lists (agent, G_STRFUNC,
1240 ", conncheck timer stopped");
1242 /* Stopping the timer so destroy the source.. this will allow
1243 the timer to be reset if we get a set_remote_candidates after this
1245 conn_check_stop (agent);
1247 /* XXX: what to signal, is all processing now really done? */
1248 nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
1255 static gboolean priv_conn_remote_consent_tick_agent_locked (
1256 NiceAgent *agent, gpointer pointer)
1258 CandidatePair *pair = (CandidatePair *) pointer;
1259 guint64 consent_timeout = 0;
1262 if (pair->remote_consent.tick_source) {
1263 g_source_destroy (pair->remote_consent.tick_source);
1264 g_source_unref (pair->remote_consent.tick_source);
1266 pair->remote_consent.tick_source = NULL;
1268 if (agent->consent_freshness) {
1269 consent_timeout = NICE_AGENT_TIMER_CONSENT_TIMEOUT * 1000;
1271 consent_timeout = NICE_AGENT_TIMER_KEEPALIVE_TIMEOUT* 1000;
1274 now = g_get_monotonic_time();
1275 if (now - pair->remote_consent.last_received > consent_timeout) {
1276 guint64 time_since = now - pair->remote_consent.last_received;
1277 pair->remote_consent.have = FALSE;
1278 nice_debug ("Agent %p : pair %p consent for stream/component %u/%u timed "
1279 "out! -> FAILED. Last consent received: %" G_GUINT64_FORMAT ".%" G_GUINT64_FORMAT "s ago",
1280 agent, pair, pair->keepalive.stream_id, pair->keepalive.component_id,
1281 time_since / G_USEC_PER_SEC, time_since % G_USEC_PER_SEC);
1282 agent_signal_component_state_change (agent, pair->keepalive.stream_id,
1283 pair->keepalive.component_id, NICE_COMPONENT_STATE_FAILED);
1285 guint64 delay = (consent_timeout - (now - pair->remote_consent.last_received)) / 1000;
1286 nice_debug ("Agent %p : pair %p rechecking consent in %" G_GUINT64_FORMAT ".%03" G_GUINT64_FORMAT "s",
1287 agent, pair, delay / 1000, delay % 1000);
1288 agent_timeout_add_with_context (agent,
1289 &pair->remote_consent.tick_source,
1290 "Pair remote consent", delay,
1291 priv_conn_remote_consent_tick_agent_locked, pair);
1297 static guint32 peer_reflexive_candidate_priority (NiceAgent *agent,
1298 NiceCandidate *local_candidate)
1300 NiceCandidate *candidate_priority =
1301 nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
1304 candidate_priority->transport = local_candidate->transport;
1305 candidate_priority->component_id = local_candidate->component_id;
1306 candidate_priority->base_addr = local_candidate->addr;
1307 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
1308 priority = nice_candidate_jingle_priority (candidate_priority);
1309 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
1310 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
1311 priority = nice_candidate_msn_priority (candidate_priority);
1312 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1313 priority = nice_candidate_ms_ice_priority (candidate_priority,
1314 agent->reliable, FALSE);
1316 priority = nice_candidate_ice_priority (candidate_priority,
1317 agent->reliable, FALSE);
1319 nice_candidate_free (candidate_priority);
1324 /* Returns the priority of a local candidate of type peer-reflexive that
1325 * would be learned as a consequence of a check from this local
1326 * candidate. See RFC 5245, section 7.1.2.1. "PRIORITY and USE-CANDIDATE".
1327 * RFC 5245 is more explanatory than RFC 8445 on this detail.
1329 * Apply to local candidates of type host only, because candidates of type
1330 * relay are supposed to have a public IP address, that wont generate
1331 * a peer-reflexive address. Server-reflexive candidates are not
1332 * concerned too, because no STUN request is sent with a local candidate
1335 static guint32 stun_request_priority (NiceAgent *agent,
1336 NiceCandidate *local_candidate)
1338 if (local_candidate->type == NICE_CANDIDATE_TYPE_HOST)
1339 return peer_reflexive_candidate_priority (agent, local_candidate);
1341 return local_candidate->priority;
1344 static void ms_ice2_legacy_conncheck_send(StunMessage *msg, NiceSocket *sock,
1345 const NiceAddress *remote_addr)
1347 uint32_t *fingerprint_attr;
1348 uint32_t fingerprint_orig;
1349 uint16_t fingerprint_len;
1352 if (msg->agent->ms_ice2_send_legacy_connchecks == FALSE) {
1356 fingerprint_attr = (uint32_t *)stun_message_find (msg,
1357 STUN_ATTRIBUTE_FINGERPRINT, &fingerprint_len);
1359 if (fingerprint_attr == NULL) {
1360 nice_debug ("FINGERPRINT not found.");
1364 if (fingerprint_len != sizeof (fingerprint_orig)) {
1365 nice_debug ("Unexpected FINGERPRINT length %u.", fingerprint_len);
1369 memcpy (&fingerprint_orig, fingerprint_attr, sizeof (fingerprint_orig));
1371 buffer_len = stun_message_length (msg);
1373 *fingerprint_attr = stun_fingerprint (msg->buffer, buffer_len, TRUE);
1375 agent_socket_send (sock, remote_addr, buffer_len, (gchar *)msg->buffer);
1377 memcpy (fingerprint_attr, &fingerprint_orig, sizeof (fingerprint_orig));
1381 * Timer callback that handles initiating and managing connectivity
1382 * checks (paced by the Ta timer).
1384 * This function is designed for the g_timeout_add() interface.
1386 * @return will return FALSE when no more pending timers.
1388 static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
1394 guint64 min_next_tick;
1395 guint64 next_timer_tick;
1397 now = g_get_monotonic_time ();
1398 if (agent->consent_freshness) {
1399 min_next_tick = now + 1000 * NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL;
1401 min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1404 /* case 1: session established and media flowing
1405 * (ref ICE sect 11 "Keepalives" RFC-8445)
1406 * TODO: without RFC 7675 (consent freshness), keepalives should be sent
1407 * only when no packet has been sent on that pair in the last Tr seconds,
1408 * and not unconditionally.
1410 for (i = agent->streams; i; i = i->next) {
1412 NiceStream *stream = i->data;
1413 for (j = stream->components; j; j = j->next) {
1414 NiceComponent *component = j->data;
1415 if (component->selected_pair.local != NULL) {
1416 CandidatePair *p = &component->selected_pair;
1418 /* Disable keepalive checks on TCP candidates unless explicitly enabled */
1419 if (p->local->c.transport != NICE_CANDIDATE_TRANSPORT_UDP &&
1420 !NICE_AGENT_DO_KEEPALIVE_CONNCHECKS (agent))
1423 if (p->keepalive.next_tick) {
1424 if (p->keepalive.next_tick < min_next_tick)
1425 min_next_tick = p->keepalive.next_tick;
1426 if (now < p->keepalive.next_tick)
1430 if (NICE_AGENT_DO_KEEPALIVE_CONNCHECKS (agent)) {
1431 uint8_t uname[NICE_STREAM_MAX_UNAME];
1433 priv_create_username (agent, agent_find_stream (agent, stream->id),
1434 component->id, (NiceCandidate *) p->remote,
1435 (NiceCandidate *) p->local, uname, sizeof (uname), FALSE);
1436 uint8_t *password = NULL;
1437 size_t password_len = priv_get_password (agent,
1438 agent_find_stream (agent, stream->id),
1439 (NiceCandidate *) p->remote, &password);
1440 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1441 StunMessage stun_message;
1443 if (uname_len > 0) {
1444 if (nice_debug_is_enabled ()) {
1445 gchar tmpbuf[INET6_ADDRSTRLEN];
1446 nice_address_to_string (&p->remote->c.addr, tmpbuf);
1447 nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
1448 "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
1449 "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
1450 agent, tmpbuf, nice_address_get_port (&p->remote->c.addr),
1451 component->id, (int) uname_len, uname, uname_len,
1452 (int) password_len, password, password_len,
1456 buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
1457 &stun_message, stun_buffer, sizeof(stun_buffer),
1458 uname, uname_len, password, password_len,
1459 agent->controlling_mode, agent->controlling_mode,
1463 agent_to_ice_compatibility (agent));
1465 nice_debug ("Agent %p: conncheck created %zd - %p",
1466 agent, buf_len, stun_message.buffer);
1469 /* random range over 0.8 -> 1.2 as specified in RFC7675 */
1470 double modifier = g_random_double() * 0.4 + 0.8;
1471 guint64 delay = 1000 * MAX((guint64) ((NICE_AGENT_TIMER_CONSENT_DEFAULT) * modifier),
1472 NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL);
1474 p->keepalive.stream_id = stream->id;
1475 p->keepalive.component_id = component->id;
1476 p->keepalive.next_tick = now + delay;
1478 if (p->remote_consent.have) {
1479 if (p->remote_consent.last_received == 0) {
1480 p->remote_consent.last_received = g_get_monotonic_time();
1483 priv_conn_remote_consent_tick_agent_locked (agent, p);
1486 agent->media_after_tick = FALSE;
1488 /* send the conncheck */
1489 agent_socket_send (p->local->sockptr, &p->remote->c.addr,
1490 buf_len, (gchar *) stun_buffer);
1492 next_timer_tick = now + agent->timer_ta * 1000;
1499 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1500 StunMessage stun_message;
1502 buf_len = stun_usage_bind_keepalive (&component->stun_agent,
1503 &stun_message, stun_buffer, sizeof(stun_buffer));
1506 agent_socket_send (p->local->sockptr, &p->remote->c.addr, buf_len,
1507 (gchar *) stun_buffer);
1509 p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1511 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
1512 ms_ice2_legacy_conncheck_send (&stun_message,
1513 p->local->sockptr, &p->remote->c.addr);
1516 if (nice_debug_is_enabled ()) {
1517 gchar tmpbuf[INET6_ADDRSTRLEN];
1518 nice_address_to_string (&p->local->c.base_addr, tmpbuf);
1519 nice_debug ("Agent %p : resending STUN to keep the "
1520 "selected base address %s:%u alive in s%d/c%d.", agent,
1521 tmpbuf, nice_address_get_port (&p->local->c.base_addr),
1522 stream->id, component->id);
1525 next_timer_tick = now + agent->timer_ta * 1000;
1535 /* case 2: connectivity establishment ongoing
1536 * (ref ICE sect 5.1.1.4 "Keeping Candidates Alive" RFC-8445)
1538 for (i = agent->streams; i; i = i->next) {
1539 NiceStream *stream = i->data;
1540 for (j = stream->components; j; j = j->next) {
1541 NiceComponent *component = j->data;
1542 if (component->state < NICE_COMPONENT_STATE_CONNECTED &&
1543 agent->stun_server_ip) {
1544 NiceAddress stun_server;
1545 if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
1546 StunAgent stun_agent;
1547 uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
1548 StunMessage stun_message;
1549 size_t buffer_len = 0;
1551 nice_address_set_port (&stun_server, agent->stun_server_port);
1553 nice_agent_init_stun_agent (agent, &stun_agent);
1555 buffer_len = stun_usage_bind_create (&stun_agent,
1556 &stun_message, stun_buffer, sizeof(stun_buffer));
1558 for (k = component->local_candidates; k; k = k->next) {
1559 NiceCandidateImpl *candidate = (NiceCandidateImpl *) k->data;
1560 if (candidate->c.type == NICE_CANDIDATE_TYPE_HOST &&
1561 candidate->c.transport == NICE_CANDIDATE_TRANSPORT_UDP &&
1562 nice_address_ip_version (&candidate->c.addr) ==
1563 nice_address_ip_version (&stun_server)) {
1565 if (candidate->keepalive_next_tick) {
1566 if (candidate->keepalive_next_tick < min_next_tick)
1567 min_next_tick = candidate->keepalive_next_tick;
1568 if (now < candidate->keepalive_next_tick)
1572 /* send the conncheck */
1573 if (nice_debug_is_enabled ()) {
1574 gchar tmpbuf[INET6_ADDRSTRLEN];
1575 nice_address_to_string (&candidate->c.addr, tmpbuf);
1576 nice_debug ("Agent %p : resending STUN to keep the local "
1577 "candidate %s:%u alive in s%d/c%d.", agent,
1578 tmpbuf, nice_address_get_port (&candidate->c.addr),
1579 stream->id, component->id);
1581 agent_socket_send (candidate->sockptr, &stun_server,
1582 buffer_len, (gchar *)stun_buffer);
1583 candidate->keepalive_next_tick = now +
1584 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
1585 next_timer_tick = now + agent->timer_ta * 1000;
1594 next_timer_tick = min_next_tick;
1598 nice_debug ("Agent %p : %s: stopping keepalive timer", agent, G_STRFUNC);
1602 if (agent->keepalive_timer_source) {
1603 g_source_destroy (agent->keepalive_timer_source);
1604 g_source_unref (agent->keepalive_timer_source);
1605 agent->keepalive_timer_source = NULL;
1607 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1608 "Connectivity keepalive timeout", (next_timer_tick - now)/ 1000,
1609 priv_conn_keepalive_tick_agent_locked, NULL);
1613 static gboolean priv_conn_keepalive_tick_agent_locked (NiceAgent *agent,
1618 ret = priv_conn_keepalive_tick_unlocked (agent);
1620 if (agent->keepalive_timer_source) {
1621 g_source_destroy (agent->keepalive_timer_source);
1622 g_source_unref (agent->keepalive_timer_source);
1623 agent->keepalive_timer_source = NULL;
1631 static gboolean priv_turn_allocate_refresh_retransmissions_tick_agent_locked (
1632 NiceAgent *agent, gpointer pointer)
1634 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1636 g_source_destroy (cand->tick_source);
1637 g_source_unref (cand->tick_source);
1638 cand->tick_source = NULL;
1640 switch (stun_timer_refresh (&cand->timer)) {
1641 case STUN_USAGE_TIMER_RETURN_TIMEOUT:
1644 StunTransactionId id;
1646 stun_message_id (&cand->stun_message, id);
1647 stun_agent_forget_transaction (&cand->stun_agent, id);
1649 refresh_free (agent, cand);
1652 case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
1654 agent_socket_send (cand->nicesock, &cand->server,
1655 stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer);
1658 case STUN_USAGE_TIMER_RETURN_SUCCESS:
1659 agent_timeout_add_with_context (agent, &cand->tick_source,
1660 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1661 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1664 /* Nothing to do. */
1668 return G_SOURCE_REMOVE;
1671 static void priv_turn_allocate_refresh_tick_unlocked (NiceAgent *agent,
1672 CandidateRefresh *cand)
1678 size_t buffer_len = 0;
1679 StunUsageTurnCompatibility turn_compat =
1680 agent_to_turn_compatibility (agent);
1682 username = (uint8_t *)cand->candidate->turn->username;
1683 username_len = (size_t) strlen (cand->candidate->turn->username);
1684 password = (uint8_t *)cand->candidate->turn->password;
1685 password_len = (size_t) strlen (cand->candidate->turn->password);
1687 if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN ||
1688 turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) {
1689 username = cand->candidate->turn->decoded_username;
1690 password = cand->candidate->turn->decoded_password;
1691 username_len = cand->candidate->turn->decoded_username_len;
1692 password_len = cand->candidate->turn->decoded_password_len;
1695 buffer_len = stun_usage_turn_create_refresh (&cand->stun_agent,
1696 &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer),
1697 cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg,
1698 cand->disposing ? 0 : -1,
1699 username, username_len,
1700 password, password_len,
1703 nice_debug ("Agent %p : Sending allocate Refresh %zd", agent,
1706 if (cand->tick_source != NULL) {
1707 g_source_destroy (cand->tick_source);
1708 g_source_unref (cand->tick_source);
1709 cand->tick_source = NULL;
1712 if (buffer_len > 0) {
1713 stun_timer_start (&cand->timer,
1714 agent->stun_initial_timeout,
1715 agent->stun_max_retransmissions);
1717 /* send the refresh */
1718 agent_socket_send (cand->nicesock, &cand->server,
1719 buffer_len, (gchar *)cand->stun_buffer);
1721 agent_timeout_add_with_context (agent, &cand->tick_source,
1722 "Candidate TURN refresh", stun_timer_remainder (&cand->timer),
1723 priv_turn_allocate_refresh_retransmissions_tick_agent_locked, cand);
1730 * Timer callback that handles refreshing TURN allocations
1732 * This function is designed for the g_timeout_add() interface.
1734 * @return will return FALSE when no more pending timers.
1736 static gboolean priv_turn_allocate_refresh_tick_agent_locked (NiceAgent *agent,
1739 CandidateRefresh *cand = (CandidateRefresh *) pointer;
1741 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
1743 return G_SOURCE_REMOVE;
1748 * Initiates the next pending connectivity check.
1750 static void priv_schedule_next (NiceAgent *agent)
1752 if (agent->discovery_unsched_items > 0)
1753 nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
1755 /* step: schedule timer if not running yet */
1756 if (agent->conncheck_timer_source == NULL) {
1757 agent_timeout_add_with_context (agent, &agent->conncheck_timer_source,
1758 "Connectivity check schedule", agent->timer_ta,
1759 priv_conn_check_tick_agent_locked, NULL);
1762 /* step: also start the keepalive timer */
1763 if (agent->keepalive_timer_source == NULL) {
1764 agent_timeout_add_with_context (agent, &agent->keepalive_timer_source,
1765 "Connectivity keepalive timeout", agent->timer_ta,
1766 priv_conn_keepalive_tick_agent_locked, NULL);
1771 * Compares two connectivity check items. Checkpairs are sorted
1772 * in descending priority order, with highest priority item at
1773 * the start of the list.
1775 gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
1777 if (a->priority > b->priority)
1779 else if (a->priority < b->priority)
1784 /* Find a transport compatible with a given socket.
1786 * Returns TRUE when a matching transport can be guessed from
1787 * the type of the socket in an unambiguous way.
1790 nice_socket_has_compatible_transport (NiceSocket *socket,
1791 NiceCandidateTransport *transport)
1793 gboolean found = TRUE;
1796 g_assert (transport);
1798 switch (socket->type) {
1799 case NICE_SOCKET_TYPE_TCP_BSD:
1800 if (nice_tcp_bsd_socket_get_passive_parent (socket))
1801 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1803 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1805 case NICE_SOCKET_TYPE_TCP_PASSIVE:
1806 *transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
1808 case NICE_SOCKET_TYPE_TCP_ACTIVE:
1809 *transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
1811 case NICE_SOCKET_TYPE_UDP_BSD:
1812 *transport = NICE_CANDIDATE_TRANSPORT_UDP;
1821 /* Test if a local socket and a local candidate are compatible. This
1822 * function does supplementary tests when the address and port are not
1823 * sufficient to give a unique candidate. We try to avoid comparing
1824 * directly the sockptr value, when possible, to rely on objective
1825 * properties of the candidate and the socket instead, and we also
1826 * choose to ignore the conncheck list for the same reason.
1829 local_candidate_and_socket_compatible (NiceAgent *agent,
1830 NiceCandidate *lcand, NiceSocket *socket)
1832 gboolean ret = TRUE;
1833 NiceCandidateTransport transport;
1834 NiceCandidateImpl *lc = (NiceCandidateImpl *) lcand;
1839 if (nice_socket_has_compatible_transport (socket, &transport)) {
1840 ret = (lcand->transport == transport);
1841 /* tcp-active discovered peer-reflexive local candidate, where
1842 * socket is the tcp connect related socket */
1843 if (ret && transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1844 nice_address_get_port (&lcand->addr) > 0)
1845 ret = (lc->sockptr == socket);
1846 } else if (socket->type == NICE_SOCKET_TYPE_UDP_TURN)
1847 /* Socket of type udp-turn will match a unique local candidate
1848 * by its sockptr value. An an udp-turn socket doesn't carry enough
1849 * information when base socket is udp-turn-over-tcp to disambiguate
1850 * between a tcp-act and a tcp-pass local candidate.
1852 ret = (lc->sockptr == socket);
1857 /* Test if a local socket and a remote candidate are compatible.
1858 * This function is very close to its local candidate counterpart,
1859 * the difference is that we also use information from the local
1860 * candidate we may have identified previously. This is needed
1861 * to disambiguate the transport of the candidate with a socket
1866 remote_candidate_and_socket_compatible (NiceAgent *agent,
1867 NiceCandidate *lcand, NiceCandidate *rcand, NiceSocket *socket)
1869 gboolean ret = TRUE;
1870 NiceCandidateTransport transport;
1875 if (nice_socket_has_compatible_transport (socket, &transport))
1876 ret = (conn_check_match_transport (rcand->transport) == transport);
1878 /* This supplementary test with the local candidate is needed with
1879 * socket of type udp-turn, the type doesn't allow to disambiguate
1880 * between a tcp-pass and a tcp-act remote candidate
1883 ret = (conn_check_match_transport (lcand->transport) == rcand->transport);
1889 conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream,
1890 NiceComponent *component)
1894 NiceCandidate *lcand = NULL, *rcand = NULL;
1896 nice_debug ("Agent %p : conn_check_remote_candidates_set %u %u",
1897 agent, stream->id, component->id);
1899 if (stream->remote_ufrag[0] == 0)
1902 if (component->incoming_checks.head)
1903 nice_debug ("Agent %p : credentials have been set, "
1904 "we can process incoming checks", agent);
1906 for (i = component->incoming_checks.head; i;) {
1907 IncomingCheck *icheck = i->data;
1908 GList *i_next = i->next;
1910 nice_debug ("Agent %p : replaying icheck=%p (sock=%p)",
1911 agent, icheck, icheck->local_socket);
1913 /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
1914 * be handled separately */
1915 for (j = component->local_candidates; j; j = j->next) {
1916 NiceCandidate *cand = j->data;
1919 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
1922 addr = &cand->base_addr;
1924 if (nice_address_equal (&icheck->local_socket->addr, addr) &&
1925 local_candidate_and_socket_compatible (agent, cand,
1926 icheck->local_socket)) {
1932 if (lcand == NULL) {
1933 for (j = component->local_candidates; j; j = j->next) {
1934 NiceCandidate *cand = j->data;
1935 NiceAddress *addr = &cand->base_addr;
1937 /* tcp-active (not peer-reflexive discovered) local candidate, where
1938 * socket is the tcp connect related socket */
1939 if (nice_address_equal_no_port (&icheck->local_socket->addr, addr) &&
1940 nice_address_get_port (&cand->addr) == 0 &&
1941 cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE &&
1942 local_candidate_and_socket_compatible (agent, cand,
1943 icheck->local_socket)) {
1950 g_assert (lcand != NULL);
1952 for (j = component->remote_candidates; j; j = j->next) {
1953 NiceCandidate *cand = j->data;
1954 if (nice_address_equal (&cand->addr, &icheck->from) &&
1955 remote_candidate_and_socket_compatible (agent, lcand, cand,
1956 icheck->local_socket)) {
1962 if (lcand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
1963 CandidateCheckPair *pair = NULL;
1965 for (j = stream->conncheck_list; j; j = j->next) {
1966 CandidateCheckPair *p = j->data;
1967 if (lcand == p->local && rcand == p->remote) {
1973 priv_conn_check_add_for_candidate_pair_matched (agent,
1974 stream->id, component, lcand, rcand, NICE_CHECK_WAITING);
1977 priv_schedule_triggered_check (agent, stream, component,
1978 icheck->local_socket, rcand);
1979 if (icheck->use_candidate)
1980 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
1982 if (icheck->username)
1983 g_free (icheck->username);
1984 g_slice_free (IncomingCheck, icheck);
1985 g_queue_delete_link (&component->incoming_checks, i);
1991 * Handle any processing steps for connectivity checks after
1992 * remote credentials have been set. This function handles
1993 * the special case where answerer has sent us connectivity
1994 * checks before the answer (containing credentials information),
1995 * reaches us. The special case is documented in RFC 5245 sect 7.2.
1998 void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream)
2002 for (j = stream->components; j ; j = j->next) {
2003 NiceComponent *component = j->data;
2005 conn_check_remote_candidates_set(agent, stream, component);
2010 * Enforces the upper limit for connectivity checks by dropping
2011 * lower-priority pairs as described RFC 8445 section 6.1.2.5. See also
2012 * conn_check_add_for_candidate().
2013 * Returns TRUE if the pair in argument is one of the deleted pairs.
2015 static gboolean priv_limit_conn_check_list_size (NiceAgent *agent,
2016 NiceStream *stream, CandidateCheckPair *pair)
2019 guint cancelled = 0;
2020 gboolean deleted = FALSE;
2021 GSList *item = stream->conncheck_list;
2024 CandidateCheckPair *p = item->data;
2025 GSList *next = item->next;
2028 /* We remove lower-priority pairs, but only the ones that can be
2029 * safely discarded without breaking an ongoing conncheck process.
2030 * This only includes pairs that are in the frozen state (those
2031 * initially added when remote candidates are received) or in failed
2032 * state. Pairs in any other state play a role in the conncheck, and
2033 * there removal may lead to a failing conncheck that would succeed
2036 * We also remove failed pairs from the list unconditionally.
2038 if ((valid > agent->max_conn_checks && p->state == NICE_CHECK_FROZEN) ||
2039 p->state == NICE_CHECK_FAILED) {
2042 nice_debug ("Agent %p : pair %p removed.", agent, p);
2043 candidate_check_pair_free (agent, p);
2044 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list,
2052 nice_debug ("Agent %p : Pruned %d pairs. "
2053 "Conncheck list has %d elements left. "
2054 "Maximum connchecks allowed : %d", agent, cancelled,
2055 valid - cancelled, agent->max_conn_checks);
2061 * Changes the selected pair for the component if 'pair'
2062 * has higher priority than the currently selected pair. See
2063 * RFC 8445 sect 8.1.1. "Nominating Pairs"
2066 conn_check_update_selected_pair (NiceAgent *agent, NiceComponent *component,
2067 CandidateCheckPair *pair)
2069 CandidatePair cpair = { 0, };
2071 g_assert (component);
2073 /* pair is expected to have the nominated flag */
2074 g_assert (pair->nominated);
2075 if (pair->priority > component->selected_pair.priority) {
2076 gchar priority[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2077 nice_candidate_pair_priority_to_string (pair->priority, priority);
2078 nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
2079 "(prio:%s).", agent, component->id,
2080 pair->local->foundation, pair->remote->foundation, priority);
2082 cpair.local = (NiceCandidateImpl *) pair->local;
2083 cpair.remote = (NiceCandidateImpl *) pair->remote;
2084 cpair.priority = pair->priority;
2085 cpair.stun_priority = pair->stun_priority;
2086 cpair.remote_consent.have = TRUE;
2088 nice_component_update_selected_pair (agent, component, &cpair);
2090 priv_conn_keepalive_tick_unlocked (agent);
2092 agent_signal_new_selected_pair (agent, pair->stream_id, component->id,
2093 pair->local, pair->remote);
2098 * Updates the check list state.
2100 * Implements parts of the algorithm described in
2101 * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any
2102 * component, all checks have been completed and have
2103 * failed to produce a nominated pair, mark that component's
2104 * state to NICE_CHECK_FAILED.
2106 * Sends a component state changesignal via 'agent'.
2108 static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream)
2113 /* note: emitting a signal might cause the client
2114 * to remove the stream, thus the component count
2115 * must be fetched before entering the loop*/
2116 guint c, components = stream->n_components;
2118 if (stream->conncheck_list == NULL)
2121 for (i = agent->discovery_list; i; i = i->next) {
2122 CandidateDiscovery *d = i->data;
2124 /* There is still discovery ogoing for this stream,
2125 * so don't fail any of it's candidates.
2127 if (d->stream_id == stream->id && !d->done)
2130 if (agent->discovery_list != NULL)
2133 /* note: iterate the conncheck list for each component separately */
2134 for (c = 0; c < components; c++) {
2135 NiceComponent *component = NULL;
2136 if (!agent_find_component (agent, stream->id, c+1, NULL, &component))
2141 for (i = stream->conncheck_list; i; i = i->next) {
2142 CandidateCheckPair *p = i->data;
2144 g_assert (p->stream_id == stream->id);
2146 if (p->component_id == (c + 1)) {
2149 if (p->state != NICE_CHECK_FAILED &&
2150 p->state != NICE_CHECK_SUCCEEDED &&
2151 p->state != NICE_CHECK_DISCOVERED)
2156 /* note: all pairs are either failed or succeeded, and the component
2157 * has not produced a nominated pair.
2158 * Set the component to FAILED only if it actually had remote candidates
2160 if (completed && nominated == 0 &&
2161 component != NULL && component->remote_candidates != NULL)
2162 agent_signal_component_state_change (agent,
2164 (c + 1), /* component-id */
2165 NICE_COMPONENT_STATE_FAILED);
2170 * Updates the check list state for a stream component.
2172 * Implements the algorithm described in ICE sect 8.1.2
2173 * "Updating States" (ID-19) as it applies to checks of
2174 * a certain component. If there are any nominated pairs,
2175 * ICE processing may be concluded, and component state is
2178 * Sends a component state changesignal via 'agent'.
2180 void conn_check_update_check_list_state_for_ready (NiceAgent *agent,
2181 NiceStream *stream, NiceComponent *component)
2184 guint valid = 0, nominated = 0;
2186 g_assert (component);
2188 /* step: search for at least one nominated pair */
2189 for (i = stream->conncheck_list; i; i = i->next) {
2190 CandidateCheckPair *p = i->data;
2191 if (p->component_id == component->id) {
2194 if (p->nominated == TRUE) {
2201 if (nominated > 0) {
2202 /* Only go to READY if no checks are left in progress. If there are
2203 * any that are kept, then this function will be called again when the
2204 * conncheck tick timer finishes them all */
2205 if (priv_prune_pending_checks (agent, stream, component) == 0) {
2206 /* Continue through the states to give client code a nice
2207 * logical progression. See http://phabricator.freedesktop.org/D218 for
2209 if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
2210 component->state == NICE_COMPONENT_STATE_FAILED)
2211 agent_signal_component_state_change (agent, stream->id, component->id,
2212 NICE_COMPONENT_STATE_CONNECTING);
2213 if (component->state < NICE_COMPONENT_STATE_CONNECTED)
2214 agent_signal_component_state_change (agent, stream->id, component->id,
2215 NICE_COMPONENT_STATE_CONNECTED);
2216 agent_signal_component_state_change (agent, stream->id,
2217 component->id, NICE_COMPONENT_STATE_READY);
2220 nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id);
2224 * The remote party has signalled that the candidate pair
2225 * described by 'component' and 'remotecand' is nominated
2227 * return TRUE if at least one matching pair is found and got nominated (or marked to be nominated on response_arrival).
2229 static gboolean priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand)
2232 gboolean res = FALSE;
2234 g_assert (component);
2236 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2237 agent->controlling_mode)
2240 if (nice_debug_is_verbose()) {
2241 gchar remote_str[INET6_ADDRSTRLEN];
2242 gchar local_str[INET6_ADDRSTRLEN];
2243 nice_address_to_string(&remotecand->addr, remote_str);
2244 nice_address_to_string(&localcand->addr, local_str);
2245 nice_debug ("Agent %p : *** priv_mark_pair_nominated: local candidate %p [%s]:%u, remote candidate %p [%s]:%u",
2246 agent, localcand, local_str, nice_address_get_port (&localcand->addr),
2247 remotecand, remote_str, nice_address_get_port (&remotecand->addr));
2250 /* step: search for at least one nominated pair */
2251 for (i = stream->conncheck_list; i; i = i->next) {
2252 CandidateCheckPair *pair = i->data;
2254 if (nice_debug_is_verbose()) {
2255 gchar remote_str[INET6_ADDRSTRLEN];
2256 gchar local_str[INET6_ADDRSTRLEN];
2257 nice_address_to_string(&pair->remote->addr, remote_str);
2258 nice_address_to_string(&pair->local->addr, local_str);
2259 nice_debug ("Agent %p : *** priv_mark_pair_nominated: conncheck pair %p, state %u, valid %u, nom %u, disc p %p: local candidate %p [%s]:%u, remote candidate %p [%s]:%u",
2260 agent, pair, pair->state, pair->valid, pair->nominated, pair->discovered_pair,
2261 pair->local, local_str, nice_address_get_port (&pair->local->addr),
2262 pair->remote, remote_str, nice_address_get_port (&pair->remote->addr));
2265 if (pair->local == localcand && pair->remote == remotecand) {
2266 /* ICE, 7.2.1.5. Updating the Nominated Flag */
2267 /* note: TCP candidates typically produce peer reflexive
2268 * candidate, generating a "discovered" pair that can be
2271 if (pair->state == NICE_CHECK_SUCCEEDED &&
2272 pair->discovered_pair != NULL) {
2273 nice_debug ("Agent %p : priv_mark_pair_nominated: conncheck pair %p - replace with discovered pair %p",
2274 agent, pair, pair->discovered_pair);
2275 pair = pair->discovered_pair;
2276 g_assert (pair->state == NICE_CHECK_DISCOVERED);
2279 /* If the received Binding request triggered a new check to be
2280 * enqueued in the triggered-check queue (Section 7.3.1.4), once
2281 * the check is sent and if it generates a successful response,
2282 * and generates a valid pair, the agent sets the nominated flag
2283 * of the pair to true
2285 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2286 if (g_slist_find (agent->triggered_check_queue, pair) ||
2287 pair->state == NICE_CHECK_IN_PROGRESS) {
2289 /* This pair is not always in the triggered check list, for
2290 * example if it is in-progress with a lower priority than an
2291 * already nominated pair. Is that case, it is not rescheduled
2292 * for a connection check, see function
2293 * priv_schedule_triggered_check(), case NICE_CHECK_IN_PROGRESS.
2295 pair->mark_nominated_on_response_arrival = TRUE;
2297 nice_debug ("Agent %p : pair %p (%s) is %s, "
2298 "will be nominated on response receipt.",
2299 agent, pair, pair->foundation,
2300 priv_state_to_string (pair->state));
2305 !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2306 nice_debug ("Agent %p : marking pair %p (%s) as nominated",
2307 agent, pair, pair->foundation);
2308 pair->nominated = TRUE;
2312 /* Do not step down to CONNECTED if we're already at state READY*/
2313 if (component->state == NICE_COMPONENT_STATE_FAILED)
2314 agent_signal_component_state_change (agent,
2315 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
2316 conn_check_update_selected_pair (agent, component, pair);
2317 if (component->state == NICE_COMPONENT_STATE_CONNECTING)
2318 /* step: notify the client of a new component state (must be done
2319 * before the possible check list state update step */
2320 agent_signal_component_state_change (agent,
2321 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
2324 if (pair->nominated) {
2325 conn_check_update_check_list_state_for_ready (agent, stream, component);
2334 * Creates a new connectivity check pair and adds it to
2335 * the agent's list of checks.
2337 static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
2338 guint stream_id, NiceComponent *component, NiceCandidateImpl *local,
2339 NiceCandidateImpl *remote, NiceCheckState initial_state)
2342 CandidateCheckPair *pair;
2345 g_assert (local != NULL);
2346 g_assert (remote != NULL);
2348 priority = agent_candidate_pair_priority (agent, (NiceCandidate *) local,
2349 (NiceCandidate *) remote);
2351 if (component->selected_pair.priority &&
2352 priority < component->selected_pair.priority) {
2353 gchar prio1[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2354 gchar prio2[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
2356 nice_candidate_pair_priority_to_string (priority, prio1);
2357 nice_candidate_pair_priority_to_string (component->selected_pair.priority,
2359 nice_debug ("Agent %p : do not create a pair that would have a priority "
2360 "%s lower than selected pair priority %s.", agent, prio1, prio2);
2364 stream = agent_find_stream (agent, stream_id);
2365 pair = g_slice_new0 (CandidateCheckPair);
2367 pair->stream_id = stream_id;
2368 pair->component_id = component->id;
2369 pair->local = (NiceCandidate *) local;
2370 pair->remote = (NiceCandidate *) remote;
2371 /* note: we use the remote sockptr only in the case
2374 if (local->c.transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
2375 remote->c.type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2376 pair->sockptr = remote->sockptr;
2378 pair->sockptr = local->sockptr;
2379 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
2380 local->c.foundation, remote->c.foundation);
2382 pair->priority = agent_candidate_pair_priority (agent,
2383 (NiceCandidate *) local, (NiceCandidate *) remote);
2384 nice_debug ("Agent %p : creating a new pair", agent);
2385 SET_PAIR_STATE (agent, pair, initial_state);
2387 gchar tmpbuf1[INET6_ADDRSTRLEN];
2388 gchar tmpbuf2[INET6_ADDRSTRLEN];
2389 nice_address_to_string (&pair->local->addr, tmpbuf1);
2390 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2391 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
2392 tmpbuf1, nice_address_get_port (&pair->local->addr),
2393 tmpbuf2, nice_address_get_port (&pair->remote->addr));
2395 pair->stun_priority = stun_request_priority (agent, (NiceCandidate *) local);
2397 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
2398 (GCompareFunc)conn_check_compare);
2400 priv_schedule_next (agent);
2402 nice_debug ("Agent %p : added a new pair %p with foundation '%s' and "
2403 "transport %s:%s to stream %u component %u",
2404 agent, pair, pair->foundation,
2405 nice_candidate_transport_to_string (pair->local->transport),
2406 nice_candidate_transport_to_string (pair->remote->transport),
2407 stream_id, component->id);
2409 if (initial_state == NICE_CHECK_FROZEN)
2410 priv_conn_check_unfreeze_maybe (agent, pair);
2412 /* implement the hard upper limit for number of
2413 checks (see sect 5.7.3 ICE ID-19): */
2414 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
2415 if (priv_limit_conn_check_list_size (agent, stream, pair))
2422 NiceCandidateTransport
2423 conn_check_match_transport (NiceCandidateTransport transport)
2425 switch (transport) {
2426 case NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
2427 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
2429 case NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
2430 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
2432 case NICE_CANDIDATE_TRANSPORT_TCP_SO:
2433 case NICE_CANDIDATE_TRANSPORT_UDP:
2440 static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
2441 NiceAgent *agent, guint stream_id, NiceComponent *component,
2442 NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state)
2444 CandidateCheckPair *pair;
2446 pair = priv_add_new_check_pair (agent, stream_id, component,
2447 (NiceCandidateImpl *) local, (NiceCandidateImpl *) remote, initial_state);
2449 if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
2450 component->state == NICE_COMPONENT_STATE_READY) {
2451 agent_signal_component_state_change (agent,
2454 NICE_COMPONENT_STATE_CONNECTED);
2456 agent_signal_component_state_change (agent,
2459 NICE_COMPONENT_STATE_CONNECTING);
2467 _is_linklocal_to_non_linklocal (NiceAddress *laddr, NiceAddress *raddr)
2469 return nice_address_is_linklocal (laddr) != nice_address_is_linklocal (raddr);
2472 gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
2473 guint stream_id, NiceComponent *component, NiceCandidate *local,
2474 NiceCandidate *remote)
2476 gboolean ret = FALSE;
2478 g_assert (local != NULL);
2479 g_assert (remote != NULL);
2481 /* note: do not create pairs where the local candidate is a srv-reflexive
2482 * or peer-reflexive (ICE 6.1.2.4. "Pruning the pairs" RFC 8445)
2484 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
2485 agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2486 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2487 (local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
2488 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)) {
2492 /* note: do not create pairs where local candidate has TCP passive transport
2493 * (ice-tcp-13 6.2. "Forming the Check Lists") */
2494 if (local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
2498 /* note: match pairs only if transport and address family are the same
2499 * and make sure link-local stay link-local */
2500 if (local->transport == conn_check_match_transport (remote->transport) &&
2501 local->addr.s.addr.sa_family == remote->addr.s.addr.sa_family &&
2502 !_is_linklocal_to_non_linklocal (&local->addr, &remote->addr)) {
2503 if (priv_conn_check_add_for_candidate_pair_matched (agent, stream_id,
2504 component, local, remote, NICE_CHECK_FROZEN))
2512 * Forms new candidate pairs by matching the new remote candidate
2513 * 'remote_cand' with all existing local candidates of 'component'.
2514 * Implements the logic described in ICE sect 5.7.1. "Forming Candidate
2517 * @param agent context
2518 * @param component pointer to the component
2519 * @param remote remote candidate to match with
2521 * @return number of checks added, negative on fatal errors
2523 int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote)
2529 g_assert (remote != NULL);
2531 /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates",
2532 * the agent does not pair this candidate with any local candidates.
2534 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2535 remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2540 for (i = component->local_candidates; i ; i = i->next) {
2541 NiceCandidate *local = i->data;
2543 if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED)
2546 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2557 * Forms new candidate pairs by matching the new local candidate
2558 * 'local_cand' with all existing remote candidates of 'component'.
2560 * @param agent context
2561 * @param component pointer to the component
2562 * @param local local candidate to match with
2564 * @return number of checks added, negative on fatal errors
2566 int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local)
2572 g_assert (local != NULL);
2575 * note: according to 7.1.3.2.1 "Discovering Peer Reflexive
2576 * Candidates", the peer reflexive candidate is not paired
2577 * with other remote candidates
2580 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
2581 local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
2586 for (i = component->remote_candidates; i ; i = i->next) {
2588 NiceCandidate *remote = i->data;
2589 ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
2600 * Frees the CandidateCheckPair structure pointer to
2601 * by 'user data'. Compatible with GDestroyNotify.
2603 static void candidate_check_pair_free (NiceAgent *agent,
2604 CandidateCheckPair *pair)
2606 priv_remove_pair_from_triggered_check_queue (agent, pair);
2607 priv_free_all_stun_transactions (pair, NULL);
2608 g_slice_free (CandidateCheckPair, pair);
2612 * Frees all resources of all connectivity checks.
2614 void conn_check_free (NiceAgent *agent)
2617 for (i = agent->streams; i; i = i->next) {
2618 NiceStream *stream = i->data;
2620 if (stream->conncheck_list) {
2623 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent,
2625 for (item = stream->conncheck_list; item; item = item->next)
2626 candidate_check_pair_free (agent, item->data);
2627 g_slist_free (stream->conncheck_list);
2628 stream->conncheck_list = NULL;
2632 conn_check_stop (agent);
2636 * Prunes the list of connectivity checks for items related
2637 * to stream 'stream_id'.
2639 * @return TRUE on success, FALSE on a fatal error
2641 void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream)
2644 gboolean keep_going = FALSE;
2646 if (stream->conncheck_list) {
2649 nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, stream);
2651 for (item = stream->conncheck_list; item; item = item->next)
2652 candidate_check_pair_free (agent, item->data);
2653 g_slist_free (stream->conncheck_list);
2654 stream->conncheck_list = NULL;
2657 for (i = agent->streams; i; i = i->next) {
2658 NiceStream *s = i->data;
2659 if (s->conncheck_list) {
2666 conn_check_stop (agent);
2670 * Fills 'dest' with a username string for use in an outbound connectivity
2671 * checks. No more than 'dest_len' characters (including terminating
2672 * NULL) is ever written to the 'dest'.
2675 size_t priv_gen_username (NiceAgent *agent, guint component_id,
2676 gchar *remote, gchar *local, uint8_t *dest, guint dest_len)
2679 gsize remote_len = strlen (remote);
2680 gsize local_len = strlen (local);
2682 if (remote_len > 0 && local_len > 0) {
2683 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
2684 dest_len >= remote_len + local_len + 1) {
2685 memcpy (dest, remote, remote_len);
2687 memcpy (dest + len, ":", 1);
2689 memcpy (dest + len, local, local_len);
2691 } else if ((agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
2692 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
2693 dest_len >= remote_len + local_len + 4 ) {
2694 memcpy (dest, remote, remote_len);
2696 memcpy (dest + len, ":", 1);
2698 memcpy (dest + len, local, local_len);
2701 memset (dest + len, 0, 4 - (len % 4));
2702 len += 4 - (len % 4);
2704 } else if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
2705 dest_len >= remote_len + local_len) {
2706 memcpy (dest, remote, remote_len);
2708 memcpy (dest + len, local, local_len);
2710 } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2711 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
2712 gchar component_str[10];
2713 guchar *local_decoded = NULL;
2714 guchar *remote_decoded = NULL;
2715 gsize local_decoded_len;
2716 gsize remote_decoded_len;
2720 g_snprintf (component_str, sizeof(component_str), "%d", component_id);
2721 local_decoded = g_base64_decode (local, &local_decoded_len);
2722 remote_decoded = g_base64_decode (remote, &remote_decoded_len);
2724 total_len = remote_decoded_len + local_decoded_len + 3 + 2*strlen (component_str);
2725 padding = 4 - (total_len % 4);
2727 if (dest_len >= total_len + padding) {
2728 guchar pad_char[1] = {0};
2731 memcpy (dest, remote_decoded, remote_decoded_len);
2732 len += remote_decoded_len;
2733 memcpy (dest + len, ":", 1);
2735 memcpy (dest + len, component_str, strlen (component_str));
2736 len += strlen (component_str);
2738 memcpy (dest + len, ":", 1);
2741 memcpy (dest + len, local_decoded, local_decoded_len);
2742 len += local_decoded_len;
2743 memcpy (dest + len, ":", 1);
2745 memcpy (dest + len, component_str, strlen (component_str));;
2746 len += strlen (component_str);
2748 for (i = 0; i < padding; i++) {
2749 memcpy (dest + len, pad_char, 1);
2755 g_free (local_decoded);
2756 g_free (remote_decoded);
2764 * Fills 'dest' with a username string for use in an outbound connectivity
2765 * checks. No more than 'dest_len' characters (including terminating
2766 * NULL) is ever written to the 'dest'.
2769 size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
2770 guint component_id, NiceCandidate *remote, NiceCandidate *local,
2771 uint8_t *dest, guint dest_len, gboolean inbound)
2773 gchar *local_username = NULL;
2774 gchar *remote_username = NULL;
2777 if (remote && remote->username) {
2778 remote_username = remote->username;
2781 if (local && local->username) {
2782 local_username = local->username;
2786 if (remote_username == NULL) {
2787 remote_username = stream->remote_ufrag;
2789 if (local_username == NULL) {
2790 local_username = stream->local_ufrag;
2794 if (local_username && remote_username) {
2796 return priv_gen_username (agent, component_id,
2797 local_username, remote_username, dest, dest_len);
2799 return priv_gen_username (agent, component_id,
2800 remote_username, local_username, dest, dest_len);
2808 * Returns a password string for use in an outbound connectivity
2812 size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
2813 NiceCandidate *remote, uint8_t **password)
2815 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
2818 if (remote && remote->password) {
2819 *password = (uint8_t *)remote->password;
2820 return strlen (remote->password);
2824 *password = (uint8_t *)stream->remote_password;
2825 return strlen (stream->remote_password);
2831 /* Implement the computation specific in RFC 8445 section 14 */
2833 static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
2836 guint waiting_and_in_progress = 0;
2837 unsigned int rto = 0;
2839 /* we can compute precisely the number of pairs in-progress or
2840 * waiting for all streams, instead of limiting the value to one
2841 * stream, and multiplying it by the number of active streams.
2842 * Since RFC8445, this number of waiting and in-progress pairs
2843 * if maxed by the number of different foundations in the conncheck
2846 for (i = agent->streams; i ; i = i->next) {
2847 NiceStream *s = i->data;
2848 for (j = s->conncheck_list; j ; j = j->next) {
2849 CandidateCheckPair *p = j->data;
2850 if (p->state == NICE_CHECK_IN_PROGRESS ||
2851 p->state == NICE_CHECK_WAITING)
2852 waiting_and_in_progress++;
2856 rto = agent->timer_ta * waiting_and_in_progress;
2858 nice_debug ("Agent %p : timer set to %dms, "
2859 "waiting+in_progress=%d", agent, MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT),
2860 waiting_and_in_progress);
2861 return MAX (rto, STUN_TIMER_DEFAULT_TIMEOUT);
2865 * Sends a connectivity check over candidate pair 'pair'.
2867 * @return zero on success, non-zero on error
2869 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
2872 /* note: following information is supplied:
2873 * - username (for USERNAME attribute)
2874 * - password (for MESSAGE-INTEGRITY)
2875 * - priority (for PRIORITY)
2876 * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
2877 * - USE-CANDIDATE (if sent by the controlling agent)
2880 uint8_t uname[NICE_STREAM_MAX_UNAME];
2882 NiceComponent *component;
2884 uint8_t *password = NULL;
2885 uint8_t *free_password = NULL;
2887 bool controlling = agent->controlling_mode;
2888 /* XXX: add API to support different nomination modes: */
2889 bool cand_use = controlling;
2891 unsigned int timeout;
2892 StunTransaction *stun;
2894 if (!agent_find_component (agent, pair->stream_id, pair->component_id,
2895 &stream, &component))
2898 uname_len = priv_create_username (agent, stream, pair->component_id,
2899 pair->remote, pair->local, uname, sizeof (uname), FALSE);
2900 password_len = priv_get_password (agent, stream, pair->remote, &password);
2902 if (password != NULL &&
2903 (agent->compatibility == NICE_COMPATIBILITY_MSN ||
2904 agent->compatibility == NICE_COMPATIBILITY_OC2007)) {
2905 free_password = password =
2906 g_base64_decode ((gchar *) password, &password_len);
2909 if (nice_debug_is_enabled ()) {
2910 gchar tmpbuf1[INET6_ADDRSTRLEN];
2911 gchar tmpbuf2[INET6_ADDRSTRLEN];
2912 nice_address_to_string (&pair->local->addr, tmpbuf1);
2913 nice_address_to_string (&pair->remote->addr, tmpbuf2);
2914 nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, "
2915 "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
2916 "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%08x, %s.", agent,
2917 tmpbuf1, nice_address_get_port (&pair->local->addr),
2918 tmpbuf2, nice_address_get_port (&pair->remote->addr),
2919 pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1,
2920 pair, pair->component_id,
2921 (unsigned long long)agent->tie_breaker,
2922 (int) uname_len, uname, uname_len,
2923 (int) password_len, password, password_len,
2924 pair->stun_priority,
2925 controlling ? "controlling" : "controlled");
2928 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
2929 switch (agent->nomination_mode) {
2930 case NICE_NOMINATION_MODE_REGULAR:
2931 /* We are doing regular nomination, so we set the use-candidate
2932 * attrib, when the controlling agent decided which valid pair to
2933 * resend with this flag in priv_conn_check_tick_stream()
2935 cand_use = pair->use_candidate_on_next_check;
2936 nice_debug ("Agent %p : %s: set cand_use=%d "
2937 "(regular nomination).", agent, G_STRFUNC, cand_use);
2939 case NICE_NOMINATION_MODE_AGGRESSIVE:
2940 /* We are doing aggressive nomination, we set the use-candidate
2941 * attrib in every check we send, when we are the controlling
2942 * agent, RFC 5245, 8.1.1.2
2944 cand_use = controlling;
2945 nice_debug ("Agent %p : %s: set cand_use=%d "
2946 "(aggressive nomination).", agent, G_STRFUNC, cand_use);
2949 /* Nothing to do. */
2952 } else if (cand_use)
2953 pair->nominated = controlling;
2955 if (uname_len == 0) {
2956 nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent);
2957 g_free (free_password);
2961 stun = priv_add_stun_transaction (pair);
2963 buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
2964 &stun->message, stun->buffer, sizeof(stun->buffer),
2965 uname, uname_len, password, password_len,
2966 cand_use, controlling, pair->stun_priority,
2968 pair->local->foundation,
2969 agent_to_ice_compatibility (agent));
2971 nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len,
2972 stun->message.buffer);
2974 g_free (free_password);
2976 if (buffer_len == 0) {
2977 nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent);
2978 priv_remove_stun_transaction (pair, stun, component);
2982 if (nice_socket_is_reliable(pair->sockptr)) {
2983 timeout = agent->stun_reliable_timeout;
2984 stun_timer_start_reliable(&stun->timer, timeout);
2986 timeout = priv_compute_conncheck_timer (agent, stream);
2987 stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions);
2990 stun->next_tick = g_get_monotonic_time () + timeout * 1000;
2992 /* TCP-ACTIVE candidate must create a new socket before sending
2993 * by connecting to the peer. The new socket is stored in the candidate
2994 * check pair, until we discover a new local peer reflexive */
2995 if (pair->sockptr->fileno == NULL &&
2996 pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN &&
2997 pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
2998 NiceStream *stream2 = NULL;
2999 NiceComponent *component2 = NULL;
3000 NiceSocket *new_socket;
3002 if (agent_find_component (agent, pair->stream_id, pair->component_id,
3003 &stream2, &component2)) {
3004 new_socket = nice_tcp_active_socket_connect (pair->sockptr,
3005 &pair->remote->addr);
3007 nice_debug ("Agent %p: add to tcp-act socket %p a new "
3008 "tcp connect socket %p on pair %p in s/c %d/%d",
3009 agent, pair->sockptr, new_socket, pair, stream->id, component->id);
3010 pair->sockptr = new_socket;
3011 _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
3013 nice_socket_set_writable_callback (pair->sockptr, _tcp_sock_is_writable,
3016 nice_component_attach_socket (component2, new_socket);
3018 priv_remove_stun_transaction (pair, stun, component);
3023 /* send the conncheck */
3024 if (agent_socket_send (pair->sockptr, &pair->remote->addr,
3025 buffer_len, (gchar *)stun->buffer) < 0) {
3026 priv_remove_stun_transaction (pair, stun, component);
3030 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2)
3031 ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr,
3032 &pair->remote->addr);
3038 * Implemented the pruning steps described in ICE sect 8.1.2
3039 * "Updating States" (ID-19) after a pair has been nominated.
3041 * @see conn_check_update_check_list_state_failed_components()
3043 static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
3047 guint in_progress = 0;
3048 guint triggered_check = 0;
3049 gchar prio[NICE_CANDIDATE_PAIR_PRIORITY_MAX_SIZE];
3051 nice_debug ("Agent %p: Pruning pending checks for s%d/c%d",
3052 agent, stream->id, component->id);
3054 /* Called when we have at least one selected pair */
3055 priority = component->selected_pair.priority;
3056 g_assert (priority > 0);
3058 nice_candidate_pair_priority_to_string (priority, prio);
3059 nice_debug ("Agent %p : selected pair priority is %s", agent, prio);
3061 i = stream->conncheck_list;
3063 CandidateCheckPair *p = i->data;
3064 GSList *next = i->next;
3066 if (p->component_id != component->id) {
3071 /* We do not remove a pair from the conncheck list if it is also in
3072 * the triggered check queue. This is not what suggests the ICE
3073 * spec, but it proved to be more robust in the aggressive
3074 * nomination scenario, precisely because these pairs may have the
3075 * use-candidate flag set, and the peer agent may already have
3076 * selected such one.
3078 if (g_slist_find (agent->triggered_check_queue, p) &&
3079 p->state != NICE_CHECK_IN_PROGRESS) {
3080 if (p->priority < priority) {
3081 nice_debug ("Agent %p : pair %p removed.", agent, p);
3082 candidate_check_pair_free (agent, p);
3083 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3088 /* step: cancel all FROZEN and WAITING pairs for the component */
3089 else if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
3090 nice_debug ("Agent %p : pair %p removed.", agent, p);
3091 candidate_check_pair_free (agent, p);
3092 stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
3095 /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
3096 else if (p->state == NICE_CHECK_IN_PROGRESS) {
3097 if (p->priority < priority) {
3098 priv_remove_pair_from_triggered_check_queue (agent, p);
3099 if (p->retransmit) {
3100 p->retransmit = FALSE;
3101 nice_debug ("Agent %p : pair %p will not be retransmitted.",
3105 /* We must keep the higher priority pairs running because if a udp
3106 * packet was lost, we might end up using a bad candidate */
3107 nice_candidate_pair_priority_to_string (p->priority, prio);
3108 nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority "
3109 "%s is higher than priority of best nominated pair.", agent, p, prio);
3110 /* We may also have to enable the retransmit flag of pairs with
3111 * a higher priority than the first nominated pair
3113 if (!p->retransmit && p->stun_transactions) {
3114 p->retransmit = TRUE;
3115 nice_debug ("Agent %p : pair %p will be retransmitted.", agent, p);
3123 return in_progress + triggered_check;
3127 * Schedules a triggered check after a successfully inbound
3128 * connectivity check. Implements ICE sect 7.2.1.4 "Triggered Checks" (ID-19).
3130 * @param agent self pointer
3131 * @param component the check is related to
3132 * @param local_socket socket from which the inbound check was received
3133 * @param remote_cand remote candidate from which the inbound check was sent
3135 static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand)
3138 NiceCandidate *local = NULL;
3139 CandidateCheckPair *p;
3141 g_assert (remote_cand != NULL);
3143 nice_debug ("Agent %p : scheduling triggered check with socket=%p "
3144 "and remote cand=%p.", agent, local_socket, remote_cand);
3146 for (i = stream->conncheck_list; i ; i = i->next) {
3148 if (p->component_id == component->id &&
3149 p->remote == remote_cand &&
3150 p->sockptr == local_socket) {
3151 /* If we match with a peer-reflexive discovered pair, we
3152 * use the parent succeeded pair instead */
3154 if (p->succeeded_pair != NULL) {
3155 g_assert (p->state == NICE_CHECK_DISCOVERED);
3156 p = p->succeeded_pair;
3159 nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...",
3160 agent, p, p->foundation, priv_state_to_string (p->state));
3163 case NICE_CHECK_WAITING:
3164 case NICE_CHECK_FROZEN:
3165 nice_debug ("Agent %p : pair %p added for a triggered check.",
3167 priv_add_pair_to_triggered_check_queue (agent, p);
3169 case NICE_CHECK_IN_PROGRESS:
3170 /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks"
3171 * we cancel the in-progress transaction, and after the
3172 * retransmission timeout, we create a new connectivity check
3173 * for that pair. The controlling role of this new check may
3174 * be different from the role of this cancelled check.
3176 * When another pair, with a higher priority is already
3177 * nominated, so there's no reason to recheck this pair,
3178 * since it can in no way replace the nominated one.
3180 if (p->priority > component->selected_pair.priority) {
3181 nice_debug ("Agent %p : pair %p added for a triggered check.",
3183 priv_add_pair_to_triggered_check_queue (agent, p);
3186 case NICE_CHECK_FAILED:
3187 if (p->priority > component->selected_pair.priority) {
3188 nice_debug ("Agent %p : pair %p added for a triggered check.",
3190 priv_add_pair_to_triggered_check_queue (agent, p);
3191 /* If the component for this pair is in failed state, move it
3192 * back to connecting, and reinitiate the timers
3194 if (component->state == NICE_COMPONENT_STATE_FAILED)
3195 agent_signal_component_state_change (agent, stream->id,
3196 component->id, NICE_COMPONENT_STATE_CONNECTING);
3197 /* If the component if in ready state, move it back to
3198 * connected as this failed pair with a higher priority
3199 * than the nominated pair requires to pursue the
3202 else if (component->state == NICE_COMPONENT_STATE_READY)
3203 agent_signal_component_state_change (agent, stream->id,
3204 component->id, NICE_COMPONENT_STATE_CONNECTED);
3207 case NICE_CHECK_SUCCEEDED:
3208 nice_debug ("Agent %p : nothing to do for pair %p.", agent, p);
3214 /* note: the spec says the we SHOULD retransmit in-progress
3215 * checks immediately, but we won't do that now */
3221 for (i = component->local_candidates; i ; i = i->next) {
3222 NiceCandidateImpl *lc = i->data;
3224 if (lc->sockptr == local_socket)
3229 nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
3230 p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id,
3231 component, local, remote_cand, NICE_CHECK_WAITING);
3233 priv_add_pair_to_triggered_check_queue (agent, p);
3237 nice_debug ("Agent %p : Didn't find a matching pair for triggered check (remote-cand=%p).", agent, remote_cand);
3244 * Sends a reply to a successfully received STUN connectivity
3245 * check request. Implements parts of the ICE spec section 7.2 (STUN
3246 * Server Procedures).
3248 * @param agent context pointer
3249 * @param stream which stream (of the agent)
3250 * @param component which component (of the stream)
3251 * @param rcand remote candidate from which the request came, if NULL,
3252 * the response is sent immediately but no other processing is done
3253 * @param toaddr address to which reply is sent
3254 * @param socket the socket over which the request came
3255 * @param rbuf_len length of STUN message to send
3256 * @param msg the STUN message to send
3257 * @param use_candidate whether the request had USE_CANDIDATE attribute
3259 * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
3261 static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream,
3262 NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand,
3263 const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len,
3264 StunMessage *msg, gboolean use_candidate)
3266 g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
3268 if (nice_debug_is_enabled ()) {
3269 gchar tmpbuf[INET6_ADDRSTRLEN];
3270 StunTransactionId id;
3271 nice_address_to_string (toaddr, tmpbuf);
3273 /* get stun message transaction id and convert it to hex. */
3274 stun_message_id(msg, id);
3275 nice_debug ("Agent %p : STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p (c-id:%u),"
3277 "transactionId=%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx",
3280 nice_address_get_port (toaddr),
3281 sockptr->fileno ? g_socket_get_fd(sockptr->fileno) : -1,
3283 rcand, component->id,
3285 id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10],
3286 id[11], id[12], id[13], id[14], id[15]);
3289 agent_socket_send (sockptr, toaddr, rbuf_len, (const gchar*)msg->buffer);
3290 if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
3291 ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr);
3294 /* We react to this stun request when we have the remote credentials.
3295 * When credentials are not yet known, this request is stored
3296 * in incoming_checks for later processing when returning from this
3299 if (rcand && stream->remote_ufrag[0]) {
3300 priv_schedule_triggered_check (agent, stream, component, sockptr, rcand);
3302 priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
3307 * Stores information of an incoming STUN connectivity check
3308 * for later use. This is only needed when a check is received
3309 * before we get information about the remote candidates (via
3310 * SDP or other signaling means).
3312 * @return pointer to created pending check, zero on error
3314 static IncomingCheck *priv_store_pending_check (NiceAgent *agent, NiceComponent *component,
3315 const NiceAddress *from, NiceSocket *sockptr, uint8_t *username,
3316 uint16_t username_len, uint32_t priority, gboolean use_candidate)
3318 IncomingCheck *icheck = NULL;
3319 nice_debug ("Agent %p : Storing pending check.", agent);
3321 if (g_queue_get_length (&component->incoming_checks) >=
3322 NICE_AGENT_MAX_REMOTE_CANDIDATES) {
3323 nice_debug ("Agent %p : WARN: unable to store information for early incoming check.", agent);
3327 icheck = g_slice_new0 (IncomingCheck);
3328 g_queue_push_tail (&component->incoming_checks, icheck);
3329 icheck->from = *from;
3330 icheck->local_socket = sockptr;
3331 icheck->priority = priority;
3332 icheck->use_candidate = use_candidate;
3333 icheck->username_len = username_len;
3334 icheck->username = NULL;
3335 if (username_len > 0)
3336 icheck->username = g_memdup (username, username_len);
3342 * Adds a new pair, discovered from an incoming STUN response, to
3343 * the connectivity check list.
3345 * @return created pair, or NULL on fatal (memory allocation) errors
3347 static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidateImpl *local_cand, CandidateCheckPair *parent_pair)
3349 CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
3350 NiceStream *stream = agent_find_stream (agent, stream_id);
3352 pair->stream_id = stream_id;
3353 pair->component_id = component->id;;
3354 pair->local = (NiceCandidate *) local_cand;
3355 pair->remote = parent_pair->remote;
3356 pair->sockptr = local_cand->sockptr;
3357 parent_pair->discovered_pair = pair;
3358 pair->succeeded_pair = parent_pair;
3359 nice_debug ("Agent %p : creating a new pair", agent);
3360 SET_PAIR_STATE (agent, pair, NICE_CHECK_DISCOVERED);
3362 gchar tmpbuf1[INET6_ADDRSTRLEN];
3363 gchar tmpbuf2[INET6_ADDRSTRLEN];
3364 nice_address_to_string (&pair->local->addr, tmpbuf1);
3365 nice_address_to_string (&pair->remote->addr, tmpbuf2);
3366 nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
3367 tmpbuf1, nice_address_get_port (&pair->local->addr),
3368 tmpbuf2, nice_address_get_port (&pair->remote->addr));
3370 g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
3371 local_cand->c.foundation, parent_pair->remote->foundation);
3373 if (agent->controlling_mode == TRUE)
3374 pair->priority = nice_candidate_pair_priority (pair->local->priority,
3375 pair->remote->priority);
3377 pair->priority = nice_candidate_pair_priority (pair->remote->priority,
3378 pair->local->priority);
3379 pair->nominated = parent_pair->nominated;
3380 /* the peer-reflexive priority used in stun request is copied from
3381 * the parent succeeded pair. This value is not required for discovered
3382 * pair, that won't emit stun requests themselves, but may be used when
3383 * such pair becomes the selected pair, and when keepalive stun are emitted,
3384 * using the sockptr and stun_priority values from the succeeded pair.
3386 pair->stun_priority = parent_pair->stun_priority;
3387 nice_debug ("Agent %p : added a new peer-discovered pair %p with "
3388 "foundation '%s' and transport %s:%s to stream %u component %u",
3389 agent, pair, pair->foundation,
3390 nice_candidate_transport_to_string (pair->local->transport),
3391 nice_candidate_transport_to_string (pair->remote->transport),
3392 stream_id, component->id);
3394 stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
3395 (GCompareFunc)conn_check_compare);
3401 * Recalculates priorities of all candidate pairs. This
3402 * is required after a conflict in ICE roles.
3404 void recalculate_pair_priorities (NiceAgent *agent)
3408 for (i = agent->streams; i; i = i->next) {
3409 NiceStream *stream = i->data;
3410 for (j = stream->conncheck_list; j; j = j->next) {
3411 CandidateCheckPair *p = j->data;
3412 p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
3414 stream->conncheck_list = g_slist_sort (stream->conncheck_list,
3415 (GCompareFunc)conn_check_compare);
3420 * Change the agent role if different from 'control'. Can be
3421 * initiated both by handling of incoming connectivity checks,
3422 * and by processing the responses to checks sent by us.
3424 static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
3426 /* role conflict, change mode; wait for a new conn. check */
3427 if (control != agent->controlling_mode) {
3428 nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".",
3429 agent, control ? "controlling" : "controlled");
3430 agent->controlling_mode = control;
3431 /* the pair priorities depend on the roles, so recalculation
3433 recalculate_pair_priorities (agent);
3436 nice_debug ("Agent %p : Role conflict, staying with role \"%s\".",
3437 agent, control ? "controlling" : "controlled");
3441 * Checks whether the mapped address in connectivity check response
3442 * matches any of the known local candidates. If not, apply the
3443 * mechanism for "Discovering Peer Reflexive Candidates" ICE ID-19)
3445 * @param agent context pointer
3446 * @param stream which stream (of the agent)
3447 * @param component which component (of the stream)
3448 * @param p the connectivity check pair for which we got a response
3449 * @param socketptr socket used to send the reply
3450 * @param mapped_sockaddr mapped address in the response
3452 * @return pointer to a candidate pair, found in conncheck list or newly created
3454 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)
3456 CandidateCheckPair *new_pair = NULL;
3459 NiceCandidate *local_cand = NULL;
3461 nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
3463 for (i = component->local_candidates; i; i = i->next) {
3464 NiceCandidate *cand = i->data;
3466 if (nice_address_equal (&mapped, &cand->addr) &&
3467 local_candidate_and_socket_compatible (agent, cand, sockptr)) {
3473 /* The mapped address allows to look for a previously discovered
3474 * peer reflexive local candidate, and its related pair. This
3475 * new_pair will be marked 'Valid', while the pair 'p' of the
3476 * initial stun request will be marked 'Succeeded'
3478 * In the case of a tcp-act/tcp-pass pair 'p', where the local
3479 * candidate is of type tcp-act, and its port number is zero, a
3480 * conncheck on this pair *always* leads to the creation of a
3481 * discovered peer-reflexive tcp-act local candidate.
3483 for (i = stream->conncheck_list; i; i = i->next) {
3484 CandidateCheckPair *pair = i->data;
3485 if (local_cand == pair->local && remote_candidate == pair->remote) {
3492 /* note: when new_pair is distinct from p, it means new_pair is a
3493 * previously discovered peer-reflexive candidate pair, so we don't
3494 * set the valid flag on p in this case, because the valid flag is
3495 * already set on the discovered pair.
3500 /* this new_pair distinct from p may also be in state failed (if
3501 * the related succeeded pair p was in state failed previously, but
3502 * retriggered a successful check a bit later), so we force its
3503 * state back to discovered there.
3505 SET_PAIR_STATE (agent, new_pair, NICE_CHECK_DISCOVERED);
3507 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3508 priv_remove_pair_from_triggered_check_queue (agent, p);
3509 priv_free_all_stun_transactions (p, component);
3510 nice_component_add_valid_candidate (agent, component, remote_candidate);
3513 if (local_cand == NULL && !agent->force_relay) {
3514 /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
3515 * "Discovering Peer Reflexive Candidates"
3517 * The priority equal to the value of the PRIORITY attribute
3518 * in the Binding request is taken from the "parent" pair p
3520 local_cand = discovery_add_peer_reflexive_candidate (agent,
3528 nice_debug ("Agent %p : added a new peer-reflexive local candidate %p "
3529 "with transport %s", agent, local_cand,
3530 nice_candidate_transport_to_string (local_cand->transport));
3533 /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
3534 "Constructing a Valid Pair") */
3536 new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component,
3537 (NiceCandidateImpl *) local_cand, p);
3538 /* note: this is same as "adding to VALID LIST" in the spec
3541 new_pair->valid = TRUE;
3542 /* step: The agent sets the state of the pair that *generated* the check to
3543 * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States"
3545 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3546 priv_remove_pair_from_triggered_check_queue (agent, p);
3547 priv_free_all_stun_transactions (p, component);
3550 if (new_pair && new_pair->valid)
3551 nice_component_add_valid_candidate (agent, component, remote_candidate);
3558 * Tries to match STUN reply in 'buf' to an existing STUN connectivity
3559 * check transaction. If found, the reply is processed. Implements
3560 * section 7.1.2 "Processing the Response" of ICE spec (ID-19).
3562 * @return TRUE if a matching transaction is found
3564 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)
3567 struct sockaddr_storage storage;
3568 struct sockaddr addr;
3570 socklen_t socklen = sizeof (sockaddr);
3573 StunUsageIceReturn res;
3574 StunTransactionId discovery_id;
3575 StunTransactionId response_id;
3576 stun_message_id (resp, response_id);
3578 for (i = stream->conncheck_list; i; i = i->next) {
3579 CandidateCheckPair *p = i->data;
3581 for (j = p->stun_transactions, k = 0; j; j = j->next, k++) {
3582 StunTransaction *stun = j->data;
3584 stun_message_id (&stun->message, discovery_id);
3586 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)))
3589 res = stun_usage_ice_conncheck_process (resp,
3590 &sockaddr.storage, &socklen,
3591 agent_to_ice_compatibility (agent));
3592 nice_debug ("Agent %p : stun_bind_process/conncheck for %p: "
3593 "%s,res=%s,stun#=%d.",
3595 agent->controlling_mode ? "controlling" : "controlled",
3596 priv_ice_return_to_string (res), k);
3598 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
3599 res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3600 /* case: found a matching connectivity check request */
3602 CandidateCheckPair *ok_pair = NULL;
3604 nice_debug ("Agent %p : pair %p MATCHED.", agent, p);
3605 priv_remove_stun_transaction (p, stun, component);
3607 /* step: verify that response came from the same IP address we
3608 * sent the original request to (see 7.1.2.1. "Failure
3610 if (nice_address_equal (from, &p->remote->addr) == FALSE) {
3611 candidate_check_pair_fail (stream, agent, p);
3612 if (nice_debug_is_enabled ()) {
3613 gchar tmpbuf[INET6_ADDRSTRLEN];
3614 gchar tmpbuf2[INET6_ADDRSTRLEN];
3615 nice_debug ("Agent %p : pair %p FAILED"
3616 " (mismatch of source address).", agent, p);
3617 nice_address_to_string (&p->remote->addr, tmpbuf);
3618 nice_address_to_string (from, tmpbuf2);
3619 nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
3620 tmpbuf, nice_address_get_port (&p->remote->addr),
3621 tmpbuf2, nice_address_get_port (from));
3623 conn_check_update_check_list_state_for_ready (agent,
3628 if (remote_candidate == NULL) {
3629 candidate_check_pair_fail (stream, agent, p);
3630 if (nice_debug_is_enabled ()) {
3631 nice_debug ("Agent %p : pair %p FAILED "
3632 "(got a matching pair without a known remote candidate).", agent, p);
3634 conn_check_update_check_list_state_for_ready (agent,
3639 /* note: CONNECTED but not yet READY, see docs */
3641 /* step: handle the possible case of a peer-reflexive
3642 * candidate where the mapped-address in response does
3643 * not match any local candidate, see 7.1.2.2.1
3644 * "Discovering Peer Reflexive Candidates" ICE ID-19) */
3646 if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
3647 nice_debug ("Agent %p : Mapped address not found", agent);
3648 SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED);
3650 nice_component_add_valid_candidate (agent, component, p->remote);
3652 ok_pair = priv_process_response_check_for_reflexive (agent,
3653 stream, component, p, sockptr, &sockaddr.addr,
3654 local_candidate, remote_candidate);
3656 /* note: The success of this check might also
3657 * cause the state of other checks to change as well
3658 * See sect 7.2.5.3.3 (Updating Candidate Pair States) of
3659 * ICE spec (RFC8445).
3661 conn_check_unfreeze_related (agent, p);
3663 /* Note: this assignment helps to reduce the numbers of cases
3664 * to be tested. If ok_pair and p refer to distinct pairs, it
3665 * means that ok_pair is a discovered peer reflexive one,
3666 * caused by the check made on pair p. In that case, the
3667 * flags to be tested are on p, but the nominated flag will be
3668 * set on ok_pair. When there's no discovered pair, p and
3669 * ok_pair refer to the same pair.
3670 * To summarize : p is a SUCCEEDED pair, ok_pair is a
3671 * DISCOVERED, VALID, and eventually NOMINATED pair.
3676 /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
3677 Nominated Flag" (ID-19) */
3678 if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
3679 nice_debug ("Agent %p : Updating nominated flag (%s): "
3680 "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)",
3681 agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ?
3683 ok_pair, ok_pair->use_candidate_on_next_check,
3684 ok_pair->mark_nominated_on_response_arrival,
3685 p, p->use_candidate_on_next_check,
3686 p->mark_nominated_on_response_arrival);
3688 if (agent->controlling_mode) {
3689 switch (agent->nomination_mode) {
3690 case NICE_NOMINATION_MODE_REGULAR:
3691 if (p->use_candidate_on_next_check) {
3692 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3693 "(regular nomination, controlling, "
3694 "use_cand_on_next_check=1).",
3695 agent, ok_pair, ok_pair->foundation);
3696 ok_pair->nominated = TRUE;
3699 case NICE_NOMINATION_MODE_AGGRESSIVE:
3700 if (!p->nominated) {
3701 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3702 "(aggressive nomination, controlling).",
3703 agent, ok_pair, ok_pair->foundation);
3704 ok_pair->nominated = TRUE;
3712 if (p->mark_nominated_on_response_arrival) {
3713 nice_debug ("Agent %p : marking pair %p (%s) as nominated "
3714 "(%s nomination, controlled, mark_on_response=1).",
3715 agent, ok_pair, ok_pair->foundation,
3716 agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
3717 "aggressive" : "regular");
3718 ok_pair->nominated = TRUE;
3723 if (ok_pair->nominated == TRUE) {
3724 conn_check_update_selected_pair (agent, component, ok_pair);
3725 priv_print_conn_check_lists (agent, G_STRFUNC,
3726 ", got a nominated pair");
3728 /* Do not step down to CONNECTED if we're already at state READY*/
3729 if (component->state != NICE_COMPONENT_STATE_READY)
3730 /* step: notify the client of a new component state (must be done
3731 * before the possible check list state update step */
3732 agent_signal_component_state_change (agent,
3733 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
3736 /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
3737 states" and 8.1.2 "Updating States", ID-19) */
3738 conn_check_update_check_list_state_for_ready (agent, stream, component);
3739 } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
3741 gboolean controlled_mode;
3743 if (!p->retransmit) {
3744 nice_debug ("Agent %p : Role conflict with pair %p, not restarting",
3749 /* case: role conflict error, need to restart with new role */
3750 nice_debug ("Agent %p : Role conflict with pair %p, restarting",
3753 /* note: this res value indicates that the role of the peer
3754 * agent has not changed after the tie-breaker comparison, so
3755 * this is our role that must change. see ICE sect. 7.1.3.1
3756 * "Failure Cases". Our role might already have changed due to
3757 * an earlier incoming request, but if not, change role now.
3759 * Sect. 7.1.3.1 is not clear on this point, but we choose to
3760 * put the candidate pair in the triggered check list even
3761 * when the agent did not switch its role. The reason for this
3762 * interpretation is that the reception of the stun reply, even
3763 * an error reply, is a good sign that this pair will be
3764 * valid, if we retry the check after the role of both peers
3767 controlled_mode = (stun_message_find64 (&stun->message,
3768 STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) ==
3769 STUN_MESSAGE_RETURN_SUCCESS);
3771 priv_check_for_role_conflict (agent, controlled_mode);
3772 priv_remove_stun_transaction (p, stun, component);
3773 priv_add_pair_to_triggered_check_queue (agent, p);
3775 /* case: STUN error, the check STUN context was freed */
3776 candidate_check_pair_fail (stream, agent, p);
3777 conn_check_update_check_list_state_for_ready (agent, stream, component);
3787 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3788 * transaction. If found, a reply is sent.
3790 * @return TRUE if a matching transaction is found
3792 static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessage *resp, const NiceAddress *server_address)
3795 struct sockaddr_storage storage;
3796 struct sockaddr addr;
3798 socklen_t socklen = sizeof (sockaddr);
3801 struct sockaddr_storage storage;
3802 struct sockaddr addr;
3804 socklen_t alternatelen = sizeof (sockaddr);
3807 StunUsageBindReturn res;
3808 gboolean trans_found = FALSE;
3809 StunTransactionId discovery_id;
3810 StunTransactionId response_id;
3811 stun_message_id (resp, response_id);
3813 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
3814 CandidateDiscovery *d = i->data;
3816 if (d->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
3817 d->stun_message.buffer) {
3818 stun_message_id (&d->stun_message, discovery_id);
3820 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
3821 res = stun_usage_bind_process (resp, &sockaddr.addr,
3822 &socklen, &alternate.addr, &alternatelen);
3823 nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
3824 agent, d, (int)res);
3826 if (res == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
3827 /* handle alternate server */
3828 NiceAddress niceaddr;
3829 nice_address_set_from_sockaddr (&niceaddr, &alternate.addr);
3830 d->server = niceaddr;
3833 agent->discovery_unsched_items++;
3834 } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
3835 /* case: successful binding discovery, create a new local candidate */
3837 if (!agent->force_relay) {
3838 NiceAddress niceaddr;
3840 nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr);
3841 discovery_add_server_reflexive_candidate (
3846 NICE_CANDIDATE_TRANSPORT_UDP,
3851 if (agent->use_ice_tcp)
3852 discovery_discover_tcp_server_reflexive_candidates (
3860 d->stun_message.buffer = NULL;
3861 d->stun_message.buffer_len = 0;
3864 } else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
3865 /* case: STUN error, the check STUN context was freed */
3866 d->stun_message.buffer = NULL;
3867 d->stun_message.buffer_len = 0;
3879 priv_calc_turn_timeout (guint lifetime)
3882 return lifetime - 60;
3884 return lifetime / 2;
3888 priv_add_new_turn_refresh (NiceAgent *agent, CandidateDiscovery *cdisco,
3889 NiceCandidateImpl *relay_cand, guint lifetime)
3891 CandidateRefresh *cand;
3893 if (cdisco->turn->type == NICE_RELAY_TYPE_TURN_TLS &&
3894 (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
3895 agent->compatibility == NICE_COMPATIBILITY_OC2007R2))
3898 cand = g_slice_new0 (CandidateRefresh);
3900 cand->candidate = relay_cand;
3901 cand->nicesock = cdisco->nicesock;
3902 cand->server = cdisco->server;
3903 cand->stream_id = cdisco->stream_id;
3904 cand->component_id = cdisco->component_id;
3905 memcpy (&cand->stun_agent, &cdisco->stun_agent, sizeof(StunAgent));
3907 /* Use previous stun response for authentication credentials */
3908 if (cdisco->stun_resp_msg.buffer != NULL) {
3909 memcpy(cand->stun_resp_buffer, cdisco->stun_resp_buffer,
3910 sizeof(cand->stun_resp_buffer));
3911 memcpy(&cand->stun_resp_msg, &cdisco->stun_resp_msg, sizeof(StunMessage));
3912 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
3913 cand->stun_resp_msg.agent = &cand->stun_agent;
3914 cand->stun_resp_msg.key = NULL;
3918 agent->refresh_list = g_slist_append (agent->refresh_list, cand);
3919 nice_debug ("Agent %p : Adding new refresh candidate %p with timeout %d",
3920 agent, cand, priv_calc_turn_timeout (lifetime));
3921 /* step: also start the refresh timer */
3922 /* refresh should be sent 1 minute before it expires */
3923 agent_timeout_add_seconds_with_context (agent, &cand->timer_source,
3924 "Candidate TURN refresh",
3925 priv_calc_turn_timeout (lifetime),
3926 priv_turn_allocate_refresh_tick_agent_locked, cand);
3928 nice_debug ("timer source is : %p", cand->timer_source);
3930 agent->pruning_refreshes = g_slist_append (agent->pruning_refreshes, cand);
3931 nice_debug ("Agent %p : Sending request to remove TURN allocation "
3932 "for refresh %p", agent, cand);
3933 cand->disposing = TRUE;
3934 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
3935 if (relay_cand->sockptr)
3936 nice_socket_free (relay_cand->sockptr);
3937 nice_candidate_free ((NiceCandidate *)relay_cand);
3943 static void priv_handle_turn_alternate_server (NiceAgent *agent,
3944 CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate)
3946 /* We need to cancel and reset all candidate discovery turn for the same
3947 stream and type if there is an alternate server. Otherwise, we might end up
3948 with two relay components on different servers, creating candidates with
3949 unique foundations that only contain one component.
3953 for (i = agent->discovery_list; i; i = i->next) {
3954 CandidateDiscovery *d = i->data;
3957 d->type == disco->type &&
3958 d->stream_id == disco->stream_id &&
3959 d->turn->type == disco->turn->type &&
3960 nice_address_equal (&d->server, &server)) {
3961 gchar ip[INET6_ADDRSTRLEN];
3962 // Cancel the pending request to avoid a race condition with another
3963 // component responding with another altenrate-server
3964 d->stun_message.buffer = NULL;
3965 d->stun_message.buffer_len = 0;
3967 nice_address_to_string (&server, ip);
3968 nice_debug ("Agent %p : Cancelling and setting alternate server %s for "
3969 "CandidateDiscovery %p on s%d/c%d", agent, ip, d,
3970 d->stream_id, d->component_id);
3971 d->server = alternate;
3972 d->turn->server = alternate;
3974 agent->discovery_unsched_items++;
3976 if (d->turn->type == NICE_RELAY_TYPE_TURN_TCP ||
3977 d->turn->type == NICE_RELAY_TYPE_TURN_TLS) {
3979 NiceComponent *component;
3981 if (!agent_find_component (agent, d->stream_id, d->component_id,
3982 &stream, &component)) {
3983 nice_debug ("Could not find stream or component in "
3984 "priv_handle_turn_alternate_server");
3987 d->nicesock = agent_create_tcp_turn_socket (agent, stream, component,
3988 d->nicesock, &d->server, d->turn->type,
3989 nice_socket_is_reliable (d->nicesock));
3991 nice_component_attach_socket (component, d->nicesock);
3998 * Tries to match STUN reply in 'buf' to an existing STUN discovery
3999 * transaction. If found, a reply is sent.
4001 * @return TRUE if a matching transaction is found
4003 static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
4006 struct sockaddr_storage storage;
4007 struct sockaddr addr;
4009 socklen_t socklen = sizeof (sockaddr);
4012 struct sockaddr_storage storage;
4013 struct sockaddr addr;
4015 socklen_t alternatelen = sizeof (alternate);
4018 struct sockaddr_storage storage;
4019 struct sockaddr addr;
4021 socklen_t relayaddrlen = sizeof (relayaddr);
4026 StunUsageTurnReturn res;
4027 gboolean trans_found = FALSE;
4028 StunTransactionId discovery_id;
4029 StunTransactionId response_id;
4030 stun_message_id (resp, response_id);
4032 for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
4033 CandidateDiscovery *d = i->data;
4035 if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
4036 d->stun_message.buffer) {
4037 stun_message_id (&d->stun_message, discovery_id);
4039 if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
4040 res = stun_usage_turn_process (resp,
4041 &relayaddr.storage, &relayaddrlen,
4042 &sockaddr.storage, &socklen,
4043 &alternate.storage, &alternatelen,
4044 &bandwidth, &lifetime, agent_to_turn_compatibility (agent));
4045 nice_debug ("Agent %p : stun_turn_process/disc for %p res %d.",
4046 agent, d, (int)res);
4048 if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
4051 /* handle alternate server */
4052 nice_address_set_from_sockaddr (&addr, &alternate.addr);
4053 priv_handle_turn_alternate_server (agent, d, d->server, addr);
4055 } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
4056 res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
4057 /* case: successful allocate, create a new local candidate */
4058 NiceAddress niceaddr;
4059 NiceCandidateImpl *relay_cand;
4061 nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr);
4063 if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
4064 NiceAddress mappedniceaddr;
4066 /* We also received our mapped address */
4067 nice_address_set_from_sockaddr (&mappedniceaddr, &sockaddr.addr);
4069 /* TCP or TLS TURNS means the server-reflexive address was
4070 * on a TCP connection, which cannot be used for server-reflexive
4071 * discovery of candidates.
4073 if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP &&
4074 !agent->force_relay) {
4075 discovery_add_server_reflexive_candidate (
4080 NICE_CANDIDATE_TRANSPORT_UDP,
4085 if (agent->use_ice_tcp) {
4086 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4087 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4088 !nice_address_equal_no_port (&niceaddr, &d->turn->server)) {
4089 nice_debug("TURN port got allocated on an alternate server, "
4090 "ignoring bogus srflx address");
4092 discovery_discover_tcp_server_reflexive_candidates (
4103 if (nice_socket_is_reliable (d->nicesock)) {
4104 relay_cand = discovery_add_relay_candidate (
4109 NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE,
4115 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4116 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4117 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4119 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4122 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4125 relay_cand = discovery_add_relay_candidate (
4130 NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE,
4135 relay_cand = discovery_add_relay_candidate (
4140 NICE_CANDIDATE_TRANSPORT_UDP,
4147 if (d->stun_resp_msg.buffer)
4148 nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr,
4150 if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4151 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
4152 /* These data are needed on TURN socket when sending requests,
4153 * but never reach nice_turn_socket_parse_recv() where it could
4154 * be read directly, as the socket does not exist when allocate
4155 * response arrives to _nice_agent_recv(). We must set them right
4156 * after socket gets created in discovery_add_relay_candidate(),
4157 * so we are doing it here. */
4158 nice_udp_turn_socket_set_ms_realm(relay_cand->sockptr,
4160 nice_udp_turn_socket_set_ms_connection_id(relay_cand->sockptr,
4163 priv_add_new_turn_refresh (agent, d, relay_cand, lifetime);
4166 d->stun_message.buffer = NULL;
4167 d->stun_message.buffer_len = 0;
4170 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4172 uint8_t *sent_realm = NULL;
4173 uint8_t *recv_realm = NULL;
4174 uint16_t sent_realm_len = 0;
4175 uint16_t recv_realm_len = 0;
4177 sent_realm = (uint8_t *) stun_message_find (&d->stun_message,
4178 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4179 recv_realm = (uint8_t *) stun_message_find (resp,
4180 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4182 if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4183 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4184 alternatelen != sizeof(alternate)) {
4187 nice_address_set_from_sockaddr (&addr, &alternate.addr);
4189 if (!nice_address_equal (&addr, &d->server)) {
4190 priv_handle_turn_alternate_server (agent, d, d->server, addr);
4193 /* check for unauthorized error response */
4194 if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
4195 agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
4196 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
4197 stun_message_get_class (resp) == STUN_ERROR &&
4198 stun_message_find_error (resp, &code) ==
4199 STUN_MESSAGE_RETURN_SUCCESS &&
4200 recv_realm != NULL && recv_realm_len > 0) {
4202 if (code == STUN_ERROR_STALE_NONCE ||
4203 (code == STUN_ERROR_UNAUTHORIZED &&
4204 !(recv_realm_len == sent_realm_len &&
4205 sent_realm != NULL &&
4206 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4207 d->stun_resp_msg = *resp;
4208 memcpy (d->stun_resp_buffer, resp->buffer,
4209 stun_message_length (resp));
4210 d->stun_resp_msg.buffer = d->stun_resp_buffer;
4211 d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
4213 agent->discovery_unsched_items++;
4215 /* case: a real unauthorized error */
4216 d->stun_message.buffer = NULL;
4217 d->stun_message.buffer_len = 0;
4220 } else if (d->pending) {
4221 /* case: STUN error, the check STUN context was freed */
4222 d->stun_message.buffer = NULL;
4223 d->stun_message.buffer_len = 0;
4237 * Tries to match STUN reply in 'buf' to an existing STUN discovery
4238 * transaction. If found, a reply is sent.
4240 * @return TRUE if a matching transaction is found
4242 static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *resp)
4246 StunUsageTurnReturn res;
4247 gboolean trans_found = FALSE;
4248 StunTransactionId refresh_id;
4249 StunTransactionId response_id;
4250 stun_message_id (resp, response_id);
4252 for (i = agent->refresh_list; i && trans_found != TRUE;) {
4253 CandidateRefresh *cand = i->data;
4254 GSList *next = i->next;
4256 if (!cand->disposing && cand->stun_message.buffer) {
4257 stun_message_id (&cand->stun_message, refresh_id);
4259 if (memcmp (refresh_id, response_id, sizeof(StunTransactionId)) == 0) {
4260 res = stun_usage_turn_refresh_process (resp,
4261 &lifetime, agent_to_turn_compatibility (agent));
4262 nice_debug ("Agent %p : stun_turn_refresh_process for %p res %d with lifetime %u.",
4263 agent, cand, (int)res, lifetime);
4264 if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS) {
4265 /* refresh should be sent 1 minute before it expires */
4266 agent_timeout_add_seconds_with_context (agent,
4267 &cand->timer_source,
4268 "Candidate TURN refresh", priv_calc_turn_timeout (lifetime),
4269 priv_turn_allocate_refresh_tick_agent_locked, cand);
4271 g_source_destroy (cand->tick_source);
4272 g_source_unref (cand->tick_source);
4273 cand->tick_source = NULL;
4275 } else if (res == STUN_USAGE_TURN_RETURN_ERROR) {
4277 uint8_t *sent_realm = NULL;
4278 uint8_t *recv_realm = NULL;
4279 uint16_t sent_realm_len = 0;
4280 uint16_t recv_realm_len = 0;
4282 sent_realm = (uint8_t *) stun_message_find (&cand->stun_message,
4283 STUN_ATTRIBUTE_REALM, &sent_realm_len);
4284 recv_realm = (uint8_t *) stun_message_find (resp,
4285 STUN_ATTRIBUTE_REALM, &recv_realm_len);
4287 /* check for unauthorized error response */
4288 if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
4289 stun_message_get_class (resp) == STUN_ERROR &&
4290 stun_message_find_error (resp, &code) ==
4291 STUN_MESSAGE_RETURN_SUCCESS &&
4292 recv_realm != NULL && recv_realm_len > 0) {
4294 if (code == STUN_ERROR_STALE_NONCE ||
4295 (code == STUN_ERROR_UNAUTHORIZED &&
4296 !(recv_realm_len == sent_realm_len &&
4297 sent_realm != NULL &&
4298 memcmp (sent_realm, recv_realm, sent_realm_len) == 0))) {
4299 cand->stun_resp_msg = *resp;
4300 memcpy (cand->stun_resp_buffer, resp->buffer,
4301 stun_message_length (resp));
4302 cand->stun_resp_msg.buffer = cand->stun_resp_buffer;
4303 cand->stun_resp_msg.buffer_len = sizeof(cand->stun_resp_buffer);
4304 priv_turn_allocate_refresh_tick_unlocked (agent, cand);
4306 /* case: a real unauthorized error */
4307 refresh_free (agent, cand);
4310 /* case: STUN error, the check STUN context was freed */
4311 refresh_free (agent, cand);
4323 static gboolean priv_map_reply_to_relay_remove (NiceAgent *agent,
4326 StunTransactionId response_id;
4329 stun_message_id (resp, response_id);
4331 for (i = agent->refresh_list; i; i = i->next) {
4332 CandidateRefresh *cand = i->data;
4333 StunTransactionId request_id;
4334 StunUsageTurnReturn res;
4337 if (!cand->disposing || !cand->stun_message.buffer) {
4341 stun_message_id (&cand->stun_message, request_id);
4343 if (memcmp (request_id, response_id, sizeof(StunTransactionId)) == 0) {
4344 res = stun_usage_turn_refresh_process (resp, &lifetime,
4345 agent_to_turn_compatibility (agent));
4347 nice_debug ("Agent %p : priv_map_reply_to_relay_remove for %p res %d "
4348 "with lifetime %u.", agent, cand, res, lifetime);
4350 if (res != STUN_USAGE_TURN_RETURN_INVALID) {
4351 refresh_free (agent, cand);
4360 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
4361 NiceComponent *component, StunMessage *resp)
4363 guint64 now = g_get_monotonic_time();
4365 nice_debug ("Agent %p : Keepalive for selected pair %p received.",
4366 agent, &component->selected_pair);
4368 /* timeout is checked even if consent_freshness is disabled */
4369 component->selected_pair.remote_consent.last_received = now;
4377 NiceComponent *component;
4379 } conncheck_validater_data;
4381 static bool conncheck_stun_validater (StunAgent *agent,
4382 StunMessage *message, uint8_t *username, uint16_t username_len,
4383 uint8_t **password, size_t *password_len, void *user_data)
4385 conncheck_validater_data *data = (conncheck_validater_data*) user_data;
4387 gchar *ufrag = NULL;
4390 gboolean msn_msoc_nice_compatibility =
4391 data->agent->compatibility == NICE_COMPATIBILITY_MSN ||
4392 data->agent->compatibility == NICE_COMPATIBILITY_OC2007;
4394 if (data->agent->compatibility == NICE_COMPATIBILITY_OC2007 &&
4395 stun_message_get_class (message) == STUN_RESPONSE)
4396 i = data->component->remote_candidates;
4398 i = data->component->local_candidates;
4400 for (; i; i = i->next) {
4401 NiceCandidate *cand = i->data;
4405 ufrag = cand->username;
4407 ufrag = data->stream->local_ufrag;
4408 ufrag_len = ufrag? strlen (ufrag) : 0;
4410 if (ufrag && msn_msoc_nice_compatibility)
4411 ufrag = (gchar *)g_base64_decode (ufrag, &ufrag_len);
4416 stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d",
4417 username_len, ufrag_len, username_len >= ufrag_len ?
4418 memcmp (username, ufrag, ufrag_len) : 0);
4419 stun_debug_bytes (" username: ", username, username_len);
4420 stun_debug_bytes (" ufrag: ", ufrag, ufrag_len);
4421 if (ufrag_len > 0 && username_len >= ufrag_len &&
4422 memcmp (username, ufrag, ufrag_len) == 0) {
4426 pass = cand->password;
4427 else if (data->stream && data->stream->local_password[0])
4428 pass = data->stream->local_password;
4431 *password = (uint8_t *) pass;
4432 *password_len = strlen (pass);
4434 if (msn_msoc_nice_compatibility) {
4437 data->password = g_base64_decode (pass, &pass_len);
4438 *password = data->password;
4439 *password_len = pass_len;
4443 if (msn_msoc_nice_compatibility)
4446 stun_debug ("Found valid username, returning password: '%s'", *password);
4450 if (msn_msoc_nice_compatibility)
4458 * handle RENOMINATION stun attribute
4459 * @return TRUE if nomination changed. FALSE otherwise
4461 static gboolean conn_check_handle_renomination (NiceAgent *agent, NiceStream *stream,
4462 NiceComponent *component, StunMessage *req,
4463 NiceCandidate *remote_candidate, NiceCandidate *local_candidate, IncomingCheck *pending_check)
4466 if (!agent->controlling_mode && NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
4467 agent->support_renomination && remote_candidate && local_candidate)
4469 uint32_t nom_value = 0;
4470 uint16_t nom_len = 0;
4471 const void *value = stun_message_find (req, STUN_ATTRIBUTE_NOMINATION, &nom_len);
4476 memcpy (&nom_value, value, 4);
4477 nom_value = ntohl (nom_value);
4479 nice_debug ("Agent %p : received NOMINATION attr with incorrect octet length %u, expected 4 bytes",
4484 if (nice_debug_is_enabled ()) {
4485 gchar remote_str[INET6_ADDRSTRLEN];
4486 nice_address_to_string(&remote_candidate->addr, remote_str);
4487 nice_debug ("Agent %p : received NOMINATION attr for remote candidate [%s]:%u, value is %u",
4488 agent, remote_str, nice_address_get_port (&remote_candidate->addr), nom_value);
4492 * If another pair is SELECTED, change this pair's priority to be greater than
4493 * selected pair's priority so this pair gets SELECTED!
4495 if (component->selected_pair.priority &&
4496 component->selected_pair.remote && component->selected_pair.remote != (NiceCandidateImpl *) remote_candidate &&
4497 component->selected_pair.local && component->selected_pair.local != (NiceCandidateImpl *) local_candidate) {
4498 for (lst = stream->conncheck_list; lst; lst = lst->next) {
4499 CandidateCheckPair *pair = lst->data;
4500 if (pair->local == local_candidate && pair->remote == remote_candidate) {
4502 pair->priority = component->selected_pair.priority + 1;
4508 if (!priv_mark_pair_nominated (agent, stream, component, local_candidate, remote_candidate)) {
4509 /* No matching pair in conn check list. It means that we are probably handling incoming conn check,
4510 * so triggered check (pending_check) will be performed in future once we have credentials and remote candidates.
4511 * Constructed pair needs to be nominated then, so set use_candidate for pending check.
4513 if (nice_debug_is_enabled ()) {
4514 gchar remote_str[INET6_ADDRSTRLEN];
4515 nice_address_to_string(&remote_candidate->addr, remote_str);
4516 nice_debug ("Agent %p : no matching pair nominated while handling NOMINATION attr for "
4517 "remote candidate [%s]:%u, pending check: %p - %s",
4518 agent, remote_str, nice_address_get_port (&remote_candidate->addr),
4519 pending_check, pending_check ? "set use_candidate" : "skip");
4522 pending_check->use_candidate = TRUE;
4530 * Processing an incoming STUN message.
4532 * @param agent self pointer
4533 * @param stream stream the packet is related to
4534 * @param component component the packet is related to
4535 * @param nicesock socket from which the packet was received
4536 * @param from address of the sender
4537 * @param buf message contents
4538 * @param buf message length
4540 * @pre contents of 'buf' is a STUN message
4542 * @return XXX (what FALSE means exactly?)
4544 gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
4545 NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from,
4546 gchar *buf, guint len)
4549 struct sockaddr_storage storage;
4550 struct sockaddr addr;
4552 uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
4554 size_t rbuf_len = sizeof (rbuf);
4555 bool control = agent->controlling_mode;
4556 uint8_t uname[NICE_STREAM_MAX_UNAME];
4559 uint16_t username_len;
4562 StunValidationStatus valid;
4563 conncheck_validater_data validater_data = {agent, stream, component, NULL};
4565 NiceCandidate *remote_candidate = NULL;
4566 NiceCandidate *remote_candidate2 = NULL;
4567 NiceCandidate *local_candidate = NULL;
4568 gboolean discovery_msg = FALSE;
4569 IncomingCheck *pending_check = NULL;
4571 nice_address_copy_to_sockaddr (from, &sockaddr.addr);
4573 /* note: contents of 'buf' already validated, so it is
4574 * a valid and fully received STUN message */
4576 if (nice_debug_is_enabled ()) {
4577 gchar tmpbuf[INET6_ADDRSTRLEN];
4578 nice_address_to_string (from, tmpbuf);
4579 nice_debug ("Agent %p: inbound STUN packet for %u/%u (stream/component) from [%s]:%u (%u octets) :",
4580 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from), len);
4583 /* note: ICE 7.2. "STUN Server Procedures" (ID-19) */
4585 valid = stun_agent_validate (&component->stun_agent, &req,
4586 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4588 /* Check for discovery candidates stun agents */
4589 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4590 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4591 for (i = agent->discovery_list; i; i = i->next) {
4592 CandidateDiscovery *d = i->data;
4593 if (d->stream_id == stream->id && d->component_id == component->id &&
4594 d->nicesock == nicesock) {
4595 valid = stun_agent_validate (&d->stun_agent, &req,
4596 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4598 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4601 discovery_msg = TRUE;
4606 /* Check for relay refresh stun agents */
4607 if (valid == STUN_VALIDATION_BAD_REQUEST ||
4608 valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4609 for (i = agent->refresh_list; i; i = i->next) {
4610 CandidateRefresh *r = i->data;
4612 nice_debug_verbose ("Comparing r.sid=%u to sid=%u, r.cid=%u to cid=%u and %p and %p to %p",
4613 r->stream_id, stream->id, r->component_id, component->id, r->nicesock,
4614 r->candidate->sockptr, nicesock);
4616 if (r->stream_id == stream->id && r->component_id == component->id &&
4617 (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) {
4618 valid = stun_agent_validate (&r->stun_agent, &req,
4619 (uint8_t *) buf, len, conncheck_stun_validater, &validater_data);
4620 nice_debug ("Validating gave %d", valid);
4621 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE)
4623 discovery_msg = TRUE;
4629 g_free (validater_data.password);
4631 if (valid == STUN_VALIDATION_NOT_STUN ||
4632 valid == STUN_VALIDATION_INCOMPLETE_STUN ||
4633 valid == STUN_VALIDATION_BAD_REQUEST)
4635 nice_debug ("Agent %p : Incorrectly multiplexed STUN message ignored.",
4640 if (valid == STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE) {
4641 nice_debug ("Agent %p : Unknown mandatory attributes in message.", agent);
4643 if (agent->compatibility != NICE_COMPATIBILITY_MSN &&
4644 agent->compatibility != NICE_COMPATIBILITY_OC2007) {
4645 rbuf_len = stun_agent_build_unknown_attributes_error (&component->stun_agent,
4646 &msg, rbuf, rbuf_len, &req);
4648 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4653 if (valid == STUN_VALIDATION_UNAUTHORIZED) {
4654 nice_debug ("Agent %p : Integrity check failed.", agent);
4656 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4657 &req, STUN_ERROR_UNAUTHORIZED)) {
4658 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4659 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4660 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4661 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4665 if (valid == STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST) {
4666 nice_debug ("Agent %p : Integrity check failed - bad request.", agent);
4667 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4668 &req, STUN_ERROR_BAD_REQUEST)) {
4669 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4670 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4671 agent->compatibility != NICE_COMPATIBILITY_OC2007)
4672 agent_socket_send (nicesock, from, rbuf_len, (const gchar*)rbuf);
4677 if (valid == STUN_VALIDATION_FORBIDDEN) {
4678 CandidatePair *pair = &component->selected_pair;
4679 gchar tmpbuf[INET6_ADDRSTRLEN];
4680 nice_address_to_string (from, tmpbuf);
4681 nice_debug ("Agent %p : received 403: 'Forbidden' for %u/%u (stream/component) from [%s]:%u",
4682 agent, stream->id, component->id, tmpbuf, nice_address_get_port (from));
4684 for (i = stream->conncheck_list; i; i = i->next) {
4685 CandidateCheckPair *p = i->data;
4687 if (nice_address_equal (from, &p->remote->addr)) {
4688 candidate_check_pair_fail (stream, agent, p);
4692 /* if the pair was selected, it is no longer useful */
4693 if (nice_address_equal (from, &pair->remote->c.addr)) {
4694 pair->remote_consent.have = FALSE;
4695 nice_debug ("Agent %p : pair %p lost consent for %u/%u (stream/component)",
4696 agent, pair, stream->id, component->id);
4698 /* explicit revocation received, we don't need to time out anymore */
4699 if (pair->remote_consent.tick_source) {
4700 g_source_destroy (pair->remote_consent.tick_source);
4701 g_source_unref (pair->remote_consent.tick_source);
4702 pair->remote_consent.tick_source = NULL;
4705 agent_signal_component_state_change (agent, stream->id, component->id,
4706 NICE_COMPONENT_STATE_FAILED);
4712 username = (uint8_t *) stun_message_find (&req, STUN_ATTRIBUTE_USERNAME,
4715 for (i = component->local_candidates; i; i = i->next) {
4716 NiceCandidate *cand = i->data;
4719 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
4722 addr = &cand->base_addr;
4724 if (nice_address_equal (&nicesock->addr, addr) &&
4725 local_candidate_and_socket_compatible (agent, cand, nicesock)) {
4726 local_candidate = cand;
4731 for (i = component->remote_candidates; i; i = i->next) {
4732 NiceCandidate *cand = i->data;
4733 if (nice_address_equal (from, &cand->addr) &&
4734 remote_candidate_and_socket_compatible (agent, local_candidate,
4736 remote_candidate = cand;
4741 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4742 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4743 agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4744 /* We need to find which local candidate was used */
4745 for (i = component->remote_candidates;
4746 i != NULL && remote_candidate2 == NULL; i = i->next) {
4747 for (j = component->local_candidates; j; j = j->next) {
4748 gboolean inbound = TRUE;
4749 NiceCandidate *rcand = i->data;
4750 NiceCandidate *lcand = j->data;
4752 /* If we receive a response, then the username is local:remote */
4753 if (agent->compatibility != NICE_COMPATIBILITY_MSN) {
4754 if (stun_message_get_class (&req) == STUN_REQUEST ||
4755 stun_message_get_class (&req) == STUN_INDICATION) {
4762 uname_len = priv_create_username (agent, stream,
4763 component->id, rcand, lcand,
4764 uname, sizeof (uname), inbound);
4768 stun_debug ("Comparing usernames of size %d and %d: %d",
4769 username_len, uname_len, username && uname_len == username_len &&
4770 memcmp (username, uname, uname_len) == 0);
4771 stun_debug_bytes (" First username: ", username,
4772 username ? username_len : 0);
4773 stun_debug_bytes (" Second uname: ", uname, uname_len);
4776 uname_len == username_len &&
4777 memcmp (uname, username, username_len) == 0) {
4778 local_candidate = lcand;
4779 remote_candidate2 = rcand;
4786 if (component->remote_candidates &&
4787 agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
4788 local_candidate == NULL &&
4789 discovery_msg == FALSE) {
4790 /* if we couldn't match the username and the stun agent has
4791 IGNORE_CREDENTIALS then we have an integrity check failing.
4792 This could happen with the race condition of receiving connchecks
4793 before the remote candidates are added. Just drop the message, and let
4794 the retransmissions make it work. */
4795 nice_debug ("Agent %p : Username check failed.", agent);
4799 /* This is most likely caused by a second response to a request which
4800 * already has received a valid reply.
4802 if (valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
4803 nice_debug ("Agent %p : Valid STUN response for which we don't have a request, ignoring", agent);
4807 if (valid != STUN_VALIDATION_SUCCESS) {
4808 nice_debug ("Agent %p : STUN message is unsuccessful %d, ignoring", agent, valid);
4812 agent->media_after_tick = TRUE;
4814 if (stun_message_get_class (&req) == STUN_REQUEST) {
4815 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4816 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4817 if (local_candidate && remote_candidate2) {
4820 if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
4821 username = (uint8_t *) stun_message_find (&req,
4822 STUN_ATTRIBUTE_USERNAME, &username_len);
4823 uname_len = priv_create_username (agent, stream,
4824 component->id, remote_candidate2, local_candidate,
4825 uname, sizeof (uname), FALSE);
4826 memcpy (username, uname, MIN (uname_len, username_len));
4828 req.key = g_base64_decode ((gchar *) remote_candidate2->password,
4830 req.key_len = key_len;
4831 } else if (agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4832 req.key = g_base64_decode ((gchar *) local_candidate->password,
4834 req.key_len = key_len;
4837 nice_debug ("Agent %p : received MSN incoming check from unknown remote candidate. "
4838 "Ignoring request", agent);
4843 if (!component->have_local_consent) {
4844 /* RFC 7675: return forbidden to all authenticated requests if we should
4845 * signal lost consent */
4846 nice_debug("Agent %p : returning FORBIDDEN on stream/component %u/%u "
4847 "for lost local consent", agent, stream->id, component->id);
4848 if (stun_agent_init_error (&component->stun_agent, &msg, rbuf, rbuf_len,
4849 &req, STUN_ERROR_FORBIDDEN)) {
4850 rbuf_len = stun_agent_finish_message (&component->stun_agent, &msg, NULL, 0);
4851 if (rbuf_len > 0 && agent->compatibility != NICE_COMPATIBILITY_MSN &&
4852 agent->compatibility != NICE_COMPATIBILITY_OC2007) {
4853 agent_socket_send (nicesock, from, rbuf_len, (const gchar*) rbuf);
4859 rbuf_len = sizeof (rbuf);
4860 res = stun_usage_ice_conncheck_create_reply (&component->stun_agent, &req,
4861 &msg, rbuf, &rbuf_len, &sockaddr.storage, sizeof (sockaddr),
4862 &control, agent->tie_breaker,
4863 agent_to_ice_compatibility (agent));
4865 if ( agent->compatibility == NICE_COMPATIBILITY_MSN
4866 || agent->compatibility == NICE_COMPATIBILITY_OC2007) {
4870 if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT)
4871 priv_check_for_role_conflict (agent, control);
4873 if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
4874 res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
4875 /* case 1: valid incoming request, send a reply/error */
4876 bool use_candidate =
4877 stun_usage_ice_conncheck_use_candidate (&req);
4878 uint32_t priority = stun_usage_ice_conncheck_priority (&req);
4880 if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
4881 agent->compatibility == NICE_COMPATIBILITY_MSN ||
4882 agent->compatibility == NICE_COMPATIBILITY_OC2007)
4883 use_candidate = TRUE;
4885 if (stream->initial_binding_request_received != TRUE)
4886 agent_signal_initial_binding_request_received (agent, stream);
4888 if (remote_candidate == NULL) {
4889 nice_debug ("Agent %p : No matching remote candidate for incoming "
4890 "check -> peer-reflexive candidate.", agent);
4891 remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
4892 agent, stream, component, priority, from, nicesock,
4894 remote_candidate2 ? remote_candidate2 : remote_candidate);
4895 if(remote_candidate && stream->remote_ufrag[0]) {
4896 if (local_candidate &&
4897 local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
4898 priv_conn_check_add_for_candidate_pair_matched (agent,
4899 stream->id, component, local_candidate, remote_candidate,
4900 NICE_CHECK_WAITING);
4902 conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
4906 nice_component_add_valid_candidate (agent, component, remote_candidate);
4908 priv_reply_to_conn_check (agent, stream, component, local_candidate,
4909 remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate);
4911 if (stream->remote_ufrag[0] == 0) {
4912 /* case: We've got a valid binding request to a local candidate
4913 * but we do not yet know remote credentials.
4914 * As per sect 7.2 of ICE (ID-19), we send a reply
4915 * immediately but postpone all other processing until
4916 * we get information about the remote candidates */
4918 /* step: send a reply immediately but postpone other processing */
4919 pending_check = priv_store_pending_check (agent, component, from, nicesock,
4920 username, username_len, priority, use_candidate);
4921 priv_print_conn_check_lists (agent, G_STRFUNC, ", icheck stored");
4924 nice_debug ("Agent %p : Invalid STUN packet, ignoring... %s",
4925 agent, strerror(errno));
4929 /* case 2: not a new request, might be a reply... */
4930 gboolean trans_found = FALSE;
4932 /* note: ICE sect 7.1.2. "Processing the Response" (ID-19) */
4934 /* step: let's try to match the response to an existing check context */
4935 if (trans_found != TRUE)
4936 trans_found = priv_map_reply_to_conn_check_request (agent, stream,
4937 component, nicesock, from, local_candidate, remote_candidate, &req);
4939 /* step: let's try to match the response to an existing discovery */
4940 if (trans_found != TRUE)
4941 trans_found = priv_map_reply_to_discovery_request (agent, &req, from);
4943 /* step: let's try to match the response to an existing turn allocate */
4944 if (trans_found != TRUE)
4945 trans_found = priv_map_reply_to_relay_request (agent, &req);
4947 /* step: let's try to match the response to an existing turn refresh */
4948 if (trans_found != TRUE)
4949 trans_found = priv_map_reply_to_relay_refresh (agent, &req);
4951 if (trans_found != TRUE)
4952 trans_found = priv_map_reply_to_relay_remove (agent, &req);
4954 /* step: let's try to match the response to an existing keepalive conncheck */
4955 if (trans_found != TRUE)
4956 trans_found = priv_map_reply_to_keepalive_conncheck (agent, component,
4959 if (trans_found != TRUE)
4960 nice_debug ("Agent %p : Unable to match to an existing transaction, "
4961 "probably a keepalive.", agent);
4964 /* RENOMINATION attribute support */
4965 conn_check_handle_renomination(agent, stream, component, &req, remote_candidate, local_candidate, pending_check);
4970 /* Remove all pointers to the given @sock from the connection checking process.
4971 * These are entirely NiceCandidates pointed to from various places. */
4973 conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
4977 gboolean pair_failed = FALSE;
4978 gboolean selected_pair_failed = FALSE;
4979 guint p_nominated = 0, p_count = 0;
4981 if (component->selected_pair.local &&
4982 component->selected_pair.local->sockptr == sock) {
4983 nice_debug ("Agent %p: Selected pair socket %p has been destroyed, "
4984 "declaring failed", agent, sock);
4985 selected_pair_failed = TRUE;
4986 if (component->state == NICE_COMPONENT_STATE_READY)
4987 agent_signal_component_state_change (agent,
4988 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
4989 else if (component->state == NICE_COMPONENT_STATE_CONNECTED)
4990 agent_signal_component_state_change (agent,
4991 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
4994 /* Prune from the candidate check pairs. */
4995 for (l = stream->conncheck_list; l != NULL;) {
4996 CandidateCheckPair *p = l->data;
4997 GSList *next = l->next;
4999 if (p->component_id != component->id) {
5004 if (selected_pair_failed && !p->retransmit && p->stun_transactions)
5005 p->retransmit = TRUE;
5007 if ((p->local != NULL && ((NiceCandidateImpl*) p->local)->sockptr == sock) ||
5008 (p->remote != NULL && ((NiceCandidateImpl*)p->remote)->sockptr == sock) ||
5009 (p->sockptr == sock)) {
5010 nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p",
5012 if (component->selected_pair.local == ((NiceCandidateImpl *)p->local) &&
5013 component->selected_pair.remote == ((NiceCandidateImpl *)p->remote))
5014 selected_pair_failed = TRUE;
5015 candidate_check_pair_fail (stream, agent, p);
5016 candidate_check_pair_free (agent, p);
5017 stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l);
5030 agent_signal_component_state_change (agent,
5031 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
5032 else if (p_nominated == 0) {
5033 if (component->state == NICE_COMPONENT_STATE_READY)
5034 agent_signal_component_state_change (agent,
5035 stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
5036 else if (component->state == NICE_COMPONENT_STATE_CONNECTED)
5037 agent_signal_component_state_change (agent,
5038 stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
5042 /* outside of the previous loop, because it may
5043 * remove pairs from the conncheck list
5046 conn_check_update_check_list_state_for_ready (agent, stream, component);