diff --git a/libnice-0.1.14-85-g34d6044.patch b/libnice-0.1.14-85-g34d6044.patch deleted file mode 100644 index 541d010..0000000 --- a/libnice-0.1.14-85-g34d6044.patch +++ /dev/null @@ -1,11963 +0,0 @@ -From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Mon, 3 Apr 2017 14:30:10 -0400 -Subject: [PATCH 01/70] Version 0.1.14.1 - ---- - configure.ac | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 5fabdb4..b39bfe3 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -5,8 +5,8 @@ dnl Always compile with -Wall; if --enable-compile-warnings=error is passed, - dnl also use -Werror. git and pre-releases default to -Werror - - dnl use a three digit version number for releases, and four for cvs/prerelease --AC_INIT([libnice],[0.1.14]) --LIBNICE_RELEASE="yes" -+AC_INIT([libnice],[0.1.14.1]) -+LIBNICE_RELEASE="no" - - AC_CANONICAL_TARGET - --- -2.13.6 - - -From 59ce41dfb837adf4222b25490cde2e394384ad15 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= -Date: Fri, 31 Mar 2017 20:20:38 -0400 -Subject: [PATCH 02/70] conncheck: consider answer received when remote - credentials are set -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Consider that the answer is received when remote credentials -are set instead of when a remote candidate is set, -which could not happen or could cause more delay for the -connection establishment. - -Ported to git master by Olivier Crête - -Differential Revision: https://phabricator.freedesktop.org/D1704 ---- - agent/agent.c | 4 +- - agent/conncheck.c | 225 +++++++++++++++++++++++++++--------------------------- - agent/conncheck.h | 2 +- - 3 files changed, 117 insertions(+), 114 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 555fd16..4d9381c 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3240,6 +3240,8 @@ nice_agent_set_remote_credentials ( - g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG); - g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD); - -+ conn_check_remote_credentials_set(agent, stream); -+ - ret = TRUE; - goto done; - } -@@ -3342,8 +3344,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, - } - } - -- conn_check_remote_candidates_set(agent, stream, component); -- - if (added > 0) { - conn_check_schedule_next (agent); - } -diff --git a/agent/conncheck.c b/agent/conncheck.c -index dda2f2f..2abbc5e 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1243,124 +1243,124 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list) - - /* - * Handle any processing steps for connectivity checks after -- * remote candidates have been set. This function handles -+ * remote credentials have been set. This function handles - * the special case where answerer has sent us connectivity -- * checks before the answer (containing candidate information), -+ * checks before the answer (containing credentials information), - * reaches us. The special case is documented in sect 7.2 - * if ICE spec (ID-19). - */ --void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) -+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - { -- GSList *j, *k, *l, *m, *n; -+ GSList *j, *k, *l, *m, *n, *o; - - for (j = stream->conncheck_list; j ; j = j->next) { - CandidateCheckPair *pair = j->data; -- if (pair->component_id == component->id) { -- gboolean match = FALSE; -- -- /* performn delayed processing of spec steps section 7.2.1.4, -- and section 7.2.1.5 */ -- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -- -- for (k = component->incoming_checks; k; k = k->next) { -- IncomingCheck *icheck = k->data; -- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to -- * be handled separately */ -- for (l = component->remote_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&icheck->from, &cand->addr)) { -- match = TRUE; -- break; -- } -- } -- if (match != TRUE) { -- /* note: we have gotten an incoming connectivity check from -- * an address that is not a known remote candidate */ -- -- NiceCandidate *local_candidate = NULL; -- NiceCandidate *remote_candidate = NULL; -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -- agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- /* We need to find which local candidate was used */ -- uint8_t uname[NICE_STREAM_MAX_UNAME]; -- guint uname_len; -- -- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -- "stored pending check", agent); -- -- for (m = component->remote_candidates; -- m != NULL && remote_candidate == NULL; m = m->next) { -- for (n = component->local_candidates; n; n = n->next) { -- NiceCandidate *rcand = m->data; -- NiceCandidate *lcand = n->data; -- -- uname_len = priv_create_username (agent, stream, -- component->id, rcand, lcand, -- uname, sizeof (uname), TRUE); -- -- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -- icheck->username_len, uname_len, -- icheck->username && uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0); -- stun_debug_bytes (" first username: ", -- icheck->username, -- icheck->username? icheck->username_len : 0); -- stun_debug_bytes (" second username: ", uname, uname_len); -- -- if (icheck->username && -- uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0) { -- local_candidate = lcand; -- remote_candidate = rcand; -- break; -- } -- } -- } -- } else { -- for (l = component->local_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -- local_candidate = cand; -+ NiceComponent *component = -+ nice_stream_find_component_by_id (stream, pair->component_id); -+ gboolean match = FALSE; -+ -+ /* performn delayed processing of spec steps section 7.2.1.4, -+ and section 7.2.1.5 */ -+ priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -+ -+ for (k = component->incoming_checks; k; k = k->next) { -+ IncomingCheck *icheck = k->data; -+ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to -+ * be handled separately */ -+ for (l = component->remote_candidates; l; l = l->next) { -+ NiceCandidate *cand = l->data; -+ if (nice_address_equal (&icheck->from, &cand->addr)) { -+ match = TRUE; -+ break; -+ } -+ } -+ if (match != TRUE) { -+ /* note: we have gotten an incoming connectivity check from -+ * an address that is not a known remote candidate */ -+ -+ NiceCandidate *local_candidate = NULL; -+ NiceCandidate *remote_candidate = NULL; -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -+ agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ /* We need to find which local candidate was used */ -+ uint8_t uname[NICE_STREAM_MAX_UNAME]; -+ guint uname_len; -+ -+ nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -+ "stored pending check", agent); -+ -+ for (m = component->remote_candidates; -+ m != NULL && remote_candidate == NULL; m = m->next) { -+ for (n = component->local_candidates; n; n = n->next) { -+ NiceCandidate *rcand = m->data; -+ NiceCandidate *lcand = n->data; -+ -+ uname_len = priv_create_username (agent, stream, -+ component->id, rcand, lcand, -+ uname, sizeof (uname), TRUE); -+ -+ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -+ icheck->username_len, uname_len, -+ icheck->username && uname_len == icheck->username_len && -+ memcmp (uname, icheck->username, icheck->username_len) == 0); -+ stun_debug_bytes (" first username: ", -+ icheck->username, -+ icheck->username? icheck->username_len : 0); -+ stun_debug_bytes (" second username: ", uname, uname_len); -+ -+ if (icheck->username && -+ uname_len == icheck->username_len && -+ memcmp (uname, icheck->username, icheck->username_len) == 0) { -+ local_candidate = lcand; -+ remote_candidate = rcand; - break; - } - } - } -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -- local_candidate == NULL) { -- /* if we couldn't match the username, then the matching remote -- * candidate hasn't been received yet.. we must wait */ -- nice_debug ("Agent %p : Username check failed. pending check has " -- "to wait to be processed", agent); -- } else { -- NiceCandidate *candidate; -- -- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -- agent); -- candidate = -- discovery_learn_remote_peer_reflexive_candidate (agent, -- stream, -- component, -- icheck->priority, -- &icheck->from, -- icheck->local_socket, -- local_candidate, remote_candidate); -- if (candidate) { -- if (local_candidate && -- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -- priv_conn_check_add_for_candidate_pair_matched (agent, -- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -- else -- conn_check_add_for_candidate (agent, stream->id, component, candidate); -- -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ } else { -+ for (l = component->local_candidates; l; l = l->next) { -+ NiceCandidate *cand = l->data; -+ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -+ local_candidate = cand; -+ break; - } - } - } -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -+ local_candidate == NULL) { -+ /* if we couldn't match the username, then the matching remote -+ * candidate hasn't been received yet.. we must wait */ -+ nice_debug ("Agent %p : Username check failed. pending check has " -+ "to wait to be processed", agent); -+ } else { -+ NiceCandidate *candidate; -+ -+ nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -+ agent); -+ candidate = -+ discovery_learn_remote_peer_reflexive_candidate (agent, -+ stream, -+ component, -+ icheck->priority, -+ &icheck->from, -+ icheck->local_socket, -+ local_candidate, remote_candidate); -+ if (candidate) { -+ if (local_candidate && -+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -+ priv_conn_check_add_for_candidate_pair_matched (agent, -+ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -+ else -+ conn_check_add_for_candidate (agent, stream->id, component, candidate); -+ -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -+ priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ } -+ } - } - } - } -@@ -1368,9 +1368,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice - /* Once we process the pending checks, we should free them to avoid - * reprocessing them again if a dribble-mode set_remote_candidates - * is called */ -- g_slist_free_full (component->incoming_checks, -- (GDestroyNotify) incoming_check_free); -- component->incoming_checks = NULL; -+ for (o = stream->components; o; o = o->next) { -+ NiceComponent *component = o->data; -+ g_slist_free_full (component->incoming_checks, -+ (GDestroyNotify) incoming_check_free); -+ component->incoming_checks = NULL; -+ } - - stream->conncheck_list = - prune_cancelled_conn_check (stream->conncheck_list); -@@ -3628,14 +3631,14 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - if (stream->initial_binding_request_received != TRUE) - agent_signal_initial_binding_request_received (agent, stream); - -- if (component->remote_candidates && remote_candidate == NULL) { -+ if (remote_candidate == NULL) { - nice_debug ("Agent %p : No matching remote candidate for incoming check ->" - "peer-reflexive candidate.", agent); - remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( - agent, stream, component, priority, from, nicesock, - local_candidate, - remote_candidate2 ? remote_candidate2 : remote_candidate); -- if(remote_candidate) { -+ if(remote_candidate && stream->remote_ufrag != NULL) { - if (local_candidate && - local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { - CandidateCheckPair *pair; -@@ -3654,10 +3657,10 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -- if (component->remote_candidates == NULL) { -+ if (stream->remote_ufrag == NULL) { - /* case: We've got a valid binding request to a local candidate -- * but we do not yet know remote credentials nor -- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply -+ * but we do not yet know remote credentials. -+ * As per sect 7.2 of ICE (ID-19), we send a reply - * immediately but postpone all other processing until - * we get information about the remote candidates */ - -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 431c606..10319cc 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -105,7 +105,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); - void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); - gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); - gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); --void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); -+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream); - NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); - void - conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, --- -2.13.6 - - -From 0de1657e4d15d4f1911ab1fad84ea23e7013070f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 12:25:50 -0400 -Subject: [PATCH 03/70] conncheck: Use the right test for empty remote_frag - -It's now an array, not a pointer, so needs to test to emptyness. - -It's a bugfix on the previous commit, 59ce41df ---- - agent/conncheck.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 2abbc5e..7096b42 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3638,7 +3638,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - agent, stream, component, priority, from, nicesock, - local_candidate, - remote_candidate2 ? remote_candidate2 : remote_candidate); -- if(remote_candidate && stream->remote_ufrag != NULL) { -+ if(remote_candidate && stream->remote_ufrag[0]) { - if (local_candidate && - local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { - CandidateCheckPair *pair; -@@ -3657,7 +3657,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -- if (stream->remote_ufrag == NULL) { -+ if (stream->remote_ufrag[0] == 0) { - /* case: We've got a valid binding request to a local candidate - * but we do not yet know remote credentials. - * As per sect 7.2 of ICE (ID-19), we send a reply --- -2.13.6 - - -From 0672758b9621801c8f0d9e3c920370983b267a68 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 12:29:29 -0400 -Subject: [PATCH 04/70] agent: Don't set variable that won't be used - -It exits the loop immediately, so no point to set the variable. -And it makes the clang static analyzer happy. ---- - agent/agent.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 4d9381c..8ba99bc 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -4245,7 +4245,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - "Component removed during call."); - - component = NULL; -- error_reported = TRUE; - - goto recv_error; - } --- -2.13.6 - - -From db05e8b0fdc713df93cd6a4c3914e5aee38b2391 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 12:30:27 -0400 -Subject: [PATCH 05/70] Make clang-analyzer happy - -Various little things, none of which should make a functional difference. ---- - agent/agent.c | 1 - - agent/conncheck.c | 2 +- - socket/udp-turn.c | 5 ++--- - stun/usages/bind.c | 4 +++- - 4 files changed, 6 insertions(+), 6 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 8ba99bc..28d7ccf 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -1552,7 +1552,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, - - if (len == 0) { - /* Reached EOS. */ -- len = 0; - goto done; - } else if (len < 0 && - pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) { -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 7096b42..1dc13dd 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -606,7 +606,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - /* step: when there's no pair in the Waiting state, - * unfreeze a new pair and check it - */ -- res = priv_conn_check_unfreeze_next (agent); -+ priv_conn_check_unfreeze_next (agent); - - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index cc3409b..a9c57e5 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock, - - /* Split up the monolithic buffer again into the caller-provided buffers. */ - if (parsed_buffer_length > 0 && allocated_buffer) { -- parsed_buffer_length = -- memcpy_buffer_to_input_message (message, buffer, -- parsed_buffer_length); -+ memcpy_buffer_to_input_message (message, buffer, -+ parsed_buffer_length); - } - - if (allocated_buffer) -diff --git a/stun/usages/bind.c b/stun/usages/bind.c -index 8dd7afc..d56790f 100644 ---- a/stun/usages/bind.c -+++ b/stun/usages/bind.c -@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - size_t len; - StunUsageTransReturn ret; - int val; -- struct sockaddr_storage alternate_server; -+ struct sockaddr_storage alternate_server = { AF_UNSPEC } ; - socklen_t alternate_server_len = sizeof (alternate_server); - StunUsageBindReturn bind_ret; - -@@ -548,6 +548,8 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) { - stun_trans_deinit (&trans); - -+ assert (alternate_server.ss_family != AF_UNSPEC); -+ - ret = stun_trans_create (&trans, SOCK_DGRAM, 0, - (struct sockaddr *) &alternate_server, alternate_server_len); - --- -2.13.6 - - -From cd255bddc7fa0ddae056b5358a22b380c4eefc42 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 15:24:43 -0400 -Subject: [PATCH 06/70] udp-turn: Add some const to internal APIs - ---- - agent/component.c | 2 +- - socket/udp-turn.c | 10 +++++----- - socket/udp-turn.h | 7 ++++--- - 3 files changed, 10 insertions(+), 9 deletions(-) - -diff --git a/agent/component.c b/agent/component.c -index 32f7463..a679b30 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -380,7 +380,7 @@ nice_component_restart (NiceComponent *cmp) - for (i = cmp->remote_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; - -- /* note: do not remove the local candidate that is -+ /* note: do not remove the remote candidate that is - * currently part of the 'selected pair', see ICE - * 9.1.1.1. "ICE Restarts" (ID-19) */ - if (candidate == cmp->selected_pair.remote) { -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index a9c57e5..190a9ea 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data) - - NiceSocket * - nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, -- NiceSocket *base_socket, NiceAddress *server_addr, -- gchar *username, gchar *password, -+ NiceSocket *base_socket, const NiceAddress *server_addr, -+ const gchar *username, const gchar *password, - NiceTurnSocketCompatibility compatibility) - { - UdpTurnPriv *priv; -@@ -1184,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - gsize - nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - NiceAddress *from, gsize len, guint8 *buf, -- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len) -+ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len) - { - - UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv; -@@ -1194,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - ChannelBinding *binding = NULL; - - union { -- guint8 *u8; -- guint16 *u16; -+ const guint8 *u8; -+ const guint16 *u16; - } recv_buf; - - /* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN) -diff --git a/socket/udp-turn.h b/socket/udp-turn.h -index b1eeeb4..df10a1c 100644 ---- a/socket/udp-turn.h -+++ b/socket/udp-turn.h -@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - gsize - nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - NiceAddress *from, gsize len, guint8 *buf, -- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len); -+ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len); - - gboolean - nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer); - - NiceSocket * - nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, -- NiceSocket *base_socket, NiceAddress *server_addr, -- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility); -+ NiceSocket *base_socket, const NiceAddress *server_addr, -+ const gchar *username, const gchar *password, -+ NiceTurnSocketCompatibility compatibility); - - void - nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); --- -2.13.6 - - -From e56b910d2d8b70f5677bbd4be579d5b95aff33ad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 16:16:05 -0400 -Subject: [PATCH 07/70] agent: Separate return from NiceSocket and internal - enum - -The same variable was used for return values from NiceSocket and -for the internal enum, but 0 and -1 have different meanings in both. ---- - agent/agent.c | 35 +++++++++++++++++++---------------- - 1 file changed, 19 insertions(+), 16 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 28d7ccf..7b8a9fc 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3422,7 +3422,8 @@ agent_recv_message_unlocked ( - { - NiceAddress from; - GList *item; -- gint retval; -+ RecvStatus retval; -+ gint sockret; - gboolean is_turn = FALSE; - - /* We need an address for packet parsing, below. */ -@@ -3483,8 +3484,8 @@ agent_recv_message_unlocked ( - local_bufs[i + 1].buffer = message->buffers[i].buffer; - local_bufs[i + 1].size = message->buffers[i].size; - } -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - message->length = ntohs (rfc4571_frame); - } - } else { -@@ -3499,7 +3500,7 @@ agent_recv_message_unlocked ( - _priv_set_socket_tos (agent, new_socket, stream->tos); - nice_component_attach_socket (component, new_socket); - } -- retval = 0; -+ sockret = 0; - } else { - /* In the case of a real ICE-TCP connection, we can use the socket as a - * bytestream and do the read here with caching of data being read -@@ -3508,9 +3509,9 @@ agent_recv_message_unlocked ( - - /* TODO: Support bytestream reads */ - message->length = 0; -- retval = 0; -+ sockret = 0; - if (available <= 0) { -- retval = available; -+ sockret = available; - - /* If we don't call check_connect_result on an outbound connection, - * then is_connected will always return FALSE. That's why we check -@@ -3523,7 +3524,7 @@ agent_recv_message_unlocked ( - * not connected, it means that it failed to connect, so we must - * return an error to make the socket fail/closed - */ -- retval = -1; -+ sockret = -1; - } else { - gint flags = G_SOCKET_MSG_PEEK; - -@@ -3536,7 +3537,7 @@ agent_recv_message_unlocked ( - */ - if (g_socket_receive_message (nicesock->fileno, NULL, - NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0) -- retval = -1; -+ sockret = -1; - } - } else if (agent->rfc4571_expecting_length == 0) { - if ((gsize) available >= sizeof(guint16)) { -@@ -3544,8 +3545,8 @@ agent_recv_message_unlocked ( - GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)}; - NiceInputMessage local_message = { &local_buf, 1, message->from, 0}; - -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - agent->rfc4571_expecting_length = ntohs (rfc4571_frame); - available = g_socket_get_available_bytes (nicesock->fileno); - } -@@ -3589,8 +3590,8 @@ agent_recv_message_unlocked ( - off += local_bufs[i].size; - } - } -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - message->length = local_message.length; - agent->rfc4571_expecting_length -= local_message.length; - } -@@ -3598,20 +3599,22 @@ agent_recv_message_unlocked ( - } - } - } else { -- retval = nice_socket_recv_messages (nicesock, message, 1); -+ sockret = nice_socket_recv_messages (nicesock, message, 1); - } - -- if (retval == 0) { -+ if (sockret == 0) { - retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ - nice_debug_verbose ("%s: Agent %p: no message available on read attempt", - G_STRFUNC, agent); - goto done; -- } else if (retval < 0) { -+ } else if (sockret < 0) { - nice_debug ("Agent %p: %s returned %d, errno (%d) : %s", -- agent, G_STRFUNC, retval, errno, g_strerror (errno)); -+ agent, G_STRFUNC, sockret, errno, g_strerror (errno)); - - retval = RECV_ERROR; - goto done; -+ } else { -+ retval = sockret; - } - - if (retval == RECV_OOB || message->length == 0) { --- -2.13.6 - - -From 4e605885c9dcaeb3ee443ec902c9c9189b19043f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 16:16:46 -0400 -Subject: [PATCH 08/70] agent: Remove impossible case - ---- - agent/agent.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 7b8a9fc..77f27e3 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3617,7 +3617,8 @@ agent_recv_message_unlocked ( - retval = sockret; - } - -- if (retval == RECV_OOB || message->length == 0) { -+ g_assert (retval != RECV_OOB); -+ if (message->length == 0) { - retval = RECV_OOB; - nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC, - agent); --- -2.13.6 - - -From efc6a9be8cb34c899f0454c32e8a1e62b38df474 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 18:42:57 -0400 -Subject: [PATCH 09/70] tests: Use automake test-driver for valgrind - -This fixes the valgrind integration with the new test drivers. ---- - common.mk | 4 +- - scripts/valgrind-test-driver | 162 +++++++++++++++++++++++++++++++++++++++++++ - scripts/valgrind.sh | 28 -------- - 3 files changed, 165 insertions(+), 29 deletions(-) - create mode 100755 scripts/valgrind-test-driver - delete mode 100755 scripts/valgrind.sh - -diff --git a/common.mk b/common.mk -index e2ca3f4..b16380d 100644 ---- a/common.mk -+++ b/common.mk -@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice - - - check-valgrind: -- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check -+ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check -+ -+LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver - - .PHONY: check-valgrind -diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver -new file mode 100755 -index 0000000..5b660ee ---- /dev/null -+++ b/scripts/valgrind-test-driver -@@ -0,0 +1,162 @@ -+#! /bin/sh -+# test-driver - basic testsuite driver script. -+ -+scriptversion=2017-04-04.22; # UTC -+ -+# Copyright (C) 2011-2014 Free Software Foundation, Inc. -+# -+# This program is free software; you can redistribute it and/or modify -+# it under the terms of the GNU General Public License as published by -+# the Free Software Foundation; either version 2, or (at your option) -+# any later version. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see . -+ -+# As a special exception to the GNU General Public License, if you -+# distribute this file as part of a program that contains a -+# configuration script generated by Autoconf, you may include it under -+# the same distribution terms that you use for the rest of that program. -+ -+# This file is maintained in Automake, please report -+# bugs to or send patches to -+# . -+ -+# Make unconditional expansion of undefined variables an error. This -+# helps a lot in preventing typo-related bugs. -+set -u -+ -+usage_error () -+{ -+ echo "$0: $*" >&2 -+ print_usage >&2 -+ exit 2 -+} -+ -+print_usage () -+{ -+ cat <$log_file 2>&1 -+else -+ "$@" >$log_file 2>&1 -+fi -+estatus=$? -+ -+if test $enable_hard_errors = no && test $estatus -eq 99; then -+ tweaked_estatus=1 -+else -+ tweaked_estatus=$estatus -+fi -+ -+case $tweaked_estatus:$expect_failure in -+ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; -+ 0:*) col=$grn res=PASS recheck=no gcopy=no;; -+ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; -+ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; -+ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; -+ *:*) col=$red res=FAIL recheck=yes gcopy=yes;; -+esac -+ -+# Report the test outcome and exit status in the logs, so that one can -+# know whether the test passed or failed simply by looking at the '.log' -+# file, without the need of also peaking into the corresponding '.trs' -+# file (automake bug#11814). -+echo "$res $test_name (exit status: $estatus)" >>$log_file -+ -+# Report outcome to console. -+echo "${col}${res}${std}: $test_name" -+ -+# Register the test result, and other relevant metadata. -+echo ":test-result: $res" > $trs_file -+echo ":global-test-result: $res" >> $trs_file -+echo ":recheck: $recheck" >> $trs_file -+echo ":copy-in-global-log: $gcopy" >> $trs_file -+ -+# Local Variables: -+# mode: shell-script -+# sh-indentation: 2 -+# eval: (add-hook 'write-file-hooks 'time-stamp) -+# time-stamp-start: "scriptversion=" -+# time-stamp-format: "%:y-%02m-%02d.%02H" -+# time-stamp-time-zone: "UTC" -+# time-stamp-end: "; # UTC" -+# End: -diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh -deleted file mode 100755 -index 2864b6f..0000000 ---- a/scripts/valgrind.sh -+++ /dev/null -@@ -1,28 +0,0 @@ --#!/bin/sh -- --export G_SLICE=always-malloc --export G_DEBUG=gc-friendly -- --tests_dir="`dirname $0`/../tests" -- --report=`libtool --mode=execute valgrind \ -- --leak-check=full \ -- --show-reachable=no \ -- --error-exitcode=1 \ -- --suppressions=$tests_dir/libnice.supp \ -- --num-callers=30 \ -- $1 2>&1` -- --#if echo "$report" | grep -q ==; then --if test $? != 0; then -- echo "$report" -- exit 1 --fi -- --if echo "$report" | grep -q "definitely lost"; then -- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then -- echo "$report" -- exit 1 -- fi --fi -- --- -2.13.6 - - -From ae6d939e48366b80570d713b83334191b0982e71 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 20:34:05 -0400 -Subject: [PATCH 10/70] debug: Use libnice-verbose, not libnice-nice-verbose - ---- - agent/debug.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/debug.c b/agent/debug.c -index e1a298c..84fee94 100644 ---- a/agent/debug.c -+++ b/agent/debug.c -@@ -102,7 +102,7 @@ void nice_debug_init (void) - flags |= g_parse_debug_string (gflags_string, gkeys, 4); - if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) - flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; -- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { -+ if (gflags_string && strstr (gflags_string, "libnice-verbose")) { - flags |= NICE_DEBUG_NICE_VERBOSE; - } - --- -2.13.6 - - -From 10c557f23f8337f1304fff27bd85d2eb713cb249 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Wed, 5 Apr 2017 17:01:35 -0400 -Subject: [PATCH 11/70] test-credentials: Fix leak - ---- - tests/test-credentials.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/tests/test-credentials.c b/tests/test-credentials.c -index 1de4e49..f1ea89d 100644 ---- a/tests/test-credentials.c -+++ b/tests/test-credentials.c -@@ -184,6 +184,8 @@ int main (void) - nice_agent_get_local_credentials (lagent, 1, &ufrag, &password); - g_assert (g_strcmp0("unicorns", ufrag) == 0); - g_assert (g_strcmp0("awesome", password) == 0); -+ g_free (ufrag); -+ g_free (password); - - nice_agent_gather_candidates (lagent, 1); - nice_agent_gather_candidates (ragent, 1); --- -2.13.6 - - -From 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 14:41:51 -0400 -Subject: [PATCH 12/70] candidate: Add equality check function - -Add a function that can check if two candidates point to the same place. - -https://phabricator.freedesktop.org/T104 - -Reviewed-by: Philip Withnall -Differential Revision: https://phabricator.freedesktop.org/D1715 ---- - agent/candidate.c | 11 +++++++++++ - agent/candidate.h | 18 +++++++++++++++++- - docs/reference/libnice/libnice-docs.xml | 4 ++++ - docs/reference/libnice/libnice-sections.txt | 1 + - nice/libnice.sym | 1 + - 5 files changed, 34 insertions(+), 1 deletion(-) - -diff --git a/agent/candidate.c b/agent/candidate.c -index 27966ef..85f8f65 100644 ---- a/agent/candidate.c -+++ b/agent/candidate.c -@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate) - - return copy; - } -+ -+NICEAPI_EXPORT gboolean -+nice_candidate_equal_target (const NiceCandidate *candidate1, -+ const NiceCandidate *candidate2) -+{ -+ g_return_val_if_fail (candidate1 != NULL, FALSE); -+ g_return_val_if_fail (candidate2 != NULL, FALSE); -+ -+ return (candidate1->transport == candidate2->transport && -+ nice_address_equal (&candidate1->addr, &candidate2->addr)); -+} -diff --git a/agent/candidate.h b/agent/candidate.h -index fadfce3..e556c16 100644 ---- a/agent/candidate.h -+++ b/agent/candidate.h -@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate); - NiceCandidate * - nice_candidate_copy (const NiceCandidate *candidate); - --GType nice_candidate_get_type (void); -+/** -+ * nice_candidate_equal_target: -+ * @candidate1: A candidate -+ * @candidate2: A candidate -+ * -+ * Verifies that the candidates point to the same place, meaning they have -+ * the same transport and the same address. It ignores all other aspects. -+ * -+ * Returns: %TRUE if the candidates point to the same place -+ * -+ * Since: 0.1.15 -+ */ -+gboolean -+nice_candidate_equal_target (const NiceCandidate *candidate1, -+ const NiceCandidate *candidate2); -+ -+ GType nice_candidate_get_type (void); - - /** - * NICE_TYPE_CANDIDATE: -diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml -index 53487bc..391be1a 100644 ---- a/docs/reference/libnice/libnice-docs.xml -+++ b/docs/reference/libnice/libnice-docs.xml -@@ -105,6 +105,10 @@ - Index of new symbols in 0.1.14 - - -+ -+ Index of new symbols in 0.1.15 -+ -+ - - - -diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt -index d377257..88a6cd2 100644 ---- a/docs/reference/libnice/libnice-sections.txt -+++ b/docs/reference/libnice/libnice-sections.txt -@@ -76,6 +76,7 @@ NICE_CANDIDATE_MAX_FOUNDATION - nice_candidate_new - nice_candidate_free - nice_candidate_copy -+nice_candidate_equal_target - - NICE_TYPE_CANDIDATE - nice_candidate_get_type -diff --git a/nice/libnice.sym b/nice/libnice.sym -index b04bb95..1e522ad 100644 ---- a/nice/libnice.sym -+++ b/nice/libnice.sym -@@ -58,6 +58,7 @@ nice_agent_set_software - nice_agent_set_stream_name - nice_agent_set_stream_tos - nice_candidate_copy -+nice_candidate_equal_target - nice_candidate_free - nice_candidate_new - nice_component_state_to_string --- -2.13.6 - - -From ffc7fddac42728bac6e4753a17bc52e5e610ae8b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 4 Apr 2017 21:27:39 -0400 -Subject: [PATCH 13/70] agent: Drop packets not from validated addresses - -This is required by the WebRTC spec. - -Remove test-mainloop as it doesnt even try to do -a negotiation. - -https://phabricator.freedesktop.org/T104 - -Differential Revision: https://phabricator.freedesktop.org/D1716 ---- - agent/agent-priv.h | 2 + - agent/agent.c | 20 +++++++++- - agent/component.c | 90 +++++++++++++++++++++++++++++++++++++++++ - agent/component.h | 10 +++++ - agent/conncheck.c | 8 +++- - tests/Makefile.am | 1 - - tests/test-mainloop.c | 108 -------------------------------------------------- - 7 files changed, 127 insertions(+), 112 deletions(-) - delete mode 100644 tests/test-mainloop.c - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 4d8c9b8..ada3630 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -114,6 +114,8 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - * MTU and estimated typical sizes of ICE STUN packet */ - #define MAX_STUN_DATAGRAM_PAYLOAD 1300 - -+#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -diff --git a/agent/agent.c b/agent/agent.c -index 77f27e3..eff62f0 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3682,8 +3682,6 @@ agent_recv_message_unlocked ( - if (retval == RECV_OOB) - goto done; - -- agent->media_after_tick = TRUE; -- - /* If the message’s stated length is equal to its actual length, it’s probably - * a STUN message; otherwise it’s probably data. */ - if (stun_message_validate_buffer_length_fast ( -@@ -3715,6 +3713,7 @@ agent_recv_message_unlocked ( - nice_debug ("%s: Valid STUN packet received.", G_STRFUNC); - retval = RECV_OOB; - g_free (big_buf); -+ agent->media_after_tick = TRUE; - goto done; - } - } -@@ -3725,6 +3724,23 @@ agent_recv_message_unlocked ( - g_free (big_buf); - } - -+ if (!nice_component_verify_remote_candidate (component, -+ message->from, nicesock)) { -+ if (nice_debug_is_verbose ()) { -+ gchar str[INET6_ADDRSTRLEN]; -+ -+ nice_address_to_string (message->from, str); -+ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" -+ " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, -+ nice_address_get_port (message->from), nicesock->type); -+ } -+ -+ retval = RECV_OOB; -+ goto done; -+ } -+ -+ agent->media_after_tick = TRUE; -+ - /* Unhandled STUN; try handling TCP data, then pass to the client. */ - if (message->length > 0 && agent->reliable) { - if (!nice_socket_is_reliable (nicesock) && -diff --git a/agent/component.c b/agent/component.c -index a679b30..ba28ffa 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -435,6 +435,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa - component->selected_pair.remote = pair->remote; - component->selected_pair.priority = pair->priority; - component->selected_pair.prflx_priority = pair->prflx_priority; -+ -+ nice_component_add_valid_candidate (component, pair->remote); - } - - /* -@@ -514,6 +516,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, - component->selected_pair.remote = remote; - component->selected_pair.priority = priority; - -+ /* Get into fallback mode where packets from any source is accepted once -+ * this has been called. This is the expected behavior of pre-ICE SIP. -+ */ -+ component->fallback_mode = TRUE; -+ - return local; - } - -@@ -1107,6 +1114,9 @@ nice_component_finalize (GObject *obj) - g_warn_if_fail (cmp->remote_candidates == NULL); - g_warn_if_fail (cmp->incoming_checks == NULL); - -+ g_list_free_full (cmp->valid_candidates, -+ (GDestroyNotify) nice_candidate_free); -+ - g_clear_object (&cmp->tcp); - g_clear_object (&cmp->stop_cancellable); - g_clear_object (&cmp->iostream); -@@ -1421,3 +1431,83 @@ turn_server_unref (TurnServer *turn) - g_slice_free (TurnServer, turn); - } - } -+ -+void -+nice_component_add_valid_candidate (NiceComponent *component, -+ const NiceCandidate *candidate) -+{ -+ guint count = 0; -+ GList *item, *last = NULL; -+ -+ for (item = component->valid_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ last = item; -+ count++; -+ if (nice_candidate_equal_target (cand, candidate)) -+ return; -+ } -+ -+ /* New candidate */ -+ -+ if (nice_debug_is_enabled ()) { -+ char str[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&candidate->addr, str); -+ nice_debug ("Agent %p : %d:%d Adding valid source" -+ " candidate: %s:%d trans: %d\n", component->agent, -+ candidate->stream_id, candidate->component_id, str, -+ nice_address_get_port (&candidate->addr), candidate->transport); -+ } -+ -+ component->valid_candidates = g_list_prepend ( -+ component->valid_candidates, nice_candidate_copy (candidate)); -+ -+ /* Delete the last one to make sure we don't have a list that is too long, -+ * the candidates are not freed on ICE restart as this would be more complex, -+ * we just keep the list not too long. -+ */ -+ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) { -+ NiceCandidate *cand = last->data; -+ -+ component->valid_candidates = g_list_delete_link ( -+ component->valid_candidates, last); -+ nice_candidate_free (cand); -+ } -+} -+ -+gboolean -+nice_component_verify_remote_candidate (NiceComponent *component, -+ const NiceAddress *address, NiceSocket *nicesock) -+{ -+ GList *item; -+ -+ if (component->fallback_mode) -+ return TRUE; -+ -+ for (item = component->valid_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD && -+ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && -+ nice_address_equal (address, &cand->addr)) { -+ /* fast return if it's already the first */ -+ if (item == component->valid_candidates) -+ return TRUE; -+ -+ /* Put the current candidate at the top so that in the normal use-case, -+ * this function becomes O(1). -+ */ -+ component->valid_candidates = g_list_remove_link ( -+ component->valid_candidates, item); -+ component->valid_candidates = g_list_concat (item, -+ component->valid_candidates); -+ -+ return TRUE; -+ } -+ } -+ -+ return FALSE; -+} -diff --git a/agent/component.h b/agent/component.h -index 6712794..a8a1222 100644 ---- a/agent/component.h -+++ b/agent/component.h -@@ -159,12 +159,14 @@ struct _NiceComponent { - NiceComponentState state; - GSList *local_candidates; /* list of NiceCandidate objs */ - GSList *remote_candidates; /* list of NiceCandidate objs */ -+ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */ - GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */ - guint socket_sources_age; /* incremented when socket_sources changes */ - GSList *incoming_checks; /* list of IncomingCheck objs */ - GList *turn_servers; /* List of TurnServer objs */ - CandidatePair selected_pair; /* independent from checklists, - see ICE 11.1. "Sending Media" (ID-19) */ -+ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */ - NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */ - NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */ - /* I/O handling. The main context must always be non-NULL, and is used for all -@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn); - void - turn_server_unref (TurnServer *turn); - -+void -+nice_component_add_valid_candidate (NiceComponent *component, -+ const NiceCandidate *candidate); -+ -+gboolean -+nice_component_verify_remote_candidate (NiceComponent *component, -+ const NiceAddress *address, NiceSocket *nicesock); -+ - - G_END_DECLS - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 1dc13dd..7ffa3db 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2627,6 +2627,7 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); -+ nice_component_add_valid_candidate (component, remote_candidate); - } - else { - if (!local_cand) { -@@ -2652,8 +2653,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - - /* note: this is same as "adding to VALID LIST" in the spec - text */ -- if (new_pair) -+ if (new_pair) { - new_pair->valid = TRUE; -+ nice_component_add_valid_candidate (component, remote_candidate); -+ } - - return new_pair; - } -@@ -2739,6 +2742,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - nice_debug ("Agent %p : Mapped address not found." - " conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); -+ nice_component_add_valid_candidate (component, p->remote); - } else { - ok_pair = priv_process_response_check_for_reflexive (agent, - stream, component, p, sockptr, &sockaddr.addr, -@@ -3654,6 +3658,8 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - } - } - -+ nice_component_add_valid_candidate (component, remote_candidate); -+ - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 7bfe075..d24a2aa 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -45,7 +45,6 @@ check_PROGRAMS = \ - test-send-recv \ - test-socket-is-based-on \ - test-priority \ -- test-mainloop \ - test-fullmode \ - test-restart \ - test-fallback \ -diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c -deleted file mode 100644 -index 7c52daa..0000000 ---- a/tests/test-mainloop.c -+++ /dev/null -@@ -1,108 +0,0 @@ --/* -- * This file is part of the Nice GLib ICE library. -- * -- * (C) 2006, 2007 Collabora Ltd. -- * Contact: Dafydd Harries -- * (C) 2006, 2007 Nokia Corporation. All rights reserved. -- * Contact: Kai Vehmanen -- * -- * The contents of this file are subject to the Mozilla Public License Version -- * 1.1 (the "License"); you may not use this file except in compliance with -- * the License. You may obtain a copy of the License at -- * http://www.mozilla.org/MPL/ -- * -- * Software distributed under the License is distributed on an "AS IS" basis, -- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -- * for the specific language governing rights and limitations under the -- * License. -- * -- * The Original Code is the Nice GLib ICE library. -- * -- * The Initial Developers of the Original Code are Collabora Ltd and Nokia -- * Corporation. All Rights Reserved. -- * -- * Contributors: -- * Dafydd Harries, Collabora Ltd. -- * Kai Vehmanen, Nokia -- * -- * Alternatively, the contents of this file may be used under the terms of the -- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -- * case the provisions of LGPL are applicable instead of those above. If you -- * wish to allow use of your version of this file only under the terms of the -- * LGPL and not to allow others to use your version of this file under the -- * MPL, indicate your decision by deleting the provisions above and replace -- * them with the notice and other provisions required by the LGPL. If you do -- * not delete the provisions above, a recipient may use your version of this -- * file under either the MPL or the LGPL. -- */ --#ifdef HAVE_CONFIG_H --# include --#endif -- --#include -- --#include --#include "socket/socket.h" -- --static GMainLoop *loop = NULL; -- --static void --recv_cb ( -- NiceAgent *agent, -- guint stream_id, -- guint component_id, -- guint len, -- gchar *buf, -- gpointer data) --{ -- g_assert (agent != NULL); -- g_assert (stream_id == 1); -- g_assert (component_id == 1); -- g_assert (len == 6); -- g_assert (0 == strncmp (buf, "\x80hello", len)); -- g_assert (42 == GPOINTER_TO_UINT (data)); -- g_main_loop_quit (loop); --} -- --int --main (void) --{ -- NiceAgent *agent; -- NiceAddress addr; -- guint stream; -- -- nice_address_init (&addr); -- -- loop = g_main_loop_new (NULL, FALSE); -- -- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245); -- nice_address_set_ipv4 (&addr, 0x7f000001); -- nice_agent_add_local_address (agent, &addr); -- stream = nice_agent_add_stream (agent, 1); -- nice_agent_gather_candidates (agent, stream); -- -- // attach to default main context -- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP, -- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42)); -- -- { -- NiceCandidate *candidate; -- GSList *candidates, *i; -- -- candidates = nice_agent_get_local_candidates (agent, 1, 1); -- candidate = candidates->data; -- -- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello"); -- for (i = candidates; i; i = i->next) -- nice_candidate_free ((NiceCandidate *) i->data); -- g_slist_free (candidates); -- } -- -- g_main_loop_run (loop); -- -- nice_agent_remove_stream (agent, stream); -- g_object_unref (agent); -- -- return 0; --} -- --- -2.13.6 - - -From 8fc22b0034d04cbc222e0637152b1cee2879eef3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Wed, 5 Apr 2017 17:43:26 -0400 -Subject: [PATCH 14/70] tests_: Add test to verify that only packets from - validated addresses pass - -https://phabricator.freedesktop.org/T104 - -Reviewed-by: Philip Withnall -Differential Revision: https://phabricator.freedesktop.org/D1717 ---- - tests/Makefile.am | 5 +- - tests/test-drop-invalid.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 521 insertions(+), 1 deletion(-) - create mode 100644 tests/test-drop-invalid.c - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index d24a2aa..62d5d64 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -54,7 +54,8 @@ check_PROGRAMS = \ - test-tcp \ - test-icetcp \ - test-credentials \ -- test-turn -+ test-turn \ -+ test-drop-invalid - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -128,6 +129,8 @@ test_credentials_LDADD = $(COMMON_LDADD) - - test_turn_LDADD = $(COMMON_LDADD) - -+test_drop_invalid_LDADD = $(COMMON_LDADD) -+ - test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) - test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) - -diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c -new file mode 100644 -index 0000000..97e3586 ---- /dev/null -+++ b/tests/test-drop-invalid.c -@@ -0,0 +1,517 @@ -+/* -+ * This file is part of the Nice GLib ICE library. -+ * -+ * Unit test for ICE full-mode related features. -+ * -+ * (C) 2007 Nokia Corporation. All rights reserved. -+ * Contact: Kai Vehmanen -+ * (C) 2017 Collabora Ltd -+ * Contact: Olivier Crete -+ * -+ * The contents of this file are subject to the Mozilla Public License Version -+ * 1.1 (the "License"); you may not use this file except in compliance with -+ * the License. You may obtain a copy of the License at -+ * http://www.mozilla.org/MPL/ -+ * -+ * Software distributed under the License is distributed on an "AS IS" basis, -+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -+ * for the specific language governing rights and limitations under the -+ * License. -+ * -+ * The Original Code is the Nice GLib ICE library. -+ * -+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia -+ * Corporation. All Rights Reserved. -+ * -+ * Contributors: -+ * Kai Vehmanen, Nokia -+ * -+ * Alternatively, the contents of this file may be used under the terms of the -+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -+ * case the provisions of LGPL are applicable instead of those above. If you -+ * wish to allow use of your version of this file only under the terms of the -+ * LGPL and not to allow others to use your version of this file under the -+ * MPL, indicate your decision by deleting the provisions above and replace -+ * them with the notice and other provisions required by the LGPL. If you do -+ * not delete the provisions above, a recipient may use your version of this -+ * file under either the MPL or the LGPL. -+ */ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "agent.h" -+ -+#include "socket/socket.h" -+ -+#include -+#include -+ -+ -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static guint global_components_ready_exit = 0; -+static guint global_components_failed = 0; -+static guint global_components_failed_exit = 0; -+static GMainLoop *global_mainloop = NULL; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static gboolean global_lagent_ibr_received = FALSE; -+static gboolean global_ragent_ibr_received = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+static gint global_ragent_read = 0; -+static guint global_exit_when_ibr_received = 0; -+ -+static void priv_print_global_status (void) -+{ -+ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done); -+ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]); -+ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]); -+ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands); -+} -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* Core of the test -+ * Assert on any unreleated packet received. This would include anything -+ * send before the negotiation is over. -+ */ -+ g_assert (len == 16); -+ g_assert (strncmp ("1234567812345678", buf, 16) == 0); -+ -+ if (component_id == 2) -+ return; -+ -+ if (GPOINTER_TO_UINT (user_data) == 2) { -+ g_debug ("right agent received %d bytes, stopping mainloop", len); -+ global_ragent_read = len; -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+ -+ if (global_lagent_gathering_done && -+ global_ragent_gathering_done) -+ g_main_loop_quit (global_mainloop); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; -+} -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ if (state == NICE_COMPONENT_STATE_FAILED) -+ global_components_failed++; -+ -+ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit); -+ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit); -+ -+ /* signal status via a global variable */ -+ if (global_components_ready == global_components_ready_exit && -+ global_components_failed == global_components_failed_exit) { -+ g_debug ("Components ready/failed achieved. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ return; -+ } -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; (void)component_id; -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, -+ gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; -+} -+ -+static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, -+ gchar *foundation, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation; -+} -+ -+static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_ibr_received = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_ibr_received = TRUE; -+ -+ if (global_exit_when_ibr_received) { -+ g_debug ("Received initial binding request. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ } -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL; -+ GSList *peer_cands = NULL; -+ GSList *item1, *item2; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ peer_cands = nice_agent_get_local_candidates (to, to_stream, component); -+ -+ /* -+ * Core of the test: -+ * -+ * Send packets that shoudl be dropped. -+ */ -+ -+ for (item1 = cands; item1; item1 = item1->next) { -+ NiceCandidate *cand = item1->data; -+ NiceSocket *nicesock = cand->sockptr; -+ -+ g_assert (nicesock); -+ -+ for (item2 = peer_cands; item2; item2 = item2->next) { -+ NiceCandidate *target_cand = item2->data; -+ -+ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB"); -+ } -+ -+ } -+ -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free); -+ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed) -+{ -+ guint ls_id, rs_id; -+ gint ret; -+ -+ /* XXX: dear compiler, this is for you */ -+ (void)baseaddr; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_components_ready_exit = ready; -+ global_components_failed = 0; -+ global_components_failed_exit = failed; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_ibr_received = -+ global_ragent_ibr_received = FALSE; -+ global_lagent_cands = -+ global_ragent_cands = 0; -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ /* step: add one stream, with RTP+RTCP components, to each agent */ -+ ls_id = nice_agent_add_stream (lagent, 2); -+ -+ rs_id = nice_agent_add_stream (ragent, 2); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000); -+ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001); -+ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345); -+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001); -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE); -+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL); -+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL); -+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ { -+ GSList *cands = NULL, *i; -+ NiceCandidate *cand = NULL; -+ -+ cands = nice_agent_get_local_candidates (lagent, ls_id, 1); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10000); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (lagent, ls_id, 2); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10001); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (ragent, rs_id, 1); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 12345); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (ragent, rs_id, 2); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10002); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ } -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (2)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (2)); -+ -+ /* step: run mainloop until local candidates are ready -+ * (see timer_cb() above) */ -+ if (global_lagent_gathering_done != TRUE || -+ global_ragent_gathering_done != TRUE) { -+ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'..."); -+ g_main_loop_run (global_mainloop); -+ g_assert (global_lagent_gathering_done == TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ } -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ /* step: pass the remote candidates to agents */ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP); -+ -+ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed..."); -+ -+ /* step: run the mainloop until connectivity checks succeed -+ * (see timer_cb() above) */ -+ g_main_loop_run (global_mainloop); -+ -+ /* note: verify that STUN binding requests were sent */ -+ g_assert (global_lagent_ibr_received == TRUE); -+ g_assert (global_ragent_ibr_received == TRUE); -+ -+ /* note: Send a packet from another address */ -+ /* These should also be ignored */ -+ { -+ NiceCandidate *local_cand = NULL; -+ NiceCandidate *remote_cand = NULL; -+ NiceSocket *tmpsock; -+ -+ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand, -+ &remote_cand)); -+ g_assert (local_cand); -+ g_assert (remote_cand); -+ -+ tmpsock = nice_udp_bsd_socket_new (NULL); -+ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD"); -+ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE"); -+ nice_socket_free (tmpsock); -+ } -+ -+ /* note: test payload send and receive */ -+ global_ragent_read = 0; -+ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"); -+ g_assert (ret != -1); -+ g_debug ("Sent %d bytes", ret); -+ g_assert (ret == 16); -+ while (global_ragent_read != 16) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_ragent_read == 16); -+ -+ g_debug ("test-drop-invalid: Ran mainloop, removing streams..."); -+ -+ /* step: clean up resources and exit */ -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ return 0; -+} -+ -+int main (void) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ NiceAddress baseaddr; -+ int result; -+ guint timer_id; -+ -+#ifdef G_OS_WIN32 -+ WSADATA w; -+ -+ WSAStartup(0x0202, &w); -+#endif -+ -+ global_mainloop = g_main_loop_new (NULL, FALSE); -+ -+ /* step: create the agents L and R */ -+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_RFC5245); -+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_RFC5245); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ -+ -+ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent"); -+ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent"); -+ -+ /* step: add a timer to catch state changes triggered by signals */ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ /* step: specify which local interface to use */ -+ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1")) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &baseaddr); -+ nice_agent_add_local_address (ragent, &baseaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-candidate", -+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "new-candidate", -+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received", -+ G_CALLBACK (cb_initial_binding_request_received), -+ GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received", -+ G_CALLBACK (cb_initial_binding_request_received), -+ GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ -+ -+ /* step: run test the first time */ -+ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time"); -+ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0); -+ priv_print_global_status (); -+ g_assert (result == 0); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY); -+ /* When using TURN, we get peer reflexive candidates for the host cands -+ that we removed so we can get another new_selected_pair signal later -+ depending on timing/racing, we could double (or not) the amount we expected -+ */ -+ -+ /* note: verify that correct number of local candidates were reported */ -+ g_assert (global_lagent_cands == 2); -+ g_assert (global_ragent_cands == 2); -+ -+ g_object_unref (lagent); -+ g_object_unref (ragent); -+ -+ g_main_loop_unref (global_mainloop); -+ global_mainloop = NULL; -+ -+ g_source_remove (timer_id); -+#ifdef G_OS_WIN32 -+ WSACleanup(); -+#endif -+ return result; -+} --- -2.13.6 - - -From f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 11 Apr 2017 16:42:55 -0400 -Subject: [PATCH 15/70] conncheck: Check the controlling state when the req was - sent - -It was checking when the pair was created, but the role may have -already changed when the request is sent. ---- - agent/conncheck.c | 30 +++++++++++++++++++----------- - agent/conncheck.h | 1 - - 2 files changed, 19 insertions(+), 12 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 7ffa3db..5501c2b 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1666,7 +1666,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - tmpbuf2, nice_address_get_port (&pair->remote->addr)); - } - pair->nominated = use_candidate; -- pair->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); - -@@ -2531,7 +2530,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->priority = nice_candidate_pair_priority (pair->remote->priority, - pair->local->priority); - pair->nominated = FALSE; -- pair->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local_cand)); - nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); -@@ -2777,16 +2775,26 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { - /* case: role conflict error, need to restart with new role */ - nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- /* note: our role might already have changed due to an -- * incoming request, but if not, change role now; -- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ -- priv_check_for_role_conflict (agent, !p->controlling); - -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_WAITING; -- priv_add_pair_to_triggered_check_queue (agent, p); -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ if (p->stun_message.buffer != NULL) { -+ guint64 tie; -+ gboolean controlled_mode; -+ -+ /* note: our role might already have changed due to an -+ * incoming request, but if not, change role now; -+ * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ -+ controlled_mode = (stun_message_find64 (&p->stun_message, -+ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -+ STUN_MESSAGE_RETURN_SUCCESS); -+ -+ priv_check_for_role_conflict (agent, controlled_mode); -+ -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; -+ p->state = NICE_CHECK_WAITING; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ } - trans_found = TRUE; - } else { - /* case: STUN error, the check STUN context was freed */ -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 10319cc..c204475 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -85,7 +85,6 @@ struct _CandidateCheckPair - gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; - NiceCheckState state; - gboolean nominated; -- gboolean controlling; - gboolean timer_restarted; - gboolean valid; - guint64 priority; --- -2.13.6 - - -From b0538d8c51f65019867b56a45cf90a70bef38f01 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 11 Apr 2017 18:31:21 -0400 -Subject: [PATCH 16/70] agent: Ignore remote candidate of non-accepted types - -If we disable ice-tcp or ice-udp, ignore the remote -candidates for those types. ---- - agent/agent.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/agent/agent.c b/agent/agent.c -index eff62f0..77fb1eb 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3112,6 +3112,13 @@ static gboolean priv_add_remote_candidate ( - NiceComponent *component; - NiceCandidate *candidate; - -+ if (transport == NICE_CANDIDATE_TRANSPORT_UDP && -+ !agent->use_ice_udp) -+ return FALSE; -+ if (transport != NICE_CANDIDATE_TRANSPORT_UDP && -+ !agent->use_ice_tcp) -+ return FALSE; -+ - if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) - return FALSE; - --- -2.13.6 - - -From f6f704c5e8d2193bc67ba2b697c77694e1698c43 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Thu, 9 Jun 2016 22:22:33 +0200 -Subject: [PATCH 17/70] stun timer: fix timeout of the last retransmission - -According to RFC 5389, section 7.2.1, a special timeout is applied to -the last retransmission (Rm * RTO), with Rm default value of 16, instead -of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7. - -As spotted by Olivier Crete, stun_timer_* is a public API, that cannot -be changed, and the initial delay (RTO) is not preserved in the -stun_timer_s struct. So we use a hack that implicitely guess Rm from the -number of transmissions Rc, by generalizing the default value of the -spec for Rm and Rc to other values of Rc passed in stun_timer_start( - -According to the spec, with the default value of Rc=7, the last delay -should be (64 * RTO), and it is instead (16 * RTO). So the last delay -can be computed by dividing the penultimate delay by two, instead of -multiplying it by two. - -Differential Revision: https://phabricator.freedesktop.org/D1108 ---- - stun/usages/timer.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/stun/usages/timer.c b/stun/usages/timer.c -index 2862ab8..5370cba 100644 ---- a/stun/usages/timer.c -+++ b/stun/usages/timer.c -@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer) - if (timer->retransmissions >= timer->max_retransmissions) - return STUN_USAGE_TIMER_RETURN_TIMEOUT; - -- add_delay (&timer->deadline, timer->delay *= 2); -+ if (timer->retransmissions == timer->max_retransmissions - 1) -+ timer->delay = timer->delay / 2; -+ else -+ timer->delay = timer->delay * 2; -+ add_delay (&timer->deadline, timer->delay); - timer->retransmissions++; - return STUN_USAGE_TIMER_RETURN_RETRANSMIT; - } --- -2.13.6 - - -From 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 5 Apr 2016 21:32:39 +0200 -Subject: [PATCH 18/70] agent: do not create a GSource for UDP TURN socket - -With this patch, we don't create a new GSource for udp-turn socket, -because it would duplicate the packets already received on the base UDP -socket, as the underlying GSocket is the same. This is a race condition, -because an UDP packet arriving on the base socket, may randomly be -handled by the GSource callback created for the base socket (udp-bsd) of -the callback created for the udp-turn socket. Moreover this callback -already knows how to parse UDP datagrams received from a known turn -server. - -This patch also prevents a subtle bug, when a STUN request is received -directly from a peer, is handled by the udp turn socket. If the agent -already has a valid permission for this remote candidate, established -for another pair, it will happily send the STUN reply through the turn -relay. This generates a source address mismatch on the peer agent, when -it'll receive the STUN response from the turn relay instead of the -initial address the request has been sent to. - -Differential Revision: https://phabricator.freedesktop.org/D932 ---- - agent/component.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/agent/component.c b/agent/component.c -index ba28ffa..ab665b6 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context) - if (socket_source->socket->fileno == NULL) - return; - -+ /* Do not create a GSource for UDP turn socket, because it -+ * would duplicate the packets already received on the base -+ * UDP socket. -+ */ -+ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN) -+ return; -+ - /* Create a source. */ - source = g_socket_create_source (socket_source->socket->fileno, - G_IO_IN, NULL); --- -2.13.6 - - -From d5446a72233eab8501be0b3fb9060c8be3ba034b Mon Sep 17 00:00:00 2001 -From: Philip Withnall -Date: Mon, 1 May 2017 08:51:40 +0100 -Subject: [PATCH 19/70] examples: Stop installing the examples -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -There’s no point in installing them; their benefit is in providing -example code to developers. - -Debian doesn’t package them; Fedora packages them in a separate -subpackage which will have to disappear. - -Signed-off-by: Philip Withnall -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1737 ---- - examples/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/examples/Makefile.am b/examples/Makefile.am -index 1e7decf..9c80854 100644 ---- a/examples/Makefile.am -+++ b/examples/Makefile.am -@@ -18,7 +18,7 @@ AM_CFLAGS = \ - $(GLIB_CFLAGS) \ - $(GUPNP_CFLAGS) - --bin_PROGRAMS = simple-example threaded-example sdp-example -+noinst_PROGRAMS = simple-example threaded-example sdp-example - - simple_example_SOURCES = simple-example.c - simple_example_LDADD = $(top_builddir)/agent/libagent.la \ --- -2.13.6 - - -From b4abda09c79e4ce372a3771300abf568c85c7ff5 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Thu, 21 Apr 2016 18:18:59 +0200 -Subject: [PATCH 20/70] interfaces: ignore predefined network interfaces - -Some interfaces, like the one managed by libvirtd to provide a network -bridge to locally hosted virtual machines, can be completely ignored -when gathering ICE candidates. The motivation for adding this -possibility is that, ignoring them doesn't remove capabilities, and -improves the overall speed of the connection check method, by reducing -the number of pairs to be tested. This patch adds the possibility to -define such interfaces in the configuration script. - -Differential Revision: https://phabricator.freedesktop.org/D948 ---- - agent/interfaces.c | 6 ++++++ - configure.ac | 14 ++++++++++++++ - 2 files changed, 20 insertions(+) - -diff --git a/agent/interfaces.c b/agent/interfaces.c -index 0fa2fd7..a81888e 100644 ---- a/agent/interfaces.c -+++ b/agent/interfaces.c -@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback) - nice_debug ("Ignoring loopback interface"); - g_free (addr_string); - } -+#ifdef IGNORED_IFACE_PREFIX -+ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) { -+ nice_debug ("Ignoring interface %s as it matches prefix %s", -+ ifa->ifa_name, IGNORED_IFACE_PREFIX); -+ g_free (addr_string); -+#endif - } else { - if (nice_interfaces_is_private_ip (ifa->ifa_addr)) - ips = add_ip_to_list (ips, addr_string, TRUE); -diff --git a/configure.ac b/configure.ac -index b39bfe3..98bbc08 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false) - # GObject introspection - GOBJECT_INTROSPECTION_CHECK([1.30.0]) - -+dnl Ignore a specific network interface name prefix from the connection check -+AC_MSG_CHECKING([whether to ignore a specific network interface name prefix]) -+AC_ARG_WITH([ignored-network-interface-prefix], -+ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string], -+ [Ignore network interfaces whose name starts with "string" from the ICE connection -+ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge -+ handled by libvirtd, do not help in finding connectivity.])], -+ [interface_prefix="$withval"]) -+AS_IF([test -n "$interface_prefix"], -+ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"], -+ [Ignore this network interface prefix from the connection check]) -+ AC_MSG_RESULT([yes, $interface_prefix])], -+ [AC_MSG_RESULT([no])]) -+ - AC_CONFIG_MACRO_DIR(m4) - - AC_OUTPUT --- -2.13.6 - - -From 80c613699786567fd93db74377138600794a86e0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Thu, 8 Jun 2017 16:34:21 -0400 -Subject: [PATCH 21/70] agent: Use base_addr to generate rport in SDP - -Reported by Capricornus (zhushengliang) - -https://phabricator.freedesktop.org/T7763 ---- - agent/agent.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 77fb1eb..1ff09af 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -5690,7 +5690,7 @@ _generate_candidate_sdp (NiceAgent *agent, - g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type)); - if (nice_address_is_valid (&candidate->base_addr) && - !nice_address_equal (&candidate->addr, &candidate->base_addr)) { -- port = nice_address_get_port (&candidate->addr); -+ port = nice_address_get_port (&candidate->base_addr); - nice_address_to_string (&candidate->base_addr, ip4); - g_string_append_printf (sdp, " raddr %s rport %d", ip4, - port == 0 ? 9 : port); --- -2.13.6 - - -From 8bb210c5af4bcaf342d7fa4fef6034269e976532 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Thu, 9 Jun 2016 23:28:43 +0200 -Subject: [PATCH 22/70] stun timer: make properties for stun timer tunables - -Three STUN binding request properties should be customisable. RFC 5245 -describes the retransmission timer of the STUN transaction 'RTO', and -RFC 5389 describes the number of retransmissions to send until a -response is received 'Rc'. The third property is the 'RTO' when -a reliable connection is used. - -RFC 5389 introduces a supplementary property 'Rm' as a multiplier used -to compute the final timeout RTO * Rm. However, this property is not -added in libnice, because this would require breaking the public API for -STUN. Currently, our STUN implementation hardcodes a division by two for -this final timeout. - -Differential Revision: https://phabricator.freedesktop.org/D1109 ---- - agent/agent-priv.h | 4 ++- - agent/agent.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ - agent/conncheck.c | 16 +++++---- - agent/discovery.c | 8 ++--- - stun/usages/timer.h | 6 +++- - 5 files changed, 118 insertions(+), 13 deletions(-) - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index ada3630..162ea63 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -106,7 +106,6 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - - #define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */ - #define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */ --#define NICE_AGENT_TIMER_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */ - #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */ - - -@@ -132,6 +131,9 @@ struct _NiceAgent - guint timer_ta; /* property: timer Ta */ - guint max_conn_checks; /* property: max connectivity checks */ - gboolean force_relay; /* property: force relay */ -+ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ -+ guint stun_initial_timeout; /* property: stun initial timeout, RTO */ -+ guint stun_reliable_timeout; /* property: stun reliable timeout */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -diff --git a/agent/agent.c b/agent/agent.c -index 1ff09af..25d7886 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -113,6 +113,9 @@ enum - PROP_BYTESTREAM_TCP, - PROP_KEEPALIVE_CONNCHECK, - PROP_FORCE_RELAY, -+ PROP_STUN_MAX_RETRANSMISSIONS, -+ PROP_STUN_INITIAL_TIMEOUT, -+ PROP_STUN_RELIABLE_TIMEOUT, - }; - - -@@ -708,6 +711,76 @@ nice_agent_class_init (NiceAgentClass *klass) - FALSE, - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:stun-max-retransmissions -+ * -+ * The maximum number of retransmissions of the STUN binding requests -+ * used in the gathering stage, to find our local candidates, and used -+ * in the connection check stage, to test the validity of each -+ * constructed pair. This property is described as 'Rc' in the RFC -+ * 5389, with a default value of 7. The timeout of each STUN request -+ * is doubled for each retransmission, so the choice of this value has -+ * a direct impact on the time needed to move from the CONNECTED state -+ * to the READY state, and on the time needed to complete the GATHERING -+ * state. -+ * -+ * Since: UNRELEASED -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, -+ g_param_spec_uint ( -+ "stun-max-retransmissions", -+ "STUN Max Retransmissions", -+ "Maximum number of STUN binding requests retransmissions " -+ "described as 'Rc' in the STUN specification.", -+ 1, 99, -+ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * NiceAgent:stun-initial-timeout -+ * -+ * The initial timeout (msecs) of the STUN binding requests -+ * used in the gathering stage, to find our local candidates. -+ * This property is described as 'RTO' in the RFC 5389 and RFC 5245. -+ * This timeout is doubled for each retransmission, until -+ * #NiceAgent:stun-max-retransmissions have been done, -+ * with an exception for the last restransmission, where the timeout is -+ * divided by two instead (RFC 5389 indicates that a customisable -+ * multiplier 'Rm' to 'RTO' should be used). -+ * -+ * Since: UNRELEASED -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, -+ g_param_spec_uint ( -+ "stun-initial-timeout", -+ "STUN Initial Timeout", -+ "STUN timeout in msecs of the initial binding requests used in the " -+ "gathering state, described as 'RTO' in the ICE specification.", -+ 20, 9999, -+ STUN_TIMER_DEFAULT_TIMEOUT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * NiceAgent:stun-reliable-timeout -+ * -+ * The initial timeout of the STUN binding requests used -+ * for a reliable timer. -+ * -+ * Since: UNRELEASED -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, -+ g_param_spec_uint ( -+ "stun-reliable-timeout", -+ "STUN Reliable Timeout", -+ "STUN timeout in msecs of the initial binding requests used for " -+ "a reliable timer.", -+ 20, 99999, -+ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ - /* install signals */ - - /** -@@ -1187,6 +1260,18 @@ nice_agent_get_property ( - g_value_set_boolean (value, agent->force_relay); - break; - -+ case PROP_STUN_MAX_RETRANSMISSIONS: -+ g_value_set_uint (value, agent->stun_max_retransmissions); -+ break; -+ -+ case PROP_STUN_INITIAL_TIMEOUT: -+ g_value_set_uint (value, agent->stun_initial_timeout); -+ break; -+ -+ case PROP_STUN_RELIABLE_TIMEOUT: -+ g_value_set_uint (value, agent->stun_reliable_timeout); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1374,6 +1459,18 @@ nice_agent_set_property ( - agent->force_relay = g_value_get_boolean (value); - break; - -+ case PROP_STUN_MAX_RETRANSMISSIONS: -+ agent->stun_max_retransmissions = g_value_get_uint (value); -+ break; -+ -+ case PROP_STUN_INITIAL_TIMEOUT: -+ agent->stun_initial_timeout = g_value_get_uint (value); -+ break; -+ -+ case PROP_STUN_RELIABLE_TIMEOUT: -+ agent->stun_reliable_timeout = g_value_get_uint (value); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 5501c2b..14fdcd9 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -888,8 +888,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - agent, buf_len, p->keepalive.stun_message.buffer); - - if (buf_len > 0) { -- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&p->keepalive.timer, -+ agent->stun_initial_timeout, -+ agent->stun_max_retransmissions); - - agent->media_after_tick = FALSE; - -@@ -1116,8 +1117,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand) - } - - if (buffer_len > 0) { -- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&cand->timer, -+ cand->agent->stun_initial_timeout, -+ cand->agent->stun_max_retransmissions); - - /* send the refresh */ - agent_socket_send (cand->nicesock, &cand->server, -@@ -2171,11 +2173,11 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - - if (buffer_len > 0) { - if (nice_socket_is_reliable(pair->sockptr)) { -- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -+ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); - } else { - stun_timer_start (&pair->timer, - priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ agent->stun_max_retransmissions); - } - - /* TCP-ACTIVE candidate must create a new socket before sending -@@ -2340,7 +2342,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { - stun_timer_start (&p->timer, - priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ agent->stun_max_retransmissions); - p->timer_restarted = TRUE; - } - } -diff --git a/agent/discovery.c b/agent/discovery.c -index 7a890a0..4cc99c2 100644 ---- a/agent/discovery.c -+++ b/agent/discovery.c -@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) - - if (buffer_len > 0) { - if (nice_socket_is_reliable (cand->nicesock)) { -- stun_timer_start_reliable (&cand->timer, -- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -+ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); - } else { -- stun_timer_start (&cand->timer, 200, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&cand->timer, -+ agent->stun_initial_timeout, -+ agent->stun_max_retransmissions); - } - - /* send the conncheck */ -diff --git a/stun/usages/timer.h b/stun/usages/timer.h -index e74353b..097e75b 100644 ---- a/stun/usages/timer.h -+++ b/stun/usages/timer.h -@@ -132,7 +132,11 @@ struct stun_timer_s { - * The default intial timeout to use for the timer - * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most - * cases as it is also what is used by SIP style VoIP when sending A-Law and -- * mu-Law audio, so 200ms should be hyper safe. -+ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout -+ * of 200ms, a default of 7 transmissions, the last timeout will be -+ * 16 * 200ms, and we expect to receive a response from the stun server -+ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial -+ * stun request has been sent. - */ - #define STUN_TIMER_DEFAULT_TIMEOUT 200 - --- -2.13.6 - - -From 7a2c1edf502849a868b6f1026e8e2c343dee4ded Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 6 Jun 2016 22:24:50 +0200 -Subject: [PATCH 23/70] conncheck: update selected pair when nominated flag is - set - -This modifies commit 8f1f615. It is better focused to update the -selected pair just after its nominated flag has been set. We also keep -the code homogeneous with other places, where the call to -priv_update_selected_pair() immediately follows the setting of -pair->nominated. Moreover in priv_update_check_list_state_for_ready(), -we would call priv_update_selected_pair() more times that necessary when -iterating on all nominated pairs. - -Differential Revision: https://phabricator.freedesktop.org/D1125 ---- - agent/conncheck.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 14fdcd9..8c55269 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data); - static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - NiceAgent *agent, guint stream_id, NiceComponent *component, - NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); -+static gboolean priv_update_selected_pair (NiceAgent *agent, -+ NiceComponent *component, CandidateCheckPair *pair); - - static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - { -@@ -515,6 +517,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -+ priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ - } -@@ -1530,7 +1533,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream - ++valid; - if (p->nominated == TRUE) { - ++nominated; -- priv_update_selected_pair (agent, component, p); - } - } - } --- -2.13.6 - - -From 3a58ba6120b188d78c5709e0349c0346bfa21c1a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 1 Feb 2016 11:10:21 +0100 -Subject: [PATCH 24/70] conncheck: peer reflexive candidates are not paired - -This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning -Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive -Candidates", where discovered candidates do not cause the creation -of new pairs to be checked. - -Differential Revision: https://phabricator.freedesktop.org/D805 ---- - agent/conncheck.c | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 8c55269..cdf1025 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1787,6 +1787,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone - - g_assert (remote != NULL); - -+ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates", -+ * the agent does not pair this candidate with any local candidates. -+ */ -+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && -+ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) -+ { -+ return added; -+ } -+ - for (i = component->local_candidates; i ; i = i->next) { - NiceCandidate *local = i->data; - -@@ -1821,6 +1830,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC - - g_assert (local != NULL); - -+ /* -+ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive -+ * Candidates", the peer reflexive candidate is not paired -+ * with other remote candidates -+ */ -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && -+ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) -+ { -+ return added; -+ } -+ - for (i = component->remote_candidates; i ; i = i->next) { - - NiceCandidate *remote = i->data; --- -2.13.6 - - -From a602ff57aae6a6afdeab843954c48e6fb5d82d31 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 13:02:45 +0200 -Subject: [PATCH 25/70] conncheck: fix pair state transition when successful - response is received - -According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a -successful check should go to state succeeded, not only the valid -pair built in section 7.1.3.2.2. - -Differential Revision: https://phabricator.freedesktop.org/D810 ---- - agent/conncheck.c | 17 +++++++++++++---- - 1 file changed, 13 insertions(+), 4 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index cdf1025..7fc2a1d 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2654,7 +2654,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - } - else { - if (!local_cand) { -- if (!agent->force_relay) -+ if (!agent->force_relay) { -+ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1. -+ * "Discovering Peer Reflexive Candidates" -+ */ - local_cand = discovery_add_peer_reflexive_candidate (agent, - stream->id, - component->id, -@@ -2662,8 +2665,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - sockptr, - local_candidate, - remote_candidate); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p", -+ agent, local_cand); -+ } - } - - /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 -@@ -2671,7 +2675,12 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (local_cand) - new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, - local_cand, p); -- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); -+ /* step: The agent sets the state of the pair that *generated* the check to -+ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" -+ */ -+ p->state = NICE_CHECK_SUCCEEDED; -+ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", -+ agent, p, new_pair); - } - - /* note: this is same as "adding to VALID LIST" in the spec --- -2.13.6 - - -From 0636f9addc041cf93c4ff4eaa351b1768d48a32e Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 19 Apr 2016 13:12:48 +0200 -Subject: [PATCH 26/70] conncheck: implement ice regular nomination method - -This patch implements Regular Nomation as described in RFC5245 -8.1.1.1. The controlling agent lets valid pairs accumulate, and -decides which pair to recheck with the use-candidate attribute set. -priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated -pair when acting as a STUN server, and -priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to -update the nominated pair when acting as a STUN client. A new -property is also added to the agent to control the nomination -mode, which can be regular of aggressive, with default value -set to aggressive. - -Two new flags are introduced in the CandidateCheckPair structure: - -- use_candidate_on_next_check indicates the STUN client to add the - use-candidate attribute when the pair will be checked. At this - time, the nominated flag has not been set on this pair yet. - -- mark_nominated_on_response_arrival indicates the STUN server - to nominate the pair when its succesfull response to a - previous triggered check will arrive (7.2.1.5, item #2) - -Differential Revision: https://phabricator.freedesktop.org/D811 ---- - agent/Makefile.am | 23 ++++ - agent/agent-priv.h | 8 ++ - agent/agent.c | 59 +++++++++ - agent/agent.h | 43 ++++++- - agent/conncheck.c | 178 +++++++++++++++++++++++++++- - agent/conncheck.h | 2 + - configure.ac | 1 + - docs/reference/libnice/libnice-sections.txt | 2 + - 8 files changed, 309 insertions(+), 7 deletions(-) - -diff --git a/agent/Makefile.am b/agent/Makefile.am -index b585393..915f312 100644 ---- a/agent/Makefile.am -+++ b/agent/Makefile.am -@@ -22,6 +22,12 @@ if WINDOWS - AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP - endif - -+BUILT_SOURCES = \ -+ agent-enum-types.h \ -+ agent-enum-types.c -+ -+CLEANFILES += $(BUILT_SOURCES) -+ - noinst_LTLIBRARIES = libagent.la - - libagent_la_SOURCES = \ -@@ -54,6 +60,23 @@ libagent_la_SOURCES = \ - outputstream.c \ - $(BUILT_SOURCES) - -+agent-enum-types.h: agent.h Makefile -+ $(AM_V_GEN)$(GLIB_MKENUMS) \ -+ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include \n\nG_BEGIN_DECLS\n" \ -+ --fprod "/* enumerations from \"@filename@\" */\n" \ -+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ -+ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \ -+ $(addprefix $(srcdir)/,agent.h) > $@ -+ -+agent-enum-types.c: agent.h Makefile agent-enum-types.h -+ $(AM_V_GEN)$(GLIB_MKENUMS) \ -+ --fhead "#include \n#include \n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \ -+ --fprod "\n/* enumerations from \"@filename@\" */" \ -+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \ -+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ -+ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \ -+ $(addprefix $(srcdir)/,agent.h) > $@ -+ - libagent_la_LIBADD = \ - $(top_builddir)/random/libnice-random.la \ - $(top_builddir)/socket/libsocket.la \ -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 162ea63..3384180 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -115,6 +115,13 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - - #define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ - -+/* A convenient macro to test if the agent is compatible with RFC5245 -+ * or OC2007R2. Specifically these two modes share the support -+ * of the regular or aggressive nomination mode */ -+#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \ -+ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ -+ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -@@ -134,6 +141,7 @@ struct _NiceAgent - guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ - guint stun_initial_timeout; /* property: stun initial timeout, RTO */ - guint stun_reliable_timeout; /* property: stun reliable timeout */ -+ NiceNominationMode nomination_mode; /* property: Nomination mode */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -diff --git a/agent/agent.c b/agent/agent.c -index 25d7886..577a7e0 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -73,6 +73,7 @@ - #include "interfaces.h" - - #include "pseudotcp.h" -+#include "agent-enum-types.h" - - /* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b - * wide. */ -@@ -116,6 +117,7 @@ enum - PROP_STUN_MAX_RETRANSMISSIONS, - PROP_STUN_INITIAL_TIMEOUT, - PROP_STUN_RELIABLE_TIMEOUT, -+ PROP_NOMINATION_MODE, - }; - - -@@ -440,6 +442,24 @@ nice_agent_class_init (NiceAgentClass *klass) - G_PARAM_READWRITE)); - - /** -+ * NiceAgent:nomination-mode: -+ * -+ * The nomination mode used in the ICE specification for describing -+ * the selection of valid pairs to be used upstream. -+ * See also: #NiceNominationMode -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, -+ g_param_spec_enum ( -+ "nomination-mode", -+ "ICE nomination mode", -+ "Nomination mode used in the ICE specification for describing " -+ "the selection of valid pairs to be used upstream", -+ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ -+ /** - * NiceAgent:proxy-ip: - * - * The proxy server IP used to bypass a proxy firewall -@@ -1096,6 +1116,7 @@ nice_agent_init (NiceAgent *agent) - agent->stun_server_port = DEFAULT_STUN_PORT; - agent->controlling_mode = TRUE; - agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; -+ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; - - agent->discovery_list = NULL; - agent->discovery_unsched_items = 0; -@@ -1144,6 +1165,23 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) - } - - -+NICEAPI_EXPORT NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ gboolean reliable, -+ NiceNominationMode nomination) -+{ -+ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, -+ "compatibility", compat, -+ "main-context", ctx, -+ "reliable", reliable, -+ "nomination-mode", nomination, -+ NULL); -+ -+ return agent; -+} -+ -+ - static void - nice_agent_get_property ( - GObject *object, -@@ -1190,6 +1228,10 @@ nice_agent_get_property ( - /* XXX: should we prune the list of already existing checks? */ - break; - -+ case PROP_NOMINATION_MODE: -+ g_value_set_enum (value, agent->nomination_mode); -+ break; -+ - case PROP_PROXY_IP: - g_value_set_string (value, agent->proxy_ip); - break; -@@ -1394,6 +1436,10 @@ nice_agent_set_property ( - agent->max_conn_checks = g_value_get_uint (value); - break; - -+ case PROP_NOMINATION_MODE: -+ agent->nomination_mode = g_value_get_enum (value); -+ break; -+ - case PROP_PROXY_IP: - g_free (agent->proxy_ip); - agent->proxy_ip = g_value_dup_string (value); -@@ -3298,6 +3344,19 @@ static gboolean priv_add_remote_candidate ( - username, password, priority); - } - -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ /* note: If there are TCP candidates for a media stream, -+ * a controlling agent MUST use the regular selection algorithm, -+ * RFC 6544, sect 8, "Concluding ICE Processing" -+ */ -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE && -+ transport != NICE_CANDIDATE_TRANSPORT_UDP) { -+ nice_debug ("Agent %p : we have TCP candidates, switching back " -+ "to regular nomination mode", agent); -+ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR; -+ } -+ } -+ - if (base_addr) - candidate->base_addr = *base_addr; - -diff --git a/agent/agent.h b/agent/agent.h -index 47c4d5a..6e233c6 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -377,6 +377,26 @@ typedef enum - NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP, - } NiceProxyType; - -+/** -+ * NiceNominationMode: -+ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode -+ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode -+ * -+ * An enum to specity the kind of nomination mode to use by -+ * the agent, as described in RFC 5245. Two modes exists, -+ * regular and aggressive. They differ by the way the controlling -+ * agent chooses to put the USE-CANDIDATE attribute in its STUN -+ * messages. The aggressive mode is supposed to nominate a pair -+ * faster, than the regular mode, potentially causing the nominated -+ * pair to change until the connection check completes. -+ * -+ * Since: UNRELEASED -+ */ -+typedef enum -+{ -+ NICE_NOMINATION_MODE_REGULAR = 0, -+ NICE_NOMINATION_MODE_AGGRESSIVE, -+} NiceNominationMode; - - /** - * NiceAgentRecvFunc: -@@ -429,6 +449,28 @@ NiceAgent * - nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - - /** -+ * nice_agent_new_full: -+ * @ctx: The Glib Mainloop Context to use for timers -+ * @compat: The compatibility mode of the agent -+ * @reliable: The reliability mode of the agent -+ * @nomination: The nomination mode of the agent -+ * -+ * Create a new #NiceAgent with parameters that must be be defined at -+ * construction time. -+ * The returned object must be freed with g_object_unref() -+ * See also: #NiceNominationMode -+ * -+ * Since: UNRELEASED -+ * -+ * Returns: The new agent GObject -+ */ -+NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ gboolean reliable, -+ NiceNominationMode nomination); -+ -+/** - * nice_agent_add_local_address: - * @agent: The #NiceAgent Object - * @addr: The address to listen to -@@ -447,7 +489,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - gboolean - nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr); - -- - /** - * nice_agent_add_stream: - * @agent: The #NiceAgent Object -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 7fc2a1d..6827e6e 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -502,7 +502,62 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - if (s_nominated < stream->n_components && - s_waiting_for_nomination) { - keep_timer_going = TRUE; -- if (agent->controlling_mode) { -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && -+ agent->controlling_mode && -+ ((waiting == 0 && s_inprogress == 0) || -+ (s_succeeded + s_discovered) >= 5 * stream->n_components)){ -+ /* ICE 8.1.1.1 Regular nomination -+ * we choose to nominate the valid pair if -+ * there is no pair left waiting or in-progress or -+ * if there are at least 5 valid pairs per stream on average. -+ * -+ * This is the "stopping criterion" described in 8.1.1.1, and is -+ * a "local optimization" between accumulating more valid pairs, -+ * and limiting the time spent waiting for in-progress connections -+ * checks until they finally fail. -+ */ -+ GSList *component_item; -+ -+ for (component_item = stream->components; component_item; -+ component_item = component_item->next) { -+ NiceComponent *component = component_item->data; -+ gboolean already_done = FALSE; -+ -+ /* verify that the choice of the pair to be nominated -+ * has not already been done -+ */ -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ if (p->component_id == component->id && -+ p->use_candidate_on_next_check) { -+ already_done = TRUE; -+ break; -+ } -+ } -+ -+ /* choose a pair to be nominated in the list of valid -+ * pairs, and add it to the triggered checks list -+ */ -+ if (!already_done) { -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ /* note: highest priority item selected (list always sorted) */ -+ if (p->component_id == component->id && -+ !p->nominated && -+ !p->use_candidate_on_next_check && -+ p->valid) { -+ nice_debug ("Agent %p : restarting check %p with " -+ "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->use_candidate_on_next_check = TRUE; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ break; /* move to the next component */ -+ } -+ } -+ } -+ } -+ } -+ } else if (agent->controlling_mode) { - GSList *component_item; - - for (component_item = stream->components; component_item; -@@ -1571,10 +1626,40 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - - g_assert (component); - -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && -+ agent->controlling_mode) -+ return; -+ - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; -- if (pair->local == localcand && pair->remote == remotecand) { -+ if (pair->local == localcand && pair->remote == remotecand && -+ NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ /* ICE, 7.2.1.5. Updating the Nominated Flag */ -+ /* note: TCP candidates typically produce peer reflexive -+ * candidate, generating a "discovered" pair that can be -+ * nominated. -+ */ -+ if (pair->valid) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated", -+ agent, pair, pair->foundation); -+ pair->nominated = TRUE; -+ priv_update_selected_pair (agent, component, pair); -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) { -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (pair->state == NICE_CHECK_IN_PROGRESS) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is in-progress, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } -+ } else if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; - if (pair->valid) { -@@ -2174,7 +2259,35 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - pair->prflx_priority, controlling); - } - -- if (cand_use) -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ { -+ /* We are doing regular nomination, so we set the use-candidate -+ * attrib, when the controlling agent decided which valid pair to -+ * resend with this flag in priv_conn_check_tick_stream() -+ */ -+ cand_use = pair->use_candidate_on_next_check; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(regular nomination).", agent, G_STRFUNC, cand_use); -+ break; -+ } -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ { -+ /* We are doing aggressive nomination, we set the use-candidate -+ * attrib in every check we send, when we are the controlling -+ * agent, RFC 5245, 8.1.1.2 -+ */ -+ cand_use = controlling; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(aggressive nomination).", agent, G_STRFUNC, cand_use); -+ break; -+ } -+ default: -+ /* Nothing to do. */ -+ break; -+ } -+ } else if (cand_use) - pair->nominated = controlling; - - if (uname_len > 0) { -@@ -2781,12 +2894,66 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - local_candidate, remote_candidate); - } - -- -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ - if (!ok_pair) - ok_pair = p; - - /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the - Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, control=1, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(aggressive nomination, control=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ default: -+ /* Nothing to do */ -+ break; -+ } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, control=0, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; -+ } -+ } -+ } -+ - if (ok_pair->nominated == TRUE) { - priv_update_selected_pair (agent, component, ok_pair); - priv_print_conn_check_lists (agent, G_STRFUNC, -@@ -3668,8 +3835,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - stun_usage_ice_conncheck_use_candidate (&req); - uint32_t priority = stun_usage_ice_conncheck_priority (&req); - -- if (agent->controlling_mode || -- agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || - agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007) - use_candidate = TRUE; -diff --git a/agent/conncheck.h b/agent/conncheck.h -index c204475..0f988de 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -87,6 +87,8 @@ struct _CandidateCheckPair - gboolean nominated; - gboolean timer_restarted; - gboolean valid; -+ gboolean use_candidate_on_next_check; -+ gboolean mark_nominated_on_response_arrival; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ -diff --git a/configure.ac b/configure.ac -index 98bbc08..6c106ff 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -57,6 +57,7 @@ AC_PROG_CC - AM_PROG_AR - LT_PREREQ([2.2.6]) - LT_INIT([dlopen win32-dll disable-static]) -+AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) - - # Check Operating System - AC_MSG_CHECKING([operating system]) -diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt -index 88a6cd2..a481106 100644 ---- a/docs/reference/libnice/libnice-sections.txt -+++ b/docs/reference/libnice/libnice-sections.txt -@@ -5,6 +5,7 @@ NiceAgent - NiceComponentState - NiceComponentType - NiceProxyType -+NiceNominationMode - NiceCompatibility - NiceAgentRecvFunc - NiceInputMessage -@@ -12,6 +13,7 @@ NiceOutputMessage - NICE_AGENT_MAX_REMOTE_CANDIDATES - nice_agent_new - nice_agent_new_reliable -+nice_agent_new_full - nice_agent_add_local_address - nice_agent_set_port_range - nice_agent_add_stream --- -2.13.6 - - -From 4497d9b7afaaea7124db4a2cd13546d9366b5986 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Wed, 22 Jun 2016 15:44:39 +0200 -Subject: [PATCH 27/70] test-nomination: added a new test for the nomination - mode - -Differential Revision: https://phabricator.freedesktop.org/D1107 ---- - tests/Makefile.am | 5 +- - tests/test-nomination.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 267 insertions(+), 1 deletion(-) - create mode 100644 tests/test-nomination.c - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 62d5d64..b623764 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -55,7 +55,8 @@ check_PROGRAMS = \ - test-icetcp \ - test-credentials \ - test-turn \ -- test-drop-invalid -+ test-drop-invalid \ -+ test-nomination - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -131,6 +132,8 @@ test_turn_LDADD = $(COMMON_LDADD) - - test_drop_invalid_LDADD = $(COMMON_LDADD) - -+test_nomination_LDADD = $(COMMON_LDADD) -+ - test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) - test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) - -diff --git a/tests/test-nomination.c b/tests/test-nomination.c -new file mode 100644 -index 0000000..b5a5e5f ---- /dev/null -+++ b/tests/test-nomination.c -@@ -0,0 +1,263 @@ -+#include -+#include -+#include -+ -+#include -+#include -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* -+ * Lets ignore stun packets that got through -+ */ -+ if (len < 8) -+ return; -+ if (strncmp ("12345678", buf, 8)) -+ return; -+ -+ if (component_id != 1) -+ return; -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+} -+ -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ g_assert (state != NICE_COMPONENT_STATE_FAILED); -+ -+ g_debug ("test-nomination: checks READY %u.", global_components_ready); -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, -+ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static void -+run_test(NiceNominationMode l_nomination_mode, -+ NiceNominationMode r_nomination_mode) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ const gchar *localhost; -+ NiceAddress localaddr; -+ guint ls_id, rs_id; -+ gulong timer_id; -+ -+ localhost = "127.0.0.1"; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_cands = global_ragent_cands = 0; -+ -+ lagent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ FALSE, /* reliable */ -+ l_nomination_mode); -+ -+ ragent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ FALSE, /* reliable */ -+ r_nomination_mode); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ nice_agent_set_software (lagent, "Test-nomination, Left Agent"); -+ nice_agent_set_software (ragent, "Test-nomination, Right Agent"); -+ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ if (!nice_address_set_from_string (&localaddr, localhost)) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &localaddr); -+ nice_agent_add_local_address (ragent, &localaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 1); -+ rs_id = nice_agent_add_stream (ragent, 1); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); -+ -+ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'..."); -+ while (!global_lagent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_gathering_done == TRUE); -+ while (!global_ragent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); -+ -+ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || -+ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ g_source_remove (timer_id); -+ -+ g_clear_object(&lagent); -+ g_clear_object(&ragent); -+} -+ -+static void -+regular (void) -+{ -+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR); -+} -+ -+static void -+aggressive (void) -+{ -+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE); -+} -+ -+static void -+mixed_ra (void) -+{ -+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE); -+} -+ -+static void -+mixed_ar (void) -+{ -+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR); -+} -+ -+int -+main (int argc, char **argv) -+{ -+ int ret; -+ -+ g_test_init (&argc, &argv, NULL); -+ -+ g_test_add_func ("/nice/nomination/regular", regular); -+ g_test_add_func ("/nice/nomination/aggressive", aggressive); -+ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra); -+ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar); -+ -+ ret = g_test_run (); -+ -+ return ret; -+} --- -2.13.6 - - -From 58d061df8f5425dc1add9c6030a2f891ebda4616 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 7 Mar 2016 16:35:09 +0100 -Subject: [PATCH 28/70] conncheck: update pair valid property selectively - -With this patch, we fix a corner case when the succeeded pair is a -peer-reflexive candidate pair, that already has been discovered -previously, In this case, the current pair -p- should not be marked -valid, because the valid flag is already set on the discovered pair. - -Differential Revision: https://phabricator.freedesktop.org/D1124 ---- - agent/conncheck.c | 18 +++++++++++++----- - 1 file changed, 13 insertions(+), 5 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 6827e6e..ef8df68 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2760,6 +2760,13 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - } - - if (new_pair) { -+ /* note: when new_pair is distinct from p, it means new_pair is a -+ * previously discovered peer-reflexive candidate pair, so we don't -+ * set the valid flag on p in this case, because the valid flag is -+ * already set on the discovered pair. -+ */ -+ if (new_pair == p) -+ p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); -@@ -2788,6 +2795,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (local_cand) - new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, - local_cand, p); -+ /* note: this is same as "adding to VALID LIST" in the spec -+ text */ -+ if (new_pair) -+ new_pair->valid = TRUE; - /* step: The agent sets the state of the pair that *generated* the check to - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ -@@ -2796,12 +2807,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - agent, p, new_pair); - } - -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -- if (new_pair) { -- new_pair->valid = TRUE; -+ if (new_pair && new_pair->valid) - nice_component_add_valid_candidate (component, remote_candidate); -- } -+ - - return new_pair; - } --- -2.13.6 - - -From 15c0546f624113b8c0546a1f883a48bff7020f1b Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 19 Apr 2016 17:06:32 +0200 -Subject: [PATCH 29/70] conncheck: improve the selection of the pairs to be - checked - -This patch aims to implement more closely the algorithm described -in RFC 5245 indicating how pairs are transitionned from state Frozen -to Waiting. This is described in 7.1.3.2 when a check succeeded, and -correspond to modifications in function priv_conn_check_unfreeze_related(). -This is also described in 5.7.4 when defining the initial state of the -pairs in a conncheck, and correspond to modifications in function -priv_conn_check_unfreeze_next(). - -This patch introduces the notion of active and frozen check list. It -allows us to define the timer restranmission delay as described in 16.1. - -Another modification in priv_conn_check_tick_unlocked() is that every -stream in handled consecutively, and in an independant way. The pacing -was previously of a single STUN request emitted per callback, it is now -of a triggered check per callback OR a single STUN per callback AND per -stream per callback. - -The description of ordinary checks per stream in 5.8 is detailled in -function priv_conn_check_tick_stream(), and a remaining of the code -used to nominate a pair by the controlling agent is put in a dedicated -function priv_conn_check_tick_stream_nominate() - -Differential Revision: https://phabricator.freedesktop.org/D813 ---- - agent/conncheck.c | 535 ++++++++++++++++++++++++++++++++++++++---------------- - agent/stream.c | 21 --- - agent/stream.h | 3 - - 3 files changed, 381 insertions(+), 178 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index ef8df68..6b1b7e3 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -212,6 +212,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) - } - - /* -+ * Check if the conncheck list if Active according to -+ * ICE spec, 5.7.4 (Computing States) -+ * -+ * note: the ICE spec in unclear about that, but the check list should -+ * be considered active when there is at least a pair in Waiting state -+ * OR a pair in In-Progress state. -+ */ -+static gboolean -+priv_is_checklist_active (NiceStream *stream) -+{ -+ GSList *i; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS) -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* -+ * Check if the conncheck list if Frozen according to -+ * ICE spec, 5.7.4 (Computing States) -+ */ -+static gboolean -+priv_is_checklist_frozen (NiceStream *stream) -+{ -+ GSList *i; -+ -+ if (stream->conncheck_list == NULL) -+ return FALSE; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state != NICE_CHECK_FROZEN) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* -+ * Check if all components of the stream have -+ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.) -+ */ -+static gboolean -+priv_all_components_have_valid_pair (NiceStream *stream) -+{ -+ guint i; -+ GSList *j; -+ -+ for (i = 1; i <= stream->n_components; i++) { -+ for (j = stream->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (p->component_id == i && p->valid) -+ break; -+ } -+ if (j == NULL) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* -+ * Check if the foundation in parameter matches the foundation -+ * of a valid pair in the conncheck list [of stream] (used for ICE spec, -+ * 7.1.3.2.3, point 2.) -+ */ -+static gboolean -+priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream) -+{ -+ GSList *i; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->valid && -+ strncmp (p->foundation, foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* - * Finds the next connectivity check in WAITING state. - */ - static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list) -@@ -220,7 +303,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - - /* note: list is sorted in priority order to first waiting check has - * the highest priority */ -- - for (i = conn_check_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->state == NICE_CHECK_WAITING) -@@ -231,6 +313,74 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - } - - /* -+ * Finds the next connectivity check in FROZEN state. -+ */ -+static CandidateCheckPair * -+priv_conn_check_find_next_frozen (GSList *conn_check_list) -+{ -+ GSList *i; -+ -+ /* note: list is sorted in priority order to first frozen check has -+ * the highest priority */ -+ for (i = conn_check_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_FROZEN) -+ return p; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Returns the number of check lists of the agent -+ */ -+static guint -+priv_number_of_check_lists (NiceAgent *agent) -+{ -+ guint n = 0; -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (stream->conncheck_list != NULL) -+ n++; -+ } -+ return n; -+} -+ -+/* -+ * Returns the number of active check lists of the agent -+ */ -+static guint -+priv_number_of_active_check_lists (NiceAgent *agent) -+{ -+ guint n = 0; -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) -+ if (priv_is_checklist_active (i->data)) -+ n++; -+ return n; -+} -+ -+/* -+ * Returns the first stream of the agent having a Frozen -+ * connection check list -+ */ -+static NiceStream * -+priv_find_first_frozen_check_list (NiceAgent *agent) -+{ -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (priv_is_checklist_frozen (stream)) -+ return stream; -+ } -+ return NULL; -+} -+ -+/* - * Initiates a new connectivity check for a ICE candidate pair. - * - * @return TRUE on success, FALSE on error -@@ -248,58 +398,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * - /* - * Unfreezes the next connectivity check in the list. Follows the - * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec -- * (ID-19), with some exceptions (see comments in code). -+ * (RFC5245) - * - * See also sect 7.1.2.2.3 (Updating Pair States), and - * priv_conn_check_unfreeze_related(). - * - * @return TRUE on success, and FALSE if no frozen candidates were found. - */ --static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) -+static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream) - { -- CandidateCheckPair *pair = NULL; - GSList *i, *j; -- -- /* XXX: the unfreezing is implemented a bit differently than in the -- * current ICE spec, but should still be interoperate: -- * - checks are not grouped by foundation -- * - one frozen check is unfrozen (lowest component-id, highest -- * priority) -- */ -+ GSList *found_list = NULL; -+ gboolean result = FALSE; - - priv_print_conn_check_lists (agent, G_STRFUNC, NULL); - -- for (i = agent->streams; i; i = i->next) { -- NiceStream *stream = i->data; -- guint64 max_frozen_priority = 0; -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p1 = i->data; -+ CandidateCheckPair *pair = NULL; -+ guint lowest_component_id = stream->n_components + 1; -+ guint64 highest_priority = 0; - -+ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp)) -+ continue; -+ found_list = g_slist_prepend (found_list, p1->foundation); - - for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *p = j->data; -- -- /* XXX: the prio check could be removed as the pairs are sorted -- * already */ -- -- if (p->state == NICE_CHECK_FROZEN) { -- if (p->priority > max_frozen_priority) { -- max_frozen_priority = p->priority; -- pair = p; -- } -+ CandidateCheckPair *p2 = i->data; -+ if (strncmp (p2->foundation, p1->foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { -+ if (p2->component_id < lowest_component_id || -+ (p2->component_id == lowest_component_id && -+ p2->priority > highest_priority)) { -+ pair = p2; -+ lowest_component_id = p2->component_id; -+ highest_priority = p2->priority; -+ } - } - } - -- if (pair) -- break; -- } -- -- if (pair) { -- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -- return TRUE; -+ if (pair) { -+ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", -+ agent, pair, pair->stream_id, pair->component_id, pair->foundation); -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ result = TRUE; -+ } - } -- -- return FALSE; -+ g_slist_free (found_list); -+ return result; - } - - /* -@@ -316,7 +463,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) - { - GSList *i, *j; -- guint unfrozen = 0; - - g_assert (ok_check); - g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); -@@ -336,40 +482,59 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); - p->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- ++unfrozen; - } - } - } - - /* step: perform the step (2) of 'Updating Pair States' */ - stream = agent_find_stream (agent, ok_check->stream_id); -- if (nice_stream_all_components_ready (stream)) { -- /* step: unfreeze checks from other streams */ -+ if (priv_all_components_have_valid_pair (stream)) { - for (i = agent->streams; i ; i = i->next) { -+ /* the agent examines the check list for each other -+ * media stream in turn -+ */ - NiceStream *s = i->data; -- for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *p = j->data; -- -- if (p->stream_id == s->id && -- p->stream_id != ok_check->stream_id) { -- if (p->state == NICE_CHECK_FROZEN && -- strcmp (p->foundation, ok_check->foundation) == 0) { -+ if (s->id == ok_check->stream_id) -+ continue; -+ if (priv_is_checklist_active (s)) { -+ /* checklist is Active -+ */ -+ for (j = s->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (p->state == NICE_CHECK_FROZEN && -+ priv_foundation_matches_a_valid_pair (p->foundation, stream)) { - nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); - p->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- ++unfrozen; -- -- } -- } -+ } -+ } -+ } else if (priv_is_checklist_frozen (s)) { -+ /* checklist is Frozen -+ */ -+ gboolean match_found = FALSE; -+ /* check if there is one pair in the check list whose -+ * foundation matches a pair in the valid list under -+ * consideration -+ */ -+ for (j = s->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { -+ match_found = TRUE; -+ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); -+ p->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ } -+ } -+ -+ if (!match_found) { -+ /* set the pair with the lowest component ID -+ * and highest priority to Waiting -+ */ -+ priv_conn_check_unfreeze_next (agent, s); -+ } - } -- /* note: only unfreeze check from one stream at a time */ -- if (unfrozen) -- break; - } - } -- -- if (unfrozen == 0) -- priv_conn_check_unfreeze_next (agent); - } - - static void -@@ -400,14 +565,13 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) - { - gboolean keep_timer_going = FALSE; -- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, -- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; -- guint frozen = 0, waiting = 0; -- GSList *i, *k; -+ GSList *i; -+ CandidateCheckPair *pair; - -+ /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -451,7 +615,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - p->next_tick = *now; - g_time_val_add (&p->next_tick, timeout * 1000); - -- *stun_transmitted = TRUE; - return TRUE; - } - case STUN_USAGE_TIMER_RETURN_SUCCESS: -@@ -471,7 +634,57 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - } - } - } -+ } - -+ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" -+ * note: This code is executed when the triggered checks list is -+ * empty, and when no STUN message has been sent (pacing constraint) -+ */ -+ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -+ if (pair) { -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ -+ /* note: this is unclear in the ICE spec, but a check list in Frozen -+ * state (where all pairs are in Frozen state) is not supposed to -+ * change its state by an ordinary check, but only by the success of -+ * checks in other check lists, in priv_conn_check_unfreeze_related(). -+ * The underlying idea is to concentrate the checks on a single check -+ * list initially. -+ */ -+ if (priv_is_checklist_frozen (stream)) -+ return keep_timer_going; -+ -+ /* step: ordinary check continued, if there's no pair in the waiting -+ * state, pick a pair in the frozen state -+ */ -+ pair = priv_conn_check_find_next_frozen (stream->conncheck_list); -+ if (pair) { -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ return keep_timer_going; -+} -+ -+static gboolean -+priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) -+{ -+ gboolean keep_timer_going = FALSE; -+ guint s_inprogress = 0; -+ guint s_succeeded = 0; -+ guint s_discovered = 0; -+ guint s_nominated = 0; -+ guint s_waiting_for_nomination = 0; -+ guint s_valid = 0; -+ guint frozen = 0; -+ guint waiting = 0; -+ GSList *i, *k; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; - if (p->state == NICE_CHECK_FROZEN) - ++frozen; - else if (p->state == NICE_CHECK_IN_PROGRESS) -@@ -504,13 +717,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - keep_timer_going = TRUE; - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { - if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && -- agent->controlling_mode && -- ((waiting == 0 && s_inprogress == 0) || -- (s_succeeded + s_discovered) >= 5 * stream->n_components)){ -+ agent->controlling_mode) { -+#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2 - /* ICE 8.1.1.1 Regular nomination -- * we choose to nominate the valid pair if -- * there is no pair left waiting or in-progress or -- * if there are at least 5 valid pairs per stream on average. -+ * we choose to nominate the valid pair of a component if -+ * - there is no pair left frozen, waiting or in-progress, or -+ * - if there are at least two valid pairs, or -+ * - if there is at least one valid pair of type HOST-HOST - * - * This is the "stopping criterion" described in 8.1.1.1, and is - * a "local optimization" between accumulating more valid pairs, -@@ -523,36 +736,63 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - component_item = component_item->next) { - NiceComponent *component = component_item->data; - gboolean already_done = FALSE; -+ gboolean stopping_criterion = FALSE; -+ guint p_valid = 0; -+ guint p_frozen = 0; -+ guint p_waiting = 0; -+ guint p_inprogress = 0; -+ guint p_host_host_valid = 0; - - /* verify that the choice of the pair to be nominated - * has not already been done - */ - for (k = stream->conncheck_list; k ; k = k->next) { - CandidateCheckPair *p = k->data; -- if (p->component_id == component->id && -- p->use_candidate_on_next_check) { -- already_done = TRUE; -- break; -+ if (p->component_id == component->id) { -+ if (p->use_candidate_on_next_check) -+ already_done = TRUE; -+ if (p->state == NICE_CHECK_FROZEN) -+ p_frozen++; -+ else if (p->state == NICE_CHECK_WAITING) -+ p_waiting++; -+ else if (p->state == NICE_CHECK_IN_PROGRESS) -+ p_inprogress++; -+ if (p->valid) -+ p_valid++; -+ if (p->valid && -+ p->local->type == NICE_CANDIDATE_TYPE_HOST && -+ p->remote->type == NICE_CANDIDATE_TYPE_HOST) -+ p_host_host_valid++; - } - } - -- /* choose a pair to be nominated in the list of valid -- * pairs, and add it to the triggered checks list -+ if (already_done) -+ continue; -+ -+ stopping_criterion = -+ (p_host_host_valid > 0 || -+ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS || -+ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0)); -+ -+ if (!stopping_criterion) -+ continue; -+ -+ /* when the stopping criterion is satisfied, we choose -+ * a pair to be nominated in the list of valid pairs, -+ * and add it to the triggered checks list - */ -- if (!already_done) { -- for (k = stream->conncheck_list; k ; k = k->next) { -- CandidateCheckPair *p = k->data; -- /* note: highest priority item selected (list always sorted) */ -- if (p->component_id == component->id && -- !p->nominated && -- !p->use_candidate_on_next_check && -- p->valid) { -- nice_debug ("Agent %p : restarting check %p with " -- "USE-CANDIDATE attrib (regular nomination)", agent, p); -- p->use_candidate_on_next_check = TRUE; -- priv_add_pair_to_triggered_check_queue (agent, p); -- break; /* move to the next component */ -- } -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ /* note: highest priority item selected (list always sorted) */ -+ if (p->component_id == component->id && -+ !p->nominated && -+ !p->use_candidate_on_next_check && -+ p->valid) { -+ nice_debug ("Agent %p : restarting check %p with " -+ "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->use_candidate_on_next_check = TRUE; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ break; /* move to the next component */ - } - } - } -@@ -615,70 +855,55 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - { - CandidateCheckPair *pair = NULL; - gboolean keep_timer_going = FALSE; -- gboolean res; -- /* note: we try to only generate a single stun transaction per timer -- * callback, to respect some pacing of STUN transaction, as per -- * appendix B.1 of ICE spec. -- */ -- gboolean stun_transmitted = FALSE; - GSList *i, *j; - GTimeVal now; - -- /* step: process ongoing STUN transactions */ - g_get_current_time (&now); - -- for (j = agent->streams; j; j = j->next) { -- NiceStream *stream = j->data; -- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); -- if (res) -- keep_timer_going = res; -- if (stun_transmitted) -- return TRUE; -- } -- -- /* step: first initiate a conncheck with a pair from the triggered list */ -- pair = priv_get_pair_from_triggered_check_queue (agent); -- -- if (pair) { -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a pair from triggered check list"); -- priv_conn_check_initiate (agent, pair); -+ /* the conncheck really starts when we have built -+ * a connection check list for each stream -+ */ -+ if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) - return TRUE; -- } - -- /* step: when the triggered list is empty, -- * find the highest priority waiting check and send it */ -- for (i = agent->streams; i ; i = i->next) { -- NiceStream *stream = i->data; -- -- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -- if (pair) -- break; -+ /* configure the initial state of the check lists of the agent -+ * as described in ICE spec, 5.7.4 -+ * -+ * if all pairs in all check lists are in frozen state, then -+ * we are in the initial state (5.7.4, point 1.) -+ */ -+ if (priv_number_of_active_check_lists (agent) == 0) { -+ /* set some pairs of the first stream in the waiting state -+ * ICE spec, 5.7.4, point 2. -+ * -+ * note: we adapt the ICE spec here, by selecting the first -+ * frozen check list, which is not necessarily the check -+ * list of the first stream (the first stream may be completed) -+ */ -+ NiceStream *stream = priv_find_first_frozen_check_list (agent); -+ if (stream) -+ priv_conn_check_unfreeze_next (agent, stream); - } - -+ /* step: perform a test from the triggered checks list, -+ * ICE spec, 5.8 "Scheduling Checks" -+ */ -+ pair = priv_get_pair_from_triggered_check_queue (agent); -+ - if (pair) { - priv_conn_check_initiate (agent, pair); - return TRUE; - } - -- /* step: when there's no pair in the Waiting state, -- * unfreeze a new pair and check it -+ /* step: process ongoing STUN transactions and -+ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" - */ -- priv_conn_check_unfreeze_next (agent); -- - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -- -- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -- if (pair) -- break; -- } -- -- if (pair) { -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a pair in Waiting state"); -- priv_conn_check_initiate (agent, pair); -- return TRUE; -+ if (priv_conn_check_tick_stream (stream, agent, &now)) -+ keep_timer_going = TRUE; -+ if (priv_conn_check_tick_stream_nominate (stream, agent)) -+ keep_timer_going = TRUE; - } - - /* step: stop timer if no work left */ -@@ -2169,30 +2394,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - - /* Implement the computation specific in RFC 5245 section 16 */ - --static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) -+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream) - { -- GSList *item1, *item2; -+ GSList *i; - guint waiting_and_in_progress = 0; -+ guint n = 0; - unsigned int rto = 0; - -- -- for (item1 = agent->streams; item1; item1 = item1->next) { -- NiceStream *stream = item1->data;; -- for (item2 = stream->conncheck_list; item2; item2 = item2->next) { -- CandidateCheckPair *pair = item2->data; -- -- if (pair->state == NICE_CHECK_IN_PROGRESS || -- pair->state == NICE_CHECK_WAITING) -- waiting_and_in_progress++; -- } -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_IN_PROGRESS || -+ p->state == NICE_CHECK_WAITING) -+ waiting_and_in_progress++; - } - -- rto = agent->timer_ta * waiting_and_in_progress; -+ n = priv_number_of_active_check_lists (agent); -+ rto = agent->timer_ta * n * waiting_and_in_progress; - - /* We assume non-reliable streams are RTP, so we use 100 as the max */ -- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", -+ nice_debug ("Agent %p : timer set to %dms, " -+ "waiting+in_progress=%d, nb_active=%d", - agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), -- waiting_and_in_progress); -+ waiting_and_in_progress, n); - if (agent->reliable) - return MAX (rto, 500); - else -@@ -2312,7 +2535,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); - } else { - stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent), -+ priv_compute_conncheck_timer (agent, stream), - agent->stun_max_retransmissions); - } - -@@ -2477,7 +2700,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p->timer_restarted ? "no" : "yes"); - if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { - stun_timer_start (&p->timer, -- priv_compute_conncheck_timer (agent), -+ priv_compute_conncheck_timer (agent, stream), - agent->stun_max_retransmissions); - p->timer_restarted = TRUE; - } -@@ -2769,7 +2992,6 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); -- priv_conn_check_unfreeze_related (agent, stream, p); - nice_component_add_valid_candidate (component, remote_candidate); - } - else { -@@ -2894,7 +3116,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - g_assert_not_reached (); - nice_debug ("Agent %p : Mapped address not found." - " conncheck %p SUCCEEDED.", agent, p); -- priv_conn_check_unfreeze_related (agent, stream, p); - nice_component_add_valid_candidate (component, p->remote); - } else { - ok_pair = priv_process_response_check_for_reflexive (agent, -@@ -2902,6 +3123,12 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - local_candidate, remote_candidate); - } - -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ - /* Note: this assignment helps to reduce the numbers of cases - * to be tested. If ok_pair and p refer to distinct pairs, it - * means that ok_pair is a discovered peer reflexive one, -diff --git a/agent/stream.c b/agent/stream.c -index 8121e12..533ff15 100644 ---- a/agent/stream.c -+++ b/agent/stream.c -@@ -104,27 +104,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) - } - - /* -- * Returns true if all components of the stream are either -- * 'CONNECTED' or 'READY' (connected plus nominated). -- */ --gboolean --nice_stream_all_components_ready (NiceStream *stream) --{ -- GSList *i; -- -- for (i = stream->components; i; i = i->next) { -- NiceComponent *component = i->data; -- if (component && -- !(component->state == NICE_COMPONENT_STATE_CONNECTED || -- component->state == NICE_COMPONENT_STATE_READY)) -- return FALSE; -- } -- -- return TRUE; --} -- -- --/* - * Initialized the local crendentials for the stream. - */ - void -diff --git a/agent/stream.h b/agent/stream.h -index f9188cb..954ba66 100644 ---- a/agent/stream.h -+++ b/agent/stream.h -@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); - void - nice_stream_close (NiceStream *stream); - --gboolean --nice_stream_all_components_ready (NiceStream *stream); -- - NiceComponent * - nice_stream_find_component_by_id (NiceStream *stream, guint id); - --- -2.13.6 - - -From ead3453d04fc70865d176ab073636f8b9078cbbc Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 13:20:38 +0200 -Subject: [PATCH 30/70] conncheck: invoke the debug dump in more places - -Differential Revision: https://phabricator.freedesktop.org/D1123 ---- - agent/conncheck.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 6b1b7e3..2d2224d 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -642,6 +642,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - */ - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Waiting state"); - priv_conn_check_initiate (agent, pair); - return TRUE; - } -@@ -661,6 +663,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - */ - pair = priv_conn_check_find_next_frozen (stream->conncheck_list); - if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Frozen state"); - pair->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, pair); - priv_conn_check_initiate (agent, pair); -@@ -891,6 +895,8 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - pair = priv_get_pair_from_triggered_check_queue (agent); - - if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair from triggered check list"); - priv_conn_check_initiate (agent, pair); - return TRUE; - } --- -2.13.6 - - -From 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 19 Apr 2016 17:59:27 +0200 -Subject: [PATCH 31/70] conncheck: improve triggered check of in-progress pairs - -This patch update the way triggered checks of in-progress pairs are -handled, according to ICE spec, section 7.2.1.4. Previously the same -connection check was retransmitted with an updated timeout. This causes -problems when a controlling role switch occurs in this time frame. -This is the reason why a new connection check must be generated -reflecting the updated role. We introduce a new flag "recheck_on_timeout" -in the pair indicating that the pair must be rechecked at the next timer -expiration. - -Differential Revision: https://phabricator.freedesktop.org/D875 ---- - agent/conncheck.c | 88 +++++++++++++++++++++++++++++++++++++++++++++---------- - agent/conncheck.h | 2 +- - 2 files changed, 74 insertions(+), 16 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 2d2224d..3a489fe 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -558,6 +558,37 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP - } - - /* -+ * Function that resubmits a new connection check, after a previous -+ * check in in-progress state got cancelled due to an incoming stun -+ * request matching this same pair -+ * -+ * @return will return TRUE if the pair is scheduled for recheck -+ */ -+static gboolean -+priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) -+{ -+ if (p->recheck_on_timeout) { -+ g_assert (p->state == NICE_CHECK_IN_PROGRESS); -+ /* this cancelled pair may have the flag 'mark nominated -+ * on response arrival' set, we want to keep it, because -+ * this is needed to nominate this pair in aggressive -+ * nomination, when the agent is in controlled mode. -+ * -+ * this cancelled pair may also have the flag 'use candidate -+ * on next check' set, that we want to preserve too. -+ */ -+ nice_debug ("Agent %p : pair %p was cancelled, " -+ "triggering a new connection check", agent, p); -+ p->recheck_on_timeout = FALSE; -+ p->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* - * Helper function for connectivity check timer callback that - * runs through the stream specific part of the state machine. - * -@@ -584,8 +615,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - switch (stun_timer_refresh (&p->timer)) { - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - { -- /* case: error, abort processing */ - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; -+ -+ /* case: error, abort processing */ - nice_address_to_string (&p->local->addr, tmpbuf1); - nice_address_to_string (&p->remote->addr, tmpbuf2); - nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -@@ -600,8 +640,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - } - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: - { -- /* case: not ready, so schedule a new timeout */ - unsigned int timeout = stun_timer_remainder (&p->timer); -+ -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; -+ -+ /* case: not ready, so schedule a new timeout */ - nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " - "(timeout %dms, delay=%dms, retrans=%d).", - agent, p, timeout, p->timer.delay, p->timer.retransmissions); -@@ -642,6 +691,12 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - */ - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) { -+ /* remove the pair from the triggered check list if needed. This -+ * may happen with the cancelled pair, that's just been added -+ * in state waiting to the triggered check list above in the -+ * same function. -+ */ -+ priv_remove_pair_from_triggered_check_queue (agent, pair); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair in Waiting state"); - priv_conn_check_initiate (agent, pair); -@@ -794,6 +849,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->valid) { - nice_debug ("Agent %p : restarting check %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->recheck_on_timeout = FALSE; - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ -@@ -816,6 +872,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -+ p->recheck_on_timeout = FALSE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ -@@ -2697,19 +2754,20 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p->state == NICE_CHECK_FROZEN) - priv_add_pair_to_triggered_check_queue (agent, p); - else if (p->state == NICE_CHECK_IN_PROGRESS) { -- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), -- * we should cancel the existing one, instead we reset our timer, so -- * we'll resend the exiting transactions faster if needed...? :P -- */ -- nice_debug ("Agent %p : check already in progress, " -- "restarting the timer again?: %s ..", agent, -- p->timer_restarted ? "no" : "yes"); -- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { -- stun_timer_start (&p->timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -- p->timer_restarted = TRUE; -- } -+ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -+ * we cancel the in-progress transaction, and after the -+ * retransmission timeout, we create a new connectivity check -+ * for that pair. The controlling role of this new check may -+ * be different from the role of this cancelled check. -+ */ -+ if (!nice_socket_is_reliable (p->sockptr)) { -+ nice_debug ("Agent %p : check already in progress, " -+ "cancelling this check..", agent); -+ /* this flag will determine the action at the retransmission -+ * timeout of the stun timer -+ */ -+ p->recheck_on_timeout = TRUE; -+ } - } - else if (p->state == NICE_CHECK_SUCCEEDED || - p->state == NICE_CHECK_DISCOVERED) { -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 0f988de..785a6cd 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -85,10 +85,10 @@ struct _CandidateCheckPair - gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; - NiceCheckState state; - gboolean nominated; -- gboolean timer_restarted; - gboolean valid; - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; -+ gboolean recheck_on_timeout; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ --- -2.13.6 - - -From 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 13:25:16 +0200 -Subject: [PATCH 32/70] conncheck: link succeeded and discovered pairs - -When the agent has the role of the stun server, is in controlled mode, -and receives a pair with the "use-candidate" attribute set, it must find -a matching succeded or discovered pair in its conncheck list. This is -described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1. -When a matching pair is in succeeded state, the agent must nominate the -valid pair (a discovered pair) constructed from section 7.1.3.2.2, -that's been created from this succeeded one. To make this lookup, we -introduce a new "discovered_pair" member of the CandidateCheckPair -struct, that links the succeeded pair, and its discovered pair -if any. - -Differential Revision: https://phabricator.freedesktop.org/D878 ---- - agent/conncheck.c | 7 +++++++ - agent/conncheck.h | 1 + - 2 files changed, 8 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 3a489fe..99cb6d2 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1928,6 +1928,12 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - * candidate, generating a "discovered" pair that can be - * nominated. - */ -+ if (pair->state == NICE_CHECK_SUCCEEDED && -+ pair->discovered_pair != NULL) { -+ pair = pair->discovered_pair; -+ g_assert (pair->state == NICE_CHECK_DISCOVERED); -+ } -+ - if (pair->valid) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", - agent, pair, pair->foundation); -@@ -2936,6 +2942,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->remote = parent_pair->remote; - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; -+ parent_pair->discovered_pair = pair; - nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 785a6cd..dd47ebe 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -89,6 +89,7 @@ struct _CandidateCheckPair - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; - gboolean recheck_on_timeout; -+ struct _CandidateCheckPair *discovered_pair; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ --- -2.13.6 - - -From 9103a5f2e184211fc160d1d3070ce4d043c71ff0 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 19 Apr 2016 18:16:26 +0200 -Subject: [PATCH 33/70] conncheck: use the right pair when retriggering a check - -This patch completes the previous patch by adding a link back from the -discovered pair, to the parent pair that generated this check. This link -is needed by the ICE spec, to comply with section 8.1.1.1, "Regular -nomination", where the check to be retriggered is the initial check that -caused the discovery of the valid pair. When the valid pair is a -peer-reflexive pair, the retriggered check must target the succeeded -pair, and not the valid discovered pair. - -Differential Revision: https://phabricator.freedesktop.org/D879 ---- - agent/conncheck.c | 21 ++++++++++++++++++--- - agent/conncheck.h | 1 + - 2 files changed, 19 insertions(+), 3 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 99cb6d2..79685df 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -847,6 +847,16 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - !p->nominated && - !p->use_candidate_on_next_check && - p->valid) { -+ /* According a ICE spec, sect 8.1.1.1. "Regular -+ * Nomination", we enqueue the check that produced this -+ * valid pair. When this pair has been discovered, we want -+ * to test its parent pair instead. -+ */ -+ if (p->succeeded_pair != NULL) { -+ g_assert (p->state == NICE_CHECK_DISCOVERED); -+ p = p->succeeded_pair; -+ } -+ g_assert (p->state == NICE_CHECK_SUCCEEDED); - nice_debug ("Agent %p : restarting check %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); - p->recheck_on_timeout = FALSE; -@@ -2754,6 +2764,11 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * tcp-active we don't want to retrigger a check on a pair that - * was FAILED when a peer-reflexive pair was created */ - -+ if (p->succeeded_pair != NULL) { -+ g_assert (p->state == NICE_CHECK_DISCOVERED); -+ p = p->succeeded_pair; -+ } -+ - nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); - - if (p->state == NICE_CHECK_WAITING || -@@ -2775,8 +2790,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p->recheck_on_timeout = TRUE; - } - } -- else if (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) { -+ else if (p->state == NICE_CHECK_SUCCEEDED) { - nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); - /* note: this is a bit unsure corner-case -- let's do the - same state update as for processing responses to our own checks */ -@@ -2943,6 +2957,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; - parent_pair->discovered_pair = pair; -+ pair->succeeded_pair = parent_pair; - nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; -@@ -4163,7 +4178,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - - pair = priv_conn_check_add_for_candidate_pair_matched (agent, - stream->id, component, local_candidate, remote_candidate, -- NICE_CHECK_DISCOVERED); -+ NICE_CHECK_SUCCEEDED); - if (pair) { - pair->valid = TRUE; - } -diff --git a/agent/conncheck.h b/agent/conncheck.h -index dd47ebe..c07fb22 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -90,6 +90,7 @@ struct _CandidateCheckPair - gboolean mark_nominated_on_response_arrival; - gboolean recheck_on_timeout; - struct _CandidateCheckPair *discovered_pair; -+ struct _CandidateCheckPair *succeeded_pair; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ --- -2.13.6 - - -From 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 13:30:04 +0200 -Subject: [PATCH 34/70] conncheck: fix a nomination corner case - -This patch add two supplementary cases, not covered by the ICE spec, -sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent -receives a STUN request with the USE-CANDIDATE flag, for a pair that is -in the waiting state. We consider that this case is similar to the -in-progress state, and should be handled in the same way. We also accept -when the pair is in frozen state. This latter case happens in the -new-dribble test, when an agent replays incoming early connchecks. - -Differential Revision: https://phabricator.freedesktop.org/D880 ---- - agent/conncheck.c | 35 +++++++++++++++++++++++++++++++++-- - 1 file changed, 33 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 79685df..4f4af40 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1963,6 +1963,29 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - "will be nominated on response receipt.", - agent, pair, pair->foundation); - } -+ /* note: this case is not covered by the ICE spec, 7.2.1.5, -+ * "Updating the Nominated Flag", but a pair in waiting state -+ * deserves the same treatment than a pair in-progress. -+ */ -+ if (pair->state == NICE_CHECK_WAITING) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is waiting, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } -+ /* note: this case is not covered by the ICE spec, 7.2.1.5, -+ * "Updating the Nominated Flag" either, but a pair in frozen -+ * state, and in the triggered check list should also be -+ * considered like a pair in-progress. This case happens with -+ * the new-dribble test, when an agent replays incoming early -+ * connchecks. -+ */ -+ if (pair->state == NICE_CHECK_FROZEN) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is frozen, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } - } else if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; -@@ -2703,17 +2726,25 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - "is %" G_GUINT64_FORMAT, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ -+ /* note: this case is not covered by the ICE spec, 8.1.2 -+ * "Updating States", but a pair in waiting state which will be -+ * nominated on response receipt should be treated the same way -+ * that an in-progress pair. -+ */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->component_id == component_id) { - if (p->state == NICE_CHECK_FROZEN || -- p->state == NICE_CHECK_WAITING) { -+ (p->state == NICE_CHECK_WAITING && -+ !p->mark_nominated_on_response_arrival)) { - p->state = NICE_CHECK_CANCELLED; - nice_debug ("Agent XXX : pair %p state CANCELED", p); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- if (p->state == NICE_CHECK_IN_PROGRESS) { -+ if (p->state == NICE_CHECK_IN_PROGRESS || -+ (p->state == NICE_CHECK_WAITING && -+ p->mark_nominated_on_response_arrival)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { - p->stun_message.buffer = NULL; --- -2.13.6 - - -From afd8d41bb34afb3864e838ef79026ae4ef15c0d4 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 13:32:49 +0200 -Subject: [PATCH 35/70] conncheck: new pairs never have the nominated flag - preset - -This patch disables the possibility to set the nominated flag of a -candidate pair at creation time. This possibility was used when a new -pair is created from a new peer reflexive remote candidate, when the -agent is in controlled mode, and an stun request with USE-CANDIDATE is -received. In this case, since previous commit "conncheck: fix a -nomination corner case", we set the nominated flag when the stun -response of this new pair will arrive, and not before. Consequently, -this flag is no longer required when the pair is created. - -Differential Revision: https://phabricator.freedesktop.org/D881 ---- - agent/conncheck.c | 21 +++++++++++---------- - 1 file changed, 11 insertions(+), 10 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 4f4af40..3cd0424 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -65,7 +65,7 @@ - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); - static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); - static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); --static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); - static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); - static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, - guint component_id, NiceCandidate *remote, NiceCandidate *local, -@@ -1573,7 +1573,8 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); - if (icheck->use_candidate) - priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, pair->remote); - } - } - } -@@ -1716,7 +1717,8 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - - if (icheck->use_candidate) - priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, candidate); - } - } - } -@@ -2043,7 +2045,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority) - */ - static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - guint stream_id, NiceComponent *component, NiceCandidate *local, -- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) -+ NiceCandidate *remote, NiceCheckState initial_state) - { - NiceStream *stream; - CandidateCheckPair *pair; -@@ -2081,7 +2083,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr)); - } -- pair->nominated = use_candidate; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); - -@@ -2127,7 +2128,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - agent, local->foundation, remote->foundation, - stream_id, component->id); - pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, -- initial_state, FALSE); -+ initial_state); - if (component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY) { - agent_signal_component_state_change (agent, -@@ -2774,9 +2775,8 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - * @param component the check is related to - * @param local_socket socket from which the inbound check was received - * @param remote_cand remote candidate from which the inbound check was sent -- * @param use_candidate whether the original check had USE-CANDIDATE attribute set - */ --static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand) - { - GSList *i; - NiceCandidate *local = NULL; -@@ -2872,7 +2872,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - - if (i) { - nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); -- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate); -+ priv_add_new_check_pair (agent, stream->id, component, -+ local, remote_cand, NICE_CHECK_WAITING); - return TRUE; - } - else { -@@ -2926,7 +2927,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, - - if (rcand) { - /* note: upon successful check, make the reserve check immediately */ -- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); -+ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); - - if (use_candidate) - priv_mark_pair_nominated (agent, stream, component, lcand, rcand); --- -2.13.6 - - -From 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 12:56:28 +0200 -Subject: [PATCH 36/70] conncheck: update the pair state in triggered check - list - -With this patch, we update the state of the pair to waiting when -it is put in the triggered check queue. We also take care to call -priv_schedule_triggered_check() before priv_mark_pair_nominated() -so a pair to be rechecked and put on the triggered check queue -will have a unique state to be tested in the following call to -priv_mark_pair_nominated() when evaluating its nomination attributes. - -Differential Revision: https://phabricator.freedesktop.org/D883 ---- - agent/conncheck.c | 33 +++++++++------------------------ - 1 file changed, 9 insertions(+), 24 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 3cd0424..9950970 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -183,6 +183,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa - { - g_assert (pair); - -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); - if (agent->triggered_check_queue == NULL || - g_slist_find (agent->triggered_check_queue, pair) == NULL) - agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -@@ -580,8 +582,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - nice_debug ("Agent %p : pair %p was cancelled, " - "triggering a new connection check", agent, p); - p->recheck_on_timeout = FALSE; -- p->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } -@@ -1571,10 +1571,10 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - if (nice_address_equal (&icheck->from, &pair->remote->addr) && - icheck->local_socket == pair->sockptr) { - nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); - priv_schedule_triggered_check (agent, stream, component, - icheck->local_socket, pair->remote); -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); - } - } - } -@@ -1715,10 +1715,10 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - else - conn_check_add_for_candidate (agent, stream->id, component, candidate); - -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); - priv_schedule_triggered_check (agent, stream, component, - icheck->local_socket, candidate); -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); - } - } - } -@@ -1967,7 +1967,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - } - /* note: this case is not covered by the ICE spec, 7.2.1.5, - * "Updating the Nominated Flag", but a pair in waiting state -- * deserves the same treatment than a pair in-progress. -+ * deserves the same treatment than a pair in-progress. A pair -+ * can be in waiting state as the result of being enqueued in -+ * the triggered check list for example. - */ - if (pair->state == NICE_CHECK_WAITING) { - pair->mark_nominated_on_response_arrival = TRUE; -@@ -1975,19 +1977,6 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - "will be nominated on response receipt.", - agent, pair, pair->foundation); - } -- /* note: this case is not covered by the ICE spec, 7.2.1.5, -- * "Updating the Nominated Flag" either, but a pair in frozen -- * state, and in the triggered check list should also be -- * considered like a pair in-progress. This case happens with -- * the new-dribble test, when an agent replays incoming early -- * connchecks. -- */ -- if (pair->state == NICE_CHECK_FROZEN) { -- pair->mark_nominated_on_response_arrival = TRUE; -- nice_debug ("Agent %p : pair %p (%s) is frozen, " -- "will be nominated on response receipt.", -- agent, pair, pair->foundation); -- } - } else if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; -@@ -2926,9 +2915,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, - } - - if (rcand) { -- /* note: upon successful check, make the reserve check immediately */ - priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); -- - if (use_candidate) - priv_mark_pair_nominated (agent, stream, component, lcand, rcand); - } -@@ -3345,9 +3332,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - - p->stun_message.buffer = NULL; - p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_WAITING; - priv_add_pair_to_triggered_check_queue (agent, p); -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); - } - trans_found = TRUE; - } else { --- -2.13.6 - - -From 11d4e37a030eb144a355dc26c705ef5aa5a975a7 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Fri, 1 Apr 2016 17:31:44 +0200 -Subject: [PATCH 37/70] conncheck: remove a useless pair recheck - -This exception to the ICE spec is no longer needed: when a pair is in -the succeeded state, there is no needed to recheck it again upon -reception of an incoming stun request on it. - -Differential Revision: https://phabricator.freedesktop.org/D884 ---- - agent/conncheck.c | 17 ----------------- - 1 file changed, 17 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 9950970..95e2556 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2820,23 +2820,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * that causes the ready -> connected transition. - */ - priv_update_check_list_state_for_ready (agent, stream, component); -- -- /* note: this new check is required by the new-dribble test, -- * when early icheck on the peer controlled agent causes an -- * incoming stun request to an already succeeded (and -- * nominated) pair on the controlling agent. If the -- * controlling agent doesn't retrigger a check with -- * USE-CANDIDATE=1, the peer agent has no way to nominate it. -- * -- * This behavior differs from ICE spec 7.2.1.4 -- */ -- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || -- agent->compatibility == NICE_COMPATIBILITY_WLM2009 || -- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -- agent->controlling_mode) { -- priv_add_pair_to_triggered_check_queue (agent, p); -- conn_check_schedule_next(agent); -- } - } else if (p->state == NICE_CHECK_FAILED) { - /* 7.2.1.4 Triggered Checks - * If the state of the pair is Failed, it is changed to Waiting --- -2.13.6 - - -From 8fa648a15a6700d08165fe97a09f5c068abae1e6 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 11 Apr 2016 13:13:51 +0200 -Subject: [PATCH 38/70] conncheck: dont cancel a pair for triggered check - -This patch adds another supplementary "corner" case, not covered by the -ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in -the triggered check list should be considered like an in-progress pair, -and cancelled only if its priority is lower than the priority of the -nominated pair. This is required in some aggressive nomination -situations for both peers to select the same pair, having the highest -priority. - -Differential Revision: https://phabricator.freedesktop.org/D933 ---- - agent/conncheck.c | 48 ++++++++++++++++++++++++++++++++---------------- - 1 file changed, 32 insertions(+), 16 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 95e2556..79f678a 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -64,7 +64,7 @@ - - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); - static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); --static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); -+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id); - static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); - static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); - static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, -@@ -176,6 +176,16 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - } - } - -+/* Verify if the pair is in the triggered checks list -+ */ -+ -+static gboolean -+priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) -+{ -+ g_assert (pair); -+ return (g_slist_find (agent->triggered_check_queue, pair) != NULL); -+} -+ - /* Add the pair to the triggered checks list, if not already present - */ - static void -@@ -1897,7 +1907,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream - /* Only go to READY if no checks are left in progress. If there are - * any that are kept, then this function will be called again when the - * conncheck tick timer finishes them all */ -- if (priv_prune_pending_checks (stream, component->id) == 0) { -+ if (priv_prune_pending_checks (agent, stream, component->id) == 0) { - /* Continue through the states to give client code a nice - * logical progression. See http://phabricator.freedesktop.org/D218 for - * discussion. */ -@@ -2693,14 +2703,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - * @see priv_update_check_list_state_failed_components() - */ --static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) -+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id) - { - GSList *i; - guint64 highest_nominated_priority = 0; - guint in_progress = 0; - -- nice_debug ("Agent XXX: Finding highest priority for component %d", -- component_id); -+ nice_debug ("Agent %p: Finding highest priority for component %d", -+ agent, component_id); - - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -@@ -2712,41 +2722,47 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - } - } - -- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority " -- "is %" G_GUINT64_FORMAT, highest_nominated_priority); -+ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority " -+ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ - /* note: this case is not covered by the ICE spec, 8.1.2 - * "Updating States", but a pair in waiting state which will be - * nominated on response receipt should be treated the same way -- * that an in-progress pair. -+ * that an in-progress pair. A pair in waiting state and in -+ * the triggered check list should also be treated like an in-progress -+ * pair. - */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -+ - if (p->component_id == component_id) { -+ gboolean like_in_progress = -+ p->mark_nominated_on_response_arrival || -+ priv_is_pair_in_triggered_check_queue (agent, p); -+ - if (p->state == NICE_CHECK_FROZEN || -- (p->state == NICE_CHECK_WAITING && -- !p->mark_nominated_on_response_arrival)) { -+ (p->state == NICE_CHECK_WAITING && !like_in_progress)) { - p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ - if (p->state == NICE_CHECK_IN_PROGRESS || -- (p->state == NICE_CHECK_WAITING && -- p->mark_nominated_on_response_arrival)) { -+ (p->state == NICE_CHECK_WAITING && like_in_progress)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { - p->stun_message.buffer = NULL; - p->stun_message.buffer_len = 0; - p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); - } else { - /* We must keep the higher priority pairs running because if a udp - * packet was lost, we might end up using a bad candidate */ -- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %" -+ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %" - G_GUINT64_FORMAT " is higher than currently nominated pair %" -- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority); -+ G_GUINT64_FORMAT, agent, -+ p, p->priority, highest_nominated_priority); - in_progress++; - } - } --- -2.13.6 - - -From 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 14 Jun 2016 21:04:49 +0200 -Subject: [PATCH 39/70] conncheck: try to change earlier to state ready - -We check if we can move from state connected to ready just -after a pair expired its retransmission count. This pair -will be marked failed, and will no longer be in-progress. -The number of in-progress dropping down to zero is one -of the conditions needed to make the transition to ready, -per component (and not globally as it's the case in other -locations where this check function is called). - -Differential Revision: https://phabricator.freedesktop.org/D1117 ---- - agent/conncheck.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 79f678a..d31b77f 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -626,6 +626,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - { - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ NiceComponent *component; - - /* case: conncheck cancelled due to in-progress incoming - * check, requeing the pair, ICE spec, sect 7.2.1.4 -@@ -646,6 +647,16 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - priv_print_conn_check_lists (agent, G_STRFUNC, - ", retransmission failed"); - -+ /* perform a check if a transition state from connected to -+ * ready can be performed. This may happen here, when the last -+ * in-progress pair has expired its retransmission count -+ * in priv_conn_check_tick_stream(), which is a condition to -+ * make the transition connected to ready. -+ */ -+ if (agent_find_component (agent, p->stream_id, p->component_id, -+ NULL, &component)) -+ priv_update_check_list_state_for_ready (agent, stream, -+ component); - break; - } - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: --- -2.13.6 - - -From 59fe48517c0b7db77b99183d31fdd84b55adb5d4 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 14 Jun 2016 21:12:16 +0200 -Subject: [PATCH 40/70] conncheck: fix a state transition case - -When a new stun request hits a valid pair, of a failed component, we may -have a transition from state failed to connected. In this situation, we -do a logical progression failed -> connecting -> connected, like we do -in function priv_update_check_list_state_for_ready() - -Similarily, when a new stun request hits a failed pair, of a failed -component, triggering a new conncheck for this pair may also cause the -component state to move back from failed to connecting state. - -Differential Revision: https://phabricator.freedesktop.org/D1118 ---- - agent/conncheck.c | 21 ++++++++++++++++----- - 1 file changed, 16 insertions(+), 5 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index d31b77f..e1a5cf1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1973,12 +1973,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - pair->nominated = TRUE; - priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -+ if (component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } - priv_update_check_list_state_for_ready (agent, stream, component); - } else if (pair->state == NICE_CHECK_IN_PROGRESS) { - pair->mark_nominated_on_response_arrival = TRUE; -@@ -2004,13 +2006,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - if (pair->valid) { - priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -+ if (component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -- - } - priv_update_check_list_state_for_ready (agent, stream, component); - } -@@ -2854,6 +2857,14 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - pair (representing a new STUN Binding request transaction), by - enqueueing the pair in the triggered check queue. */ - priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } - } - - /* note: the spec says the we SHOULD retransmit in-progress --- -2.13.6 - - -From f19d209decac432a1597d84c3d5809d2208f7457 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 14 Jun 2016 21:20:49 +0200 -Subject: [PATCH 41/70] conncheck: do not recheck a just succeeded pair - -We cancel the potential in-progress transaction cancellation, caused by -sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before -transmission timeout, or just after timeout, when the pair is -temporarily put on the triggered check list on the way to be -rechecked. This situation is not covered by the RFC 5245. - -Differential Revision: https://phabricator.freedesktop.org/D1119 ---- - agent/conncheck.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index e1a5cf1..4b785b5 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3117,6 +3117,16 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (new_pair == p) - p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; -+ /* note: we cancel the potential in-progress transaction -+ * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if -+ * we receive a valid reply before transmission timeout... -+ */ -+ p->recheck_on_timeout = FALSE; -+ /* ... or just after the transmission timeout, while the pair is -+ * temporarily put on the triggered check list on the way to be -+ * be rechecked: we remove it from the list too. -+ */ -+ priv_remove_pair_from_triggered_check_queue (agent, p); - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - nice_component_add_valid_candidate (component, remote_candidate); - } -@@ -3151,6 +3161,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ - p->state = NICE_CHECK_SUCCEEDED; -+ p->recheck_on_timeout = FALSE; -+ priv_remove_pair_from_triggered_check_queue (agent, p); - nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", - agent, p, new_pair); - } --- -2.13.6 - - -From d516fca1b0e0a6606afec797bdc0690104e779a9 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 14 Jun 2016 21:32:26 +0200 -Subject: [PATCH 42/70] conncheck: adjust recheck on timeout strategy - -The pair recheck on timeout can easily cause repetitive rechecks in -a ping-pong effect, if both peers with the same behaviour try to -check the same pair almost simultaneously, and if the network rtt -is greater than the initial timer rto. The reply to the initial -stun request may arrive after the in-progress conncheck -cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -creates a new stun request, and forgets the initial one. -The conncheck timer is restarted with the same initial value, -so the same situation happens again later. - -We choose to avoid resetting the timer in such situation. After enough -retransmissions, the timeout delay, that doubles after each timeout, -becomes longer than the rtt, and the stun reply can be handled. - -Differential Revision: https://phabricator.freedesktop.org/D1115 ---- - agent/conncheck.c | 30 ++++++++++++++++++++++++++---- - 1 file changed, 26 insertions(+), 4 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 4b785b5..88d2534 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -591,7 +591,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - */ - nice_debug ("Agent %p : pair %p was cancelled, " - "triggering a new connection check", agent, p); -- p->recheck_on_timeout = FALSE; - priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } -@@ -2650,9 +2649,32 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - if (nice_socket_is_reliable(pair->sockptr)) { - stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); - } else { -- stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -+ StunTimer *timer = &pair->timer; -+ -+ if (pair->recheck_on_timeout) -+ /* The pair recheck on timeout can easily cause repetitive rechecks in -+ * a ping-pong effect, if both peers with the same behaviour try to -+ * check the same pair almost simultaneously, and if the network rtt -+ * is greater than the initial timer rto. The reply to the initial -+ * stun request may arrive after the in-progress conncheck -+ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -+ * creates a new stun request, and forgets the initial one. -+ * The conncheck timer is restarted with the same initial value, -+ * so the same situation happens again later. -+ * -+ * We choose to avoid resetting the timer in such situation. -+ * After enough retransmissions, the timeout delay becomes -+ * longer than the rtt, and the stun reply can be handled. -+ */ -+ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -+ agent, pair, -+ timer->retransmissions, timer->max_retransmissions, -+ timer->delay - stun_timer_remainder (timer), timer->delay); -+ else -+ stun_timer_start (timer, -+ priv_compute_conncheck_timer (agent, stream), -+ agent->stun_max_retransmissions); -+ pair->recheck_on_timeout = FALSE; - } - - /* TCP-ACTIVE candidate must create a new socket before sending --- -2.13.6 - - -From 95f8805eb7b77755337e28daf1f134587d42b35f Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Thu, 16 Jun 2016 17:32:39 +0200 -Subject: [PATCH 43/70] conncheck: remove cancelled pair state - -Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for -removal after the nomination of a pair with an higher priority, -described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They -include also pairs that overflow the conncheck list size, but this is a -somewhat more marginal situation. So we are mainly interested in the -first use case of this state. - -This state mixes two different situations, that deserve a distinct -handling : on one side, there are waiting or frozen pairs that must be -removed, this is an immediate action that doesn't need a dedicated state -for that. And on the other side, there are in-progress pairs that -should no longer be retransmitted, because another pair with a higher -priority has already been nominated. - -This patch removes the cancelled state, and adds a flag -retransmit_on_timeout to deal with this last situation. Note that this -case should not generate a triggered check, as per described in section -7.2.1.4, when the state of the pair is In-Progress or Failed, since this -pair of lower priority has no hope to replace the nominated one. - -Differential Revision: https://phabricator.freedesktop.org/D1114 ---- - agent/conncheck.c | 142 +++++++++++++++++++++++++++++------------------------- - agent/conncheck.h | 3 +- - 2 files changed, 77 insertions(+), 68 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 88d2534..b0e2222 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -100,8 +100,6 @@ priv_state_to_gchar (NiceCheckState state) - return 'F'; - case NICE_CHECK_FROZEN: - return 'Z'; -- case NICE_CHECK_CANCELLED: -- return 'C'; - case NICE_CHECK_DISCOVERED: - return 'D'; - default: -@@ -627,6 +625,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; - NiceComponent *component; - -+timer_timeout: - /* case: conncheck cancelled due to in-progress incoming - * check, requeing the pair, ICE spec, sect 7.2.1.4 - * "Triggered Checks", "If the state of that pair is -@@ -662,6 +661,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - { - unsigned int timeout = stun_timer_remainder (&p->timer); - -+ /* case: retransmission stopped, due to the nomination of -+ * a pair with a higher priority than this in-progress pair, -+ * ICE spec, sect 8.1.2 "Updating States", item 2.2 -+ */ -+ if (!p->retransmit_on_timeout) -+ goto timer_timeout; -+ - /* case: conncheck cancelled due to in-progress incoming - * check, requeing the pair, ICE spec, sect 7.2.1.4 - * "Triggered Checks", "If the state of that pair is -@@ -1600,26 +1606,6 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - } - - --static GSList *prune_cancelled_conn_check (GSList *conncheck_list) --{ -- GSList *item = conncheck_list; -- -- while (item) { -- CandidateCheckPair *pair = item->data; -- GSList *next = item->next; -- -- if (pair->state == NICE_CHECK_CANCELLED) { -- conn_check_free_item (pair); -- conncheck_list = g_slist_delete_link (conncheck_list, item); -- } -- -- item = next; -- } -- -- return conncheck_list; --} -- -- - /* - * Handle any processing steps for connectivity checks after - * remote credentials have been set. This function handles -@@ -1754,9 +1740,6 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - (GDestroyNotify) incoming_check_free); - component->incoming_checks = NULL; - } -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); - } - - /* -@@ -1764,7 +1747,7 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - * in ICE spec section 5.7.3 (ID-19). See also - * conn_check_add_for_candidate(). - */ --static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) -+static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) - { - guint valid = 0; - guint cancelled = 0; -@@ -1772,22 +1755,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper - - while (item) { - CandidateCheckPair *pair = item->data; -+ GSList *next = item->next; - -- if (pair->state != NICE_CHECK_CANCELLED) { -- valid++; -- if (valid > upper_limit) { -- pair->state = NICE_CHECK_CANCELLED; -+ valid++; -+ if (valid > upper_limit) { -+ conn_check_free_item (pair); -+ conncheck_list = g_slist_delete_link (conncheck_list, item); - cancelled++; -- } - } -- -- item = item->next; -+ item = next; - } - - if (cancelled > 0) - nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements" - " left. Maximum connchecks allowed : %d", cancelled, valid, - upper_limit); -+ return conncheck_list; - } - - /* -@@ -2097,6 +2080,7 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - } - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); -+ pair->retransmit_on_timeout = TRUE; - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -2106,7 +2090,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - /* implement the hard upper limit for number of - checks (see sect 5.7.3 ICE ID-19): */ - if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { -- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); -+ stream->conncheck_list = priv_limit_conn_check_list_size ( -+ stream->conncheck_list, agent->max_conn_checks); - } - - return pair; -@@ -2769,8 +2754,10 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - * the triggered check list should also be treated like an in-progress - * pair. - */ -- for (i = stream->conncheck_list; i; i = i->next) { -+ i = stream->conncheck_list; -+ while (i) { - CandidateCheckPair *p = i->data; -+ GSList *next = i->next; - - if (p->component_id == component_id) { - gboolean like_in_progress = -@@ -2779,19 +2766,20 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - - if (p->state == NICE_CHECK_FROZEN || - (p->state == NICE_CHECK_WAITING && !like_in_progress)) { -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); -+ nice_debug ("Agent %p : pair %p removed.", agent, p); -+ conn_check_free_item (p); -+ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- if (p->state == NICE_CHECK_IN_PROGRESS || -+ else if (p->state == NICE_CHECK_IN_PROGRESS || - (p->state == NICE_CHECK_WAITING && like_in_progress)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); -+ p->retransmit_on_timeout = FALSE; -+ p->recheck_on_timeout = FALSE; -+ nice_debug ("Agent %p : pair %p will not be retransmitted.", -+ agent, p); - } else { - /* We must keep the higher priority pairs running because if a udp - * packet was lost, we might end up using a bad candidate */ -@@ -2803,6 +2791,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - } - } - } -+ i = next; - } - - return in_progress; -@@ -2841,29 +2830,42 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p = p->succeeded_pair; - } - -- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); -+ nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", -+ agent, p, p->foundation, priv_state_to_gchar (p->state)); - - if (p->state == NICE_CHECK_WAITING || -- p->state == NICE_CHECK_FROZEN) -+ p->state == NICE_CHECK_FROZEN) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -+ } - else if (p->state == NICE_CHECK_IN_PROGRESS) { - /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" - * we cancel the in-progress transaction, and after the - * retransmission timeout, we create a new connectivity check - * for that pair. The controlling role of this new check may - * be different from the role of this cancelled check. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * so there's no reason to recheck this pair, since it can in -+ * no way replace the nominated one. - */ - if (!nice_socket_is_reliable (p->sockptr)) { -- nice_debug ("Agent %p : check already in progress, " -- "cancelling this check..", agent); -- /* this flag will determine the action at the retransmission -- * timeout of the stun timer -- */ -- p->recheck_on_timeout = TRUE; -+ if (p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p will be rechecked " -+ "on stun timer timeout.", agent, p); -+ /* this flag will determine the action at the retransmission -+ * timeout of the stun timer -+ */ -+ p->recheck_on_timeout = TRUE; -+ } else -+ nice_debug ("Agent %p : pair %p won't be retransmitted.", -+ agent, p); - } - } - else if (p->state == NICE_CHECK_SUCCEEDED) { -- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); -+ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); - /* note: this is a bit unsure corner-case -- let's do the - same state update as for processing responses to our own checks */ - /* note: this update is required by the dribble test, to -@@ -2875,18 +2877,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - } else if (p->state == NICE_CHECK_FAILED) { - /* 7.2.1.4 Triggered Checks - * If the state of the pair is Failed, it is changed to Waiting -- and the agent MUST create a new connectivity check for that -- pair (representing a new STUN Binding request transaction), by -- enqueueing the pair in the triggered check queue. */ -- priv_add_pair_to_triggered_check_queue (agent, p); -- /* If the component for this pair is in failed state, move it -- * back to connecting, and reinitiate the timers -+ * and the agent MUST create a new connectivity check for that -+ * pair (representing a new STUN Binding request transaction), by -+ * enqueueing the pair in the triggered check queue. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * we apply the same strategy than with an in-progress pair -+ * above. - */ -- if (component->state == NICE_COMPONENT_STATE_FAILED) { -- agent_signal_component_state_change (agent, stream->id, -- component->id, NICE_COMPONENT_STATE_CONNECTING); -- conn_check_schedule_next (agent); -- } -+ if (p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } -+ } else -+ nice_debug ("Agent %p : pair %p won't be retransmitted.", -+ agent, p); - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -3401,10 +3415,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - } - } - -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); -- - return trans_found; - } - -diff --git a/agent/conncheck.h b/agent/conncheck.h -index c07fb22..909d469 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -56,7 +56,6 @@ - * @NICE_CHECK_SUCCEEDED: Connection successfully checked. - * @NICE_CHECK_FAILED: No connectivity; retransmissions ceased. - * @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING. -- * @NICE_CHECK_CANCELLED: Check cancelled. - * @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list. - * - * States for checking a candidate pair. -@@ -68,7 +67,6 @@ typedef enum - NICE_CHECK_SUCCEEDED, - NICE_CHECK_FAILED, - NICE_CHECK_FROZEN, -- NICE_CHECK_CANCELLED, - NICE_CHECK_DISCOVERED, - } NiceCheckState; - -@@ -89,6 +87,7 @@ struct _CandidateCheckPair - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; - gboolean recheck_on_timeout; -+ gboolean retransmit_on_timeout; - struct _CandidateCheckPair *discovered_pair; - struct _CandidateCheckPair *succeeded_pair; - guint64 priority; --- -2.13.6 - - -From 07366a5bca7e4818b8df29d9c7c220da8f752547 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 21 Jun 2016 21:47:42 +0200 -Subject: [PATCH 44/70] conncheck: fix the component failed transition - -This patch fixes the transition of a component from connecting to -failed, that previously occured due to the propagation of the -keep_timer_going variable, and to the final call to function -priv_update_check_list_failed_components(), after the global agent -timer was stopped. - -Previously, the code almost never entered to failed state, because the -timer was going one, as long as the number of nominated pair was not -enough, and as long as there were valid pairs not yet nominated. Even -if all pair timers were over. - -The definition of the Failed state of a conncheck list is somewhat -contradictory in the spec, depending on weather you read : - - * sect 5.7.4. "Computing States", - "Failed: In this state, the ICE checks have not completed successfully - for this media stream." - - or - - * sect 7.1.3.3. "Check List and Timer State Updates", - "If all of the pairs in the check list are now either in the Failed or - Succeeded state: If there is not a pair in the valid list for each - component of the media stream, the state of the check list is set to - Failed." - -Our understanding of the ICE spec is that, the proper way to enter failed -state instead in when all connchecks have no longer in-progress pairs. -All pairs are either in state succeeded, discovered, or failed. No timer -is still running, and we have no hope that the conncheck list changes -again, except if an unexpected STUN packet arrives later. All pairs in -frozen state is a special case, that is handled separately (sect -7.1.3.3). - -A special grace delay is added before declaring a component in state -Failed. This delay is not part of the RFC, and it is aimed to limit the -cases when a conncheck list is reactivated just after it's been declared -failed, causing a user visible transition from connecting to failed, and -back from failed to connecting again. This is also required by the test -suite, that counts exactly the number of time each state is entered, and -doesn't expect these transcient failed states to happen (frequent due to -the nature of the testsuite, less frequent in real life). - -Differential Revision: https://phabricator.freedesktop.org/D1111 ---- - agent/agent-priv.h | 14 +++++++++++ - agent/conncheck.c | 71 +++++++++++++++++++++++++++++++++++++++++++----------- - 2 files changed, 71 insertions(+), 14 deletions(-) - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 3384180..714ecff 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -122,6 +122,18 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ - (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) - -+/* A grace period before declaring a component as failed, in msecs. This -+ * delay is added to reduce the chance to see the agent receiving new -+ * stun activity just after the conncheck list has been declared failed, -+ * reactiviting conncheck activity, and causing a (valid) state -+ * transitions like that: connecting -> failed -> connecting -> -+ * connected -> ready. -+ * Such transitions are not buggy per-se, but may break the -+ * test-suite, that counts precisely the number of time each state -+ * has been set, and doesnt expect these transcient failed states. -+ */ -+#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000 -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -@@ -176,6 +188,8 @@ struct _NiceAgent - guint16 rfc4571_expecting_length; - gboolean use_ice_udp; - gboolean use_ice_tcp; -+ -+ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ - /* XXX: add pointer to internal data struct for ABI-safe extensions */ - }; - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index b0e2222..63db471 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -709,7 +709,7 @@ timer_timeout: - } - } - } -- } -+ } - - /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" - * note: This code is executed when the triggered checks list is -@@ -795,11 +795,8 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - if (s_inprogress) - keep_timer_going = TRUE; - -- /* note: if some components have established connectivity, -- * but yet no nominated pair, keep timer going */ - if (s_nominated < stream->n_components && - s_waiting_for_nomination) { -- keep_timer_going = TRUE; - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { - if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && - agent->controlling_mode) { -@@ -888,6 +885,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->recheck_on_timeout = FALSE; - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); -+ keep_timer_going = TRUE; - break; /* move to the next component */ - } - } -@@ -911,6 +909,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->recheck_on_timeout = FALSE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); -+ keep_timer_going = TRUE; - break; /* move to the next component */ - } - } -@@ -937,6 +936,7 @@ conn_check_stop (NiceAgent *agent) - g_source_destroy (agent->conncheck_timer_source); - g_source_unref (agent->conncheck_timer_source); - agent->conncheck_timer_source = NULL; -+ agent->conncheck_timer_grace_period = 0; - } - - -@@ -1005,9 +1005,39 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - keep_timer_going = TRUE; - } - -+ /* step: if no work left and a conncheck list of a stream is still -+ * frozen, set the pairs to waiting, according to ICE SPEC, sect -+ * 7.1.3.3. "Check List and Timer State Updates" -+ */ -+ if (!keep_timer_going) { -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (priv_is_checklist_frozen (stream)) { -+ nice_debug ("Agent %p : stream %d conncheck list is still " -+ "frozen, while other lists are completed. Unfreeze it.", -+ agent, stream->id); -+ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream); -+ } -+ } -+ } -+ -+ /* note: we provide a grace period before declaring a component as -+ * failed. Components marked connected, and then ready follow another -+ * code path, and are not concerned by this grace period. -+ */ -+ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0) -+ nice_debug ("Agent %p : waiting %d msecs before checking " -+ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD); -+ -+ if (keep_timer_going) -+ agent->conncheck_timer_grace_period = 0; -+ else -+ agent->conncheck_timer_grace_period += agent->timer_ta; -+ - /* step: stop timer if no work left */ -- if (keep_timer_going != TRUE) { -- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); -+ if (!keep_timer_going && -+ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) { -+ nice_debug ("Agent %p : checking for failed components now.", agent); - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; - priv_update_check_list_failed_components (agent, stream); -@@ -1017,6 +1047,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - } - } - -+ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", conncheck timer stopped"); - -@@ -1027,9 +1058,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - - /* XXX: what to signal, is all processing now really done? */ - nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); -+ return FALSE; - } - -- return keep_timer_going; -+ return TRUE; - } - - static gboolean priv_conn_check_tick (gpointer pointer) -@@ -1810,15 +1842,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp - * Updates the check list state. - * - * Implements parts of the algorithm described in -- * ICE sect 8.1.2. "Updating States" (ID-19): if for any -+ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any - * component, all checks have been completed and have -- * failed, mark that component's state to NICE_CHECK_FAILED. -+ * failed to produce a nominated pair, mark that component's -+ * state to NICE_CHECK_FAILED. - * - * Sends a component state changesignal via 'agent'. - */ - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) - { - GSList *i; -+ gboolean completed; -+ guint nominated; - /* note: emitting a signal might cause the client - * to remove the stream, thus the component count - * must be fetched before entering the loop*/ -@@ -1842,6 +1877,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) - continue; - -+ nominated = 0; -+ completed = TRUE; - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -1849,16 +1886,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - g_assert (p->stream_id == stream->id); - - if (p->component_id == (c + 1)) { -- if (p->state != NICE_CHECK_FAILED) -- break; -+ if (p->nominated) -+ ++nominated; -+ if (p->state != NICE_CHECK_FAILED && -+ p->state != NICE_CHECK_SUCCEEDED && -+ p->state != NICE_CHECK_DISCOVERED) -+ completed = FALSE; - } - } - -- /* note: all checks have failed -+ /* note: all pairs are either failed or succeeded, and the component -+ * has not produced a nominated pair. - * Set the component to FAILED only if it actually had remote candidates - * that failed.. */ -- if (i == NULL && comp != NULL && comp->remote_candidates != NULL) -- agent_signal_component_state_change (agent, -+ if (completed && nominated == 0 && -+ comp != NULL && comp->remote_candidates != NULL) -+ agent_signal_component_state_change (agent, - stream->id, - (c + 1), /* component-id */ - NICE_COMPONENT_STATE_FAILED); --- -2.13.6 - - -From 195db6b344fc4f9fadc39419dfeec2fc14b23fac Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Fri, 15 Jul 2016 23:31:42 +0200 -Subject: [PATCH 45/70] agent: add new pairs only for gathering streams - -At the end of the local candidate gathering process, we only create new -pairs for streams that are in gathering state. - -Other stream that may be in ready state for example, due to a -previously succeeded conncheck process, may have accumulated some -couples (local,remote) candidates that have not resulted in the creation -a new pair during this previous conncheck process, and we don't want -these new pairs to be added now, because it would generate unneeded -transition changes for a stream unconcerned by this gathering. - -Differential Revision: https://phabricator.freedesktop.org/D1755 ---- - agent/agent.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/agent/agent.c b/agent/agent.c -index 577a7e0..e3705ed 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -2032,6 +2032,17 @@ void agent_gathering_done (NiceAgent *agent) - - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; -+ -+ /* We ignore streams not in gathering state, typically already in -+ * ready state. Such streams may have couples (local,remote) -+ * candidates that have not resulted in the creation a new pair -+ * during a previous conncheck session, and we don't want these new -+ * pairs to be added now, because it would generate unneeded -+ * transition changes for a stream unconcerned by this gathering. -+ */ -+ if (!stream->gathering) -+ continue; -+ - for (j = stream->components; j; j = j->next) { - NiceComponent *component = j->data; - --- -2.13.6 - - -From b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 28 May 2017 22:20:36 +0200 -Subject: [PATCH 46/70] stun: fix gcc7 implicit fallthrough warning - -Differential Revision: https://phabricator.freedesktop.org/D1754 ---- - stun/stunmessage.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/stun/stunmessage.c b/stun/stunmessage.c -index e8184c4..4cc3392 100644 ---- a/stun/stunmessage.c -+++ b/stun/stunmessage.c -@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type, - /* Only fingerprint may come after M-I */ - if (type == STUN_ATTRIBUTE_FINGERPRINT) - break; -+ return NULL; - - case STUN_ATTRIBUTE_FINGERPRINT: - /* Nothing may come after FPR */ --- -2.13.6 - - -From c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 18 Jun 2017 10:12:58 +0200 -Subject: [PATCH 47/70] agent: remove spurious newlines - -Differential Revision: https://phabricator.freedesktop.org/D1756 ---- - agent/agent.c | 2 +- - agent/component.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index e3705ed..27e6193 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3905,7 +3905,7 @@ agent_recv_message_unlocked ( - - nice_address_to_string (message->from, str); - nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" -- " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, -+ " %s:%d sock-type: %d", agent, stream->id, component->id, str, - nice_address_get_port (message->from), nicesock->type); - } - -diff --git a/agent/component.c b/agent/component.c -index ab665b6..6e207d3 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -1461,7 +1461,7 @@ nice_component_add_valid_candidate (NiceComponent *component, - char str[INET6_ADDRSTRLEN]; - nice_address_to_string (&candidate->addr, str); - nice_debug ("Agent %p : %d:%d Adding valid source" -- " candidate: %s:%d trans: %d\n", component->agent, -+ " candidate: %s:%d trans: %d", component->agent, - candidate->stream_id, candidate->component_id, str, - nice_address_get_port (&candidate->addr), candidate->transport); - } --- -2.13.6 - - -From e3ddaa285e389baf3f26cfb6964919718a8f6a00 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Wed, 21 Jun 2017 16:55:32 -0400 -Subject: [PATCH 48/70] agent: Adjust the nice_agent_new_full() to use flags - -This makes it easier to read and more extensible. ---- - agent/agent.c | 9 +++++---- - agent/agent.h | 27 ++++++++++++++++++++++----- - tests/test-nomination.c | 8 ++++---- - 3 files changed, 31 insertions(+), 13 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 27e6193..8fd8ead 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -1168,14 +1168,15 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) - NICEAPI_EXPORT NiceAgent * - nice_agent_new_full (GMainContext *ctx, - NiceCompatibility compat, -- gboolean reliable, -- NiceNominationMode nomination) -+ NiceAgentOption flags) - { - NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, - "compatibility", compat, - "main-context", ctx, -- "reliable", reliable, -- "nomination-mode", nomination, -+ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE, -+ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ? -+ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE, -+ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, - NULL); - - return agent; -diff --git a/agent/agent.h b/agent/agent.h -index 6e233c6..ed6f6e4 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -399,6 +399,25 @@ typedef enum - } NiceNominationMode; - - /** -+ * NiceAgentOption: -+ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default -+ * is aggrssive mode (see #NiceNominationMode). -+ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable(). -+ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode -+ * -+ * These are options that can be passed to nice_agent_new_full(). They set -+ * various properties on the agent. Not including them sets the property to -+ * the other value. -+ * -+ * Since: UNRELEASED -+ */ -+typedef enum { -+ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, -+ NICE_AGENT_OPTION_RELIABLE = 1 << 1, -+ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, -+} NiceAgentOption; -+ -+/** - * NiceAgentRecvFunc: - * @agent: The #NiceAgent Object - * @stream_id: The id of the stream -@@ -452,13 +471,12 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - * nice_agent_new_full: - * @ctx: The Glib Mainloop Context to use for timers - * @compat: The compatibility mode of the agent -- * @reliable: The reliability mode of the agent -- * @nomination: The nomination mode of the agent -+ * @flags: Flags to set the properties - * - * Create a new #NiceAgent with parameters that must be be defined at - * construction time. - * The returned object must be freed with g_object_unref() -- * See also: #NiceNominationMode -+ * See also: #NiceNominationMode and #NiceAgentOption - * - * Since: UNRELEASED - * -@@ -467,8 +485,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - NiceAgent * - nice_agent_new_full (GMainContext *ctx, - NiceCompatibility compat, -- gboolean reliable, -- NiceNominationMode nomination); -+ NiceAgentOption flags); - - /** - * nice_agent_add_local_address: -diff --git a/tests/test-nomination.c b/tests/test-nomination.c -index b5a5e5f..bf21557 100644 ---- a/tests/test-nomination.c -+++ b/tests/test-nomination.c -@@ -140,13 +140,13 @@ run_test(NiceNominationMode l_nomination_mode, - - lagent = nice_agent_new_full (NULL, - NICE_COMPATIBILITY_RFC5245, -- FALSE, /* reliable */ -- l_nomination_mode); -+ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); - - ragent = nice_agent_new_full (NULL, - NICE_COMPATIBILITY_RFC5245, -- FALSE, /* reliable */ -- r_nomination_mode); -+ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); - - g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); - g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); --- -2.13.6 - - -From dcb0d647174416a292492f8deca86f83a2ef124c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Wed, 21 Jun 2017 17:07:17 -0400 -Subject: [PATCH 49/70] Repleace UNRELEASED with 0.1.15 - ---- - agent/agent.c | 8 ++++---- - agent/agent.h | 6 +++--- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 8fd8ead..15af9ed 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -448,7 +448,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * the selection of valid pairs to be used upstream. - * See also: #NiceNominationMode - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, - g_param_spec_enum ( -@@ -744,7 +744,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * to the READY state, and on the time needed to complete the GATHERING - * state. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - - g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, -@@ -769,7 +769,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * divided by two instead (RFC 5389 indicates that a customisable - * multiplier 'Rm' to 'RTO' should be used). - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - - g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, -@@ -788,7 +788,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * The initial timeout of the STUN binding requests used - * for a reliable timer. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - - g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, -diff --git a/agent/agent.h b/agent/agent.h -index ed6f6e4..520c4c5 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -390,7 +390,7 @@ typedef enum - * faster, than the regular mode, potentially causing the nominated - * pair to change until the connection check completes. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - typedef enum - { -@@ -409,7 +409,7 @@ typedef enum - * various properties on the agent. Not including them sets the property to - * the other value. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - typedef enum { - NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, -@@ -478,7 +478,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - * The returned object must be freed with g_object_unref() - * See also: #NiceNominationMode and #NiceAgentOption - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - * - * Returns: The new agent GObject - */ --- -2.13.6 - - -From 2c50d73b82f2ec2422a8e0ea393194486c193c64 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Wed, 10 Feb 2016 23:20:39 -0500 -Subject: [PATCH 50/70] agent: Don't crash if recv cancelled without a GError - ---- - agent/agent.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 15af9ed..e48d7f3 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -4279,7 +4279,10 @@ static gboolean - nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) - { - GError **error = user_data; -- return !g_cancellable_set_error_if_cancelled (cancellable, error); -+ -+ if (error && *error) -+ g_cancellable_set_error_if_cancelled (cancellable, error); -+ return G_SOURCE_REMOVE; - } - - static gint --- -2.13.6 - - -From 63d273cea42def3567701ad9feab91f63cf9345f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Thu, 11 Feb 2016 22:16:48 -0500 -Subject: [PATCH 51/70] component: Use non-GClosure dummy callbacks - -GClosures are not that cheap to setup ---- - agent/component.c | 18 +++++++++++++++--- - 1 file changed, 15 insertions(+), 3 deletions(-) - -diff --git a/agent/component.c b/agent/component.c -index 6e207d3..6eee90e 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -1005,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass) - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - } - -+static gboolean -+dummy_callback (gpointer data) -+{ -+ return G_SOURCE_CONTINUE; -+} -+ -+static void -+source_set_dummy_callback (GSource *source) -+{ -+ g_source_set_callback (source, dummy_callback, NULL, NULL); -+} -+ - static void - nice_component_init (NiceComponent *component) - { -@@ -1027,7 +1039,7 @@ nice_component_init (NiceComponent *component) - component->stop_cancellable = g_cancellable_new (); - component->stop_cancellable_source = - g_cancellable_source_new (component->stop_cancellable); -- g_source_set_dummy_callback (component->stop_cancellable_source); -+ source_set_dummy_callback (component->stop_cancellable_source); - g_source_attach (component->stop_cancellable_source, component->own_ctx); - component->ctx = g_main_context_ref (component->own_ctx); - -@@ -1242,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_) - child_socket_source->source = - g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, - NULL); -- g_source_set_dummy_callback (child_socket_source->source); -+ source_set_dummy_callback (child_socket_source->source); - g_source_add_child_source (source, child_socket_source->source); - g_source_unref (child_socket_source->source); - component_source->socket_sources = -@@ -1387,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id, - GSource *cancellable_source; - - cancellable_source = g_cancellable_source_new (cancellable); -- g_source_set_dummy_callback (cancellable_source); -+ source_set_dummy_callback (cancellable_source); - g_source_add_child_source ((GSource *) component_source, - cancellable_source); - g_source_unref (cancellable_source); --- -2.13.6 - - -From 9f800d3597767855accccc592c34bc4e945f5bd5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Wed, 21 Jun 2017 20:42:57 -0400 -Subject: [PATCH 52/70] configure: Remove -Wswitch-enum - -Creates useless warnings when other libraries change. - -https://phabricator.freedesktop.org/T7770 ---- - configure.ac | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/configure.ac b/configure.ac -index 6c106ff..16988ad 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -154,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \ - ]) - AS_IF([test "$enable_compile_warnings" = "maximum" -o \ - "$enable_compile_warnings" = "error"],[ -- NICE_ADD_FLAG([-Wswitch-enum]) - NICE_ADD_FLAG([-Wswitch-default]) - NICE_ADD_FLAG([-Waggregate-return]) - ]) --- -2.13.6 - - -From dbaf8f5ccd76089e340883887c7e08e6c04de80a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 12 Apr 2016 13:22:21 +0200 -Subject: [PATCH 53/70] conncheck: improve role conflict debug - -This patch displays explicitely the controlling or controlled -role of the agent. - -Differential Revision: https://phabricator.freedesktop.org/D874 ---- - agent/conncheck.c | 24 +++++++++++++++++++----- - 1 file changed, 19 insertions(+), 5 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 63db471..8945e0f 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3134,14 +3134,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) - { - /* role conflict, change mode; wait for a new conn. check */ - if (control != agent->controlling_mode) { -- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control); -+ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".", -+ agent, control ? "controlling" : "controlled"); - agent->controlling_mode = control; - /* the pair priorities depend on the roles, so recalculation - * is needed */ - priv_recalculate_pair_priorities (agent); - } - else -- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control); -+ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", -+ agent, control ? "controlling" : "controlled"); - } - - /* -@@ -3429,13 +3431,25 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - /* case: role conflict error, need to restart with new role */ - nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); - -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ -+ - if (p->stun_message.buffer != NULL) { - guint64 tie; - gboolean controlled_mode; - -- /* note: our role might already have changed due to an -- * incoming request, but if not, change role now; -- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ - controlled_mode = (stun_message_find64 (&p->stun_message, - STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == - STUN_MESSAGE_RETURN_SUCCESS); --- -2.13.6 - - -From 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Tue, 5 Sep 2017 14:50:29 -0400 -Subject: [PATCH 54/70] agent: Set error if it isn't set - ---- - agent/agent.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index e48d7f3..a4dcc0c 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -4280,7 +4280,7 @@ nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) - { - GError **error = user_data; - -- if (error && *error) -+ if (error && !*error) - g_cancellable_set_error_if_cancelled (cancellable, error); - return G_SOURCE_REMOVE; - } --- -2.13.6 - - -From 25be00271a4c8c684a2d435d29ae0811dbf5e21c Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 26 Jun 2017 20:36:35 +0200 -Subject: [PATCH 55/70] conncheck: reorder some chunks of code - -With this patch we simplify the levels of code indentation. - -Differential Revision: https://phabricator.freedesktop.org/D1758 ---- - agent/conncheck.c | 858 +++++++++++++++++++++++++++--------------------------- - 1 file changed, 422 insertions(+), 436 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 8945e0f..874f7b1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -608,106 +608,106 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - gboolean keep_timer_going = FALSE; - GSList *i; - CandidateCheckPair *pair; -+ unsigned int timeout; - - /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; -+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ NiceComponent *component; -+ -+ if (!agent_find_component (agent, p->stream_id, p->component_id, -+ NULL, &component)) -+ continue; -+ -+ if (p->state != NICE_CHECK_IN_PROGRESS) -+ continue; - -- if (p->state == NICE_CHECK_IN_PROGRESS) { -- if (p->stun_message.buffer == NULL) { -- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -- } else if (priv_timer_expired (&p->next_tick, now)) { -- switch (stun_timer_refresh (&p->timer)) { -- case STUN_USAGE_TIMER_RETURN_TIMEOUT: -- { -- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -- NiceComponent *component; -+ if (p->stun_message.buffer == NULL) { -+ nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -+ p->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ continue; -+ } - -+ if (!priv_timer_expired (&p->next_tick, now)) -+ continue; -+ -+ switch (stun_timer_refresh (&p->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: - timer_timeout: -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; - -- /* case: error, abort processing */ -- nice_address_to_string (&p->local->addr, tmpbuf1); -- nice_address_to_string (&p->remote->addr, tmpbuf2); -- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -- tmpbuf1, nice_address_get_port (&p->local->addr), -- tmpbuf2, nice_address_get_port (&p->remote->addr)); -- candidate_check_pair_fail (stream, agent, p); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", retransmission failed"); -- -- /* perform a check if a transition state from connected to -- * ready can be performed. This may happen here, when the last -- * in-progress pair has expired its retransmission count -- * in priv_conn_check_tick_stream(), which is a condition to -- * make the transition connected to ready. -- */ -- if (agent_find_component (agent, p->stream_id, p->component_id, -- NULL, &component)) -- priv_update_check_list_state_for_ready (agent, stream, -- component); -- break; -- } -- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -- { -- unsigned int timeout = stun_timer_remainder (&p->timer); -+ /* case: error, abort processing */ -+ nice_address_to_string (&p->local->addr, tmpbuf1); -+ nice_address_to_string (&p->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -+ tmpbuf1, nice_address_get_port (&p->local->addr), -+ tmpbuf2, nice_address_get_port (&p->remote->addr)); -+ candidate_check_pair_fail (stream, agent, p); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", retransmission failed"); -+ -+ /* perform a check if a transition state from connected to -+ * ready can be performed. This may happen here, when the last -+ * in-progress pair has expired its retransmission count -+ * in priv_conn_check_tick_stream(), which is a condition to -+ * make the transition connected to ready. -+ */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ break; -+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -+ timeout = stun_timer_remainder (&p->timer); - -- /* case: retransmission stopped, due to the nomination of -- * a pair with a higher priority than this in-progress pair, -- * ICE spec, sect 8.1.2 "Updating States", item 2.2 -- */ -- if (!p->retransmit_on_timeout) -- goto timer_timeout; -+ /* case: retransmission stopped, due to the nomination of -+ * a pair with a higher priority than this in-progress pair, -+ * ICE spec, sect 8.1.2 "Updating States", item 2.2 -+ */ -+ if (!p->retransmit_on_timeout) -+ goto timer_timeout; - -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; - -- /* case: not ready, so schedule a new timeout */ -- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -- "(timeout %dms, delay=%dms, retrans=%d).", -- agent, p, timeout, p->timer.delay, p->timer.retransmissions); -+ /* case: not ready, so schedule a new timeout */ -+ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -+ "(timeout %dms, delay=%dms, retrans=%d).", -+ agent, p, timeout, p->timer.delay, p->timer.retransmissions); - -- agent_socket_send (p->sockptr, &p->remote->addr, -- stun_message_length (&p->stun_message), -- (gchar *)p->stun_buffer); -+ agent_socket_send (p->sockptr, &p->remote->addr, -+ stun_message_length (&p->stun_message), -+ (gchar *)p->stun_buffer); - - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ p->next_tick = *now; -+ g_time_val_add (&p->next_tick, timeout * 1000); - -- return TRUE; -- } -- case STUN_USAGE_TIMER_RETURN_SUCCESS: -- { -- unsigned int timeout = stun_timer_remainder (&p->timer); -+ return TRUE; -+ case STUN_USAGE_TIMER_RETURN_SUCCESS: -+ timeout = stun_timer_remainder (&p->timer); - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ p->next_tick = *now; -+ g_time_val_add (&p->next_tick, timeout * 1000); - -- keep_timer_going = TRUE; -- break; -- } -- default: -- /* Nothing to do. */ -- break; -- } -- } -+ keep_timer_going = TRUE; -+ break; -+ default: -+ /* Nothing to do. */ -+ break; - } - } - -@@ -2628,27 +2628,23 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { - switch (agent->nomination_mode) { - case NICE_NOMINATION_MODE_REGULAR: -- { -- /* We are doing regular nomination, so we set the use-candidate -- * attrib, when the controlling agent decided which valid pair to -- * resend with this flag in priv_conn_check_tick_stream() -- */ -- cand_use = pair->use_candidate_on_next_check; -- nice_debug ("Agent %p : %s: set cand_use=%d " -- "(regular nomination).", agent, G_STRFUNC, cand_use); -- break; -- } -+ /* We are doing regular nomination, so we set the use-candidate -+ * attrib, when the controlling agent decided which valid pair to -+ * resend with this flag in priv_conn_check_tick_stream() -+ */ -+ cand_use = pair->use_candidate_on_next_check; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(regular nomination).", agent, G_STRFUNC, cand_use); -+ break; - case NICE_NOMINATION_MODE_AGGRESSIVE: -- { -- /* We are doing aggressive nomination, we set the use-candidate -- * attrib in every check we send, when we are the controlling -- * agent, RFC 5245, 8.1.1.2 -- */ -- cand_use = controlling; -- nice_debug ("Agent %p : %s: set cand_use=%d " -- "(aggressive nomination).", agent, G_STRFUNC, cand_use); -- break; -- } -+ /* We are doing aggressive nomination, we set the use-candidate -+ * attrib in every check we send, when we are the controlling -+ * agent, RFC 5245, 8.1.1.2 -+ */ -+ cand_use = controlling; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(aggressive nomination).", agent, G_STRFUNC, cand_use); -+ break; - default: - /* Nothing to do. */ - break; -@@ -2656,107 +2652,105 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - } else if (cand_use) - pair->nominated = controlling; - -- if (uname_len > 0) { -- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -- uname, uname_len, password, password_len, -- cand_use, controlling, pair->prflx_priority, -- agent->tie_breaker, -- pair->local->foundation, -- agent_to_ice_compatibility (agent)); -+ if (uname_len == 0) { -+ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -+ pair->stun_message.buffer = NULL; -+ pair->stun_message.buffer_len = 0; -+ return -1; -+ } - -- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -- pair->stun_message.buffer); -+ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -+ &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -+ uname, uname_len, password, password_len, -+ cand_use, controlling, pair->prflx_priority, -+ agent->tie_breaker, -+ pair->local->foundation, -+ agent_to_ice_compatibility (agent)); - -- if (agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- g_free (password); -- } -+ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -+ pair->stun_message.buffer); - -- if (buffer_len > 0) { -- if (nice_socket_is_reliable(pair->sockptr)) { -- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); -- } else { -- StunTimer *timer = &pair->timer; -- -- if (pair->recheck_on_timeout) -- /* The pair recheck on timeout can easily cause repetitive rechecks in -- * a ping-pong effect, if both peers with the same behaviour try to -- * check the same pair almost simultaneously, and if the network rtt -- * is greater than the initial timer rto. The reply to the initial -- * stun request may arrive after the in-progress conncheck -- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -- * creates a new stun request, and forgets the initial one. -- * The conncheck timer is restarted with the same initial value, -- * so the same situation happens again later. -- * -- * We choose to avoid resetting the timer in such situation. -- * After enough retransmissions, the timeout delay becomes -- * longer than the rtt, and the stun reply can be handled. -- */ -- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -- agent, pair, -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay); -- else -- stun_timer_start (timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -- pair->recheck_on_timeout = FALSE; -- } -+ if (agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ g_free (password); -+ } - -- /* TCP-ACTIVE candidate must create a new socket before sending -- * by connecting to the peer. The new socket is stored in the candidate -- * check pair, until we discover a new local peer reflexive */ -- if (pair->sockptr->fileno == NULL && -- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && -- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -- NiceStream *stream2 = NULL; -- NiceComponent *component2 = NULL; -- NiceSocket *new_socket; -- -- if (agent_find_component (agent, pair->stream_id, pair->component_id, -- &stream2, &component2)) { -- new_socket = nice_tcp_active_socket_connect (pair->sockptr, -- &pair->remote->addr); -- if (new_socket) { -- pair->sockptr = new_socket; -- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -- -- if (agent->reliable) { -- nice_socket_set_writable_callback (pair->sockptr, -- _tcp_sock_is_writable, component2); -- } -+ if (buffer_len == 0) { -+ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -+ pair->stun_message.buffer = NULL; -+ pair->stun_message.buffer_len = 0; -+ return -1; -+ } - -- nice_component_attach_socket (component2, new_socket); -- } -- } -- } -- /* send the conncheck */ -- agent_socket_send (pair->sockptr, &pair->remote->addr, -- buffer_len, (gchar *)pair->stun_buffer); -+ if (nice_socket_is_reliable(pair->sockptr)) -+ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); -+ else { -+ StunTimer *timer = &pair->timer; -+ -+ if (pair->recheck_on_timeout) -+ /* The pair recheck on timeout can easily cause repetitive rechecks in -+ * a ping-pong effect, if both peers with the same behaviour try to -+ * check the same pair almost simultaneously, and if the network rtt -+ * is greater than the initial timer rto. The reply to the initial -+ * stun request may arrive after the in-progress conncheck -+ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -+ * creates a new stun request, and forgets the initial one. -+ * The conncheck timer is restarted with the same initial value, -+ * so the same situation happens again later. -+ * -+ * We choose to avoid resetting the timer in such situation. -+ * After enough retransmissions, the timeout delay becomes -+ * longer than the rtt, and the stun reply can be handled. -+ */ -+ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -+ agent, pair, -+ timer->retransmissions, timer->max_retransmissions, -+ timer->delay - stun_timer_remainder (timer), timer->delay); -+ else -+ stun_timer_start (timer, -+ priv_compute_conncheck_timer (agent, stream), -+ agent->stun_max_retransmissions); -+ pair->recheck_on_timeout = FALSE; -+ } - -- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { -- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -- &pair->remote->addr); -+ /* TCP-ACTIVE candidate must create a new socket before sending -+ * by connecting to the peer. The new socket is stored in the candidate -+ * check pair, until we discover a new local peer reflexive */ -+ if (pair->sockptr->fileno == NULL && -+ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && -+ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -+ NiceStream *stream2 = NULL; -+ NiceComponent *component2 = NULL; -+ NiceSocket *new_socket; -+ -+ if (agent_find_component (agent, pair->stream_id, pair->component_id, -+ &stream2, &component2)) { -+ new_socket = nice_tcp_active_socket_connect (pair->sockptr, -+ &pair->remote->addr); -+ if (new_socket) { -+ pair->sockptr = new_socket; -+ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -+ -+ if (agent->reliable) -+ nice_socket_set_writable_callback (pair->sockptr, -+ _tcp_sock_is_writable, component2); -+ -+ nice_component_attach_socket (component2, new_socket); - } -- -- timeout = stun_timer_remainder (&pair->timer); -- /* note: convert from milli to microseconds for g_time_val_add() */ -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -- } else { -- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -- return -1; - } -- } else { -- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -- return -1; - } -+ /* send the conncheck */ -+ agent_socket_send (pair->sockptr, &pair->remote->addr, -+ buffer_len, (gchar *)pair->stun_buffer); -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -+ &pair->remote->addr); -+ -+ timeout = stun_timer_remainder (&pair->timer); -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ g_get_current_time (&pair->next_tick); -+ g_time_val_add (&pair->next_tick, timeout * 1000); - - return 0; - } -@@ -2876,74 +2870,74 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", - agent, p, p->foundation, priv_state_to_gchar (p->state)); - -- if (p->state == NICE_CHECK_WAITING || -- p->state == NICE_CHECK_FROZEN) { -- nice_debug ("Agent %p : pair %p added for a triggered check.", -- agent, p); -- priv_add_pair_to_triggered_check_queue (agent, p); -- } -- else if (p->state == NICE_CHECK_IN_PROGRESS) { -- /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -- * we cancel the in-progress transaction, and after the -- * retransmission timeout, we create a new connectivity check -- * for that pair. The controlling role of this new check may -- * be different from the role of this cancelled check. -- * -- * note: the flag retransmit_on_timeout unset means that -- * another pair, with a higher priority is already nominated, -- * so there's no reason to recheck this pair, since it can in -- * no way replace the nominated one. -- */ -- if (!nice_socket_is_reliable (p->sockptr)) { -- if (p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p will be rechecked " -- "on stun timer timeout.", agent, p); -- /* this flag will determine the action at the retransmission -- * timeout of the stun timer -- */ -- p->recheck_on_timeout = TRUE; -- } else -- nice_debug ("Agent %p : pair %p won't be retransmitted.", -- agent, p); -- } -- } -- else if (p->state == NICE_CHECK_SUCCEEDED) { -- nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); -- /* note: this is a bit unsure corner-case -- let's do the -- same state update as for processing responses to our own checks */ -- /* note: this update is required by the dribble test, to -- * ensure the transition ready -> connected -> ready, because -- * an incoming stun request generates a discovered peer reflexive, -- * that causes the ready -> connected transition. -- */ -- priv_update_check_list_state_for_ready (agent, stream, component); -- } else if (p->state == NICE_CHECK_FAILED) { -- /* 7.2.1.4 Triggered Checks -- * If the state of the pair is Failed, it is changed to Waiting -- * and the agent MUST create a new connectivity check for that -- * pair (representing a new STUN Binding request transaction), by -- * enqueueing the pair in the triggered check queue. -- * -- * note: the flag retransmit_on_timeout unset means that -- * another pair, with a higher priority is already nominated, -- * we apply the same strategy than with an in-progress pair -- * above. -- */ -- if (p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p added for a triggered check.", -+ switch (p->state) { -+ case NICE_CHECK_WAITING: -+ case NICE_CHECK_FROZEN: -+ nice_debug ("Agent %p : pair %p added for a triggered check.", - agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -- /* If the component for this pair is in failed state, move it -- * back to connecting, and reinitiate the timers -+ break; -+ case NICE_CHECK_IN_PROGRESS: -+ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -+ * we cancel the in-progress transaction, and after the -+ * retransmission timeout, we create a new connectivity check -+ * for that pair. The controlling role of this new check may -+ * be different from the role of this cancelled check. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * so there's no reason to recheck this pair, since it can in -+ * no way replace the nominated one. - */ -- if (component->state == NICE_COMPONENT_STATE_FAILED) { -- agent_signal_component_state_change (agent, stream->id, -- component->id, NICE_COMPONENT_STATE_CONNECTING); -- conn_check_schedule_next (agent); -+ if (!nice_socket_is_reliable (p->sockptr) && -+ p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p will be rechecked " -+ "on stun timer timeout.", agent, p); -+ /* this flag will determine the action at the retransmission -+ * timeout of the stun timer -+ */ -+ p->recheck_on_timeout = TRUE; - } -- } else -- nice_debug ("Agent %p : pair %p won't be retransmitted.", -- agent, p); -+ break; -+ case NICE_CHECK_SUCCEEDED: -+ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); -+ /* note: this is a bit unsure corner-case -- let's do the -+ same state update as for processing responses to our own checks */ -+ /* note: this update is required by the dribble test, to -+ * ensure the transition ready -> connected -> ready, because -+ * an incoming stun request generates a discovered peer reflexive, -+ * that causes the ready -> connected transition. -+ */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ break; -+ case NICE_CHECK_FAILED: -+ /* 7.2.1.4 Triggered Checks -+ * If the state of the pair is Failed, it is changed to Waiting -+ * and the agent MUST create a new connectivity check for that -+ * pair (representing a new STUN Binding request transaction), by -+ * enqueueing the pair in the triggered check queue. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * we apply the same strategy than with an in-progress pair -+ * above. -+ */ -+ if (p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } -+ } -+ break; -+ default: -+ break; - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -3271,208 +3265,200 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - socklen_t socklen = sizeof (sockaddr); - GSList *i; - StunUsageIceReturn res; -- gboolean trans_found = FALSE; - StunTransactionId discovery_id; - StunTransactionId response_id; - stun_message_id (resp, response_id); - -- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) { -+ for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -- if (p->stun_message.buffer) { -- stun_message_id (&p->stun_message, discovery_id); -+ if (p->stun_message.buffer == NULL) -+ continue; - -- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) { -- res = stun_usage_ice_conncheck_process (resp, -- &sockaddr.storage, &socklen, -- agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -- -- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* case: found a matching connectivity check request */ -- -- CandidateCheckPair *ok_pair = NULL; -- -- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- -- /* step: verify that response came from the same IP address we -- * sent the original request to (see 7.1.2.1. "Failure -- * Cases") */ -- if (nice_address_equal (from, &p->remote->addr) != TRUE) { -- -- p->state = NICE_CHECK_FAILED; -- if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- gchar tmpbuf2[INET6_ADDRSTRLEN]; -- nice_debug ("Agent %p : conncheck %p FAILED" -- " (mismatch of source address).", agent, p); -- nice_address_to_string (&p->remote->addr, tmpbuf); -- nice_address_to_string (from, tmpbuf2); -- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -- tmpbuf, nice_address_get_port (&p->remote->addr), -- tmpbuf2, nice_address_get_port (from)); -- } -- trans_found = TRUE; -- break; -- } -+ stun_message_id (&p->stun_message, discovery_id); - -- /* note: CONNECTED but not yet READY, see docs */ -- -- /* step: handle the possible case of a peer-reflexive -- * candidate where the mapped-address in response does -- * not match any local candidate, see 7.1.2.2.1 -- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -- -- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -- p->state = NICE_CHECK_SUCCEEDED; -- p->valid = TRUE; -- g_assert_not_reached (); -- nice_debug ("Agent %p : Mapped address not found." -- " conncheck %p SUCCEEDED.", agent, p); -- nice_component_add_valid_candidate (component, p->remote); -- } else { -- ok_pair = priv_process_response_check_for_reflexive (agent, -- stream, component, p, sockptr, &sockaddr.addr, -- local_candidate, remote_candidate); -- } -+ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -+ continue; - -- /* note: The success of this check might also -- * cause the state of other checks to change as well, ICE -- * spec 7.1.3.2.3 -- */ -- priv_conn_check_unfreeze_related (agent, stream, p); -- -- /* Note: this assignment helps to reduce the numbers of cases -- * to be tested. If ok_pair and p refer to distinct pairs, it -- * means that ok_pair is a discovered peer reflexive one, -- * caused by the check made on pair p. In that case, the -- * flags to be tested are on p, but the nominated flag will be -- * set on ok_pair. When there's no discovered pair, p and -- * ok_pair refer to the same pair. -- * To summarize : p is a SUCCEEDED pair, ok_pair is a -- * DISCOVERED, VALID, and eventually NOMINATED pair. -- */ -- if (!ok_pair) -- ok_pair = p; -- -- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -- Nominated Flag" (ID-19) */ -- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -- nice_debug ("Agent %p : Updating nominated flag (%s): " -- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -- "UDP" : "TCP", -- ok_pair, ok_pair->use_candidate_on_next_check, -- ok_pair->mark_nominated_on_response_arrival, -- p, p->use_candidate_on_next_check, -- p->mark_nominated_on_response_arrival); -- -- if (agent->controlling_mode) { -- switch (agent->nomination_mode) { -- case NICE_NOMINATION_MODE_REGULAR: -- if (p->use_candidate_on_next_check) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(regular nomination, control=1, " -- "use_cand_on_next_check=1).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- case NICE_NOMINATION_MODE_AGGRESSIVE: -- if (!p->nominated) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(aggressive nomination, control=1).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- default: -- /* Nothing to do */ -- break; -+ res = stun_usage_ice_conncheck_process (resp, -+ &sockaddr.storage, &socklen, -+ agent_to_ice_compatibility (agent)); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -+ "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -+ -+ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -+ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ /* case: found a matching connectivity check request */ -+ -+ CandidateCheckPair *ok_pair = NULL; -+ -+ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; -+ -+ /* step: verify that response came from the same IP address we -+ * sent the original request to (see 7.1.2.1. "Failure -+ * Cases") */ -+ if (nice_address_equal (from, &p->remote->addr) == FALSE) { -+ -+ p->state = NICE_CHECK_FAILED; -+ if (nice_debug_is_enabled ()) { -+ gchar tmpbuf[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_debug ("Agent %p : conncheck %p FAILED" -+ " (mismatch of source address).", agent, p); -+ nice_address_to_string (&p->remote->addr, tmpbuf); -+ nice_address_to_string (from, tmpbuf2); -+ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -+ tmpbuf, nice_address_get_port (&p->remote->addr), -+ tmpbuf2, nice_address_get_port (from)); -+ } -+ return TRUE; -+ } -+ -+ /* note: CONNECTED but not yet READY, see docs */ -+ -+ /* step: handle the possible case of a peer-reflexive -+ * candidate where the mapped-address in response does -+ * not match any local candidate, see 7.1.2.2.1 -+ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -+ -+ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ p->state = NICE_CHECK_SUCCEEDED; -+ p->valid = TRUE; -+ nice_debug ("Agent %p : Mapped address not found." -+ " conncheck %p SUCCEEDED.", agent, p); -+ nice_component_add_valid_candidate (component, p->remote); -+ } else -+ ok_pair = priv_process_response_check_for_reflexive (agent, -+ stream, component, p, sockptr, &sockaddr.addr, -+ local_candidate, remote_candidate); -+ -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ -+ if (!ok_pair) -+ ok_pair = p; -+ -+ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -+ Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, control=1, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; - } -- } else { -- if (p->mark_nominated_on_response_arrival) { -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(%s nomination, control=0, mark_on_response=1).", -- agent, ok_pair, ok_pair->foundation, -- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -- "aggressive" : "regular"); -+ "(aggressive nomination, control=1).", -+ agent, ok_pair, ok_pair->foundation); - ok_pair->nominated = TRUE; - } -- } -+ break; -+ default: -+ /* Nothing to do */ -+ break; - } -- -- if (ok_pair->nominated == TRUE) { -- priv_update_selected_pair (agent, component, ok_pair); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a nominated pair"); -- -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, control=0, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; - } -+ } -+ } - -- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -- states" and 8.1.2 "Updating States", ID-19) */ -- priv_update_check_list_state_for_ready (agent, stream, component); -+ if (ok_pair->nominated == TRUE) { -+ priv_update_selected_pair (agent, component, ok_pair); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a nominated pair"); - -- trans_found = TRUE; -- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -- /* case: role conflict error, need to restart with new role */ -- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- -- /* note: this res value indicates that the role of the peer -- * agent has not changed after the tie-breaker comparison, so -- * this is our role that must change. see ICE sect. 7.1.3.1 -- * "Failure Cases". Our role might already have changed due to -- * an earlier incoming request, but if not, change role now. -- * -- * Sect. 7.1.3.1 is not clear on this point, but we choose to -- * put the candidate pair in the triggered check list even -- * when the agent did not switch its role. The reason for this -- * interpretation is that the reception of the stun reply, even -- * an error reply, is a good sign that this pair will be -- * valid, if we retry the check after the role of both peers -- * has been fixed. -- */ -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } - -- if (p->stun_message.buffer != NULL) { -- guint64 tie; -- gboolean controlled_mode; -+ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -+ states" and 8.1.2 "Updating States", ID-19) */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -+ /* case: role conflict error, need to restart with new role */ -+ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -+ -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ - -- controlled_mode = (stun_message_find64 (&p->stun_message, -- STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -- STUN_MESSAGE_RETURN_SUCCESS); -+ if (p->stun_message.buffer != NULL) { -+ guint64 tie; -+ gboolean controlled_mode; - -- priv_check_for_role_conflict (agent, controlled_mode); -+ controlled_mode = (stun_message_find64 (&p->stun_message, -+ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -+ STUN_MESSAGE_RETURN_SUCCESS); - -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- priv_add_pair_to_triggered_check_queue (agent, p); -- } -- trans_found = TRUE; -- } else { -- /* case: STUN error, the check STUN context was freed */ -- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- trans_found = TRUE; -- } -+ priv_check_for_role_conflict (agent, controlled_mode); -+ -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; -+ priv_add_pair_to_triggered_check_queue (agent, p); - } -+ } else { -+ /* case: STUN error, the check STUN context was freed */ -+ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; - } -+ return TRUE; - } - -- return trans_found; -+ return FALSE; - } - - /* --- -2.13.6 - - -From ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 26 Jun 2017 20:41:49 +0200 -Subject: [PATCH 56/70] conncheck: make debug sentences more accurate - -We add a helper function to print the pair state in-extenso. - -Differential Revision: https://phabricator.freedesktop.org/D1759 ---- - agent/conncheck.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++------- - 1 file changed, 61 insertions(+), 9 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 874f7b1..9517ee1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -108,6 +108,54 @@ priv_state_to_gchar (NiceCheckState state) - } - - static const gchar * -+priv_state_to_string (NiceCheckState state) -+{ -+ switch (state) { -+ case NICE_CHECK_WAITING: -+ return "waiting"; -+ case NICE_CHECK_IN_PROGRESS: -+ return "in progress"; -+ case NICE_CHECK_SUCCEEDED: -+ return "succeeded"; -+ case NICE_CHECK_FAILED: -+ return "failed"; -+ case NICE_CHECK_FROZEN: -+ return "frozen"; -+ case NICE_CHECK_DISCOVERED: -+ return "discovered"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * -+priv_ice_return_to_string (StunUsageIceReturn ice_return) -+{ -+ switch (ice_return) { -+ case STUN_USAGE_ICE_RETURN_SUCCESS: -+ return "success"; -+ case STUN_USAGE_ICE_RETURN_ERROR: -+ return "error"; -+ case STUN_USAGE_ICE_RETURN_INVALID: -+ return "invalid"; -+ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT: -+ return "role conflict"; -+ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST: -+ return "invalid request"; -+ case STUN_USAGE_ICE_RETURN_INVALID_METHOD: -+ return "invalid method"; -+ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR: -+ return "memory error"; -+ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS: -+ return "invalid address"; -+ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS: -+ return "no mapped address"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * - priv_candidate_type_to_string (NiceCandidateType type) - { - switch (type) { -@@ -2614,7 +2662,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - nice_address_to_string (&pair->remote->addr, tmpbuf2); - nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " - "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, -+ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr), - pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, -@@ -2622,7 +2670,8 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -- pair->prflx_priority, controlling); -+ pair->prflx_priority, -+ controlling ? "controlling" : "controlled"); - } - - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -@@ -2867,8 +2916,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p = p->succeeded_pair; - } - -- nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", -- agent, p, p->foundation, priv_state_to_gchar (p->state)); -+ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", -+ agent, p, p->foundation, priv_state_to_string (p->state)); - - switch (p->state) { - case NICE_CHECK_WAITING: -@@ -3283,8 +3332,11 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - res = stun_usage_ice_conncheck_process (resp, - &sockaddr.storage, &socklen, - agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -+ "%s,res=%s.", -+ agent, p, -+ agent->controlling_mode ? "controlling" : "controlled", -+ priv_ice_return_to_string (res)); - - if (res == STUN_USAGE_ICE_RETURN_SUCCESS || - res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -@@ -3370,7 +3422,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - case NICE_NOMINATION_MODE_REGULAR: - if (p->use_candidate_on_next_check) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(regular nomination, control=1, " -+ "(regular nomination, controlling, " - "use_cand_on_next_check=1).", - agent, ok_pair, ok_pair->foundation); - ok_pair->nominated = TRUE; -@@ -3379,7 +3431,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - case NICE_NOMINATION_MODE_AGGRESSIVE: - if (!p->nominated) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(aggressive nomination, control=1).", -+ "(aggressive nomination, controlling).", - agent, ok_pair, ok_pair->foundation); - ok_pair->nominated = TRUE; - } -@@ -3391,7 +3443,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - } else { - if (p->mark_nominated_on_response_arrival) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(%s nomination, control=0, mark_on_response=1).", -+ "(%s nomination, controlled, mark_on_response=1).", - agent, ok_pair, ok_pair->foundation, - agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? - "aggressive" : "regular"); --- -2.13.6 - - -From e860948b5fe3a791119957f26045b8f5159baeff Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 26 Jun 2017 21:06:36 +0200 -Subject: [PATCH 57/70] conncheck: use stun_timer_remainder less frequently - -We try to use stun_timer_remainder() less frequently, particularily -in the debug messages, and favour of the next_tick value associated -to the pair. - -Differential Revision: https://phabricator.freedesktop.org/D1760 ---- - agent/conncheck.c | 77 ++++++++++++++++++++++++++++++++++--------------------- - 1 file changed, 48 insertions(+), 29 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 9517ee1..8075d4f 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -86,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - now->tv_sec >= timer->tv_sec; - } - -+static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now) -+{ -+ unsigned int delay; -+ if (now->tv_sec > timer->tv_sec || -+ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec)) -+ return 0; -+ delay = (timer->tv_sec - now->tv_sec) * 1000; -+ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000; -+ return delay; -+} -+ - static gchar - priv_state_to_gchar (NiceCheckState state) - { -@@ -180,10 +191,13 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - { - GSList *i, *k; - guint j; -+ GTimeVal now; - - if (!nice_debug_is_verbose ()) - return; - -+ g_get_current_time (&now); -+ - #define PRIORITY_LEN 32 - - nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", -@@ -209,7 +223,8 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - priv_candidate_type_to_string (pair->local->type), - priv_candidate_type_to_string (pair->remote->type), - timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay, -+ timer->delay - priv_timer_remainder (&pair->next_tick, &now), -+ timer->delay, - local_addr, nice_address_get_port (&pair->local->addr), - remote_addr, nice_address_get_port (&pair->remote->addr), - priv_state_to_gchar (pair->state), -@@ -445,8 +460,6 @@ priv_find_first_frozen_check_list (NiceAgent *agent) - */ - static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) - { -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); - pair->state = NICE_CHECK_IN_PROGRESS; - nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); - conn_check_send (agent, pair); -@@ -651,12 +664,15 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) - { - gboolean keep_timer_going = FALSE; - GSList *i; - CandidateCheckPair *pair; - unsigned int timeout; -+ GTimeVal now; -+ -+ g_get_current_time (&now); - - /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { -@@ -678,7 +694,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - continue; - } - -- if (!priv_timer_expired (&p->next_tick, now)) -+ if (!priv_timer_expired (&p->next_tick, &now)) - continue; - - switch (stun_timer_refresh (&p->timer)) { -@@ -712,8 +728,6 @@ timer_timeout: - priv_update_check_list_state_for_ready (agent, stream, component); - break; - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -- timeout = stun_timer_remainder (&p->timer); -- - /* case: retransmission stopped, due to the nomination of - * a pair with a higher priority than this in-progress pair, - * ICE spec, sect 8.1.2 "Updating States", item 2.2 -@@ -730,9 +744,13 @@ timer_timeout: - break; - - /* case: not ready, so schedule a new timeout */ -+ timeout = stun_timer_remainder (&p->timer); -+ - nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -- "(timeout %dms, delay=%dms, retrans=%d).", -- agent, p, timeout, p->timer.delay, p->timer.retransmissions); -+ "(timer=%d/%d %d/%dms).", -+ agent, p, -+ p->timer.retransmissions, p->timer.max_retransmissions, -+ p->timer.delay - timeout, p->timer.delay); - - agent_socket_send (p->sockptr, &p->remote->addr, - stun_message_length (&p->stun_message), -@@ -740,7 +758,7 @@ timer_timeout: - - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -+ p->next_tick = now; - g_time_val_add (&p->next_tick, timeout * 1000); - - return TRUE; -@@ -748,7 +766,7 @@ timer_timeout: - timeout = stun_timer_remainder (&p->timer); - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -+ p->next_tick = now; - g_time_val_add (&p->next_tick, timeout * 1000); - - keep_timer_going = TRUE; -@@ -1001,9 +1019,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - CandidateCheckPair *pair = NULL; - gboolean keep_timer_going = FALSE; - GSList *i, *j; -- GTimeVal now; -- -- g_get_current_time (&now); - - /* the conncheck really starts when we have built - * a connection check list for each stream -@@ -1047,7 +1062,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - */ - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -- if (priv_conn_check_tick_stream (stream, agent, &now)) -+ if (priv_conn_check_tick_stream (stream, agent)) - keep_timer_going = TRUE; - if (priv_conn_check_tick_stream_nominate (stream, agent)) - keep_timer_going = TRUE; -@@ -2731,12 +2746,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - return -1; - } - -- if (nice_socket_is_reliable(pair->sockptr)) -- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); -- else { -+ if (nice_socket_is_reliable(pair->sockptr)) { -+ timeout = agent->stun_reliable_timeout; -+ stun_timer_start_reliable(&pair->timer, timeout); -+ } else { - StunTimer *timer = &pair->timer; - -- if (pair->recheck_on_timeout) -+ if (pair->recheck_on_timeout) { -+ GTimeVal now; - /* The pair recheck on timeout can easily cause repetitive rechecks in - * a ping-pong effect, if both peers with the same behaviour try to - * check the same pair almost simultaneously, and if the network rtt -@@ -2751,17 +2768,24 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * After enough retransmissions, the timeout delay becomes - * longer than the rtt, and the stun reply can be handled. - */ -+ -+ g_get_current_time (&now); -+ timeout = priv_timer_remainder (&pair->next_tick, &now); - nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", - agent, pair, - timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay); -- else -- stun_timer_start (timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -+ timer->delay - timeout, -+ timer->delay); -+ } else { -+ timeout = priv_compute_conncheck_timer (agent, stream); -+ stun_timer_start (timer, timeout, agent->stun_max_retransmissions); -+ } - pair->recheck_on_timeout = FALSE; - } - -+ g_get_current_time (&pair->next_tick); -+ g_time_val_add (&pair->next_tick, timeout * 1000); -+ - /* TCP-ACTIVE candidate must create a new socket before sending - * by connecting to the peer. The new socket is stored in the candidate - * check pair, until we discover a new local peer reflexive */ -@@ -2796,11 +2820,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, - &pair->remote->addr); - -- timeout = stun_timer_remainder (&pair->timer); -- /* note: convert from milli to microseconds for g_time_val_add() */ -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -- - return 0; - } - --- -2.13.6 - - -From 36f306f4a95f1c2b3e9c584b5a645a78e231c020 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 26 Jun 2017 21:41:44 +0200 -Subject: [PATCH 58/70] conncheck: support several stun requests per pair - -This patch should improve the reliabily of the connection check by -keeping the record of several simultaneous ongoing stun requests per -pair. A new stun request on an in-progress pair typically is caused by -in inbound stun request from the peer on this same pair. This is named -"Triggered Checks" in the spec. When this situation arises, it is fair -to handle these two stun requests simultaneously, the triggered check, -and the initial ordinary check, since both can potentially succeed. - -Differential Revision: https://phabricator.freedesktop.org/D1761 ---- - agent/conncheck.c | 701 +++++++++++++++++++++++++++--------------------------- - agent/conncheck.h | 21 +- - 2 files changed, 361 insertions(+), 361 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 8075d4f..2a85738 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -189,8 +189,8 @@ priv_candidate_type_to_string (NiceCandidateType type) - static void - priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) - { -- GSList *i, *k; -- guint j; -+ GSList *i, *k, *l; -+ guint j, m; - GTimeVal now; - - if (!nice_debug_is_verbose ()) -@@ -210,27 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - if (pair->component_id == j) { - gchar local_addr[INET6_ADDRSTRLEN]; - gchar remote_addr[INET6_ADDRSTRLEN]; -- StunTimer *timer = &pair->timer; - - nice_address_to_string (&pair->local->addr, local_addr); - nice_address_to_string (&pair->remote->addr, remote_addr); - - nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -- "f=%s t=%s:%s timer=%d/%d %d/%dms " -- "[%s]:%u > [%s]:%u state=%c%s%s%s", -+ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s", - agent, pair->stream_id, pair->component_id, pair, - pair->foundation, - priv_candidate_type_to_string (pair->local->type), - priv_candidate_type_to_string (pair->remote->type), -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - priv_timer_remainder (&pair->next_tick, &now), -- timer->delay, - local_addr, nice_address_get_port (&pair->local->addr), - remote_addr, nice_address_get_port (&pair->remote->addr), - priv_state_to_gchar (pair->state), - pair->valid ? "V" : "", - pair->nominated ? "N" : "", - g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); -+ -+ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) { -+ StunTransaction *stun = l->data; -+ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -+ "stun#=%d timer=%d/%d %d/%dms buf=%p %s", -+ agent, pair->stream_id, pair->component_id, pair, m, -+ stun->timer.retransmissions, stun->timer.max_retransmissions, -+ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now), -+ stun->timer.delay, -+ stun->message.buffer, -+ (m == 0 && pair->retransmit) ? "(R)" : ""); -+ } - } - } - } -@@ -608,52 +615,92 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - } - } - -+/* -+ * Create a new STUN transaction and add it to the list -+ * of ongoing stun transactions of a pair. -+ * -+ * @pair the pair the new stun transaction should be added to. -+ * @return the created stun transaction. -+ */ -+static StunTransaction * -+priv_add_stun_transaction (CandidateCheckPair *pair) -+{ -+ StunTransaction *stun = g_slice_new0 (StunTransaction); -+ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun); -+ pair->retransmit = TRUE; -+ return stun; -+} -+ -+/* -+ * Forget a STUN transaction. -+ * -+ * @data the stun transaction to be forgotten. -+ * @user_data the component contained the concerned stun agent. -+ */ - static void --candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) -+priv_forget_stun_transaction (gpointer data, gpointer user_data) - { -+ StunTransaction *stun = data; -+ NiceComponent *component = user_data; - StunTransactionId id; -- NiceComponent *component; -- -- component = nice_stream_find_component_by_id (stream, p->component_id); -- -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); - -- if (p->stun_message.buffer != NULL) { -- stun_message_id (&p->stun_message, id); -+ if (stun->message.buffer != NULL) { -+ stun_message_id (&stun->message, id); - stun_agent_forget_transaction (&component->stun_agent, id); - } -+} - -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+static void -+priv_free_stun_transaction (gpointer data) -+{ -+ g_slice_free (StunTransaction, data); - } - - /* -- * Function that resubmits a new connection check, after a previous -- * check in in-progress state got cancelled due to an incoming stun -- * request matching this same pair -+ * Remove a STUN transaction from a pair, and forget it -+ * from the related component stun agent. - * -- * @return will return TRUE if the pair is scheduled for recheck -+ * @pair the pair the stun transaction should be removed from. -+ * @stun the stun transaction to be removed. -+ * @component the component containing the stun agent used to -+ * forget the stun transaction. - */ --static gboolean --priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) -+static void -+priv_remove_stun_transaction (CandidateCheckPair *pair, -+ StunTransaction *stun, NiceComponent *component) - { -- if (p->recheck_on_timeout) { -- g_assert (p->state == NICE_CHECK_IN_PROGRESS); -- /* this cancelled pair may have the flag 'mark nominated -- * on response arrival' set, we want to keep it, because -- * this is needed to nominate this pair in aggressive -- * nomination, when the agent is in controlled mode. -- * -- * this cancelled pair may also have the flag 'use candidate -- * on next check' set, that we want to preserve too. -- */ -- nice_debug ("Agent %p : pair %p was cancelled, " -- "triggering a new connection check", agent, p); -- priv_add_pair_to_triggered_check_queue (agent, p); -- return TRUE; -- } -- return FALSE; -+ priv_forget_stun_transaction (stun, component); -+ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun); -+ priv_free_stun_transaction (stun); -+} -+ -+/* -+ * Remove all STUN transactions from a pair, and forget them -+ * from the related component stun agent. -+ * -+ * @pair the pair the stun list should be cleared. -+ * @component the component containing the stun agent used to -+ * forget the stun transactions. -+ */ -+static void -+priv_free_all_stun_transactions (CandidateCheckPair *pair, -+ NiceComponent *component) -+{ -+ if (component) -+ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component); -+ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction); -+ pair->stun_transactions = NULL; -+} -+ -+static void -+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) -+{ -+ NiceComponent *component; -+ -+ component = nice_stream_find_component_by_id (stream, p->component_id); -+ p->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ priv_free_all_stun_transactions (p, component); - } - - /* -@@ -667,7 +714,7 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) - { - gboolean keep_timer_going = FALSE; -- GSList *i; -+ GSList *i, *j; - CandidateCheckPair *pair; - unsigned int timeout; - GTimeVal now; -@@ -679,39 +726,59 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - CandidateCheckPair *p = i->data; - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; - NiceComponent *component; -+ StunTransaction *stun; -+ -+ if (p->stun_transactions == NULL) -+ continue; - - if (!agent_find_component (agent, p->stream_id, p->component_id, - NULL, &component)) - continue; - -- if (p->state != NICE_CHECK_IN_PROGRESS) -- continue; -+ /* The first stun transaction of the list may eventually be -+ * retransmitted, other stun transactions just have their -+ * timer updated. -+ */ - -- if (p->stun_message.buffer == NULL) { -- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -- continue; -+ j = p->stun_transactions->next; -+ -+ /* process all stun transactions except the first one */ -+ while (j) { -+ StunTransaction *s = j->data; -+ GSList *next = j->next; -+ -+ if (priv_timer_expired (&s->next_tick, &now)) -+ switch (stun_timer_refresh (&s->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: -+ priv_remove_stun_transaction (p, s, component); -+ break; -+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -+ timeout = stun_timer_remainder (&s->timer); -+ s->next_tick = now; -+ g_time_val_add (&s->next_tick, timeout * 1000); -+ break; -+ default: -+ break; -+ } -+ j = next; - } - -- if (!priv_timer_expired (&p->next_tick, &now)) -+ if (p->state != NICE_CHECK_IN_PROGRESS) - continue; - -- switch (stun_timer_refresh (&p->timer)) { -- case STUN_USAGE_TIMER_RETURN_TIMEOUT: --timer_timeout: -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ /* process the first stun transaction of the list */ -+ stun = p->stun_transactions->data; -+ if (!priv_timer_expired (&stun->next_tick, &now)) -+ continue; - -+ switch (stun_timer_refresh (&stun->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: -+timer_return_timeout: - /* case: error, abort processing */ - nice_address_to_string (&p->local->addr, tmpbuf1); - nice_address_to_string (&p->remote->addr, tmpbuf2); -- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on " -+ "connectivity check %p", agent, p); - nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, - tmpbuf1, nice_address_get_port (&p->local->addr), - tmpbuf2, nice_address_get_port (&p->remote->addr)); -@@ -732,42 +799,33 @@ timer_timeout: - * a pair with a higher priority than this in-progress pair, - * ICE spec, sect 8.1.2 "Updating States", item 2.2 - */ -- if (!p->retransmit_on_timeout) -- goto timer_timeout; -- -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ if (!p->retransmit) -+ goto timer_return_timeout; - - /* case: not ready, so schedule a new timeout */ -- timeout = stun_timer_remainder (&p->timer); -+ timeout = stun_timer_remainder (&stun->timer); - - nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " - "(timer=%d/%d %d/%dms).", - agent, p, -- p->timer.retransmissions, p->timer.max_retransmissions, -- p->timer.delay - timeout, p->timer.delay); -+ stun->timer.retransmissions, stun->timer.max_retransmissions, -+ stun->timer.delay - timeout, stun->timer.delay); - - agent_socket_send (p->sockptr, &p->remote->addr, -- stun_message_length (&p->stun_message), -- (gchar *)p->stun_buffer); -- -+ stun_message_length (&stun->message), -+ (gchar *)stun->buffer); - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); - - return TRUE; - case STUN_USAGE_TIMER_RETURN_SUCCESS: -- timeout = stun_timer_remainder (&p->timer); -+ timeout = stun_timer_remainder (&stun->timer); - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); - - keep_timer_going = TRUE; - break; -@@ -948,7 +1006,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - g_assert (p->state == NICE_CHECK_SUCCEEDED); - nice_debug ("Agent %p : restarting check %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); -- p->recheck_on_timeout = FALSE; - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); - keep_timer_going = TRUE; -@@ -972,7 +1029,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -- p->recheck_on_timeout = FALSE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); - keep_timer_going = TRUE; -@@ -2186,7 +2242,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - } - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); -- pair->retransmit_on_timeout = TRUE; - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -2381,8 +2436,7 @@ static void conn_check_free_item (gpointer data) - - if (pair->agent) - priv_remove_pair_from_triggered_check_queue (pair->agent, pair); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -+ priv_free_all_stun_transactions (pair, NULL); - g_slice_free (CandidateCheckPair, pair); - } - -@@ -2655,6 +2709,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - bool cand_use = controlling; - size_t buffer_len; - unsigned int timeout; -+ StunTransaction *stun; - - if (!agent_find_component (agent, pair->stream_id, pair->component_id, - &stream, &component)) -@@ -2718,13 +2773,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - - if (uname_len == 0) { - nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; - return -1; - } - -+ stun = priv_add_stun_transaction (pair); -+ - buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -+ &stun->message, stun->buffer, sizeof(stun->buffer), - uname, uname_len, password, password_len, - cand_use, controlling, pair->prflx_priority, - agent->tie_breaker, -@@ -2732,7 +2787,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - agent_to_ice_compatibility (agent)); - - nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -- pair->stun_message.buffer); -+ stun->message.buffer); - - if (agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007) { -@@ -2741,50 +2796,20 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - - if (buffer_len == 0) { - nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -+ priv_remove_stun_transaction (pair, stun, component); - return -1; - } - - if (nice_socket_is_reliable(pair->sockptr)) { - timeout = agent->stun_reliable_timeout; -- stun_timer_start_reliable(&pair->timer, timeout); -+ stun_timer_start_reliable(&stun->timer, timeout); - } else { -- StunTimer *timer = &pair->timer; -- -- if (pair->recheck_on_timeout) { -- GTimeVal now; -- /* The pair recheck on timeout can easily cause repetitive rechecks in -- * a ping-pong effect, if both peers with the same behaviour try to -- * check the same pair almost simultaneously, and if the network rtt -- * is greater than the initial timer rto. The reply to the initial -- * stun request may arrive after the in-progress conncheck -- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -- * creates a new stun request, and forgets the initial one. -- * The conncheck timer is restarted with the same initial value, -- * so the same situation happens again later. -- * -- * We choose to avoid resetting the timer in such situation. -- * After enough retransmissions, the timeout delay becomes -- * longer than the rtt, and the stun reply can be handled. -- */ -- -- g_get_current_time (&now); -- timeout = priv_timer_remainder (&pair->next_tick, &now); -- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -- agent, pair, -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - timeout, -- timer->delay); -- } else { -- timeout = priv_compute_conncheck_timer (agent, stream); -- stun_timer_start (timer, timeout, agent->stun_max_retransmissions); -- } -- pair->recheck_on_timeout = FALSE; -+ timeout = priv_compute_conncheck_timer (agent, stream); -+ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); - } - -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -+ g_get_current_time (&stun->next_tick); -+ g_time_val_add (&stun->next_tick, timeout * 1000); - - /* TCP-ACTIVE candidate must create a new socket before sending - * by connecting to the peer. The new socket is stored in the candidate -@@ -2814,10 +2839,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - } - /* send the conncheck */ - agent_socket_send (pair->sockptr, &pair->remote->addr, -- buffer_len, (gchar *)pair->stun_buffer); -+ buffer_len, (gchar *)stun->buffer); - - if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) -- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -+ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, - &pair->remote->addr); - - return 0; -@@ -2881,8 +2906,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - (p->state == NICE_CHECK_WAITING && like_in_progress)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { -- p->retransmit_on_timeout = FALSE; -- p->recheck_on_timeout = FALSE; -+ p->retransmit = FALSE; - nice_debug ("Agent %p : pair %p will not be retransmitted.", - agent, p); - } else { -@@ -2938,9 +2962,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", - agent, p, p->foundation, priv_state_to_string (p->state)); - -- switch (p->state) { -- case NICE_CHECK_WAITING: -- case NICE_CHECK_FROZEN: -+ switch (p->state) { -+ case NICE_CHECK_WAITING: -+ case NICE_CHECK_FROZEN: - nice_debug ("Agent %p : pair %p added for a triggered check.", - agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -@@ -2952,19 +2976,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * for that pair. The controlling role of this new check may - * be different from the role of this cancelled check. - * -- * note: the flag retransmit_on_timeout unset means that -+ * note: the flag retransmit unset means that - * another pair, with a higher priority is already nominated, - * so there's no reason to recheck this pair, since it can in - * no way replace the nominated one. - */ -- if (!nice_socket_is_reliable (p->sockptr) && -- p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p will be rechecked " -- "on stun timer timeout.", agent, p); -- /* this flag will determine the action at the retransmission -- * timeout of the stun timer -+ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ } -+ break; -+ case NICE_CHECK_FAILED: -+ if (p->retransmit) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers - */ -- p->recheck_on_timeout = TRUE; -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } - } - break; - case NICE_CHECK_SUCCEEDED: -@@ -2978,32 +3013,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - */ - priv_update_check_list_state_for_ready (agent, stream, component); - break; -- case NICE_CHECK_FAILED: -- /* 7.2.1.4 Triggered Checks -- * If the state of the pair is Failed, it is changed to Waiting -- * and the agent MUST create a new connectivity check for that -- * pair (representing a new STUN Binding request transaction), by -- * enqueueing the pair in the triggered check queue. -- * -- * note: the flag retransmit_on_timeout unset means that -- * another pair, with a higher priority is already nominated, -- * we apply the same strategy than with an in-progress pair -- * above. -- */ -- if (p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p added for a triggered check.", -- agent, p); -- priv_add_pair_to_triggered_check_queue (agent, p); -- /* If the component for this pair is in failed state, move it -- * back to connecting, and reinitiate the timers -- */ -- if (component->state == NICE_COMPONENT_STATE_FAILED) { -- agent_signal_component_state_change (agent, stream->id, -- component->id, NICE_COMPONENT_STATE_CONNECTING); -- conn_check_schedule_next (agent); -- } -- } -- break; - default: - break; - } -@@ -3260,16 +3269,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (new_pair == p) - p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; -- /* note: we cancel the potential in-progress transaction -- * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if -- * we receive a valid reply before transmission timeout... -- */ -- p->recheck_on_timeout = FALSE; -- /* ... or just after the transmission timeout, while the pair is -- * temporarily put on the triggered check list on the way to be -- * be rechecked: we remove it from the list too. -- */ - priv_remove_pair_from_triggered_check_queue (agent, p); -+ priv_free_all_stun_transactions (p, component); - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - nice_component_add_valid_candidate (component, remote_candidate); - } -@@ -3304,8 +3305,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ - p->state = NICE_CHECK_SUCCEEDED; -- p->recheck_on_timeout = FALSE; - priv_remove_pair_from_triggered_check_queue (agent, p); -+ priv_free_all_stun_transactions (p, component); - nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", - agent, p, new_pair); - } -@@ -3331,7 +3332,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - struct sockaddr addr; - } sockaddr; - socklen_t socklen = sizeof (sockaddr); -- GSList *i; -+ GSList *i, *j; -+ guint k; - StunUsageIceReturn res; - StunTransactionId discovery_id; - StunTransactionId response_id; -@@ -3340,193 +3342,186 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -- if (p->stun_message.buffer == NULL) -- continue; -- -- stun_message_id (&p->stun_message, discovery_id); -- -- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -- continue; -- -- res = stun_usage_ice_conncheck_process (resp, -- &sockaddr.storage, &socklen, -- agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -- "%s,res=%s.", -- agent, p, -- agent->controlling_mode ? "controlling" : "controlled", -- priv_ice_return_to_string (res)); -- -- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* case: found a matching connectivity check request */ -- -- CandidateCheckPair *ok_pair = NULL; -- -- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- -- /* step: verify that response came from the same IP address we -- * sent the original request to (see 7.1.2.1. "Failure -- * Cases") */ -- if (nice_address_equal (from, &p->remote->addr) == FALSE) { -- -- p->state = NICE_CHECK_FAILED; -- if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- gchar tmpbuf2[INET6_ADDRSTRLEN]; -- nice_debug ("Agent %p : conncheck %p FAILED" -- " (mismatch of source address).", agent, p); -- nice_address_to_string (&p->remote->addr, tmpbuf); -- nice_address_to_string (from, tmpbuf2); -- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -- tmpbuf, nice_address_get_port (&p->remote->addr), -- tmpbuf2, nice_address_get_port (from)); -- } -- return TRUE; -- } -- -- /* note: CONNECTED but not yet READY, see docs */ -- -- /* step: handle the possible case of a peer-reflexive -- * candidate where the mapped-address in response does -- * not match any local candidate, see 7.1.2.2.1 -- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -- -- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- p->state = NICE_CHECK_SUCCEEDED; -- p->valid = TRUE; -- nice_debug ("Agent %p : Mapped address not found." -- " conncheck %p SUCCEEDED.", agent, p); -- nice_component_add_valid_candidate (component, p->remote); -- } else -- ok_pair = priv_process_response_check_for_reflexive (agent, -- stream, component, p, sockptr, &sockaddr.addr, -- local_candidate, remote_candidate); -- -- /* note: The success of this check might also -- * cause the state of other checks to change as well, ICE -- * spec 7.1.3.2.3 -- */ -- priv_conn_check_unfreeze_related (agent, stream, p); -- -- /* Note: this assignment helps to reduce the numbers of cases -- * to be tested. If ok_pair and p refer to distinct pairs, it -- * means that ok_pair is a discovered peer reflexive one, -- * caused by the check made on pair p. In that case, the -- * flags to be tested are on p, but the nominated flag will be -- * set on ok_pair. When there's no discovered pair, p and -- * ok_pair refer to the same pair. -- * To summarize : p is a SUCCEEDED pair, ok_pair is a -- * DISCOVERED, VALID, and eventually NOMINATED pair. -- */ -- if (!ok_pair) -- ok_pair = p; -- -- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -- Nominated Flag" (ID-19) */ -- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -- nice_debug ("Agent %p : Updating nominated flag (%s): " -- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -- "UDP" : "TCP", -- ok_pair, ok_pair->use_candidate_on_next_check, -- ok_pair->mark_nominated_on_response_arrival, -- p, p->use_candidate_on_next_check, -- p->mark_nominated_on_response_arrival); -- -- if (agent->controlling_mode) { -- switch (agent->nomination_mode) { -- case NICE_NOMINATION_MODE_REGULAR: -- if (p->use_candidate_on_next_check) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(regular nomination, controlling, " -- "use_cand_on_next_check=1).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- case NICE_NOMINATION_MODE_AGGRESSIVE: -- if (!p->nominated) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(aggressive nomination, controlling).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- default: -- /* Nothing to do */ -- break; -- } -- } else { -- if (p->mark_nominated_on_response_arrival) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(%s nomination, controlled, mark_on_response=1).", -- agent, ok_pair, ok_pair->foundation, -- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -- "aggressive" : "regular"); -- ok_pair->nominated = TRUE; -- } -- } -- } -- -- if (ok_pair->nominated == TRUE) { -- priv_update_selected_pair (agent, component, ok_pair); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a nominated pair"); -+ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) { -+ StunTransaction *stun = j->data; -+ -+ stun_message_id (&stun->message, discovery_id); -+ -+ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -+ continue; -+ -+ res = stun_usage_ice_conncheck_process (resp, -+ &sockaddr.storage, &socklen, -+ agent_to_ice_compatibility (agent)); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -+ "%s,res=%s,stun#=%d.", -+ agent, p, -+ agent->controlling_mode ? "controlling" : "controlled", -+ priv_ice_return_to_string (res), k); -+ -+ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -+ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ /* case: found a matching connectivity check request */ -+ -+ CandidateCheckPair *ok_pair = NULL; -+ -+ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -+ priv_remove_stun_transaction (p, stun, component); -+ -+ /* step: verify that response came from the same IP address we -+ * sent the original request to (see 7.1.2.1. "Failure -+ * Cases") */ -+ if (nice_address_equal (from, &p->remote->addr) == FALSE) { -+ candidate_check_pair_fail (stream, agent, p); -+ if (nice_debug_is_enabled ()) { -+ gchar tmpbuf[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_debug ("Agent %p : conncheck %p FAILED" -+ " (mismatch of source address).", agent, p); -+ nice_address_to_string (&p->remote->addr, tmpbuf); -+ nice_address_to_string (from, tmpbuf2); -+ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -+ tmpbuf, nice_address_get_port (&p->remote->addr), -+ tmpbuf2, nice_address_get_port (from)); -+ } -+ return TRUE; -+ } - -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -+ /* note: CONNECTED but not yet READY, see docs */ -+ -+ /* step: handle the possible case of a peer-reflexive -+ * candidate where the mapped-address in response does -+ * not match any local candidate, see 7.1.2.2.1 -+ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -+ -+ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ p->state = NICE_CHECK_SUCCEEDED; -+ p->valid = TRUE; -+ nice_debug ("Agent %p : Mapped address not found." -+ " conncheck %p SUCCEEDED.", agent, p); -+ nice_component_add_valid_candidate (component, p->remote); -+ } else -+ ok_pair = priv_process_response_check_for_reflexive (agent, -+ stream, component, p, sockptr, &sockaddr.addr, -+ local_candidate, remote_candidate); -+ -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ -+ if (!ok_pair) -+ ok_pair = p; -+ -+ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -+ Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, controlling, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(aggressive nomination, controlling).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ default: -+ /* Nothing to do */ -+ break; -+ } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, controlled, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; -+ } -+ } -+ } - -- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -- states" and 8.1.2 "Updating States", ID-19) */ -- priv_update_check_list_state_for_ready (agent, stream, component); -- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -- /* case: role conflict error, need to restart with new role */ -- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- -- /* note: this res value indicates that the role of the peer -- * agent has not changed after the tie-breaker comparison, so -- * this is our role that must change. see ICE sect. 7.1.3.1 -- * "Failure Cases". Our role might already have changed due to -- * an earlier incoming request, but if not, change role now. -- * -- * Sect. 7.1.3.1 is not clear on this point, but we choose to -- * put the candidate pair in the triggered check list even -- * when the agent did not switch its role. The reason for this -- * interpretation is that the reception of the stun reply, even -- * an error reply, is a good sign that this pair will be -- * valid, if we retry the check after the role of both peers -- * has been fixed. -- */ -+ if (ok_pair->nominated == TRUE) { -+ priv_update_selected_pair (agent, component, ok_pair); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a nominated pair"); -+ -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } - -- if (p->stun_message.buffer != NULL) { -+ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -+ states" and 8.1.2 "Updating States", ID-19) */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { - guint64 tie; - gboolean controlled_mode; - -- controlled_mode = (stun_message_find64 (&p->stun_message, -+ /* case: role conflict error, need to restart with new role */ -+ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -+ -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ -+ controlled_mode = (stun_message_find64 (&stun->message, - STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == - STUN_MESSAGE_RETURN_SUCCESS); - - priv_check_for_role_conflict (agent, controlled_mode); -- -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+ priv_remove_stun_transaction (p, stun, component); - priv_add_pair_to_triggered_check_queue (agent, p); -+ } else { -+ /* case: STUN error, the check STUN context was freed */ -+ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -+ candidate_check_pair_fail (stream, agent, p); - } -- } else { -- /* case: STUN error, the check STUN context was freed */ -- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+ return TRUE; - } -- return TRUE; - } - - return FALSE; -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 909d469..e16dc67 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -71,6 +71,15 @@ typedef enum - } NiceCheckState; - - typedef struct _CandidateCheckPair CandidateCheckPair; -+typedef struct _StunTransaction StunTransaction; -+ -+struct _StunTransaction -+{ -+ GTimeVal next_tick; /* next tick timestamp */ -+ StunTimer timer; -+ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; -+ StunMessage message; -+}; - - struct _CandidateCheckPair - { -@@ -86,16 +95,12 @@ struct _CandidateCheckPair - gboolean valid; - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; -- gboolean recheck_on_timeout; -- gboolean retransmit_on_timeout; -- struct _CandidateCheckPair *discovered_pair; -- struct _CandidateCheckPair *succeeded_pair; -+ gboolean retransmit; /* if the first stun request must be retransmitted */ -+ CandidateCheckPair *discovered_pair; -+ CandidateCheckPair *succeeded_pair; - guint64 priority; - guint32 prflx_priority; -- GTimeVal next_tick; /* next tick timestamp */ -- StunTimer timer; -- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; -- StunMessage stun_message; -+ GSList *stun_transactions; /* a list of ongoing stun requests */ - }; - - int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); --- -2.13.6 - - -From 6fe64fdbc53ab87dffd79972f492665cff14c0a0 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 27 Jun 2017 11:01:14 +0200 -Subject: [PATCH 59/70] conncheck: update the state of triggered checks pairs - -With this patch, we fix an ambiguity of some parts of the spec, when -the document refers to in-progress pairs, that also concern pairs in -the triggered checks list. - -The first cast is in section 7.1.2.5, "Updating the Nominated Flag", -when the in-progress pair will be nominated on response arrival. This is -handled in function priv_mark_pair_nominated(), when a pair is put to -the triggered check list in reaction to a matching inbound stun request. -Such a pair in priv_mark_pair_nominated() will _always_ be in the -triggered check list, from the previously called function -priv_schedule_triggered_check(). - -The second case is in section 8.1.2, "Updating State" when an in-progress -pair stops its retransmission when another pair of higher priority is -already nominated. This is handled by function priv_prune_pending_checks(). - -Until now, pairs enqueued in the triggered check list move transiently -to state waiting, according to 7.2.1.4. But this state causes wrong -decisions in the two previous cases, because such pairs should in fact -rather be considered "like in-progress", to avoid discarding them -inadvertantly. - -This patch update the state of the triggered check list -pairs to in-progress. It allows to remove exception handling cited -above: the code is a bit more simple, and allows some refactoring -in priv_mark_pair_nominated() between RFC and compatibility modes. - -Differential Revision: https://phabricator.freedesktop.org/D1762 ---- - agent/conncheck.c | 96 +++++++++++++++---------------------------------------- - 1 file changed, 25 insertions(+), 71 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 2a85738..628c708 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -244,16 +244,6 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - } - } - --/* Verify if the pair is in the triggered checks list -- */ -- --static gboolean --priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) --{ -- g_assert (pair); -- return (g_slist_find (agent->triggered_check_queue, pair) != NULL); --} -- - /* Add the pair to the triggered checks list, if not already present - */ - static void -@@ -261,8 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa - { - g_assert (pair); - -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ pair->state = NICE_CHECK_IN_PROGRESS; -+ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); - if (agent->triggered_check_queue == NULL || - g_slist_find (agent->triggered_check_queue, pair) == NULL) - agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -@@ -841,12 +831,6 @@ timer_return_timeout: - */ - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) { -- /* remove the pair from the triggered check list if needed. This -- * may happen with the cancelled pair, that's just been added -- * in state waiting to the triggered check list above in the -- * same function. -- */ -- priv_remove_pair_from_triggered_check_queue (agent, pair); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair in Waiting state"); - priv_conn_check_initiate (agent, pair); -@@ -1109,7 +1093,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - if (pair) { - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair from triggered check list"); -- priv_conn_check_initiate (agent, pair); -+ conn_check_send (agent, pair); - return TRUE; - } - -@@ -2098,8 +2082,7 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; -- if (pair->local == localcand && pair->remote == remotecand && -- NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ if (pair->local == localcand && pair->remote == remotecand) { - /* ICE, 7.2.1.5. Updating the Nominated Flag */ - /* note: TCP candidates typically produce peer reflexive - * candidate, generating a "discovered" pair that can be -@@ -2111,44 +2094,27 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - g_assert (pair->state == NICE_CHECK_DISCOVERED); - } - -- if (pair->valid) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated", -- agent, pair, pair->foundation); -- pair->nominated = TRUE; -- priv_update_selected_pair (agent, component, pair); -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state == NICE_COMPONENT_STATE_FAILED) -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -- if (component->state == NICE_COMPONENT_STATE_CONNECTING) -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- priv_update_check_list_state_for_ready (agent, stream, component); -- } else if (pair->state == NICE_CHECK_IN_PROGRESS) { -+ /* If the state of this pair is In-Progress, [...] the resulting -+ * valid pair has its nominated flag set when the response -+ * arrives. -+ */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && -+ pair->state == NICE_CHECK_IN_PROGRESS) { - pair->mark_nominated_on_response_arrival = TRUE; - nice_debug ("Agent %p : pair %p (%s) is in-progress, " - "will be nominated on response receipt.", - agent, pair, pair->foundation); - } -- /* note: this case is not covered by the ICE spec, 7.2.1.5, -- * "Updating the Nominated Flag", but a pair in waiting state -- * deserves the same treatment than a pair in-progress. A pair -- * can be in waiting state as the result of being enqueued in -- * the triggered check list for example. -- */ -- if (pair->state == NICE_CHECK_WAITING) { -- pair->mark_nominated_on_response_arrival = TRUE; -- nice_debug ("Agent %p : pair %p (%s) is waiting, " -- "will be nominated on response receipt.", -+ -+ if (pair->valid || -+ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated", - agent, pair, pair->foundation); -+ pair->nominated = TRUE; - } -- } else if (pair->local == localcand && pair->remote == remotecand) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); -- pair->nominated = TRUE; -+ - if (pair->valid) { -- priv_update_selected_pair (agent, component, pair); -+ priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ - if (component->state == NICE_COMPONENT_STATE_FAILED) - agent_signal_component_state_change (agent, -@@ -2159,7 +2125,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); - } -- priv_update_check_list_state_for_ready (agent, stream, component); -+ -+ if (pair->nominated) -+ priv_update_check_list_state_for_ready (agent, stream, component); - } - } - } -@@ -2731,12 +2699,12 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - nice_address_to_string (&pair->local->addr, tmpbuf1); - nice_address_to_string (&pair->remote->addr, tmpbuf2); - nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " -- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -+ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " - "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr), - pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, -- pair->foundation, pair->component_id, -+ pair, pair->component_id, - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -@@ -2877,35 +2845,21 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ -- /* note: this case is not covered by the ICE spec, 8.1.2 -- * "Updating States", but a pair in waiting state which will be -- * nominated on response receipt should be treated the same way -- * that an in-progress pair. A pair in waiting state and in -- * the triggered check list should also be treated like an in-progress -- * pair. -- */ - i = stream->conncheck_list; - while (i) { - CandidateCheckPair *p = i->data; - GSList *next = i->next; - - if (p->component_id == component_id) { -- gboolean like_in_progress = -- p->mark_nominated_on_response_arrival || -- priv_is_pair_in_triggered_check_queue (agent, p); -- -- if (p->state == NICE_CHECK_FROZEN || -- (p->state == NICE_CHECK_WAITING && !like_in_progress)) { -+ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) { - nice_debug ("Agent %p : pair %p removed.", agent, p); - conn_check_free_item (p); - stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- else if (p->state == NICE_CHECK_IN_PROGRESS || -- (p->state == NICE_CHECK_WAITING && like_in_progress)) { -- if (highest_nominated_priority != 0 && -- p->priority < highest_nominated_priority) { -+ else if (p->state == NICE_CHECK_IN_PROGRESS) { -+ if (p->priority < highest_nominated_priority) { - p->retransmit = FALSE; - nice_debug ("Agent %p : pair %p will not be retransmitted.", - agent, p); --- -2.13.6 - - -From 72dd26a3368d3506fe8faca7067a02784fb5f0fd Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Wed, 28 Jun 2017 12:06:48 +0200 -Subject: [PATCH 60/70] conncheck: forgot to put a pair in triggered check list - -When a new pair is created from an unknown remote candidate, it -should be enqueue for a triggered check, to allow it to be marked -as nominated on response arrival in priv_mark_pair_nominated(). -Creating it in waiting state is not sufficient since the update -in priv_mark_pair_nominated() from previous commits. - -Differential Revision: https://phabricator.freedesktop.org/D1763 ---- - agent/conncheck.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 628c708..0e3ce88 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2893,11 +2893,12 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - { - GSList *i; - NiceCandidate *local = NULL; -+ CandidateCheckPair *p; - - g_assert (remote_cand != NULL); - - for (i = stream->conncheck_list; i ; i = i->next) { -- CandidateCheckPair *p = i->data; -+ p = i->data; - if (p->component_id == component->id && - p->remote == remote_cand && - ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && -@@ -2986,8 +2987,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - - if (i) { - nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); -- priv_add_new_check_pair (agent, stream->id, component, -+ p = priv_add_new_check_pair (agent, stream->id, component, - local, remote_cand, NICE_CHECK_WAITING); -+ priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } - else { --- -2.13.6 - - -From 14102d44449d2eb4148588ce54fa897fa13b87ad Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 2 Jul 2017 16:02:09 +0200 -Subject: [PATCH 61/70] conncheck: change state before updating nominated pairs - -When a pair is nominated while in state failed, we first move -back to state connecting, then we update the selected pair, and -finally we move to state connected. ---- - agent/conncheck.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 0e3ce88..e584c0e 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2114,11 +2114,11 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - } - - if (pair->valid) { -- priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ - if (component->state == NICE_COMPONENT_STATE_FAILED) - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ priv_update_selected_pair (agent, component, pair); - if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ --- -2.13.6 - - -From 1a1803a45778000720c93d91060cedeb19124a27 Mon Sep 17 00:00:00 2001 -From: Philip Withnall -Date: Tue, 12 Sep 2017 13:23:31 +0100 -Subject: [PATCH 62/70] tests: Fix copyright dates in test-gstreamer.c - -This code is not 1000 years old. - -Signed-off-by: Philip Withnall ---- - tests/test-gstreamer.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c -index 74d7133..a0be68e 100644 ---- a/tests/test-gstreamer.c -+++ b/tests/test-gstreamer.c -@@ -1,7 +1,7 @@ - /* - * This file is part of the Nice GLib ICE library. - * -- * (C) 1015 Kurento. -+ * (C) 2015 Kurento. - * Contact: Jose Antonio Santos Cadenas - * - * The contents of this file are subject to the Mozilla Public License Version --- -2.13.6 - - -From 4c4834ab634f735145c8f758a22cbdd9cab79bac Mon Sep 17 00:00:00 2001 -From: Philip Withnall -Date: Tue, 12 Sep 2017 13:23:53 +0100 -Subject: [PATCH 63/70] tests: Fix agent.h header inclusion in test-gstreamer.c - -Spotted by Lukas Gradl on the mailing list. - -Signed-off-by: Philip Withnall ---- - tests/test-gstreamer.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c -index a0be68e..f060efc 100644 ---- a/tests/test-gstreamer.c -+++ b/tests/test-gstreamer.c -@@ -34,7 +34,7 @@ - */ - - #include --#include -+#include "agent.h" - - #define RTP_HEADER_SIZE 12 - #define RTP_PAYLOAD_SIZE 1024 --- -2.13.6 - - -From fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 Mon Sep 17 00:00:00 2001 -From: Philip Withnall -Date: Thu, 3 Aug 2017 12:20:32 +0100 -Subject: [PATCH 64/70] stun: Fix FD leak in test/utility code -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -https://phabricator.freedesktop.org/T7798 - -Signed-off-by: Philip Withnall -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1819 ---- - stun/usages/bind.c | 29 ++++++++++++++++++++++------- - 1 file changed, 22 insertions(+), 7 deletions(-) - -diff --git a/stun/usages/bind.c b/stun/usages/bind.c -index d56790f..ee600a0 100644 ---- a/stun/usages/bind.c -+++ b/stun/usages/bind.c -@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen); - if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { - stun_debug ("STUN transaction failed: couldn't create transport."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) { - stun_debug ("STUN transaction failed: couldn't send request."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, -@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - switch (stun_timer_refresh (&timer)) { - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - stun_debug ("STUN transaction failed: time out."); -- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! -+ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! -+ goto done; - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: - stun_debug ("STUN transaction retransmitted (timeout %dms).", - stun_timer_remainder (&timer)); - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) { - stun_debug ("STUN transaction failed: couldn't resend request."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - continue; - case STUN_USAGE_TIMER_RETURN_SUCCESS: -@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - - valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL); - if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE) -- return STUN_USAGE_BIND_RETURN_ERROR; -+ { -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; -+ } - - if (valid != STUN_VALIDATION_SUCCESS) { - ret = STUN_USAGE_TRANS_RETURN_RETRY; -@@ -554,12 +561,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - (struct sockaddr *) &alternate_server, alternate_server_len); - - if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) -- return STUN_USAGE_BIND_RETURN_ERROR; -+ { -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; -+ } - - stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -@@ -573,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - } - while (ret == STUN_USAGE_TRANS_RETURN_RETRY); - -+done: -+ if (trans.fd != -1) -+ stun_trans_deinit (&trans); -+ - return bind_ret; - } --- -2.13.6 - - -From 02216a6766caccb652387d5ee19686149eedbc93 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Tue, 21 Nov 2017 15:12:45 +0100 -Subject: [PATCH 65/70] agent: prevent external role change while conncheck is - running - -With this patch, we stash the controlling mode property change, and -apply it safely, when it won't interfere with an ongoing conncheck -running. According to RFC5245, sect 5.2. "Determining Role", the role -is determined for a session, and persists unless an ICE is restarted. - -Differential Revision: https://phabricator.freedesktop.org/D1887 ---- - agent/agent-priv.h | 4 +++- - agent/agent.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- - tests/test-restart.c | 15 ++++++++++++++ - 3 files changed, 74 insertions(+), 3 deletions(-) - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 714ecff..7269be0 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -146,7 +146,7 @@ struct _NiceAgent - NiceProxyType proxy_type; /* property: Proxy type */ - gchar *proxy_username; /* property: Proxy username */ - gchar *proxy_password; /* property: Proxy password */ -- gboolean controlling_mode; /* property: controlling-mode */ -+ gboolean saved_controlling_mode;/* property: controlling-mode */ - guint timer_ta; /* property: timer Ta */ - guint max_conn_checks; /* property: max connectivity checks */ - gboolean force_relay; /* property: force relay */ -@@ -190,6 +190,8 @@ struct _NiceAgent - gboolean use_ice_tcp; - - guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ -+ gboolean controlling_mode; /* controlling mode used by the -+ conncheck */ - /* XXX: add pointer to internal data struct for ABI-safe extensions */ - }; - -diff --git a/agent/agent.c b/agent/agent.c -index a4dcc0c..0773c53 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -405,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass) - 1, /* not a construct property, ignored */ - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:controlling-mode: -+ * -+ * Whether the agent has the controlling role. This property should -+ * be modified before gathering candidates, any modification occuring -+ * later will be hold until ICE is restarted. -+ */ - g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE, - g_param_spec_boolean ( - "controlling-mode", -@@ -1107,6 +1114,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) - } - - static void -+priv_update_controlling_mode (NiceAgent *agent, gboolean value) -+{ -+ gboolean update_controlling_mode; -+ GSList *i, *j; -+ -+ agent->saved_controlling_mode = value; -+ /* It is safe to update the agent controlling mode when all -+ * components are still in state disconnected. When we leave -+ * this state, the role must stay under the control of the -+ * conncheck algorithm exclusively, until the conncheck is -+ * eventually restarted. See RFC5245, sect 5.2. Determining Role -+ */ -+ if (agent->controlling_mode != agent->saved_controlling_mode) { -+ update_controlling_mode = TRUE; -+ for (i = agent->streams; -+ i && update_controlling_mode; i = i->next) { -+ NiceStream *stream = i->data; -+ for (j = stream->components; -+ j && update_controlling_mode; j = j->next) { -+ NiceComponent *component = j->data; -+ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED) -+ update_controlling_mode = FALSE; -+ } -+ } -+ if (update_controlling_mode) { -+ agent->controlling_mode = agent->saved_controlling_mode; -+ nice_debug ("Agent %p : Property set, changing role to \"%s\".", -+ agent, agent->controlling_mode ? "controlling" : "controlled"); -+ } else { -+ nice_debug ("Agent %p : Property set, role switch requested " -+ "but conncheck already started.", agent); -+ nice_debug ("Agent %p : Property set, staying with role \"%s\" " -+ "until restart.", agent, -+ agent->controlling_mode ? "controlling" : "controlled"); -+ } -+ } else -+ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent, -+ agent->controlling_mode ? "controlling" : "controlled"); -+} -+ -+static void - nice_agent_init (NiceAgent *agent) - { - agent->next_candidate_id = 1; -@@ -1115,6 +1163,7 @@ nice_agent_init (NiceAgent *agent) - /* set defaults; not construct params, so set here */ - agent->stun_server_port = DEFAULT_STUN_PORT; - agent->controlling_mode = TRUE; -+ agent->saved_controlling_mode = TRUE; - agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; - agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; - -@@ -1213,7 +1262,7 @@ nice_agent_get_property ( - break; - - case PROP_CONTROLLING_MODE: -- g_value_set_boolean (value, agent->controlling_mode); -+ g_value_set_boolean (value, agent->saved_controlling_mode); - break; - - case PROP_FULL_MODE: -@@ -1422,7 +1471,7 @@ nice_agent_set_property ( - break; - - case PROP_CONTROLLING_MODE: -- agent->controlling_mode = g_value_get_boolean (value); -+ priv_update_controlling_mode (agent, g_value_get_boolean (value)); - break; - - case PROP_FULL_MODE: -@@ -4930,6 +4979,11 @@ nice_agent_restart ( - /* step: regenerate tie-breaker value */ - priv_generate_tie_breaker (agent); - -+ /* step: reset controlling mode from the property value */ -+ agent->controlling_mode = agent->saved_controlling_mode; -+ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".", -+ agent, agent->controlling_mode ? "controlling" : "controlled"); -+ - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; - -diff --git a/tests/test-restart.c b/tests/test-restart.c -index c2cbe9a..afc51b6 100644 ---- a/tests/test-restart.c -+++ b/tests/test-restart.c -@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands); - cdes.addr = laddr_rtcp; - nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands); -+ /* This role switch request will be effective after restart. We test -+ * here that the role cannot be externally modified after conncheck -+ * has started. */ -+ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL); -+ g_assert (ragent->controlling_mode == FALSE); - - g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed..."); - -@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - global_ragent_read = 0; - g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16); - -+ /* Both agent have a distinct role at the end of the conncheck */ -+ g_assert (lagent->controlling_mode == TRUE); -+ g_assert (ragent->controlling_mode == FALSE); - /* step: restart agents, exchange updated credentials */ - tie_breaker = ragent->tie_breaker; - nice_agent_restart (ragent); - g_assert (tie_breaker != ragent->tie_breaker); -+ /* This role switch of ragent should be done now, and both agents -+ * have now the same role, which should generate a role conflict -+ * resolution situation */ -+ g_assert (lagent->controlling_mode == TRUE); -+ g_assert (ragent->controlling_mode == TRUE); - nice_agent_restart (lagent); - { - gchar *ufrag = NULL, *password = NULL; -@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - /* note: verify binding requests were resent after restart */ - g_assert (global_lagent_ibr_received == TRUE); - g_assert (global_ragent_ibr_received == TRUE); -+ /* note: verify that a role switch occured for one of the agents */ -+ g_assert (ragent->controlling_mode != lagent->controlling_mode); - - g_debug ("test-restart: Ran mainloop, removing streams..."); - --- -2.13.6 - - -From c63349894b3fe974494453a883dfb5ad05df5a46 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Thu, 23 Nov 2017 18:31:31 +0100 -Subject: [PATCH 66/70] Makefile: really enable debug for tests - -Differential Revision: https://phabricator.freedesktop.org/D1888 ---- - tests/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index b623764..e94822d 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" - - AM_TESTS_ENVIRONMENT = \ - G_MESSAGES_DEBUG=all \ -- NICE_DEBUG=all; -+ NICE_DEBUG=all - - COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) - --- -2.13.6 - - -From 17f30e4465efe9533799b02d6f95feeaf0f2748c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miguel=20Par=C3=ADs?= -Date: Wed, 8 Nov 2017 16:26:47 +0000 -Subject: [PATCH 67/70] conncheck: do not require that all streams have a - connection check list - -One or more streams might not have any connection check list if the -number of streams differs from the peer agent. -Differential Revision: https://phabricator.freedesktop.org/D1880 ---- - agent/conncheck.c | 23 ---- - tests/Makefile.am | 3 + - tests/test-different-number-streams.c | 208 ++++++++++++++++++++++++++++++++++ - 3 files changed, 211 insertions(+), 23 deletions(-) - create mode 100644 tests/test-different-number-streams.c - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index e584c0e..beb43c3 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -402,23 +402,6 @@ priv_conn_check_find_next_frozen (GSList *conn_check_list) - } - - /* -- * Returns the number of check lists of the agent -- */ --static guint --priv_number_of_check_lists (NiceAgent *agent) --{ -- guint n = 0; -- GSList *i; -- -- for (i = agent->streams; i ; i = i->next) { -- NiceStream *stream = i->data; -- if (stream->conncheck_list != NULL) -- n++; -- } -- return n; --} -- --/* - * Returns the number of active check lists of the agent - */ - static guint -@@ -1060,12 +1043,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - gboolean keep_timer_going = FALSE; - GSList *i, *j; - -- /* the conncheck really starts when we have built -- * a connection check list for each stream -- */ -- if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) -- return TRUE; -- - /* configure the initial state of the check lists of the agent - * as described in ICE spec, 5.7.4 - * -diff --git a/tests/Makefile.am b/tests/Makefile.am -index e94822d..30d6f8e 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -46,6 +46,7 @@ check_PROGRAMS = \ - test-socket-is-based-on \ - test-priority \ - test-fullmode \ -+ test-different-number-streams \ - test-restart \ - test-fallback \ - test-thread \ -@@ -114,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD) - - test_fullmode_LDADD = $(COMMON_LDADD) - -+test_different_number_streams_LDADD = $(COMMON_LDADD) -+ - test_restart_LDADD = $(COMMON_LDADD) - - test_fallback_LDADD = $(COMMON_LDADD) -diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c -new file mode 100644 -index 0000000..7fd4763 ---- /dev/null -+++ b/tests/test-different-number-streams.c -@@ -0,0 +1,208 @@ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include "agent.h" -+ -+#include -+#include -+ -+#define ADD_2_STREAMS TRUE -+#define USE_SECOND_STREAM TRUE -+ -+static GMainLoop *global_mainloop = NULL; -+ -+static guint global_components_ready = 0; -+static guint global_components_ready_exit = 0; -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id); -+} -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)", -+ agent, stream_id, component_id, nice_component_state_to_string (state)); -+ -+ if (state == NICE_COMPONENT_STATE_READY) { -+ global_components_ready++; -+ } -+ -+ /* signal status via a global variable */ -+ if (global_components_ready == global_components_ready_exit) { -+ g_debug ("Components ready/failed achieved. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id); -+} -+ -+int main (void) -+{ -+ NiceAgent *lagent, *ragent; -+ guint timer_id; -+ guint ls_id, rs_id_1, rs_id_2; -+ gchar *lufrag = NULL, *lpassword = NULL; -+ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL; -+ -+#ifdef G_OS_WIN32 -+ WSADATA w; -+ -+ WSAStartup(0x0202, &w); -+#endif -+ -+ global_mainloop = g_main_loop_new (NULL, FALSE); -+ -+ /* step: create the agents L and R */ -+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_GOOGLE); -+ g_debug ("lagent: %p", lagent); -+ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent"); -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), NULL); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), NULL); -+ -+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_GOOGLE); -+ g_debug ("ragent: %p", ragent); -+ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent"); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), NULL); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), NULL); -+ -+ /* step: add a timer to catch state changes triggered by signals */ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 2); -+ g_assert (ls_id > 0); -+ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword); -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ -+ global_components_ready_exit = 4; -+ -+ if (ADD_2_STREAMS) { -+ rs_id_1 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_1 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); -+ -+ rs_id_2 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_2 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2); -+ -+ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword); -+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2); -+ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); -+ -+ if (USE_SECOND_STREAM) { -+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP); -+ } else { -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); -+ } -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ } else { -+ rs_id_1 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_1 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); -+ -+ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword); -+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1); -+ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); -+ } -+ -+ /* step: run the mainloop until connectivity checks succeed -+ * (see timer_cb() above) */ -+ g_main_loop_run (global_mainloop); -+ -+ g_free (lufrag); -+ g_free (lpassword); -+ g_free (rufrag1); -+ g_free (rpassword1); -+ g_free (rufrag2); -+ g_free (rpassword2); -+ g_object_unref (lagent); -+ g_object_unref (ragent); -+ -+ g_main_loop_unref (global_mainloop); -+ global_mainloop = NULL; -+ -+ g_source_remove (timer_id); -+ -+#ifdef G_OS_WIN32 -+ WSACleanup(); -+#endif -+ -+ return 0; -+} --- -2.13.6 - - -From 59fcf95d505c3995f858b826d10cd48321ed383e Mon Sep 17 00:00:00 2001 -From: Youness Alaoui -Date: Mon, 27 Nov 2017 17:07:02 -0500 -Subject: [PATCH 68/70] turn: Add support for ALTERNATE_SERVER in OC2007 - Compatibility - -The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in -allocation responses, and if they are not handled, we end up using the -main turn server to send allocation requests that then get sent to the -alternate server which will return the XOR_MAPPED_ADDRESS containing -the IP address of the turn server that proxied the message instead of -our own actual external IP. ---- - agent/conncheck.c | 14 ++++++++++++++ - stun/usages/turn.c | 11 +++++++++++ - stun/usages/turn.h | 4 ++++ - 3 files changed, 29 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index beb43c3..229c8b1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3764,6 +3764,20 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - recv_realm = (uint8_t *) stun_message_find (resp, - STUN_ATTRIBUTE_REALM, &recv_realm_len); - -+ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -+ alternatelen != sizeof(alternate)) { -+ NiceAddress alternate_addr; -+ -+ nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); -+ -+ if (!nice_address_equal (&alternate_addr, &d->server)) { -+ nice_address_set_from_sockaddr (&d->server, &alternate.addr); -+ nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ -+ d->pending = FALSE; -+ } -+ } - /* check for unauthorized error response */ - if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || - agent->compatibility == NICE_COMPATIBILITY_OC2007 || -diff --git a/stun/usages/turn.c b/stun/usages/turn.c -index 3b94959..ec12642 100644 ---- a/stun/usages/turn.c -+++ b/stun/usages/turn.c -@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, - stun_debug (" STUN error message received (code: %d)", code); - - /* ALTERNATE-SERVER mechanism */ -+ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 && -+ alternate_server && alternate_server_len && -+ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER, -+ alternate_server, -+ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) { -+ stun_debug ("Found alternate server"); -+ /* The ALTERNATE_SERVER will always be returned by the MS turn server. -+ * We need to check if the ALTERNATE_SERVER is the same as the current -+ * server to decide whether we need to switch servers or not. -+ */ -+ } - if ((code / 100) == 3) { - if (alternate_server && alternate_server_len) { - if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, -diff --git a/stun/usages/turn.h b/stun/usages/turn.h -index 7a2d4e6..83fa00a 100644 ---- a/stun/usages/turn.h -+++ b/stun/usages/turn.h -@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, - * Allocate request, in case the currently used TURN server is requesting the use - * of an alternate server. This argument will only be filled if the return value - * of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER -+ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the -+ * @alternate_server could be filled at any time, and should only be considered -+ * if the request was sent to a different server than the address returned -+ * in the @alternate_server field - * @alternate_server_len: The length of @alternate_server - * @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us - * @lifetime: A pointer to fill with the lifetime of the allocation --- -2.13.6 - - -From 4172d48852ecd1c86cc7bd4665b23697603d1eed Mon Sep 17 00:00:00 2001 -From: Youness Alaoui -Date: Tue, 28 Nov 2017 15:14:11 -0500 -Subject: [PATCH 69/70] discovery: Increase discovery_unsched_items whenever we - restart a check - -The discovery_unsched_items is decremented every time a DiscoveryCandidate -goes from non-pending to pending. So if we restart a check by setting -pending to FALSE, we should re-increase the discovery_unsched_items. ---- - agent/conncheck.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 229c8b1..5b08311 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3507,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa - d->server = niceaddr; - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { - /* case: successful binding discovery, create a new local candidate */ - -@@ -3648,6 +3649,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || - res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { - /* case: successful allocate, create a new local candidate */ -@@ -3776,6 +3778,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } - } - /* check for unauthorized error response */ -@@ -3798,6 +3801,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - d->stun_resp_msg.buffer = d->stun_resp_buffer; - d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer); - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else { - /* case: a real unauthorized error */ - d->stun_message.buffer = NULL; --- -2.13.6 - - -From fb2f1f77a31baa91968fc81c205f980b6913f403 Mon Sep 17 00:00:00 2001 -From: Youness Alaoui -Date: Tue, 28 Nov 2017 16:05:18 -0500 -Subject: [PATCH 70/70] conncheck: handle alternate-server for turn relays - differently - -If a relay gives us an alternate-server, we need to cancel and reset -every candidate discovery attempt that uses the same server, to avoid -ending up with one component on one server and the other component on -another server (causing relay candidates with mismatched foundations). ---- - agent/conncheck.c | 56 ++++++++++++++++++++++++++++++++++++++++++------------- - 1 file changed, 43 insertions(+), 13 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 5b08311..c8a4edf 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3592,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand - return cand; - } - -+static void priv_handle_turn_alternate_server (NiceAgent *agent, -+ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate) -+{ -+ /* We need to cancel and reset all candidate discovery turn for the same -+ stream and type if there is an alternate server. Otherwise, we might end up -+ with two relay components on different servers, creating candidates with -+ unique foundations that only contain one component. -+ */ -+ GSList *i; -+ -+ for (i = agent->discovery_list; i; i = i->next) { -+ CandidateDiscovery *d = i->data; -+ -+ if (!d->done && -+ d->type == disco->type && -+ d->stream == disco->stream && -+ d->turn->type == disco->turn->type && -+ nice_address_equal (&d->server, &server)) { -+ gchar ip[INET6_ADDRSTRLEN]; -+ // Cancel the pending request to avoid a race condition with another -+ // component responding with another altenrate-server -+ d->stun_message.buffer = NULL; -+ d->stun_message.buffer_len = 0; -+ -+ nice_address_to_string (&server, ip); -+ nice_debug ("Agent %p : Cancelling and setting alternate server %s for " -+ "CandidateDiscovery %p", agent, ip, d); -+ d->server = alternate; -+ d->turn->server = alternate; -+ d->pending = FALSE; -+ agent->discovery_unsched_items++; -+ } -+ } -+} -+ - /* - * Tries to match STUN reply in 'buf' to an existing STUN discovery - * transaction. If found, a reply is sent. -@@ -3644,12 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - agent, d, (int)res); - - if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) { -- /* handle alternate server */ -- nice_address_set_from_sockaddr (&d->server, &alternate.addr); -- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ NiceAddress addr; - -- d->pending = FALSE; -- agent->discovery_unsched_items++; -+ /* handle alternate server */ -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); - } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || - res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { - /* case: successful allocate, create a new local candidate */ -@@ -3769,16 +3803,12 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && - alternatelen != sizeof(alternate)) { -- NiceAddress alternate_addr; -- -- nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); -+ NiceAddress addr; - -- if (!nice_address_equal (&alternate_addr, &d->server)) { -- nice_address_set_from_sockaddr (&d->server, &alternate.addr); -- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); - -- d->pending = FALSE; -- agent->discovery_unsched_items++; -+ if (!nice_address_equal (&addr, &d->server)) { -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); - } - } - /* check for unauthorized error response */ --- -2.13.6 - -From db6166ee247a8f9fa4ebe2a08d223de73cbd2e96 Mon Sep 17 00:00:00 2001 -From: Jozsef Vass -Date: Mon, 8 Jan 2018 10:53:23 -0800 -Subject: [PATCH 01/15] agent: Fixes incompatible pointer type warning on OSX. - -The variable tie is actually never read. ---- - agent/conncheck.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index c8a4edf..38a90cd 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3421,7 +3421,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - states" and 8.1.2 "Updating States", ID-19) */ - priv_update_check_list_state_for_ready (agent, stream, component); - } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -- guint64 tie; -+ uint64_t tie; - gboolean controlled_mode; - - /* case: role conflict error, need to restart with new role */ --- -2.14.3 - - -From ae3e5acc775ee6c1701ff9a2404b14e4d5dd6c20 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 26 Nov 2017 17:13:19 +0100 -Subject: [PATCH 02/15] conncheck: rework early stun requests handling -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -With this patch we simplify the code used to handle the incoming stun -request when remote candidates or remote credentials have not been -received yet. - -When the remote credentials is unknown, the stun request is stored -in a list of incoming_checks for later processing, and no further -processing is done, except responding to the request. - -When the remote credentials are received, the triggered checks for these -incoming checks can now be queued, and the related pairs are created. - -If the remote candidates have not been received when the stun request -on a valid local port arrives, a peer-reflexive remote candidate will be -created. This candidate may need to be updated later when remote -candidates are finally received, including candidate priority and -foundation, and also related pairs. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1889 ---- - agent/agent.c | 42 ++++++++++++-- - agent/conncheck.c | 168 ++++++++++-------------------------------------------- - agent/conncheck.h | 1 + - 3 files changed, 66 insertions(+), 145 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 0773c53..49fc371 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3300,6 +3300,28 @@ nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr) - return TRUE; - } - -+/* Recompute foundations of all candidate pairs from a given stream -+ * having a specific remote candidate -+ */ -+static void priv_update_pair_foundations (NiceAgent *agent, -+ guint stream_id, NiceCandidate *remote) -+{ -+ NiceStream *stream = agent_find_stream (agent, stream_id); -+ if (stream) { -+ GSList *i; -+ for (i = stream->conncheck_list; i; i = i->next) { -+ CandidateCheckPair *pair = i->data; -+ if (pair->remote == remote) { -+ g_snprintf (pair->foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", -+ pair->local->foundation, pair->remote->foundation); -+ nice_debug ("Agent %p : Updating pair %p foundation to '%s'", -+ agent, pair, pair->foundation); -+ } -+ } -+ } -+} -+ - static gboolean priv_add_remote_candidate ( - NiceAgent *agent, - guint stream_id, -@@ -3331,8 +3353,7 @@ static gboolean priv_add_remote_candidate ( - - /* If it was a discovered remote peer reflexive candidate, then it should - * be updated according to RFC 5245 section 7.2.1.3 */ -- if (candidate && candidate->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE && -- candidate->priority == priority) { -+ if (candidate && candidate->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) { - nice_debug ("Agent %p : Updating existing peer-rfx remote candidate to %s", - agent, _cand_type_to_sdp (type)); - candidate->type = type; -@@ -3375,6 +3396,13 @@ static gboolean priv_add_remote_candidate ( - g_free (candidate->password); - candidate->password = g_strdup (password); - } -+ -+ /* since the type of the existing candidate may have changed, -+ * the pairs priority and foundation related to this candidate need -+ * to be recomputed. -+ */ -+ recalculate_pair_priorities (agent); -+ priv_update_pair_foundations (agent, stream_id, candidate); - } - else { - /* case 2: add a new candidate */ -@@ -3429,12 +3457,14 @@ static gboolean priv_add_remote_candidate ( - if (foundation) - g_strlcpy (candidate->foundation, foundation, - NICE_CANDIDATE_MAX_FOUNDATION); -- } - -- if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0) { -- goto errors; -+ /* We only create a pair when a candidate is new, and not when -+ * updating an existing one. -+ */ -+ if (conn_check_add_for_candidate (agent, stream_id, -+ component, candidate) < 0) -+ goto errors; - } -- - return TRUE; - - errors: -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 38a90cd..11ef9c9 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1690,34 +1690,6 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair * - return 0; - } - --/* -- * Preprocesses a new connectivity check by going through list -- * of a any stored early incoming connectivity checks from -- * the remote peer. If a matching incoming check has been already -- * received, update the state of the new outgoing check 'pair'. -- * -- * @param agent context pointer -- * @param stream which stream (of the agent) -- * @param component pointer to component object to which 'pair'has been added -- * @param pair newly added connectivity check -- */ --static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *pair) --{ -- GSList *i; -- for (i = component->incoming_checks; i; i = i->next) { -- IncomingCheck *icheck = i->data; -- if (nice_address_equal (&icheck->from, &pair->remote->addr) && -- icheck->local_socket == pair->sockptr) { -- nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); -- priv_schedule_triggered_check (agent, stream, component, -- icheck->local_socket, pair->remote); -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); -- } -- } --} -- -- - /* - * Handle any processing steps for connectivity checks after - * remote credentials have been set. This function handles -@@ -1728,126 +1700,39 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - */ - void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - { -- GSList *j, *k, *l, *m, *n, *o; -+ GSList *j, *k, *l, *m; - -- for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *pair = j->data; -- NiceComponent *component = -- nice_stream_find_component_by_id (stream, pair->component_id); -- gboolean match = FALSE; -- -- /* performn delayed processing of spec steps section 7.2.1.4, -- and section 7.2.1.5 */ -- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -+ for (j = stream->components; j ; j = j->next) { -+ NiceComponent *component = j->data; - - for (k = component->incoming_checks; k; k = k->next) { - IncomingCheck *icheck = k->data; - /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to - * be handled separately */ - for (l = component->remote_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&icheck->from, &cand->addr)) { -- match = TRUE; -- break; -- } -- } -- if (match != TRUE) { -- /* note: we have gotten an incoming connectivity check from -- * an address that is not a known remote candidate */ -- -- NiceCandidate *local_candidate = NULL; -- NiceCandidate *remote_candidate = NULL; -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -- agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- /* We need to find which local candidate was used */ -- uint8_t uname[NICE_STREAM_MAX_UNAME]; -- guint uname_len; -- -- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -- "stored pending check", agent); -- -- for (m = component->remote_candidates; -- m != NULL && remote_candidate == NULL; m = m->next) { -- for (n = component->local_candidates; n; n = n->next) { -- NiceCandidate *rcand = m->data; -- NiceCandidate *lcand = n->data; -- -- uname_len = priv_create_username (agent, stream, -- component->id, rcand, lcand, -- uname, sizeof (uname), TRUE); -- -- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -- icheck->username_len, uname_len, -- icheck->username && uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0); -- stun_debug_bytes (" first username: ", -- icheck->username, -- icheck->username? icheck->username_len : 0); -- stun_debug_bytes (" second username: ", uname, uname_len); -- -- if (icheck->username && -- uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0) { -- local_candidate = lcand; -- remote_candidate = rcand; -- break; -- } -- } -- } -- } else { -- for (l = component->local_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -+ NiceCandidate *rcand = l->data; -+ NiceCandidate *lcand = NULL; -+ if (nice_address_equal (&rcand->addr, &icheck->from)) { -+ for (m = component->local_candidates; m; m = m->next) { -+ NiceCandidate *cand = m->data; - if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -- local_candidate = cand; -+ lcand = cand; - break; - } - } -- } -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -- local_candidate == NULL) { -- /* if we couldn't match the username, then the matching remote -- * candidate hasn't been received yet.. we must wait */ -- nice_debug ("Agent %p : Username check failed. pending check has " -- "to wait to be processed", agent); -- } else { -- NiceCandidate *candidate; -- -- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -- agent); -- candidate = -- discovery_learn_remote_peer_reflexive_candidate (agent, -- stream, -- component, -- icheck->priority, -- &icheck->from, -- icheck->local_socket, -- local_candidate, remote_candidate); -- if (candidate) { -- if (local_candidate && -- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -- priv_conn_check_add_for_candidate_pair_matched (agent, -- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -- else -- conn_check_add_for_candidate (agent, stream->id, component, candidate); -- -- priv_schedule_triggered_check (agent, stream, component, -- icheck->local_socket, candidate); -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -- } -+ g_assert (lcand != NULL); -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, rcand); -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, -+ lcand, rcand); -+ break; - } - } - } -- } -- -- /* Once we process the pending checks, we should free them to avoid -- * reprocessing them again if a dribble-mode set_remote_candidates -- * is called */ -- for (o = stream->components; o; o = o->next) { -- NiceComponent *component = o->data; -+ /* Once we process the pending checks, we should free them to avoid -+ * reprocessing them again if a dribble-mode set_remote_candidates -+ * is called */ - g_slist_free_full (component->incoming_checks, - (GDestroyNotify) incoming_check_free); - component->incoming_checks = NULL; -@@ -2964,8 +2849,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - - if (i) { - nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); -- p = priv_add_new_check_pair (agent, stream->id, component, -- local, remote_cand, NICE_CHECK_WAITING); -+ p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id, -+ component, local, remote_cand, NICE_CHECK_WAITING); - priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } -@@ -3018,7 +2903,12 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, - ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr); - } - -- if (rcand) { -+ /* We react to this stun request when we have the remote credentials. -+ * When credentials are not yet known, this request is stored -+ * in incoming_checks for later processing when returning from this -+ * function. -+ */ -+ if (rcand && stream->remote_ufrag[0]) { - priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); - if (use_candidate) - priv_mark_pair_nominated (agent, stream, component, lcand, rcand); -@@ -3114,7 +3004,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - * Recalculates priorities of all candidate pairs. This - * is required after a conflict in ICE roles. - */ --static void priv_recalculate_pair_priorities (NiceAgent *agent) -+void recalculate_pair_priorities (NiceAgent *agent) - { - GSList *i, *j; - -@@ -3143,7 +3033,7 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) - agent->controlling_mode = control; - /* the pair priorities depend on the roles, so recalculation - * is needed */ -- priv_recalculate_pair_priorities (agent); -+ recalculate_pair_priorities (agent); - } - else - nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", -diff --git a/agent/conncheck.h b/agent/conncheck.h -index e16dc67..8cfe2d6 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -119,5 +119,6 @@ conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *co - NiceSocket *sock); - - guint32 ensure_unique_priority (NiceComponent *component, guint32 priority); -+void recalculate_pair_priorities (NiceAgent *agent); - - #endif /*_NICE_CONNCHECK_H */ --- -2.14.3 - - -From 025d84b53bd4ffc0626dd25aa8351319f4d77944 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 26 Nov 2017 17:36:27 +0100 -Subject: [PATCH 03/15] test-new-dribble: make credentials swap asymmetric -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -the first case of test-new-dribble (standard-test) is updated, by making -the credentials swap between the left and right agent asymmetric. -Previously, ragent started to receive stun requests without initially -knowing lagent candidates. Now, ragent also ignores lagent credentials. -This modification allows to test changes introduced by the previous -commit. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1890 ---- - tests/test-new-dribble.c | 55 ++++++++++++++++++++---------------------------- - 1 file changed, 23 insertions(+), 32 deletions(-) - -diff --git a/tests/test-new-dribble.c b/tests/test-new-dribble.c -index 3e60ae3..947f55d 100644 ---- a/tests/test-new-dribble.c -+++ b/tests/test-new-dribble.c -@@ -269,7 +269,7 @@ static gpointer stun_thread_func (const gpointer user_data) - return NULL; - } - --static void set_credentials (NiceAgent *lagent, guint lstream, -+static void swap_credentials (NiceAgent *lagent, guint lstream, - NiceAgent *ragent, guint rstream) - { - gchar *ufrag = NULL, *password = NULL; -@@ -279,12 +279,6 @@ static void set_credentials (NiceAgent *lagent, guint lstream, - - g_free (ufrag); - g_free (password); -- -- nice_agent_get_local_credentials (ragent, rstream, &ufrag, &password); -- nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -- -- g_free (ufrag); -- g_free (password); - } - - static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -@@ -500,12 +494,10 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent) - g_cancellable_reset (global_cancellable); - g_assert (ragent_candidate_gathering_done); - -- set_credentials (lagent, global_ls_id, ragent, global_rs_id); - - g_debug ("Setting local candidates of ragent as remote candidates of lagent"); -- swap_candidates (ragent, global_rs_id, -- lagent, global_ls_id, -- TRUE); -+ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, TRUE); -+ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); - - while (!data_received) - g_main_context_iteration (NULL, TRUE); -@@ -514,9 +506,9 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent) - data_received); - - g_debug ("Setting local candidates of lagent as remote candidates of ragent"); -- swap_candidates (lagent, global_ls_id, -- ragent, global_rs_id, -- FALSE); -+ swap_candidates (lagent, global_ls_id, ragent, global_rs_id, FALSE); -+ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); -+ - while (!lagent_candidate_gathering_done) - g_main_context_iteration (NULL, TRUE); - g_cancellable_reset (global_cancellable); -@@ -557,22 +549,21 @@ static void bad_credentials_test(NiceAgent *lagent, NiceAgent *ragent) - g_cancellable_reset (global_cancellable); - g_assert (ragent_candidate_gathering_done); - -- swap_candidates (ragent, global_rs_id, -- lagent, global_ls_id, -- FALSE); -+ g_debug ("Setting local candidates of ragent as remote candidates of lagent"); -+ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, FALSE); -+ - while (global_lagent_state != NICE_COMPONENT_STATE_FAILED) - g_main_context_iteration (NULL, TRUE); - g_cancellable_reset (global_cancellable); - - // Set the correct credentials and swap candidates -- set_credentials (lagent, global_ls_id, ragent, global_rs_id); -- swap_candidates (ragent, global_rs_id, -- lagent, global_ls_id, -- FALSE); -+ g_debug ("Setting local candidates of ragent as remote candidates of lagent"); -+ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, FALSE); -+ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); - -- swap_candidates (lagent, global_ls_id, -- ragent, global_rs_id, -- FALSE); -+ g_debug ("Setting local candidates of lagent as remote candidates of ragent"); -+ swap_candidates (lagent, global_ls_id, ragent, global_rs_id, FALSE); -+ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); - - while (!data_received) - g_main_context_iteration (NULL, TRUE); -@@ -628,15 +619,14 @@ static void bad_candidate_test(NiceAgent *lagent,NiceAgent *ragent) - - g_assert (global_lagent_state == NICE_COMPONENT_STATE_FAILED && - !data_received); -- set_credentials (lagent, global_ls_id, ragent, global_rs_id); - -- swap_candidates (ragent, global_rs_id, -- lagent, global_ls_id, -- FALSE); -+ g_debug ("Setting local candidates of ragent as remote candidates of lagent"); -+ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, FALSE); -+ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); - -- swap_candidates (lagent, global_ls_id, -- ragent, global_rs_id, -- FALSE); -+ g_debug ("Setting local candidates of lagent as remote candidates of ragent"); -+ swap_candidates (lagent, global_ls_id, ragent, global_rs_id, FALSE); -+ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); - - while (!data_received) - g_main_context_iteration (NULL, TRUE); -@@ -655,7 +645,8 @@ static void new_candidate_test(NiceAgent *lagent, NiceAgent *ragent) - g_debug ("test-dribblemode:%s", G_STRFUNC); - - init_test (lagent, ragent, TRUE); -- set_credentials (lagent, global_ls_id, ragent, global_rs_id); -+ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); -+ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); - - nice_agent_gather_candidates (lagent, global_ls_id); - while (!got_stun_packet) --- -2.14.3 - - -From b5dd5e2aa72a68ac9f027bbcc22700db84a35677 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 26 Nov 2017 17:49:25 +0100 -Subject: [PATCH 04/15] conncheck: the conncheck send function may fail -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -With this patch, we put the pair in state failed if we cannot send -the connection check, for example due to missing local credentials. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1891 ---- - agent/conncheck.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 11ef9c9..9618c3a 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -442,7 +442,11 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * - { - pair->state = NICE_CHECK_IN_PROGRESS; - nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); -- conn_check_send (agent, pair); -+ if (conn_check_send (agent, pair)) { -+ pair->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, pair); -+ return FALSE; -+ } - return TRUE; - } - -@@ -1070,7 +1074,11 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - if (pair) { - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair from triggered check list"); -- conn_check_send (agent, pair); -+ if (conn_check_send (agent, pair)) { -+ pair->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, pair); -+ return FALSE; -+ } - return TRUE; - } - --- -2.14.3 - - -From 47aa02885cda9ddf6e938f966a926be000611c5a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 26 Nov 2017 18:10:12 +0100 -Subject: [PATCH 05/15] conncheck: factorize pair state debug -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1892 ---- - agent/conncheck.c | 69 +++++++++++++++++++++++++------------------------------ - 1 file changed, 31 insertions(+), 38 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 9618c3a..00d02c5 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -123,22 +123,29 @@ priv_state_to_string (NiceCheckState state) - { - switch (state) { - case NICE_CHECK_WAITING: -- return "waiting"; -+ return "WAITING"; - case NICE_CHECK_IN_PROGRESS: -- return "in progress"; -+ return "IN_PROGRESS"; - case NICE_CHECK_SUCCEEDED: -- return "succeeded"; -+ return "SUCCEEDED"; - case NICE_CHECK_FAILED: -- return "failed"; -+ return "FAILED"; - case NICE_CHECK_FROZEN: -- return "frozen"; -+ return "FROZEN"; - case NICE_CHECK_DISCOVERED: -- return "discovered"; -+ return "DISCOVERED"; - default: - g_assert_not_reached (); - } - } - -+#define SET_PAIR_STATE( a, p, s ) G_STMT_START{\ -+ g_assert (p); \ -+ p->state = s; \ -+ nice_debug ("Agent %p : pair %p state %s (%s)", \ -+ a, p, priv_state_to_string (s), G_STRFUNC); \ -+}G_STMT_END -+ - static const gchar * - priv_ice_return_to_string (StunUsageIceReturn ice_return) - { -@@ -251,8 +258,7 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa - { - g_assert (pair); - -- pair->state = NICE_CHECK_IN_PROGRESS; -- nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS); - if (agent->triggered_check_queue == NULL || - g_slist_find (agent->triggered_check_queue, pair) == NULL) - agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -@@ -440,11 +446,9 @@ priv_find_first_frozen_check_list (NiceAgent *agent) - */ - static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) - { -- pair->state = NICE_CHECK_IN_PROGRESS; -- nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS); - if (conn_check_send (agent, pair)) { -- pair->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, pair); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED); - return FALSE; - } - return TRUE; -@@ -495,8 +499,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *str - if (pair) { - nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", - agent, pair, pair->stream_id, pair->component_id, pair->foundation); -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING); - result = TRUE; - } - } -@@ -535,8 +538,7 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - strncmp (p->foundation, ok_check->foundation, - NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { - nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); -- p->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING); - } - } - } -@@ -559,8 +561,7 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - if (p->state == NICE_CHECK_FROZEN && - priv_foundation_matches_a_valid_pair (p->foundation, stream)) { - nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); -- p->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING); - } - } - } else if (priv_is_checklist_frozen (s)) { -@@ -576,8 +577,7 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { - match_found = TRUE; - nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); -- p->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING); - } - } - -@@ -675,8 +675,7 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP - NiceComponent *component; - - component = nice_stream_find_component_by_id (stream, p->component_id); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ SET_PAIR_STATE (agent, p, NICE_CHECK_FAILED); - priv_free_all_stun_transactions (p, component); - } - -@@ -841,8 +840,7 @@ timer_return_timeout: - if (pair) { - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair in Frozen state"); -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING); - priv_conn_check_initiate (agent, pair); - return TRUE; - } -@@ -1075,8 +1073,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair from triggered check list"); - if (conn_check_send (agent, pair)) { -- pair->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, pair); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED); - return FALSE; - } - return TRUE; -@@ -2067,8 +2064,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation); - - pair->priority = agent_candidate_pair_priority (agent, local, remote); -- pair->state = initial_state; -- nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state); -+ nice_debug ("Agent %p : creating a new pair", agent); -+ SET_PAIR_STATE (agent, pair, initial_state); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; - gchar tmpbuf2[INET6_ADDRSTRLEN]; -@@ -2976,10 +2973,10 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->local = local_cand; - pair->remote = parent_pair->remote; - pair->sockptr = local_cand->sockptr; -- pair->state = NICE_CHECK_DISCOVERED; - parent_pair->discovered_pair = pair; - pair->succeeded_pair = parent_pair; -- nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); -+ nice_debug ("Agent %p : creating a new pair", agent); -+ SET_PAIR_STATE (agent, pair, NICE_CHECK_DISCOVERED); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; - gchar tmpbuf2[INET6_ADDRSTRLEN]; -@@ -3099,10 +3096,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - */ - if (new_pair == p) - p->valid = TRUE; -- p->state = NICE_CHECK_SUCCEEDED; -+ SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED); - priv_remove_pair_from_triggered_check_queue (agent, p); - priv_free_all_stun_transactions (p, component); -- nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - nice_component_add_valid_candidate (component, remote_candidate); - } - else { -@@ -3135,11 +3131,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - /* step: The agent sets the state of the pair that *generated* the check to - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ -- p->state = NICE_CHECK_SUCCEEDED; -+ SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED); - priv_remove_pair_from_triggered_check_queue (agent, p); - priv_free_all_stun_transactions (p, component); -- nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", -- agent, p, new_pair); - } - - if (new_pair && new_pair->valid) -@@ -3226,10 +3220,9 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - * "Discovering Peer Reflexive Candidates" ICE ID-19) */ - - if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- p->state = NICE_CHECK_SUCCEEDED; -+ nice_debug ("Agent %p : Mapped address not found", agent); -+ SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED); - p->valid = TRUE; -- nice_debug ("Agent %p : Mapped address not found." -- " conncheck %p SUCCEEDED.", agent, p); - nice_component_add_valid_candidate (component, p->remote); - } else - ok_pair = priv_process_response_check_for_reflexive (agent, --- -2.14.3 - - -From 05f1e30239a448385709df0edd43ec3ac5173218 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Sun, 26 Nov 2017 19:31:39 +0100 -Subject: [PATCH 06/15] conncheck: make debug more homonegeous -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1893 ---- - agent/conncheck.c | 35 ++++++++++++++++++----------------- - 1 file changed, 18 insertions(+), 17 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 00d02c5..25bfd80 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -753,8 +753,8 @@ timer_return_timeout: - /* case: error, abort processing */ - nice_address_to_string (&p->local->addr, tmpbuf1); - nice_address_to_string (&p->remote->addr, tmpbuf2); -- nice_debug ("Agent %p : Retransmissions failed, giving up on " -- "connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p", -+ agent, p); - nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, - tmpbuf1, nice_address_get_port (&p->local->addr), - tmpbuf2, nice_address_get_port (&p->remote->addr)); -@@ -973,7 +973,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p = p->succeeded_pair; - } - g_assert (p->state == NICE_CHECK_SUCCEEDED); -- nice_debug ("Agent %p : restarting check %p with " -+ nice_debug ("Agent %p : restarting check of pair %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); -@@ -996,7 +996,8 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - if (p->component_id == component->id && - (p->state == NICE_CHECK_SUCCEEDED || - p->state == NICE_CHECK_DISCOVERED)) { -- nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); -+ nice_debug ("Agent %p : restarting check of pair %p as the " -+ "nominated pair.", agent, p); - p->nominated = TRUE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); -@@ -2081,7 +2082,9 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); - -- nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id); -+ nice_debug ("Agent %p : added a new pair %p with foundation '%s' to " -+ "stream %u component %u.", agent, pair, pair->foundation, stream_id, -+ component->id); - - /* implement the hard upper limit for number of - checks (see sect 5.7.3 ICE ID-19): */ -@@ -2117,9 +2120,6 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - { - CandidateCheckPair *pair; - -- nice_debug ("Agent %p : Adding check pair between %s and %s for s%d/c%d", -- agent, local->foundation, remote->foundation, -- stream_id, component->id); - pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, - initial_state); - if (component->state == NICE_COMPONENT_STATE_CONNECTED || -@@ -2997,7 +2997,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->nominated = FALSE; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local_cand)); -- nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); -+ nice_debug ("Agent %p : added a new peer-discovered pair with " -+ "foundation '%s'.", agent, pair->foundation); - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -3190,7 +3191,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - - CandidateCheckPair *ok_pair = NULL; - -- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -+ nice_debug ("Agent %p : pair %p MATCHED.", agent, p); - priv_remove_stun_transaction (p, stun, component); - - /* step: verify that response came from the same IP address we -@@ -3201,7 +3202,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - gchar tmpbuf2[INET6_ADDRSTRLEN]; -- nice_debug ("Agent %p : conncheck %p FAILED" -+ nice_debug ("Agent %p : pair %p FAILED" - " (mismatch of source address).", agent, p); - nice_address_to_string (&p->remote->addr, tmpbuf); - nice_address_to_string (from, tmpbuf2); -@@ -3316,7 +3317,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - gboolean controlled_mode; - - /* case: role conflict error, need to restart with new role */ -- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -+ nice_debug ("Agent %p : Role conflict with pair %p, restarting", -+ agent, p); - - /* note: this res value indicates that the role of the peer - * agent has not changed after the tie-breaker comparison, so -@@ -3341,7 +3343,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - priv_add_pair_to_triggered_check_queue (agent, p); - } else { - /* case: STUN error, the check STUN context was freed */ -- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); - candidate_check_pair_fail (stream, agent, p); - } - return TRUE; -@@ -4228,8 +4229,8 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - agent_signal_initial_binding_request_received (agent, stream); - - if (remote_candidate == NULL) { -- nice_debug ("Agent %p : No matching remote candidate for incoming check ->" -- "peer-reflexive candidate.", agent); -+ nice_debug ("Agent %p : No matching remote candidate for incoming " -+ "check -> peer-reflexive candidate.", agent); - remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( - agent, stream, component, priority, from, nicesock, - local_candidate, -@@ -4332,8 +4333,8 @@ conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *co - if ((p->local != NULL && p->local->sockptr == sock) || - (p->remote != NULL && p->remote->sockptr == sock) || - (p->sockptr == sock)) { -- nice_debug ("Agent %p : Retransmissions failed, giving up on " -- "connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p", -+ agent, p); - candidate_check_pair_fail (stream, agent, p); - conn_check_free_item (p); - stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l); --- -2.14.3 - - -From 00dfcc6a625e6c6ed758a4b3b4d0858113508a2f Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 27 Nov 2017 23:56:17 +0100 -Subject: [PATCH 07/15] socket: ping the stun server address on the right - socket -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Verify the compatibility of the socket domain with the stun server -IP address, before sending a request. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1894 ---- - agent/agent.c | 12 +++++++----- - agent/conncheck.c | 4 +++- - 2 files changed, 10 insertions(+), 6 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 49fc371..3306378 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3062,11 +3062,13 @@ nice_agent_gather_candidates ( - if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) { - nice_address_set_port (&stun_server, agent->stun_server_port); - -- priv_add_new_candidate_discovery_stun (agent, -- host_candidate->sockptr, -- stun_server, -- stream, -- cid); -+ if (nice_address_ip_version (&host_candidate->addr) == -+ nice_address_ip_version (&stun_server)) -+ priv_add_new_candidate_discovery_stun (agent, -+ host_candidate->sockptr, -+ stun_server, -+ stream, -+ cid); - } - } - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 25bfd80..4d91f41 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1459,7 +1459,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *candidate = (NiceCandidate *) k->data; - if (candidate->type == NICE_CANDIDATE_TYPE_HOST && -- candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP) { -+ candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP && -+ nice_address_ip_version (&candidate->addr) == -+ nice_address_ip_version (&stun_server)) { - /* send the conncheck */ - nice_debug ("Agent %p : resending STUN on %s to keep the " - "candidate alive.", agent, candidate->foundation); --- -2.14.3 - - -From ea05a3d51990d17bbe25984eb5730849f46bfae0 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Wed, 29 Nov 2017 11:04:04 +0100 -Subject: [PATCH 08/15] conncheck: dont fail a stream with a empty conncheck - list -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Since commit 17f30e4, we may have a stream with an empty conncheck list, -and such a stream obviously should not be tested for failed components. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1895 ---- - agent/conncheck.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 4d91f41..0ebe7e9 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1832,6 +1832,9 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - * must be fetched before entering the loop*/ - guint c, components = stream->n_components; - -+ if (stream->conncheck_list == NULL) -+ return; -+ - for (i = agent->discovery_list; i; i = i->next) { - CandidateDiscovery *d = i->data; - -@@ -1846,8 +1849,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - - /* note: iterate the conncheck list for each component separately */ - for (c = 0; c < components; c++) { -- NiceComponent *comp = NULL; -- if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) -+ NiceComponent *component = NULL; -+ if (!agent_find_component (agent, stream->id, c+1, NULL, &component)) - continue; - - nominated = 0; -@@ -1873,7 +1876,7 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - * Set the component to FAILED only if it actually had remote candidates - * that failed.. */ - if (completed && nominated == 0 && -- comp != NULL && comp->remote_candidates != NULL) -+ component != NULL && component->remote_candidates != NULL) - agent_signal_component_state_change (agent, - stream->id, - (c + 1), /* component-id */ --- -2.14.3 - - -From a9ac0487b0d1708d780d7c0b7a2206c71a8c7163 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Thu, 30 Nov 2017 20:11:22 +0100 -Subject: [PATCH 09/15] discovery: ignore all non-relay local candidates when - relay is forced -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The tcp server reflexive discovered local candidates must be ignored -when force_relay is set. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1899 ---- - agent/discovery.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/agent/discovery.c b/agent/discovery.c -index 4cc99c2..e2142a2 100644 ---- a/agent/discovery.c -+++ b/agent/discovery.c -@@ -688,7 +688,8 @@ discovery_discover_tcp_server_reflexive_candidates ( - - caddr = c->addr; - nice_address_set_port (&caddr, 0); -- if (c->transport != NICE_CANDIDATE_TRANSPORT_UDP && -+ if (agent->force_relay == FALSE && -+ c->transport != NICE_CANDIDATE_TRANSPORT_UDP && - c->type == NICE_CANDIDATE_TYPE_HOST && - nice_address_equal (&base_addr, &caddr)) { - nice_address_set_port (address, nice_address_get_port (&c->addr)); --- -2.14.3 - - -From 5a644f459dc75c80dfb19c7772f74e37a0258771 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet -Date: Mon, 11 Dec 2017 08:50:33 +0100 -Subject: [PATCH 10/15] agent: make candidate username and password immutable -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -With this patch we prevent the username and the password of a candidate -to be modified during a session, as required by the RFC, sect 9.1.2. -This is also needed from a memory management point of view, because the -password string pointer may be recorded in the components stun agent -sent_ids[] struct key member, and freeing these values there may cause -an use-after-free condition, when an inbound stun is received from this -candidate. This behavior has been observed with pidgin, xmpp, and -farstream when a same remote candidates are "updated" several times, -even if the credentials don't change in this case. - -Reviewed-by: Olivier Crête -Differential Revision: https://phabricator.freedesktop.org/D1917 ---- - agent/agent.c | 19 +++++++++++++------ - 1 file changed, 13 insertions(+), 6 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 3306378..dbece3b 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3388,15 +3388,22 @@ static gboolean priv_add_remote_candidate ( - * this is essential to overcome a race condition where we might receive - * a valid binding request from a valid candidate that wasn't yet added to - * our list of candidates.. this 'update' will make the peer-rflx a -- * server-rflx/host candidate again and restore that user/pass it needed -- * to have in the first place */ -+ * server-rflx/host candidate again */ - if (username) { -- g_free (candidate->username); -- candidate->username = g_strdup (username); -+ if (candidate->username == NULL) -+ candidate->username = g_strdup (username); -+ else if (g_strcmp0 (username, candidate->username)) -+ nice_debug ("Agent %p : Candidate username '%s' is not allowed " -+ "to change to '%s' now (ICE restart only).", agent, -+ candidate->username, username); - } - if (password) { -- g_free (candidate->password); -- candidate->password = g_strdup (password); -+ if (candidate->password == NULL) -+ candidate->password = g_strdup (password); -+ else if (g_strcmp0 (password, candidate->password)) -+ nice_debug ("Agent %p : candidate password '%s' is not allowed " -+ "to change to '%s' now (ICE restart only).", agent, -+ candidate->password, password); - } - - /* since the type of the existing candidate may have changed, --- -2.14.3 - - -From 54fb03427ebc13413cd1ddd5d9e91c1751eac0cb Mon Sep 17 00:00:00 2001 -From: Jakub Adam -Date: Sat, 3 Feb 2018 23:59:20 +0100 -Subject: [PATCH 11/15] discovery: ignore bogus Skype for Business srflx - addresses - -If main SfB TURN server sends our allocation request to an alternate -server, the response will have XOR_MAPPED_ADDRESS containing the IP -address of the turn server that proxied the message instead of our own -actual external IP. - -Before we create server reflexive candidates upon receiving an allocate -response, check that the TURN port got assigned on the same server we -sent out allocate request to. Otherwise, the request was proxied and -XOR_MAPPED_ADDRESS contains a bogus value we should ignore. - -Issue introduced by 59fcf95d505c3995f858b826d10cd48321ed383e. -Differential Revision: https://phabricator.freedesktop.org/D1949 ---- - agent/conncheck.c | 31 +++++++++++++++++++++---------- - 1 file changed, 21 insertions(+), 10 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 0ebe7e9..19729c2 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3587,9 +3587,13 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - NiceAddress niceaddr; - NiceCandidate *relay_cand; - -+ nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr); -+ - if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { -+ NiceAddress mappedniceaddr; -+ - /* We also received our mapped address */ -- nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr); -+ nice_address_set_from_sockaddr (&mappedniceaddr, &sockaddr.addr); - - /* TCP or TLS TURNS means the server-reflexive address was - * on a TCP connection, which cannot be used for server-reflexive -@@ -3601,21 +3605,28 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - d->agent, - d->stream->id, - d->component->id, -- &niceaddr, -+ &mappedniceaddr, - NICE_CANDIDATE_TRANSPORT_UDP, - d->nicesock, - FALSE); - } -- if (d->agent->use_ice_tcp) -- discovery_discover_tcp_server_reflexive_candidates ( -- d->agent, -- d->stream->id, -- d->component->id, -- &niceaddr, -- d->nicesock); -+ if (d->agent->use_ice_tcp) { -+ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -+ !nice_address_equal_no_port (&niceaddr, &d->turn->server)) { -+ nice_debug("TURN port got allocated on an alternate server, " -+ "ignoring bogus srflx address"); -+ } else { -+ discovery_discover_tcp_server_reflexive_candidates ( -+ d->agent, -+ d->stream->id, -+ d->component->id, -+ &mappedniceaddr, -+ d->nicesock); -+ } -+ } - } - -- nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr); - if (nice_socket_is_reliable (d->nicesock)) { - relay_cand = discovery_add_relay_candidate ( - d->agent, --- -2.14.3 - - -From 922ee4e61b4d9c6b403933f4a3261e67589d5099 Mon Sep 17 00:00:00 2001 -From: Jakub Adam -Date: Wed, 19 Apr 2017 14:17:04 +0200 -Subject: [PATCH 12/15] agent: don't require "reliable" be TRUE in order to use - "ice-tcp" - -Setting writable socket callbacks doesn't have to be limited to reliable -agents. TCP sockets need the callback in any case for correct operation -and calling nice_socket_set_writable_callback() on a NiceSocket that has -UDP as its base has no effect. - -Differential Revision: https://phabricator.freedesktop.org/D1726 ---- - agent/agent.c | 11 ++++------- - agent/conncheck.c | 5 ++--- - 2 files changed, 6 insertions(+), 10 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index dbece3b..89e3514 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -2547,9 +2547,8 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - if (nicesock == NULL) - return; - -- if (agent->reliable) -- nice_socket_set_writable_callback (nicesock, _tcp_sock_is_writable, -- component); -+ nice_socket_set_writable_callback (nicesock, _tcp_sock_is_writable, component); -+ - if (turn->type == NICE_RELAY_TYPE_TURN_TLS && - agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - nicesock = nice_pseudossl_socket_new (nicesock, -@@ -3033,10 +3032,8 @@ nice_agent_gather_candidates ( - found_local_address = TRUE; - nice_address_set_port (addr, 0); - -- -- if (agent->reliable) -- nice_socket_set_writable_callback (host_candidate->sockptr, -- _tcp_sock_is_writable, component); -+ nice_socket_set_writable_callback (host_candidate->sockptr, -+ _tcp_sock_is_writable, component); - - #ifdef HAVE_GUPNP - if (agent->upnp_enabled && agent->upnp && -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 19729c2..64a3cb8 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2669,9 +2669,8 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - pair->sockptr = new_socket; - _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); - -- if (agent->reliable) -- nice_socket_set_writable_callback (pair->sockptr, -- _tcp_sock_is_writable, component2); -+ nice_socket_set_writable_callback (pair->sockptr, _tcp_sock_is_writable, -+ component2); - - nice_component_attach_socket (component2, new_socket); - } --- -2.14.3 - - -From e6217f8eba6ea17d90eac67ef5fa5412fbf10dad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Fri, 4 May 2018 15:44:05 +0200 -Subject: [PATCH 13/15] Ignore function case warnings - -This makes GLib usage annoying as it makes GSourceFunc casts invalid. ---- - configure.ac | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/configure.ac b/configure.ac -index 16988ad..36bd622 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -136,6 +136,7 @@ AS_IF([test "x$enable_compile_warnings" != "xno" -a \ - NICE_ADD_FLAG([-Wcast-align]) - NICE_ADD_FLAG([-Wformat-nonliteral]) - NICE_ADD_FLAG([-Wformat-security]) -+ NICE_ADD_FLAG([-Wno-cast-function-type]) - ]) - AS_IF([test "$enable_compile_warnings" = "yes" -o \ - "$enable_compile_warnings" = "maximum" -o \ --- -2.14.3 - - -From 3a9d92818b4c2f083e26fe164a1be82212bd4061 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Fri, 4 May 2018 16:44:45 +0200 -Subject: [PATCH 14/15] stund: Pass the right length for ipv6 - ---- - stun/tools/stund.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/stun/tools/stund.c b/stun/tools/stund.c -index addc4fa..c148e51 100644 ---- a/stun/tools/stund.c -+++ b/stun/tools/stund.c -@@ -100,6 +100,8 @@ int listen_socket (int fam, int type, int proto, unsigned int port) - struct sockaddr_in6 in6; - struct sockaddr_storage storage; - } addr; -+ int len; -+ - if (fd == -1) - { - perror ("Error opening IP port"); -@@ -118,6 +120,7 @@ int listen_socket (int fam, int type, int proto, unsigned int port) - { - case AF_INET: - addr.in.sin_port = htons (port); -+ len = sizeof (struct sockaddr_in); - break; - - case AF_INET6: -@@ -125,13 +128,14 @@ int listen_socket (int fam, int type, int proto, unsigned int port) - setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes)); - #endif - addr.in6.sin6_port = htons (port); -+ len = sizeof (struct sockaddr_in6); - break; - - default: - assert (0); /* should never be reached */ - } - -- if (bind (fd, &addr.addr, sizeof (struct sockaddr))) -+ if (bind (fd, &addr.addr, len)) - { - perror ("Error opening IP port"); - goto error; --- -2.14.3 - - -From 34d60446ddfcdb98f2543611151ef8fbc5be4805 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= -Date: Fri, 4 May 2018 16:50:45 +0200 -Subject: [PATCH 15/15] stund: Pass sockaddr_storage size for both families - ---- - stun/tools/stund.c | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) - -diff --git a/stun/tools/stund.c b/stun/tools/stund.c -index c148e51..00a0881 100644 ---- a/stun/tools/stund.c -+++ b/stun/tools/stund.c -@@ -100,15 +100,12 @@ int listen_socket (int fam, int type, int proto, unsigned int port) - struct sockaddr_in6 in6; - struct sockaddr_storage storage; - } addr; -- int len; - - if (fd == -1) - { - perror ("Error opening IP port"); - return -1; - } -- if (fd < 3) -- goto error; - - memset (&addr, 0, sizeof (addr)); - addr.storage.ss_family = fam; -@@ -120,7 +117,6 @@ int listen_socket (int fam, int type, int proto, unsigned int port) - { - case AF_INET: - addr.in.sin_port = htons (port); -- len = sizeof (struct sockaddr_in); - break; - - case AF_INET6: -@@ -128,14 +124,13 @@ int listen_socket (int fam, int type, int proto, unsigned int port) - setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes)); - #endif - addr.in6.sin6_port = htons (port); -- len = sizeof (struct sockaddr_in6); - break; - - default: - assert (0); /* should never be reached */ - } - -- if (bind (fd, &addr.addr, len)) -+ if (bind (fd, &addr.addr, sizeof (struct sockaddr_storage))) - { - perror ("Error opening IP port"); - goto error; -@@ -192,7 +187,7 @@ static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent) - StunValidationStatus validation; - StunAgent *agent = NULL; - -- addr_len = sizeof (struct sockaddr_in); -+ addr_len = sizeof (struct sockaddr_storage); - len = recvfrom (sock, buf, sizeof(buf), 0, &addr.addr, &addr_len); - if (len == (size_t)-1) - return -1; --- -2.14.3 - diff --git a/libnice-0.1.14-tests-i686.patch b/libnice-0.1.14-tests-i686.patch deleted file mode 100644 index 6558549..0000000 --- a/libnice-0.1.14-tests-i686.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 3f8364b41207d8c26d3d3be518a7d9ebf4243b92 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Fri, 9 Feb 2018 18:01:57 +0100 -Subject: [PATCH] tests: make them compile on i686 - ---- - tests/test-pseudotcp-fuzzy.c | 4 ++-- - tests/test-pseudotcp.c | 2 +- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/tests/test-pseudotcp-fuzzy.c b/tests/test-pseudotcp-fuzzy.c -index 4a714e6..030c03f 100644 ---- a/tests/test-pseudotcp-fuzzy.c -+++ b/tests/test-pseudotcp-fuzzy.c -@@ -129,7 +129,7 @@ write_to_sock (PseudoTcpSocket *sock) - total += wlen; - total_read += wlen; - if (wlen < (gint) len) { -- g_debug ("seeking %ld from %lu", wlen - len, ftell (in)); -+ g_debug ("seeking %ld from %lu", (long) wlen - len, ftell (in)); - fseek (in, wlen - len, SEEK_CUR); - g_assert (!feof (in)); - g_debug ("Socket queue full after %d bytes written", total); -@@ -355,7 +355,7 @@ static void adjust_clock (PseudoTcpSocket *sock) - - if (pseudo_tcp_socket_get_next_clock (sock, &timeout)) { - timeout -= g_get_monotonic_time () / 1000; -- g_debug ("Socket %p: Adjusting clock to %ld ms", sock, timeout); -+ g_debug ("Socket %p: Adjusting clock to %ld ms", sock, (long) timeout); - if (sock == left) { - if (left_clock != 0) - g_source_remove (left_clock); -diff --git a/tests/test-pseudotcp.c b/tests/test-pseudotcp.c -index 1a8391a..584a0d0 100644 ---- a/tests/test-pseudotcp.c -+++ b/tests/test-pseudotcp.c -@@ -81,7 +81,7 @@ static void write_to_sock (PseudoTcpSocket *sock) - total += wlen; - total_read += wlen; - if (wlen < (gint) len) { -- g_debug ("seeking %ld from %lu", wlen - len, ftell (in)); -+ g_debug ("seeking %ld from %lu", (long) wlen - len, ftell (in)); - fseek (in, wlen - len, SEEK_CUR); - g_assert (!feof (in)); - g_debug ("Socket queue full after %d bytes written", total); --- -2.13.6 - diff --git a/libnice-0.1.14-tests-koji.patch b/libnice-0.1.14-tests-koji.patch deleted file mode 100644 index 997e712..0000000 --- a/libnice-0.1.14-tests-koji.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 527c30ba453753e75d3d31be29a277ea6adc17c0 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Fri, 9 Feb 2018 19:10:20 +0100 -Subject: [PATCH] tests: make the test-suite more verbose - -... and skip test-gstreamer if user's home is not /builddir (a heuristic -to detect mock) because multicast traffic is blocked on Koji buildhosts. ---- - configure.ac | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 16988ad..b7b74fa 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -12,7 +12,7 @@ AC_CANONICAL_TARGET - - AC_CONFIG_SRCDIR([agent/agent.c]) - AC_CONFIG_HEADERS([config.h]) --AM_INIT_AUTOMAKE([1.12 -Wall -Wno-portability subdir-objects]) -+AM_INIT_AUTOMAKE([1.12 -Wall -Wno-portability subdir-objects serial-tests]) - - AC_CONFIG_FILES([ - Makefile -@@ -263,7 +263,7 @@ AC_SUBST(gstplugindir) - AC_SUBST(gstplugin010dir) - - AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes) --AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes) -+AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes -a "$HOME" != /builddir) - AM_CONDITIONAL(WITH_GSTREAMER010, test "$with_gstreamer010" = yes) - - GUPNP_IGD_REQUIRED=0.2.4 --- -2.13.6 - diff --git a/libnice-0.1.14-turn-verify.patch b/libnice-0.1.14-turn-verify.patch deleted file mode 100644 index 2ce2f86..0000000 --- a/libnice-0.1.14-turn-verify.patch +++ /dev/null @@ -1,32 +0,0 @@ -From e4d92bf96d0bb64df35790e5b49c58bfa6e9fbcc Mon Sep 17 00:00:00 2001 -From: Jakub Adam -Date: Thu, 20 Apr 2017 06:47:00 +0200 -Subject: [PATCH] component: accept TURN in - nice_component_verify_remote_candidate() - -When TURN is in operation, agent_recv_message_unlocked() may receive from -NiceSocket with type = NICE_SOCKET_TYPE_UDP_TURN. Such messages were always -dropped due to failed nice_component_verify_remote_candidate(). - -Bug: https://phabricator.freedesktop.org/D1727 ---- - agent/component.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/agent/component.c b/agent/component.c -index 6eee90e..3e8a7a6 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -1510,7 +1510,8 @@ nice_component_verify_remote_candidate (NiceComponent *component, - (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || - cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || - cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || -- cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && -+ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP || -+ nicesock->type == NICE_SOCKET_TYPE_UDP_TURN) && - nice_address_equal (address, &cand->addr)) { - /* fast return if it's already the first */ - if (item == component->valid_candidates) --- -2.14.3 - diff --git a/libnice-0.1.14.tar.gz b/libnice-0.1.14.tar.gz deleted file mode 100644 index aaabd37..0000000 Binary files a/libnice-0.1.14.tar.gz and /dev/null differ diff --git a/libnice-0.1.19.tar.gz b/libnice-0.1.19.tar.gz new file mode 100644 index 0000000..c82aa7c Binary files /dev/null and b/libnice-0.1.19.tar.gz differ diff --git a/libnice.spec b/libnice.spec index 1c42262..c3e5e2f 100644 --- a/libnice.spec +++ b/libnice.spec @@ -1,19 +1,15 @@ Name: libnice -Version: 0.1.14 -Release: 12 +Version: 0.1.19 +Release: 1 Summary: An implementation of ICE standard License: LGPLv2 and MPLv1.1 URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz -Patch0001: libnice-0.1.14-85-g34d6044.patch -Patch0002: libnice-0.1.14-tests-i686.patch -Patch0003: libnice-0.1.14-tests-koji.patch -Patch0004: libnice-0.1.14-turn-verify.patch BuildRequires: autoconf automake glib2-devel gnutls-devel >= 2.12.0 BuildRequires: gobject-introspection-devel gstreamer1-devel >= 0.11.91 BuildRequires: gstreamer1-plugins-base-devel >= 0.11.91 -BuildRequires: gtk-doc +BuildRequires: gtk-doc gupnp-igd-devel >= 0.1.2 graphviz meson %description Libnice is an implementation of the IETF's Interactive Connectivity @@ -40,23 +36,18 @@ This package provides Libraries and header files for libnice. %prep %autosetup -n %{name}-%{version} -p1 -chmod 0755 scripts/valgrind-test-driver -sed -e 's/test-new-dribble/#&/' -e 's/test-send-recv/#&/' -i tests/Makefile.am -autoreconf -fiv +sed -e "s/^ 'test-set-port-range'/#&/" -i tests/meson.build %build -%configure --enable-compile-warnings=yes --disable-static --without-gstreamer-0.10 -sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool -sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool -%make_build V=1 +%meson -D gtk_doc=enabled +%meson_build %install -%make_install +%meson_install %delete_la %check -export LD_LIBRARY_PATH="$PWD/nice/.libs" -make check +%meson_test %post -p /sbin/ldconfig %postun -p /sbin/ldconfig @@ -76,6 +67,9 @@ make check %{_datadir}/{gtk-doc/html/libnice/,gir-1.0/Nice-0.1.gir} %changelog +* Thu Aug 04 2022 yaoxin - 0.1.19-1 +- Update to 0.1.19 + * Tue Mar 29 2022 wangkai - 0.1.14-12 - Do check on X86 and ARM64