The package rpms/libnice.git has added or updated architecture specific content in its spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s): https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=cb05dbf2151a8... https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=368c412d96e96... https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=729f8ecea839d....
Change: -%ifarch armv7hl +%ifarch x86_64 %{ix86} +%ifarch armv7hl
Thanks.
Full change: ============
commit 617b0abe3745d5595d7af6e4cf21a433dafe130f Merge: 7a9cb02 a04a646 Author: Kamil Dudka kdudka@redhat.com Date: Thu Jun 28 12:15:48 2018 +0200
Merge branch 'f28' into f27
diff --cc libnice.spec index 98da5b7,f27e02f..8201309 --- a/libnice.spec +++ b/libnice.spec @@@ -1,13 -1,13 +1,17 @@@ -# disable building of plugin for gstreamer 0.10 +# enable/disable building of plugin for gstreamer 0.10 +%if 0%{?rhel} > 7 %bcond_with gst010 +%else +%bcond_without gst010 +%endif
+ %global upstream_date 20180504 + %global upstream_rnum 85 + %global upstream_hash 34d6044 + Name: libnice Version: 0.1.14 - Release: 1%{?dist} + Release: 7.%{upstream_date}git%{upstream_hash}%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries @@@ -121,12 -149,6 +162,8 @@@ make chec %{_libdir}/gstreamer-1.0/libgstnice.so
+%files examples - %{_bindir}/sdp-example - %{_bindir}/simple-example - %{_bindir}/threaded-example - + %files devel %{_includedir}/* %{_libdir}/*.so @@@ -136,6 -158,37 +173,36 @@@
%changelog + * Mon May 07 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-7.20180504git34d6044 + - component: accept TURN in nice_component_verify_remote_candidate() (#1541646) + - update to 0.1.14-85-g34d6044 (#1541646) + + * Mon Apr 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-6.20171128gitfb2f1f7 + - temporarily make the upstream test-suite run on Intel arches only + - disable test-send-recv, which fails in Koji + + * Fri Mar 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-5.20171128gitfb2f1f7 + - do not build with -Werror by default + - make the build more verbose + + * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 + - enable make check again + - make tests pass in Koji + - disable test-new-dribble that sometimes hangs indefinitely + - make tests compile on i686 + - make the package build on armv7hl + - make the package build on Fedora 28 + - avoid build failure if gstreamer-plugins-base-devel is installed + - move autoreconf invocation to %%prep + - use Name Version Release that explicitly identifies an SCM snapshot (#1541646) + + * Fri Feb 09 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-3 + - update to 0.1.14-70-gfb2f1f7 with alternate server fixes for SIPE + - add autoreconf build step -- remove examples subpackage as examples are no longer installed + + * Wed Feb 07 2018 Fedora Release Engineering releng@fedoraproject.org - 0.1.14-2 + - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + * Mon Jan 29 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-1 - Update to 0.1.14
commit a04a646f78d6c1834cc1a32d154a8a34c42f226a Author: Kamil Dudka kdudka@redhat.com Date: Mon May 7 17:37:42 2018 +0200
Resolves: #1541646 - component: accept TURN in nice_component_verify_remote_candidate()
diff --git a/libnice-0.1.14-turn-verify.patch b/libnice-0.1.14-turn-verify.patch new file mode 100644 index 0000000..2ce2f86 --- /dev/null +++ b/libnice-0.1.14-turn-verify.patch @@ -0,0 +1,32 @@ +From e4d92bf96d0bb64df35790e5b49c58bfa6e9fbcc Mon Sep 17 00:00:00 2001 +From: Jakub Adam jakub.adam@ktknet.cz +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.spec b/libnice.spec index 26f66dc..f27e02f 100644 --- a/libnice.spec +++ b/libnice.spec @@ -22,6 +22,9 @@ Patch2: libnice-0.1.14-tests-i686.patch # make tests pass in Koji Patch3: libnice-0.1.14-tests-koji.patch
+# component: accept TURN in nice_component_verify_remote_candidate() (#1541646) +Patch4: libnice-0.1.14-turn-verify.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: glib2-devel @@ -82,6 +85,7 @@ developing applications that use %{name}. %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 chmod 0755 scripts/valgrind-test-driver
# disable test-new-dribble, which sometimes hangs indefinitely, and @@ -155,6 +159,7 @@ make check
%changelog * Mon May 07 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-7.20180504git34d6044 +- component: accept TURN in nice_component_verify_remote_candidate() (#1541646) - update to 0.1.14-85-g34d6044 (#1541646)
* Mon Apr 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-6.20171128gitfb2f1f7
commit 9cda08004416b0d369badbae81066eeb25e0039c Author: Kamil Dudka kdudka@redhat.com Date: Mon May 7 15:57:12 2018 +0200
Resolves: #1541646 - update to 0.1.14-85-g34d6044
diff --git a/libnice-0.1.14-70-gfb2f1f7.patch b/libnice-0.1.14-70-gfb2f1f7.patch deleted file mode 100644 index db148cb..0000000 --- a/libnice-0.1.14-70-gfb2f1f7.patch +++ /dev/null @@ -1,10531 +0,0 @@ -From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= olivier.crete@collabora.com -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?= mparisdiaz@gmail.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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 http://www.gnu.org/licenses/. -+ -+# 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 bug-automake@gnu.org or send patches to -+# automake-patches@gnu.org. -+ -+# 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 <<END -+Usage: -+ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH -+ [--expect-failure={yes|no}] [--color-tests={yes|no}] -+ [--enable-hard-errors={yes|no}] [--] -+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] -+The '--test-name', '--log-file' and '--trs-file' options are mandatory. -+END -+} -+ -+test_name= # Used for reporting. -+log_file= # Where to save the output of the test script. -+trs_file= # Where to save the metadata of the test run. -+expect_failure=no -+color_tests=no -+enable_hard_errors=yes -+while test $# -gt 0; do -+ case $1 in -+ --help) print_usage; exit $?;; -+ --version) echo "test-driver $scriptversion"; exit $?;; -+ --test-name) test_name=$2; shift;; -+ --log-file) log_file=$2; shift;; -+ --trs-file) trs_file=$2; shift;; -+ --color-tests) color_tests=$2; shift;; -+ --expect-failure) expect_failure=$2; shift;; -+ --enable-hard-errors) enable_hard_errors=$2; shift;; -+ --) shift; break;; -+ -*) usage_error "invalid option: '$1'";; -+ *) break;; -+ esac -+ shift -+done -+ -+missing_opts= -+test x"$test_name" = x && missing_opts="$missing_opts --test-name" -+test x"$log_file" = x && missing_opts="$missing_opts --log-file" -+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" -+if test x"$missing_opts" != x; then -+ usage_error "the following mandatory options are missing:$missing_opts" -+fi -+ -+if test $# -eq 0; then -+ usage_error "missing argument" -+fi -+ -+if test $color_tests = yes; then -+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. -+ red='[0;31m' # Red. -+ grn='[0;32m' # Green. -+ lgn='[1;32m' # Light green. -+ blu='[1;34m' # Blue. -+ mgn='[0;35m' # Magenta. -+ std='[m' # No color. -+else -+ red= grn= lgn= blu= mgn= std= -+fi -+ -+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' -+trap "st=129; $do_exit" 1 -+trap "st=130; $do_exit" 2 -+trap "st=141; $do_exit" 13 -+trap "st=143; $do_exit" 15 -+ -+# Test script is run here. -+top_srcdir="`dirname $0`/.." -+tests_dir="${top_srcdir}/tests" -+ -+USE_VALGRIND="`printenv USE_VALGRIND`" -+ -+if test "x${USE_VALGRIND}" = "x1"; then -+ ${top_srcdir}/libtool --mode=execute valgrind \ -+ --leak-check=full \ -+ --show-reachable=no \ -+ --error-exitcode=1 \ -+ --suppressions=$tests_dir/libnice.supp \ -+ --num-callers=30 "$@" >$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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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 philip.withnall@collabora.co.uk -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 @@ - <title>Index of new symbols in 0.1.14</title> - <xi:include href="xml/api-index-0.1.14.xml">xi:fallback/</xi:include> - </index> -+ <index role="0.1.15"> -+ <title>Index of new symbols in 0.1.15</title> -+ <xi:include href="xml/api-index-0.1.15.xml">xi:fallback/</xi:include> -+ </index> - <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> - </part> - </book> -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 - <SUBSECTION Standard> - 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?= olivier.crete@collabora.com -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 <config.h> --#endif -- --#include <string.h> -- --#include <nice/nice.h> --#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?= olivier.crete@collabora.com -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 philip.withnall@collabora.co.uk -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 olivier.crete@collabora.com -+ * -+ * 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 <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include "socket/socket.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+ -+ -+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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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 fabrice@bellet.info -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 fabrice@bellet.info -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 withnall@endlessm.com -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 withnall@endlessm.com -Reviewed-by: Olivier Crête olivier.crete@collabora.com -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 fabrice@bellet.info -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?= olivier.crete@collabora.com -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 <glib-object.h>\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 <config.h>\n#include <glib-object.h>\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. -+ * <para> See also: #NiceNominationMode </para> -+ * -+ * 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() -+ * <para> See also: #NiceNominationMode </para> -+ * -+ * 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 fabrice@bellet.info -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 <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <gio/gio.h> -+#include <agent.h> -+ -+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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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?= olivier.crete@collabora.com -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() -- * <para> See also: #NiceNominationMode </para> -+ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> - * - * 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?= olivier.crete@collabora.com -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. - * <para> See also: #NiceNominationMode </para> - * -- * 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() - * <para> See also: #NiceNominationMode and #NiceAgentOption</para> - * -- * 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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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?= olivier.crete@collabora.com -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 fabrice@bellet.info -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?= olivier.crete@collabora.com -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 fabrice@bellet.info -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 withnall@endlessm.com -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 withnall@endlessm.com ---- - 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 withnall@endlessm.com -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 withnall@endlessm.com ---- - 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 <gst/check/gstcheck.h> --#include <nice/agent.h> -+#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 withnall@endlessm.com -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 withnall@endlessm.com -Reviewed-by: Olivier Crête olivier.crete@collabora.com -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 fabrice@bellet.info -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 fabrice@bellet.info -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?= mparisdiaz@gmail.com -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 <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+#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 kakaroto@kakaroto.homelinux.net -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 kakaroto@kakaroto.homelinux.net -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 kakaroto@kakaroto.homelinux.net -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 - diff --git a/libnice-0.1.14-85-g34d6044.patch b/libnice-0.1.14-85-g34d6044.patch new file mode 100644 index 0000000..541d010 --- /dev/null +++ b/libnice-0.1.14-85-g34d6044.patch @@ -0,0 +1,11963 @@ +From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= olivier.crete@collabora.com +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?= mparisdiaz@gmail.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 http://www.gnu.org/licenses/. ++ ++# 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 bug-automake@gnu.org or send patches to ++# automake-patches@gnu.org. ++ ++# 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 <<END ++Usage: ++ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH ++ [--expect-failure={yes|no}] [--color-tests={yes|no}] ++ [--enable-hard-errors={yes|no}] [--] ++ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] ++The '--test-name', '--log-file' and '--trs-file' options are mandatory. ++END ++} ++ ++test_name= # Used for reporting. ++log_file= # Where to save the output of the test script. ++trs_file= # Where to save the metadata of the test run. ++expect_failure=no ++color_tests=no ++enable_hard_errors=yes ++while test $# -gt 0; do ++ case $1 in ++ --help) print_usage; exit $?;; ++ --version) echo "test-driver $scriptversion"; exit $?;; ++ --test-name) test_name=$2; shift;; ++ --log-file) log_file=$2; shift;; ++ --trs-file) trs_file=$2; shift;; ++ --color-tests) color_tests=$2; shift;; ++ --expect-failure) expect_failure=$2; shift;; ++ --enable-hard-errors) enable_hard_errors=$2; shift;; ++ --) shift; break;; ++ -*) usage_error "invalid option: '$1'";; ++ *) break;; ++ esac ++ shift ++done ++ ++missing_opts= ++test x"$test_name" = x && missing_opts="$missing_opts --test-name" ++test x"$log_file" = x && missing_opts="$missing_opts --log-file" ++test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" ++if test x"$missing_opts" != x; then ++ usage_error "the following mandatory options are missing:$missing_opts" ++fi ++ ++if test $# -eq 0; then ++ usage_error "missing argument" ++fi ++ ++if test $color_tests = yes; then ++ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. ++ red='[0;31m' # Red. ++ grn='[0;32m' # Green. ++ lgn='[1;32m' # Light green. ++ blu='[1;34m' # Blue. ++ mgn='[0;35m' # Magenta. ++ std='[m' # No color. ++else ++ red= grn= lgn= blu= mgn= std= ++fi ++ ++do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' ++trap "st=129; $do_exit" 1 ++trap "st=130; $do_exit" 2 ++trap "st=141; $do_exit" 13 ++trap "st=143; $do_exit" 15 ++ ++# Test script is run here. ++top_srcdir="`dirname $0`/.." ++tests_dir="${top_srcdir}/tests" ++ ++USE_VALGRIND="`printenv USE_VALGRIND`" ++ ++if test "x${USE_VALGRIND}" = "x1"; then ++ ${top_srcdir}/libtool --mode=execute valgrind \ ++ --leak-check=full \ ++ --show-reachable=no \ ++ --error-exitcode=1 \ ++ --suppressions=$tests_dir/libnice.supp \ ++ --num-callers=30 "$@" >$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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 philip.withnall@collabora.co.uk +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 @@ + <title>Index of new symbols in 0.1.14</title> + <xi:include href="xml/api-index-0.1.14.xml">xi:fallback/</xi:include> + </index> ++ <index role="0.1.15"> ++ <title>Index of new symbols in 0.1.15</title> ++ <xi:include href="xml/api-index-0.1.15.xml">xi:fallback/</xi:include> ++ </index> + <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + </part> + </book> +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 + <SUBSECTION Standard> + 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?= olivier.crete@collabora.com +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 <config.h> +-#endif +- +-#include <string.h> +- +-#include <nice/nice.h> +-#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?= olivier.crete@collabora.com +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 philip.withnall@collabora.co.uk +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 olivier.crete@collabora.com ++ * ++ * 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 <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include "socket/socket.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++ ++ ++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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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 withnall@endlessm.com +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 withnall@endlessm.com +Reviewed-by: Olivier Crête olivier.crete@collabora.com +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 fabrice@bellet.info +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 <glib-object.h>\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 <config.h>\n#include <glib-object.h>\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. ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * 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() ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * 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 fabrice@bellet.info +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 <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#include <gio/gio.h> ++#include <agent.h> ++ ++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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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?= olivier.crete@collabora.com +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() +- * <para> See also: #NiceNominationMode </para> ++ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * + * 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?= olivier.crete@collabora.com +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. + * <para> See also: #NiceNominationMode </para> + * +- * 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() + * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * +- * 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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 withnall@endlessm.com +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 withnall@endlessm.com +--- + 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 withnall@endlessm.com +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 withnall@endlessm.com +--- + 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 <gst/check/gstcheck.h> +-#include <nice/agent.h> ++#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 withnall@endlessm.com +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 withnall@endlessm.com +Reviewed-by: Olivier Crête olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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?= mparisdiaz@gmail.com +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 <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#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 kakaroto@kakaroto.homelinux.net +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 kakaroto@kakaroto.homelinux.net +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 kakaroto@kakaroto.homelinux.net +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 jozsef@discordapp.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 fabrice@bellet.info +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 olivier.crete@collabora.com +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 jakub.adam@ktknet.cz +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 jakub.adam@ktknet.cz +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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-koji.patch b/libnice-0.1.14-tests-koji.patch index f9feb34..997e712 100644 --- a/libnice-0.1.14-tests-koji.patch +++ b/libnice-0.1.14-tests-koji.patch @@ -22,7 +22,7 @@ index 16988ad..b7b74fa 100644
AC_CONFIG_FILES([ Makefile -@@ -262,7 +262,7 @@ AC_SUBST(gstplugindir) +@@ -263,7 +263,7 @@ AC_SUBST(gstplugindir) AC_SUBST(gstplugin010dir)
AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes) diff --git a/libnice.spec b/libnice.spec index c0960ef..26f66dc 100644 --- a/libnice.spec +++ b/libnice.spec @@ -1,16 +1,20 @@ # disable building of plugin for gstreamer 0.10 %bcond_with gst010
+%global upstream_date 20180504 +%global upstream_rnum 85 +%global upstream_hash 34d6044 + Name: libnice Version: 0.1.14 -Release: 6.20171128gitfb2f1f7%{?dist} +Release: 7.%{upstream_date}git%{upstream_hash}%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz -Patch1: libnice-0.1.14-70-gfb2f1f7.patch +Patch1: libnice-0.1.14-%{upstream_rnum}-g%{upstream_hash}.patch
# make tests compile on i686 Patch2: libnice-0.1.14-tests-i686.patch @@ -150,6 +154,9 @@ make check
%changelog +* Mon May 07 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-7.20180504git34d6044 +- update to 0.1.14-85-g34d6044 (#1541646) + * Mon Apr 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-6.20171128gitfb2f1f7 - temporarily make the upstream test-suite run on Intel arches only - disable test-send-recv, which fails in Koji
commit 368c412d96e96c3adf1163a1bdeadf1ee2dec294 Author: Kamil Dudka kdudka@redhat.com Date: Mon Apr 16 16:09:02 2018 +0200
temporarily make the upstream test-suite run on Intel arches only
diff --git a/libnice.spec b/libnice.spec index 87ef224..c0960ef 100644 --- a/libnice.spec +++ b/libnice.spec @@ -107,8 +107,13 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%check +# Temporarily make the upstream test-suite run on Intel arches only because we +# are getting random crashes in Koji on secondary arches but I have not been +# able to reproduce them locally so far. +%ifarch x86_64 %{ix86} export LD_LIBRARY_PATH="$PWD/nice/.libs" make check +%endif
%post -p /sbin/ldconfig @@ -146,6 +151,7 @@ make check
%changelog * Mon Apr 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-6.20171128gitfb2f1f7 +- temporarily make the upstream test-suite run on Intel arches only - disable test-send-recv, which fails in Koji
* Fri Mar 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-5.20171128gitfb2f1f7
commit d274df7d77937ce5004f9c1c7d3e86fe565e0f28 Author: Kamil Dudka kdudka@redhat.com Date: Mon Apr 16 15:18:17 2018 +0200
disable test-send-recv, which fails in Koji
... due to insufficiently configured network
diff --git a/libnice.spec b/libnice.spec index 4b4202d..87ef224 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@
Name: libnice Version: 0.1.14 -Release: 5.20171128gitfb2f1f7%{?dist} +Release: 6.20171128gitfb2f1f7%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries @@ -80,8 +80,11 @@ developing applications that use %{name}. %patch3 -p1 chmod 0755 scripts/valgrind-test-driver
-# disable test-new-dribble that sometimes hangs indefinitely -sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am +# disable test-new-dribble, which sometimes hangs indefinitely, and +# test-send-recv, which fails in Koji due to insufficiently configured network +sed -e 's/test-new-dribble/#&/' \ + -e 's/test-send-recv/#&/' \ + -i tests/Makefile.am
autoreconf -fiv
@@ -142,6 +145,9 @@ make check
%changelog +* Mon Apr 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-6.20171128gitfb2f1f7 +- disable test-send-recv, which fails in Koji + * Fri Mar 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-5.20171128gitfb2f1f7 - do not build with -Werror by default - make the build more verbose
commit cb05dbf2151a8ed7c8de960914286bd4e08b5ccf Author: Kamil Dudka kdudka@redhat.com Date: Fri Mar 16 17:20:56 2018 +0100
do not build with -Werror by default
diff --git a/libnice.spec b/libnice.spec index 108c26f..4b4202d 100644 --- a/libnice.spec +++ b/libnice.spec @@ -87,18 +87,7 @@ autoreconf -fiv
%build -export CFLAGS="$RPM_OPT_FLAGS" -%if 0%{?fedora} == 28 -# FIXME: This makes the code compile on Fedora 28 but the resulting binaries -# cannot work properly!!! -CFLAGS="$CFLAGS -Wno-error=cast-function-type" -%endif -%ifarch armv7hl -# FIXME: basically the same problem on armv7hl with both Fedora 27 and 28 -CFLAGS="$CFLAGS -Wno-error=cast-align" -%endif - -%configure --disable-static \ +%configure --enable-compile-warnings=yes --disable-static \ %if %{with gst010} --with-gstreamer-0.10 %else @@ -154,6 +143,7 @@ make check
%changelog * Fri Mar 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-5.20171128gitfb2f1f7 +- do not build with -Werror by default - make the build more verbose
* Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7
commit 288dc3b59279d38e0d188f838efaa9fe3c466aad Author: Kamil Dudka kdudka@redhat.com Date: Fri Mar 16 17:18:56 2018 +0100
make the build more verbose
... mainly to see compiler flags
diff --git a/libnice.spec b/libnice.spec index bb22cea..108c26f 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@
Name: libnice Version: 0.1.14 -Release: 4.20171128gitfb2f1f7%{?dist} +Release: 5.20171128gitfb2f1f7%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries @@ -106,7 +106,7 @@ CFLAGS="$CFLAGS -Wno-error=cast-align" %endif 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 %{?_smp_mflags} +make %{?_smp_mflags} V=1
%install @@ -153,6 +153,9 @@ make check
%changelog +* Fri Mar 16 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-5.20171128gitfb2f1f7 +- make the build more verbose + * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 - enable make check again - make tests pass in Koji
commit 63a783842195268faa634e86d09505769c01db10 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 16:46:01 2018 +0100
enable make check again
diff --git a/libnice.spec b/libnice.spec index f341e45..bb22cea 100644 --- a/libnice.spec +++ b/libnice.spec @@ -78,15 +78,13 @@ developing applications that use %{name}. %patch1 -p1 %patch2 -p1 %patch3 -p1 +chmod 0755 scripts/valgrind-test-driver
# disable test-new-dribble that sometimes hangs indefinitely sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am
autoreconf -fiv
-%check -#make check -
%build export CFLAGS="$RPM_OPT_FLAGS" @@ -116,6 +114,11 @@ make install DESTDIR=$RPM_BUILD_ROOT find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
+%check +export LD_LIBRARY_PATH="$PWD/nice/.libs" +make check + + %post -p /sbin/ldconfig
@@ -151,6 +154,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- enable make check again - make tests pass in Koji - disable test-new-dribble that sometimes hangs indefinitely - make tests compile on i686
commit 8535255eda778d7739e2358b2dac12e94991afe3 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 19:16:26 2018 +0100
make tests pass in Koji
diff --git a/libnice-0.1.14-tests-koji.patch b/libnice-0.1.14-tests-koji.patch new file mode 100644 index 0000000..f9feb34 --- /dev/null +++ b/libnice-0.1.14-tests-koji.patch @@ -0,0 +1,36 @@ +From 527c30ba453753e75d3d31be29a277ea6adc17c0 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka kdudka@redhat.com +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 +@@ -262,7 +262,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.spec b/libnice.spec index e937514..f341e45 100644 --- a/libnice.spec +++ b/libnice.spec @@ -15,6 +15,9 @@ Patch1: libnice-0.1.14-70-gfb2f1f7.patch # make tests compile on i686 Patch2: libnice-0.1.14-tests-i686.patch
+# make tests pass in Koji +Patch3: libnice-0.1.14-tests-koji.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: glib2-devel @@ -74,6 +77,7 @@ developing applications that use %{name}. %setup -q %patch1 -p1 %patch2 -p1 +%patch3 -p1
# disable test-new-dribble that sometimes hangs indefinitely sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am @@ -147,6 +151,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- make tests pass in Koji - disable test-new-dribble that sometimes hangs indefinitely - make tests compile on i686 - make the package build on armv7hl
commit d99a0c0fb4cc3024b35b03a3da993be3eed43630 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 23:03:38 2018 +0100
disable test-new-dribble that sometimes hangs indefinitely
diff --git a/libnice.spec b/libnice.spec index b8c13cf..e937514 100644 --- a/libnice.spec +++ b/libnice.spec @@ -74,6 +74,10 @@ developing applications that use %{name}. %setup -q %patch1 -p1 %patch2 -p1 + +# disable test-new-dribble that sometimes hangs indefinitely +sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am + autoreconf -fiv
%check @@ -143,6 +147,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- disable test-new-dribble that sometimes hangs indefinitely - make tests compile on i686 - make the package build on armv7hl - make the package build on Fedora 28
commit 554edd4e12901c7dc91d9601d7d9a673515b26c6 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 18:05:03 2018 +0100
make tests/test-pseudotcp.c compile on i686
diff --git a/libnice-0.1.14-tests-i686.patch b/libnice-0.1.14-tests-i686.patch new file mode 100644 index 0000000..6558549 --- /dev/null +++ b/libnice-0.1.14-tests-i686.patch @@ -0,0 +1,48 @@ +From 3f8364b41207d8c26d3d3be518a7d9ebf4243b92 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka kdudka@redhat.com +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.spec b/libnice.spec index 17156df..b8c13cf 100644 --- a/libnice.spec +++ b/libnice.spec @@ -12,6 +12,9 @@ URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz Patch1: libnice-0.1.14-70-gfb2f1f7.patch
+# make tests compile on i686 +Patch2: libnice-0.1.14-tests-i686.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: glib2-devel @@ -70,6 +73,7 @@ developing applications that use %{name}. %prep %setup -q %patch1 -p1 +%patch2 -p1 autoreconf -fiv
%check @@ -139,6 +143,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- make tests compile on i686 - make the package build on armv7hl - make the package build on Fedora 28 - avoid build failure if gstreamer-plugins-base-devel is installed
commit 729f8ecea839d9dcc79fbc0d2f92ced1c9925396 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 19:13:28 2018 +0100
make the package build on armv7hl
FIXME: The resulting binaries cannot work properly!!!
diff --git a/libnice.spec b/libnice.spec index b51908b..17156df 100644 --- a/libnice.spec +++ b/libnice.spec @@ -83,6 +83,10 @@ export CFLAGS="$RPM_OPT_FLAGS" # cannot work properly!!! CFLAGS="$CFLAGS -Wno-error=cast-function-type" %endif +%ifarch armv7hl +# FIXME: basically the same problem on armv7hl with both Fedora 27 and 28 +CFLAGS="$CFLAGS -Wno-error=cast-align" +%endif
%configure --disable-static \ %if %{with gst010} @@ -135,6 +139,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- make the package build on armv7hl - make the package build on Fedora 28 - avoid build failure if gstreamer-plugins-base-devel is installed - move autoreconf invocation to %%prep
commit 6eb057143c035b930de9e99ba8376580f7d6762b Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 17:48:10 2018 +0100
make the package build on Fedora 28
FIXME: The resulting binaries cannot work properly!!!
diff --git a/libnice.spec b/libnice.spec index 1575942..b51908b 100644 --- a/libnice.spec +++ b/libnice.spec @@ -77,6 +77,13 @@ autoreconf -fiv
%build +export CFLAGS="$RPM_OPT_FLAGS" +%if 0%{?fedora} == 28 +# FIXME: This makes the code compile on Fedora 28 but the resulting binaries +# cannot work properly!!! +CFLAGS="$CFLAGS -Wno-error=cast-function-type" +%endif + %configure --disable-static \ %if %{with gst010} --with-gstreamer-0.10 @@ -128,6 +135,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- make the package build on Fedora 28 - avoid build failure if gstreamer-plugins-base-devel is installed - move autoreconf invocation to %%prep - use Name Version Release that explicitly identifies an SCM snapshot (#1541646)
commit 15e5f2042d406591e98abff299951b3c3a562e33 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 16:50:16 2018 +0100
avoid build failure if gstreamer-plugins-base-devel is installed
diff --git a/libnice.spec b/libnice.spec index d6bb439..1575942 100644 --- a/libnice.spec +++ b/libnice.spec @@ -77,7 +77,12 @@ autoreconf -fiv
%build -%configure --disable-static +%configure --disable-static \ +%if %{with gst010} + --with-gstreamer-0.10 +%else + --without-gstreamer-0.10 +%endif 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 %{?_smp_mflags} @@ -123,6 +128,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- avoid build failure if gstreamer-plugins-base-devel is installed - move autoreconf invocation to %%prep - use Name Version Release that explicitly identifies an SCM snapshot (#1541646)
commit 0acfa758ecc36ea8b0049fb6f34507a43e769ab0 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 16:01:32 2018 +0100
move autoreconf invocation to %prep
diff --git a/libnice.spec b/libnice.spec index a1850c8..d6bb439 100644 --- a/libnice.spec +++ b/libnice.spec @@ -70,13 +70,13 @@ developing applications that use %{name}. %prep %setup -q %patch1 -p1 +autoreconf -fiv
%check #make check
%build -autoreconf -f -i %configure --disable-static 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 @@ -123,6 +123,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- move autoreconf invocation to %%prep - use Name Version Release that explicitly identifies an SCM snapshot (#1541646)
* Fri Feb 09 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-3
commit 794e7ad7d94e48b6a20f923c99c68daf642f6095 Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 15:59:27 2018 +0100
Related: #1541646 - use Name Version Release that explicitly identifies an SCM snapshot
diff --git a/libnice.spec b/libnice.spec index 63e60a2..a1850c8 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@
Name: libnice Version: 0.1.14 -Release: 3%{?dist} +Release: 4.20171128gitfb2f1f7%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries @@ -122,6 +122,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog +* Fri Feb 09 2018 Kamil Dudka kdudka@redhat.com - 0.1.14-4.20171128gitfb2f1f7 +- use Name Version Release that explicitly identifies an SCM snapshot (#1541646) + * Fri Feb 09 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-3 - update to 0.1.14-70-gfb2f1f7 with alternate server fixes for SIPE - add autoreconf build step
commit f1ca1fa20f059e7c39e252302c3edb61acb7bf6d Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 15:48:29 2018 +0100
libnice-0.1.14-70-gfb2f1f7.patch: now produced by git format-patch
... so that we are able to easily bisect over the upstream commits
diff --git a/libnice-0.1.14-20171128.patch b/libnice-0.1.14-20171128.patch deleted file mode 100644 index ff2d2ae..0000000 --- a/libnice-0.1.14-20171128.patch +++ /dev/null @@ -1,6711 +0,0 @@ -commit fb2f1f77a31baa91968fc81c205f980b6913f403 -Author: Youness Alaoui kakaroto@kakaroto.homelinux.net -Date: Tue Nov 28 16:05:18 2017 -0500 - - 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). - -commit 4172d48852ecd1c86cc7bd4665b23697603d1eed -Author: Youness Alaoui kakaroto@kakaroto.homelinux.net -Date: Tue Nov 28 15:14:11 2017 -0500 - - 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. - -commit 59fcf95d505c3995f858b826d10cd48321ed383e -Author: Youness Alaoui kakaroto@kakaroto.homelinux.net -Date: Mon Nov 27 17:07:02 2017 -0500 - - 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. - -commit 17f30e4465efe9533799b02d6f95feeaf0f2748c -Author: Miguel París mparisdiaz@gmail.com -Date: Wed Nov 8 16:26:47 2017 +0000 - - 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 - -commit c63349894b3fe974494453a883dfb5ad05df5a46 -Author: Fabrice Bellet fabrice@bellet.info -Date: Thu Nov 23 18:31:31 2017 +0100 - - Makefile: really enable debug for tests - - Differential Revision: https://phabricator.freedesktop.org/D1888 - -commit 02216a6766caccb652387d5ee19686149eedbc93 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Nov 21 15:12:45 2017 +0100 - - 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 - -commit fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 -Author: Philip Withnall withnall@endlessm.com -Date: Thu Aug 3 12:20:32 2017 +0100 - - stun: Fix FD leak in test/utility code - - https://phabricator.freedesktop.org/T7798 - - Signed-off-by: Philip Withnall withnall@endlessm.com - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D1819 - -commit 4c4834ab634f735145c8f758a22cbdd9cab79bac -Author: Philip Withnall withnall@endlessm.com -Date: Tue Sep 12 13:23:53 2017 +0100 - - tests: Fix agent.h header inclusion in test-gstreamer.c - - Spotted by Lukas Gradl on the mailing list. - - Signed-off-by: Philip Withnall withnall@endlessm.com - -commit 1a1803a45778000720c93d91060cedeb19124a27 -Author: Philip Withnall withnall@endlessm.com -Date: Tue Sep 12 13:23:31 2017 +0100 - - tests: Fix copyright dates in test-gstreamer.c - - This code is not 1000 years old. - - Signed-off-by: Philip Withnall withnall@endlessm.com - -commit 14102d44449d2eb4148588ce54fa897fa13b87ad -Author: Fabrice Bellet fabrice@bellet.info -Date: Sun Jul 2 16:02:09 2017 +0200 - - 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. - -commit 72dd26a3368d3506fe8faca7067a02784fb5f0fd -Author: Fabrice Bellet fabrice@bellet.info -Date: Wed Jun 28 12:06:48 2017 +0200 - - 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 - -commit 6fe64fdbc53ab87dffd79972f492665cff14c0a0 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Jun 27 11:01:14 2017 +0200 - - 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 - -commit 36f306f4a95f1c2b3e9c584b5a645a78e231c020 -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Jun 26 21:41:44 2017 +0200 - - 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 - -commit e860948b5fe3a791119957f26045b8f5159baeff -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Jun 26 21:06:36 2017 +0200 - - 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 - -commit ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Jun 26 20:41:49 2017 +0200 - - 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 - -commit 25be00271a4c8c684a2d435d29ae0811dbf5e21c -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Jun 26 20:36:35 2017 +0200 - - conncheck: reorder some chunks of code - - With this patch we simplify the levels of code indentation. - - Differential Revision: https://phabricator.freedesktop.org/D1758 - -commit 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Sep 5 14:50:29 2017 -0400 - - agent: Set error if it isn't set - -commit dbaf8f5ccd76089e340883887c7e08e6c04de80a -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 13:22:21 2016 +0200 - - conncheck: improve role conflict debug - - This patch displays explicitely the controlling or controlled - role of the agent. - - Differential Revision: https://phabricator.freedesktop.org/D874 - -commit 9f800d3597767855accccc592c34bc4e945f5bd5 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Jun 21 20:42:57 2017 -0400 - - configure: Remove -Wswitch-enum - - Creates useless warnings when other libraries change. - - https://phabricator.freedesktop.org/T7770 - -commit 63d273cea42def3567701ad9feab91f63cf9345f -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Feb 11 22:16:48 2016 -0500 - - component: Use non-GClosure dummy callbacks - - GClosures are not that cheap to setup - -commit 2c50d73b82f2ec2422a8e0ea393194486c193c64 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Feb 10 23:20:39 2016 -0500 - - agent: Don't crash if recv cancelled without a GError - -commit dcb0d647174416a292492f8deca86f83a2ef124c -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Jun 21 17:07:17 2017 -0400 - - Repleace UNRELEASED with 0.1.15 - -commit e3ddaa285e389baf3f26cfb6964919718a8f6a00 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Jun 21 16:55:32 2017 -0400 - - agent: Adjust the nice_agent_new_full() to use flags - - This makes it easier to read and more extensible. - -commit c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 -Author: Fabrice Bellet fabrice@bellet.info -Date: Sun Jun 18 10:12:58 2017 +0200 - - agent: remove spurious newlines - - Differential Revision: https://phabricator.freedesktop.org/D1756 - -commit b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 -Author: Fabrice Bellet fabrice@bellet.info -Date: Sun May 28 22:20:36 2017 +0200 - - stun: fix gcc7 implicit fallthrough warning - - Differential Revision: https://phabricator.freedesktop.org/D1754 - -commit 195db6b344fc4f9fadc39419dfeec2fc14b23fac -Author: Fabrice Bellet fabrice@bellet.info -Date: Fri Jul 15 23:31:42 2016 +0200 - - 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 - -commit 07366a5bca7e4818b8df29d9c7c220da8f752547 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Jun 21 21:47:42 2016 +0200 - - 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 - -commit 95f8805eb7b77755337e28daf1f134587d42b35f -Author: Fabrice Bellet fabrice@bellet.info -Date: Thu Jun 16 17:32:39 2016 +0200 - - 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 - -commit d516fca1b0e0a6606afec797bdc0690104e779a9 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Jun 14 21:32:26 2016 +0200 - - 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 - -commit f19d209decac432a1597d84c3d5809d2208f7457 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Jun 14 21:20:49 2016 +0200 - - 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 - -commit 59fe48517c0b7db77b99183d31fdd84b55adb5d4 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Jun 14 21:12:16 2016 +0200 - - 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 - -commit 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Jun 14 21:04:49 2016 +0200 - - 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 - -commit 8fa648a15a6700d08165fe97a09f5c068abae1e6 -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Apr 11 13:13:51 2016 +0200 - - 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 - -commit 11d4e37a030eb144a355dc26c705ef5aa5a975a7 -Author: Fabrice Bellet fabrice@bellet.info -Date: Fri Apr 1 17:31:44 2016 +0200 - - 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 - -commit 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 12:56:28 2016 +0200 - - 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 - -commit afd8d41bb34afb3864e838ef79026ae4ef15c0d4 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 13:32:49 2016 +0200 - - 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 - -commit 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 13:30:04 2016 +0200 - - 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 - -commit 9103a5f2e184211fc160d1d3070ce4d043c71ff0 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 19 18:16:26 2016 +0200 - - 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 - -commit 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 13:25:16 2016 +0200 - - 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 - -commit 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 19 17:59:27 2016 +0200 - - 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 - -commit ead3453d04fc70865d176ab073636f8b9078cbbc -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 13:20:38 2016 +0200 - - conncheck: invoke the debug dump in more places - - Differential Revision: https://phabricator.freedesktop.org/D1123 - -commit 15c0546f624113b8c0546a1f883a48bff7020f1b -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 19 17:06:32 2016 +0200 - - 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 - -commit 58d061df8f5425dc1add9c6030a2f891ebda4616 -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Mar 7 16:35:09 2016 +0100 - - 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 - -commit 0636f9addc041cf93c4ff4eaa351b1768d48a32e -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 19 13:12:48 2016 +0200 - - 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 - -commit a602ff57aae6a6afdeab843954c48e6fb5d82d31 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 12 13:02:45 2016 +0200 - - 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 - -commit 3a58ba6120b188d78c5709e0349c0346bfa21c1a -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Feb 1 11:10:21 2016 +0100 - - 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 - -commit 7a2c1edf502849a868b6f1026e8e2c343dee4ded -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Jun 6 22:24:50 2016 +0200 - - 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 - -commit 8bb210c5af4bcaf342d7fa4fef6034269e976532 -Author: Fabrice Bellet fabrice@bellet.info -Date: Thu Jun 9 23:28:43 2016 +0200 - - 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 - -commit 80c613699786567fd93db74377138600794a86e0 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Jun 8 16:34:21 2017 -0400 - - agent: Use base_addr to generate rport in SDP - - Reported by Capricornus (zhushengliang) - - https://phabricator.freedesktop.org/T7763 - -commit b4abda09c79e4ce372a3771300abf568c85c7ff5 -Author: Fabrice Bellet fabrice@bellet.info -Date: Thu Apr 21 18:18:59 2016 +0200 - - 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 - -commit d5446a72233eab8501be0b3fb9060c8be3ba034b -Author: Philip Withnall withnall@endlessm.com -Date: Mon May 1 08:51:40 2017 +0100 - - examples: Stop installing the examples - - 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 withnall@endlessm.com - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D1737 - -commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 -Author: Fabrice Bellet fabrice@bellet.info -Date: Tue Apr 5 21:32:39 2016 +0200 - - 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 - -commit f6f704c5e8d2193bc67ba2b697c77694e1698c43 -Author: Fabrice Bellet fabrice@bellet.info -Date: Thu Jun 9 22:22:33 2016 +0200 - - 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 - -commit b0538d8c51f65019867b56a45cf90a70bef38f01 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 11 18:31:21 2017 -0400 - - agent: Ignore remote candidate of non-accepted types - - If we disable ice-tcp or ice-udp, ignore the remote - candidates for those types. - -commit f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 11 16:42:55 2017 -0400 - - 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. - -commit 8fc22b0034d04cbc222e0637152b1cee2879eef3 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Apr 5 17:43:26 2017 -0400 - - tests_: Add test to verify that only packets from validated addresses pass - - https://phabricator.freedesktop.org/T104 - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D1717 - -commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 21:27:39 2017 -0400 - - 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 - -commit 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 14:41:51 2017 -0400 - - 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 philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D1715 - -commit 10c557f23f8337f1304fff27bd85d2eb713cb249 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Apr 5 17:01:35 2017 -0400 - - test-credentials: Fix leak - -commit ae6d939e48366b80570d713b83334191b0982e71 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 20:34:05 2017 -0400 - - debug: Use libnice-verbose, not libnice-nice-verbose - -commit efc6a9be8cb34c899f0454c32e8a1e62b38df474 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 18:42:57 2017 -0400 - - tests: Use automake test-driver for valgrind - - This fixes the valgrind integration with the new test drivers. - -commit 4e605885c9dcaeb3ee443ec902c9c9189b19043f -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 16:16:46 2017 -0400 - - agent: Remove impossible case - -commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 16:16:05 2017 -0400 - - 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. - -commit cd255bddc7fa0ddae056b5358a22b380c4eefc42 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 15:24:43 2017 -0400 - - udp-turn: Add some const to internal APIs - -commit db05e8b0fdc713df93cd6a4c3914e5aee38b2391 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 12:30:27 2017 -0400 - - Make clang-analyzer happy - - Various little things, none of which should make a functional difference. - -commit 0672758b9621801c8f0d9e3c920370983b267a68 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 12:29:29 2017 -0400 - - 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. - -commit 0de1657e4d15d4f1911ab1fad84ea23e7013070f -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Apr 4 12:25:50 2017 -0400 - - 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 - -commit 59ce41dfb837adf4222b25490cde2e394384ad15 -Author: Miguel París Díaz mparisdiaz@gmail.com -Date: Fri Mar 31 20:20:38 2017 -0400 - - conncheck: consider answer received when remote credentials are set - - 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 -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 <glib-object.h>\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 <config.h>\n#include <glib-object.h>\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 4d8c9b8..7269be0 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) */ - - -@@ -114,6 +113,27 @@ 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 */ -+ -+/* 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) -+ -+/* 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 */ -@@ -126,10 +146,14 @@ 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 */ -+ 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 */ -@@ -164,6 +188,10 @@ struct _NiceAgent - guint16 rfc4571_expecting_length; - gboolean use_ice_udp; - 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 555fd16..0773c53 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. */ -@@ -113,6 +114,10 @@ enum - PROP_BYTESTREAM_TCP, - PROP_KEEPALIVE_CONNCHECK, - PROP_FORCE_RELAY, -+ PROP_STUN_MAX_RETRANSMISSIONS, -+ PROP_STUN_INITIAL_TIMEOUT, -+ PROP_STUN_RELIABLE_TIMEOUT, -+ PROP_NOMINATION_MODE, - }; - - -@@ -400,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", -@@ -436,6 +448,24 @@ nice_agent_class_init (NiceAgentClass *klass) - 0, /* default set in init */ - 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. -+ * <para> See also: #NiceNominationMode </para> -+ * -+ * Since: 0.1.15 -+ */ -+ 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: - * -@@ -708,6 +738,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: 0.1.15 -+ */ -+ -+ 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: 0.1.15 -+ */ -+ -+ 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: 0.1.15 -+ */ -+ -+ 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 */ - - /** -@@ -1013,6 +1113,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) - nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker); - } - -+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) - { -@@ -1022,7 +1163,9 @@ 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; - - agent->discovery_list = NULL; - agent->discovery_unsched_items = 0; -@@ -1071,6 +1214,24 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) - } - - -+NICEAPI_EXPORT NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ NiceAgentOption flags) -+{ -+ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, -+ "compatibility", compat, -+ "main-context", ctx, -+ "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; -+} -+ -+ - static void - nice_agent_get_property ( - GObject *object, -@@ -1101,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: -@@ -1117,6 +1278,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; -@@ -1187,6 +1352,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); - } -@@ -1294,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: -@@ -1309,6 +1486,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); -@@ -1374,6 +1555,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); - } -@@ -1552,7 +1745,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) { -@@ -1890,6 +2082,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; - -@@ -3113,6 +3316,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; - -@@ -3195,6 +3405,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; - -@@ -3240,6 +3463,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 +3567,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); - } -@@ -3423,7 +3646,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. */ -@@ -3484,8 +3708,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 { -@@ -3500,7 +3724,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 -@@ -3509,9 +3733,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 -@@ -3524,7 +3748,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; - -@@ -3537,7 +3761,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)) { -@@ -3545,8 +3769,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); - } -@@ -3590,8 +3814,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; - } -@@ -3599,23 +3823,26 @@ 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) { -+ 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); -@@ -3679,8 +3906,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 ( -@@ -3712,6 +3937,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; - } - } -@@ -3722,6 +3948,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", 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) && -@@ -4085,7 +4328,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 -@@ -4245,7 +4491,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - "Component removed during call."); - - component = NULL; -- error_reported = TRUE; - - goto recv_error; - } -@@ -4734,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; - -@@ -5665,7 +5915,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); -diff --git a/agent/agent.h b/agent/agent.h -index 47c4d5a..520c4c5 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -377,6 +377,45 @@ 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: 0.1.15 -+ */ -+typedef enum -+{ -+ NICE_NOMINATION_MODE_REGULAR = 0, -+ NICE_NOMINATION_MODE_AGGRESSIVE, -+} 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: 0.1.15 -+ */ -+typedef enum { -+ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, -+ NICE_AGENT_OPTION_RELIABLE = 1 << 1, -+ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, -+} NiceAgentOption; - - /** - * NiceAgentRecvFunc: -@@ -428,6 +467,26 @@ nice_agent_new (GMainContext *ctx, NiceCompatibility compat); - 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 -+ * @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() -+ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> -+ * -+ * Since: 0.1.15 -+ * -+ * Returns: The new agent GObject -+ */ -+NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ NiceAgentOption flags); -+ - /** - * nice_agent_add_local_address: - * @agent: The #NiceAgent Object -@@ -447,7 +506,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/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/agent/component.c b/agent/component.c -index 32f7463..6eee90e 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); -@@ -380,7 +387,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) { -@@ -435,6 +442,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 +523,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; - } - -@@ -991,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) - { -@@ -1013,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); - -@@ -1107,6 +1133,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); -@@ -1225,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 = -@@ -1370,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); -@@ -1421,3 +1450,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", 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 dda2f2f..c8a4edf 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -64,8 +64,8 @@ - - 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 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, - guint component_id, NiceCandidate *remote, NiceCandidate *local, -@@ -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) - { -@@ -84,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) - { -@@ -98,8 +111,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: -@@ -107,6 +118,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) - { -@@ -130,12 +189,15 @@ 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 ()) - return; - -+ g_get_current_time (&now); -+ - #define PRIORITY_LEN 32 - - nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", -@@ -148,26 +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 - stun_timer_remainder (timer), 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)" : ""); -+ } - } - } - } -@@ -181,6 +251,8 @@ 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); - 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); -@@ -209,6 +281,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) - return pair; - } - -+/* -+ * 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. - */ -@@ -218,7 +373,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) -@@ -228,6 +382,57 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - return NULL; - } - -+/* -+ * 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 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. - * -@@ -235,8 +440,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - */ - 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); -@@ -246,58 +449,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; - } - - /* -@@ -314,7 +514,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); -@@ -334,60 +533,147 @@ 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); -+/* -+ * 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; -+ -+ if (stun->message.buffer != NULL) { -+ stun_message_id (&stun->message, id); -+ stun_agent_forget_transaction (&component->stun_agent, id); -+ } -+} -+ -+static void -+priv_free_stun_transaction (gpointer data) -+{ -+ g_slice_free (StunTransaction, data); -+} -+ -+/* -+ * Remove a STUN transaction from a pair, and forget it -+ * from the related component stun agent. -+ * -+ * @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 void -+priv_remove_stun_transaction (CandidateCheckPair *pair, -+ StunTransaction *stun, NiceComponent *component) -+{ -+ 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); -- -- if (p->stun_message.buffer != NULL) { -- stun_message_id (&p->stun_message, id); -- stun_agent_forget_transaction (&component->stun_agent, id); -- } -- -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+ priv_free_all_stun_transactions (p, component); - } - - /* -@@ -398,78 +684,183 @@ 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) - { - 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, *j; -+ 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) { - CandidateCheckPair *p = i->data; -+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ NiceComponent *component; -+ StunTransaction *stun; - -- 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: -- { -- /* case: error, abort processing */ -- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -- 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"); -- -- break; -- } -- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -- { -- /* case: not ready, so schedule a new timeout */ -- unsigned int 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); -+ if (p->stun_transactions == NULL) -+ continue; - -- agent_socket_send (p->sockptr, &p->remote->addr, -- stun_message_length (&p->stun_message), -- (gchar *)p->stun_buffer); -+ if (!agent_find_component (agent, p->stream_id, p->component_id, -+ NULL, &component)) -+ continue; - -+ /* The first stun transaction of the list may eventually be -+ * retransmitted, other stun transactions just have their -+ * timer updated. -+ */ - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ j = p->stun_transactions->next; - -- *stun_transmitted = TRUE; -- return TRUE; -- } -- case STUN_USAGE_TIMER_RETURN_SUCCESS: -- { -- unsigned int timeout = stun_timer_remainder (&p->timer); -+ /* process all stun transactions except the first one */ -+ while (j) { -+ StunTransaction *s = j->data; -+ GSList *next = j->next; - -- /* 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; -- } -+ 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: -- /* Nothing to do. */ - break; -- } - } -+ j = next; - } - -+ if (p->state != NICE_CHECK_IN_PROGRESS) -+ continue; -+ -+ /* 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 : 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: -+ /* 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) -+ goto timer_return_timeout; -+ -+ /* case: not ready, so schedule a new timeout */ -+ timeout = stun_timer_remainder (&stun->timer); -+ -+ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -+ "(timer=%d/%d %d/%dms).", -+ agent, p, -+ 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 (&stun->message), -+ (gchar *)stun->buffer); -+ -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ 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 (&stun->timer); -+ -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); -+ -+ keep_timer_going = TRUE; -+ break; -+ default: -+ /* Nothing to do. */ -+ break; -+ } -+ } -+ -+ /* 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_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Waiting state"); -+ 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) { -+ 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); -+ 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) -@@ -495,12 +886,102 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - 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 (agent->controlling_mode) { -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && -+ 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 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, -+ * 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; -+ 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) { -+ 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++; -+ } -+ } -+ -+ 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 -+ */ -+ 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) { -+ /* 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->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 */ -+ } -+ } -+ } -+ } -+ } else if (agent->controlling_mode) { - GSList *component_item; - - for (component_item = stream->components; component_item; -@@ -515,7 +996,9 @@ 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); -+ keep_timer_going = TRUE; - break; /* move to the next component */ - } - } -@@ -542,6 +1025,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; - } - - -@@ -557,75 +1041,83 @@ 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; -+ /* 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: first initiate a conncheck with a pair from the triggered list */ -+ /* 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_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; - } - -- /* step: when the triggered list is empty, -- * find the highest priority waiting check and send it */ -+ /* step: process ongoing STUN transactions and -+ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" -+ */ - 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 (priv_conn_check_tick_stream (stream, agent)) -+ keep_timer_going = TRUE; -+ if (priv_conn_check_tick_stream_nominate (stream, agent)) -+ keep_timer_going = TRUE; - } - -- if (pair) { -- priv_conn_check_initiate (agent, pair); -- return 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); -+ } -+ } - } - -- /* step: when there's no pair in the Waiting state, -- * unfreeze a new pair and check it -+ /* 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. - */ -- res = priv_conn_check_unfreeze_next (agent); -- -- for (i = agent->streams; i ; i = i->next) { -- NiceStream *stream = i->data; -+ 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); - -- 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 (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); -@@ -635,6 +1127,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"); - -@@ -645,9 +1138,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) -@@ -888,8 +1382,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 +1611,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, -@@ -1213,154 +1709,136 @@ 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); -+ 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); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); - } - } - } - - --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 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); -+ -+ 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); -+ } -+ } - } - } - } -@@ -1368,12 +1846,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; -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); -+ 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; -+ } - } - - /* -@@ -1381,7 +1859,7 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice - * 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; -@@ -1389,22 +1867,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; - } - - /* -@@ -1444,15 +1922,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*/ -@@ -1476,6 +1957,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; - -@@ -1483,16 +1966,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); -@@ -1525,7 +2014,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); - } - } - } -@@ -1535,7 +2023,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. */ -@@ -1564,24 +2052,59 @@ 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) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); -- pair->nominated = TRUE; -+ /* 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->state == NICE_CHECK_SUCCEEDED && -+ pair->discovered_pair != NULL) { -+ pair = pair->discovered_pair; -+ g_assert (pair->state == NICE_CHECK_DISCOVERED); -+ } -+ -+ /* 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); -+ } -+ -+ 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; -+ } -+ - 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); -+ 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 */ - 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); - } - } - } -@@ -1624,7 +2147,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; -@@ -1662,8 +2185,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->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); - -@@ -1675,7 +2196,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; -@@ -1709,7 +2231,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, -@@ -1781,6 +2303,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; - -@@ -1815,6 +2346,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; -@@ -1838,8 +2381,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); - } - -@@ -2057,30 +2599,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 -@@ -2114,6 +2654,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)) -@@ -2135,99 +2676,119 @@ 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 "), " -- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, -+ "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, -- pair->prflx_priority, controlling); -+ pair->prflx_priority, -+ controlling ? "controlling" : "controlled"); - } - -- 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) { -- 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); -+ return -1; -+ } - -- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -- pair->stun_message.buffer); -+ stun = priv_add_stun_transaction (pair); - -- if (agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- g_free (password); -- } -+ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -+ &stun->message, stun->buffer, sizeof(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 (buffer_len > 0) { -- if (nice_socket_is_reliable(pair->sockptr)) { -- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -- } else { -- stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -- } -+ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -+ stun->message.buffer); - -- /* 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 (agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ g_free (password); -+ } - -- 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 (buffer_len == 0) { -+ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -+ priv_remove_stun_transaction (pair, stun, component); -+ return -1; -+ } - -- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { -- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -- &pair->remote->addr); -- } -+ if (nice_socket_is_reliable(pair->sockptr)) { -+ timeout = agent->stun_reliable_timeout; -+ stun_timer_start_reliable(&stun->timer, timeout); -+ } else { -+ timeout = priv_compute_conncheck_timer (agent, stream); -+ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); -+ } - -- 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; -+ 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 -+ * 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); -+ } - } -- } 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 *)stun->buffer); -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, -+ &pair->remote->addr); - - return 0; - } -@@ -2238,14 +2799,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; -@@ -2257,37 +2818,40 @@ 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 */ -- 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) { -- if (p->state == NICE_CHECK_FROZEN || -- p->state == NICE_CHECK_WAITING) { -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ 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) */ -- if (p->state == NICE_CHECK_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); -+ 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); - } 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++; - } - } - } -+ i = next; - } - - return in_progress; -@@ -2301,17 +2865,17 @@ 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; -+ 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 && -@@ -2322,61 +2886,67 @@ 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 */ - -- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); -+ 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 (%s) (%s) ...", -+ agent, p, p->foundation, priv_state_to_string (p->state)); - -- if (p->state == NICE_CHECK_WAITING || -- 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), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -- p->timer_restarted = TRUE; -- } -- } -- else if (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) { -- 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 */ -- /* 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); -- -- /* 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) { -+ 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); -- 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 -- 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); -+ 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 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) { -+ 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 -+ */ -+ 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: -+ 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; -+ default: -+ break; - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -2394,7 +2964,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, local, remote_cand, NICE_CHECK_WAITING, use_candidate); -+ 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 { -@@ -2447,9 +3019,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); - } -@@ -2509,6 +3079,8 @@ 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; -+ pair->succeeded_pair = parent_pair; - nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; -@@ -2528,7 +3100,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); -@@ -2567,14 +3138,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"); - } - - /* -@@ -2621,13 +3194,25 @@ 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; -+ 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); -- priv_conn_check_unfreeze_related (agent, stream, p); -+ nice_component_add_valid_candidate (component, remote_candidate); - } - 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, -@@ -2635,8 +3220,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 -@@ -2644,13 +3230,23 @@ 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); -+ /* 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" -+ */ -+ p->state = 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); - } - -- /* 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; - } -@@ -2669,134 +3265,199 @@ 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; -- 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 (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; -- } -- -- /* 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); -- priv_conn_check_unfreeze_related (agent, stream, p); -- } else { -- ok_pair = priv_process_response_check_for_reflexive (agent, -- stream, component, p, sockptr, &sockaddr.addr, -- local_candidate, remote_candidate); -- } -- -- -- if (!ok_pair) -- ok_pair = p; -- -- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -- Nominated Flag" (ID-19) */ -- 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); -+ 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); -+ } - -- 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: 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); -- 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; -- } -+ /* 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; -+ -+ /* 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); -+ 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); - } -+ return TRUE; - } - } - -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); -- -- return trans_found; -+ return FALSE; - } - - /* -@@ -2846,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 */ - -@@ -2930,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. -@@ -2982,11 +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; -+ /* 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 */ -@@ -3103,6 +3800,17 @@ 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 addr; -+ -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); -+ -+ if (!nice_address_equal (&addr, &d->server)) { -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); -+ } -+ } - /* check for unauthorized error response */ - if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || - agent->compatibility == NICE_COMPATIBILITY_OC2007 || -@@ -3123,6 +3831,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; -@@ -3619,8 +4328,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; -@@ -3628,21 +4336,21 @@ 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[0]) { - if (local_candidate && - local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { - CandidateCheckPair *pair; - - 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; - } -@@ -3651,13 +4359,15 @@ 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); - -- if (component->remote_candidates == 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 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..e16dc67 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,11 +67,19 @@ typedef enum - NICE_CHECK_SUCCEEDED, - NICE_CHECK_FAILED, - NICE_CHECK_FROZEN, -- NICE_CHECK_CANCELLED, - NICE_CHECK_DISCOVERED, - } 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 - { -@@ -85,15 +92,15 @@ struct _CandidateCheckPair - gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; - NiceCheckState state; - gboolean nominated; -- gboolean controlling; -- gboolean timer_restarted; - gboolean valid; -+ gboolean use_candidate_on_next_check; -+ gboolean mark_nominated_on_response_arrival; -+ 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); -@@ -105,7 +112,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, -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; - } - -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/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/agent/stream.c b/agent/stream.c -index 8121e12..533ff15 100644 ---- a/agent/stream.c -+++ b/agent/stream.c -@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) - return NULL; - } - --/* -- * 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. - */ -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); - -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/configure.ac b/configure.ac -index b39bfe3..16988ad 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]) -@@ -153,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]) - ]) -@@ -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 -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 @@ - <title>Index of new symbols in 0.1.14</title> - <xi:include href="xml/api-index-0.1.14.xml">xi:fallback/</xi:include> - </index> -+ <index role="0.1.15"> -+ <title>Index of new symbols in 0.1.15</title> -+ <xi:include href="xml/api-index-0.1.15.xml">xi:fallback/</xi:include> -+ </index> - <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> - </part> - </book> -diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt -index d377257..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 -@@ -76,6 +78,7 @@ NICE_CANDIDATE_MAX_FOUNDATION - nice_candidate_new - nice_candidate_free - nice_candidate_copy -+nice_candidate_equal_target - <SUBSECTION Standard> - NICE_TYPE_CANDIDATE - nice_candidate_get_type -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 \ -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 -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 http://www.gnu.org/licenses/. -+ -+# 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 bug-automake@gnu.org or send patches to -+# automake-patches@gnu.org. -+ -+# 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 <<END -+Usage: -+ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH -+ [--expect-failure={yes|no}] [--color-tests={yes|no}] -+ [--enable-hard-errors={yes|no}] [--] -+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] -+The '--test-name', '--log-file' and '--trs-file' options are mandatory. -+END -+} -+ -+test_name= # Used for reporting. -+log_file= # Where to save the output of the test script. -+trs_file= # Where to save the metadata of the test run. -+expect_failure=no -+color_tests=no -+enable_hard_errors=yes -+while test $# -gt 0; do -+ case $1 in -+ --help) print_usage; exit $?;; -+ --version) echo "test-driver $scriptversion"; exit $?;; -+ --test-name) test_name=$2; shift;; -+ --log-file) log_file=$2; shift;; -+ --trs-file) trs_file=$2; shift;; -+ --color-tests) color_tests=$2; shift;; -+ --expect-failure) expect_failure=$2; shift;; -+ --enable-hard-errors) enable_hard_errors=$2; shift;; -+ --) shift; break;; -+ -*) usage_error "invalid option: '$1'";; -+ *) break;; -+ esac -+ shift -+done -+ -+missing_opts= -+test x"$test_name" = x && missing_opts="$missing_opts --test-name" -+test x"$log_file" = x && missing_opts="$missing_opts --log-file" -+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" -+if test x"$missing_opts" != x; then -+ usage_error "the following mandatory options are missing:$missing_opts" -+fi -+ -+if test $# -eq 0; then -+ usage_error "missing argument" -+fi -+ -+if test $color_tests = yes; then -+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. -+ red='[0;31m' # Red. -+ grn='[0;32m' # Green. -+ lgn='[1;32m' # Light green. -+ blu='[1;34m' # Blue. -+ mgn='[0;35m' # Magenta. -+ std='[m' # No color. -+else -+ red= grn= lgn= blu= mgn= std= -+fi -+ -+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' -+trap "st=129; $do_exit" 1 -+trap "st=130; $do_exit" 2 -+trap "st=141; $do_exit" 13 -+trap "st=143; $do_exit" 15 -+ -+# Test script is run here. -+top_srcdir="`dirname $0`/.." -+tests_dir="${top_srcdir}/tests" -+ -+USE_VALGRIND="`printenv USE_VALGRIND`" -+ -+if test "x${USE_VALGRIND}" = "x1"; then -+ ${top_srcdir}/libtool --mode=execute valgrind \ -+ --leak-check=full \ -+ --show-reachable=no \ -+ --error-exitcode=1 \ -+ --suppressions=$tests_dir/libnice.supp \ -+ --num-callers=30 "$@" >$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 -- -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index cc3409b..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; -@@ -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) -@@ -1185,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; -@@ -1195,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); -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 */ -diff --git a/stun/usages/bind.c b/stun/usages/bind.c -index 8dd7afc..ee600a0 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; - -@@ -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; -@@ -548,16 +555,22 @@ 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); - - 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); -@@ -571,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; - } -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; - } -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 - -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 -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 7bfe075..30d6f8e 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) - -@@ -45,8 +45,8 @@ check_PROGRAMS = \ - test-send-recv \ - test-socket-is-based-on \ - test-priority \ -- test-mainloop \ - test-fullmode \ -+ test-different-number-streams \ - test-restart \ - test-fallback \ - test-thread \ -@@ -55,7 +55,9 @@ check_PROGRAMS = \ - test-tcp \ - test-icetcp \ - test-credentials \ -- test-turn -+ test-turn \ -+ test-drop-invalid \ -+ test-nomination - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -113,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) -@@ -129,6 +133,10 @@ test_credentials_LDADD = $(COMMON_LDADD) - - 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-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); -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 <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+#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; -+} -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 olivier.crete@collabora.com -+ * -+ * 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 <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include "socket/socket.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+ -+ -+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; -+} -diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c -index 74d7133..f060efc 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 -@@ -34,7 +34,7 @@ - */ - - #include <gst/check/gstcheck.h> --#include <nice/agent.h> -+#include "agent.h" - - #define RTP_HEADER_SIZE 12 - #define RTP_PAYLOAD_SIZE 1024 -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 <config.h> --#endif -- --#include <string.h> -- --#include <nice/nice.h> --#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; --} -- -diff --git a/tests/test-nomination.c b/tests/test-nomination.c -new file mode 100644 -index 0000000..bf21557 ---- /dev/null -+++ b/tests/test-nomination.c -@@ -0,0 +1,263 @@ -+#include <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <gio/gio.h> -+#include <agent.h> -+ -+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, -+ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); -+ -+ ragent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ 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); -+ -+ 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; -+} -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..."); - diff --git a/libnice-0.1.14-70-gfb2f1f7.patch b/libnice-0.1.14-70-gfb2f1f7.patch new file mode 100644 index 0000000..db148cb --- /dev/null +++ b/libnice-0.1.14-70-gfb2f1f7.patch @@ -0,0 +1,10531 @@ +From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= olivier.crete@collabora.com +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?= mparisdiaz@gmail.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 http://www.gnu.org/licenses/. ++ ++# 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 bug-automake@gnu.org or send patches to ++# automake-patches@gnu.org. ++ ++# 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 <<END ++Usage: ++ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH ++ [--expect-failure={yes|no}] [--color-tests={yes|no}] ++ [--enable-hard-errors={yes|no}] [--] ++ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] ++The '--test-name', '--log-file' and '--trs-file' options are mandatory. ++END ++} ++ ++test_name= # Used for reporting. ++log_file= # Where to save the output of the test script. ++trs_file= # Where to save the metadata of the test run. ++expect_failure=no ++color_tests=no ++enable_hard_errors=yes ++while test $# -gt 0; do ++ case $1 in ++ --help) print_usage; exit $?;; ++ --version) echo "test-driver $scriptversion"; exit $?;; ++ --test-name) test_name=$2; shift;; ++ --log-file) log_file=$2; shift;; ++ --trs-file) trs_file=$2; shift;; ++ --color-tests) color_tests=$2; shift;; ++ --expect-failure) expect_failure=$2; shift;; ++ --enable-hard-errors) enable_hard_errors=$2; shift;; ++ --) shift; break;; ++ -*) usage_error "invalid option: '$1'";; ++ *) break;; ++ esac ++ shift ++done ++ ++missing_opts= ++test x"$test_name" = x && missing_opts="$missing_opts --test-name" ++test x"$log_file" = x && missing_opts="$missing_opts --log-file" ++test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" ++if test x"$missing_opts" != x; then ++ usage_error "the following mandatory options are missing:$missing_opts" ++fi ++ ++if test $# -eq 0; then ++ usage_error "missing argument" ++fi ++ ++if test $color_tests = yes; then ++ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. ++ red='[0;31m' # Red. ++ grn='[0;32m' # Green. ++ lgn='[1;32m' # Light green. ++ blu='[1;34m' # Blue. ++ mgn='[0;35m' # Magenta. ++ std='[m' # No color. ++else ++ red= grn= lgn= blu= mgn= std= ++fi ++ ++do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' ++trap "st=129; $do_exit" 1 ++trap "st=130; $do_exit" 2 ++trap "st=141; $do_exit" 13 ++trap "st=143; $do_exit" 15 ++ ++# Test script is run here. ++top_srcdir="`dirname $0`/.." ++tests_dir="${top_srcdir}/tests" ++ ++USE_VALGRIND="`printenv USE_VALGRIND`" ++ ++if test "x${USE_VALGRIND}" = "x1"; then ++ ${top_srcdir}/libtool --mode=execute valgrind \ ++ --leak-check=full \ ++ --show-reachable=no \ ++ --error-exitcode=1 \ ++ --suppressions=$tests_dir/libnice.supp \ ++ --num-callers=30 "$@" >$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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 philip.withnall@collabora.co.uk +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 @@ + <title>Index of new symbols in 0.1.14</title> + <xi:include href="xml/api-index-0.1.14.xml">xi:fallback/</xi:include> + </index> ++ <index role="0.1.15"> ++ <title>Index of new symbols in 0.1.15</title> ++ <xi:include href="xml/api-index-0.1.15.xml">xi:fallback/</xi:include> ++ </index> + <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + </part> + </book> +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 + <SUBSECTION Standard> + 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?= olivier.crete@collabora.com +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 <config.h> +-#endif +- +-#include <string.h> +- +-#include <nice/nice.h> +-#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?= olivier.crete@collabora.com +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 philip.withnall@collabora.co.uk +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 olivier.crete@collabora.com ++ * ++ * 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 <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include "socket/socket.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++ ++ ++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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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 withnall@endlessm.com +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 withnall@endlessm.com +Reviewed-by: Olivier Crête olivier.crete@collabora.com +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 fabrice@bellet.info +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 <glib-object.h>\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 <config.h>\n#include <glib-object.h>\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. ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * 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() ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * 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 fabrice@bellet.info +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 <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#include <gio/gio.h> ++#include <agent.h> ++ ++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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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?= olivier.crete@collabora.com +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() +- * <para> See also: #NiceNominationMode </para> ++ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * + * 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?= olivier.crete@collabora.com +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. + * <para> See also: #NiceNominationMode </para> + * +- * 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() + * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * +- * 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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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?= olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 fabrice@bellet.info +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 withnall@endlessm.com +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 withnall@endlessm.com +--- + 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 withnall@endlessm.com +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 withnall@endlessm.com +--- + 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 <gst/check/gstcheck.h> +-#include <nice/agent.h> ++#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 withnall@endlessm.com +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 withnall@endlessm.com +Reviewed-by: Olivier Crête olivier.crete@collabora.com +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 fabrice@bellet.info +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 fabrice@bellet.info +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?= mparisdiaz@gmail.com +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 <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#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 kakaroto@kakaroto.homelinux.net +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 kakaroto@kakaroto.homelinux.net +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 kakaroto@kakaroto.homelinux.net +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 + diff --git a/libnice.spec b/libnice.spec index e5944fc..63e60a2 100644 --- a/libnice.spec +++ b/libnice.spec @@ -10,7 +10,7 @@ Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz -Patch1: libnice-0.1.14-20171128.patch +Patch1: libnice-0.1.14-70-gfb2f1f7.patch
BuildRequires: autoconf BuildRequires: automake
commit 5ecb7654163d99321b6206e83dca01578775405d Author: Stefan Becker chemobejk@gmail.com Date: Mon Jan 29 17:56:32 2018 +0200
update to 0.1.14-70-gfb2f1f7
diff --git a/libnice-0.1.14-20171128.patch b/libnice-0.1.14-20171128.patch new file mode 100644 index 0000000..ff2d2ae --- /dev/null +++ b/libnice-0.1.14-20171128.patch @@ -0,0 +1,6711 @@ +commit fb2f1f77a31baa91968fc81c205f980b6913f403 +Author: Youness Alaoui kakaroto@kakaroto.homelinux.net +Date: Tue Nov 28 16:05:18 2017 -0500 + + 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). + +commit 4172d48852ecd1c86cc7bd4665b23697603d1eed +Author: Youness Alaoui kakaroto@kakaroto.homelinux.net +Date: Tue Nov 28 15:14:11 2017 -0500 + + 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. + +commit 59fcf95d505c3995f858b826d10cd48321ed383e +Author: Youness Alaoui kakaroto@kakaroto.homelinux.net +Date: Mon Nov 27 17:07:02 2017 -0500 + + 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. + +commit 17f30e4465efe9533799b02d6f95feeaf0f2748c +Author: Miguel París mparisdiaz@gmail.com +Date: Wed Nov 8 16:26:47 2017 +0000 + + 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 + +commit c63349894b3fe974494453a883dfb5ad05df5a46 +Author: Fabrice Bellet fabrice@bellet.info +Date: Thu Nov 23 18:31:31 2017 +0100 + + Makefile: really enable debug for tests + + Differential Revision: https://phabricator.freedesktop.org/D1888 + +commit 02216a6766caccb652387d5ee19686149eedbc93 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Nov 21 15:12:45 2017 +0100 + + 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 + +commit fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 +Author: Philip Withnall withnall@endlessm.com +Date: Thu Aug 3 12:20:32 2017 +0100 + + stun: Fix FD leak in test/utility code + + https://phabricator.freedesktop.org/T7798 + + Signed-off-by: Philip Withnall withnall@endlessm.com + Reviewed-by: Olivier Crête olivier.crete@collabora.com + Differential Revision: https://phabricator.freedesktop.org/D1819 + +commit 4c4834ab634f735145c8f758a22cbdd9cab79bac +Author: Philip Withnall withnall@endlessm.com +Date: Tue Sep 12 13:23:53 2017 +0100 + + tests: Fix agent.h header inclusion in test-gstreamer.c + + Spotted by Lukas Gradl on the mailing list. + + Signed-off-by: Philip Withnall withnall@endlessm.com + +commit 1a1803a45778000720c93d91060cedeb19124a27 +Author: Philip Withnall withnall@endlessm.com +Date: Tue Sep 12 13:23:31 2017 +0100 + + tests: Fix copyright dates in test-gstreamer.c + + This code is not 1000 years old. + + Signed-off-by: Philip Withnall withnall@endlessm.com + +commit 14102d44449d2eb4148588ce54fa897fa13b87ad +Author: Fabrice Bellet fabrice@bellet.info +Date: Sun Jul 2 16:02:09 2017 +0200 + + 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. + +commit 72dd26a3368d3506fe8faca7067a02784fb5f0fd +Author: Fabrice Bellet fabrice@bellet.info +Date: Wed Jun 28 12:06:48 2017 +0200 + + 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 + +commit 6fe64fdbc53ab87dffd79972f492665cff14c0a0 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Jun 27 11:01:14 2017 +0200 + + 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 + +commit 36f306f4a95f1c2b3e9c584b5a645a78e231c020 +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Jun 26 21:41:44 2017 +0200 + + 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 + +commit e860948b5fe3a791119957f26045b8f5159baeff +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Jun 26 21:06:36 2017 +0200 + + 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 + +commit ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Jun 26 20:41:49 2017 +0200 + + 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 + +commit 25be00271a4c8c684a2d435d29ae0811dbf5e21c +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Jun 26 20:36:35 2017 +0200 + + conncheck: reorder some chunks of code + + With this patch we simplify the levels of code indentation. + + Differential Revision: https://phabricator.freedesktop.org/D1758 + +commit 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Sep 5 14:50:29 2017 -0400 + + agent: Set error if it isn't set + +commit dbaf8f5ccd76089e340883887c7e08e6c04de80a +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 13:22:21 2016 +0200 + + conncheck: improve role conflict debug + + This patch displays explicitely the controlling or controlled + role of the agent. + + Differential Revision: https://phabricator.freedesktop.org/D874 + +commit 9f800d3597767855accccc592c34bc4e945f5bd5 +Author: Olivier Crête olivier.crete@collabora.com +Date: Wed Jun 21 20:42:57 2017 -0400 + + configure: Remove -Wswitch-enum + + Creates useless warnings when other libraries change. + + https://phabricator.freedesktop.org/T7770 + +commit 63d273cea42def3567701ad9feab91f63cf9345f +Author: Olivier Crête olivier.crete@collabora.com +Date: Thu Feb 11 22:16:48 2016 -0500 + + component: Use non-GClosure dummy callbacks + + GClosures are not that cheap to setup + +commit 2c50d73b82f2ec2422a8e0ea393194486c193c64 +Author: Olivier Crête olivier.crete@collabora.com +Date: Wed Feb 10 23:20:39 2016 -0500 + + agent: Don't crash if recv cancelled without a GError + +commit dcb0d647174416a292492f8deca86f83a2ef124c +Author: Olivier Crête olivier.crete@collabora.com +Date: Wed Jun 21 17:07:17 2017 -0400 + + Repleace UNRELEASED with 0.1.15 + +commit e3ddaa285e389baf3f26cfb6964919718a8f6a00 +Author: Olivier Crête olivier.crete@collabora.com +Date: Wed Jun 21 16:55:32 2017 -0400 + + agent: Adjust the nice_agent_new_full() to use flags + + This makes it easier to read and more extensible. + +commit c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 +Author: Fabrice Bellet fabrice@bellet.info +Date: Sun Jun 18 10:12:58 2017 +0200 + + agent: remove spurious newlines + + Differential Revision: https://phabricator.freedesktop.org/D1756 + +commit b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 +Author: Fabrice Bellet fabrice@bellet.info +Date: Sun May 28 22:20:36 2017 +0200 + + stun: fix gcc7 implicit fallthrough warning + + Differential Revision: https://phabricator.freedesktop.org/D1754 + +commit 195db6b344fc4f9fadc39419dfeec2fc14b23fac +Author: Fabrice Bellet fabrice@bellet.info +Date: Fri Jul 15 23:31:42 2016 +0200 + + 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 + +commit 07366a5bca7e4818b8df29d9c7c220da8f752547 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Jun 21 21:47:42 2016 +0200 + + 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 + +commit 95f8805eb7b77755337e28daf1f134587d42b35f +Author: Fabrice Bellet fabrice@bellet.info +Date: Thu Jun 16 17:32:39 2016 +0200 + + 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 + +commit d516fca1b0e0a6606afec797bdc0690104e779a9 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Jun 14 21:32:26 2016 +0200 + + 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 + +commit f19d209decac432a1597d84c3d5809d2208f7457 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Jun 14 21:20:49 2016 +0200 + + 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 + +commit 59fe48517c0b7db77b99183d31fdd84b55adb5d4 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Jun 14 21:12:16 2016 +0200 + + 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 + +commit 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Jun 14 21:04:49 2016 +0200 + + 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 + +commit 8fa648a15a6700d08165fe97a09f5c068abae1e6 +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Apr 11 13:13:51 2016 +0200 + + 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 + +commit 11d4e37a030eb144a355dc26c705ef5aa5a975a7 +Author: Fabrice Bellet fabrice@bellet.info +Date: Fri Apr 1 17:31:44 2016 +0200 + + 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 + +commit 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 12:56:28 2016 +0200 + + 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 + +commit afd8d41bb34afb3864e838ef79026ae4ef15c0d4 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 13:32:49 2016 +0200 + + 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 + +commit 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 13:30:04 2016 +0200 + + 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 + +commit 9103a5f2e184211fc160d1d3070ce4d043c71ff0 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 19 18:16:26 2016 +0200 + + 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 + +commit 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 13:25:16 2016 +0200 + + 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 + +commit 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 19 17:59:27 2016 +0200 + + 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 + +commit ead3453d04fc70865d176ab073636f8b9078cbbc +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 13:20:38 2016 +0200 + + conncheck: invoke the debug dump in more places + + Differential Revision: https://phabricator.freedesktop.org/D1123 + +commit 15c0546f624113b8c0546a1f883a48bff7020f1b +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 19 17:06:32 2016 +0200 + + 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 + +commit 58d061df8f5425dc1add9c6030a2f891ebda4616 +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Mar 7 16:35:09 2016 +0100 + + 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 + +commit 0636f9addc041cf93c4ff4eaa351b1768d48a32e +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 19 13:12:48 2016 +0200 + + 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 + +commit a602ff57aae6a6afdeab843954c48e6fb5d82d31 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 12 13:02:45 2016 +0200 + + 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 + +commit 3a58ba6120b188d78c5709e0349c0346bfa21c1a +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Feb 1 11:10:21 2016 +0100 + + 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 + +commit 7a2c1edf502849a868b6f1026e8e2c343dee4ded +Author: Fabrice Bellet fabrice@bellet.info +Date: Mon Jun 6 22:24:50 2016 +0200 + + 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 + +commit 8bb210c5af4bcaf342d7fa4fef6034269e976532 +Author: Fabrice Bellet fabrice@bellet.info +Date: Thu Jun 9 23:28:43 2016 +0200 + + 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 + +commit 80c613699786567fd93db74377138600794a86e0 +Author: Olivier Crête olivier.crete@collabora.com +Date: Thu Jun 8 16:34:21 2017 -0400 + + agent: Use base_addr to generate rport in SDP + + Reported by Capricornus (zhushengliang) + + https://phabricator.freedesktop.org/T7763 + +commit b4abda09c79e4ce372a3771300abf568c85c7ff5 +Author: Fabrice Bellet fabrice@bellet.info +Date: Thu Apr 21 18:18:59 2016 +0200 + + 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 + +commit d5446a72233eab8501be0b3fb9060c8be3ba034b +Author: Philip Withnall withnall@endlessm.com +Date: Mon May 1 08:51:40 2017 +0100 + + examples: Stop installing the examples + + 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 withnall@endlessm.com + Reviewed-by: Olivier Crête olivier.crete@collabora.com + Differential Revision: https://phabricator.freedesktop.org/D1737 + +commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 +Author: Fabrice Bellet fabrice@bellet.info +Date: Tue Apr 5 21:32:39 2016 +0200 + + 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 + +commit f6f704c5e8d2193bc67ba2b697c77694e1698c43 +Author: Fabrice Bellet fabrice@bellet.info +Date: Thu Jun 9 22:22:33 2016 +0200 + + 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 + +commit b0538d8c51f65019867b56a45cf90a70bef38f01 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 11 18:31:21 2017 -0400 + + agent: Ignore remote candidate of non-accepted types + + If we disable ice-tcp or ice-udp, ignore the remote + candidates for those types. + +commit f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 11 16:42:55 2017 -0400 + + 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. + +commit 8fc22b0034d04cbc222e0637152b1cee2879eef3 +Author: Olivier Crête olivier.crete@collabora.com +Date: Wed Apr 5 17:43:26 2017 -0400 + + tests_: Add test to verify that only packets from validated addresses pass + + https://phabricator.freedesktop.org/T104 + + Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk + Differential Revision: https://phabricator.freedesktop.org/D1717 + +commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 21:27:39 2017 -0400 + + 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 + +commit 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 14:41:51 2017 -0400 + + 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 philip.withnall@collabora.co.uk + Differential Revision: https://phabricator.freedesktop.org/D1715 + +commit 10c557f23f8337f1304fff27bd85d2eb713cb249 +Author: Olivier Crête olivier.crete@collabora.com +Date: Wed Apr 5 17:01:35 2017 -0400 + + test-credentials: Fix leak + +commit ae6d939e48366b80570d713b83334191b0982e71 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 20:34:05 2017 -0400 + + debug: Use libnice-verbose, not libnice-nice-verbose + +commit efc6a9be8cb34c899f0454c32e8a1e62b38df474 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 18:42:57 2017 -0400 + + tests: Use automake test-driver for valgrind + + This fixes the valgrind integration with the new test drivers. + +commit 4e605885c9dcaeb3ee443ec902c9c9189b19043f +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 16:16:46 2017 -0400 + + agent: Remove impossible case + +commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 16:16:05 2017 -0400 + + 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. + +commit cd255bddc7fa0ddae056b5358a22b380c4eefc42 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 15:24:43 2017 -0400 + + udp-turn: Add some const to internal APIs + +commit db05e8b0fdc713df93cd6a4c3914e5aee38b2391 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 12:30:27 2017 -0400 + + Make clang-analyzer happy + + Various little things, none of which should make a functional difference. + +commit 0672758b9621801c8f0d9e3c920370983b267a68 +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 12:29:29 2017 -0400 + + 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. + +commit 0de1657e4d15d4f1911ab1fad84ea23e7013070f +Author: Olivier Crête olivier.crete@collabora.com +Date: Tue Apr 4 12:25:50 2017 -0400 + + 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 + +commit 59ce41dfb837adf4222b25490cde2e394384ad15 +Author: Miguel París Díaz mparisdiaz@gmail.com +Date: Fri Mar 31 20:20:38 2017 -0400 + + conncheck: consider answer received when remote credentials are set + + 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 +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 <glib-object.h>\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 <config.h>\n#include <glib-object.h>\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 4d8c9b8..7269be0 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) */ + + +@@ -114,6 +113,27 @@ 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 */ ++ ++/* 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) ++ ++/* 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 */ +@@ -126,10 +146,14 @@ 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 */ ++ 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 */ +@@ -164,6 +188,10 @@ struct _NiceAgent + guint16 rfc4571_expecting_length; + gboolean use_ice_udp; + 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 555fd16..0773c53 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. */ +@@ -113,6 +114,10 @@ enum + PROP_BYTESTREAM_TCP, + PROP_KEEPALIVE_CONNCHECK, + PROP_FORCE_RELAY, ++ PROP_STUN_MAX_RETRANSMISSIONS, ++ PROP_STUN_INITIAL_TIMEOUT, ++ PROP_STUN_RELIABLE_TIMEOUT, ++ PROP_NOMINATION_MODE, + }; + + +@@ -400,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", +@@ -436,6 +448,24 @@ nice_agent_class_init (NiceAgentClass *klass) + 0, /* default set in init */ + 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. ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * Since: 0.1.15 ++ */ ++ 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: + * +@@ -708,6 +738,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: 0.1.15 ++ */ ++ ++ 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: 0.1.15 ++ */ ++ ++ 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: 0.1.15 ++ */ ++ ++ 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 */ + + /** +@@ -1013,6 +1113,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) + nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker); + } + ++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) + { +@@ -1022,7 +1163,9 @@ 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; + + agent->discovery_list = NULL; + agent->discovery_unsched_items = 0; +@@ -1071,6 +1214,24 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) + } + + ++NICEAPI_EXPORT NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ NiceAgentOption flags) ++{ ++ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, ++ "compatibility", compat, ++ "main-context", ctx, ++ "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; ++} ++ ++ + static void + nice_agent_get_property ( + GObject *object, +@@ -1101,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: +@@ -1117,6 +1278,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; +@@ -1187,6 +1352,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); + } +@@ -1294,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: +@@ -1309,6 +1486,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); +@@ -1374,6 +1555,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); + } +@@ -1552,7 +1745,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) { +@@ -1890,6 +2082,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; + +@@ -3113,6 +3316,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; + +@@ -3195,6 +3405,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; + +@@ -3240,6 +3463,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 +3567,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); + } +@@ -3423,7 +3646,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. */ +@@ -3484,8 +3708,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 { +@@ -3500,7 +3724,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 +@@ -3509,9 +3733,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 +@@ -3524,7 +3748,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; + +@@ -3537,7 +3761,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)) { +@@ -3545,8 +3769,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); + } +@@ -3590,8 +3814,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; + } +@@ -3599,23 +3823,26 @@ 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) { ++ 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); +@@ -3679,8 +3906,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 ( +@@ -3712,6 +3937,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; + } + } +@@ -3722,6 +3948,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", 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) && +@@ -4085,7 +4328,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 +@@ -4245,7 +4491,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, + "Component removed during call."); + + component = NULL; +- error_reported = TRUE; + + goto recv_error; + } +@@ -4734,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; + +@@ -5665,7 +5915,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); +diff --git a/agent/agent.h b/agent/agent.h +index 47c4d5a..520c4c5 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -377,6 +377,45 @@ 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: 0.1.15 ++ */ ++typedef enum ++{ ++ NICE_NOMINATION_MODE_REGULAR = 0, ++ NICE_NOMINATION_MODE_AGGRESSIVE, ++} 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: 0.1.15 ++ */ ++typedef enum { ++ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, ++ NICE_AGENT_OPTION_RELIABLE = 1 << 1, ++ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, ++} NiceAgentOption; + + /** + * NiceAgentRecvFunc: +@@ -428,6 +467,26 @@ nice_agent_new (GMainContext *ctx, NiceCompatibility compat); + 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 ++ * @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() ++ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> ++ * ++ * Since: 0.1.15 ++ * ++ * Returns: The new agent GObject ++ */ ++NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ NiceAgentOption flags); ++ + /** + * nice_agent_add_local_address: + * @agent: The #NiceAgent Object +@@ -447,7 +506,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/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/agent/component.c b/agent/component.c +index 32f7463..6eee90e 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); +@@ -380,7 +387,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) { +@@ -435,6 +442,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 +523,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; + } + +@@ -991,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) + { +@@ -1013,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); + +@@ -1107,6 +1133,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); +@@ -1225,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 = +@@ -1370,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); +@@ -1421,3 +1450,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", 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 dda2f2f..c8a4edf 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -64,8 +64,8 @@ + + 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 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, + guint component_id, NiceCandidate *remote, NiceCandidate *local, +@@ -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) + { +@@ -84,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) + { +@@ -98,8 +111,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: +@@ -107,6 +118,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) + { +@@ -130,12 +189,15 @@ 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 ()) + return; + ++ g_get_current_time (&now); ++ + #define PRIORITY_LEN 32 + + nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", +@@ -148,26 +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 - stun_timer_remainder (timer), 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)" : ""); ++ } + } + } + } +@@ -181,6 +251,8 @@ 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); + 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); +@@ -209,6 +281,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) + return pair; + } + ++/* ++ * 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. + */ +@@ -218,7 +373,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) +@@ -228,6 +382,57 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + return NULL; + } + ++/* ++ * 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 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. + * +@@ -235,8 +440,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + */ + 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); +@@ -246,58 +449,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; + } + + /* +@@ -314,7 +514,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); +@@ -334,60 +533,147 @@ 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); ++/* ++ * 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; ++ ++ if (stun->message.buffer != NULL) { ++ stun_message_id (&stun->message, id); ++ stun_agent_forget_transaction (&component->stun_agent, id); ++ } ++} ++ ++static void ++priv_free_stun_transaction (gpointer data) ++{ ++ g_slice_free (StunTransaction, data); ++} ++ ++/* ++ * Remove a STUN transaction from a pair, and forget it ++ * from the related component stun agent. ++ * ++ * @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 void ++priv_remove_stun_transaction (CandidateCheckPair *pair, ++ StunTransaction *stun, NiceComponent *component) ++{ ++ 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); +- +- if (p->stun_message.buffer != NULL) { +- stun_message_id (&p->stun_message, id); +- stun_agent_forget_transaction (&component->stun_agent, id); +- } +- +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++ priv_free_all_stun_transactions (p, component); + } + + /* +@@ -398,78 +684,183 @@ 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) + { + 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, *j; ++ 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) { + CandidateCheckPair *p = i->data; ++ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ NiceComponent *component; ++ StunTransaction *stun; + +- 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: +- { +- /* case: error, abort processing */ +- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; +- 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"); +- +- break; +- } +- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +- { +- /* case: not ready, so schedule a new timeout */ +- unsigned int 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); ++ if (p->stun_transactions == NULL) ++ continue; + +- agent_socket_send (p->sockptr, &p->remote->addr, +- stun_message_length (&p->stun_message), +- (gchar *)p->stun_buffer); ++ if (!agent_find_component (agent, p->stream_id, p->component_id, ++ NULL, &component)) ++ continue; + ++ /* The first stun transaction of the list may eventually be ++ * retransmitted, other stun transactions just have their ++ * timer updated. ++ */ + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ j = p->stun_transactions->next; + +- *stun_transmitted = TRUE; +- return TRUE; +- } +- case STUN_USAGE_TIMER_RETURN_SUCCESS: +- { +- unsigned int timeout = stun_timer_remainder (&p->timer); ++ /* process all stun transactions except the first one */ ++ while (j) { ++ StunTransaction *s = j->data; ++ GSList *next = j->next; + +- /* 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; +- } ++ 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: +- /* Nothing to do. */ + break; +- } + } ++ j = next; + } + ++ if (p->state != NICE_CHECK_IN_PROGRESS) ++ continue; ++ ++ /* 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 : 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: ++ /* 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) ++ goto timer_return_timeout; ++ ++ /* case: not ready, so schedule a new timeout */ ++ timeout = stun_timer_remainder (&stun->timer); ++ ++ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " ++ "(timer=%d/%d %d/%dms).", ++ agent, p, ++ 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 (&stun->message), ++ (gchar *)stun->buffer); ++ ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ 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 (&stun->timer); ++ ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); ++ ++ keep_timer_going = TRUE; ++ break; ++ default: ++ /* Nothing to do. */ ++ break; ++ } ++ } ++ ++ /* 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_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Waiting state"); ++ 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) { ++ 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); ++ 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) +@@ -495,12 +886,102 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + 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 (agent->controlling_mode) { ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && ++ 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 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, ++ * 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; ++ 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) { ++ 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++; ++ } ++ } ++ ++ 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 ++ */ ++ 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) { ++ /* 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->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 */ ++ } ++ } ++ } ++ } ++ } else if (agent->controlling_mode) { + GSList *component_item; + + for (component_item = stream->components; component_item; +@@ -515,7 +996,9 @@ 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); ++ keep_timer_going = TRUE; + break; /* move to the next component */ + } + } +@@ -542,6 +1025,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; + } + + +@@ -557,75 +1041,83 @@ 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; ++ /* 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: first initiate a conncheck with a pair from the triggered list */ ++ /* 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_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; + } + +- /* step: when the triggered list is empty, +- * find the highest priority waiting check and send it */ ++ /* step: process ongoing STUN transactions and ++ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" ++ */ + 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 (priv_conn_check_tick_stream (stream, agent)) ++ keep_timer_going = TRUE; ++ if (priv_conn_check_tick_stream_nominate (stream, agent)) ++ keep_timer_going = TRUE; + } + +- if (pair) { +- priv_conn_check_initiate (agent, pair); +- return 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); ++ } ++ } + } + +- /* step: when there's no pair in the Waiting state, +- * unfreeze a new pair and check it ++ /* 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. + */ +- res = priv_conn_check_unfreeze_next (agent); +- +- for (i = agent->streams; i ; i = i->next) { +- NiceStream *stream = i->data; ++ 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); + +- 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 (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); +@@ -635,6 +1127,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"); + +@@ -645,9 +1138,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) +@@ -888,8 +1382,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 +1611,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, +@@ -1213,154 +1709,136 @@ 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); ++ 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); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); + } + } + } + + +-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 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); ++ ++ 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); ++ } ++ } + } + } + } +@@ -1368,12 +1846,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; +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); ++ 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; ++ } + } + + /* +@@ -1381,7 +1859,7 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice + * 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; +@@ -1389,22 +1867,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; + } + + /* +@@ -1444,15 +1922,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*/ +@@ -1476,6 +1957,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; + +@@ -1483,16 +1966,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); +@@ -1525,7 +2014,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); + } + } + } +@@ -1535,7 +2023,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. */ +@@ -1564,24 +2052,59 @@ 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) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); +- pair->nominated = TRUE; ++ /* 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->state == NICE_CHECK_SUCCEEDED && ++ pair->discovered_pair != NULL) { ++ pair = pair->discovered_pair; ++ g_assert (pair->state == NICE_CHECK_DISCOVERED); ++ } ++ ++ /* 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); ++ } ++ ++ 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; ++ } ++ + 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); ++ 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 */ + 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); + } + } + } +@@ -1624,7 +2147,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; +@@ -1662,8 +2185,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->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); + +@@ -1675,7 +2196,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; +@@ -1709,7 +2231,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, +@@ -1781,6 +2303,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; + +@@ -1815,6 +2346,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; +@@ -1838,8 +2381,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); + } + +@@ -2057,30 +2599,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 +@@ -2114,6 +2654,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)) +@@ -2135,99 +2676,119 @@ 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 "), " +- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, ++ "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, +- pair->prflx_priority, controlling); ++ pair->prflx_priority, ++ controlling ? "controlling" : "controlled"); + } + +- 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) { +- 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); ++ return -1; ++ } + +- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, +- pair->stun_message.buffer); ++ stun = priv_add_stun_transaction (pair); + +- if (agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- g_free (password); +- } ++ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, ++ &stun->message, stun->buffer, sizeof(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 (buffer_len > 0) { +- if (nice_socket_is_reliable(pair->sockptr)) { +- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); +- } else { +- stun_timer_start (&pair->timer, +- priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +- } ++ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, ++ stun->message.buffer); + +- /* 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 (agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ g_free (password); ++ } + +- 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 (buffer_len == 0) { ++ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); ++ priv_remove_stun_transaction (pair, stun, component); ++ return -1; ++ } + +- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { +- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, +- &pair->remote->addr); +- } ++ if (nice_socket_is_reliable(pair->sockptr)) { ++ timeout = agent->stun_reliable_timeout; ++ stun_timer_start_reliable(&stun->timer, timeout); ++ } else { ++ timeout = priv_compute_conncheck_timer (agent, stream); ++ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); ++ } + +- 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; ++ 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 ++ * 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); ++ } + } +- } 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 *)stun->buffer); ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, ++ &pair->remote->addr); + + return 0; + } +@@ -2238,14 +2799,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; +@@ -2257,37 +2818,40 @@ 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 */ +- 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) { +- if (p->state == NICE_CHECK_FROZEN || +- p->state == NICE_CHECK_WAITING) { +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ 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) */ +- if (p->state == NICE_CHECK_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); ++ 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); + } 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++; + } + } + } ++ i = next; + } + + return in_progress; +@@ -2301,17 +2865,17 @@ 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; ++ 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 && +@@ -2322,61 +2886,67 @@ 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 */ + +- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); ++ 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 (%s) (%s) ...", ++ agent, p, p->foundation, priv_state_to_string (p->state)); + +- if (p->state == NICE_CHECK_WAITING || +- 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), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +- p->timer_restarted = TRUE; +- } +- } +- else if (p->state == NICE_CHECK_SUCCEEDED || +- p->state == NICE_CHECK_DISCOVERED) { +- 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 */ +- /* 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); +- +- /* 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) { ++ 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); +- 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 +- 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); ++ 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 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) { ++ 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 ++ */ ++ 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: ++ 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; ++ default: ++ break; + } + + /* note: the spec says the we SHOULD retransmit in-progress +@@ -2394,7 +2964,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, local, remote_cand, NICE_CHECK_WAITING, use_candidate); ++ 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 { +@@ -2447,9 +3019,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); + } +@@ -2509,6 +3079,8 @@ 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; ++ pair->succeeded_pair = parent_pair; + nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; +@@ -2528,7 +3100,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); +@@ -2567,14 +3138,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"); + } + + /* +@@ -2621,13 +3194,25 @@ 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; ++ 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); +- priv_conn_check_unfreeze_related (agent, stream, p); ++ nice_component_add_valid_candidate (component, remote_candidate); + } + 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, +@@ -2635,8 +3220,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 +@@ -2644,13 +3230,23 @@ 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); ++ /* 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" ++ */ ++ p->state = 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); + } + +- /* 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; + } +@@ -2669,134 +3265,199 @@ 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; +- 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 (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; +- } +- +- /* 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); +- priv_conn_check_unfreeze_related (agent, stream, p); +- } else { +- ok_pair = priv_process_response_check_for_reflexive (agent, +- stream, component, p, sockptr, &sockaddr.addr, +- local_candidate, remote_candidate); +- } +- +- +- if (!ok_pair) +- ok_pair = p; +- +- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the +- Nominated Flag" (ID-19) */ +- 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); ++ 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); ++ } + +- 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: 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); +- 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; +- } ++ /* 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; ++ ++ /* 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); ++ 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); + } ++ return TRUE; + } + } + +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); +- +- return trans_found; ++ return FALSE; + } + + /* +@@ -2846,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 */ + +@@ -2930,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. +@@ -2982,11 +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; ++ /* 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 */ +@@ -3103,6 +3800,17 @@ 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 addr; ++ ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); ++ ++ if (!nice_address_equal (&addr, &d->server)) { ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); ++ } ++ } + /* check for unauthorized error response */ + if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || + agent->compatibility == NICE_COMPATIBILITY_OC2007 || +@@ -3123,6 +3831,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; +@@ -3619,8 +4328,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; +@@ -3628,21 +4336,21 @@ 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[0]) { + if (local_candidate && + local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { + CandidateCheckPair *pair; + + 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; + } +@@ -3651,13 +4359,15 @@ 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); + +- if (component->remote_candidates == 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 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..e16dc67 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,11 +67,19 @@ typedef enum + NICE_CHECK_SUCCEEDED, + NICE_CHECK_FAILED, + NICE_CHECK_FROZEN, +- NICE_CHECK_CANCELLED, + NICE_CHECK_DISCOVERED, + } 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 + { +@@ -85,15 +92,15 @@ struct _CandidateCheckPair + gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; + NiceCheckState state; + gboolean nominated; +- gboolean controlling; +- gboolean timer_restarted; + gboolean valid; ++ gboolean use_candidate_on_next_check; ++ gboolean mark_nominated_on_response_arrival; ++ 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); +@@ -105,7 +112,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, +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; + } + +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/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/agent/stream.c b/agent/stream.c +index 8121e12..533ff15 100644 +--- a/agent/stream.c ++++ b/agent/stream.c +@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) + return NULL; + } + +-/* +- * 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. + */ +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); + +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/configure.ac b/configure.ac +index b39bfe3..16988ad 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]) +@@ -153,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]) + ]) +@@ -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 +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 @@ + <title>Index of new symbols in 0.1.14</title> + <xi:include href="xml/api-index-0.1.14.xml">xi:fallback/</xi:include> + </index> ++ <index role="0.1.15"> ++ <title>Index of new symbols in 0.1.15</title> ++ <xi:include href="xml/api-index-0.1.15.xml">xi:fallback/</xi:include> ++ </index> + <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + </part> + </book> +diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt +index d377257..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 +@@ -76,6 +78,7 @@ NICE_CANDIDATE_MAX_FOUNDATION + nice_candidate_new + nice_candidate_free + nice_candidate_copy ++nice_candidate_equal_target + <SUBSECTION Standard> + NICE_TYPE_CANDIDATE + nice_candidate_get_type +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 \ +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 +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 http://www.gnu.org/licenses/. ++ ++# 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 bug-automake@gnu.org or send patches to ++# automake-patches@gnu.org. ++ ++# 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 <<END ++Usage: ++ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH ++ [--expect-failure={yes|no}] [--color-tests={yes|no}] ++ [--enable-hard-errors={yes|no}] [--] ++ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] ++The '--test-name', '--log-file' and '--trs-file' options are mandatory. ++END ++} ++ ++test_name= # Used for reporting. ++log_file= # Where to save the output of the test script. ++trs_file= # Where to save the metadata of the test run. ++expect_failure=no ++color_tests=no ++enable_hard_errors=yes ++while test $# -gt 0; do ++ case $1 in ++ --help) print_usage; exit $?;; ++ --version) echo "test-driver $scriptversion"; exit $?;; ++ --test-name) test_name=$2; shift;; ++ --log-file) log_file=$2; shift;; ++ --trs-file) trs_file=$2; shift;; ++ --color-tests) color_tests=$2; shift;; ++ --expect-failure) expect_failure=$2; shift;; ++ --enable-hard-errors) enable_hard_errors=$2; shift;; ++ --) shift; break;; ++ -*) usage_error "invalid option: '$1'";; ++ *) break;; ++ esac ++ shift ++done ++ ++missing_opts= ++test x"$test_name" = x && missing_opts="$missing_opts --test-name" ++test x"$log_file" = x && missing_opts="$missing_opts --log-file" ++test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" ++if test x"$missing_opts" != x; then ++ usage_error "the following mandatory options are missing:$missing_opts" ++fi ++ ++if test $# -eq 0; then ++ usage_error "missing argument" ++fi ++ ++if test $color_tests = yes; then ++ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. ++ red='[0;31m' # Red. ++ grn='[0;32m' # Green. ++ lgn='[1;32m' # Light green. ++ blu='[1;34m' # Blue. ++ mgn='[0;35m' # Magenta. ++ std='[m' # No color. ++else ++ red= grn= lgn= blu= mgn= std= ++fi ++ ++do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' ++trap "st=129; $do_exit" 1 ++trap "st=130; $do_exit" 2 ++trap "st=141; $do_exit" 13 ++trap "st=143; $do_exit" 15 ++ ++# Test script is run here. ++top_srcdir="`dirname $0`/.." ++tests_dir="${top_srcdir}/tests" ++ ++USE_VALGRIND="`printenv USE_VALGRIND`" ++ ++if test "x${USE_VALGRIND}" = "x1"; then ++ ${top_srcdir}/libtool --mode=execute valgrind \ ++ --leak-check=full \ ++ --show-reachable=no \ ++ --error-exitcode=1 \ ++ --suppressions=$tests_dir/libnice.supp \ ++ --num-callers=30 "$@" >$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 +- +diff --git a/socket/udp-turn.c b/socket/udp-turn.c +index cc3409b..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; +@@ -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) +@@ -1185,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; +@@ -1195,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); +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 */ +diff --git a/stun/usages/bind.c b/stun/usages/bind.c +index 8dd7afc..ee600a0 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; + +@@ -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; +@@ -548,16 +555,22 @@ 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); + + 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); +@@ -571,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; + } +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; + } +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 + +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 +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 7bfe075..30d6f8e 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) + +@@ -45,8 +45,8 @@ check_PROGRAMS = \ + test-send-recv \ + test-socket-is-based-on \ + test-priority \ +- test-mainloop \ + test-fullmode \ ++ test-different-number-streams \ + test-restart \ + test-fallback \ + test-thread \ +@@ -55,7 +55,9 @@ check_PROGRAMS = \ + test-tcp \ + test-icetcp \ + test-credentials \ +- test-turn ++ test-turn \ ++ test-drop-invalid \ ++ test-nomination + + dist_check_SCRIPTS = \ + check-test-fullmode-with-stun.sh \ +@@ -113,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) +@@ -129,6 +133,10 @@ test_credentials_LDADD = $(COMMON_LDADD) + + 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-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); +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 <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#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; ++} +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 olivier.crete@collabora.com ++ * ++ * 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 <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include "socket/socket.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++ ++ ++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; ++} +diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c +index 74d7133..f060efc 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 +@@ -34,7 +34,7 @@ + */ + + #include <gst/check/gstcheck.h> +-#include <nice/agent.h> ++#include "agent.h" + + #define RTP_HEADER_SIZE 12 + #define RTP_PAYLOAD_SIZE 1024 +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 <config.h> +-#endif +- +-#include <string.h> +- +-#include <nice/nice.h> +-#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; +-} +- +diff --git a/tests/test-nomination.c b/tests/test-nomination.c +new file mode 100644 +index 0000000..bf21557 +--- /dev/null ++++ b/tests/test-nomination.c +@@ -0,0 +1,263 @@ ++#include <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#include <gio/gio.h> ++#include <agent.h> ++ ++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, ++ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); ++ ++ ragent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ 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); ++ ++ 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; ++} +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..."); + diff --git a/libnice.spec b/libnice.spec index 7c8ffb1..e5944fc 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,14 +3,17 @@
Name: libnice Version: 0.1.14 -Release: 2%{?dist} +Release: 3%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz +Patch1: libnice-0.1.14-20171128.patch
+BuildRequires: autoconf +BuildRequires: automake BuildRequires: glib2-devel BuildRequires: gnutls-devel >= 2.12.0 BuildRequires: gobject-introspection-devel @@ -52,15 +55,6 @@ Requires: %{name}%{?_isa} = %{version}-%{release} The %{name}-gstreamer1 package contains a gstreamer 1.0 plugin for %{name}.
-%package examples -Summary: Simple %{name} usage examples -Group: Development/Libraries -Requires: %{name}%{?_isa} = %{version}-%{release} - -%description examples -The %{name}-examples package contains usage (simple, threaded and sdp) examples. - - %package devel Summary: Development files for %{name} Group: Development/Libraries @@ -75,12 +69,14 @@ developing applications that use %{name}.
%prep %setup -q +%patch1 -p1
%check #make check
%build +autoreconf -f -i %configure --disable-static 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 @@ -117,12 +113,6 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %{_libdir}/gstreamer-1.0/libgstnice.so
-%files examples -%{_bindir}/sdp-example -%{_bindir}/simple-example -%{_bindir}/threaded-example - - %files devel %{_includedir}/* %{_libdir}/*.so @@ -132,6 +122,11 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog +* Fri Feb 09 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-3 +- update to 0.1.14-70-gfb2f1f7 with alternate server fixes for SIPE +- add autoreconf build step +- remove examples subpackage as examples are no longer installed + * Wed Feb 07 2018 Fedora Release Engineering releng@fedoraproject.org - 0.1.14-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
commit b17a790f177dfb96d128af068bb7b391da8162df Author: Kamil Dudka kdudka@redhat.com Date: Fri Feb 9 16:47:14 2018 +0100
minor tweak of the description
... to make vim's syntax highlighting work properly on libnice.spec
diff --git a/libnice.spec b/libnice.spec index 74f0bee..7c8ffb1 100644 --- a/libnice.spec +++ b/libnice.spec @@ -24,7 +24,7 @@ BuildRequires: gupnp-igd-devel >= 0.1.2
%description -%{name} is an implementation of the IETF's draft Interactive Connectivity +%{name} is an implementation of the IETF draft Interactive Connectivity Establishment standard (ICE). ICE is useful for applications that want to establish peer-to-peer UDP data streams. It automates the process of traversing NATs and provides security against some attacks. Existing standards that use
commit 69cde2cab6ad89282caf3bd90253ee8dfe01ee7f Author: Fedora Release Engineering releng@fedoraproject.org Date: Wed Feb 7 22:49:23 2018 +0000
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
Signed-off-by: Fedora Release Engineering releng@fedoraproject.org
diff --git a/libnice.spec b/libnice.spec index f69c824..74f0bee 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@
Name: libnice Version: 0.1.14 -Release: 1%{?dist} +Release: 2%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries @@ -132,6 +132,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog +* Wed Feb 07 2018 Fedora Release Engineering releng@fedoraproject.org - 0.1.14-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + * Mon Jan 29 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-1 - Update to 0.1.14
commit 3feabe3b2d1f84cb307dff64326af6681d69f183 Author: Stefan Becker chemobejk@gmail.com Date: Thu May 4 09:40:36 2017 +0300
Update to 0.1.14
diff --git a/libnice-0.1.13-20160610.patch b/libnice-0.1.13-20160610.patch deleted file mode 100644 index a5f2edb..0000000 --- a/libnice-0.1.13-20160610.patch +++ /dev/null @@ -1,10771 +0,0 @@ -commit 30a0c230ae9b70c572060ad3037f68e102e4759a -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Jun 6 18:31:22 2016 -0400 - - conncheck: Remove pairs before freeing candidate - - Remove the whole pair before the candidate is - to be freed. - - https://phabricator.freedesktop.org/T7460 - -commit 71f7ed3eda829c3dc6afe9ed013c0ab826a1aa40 -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri Feb 19 15:01:03 2016 -0500 - - stun timer: Do 7 retransmissions as recommended - - Also reduce the normal timeout to make the test bearable. - - This is what RFC 5389 section 7.2.1 - - Differential Revision: https://phabricator.freedesktop.org/D1056 - Maniphest Task: https://phabricator.freedesktop.org/T3339 - -commit dc1e1b7a1b258fb54ba582d2fe77ccd159c9fe88 -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Jun 6 16:21:54 2016 -0400 - - timer: Maximum retransmission should include the original one - - We really care about the maximum transmissions, the first one counts. - -commit fad72879fa4a0896c55ac6fc5f77f6c05e369a2b -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri Jun 3 18:42:59 2016 -0400 - - pseudotcp: it's still a GObject - -commit f645ea6b11c167b1d7f4c5034f79664bdb8706d6 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Apr 14 13:32:51 2016 +0200 - - pseudotcp: Make sure duplicate ack representing losses have no data - - If they have data in them, they won't be recognized as duplicate acks by - the sender. - -commit adba0d4a51f4e0deac888ab08f7976cef70a8e99 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Apr 14 09:50:09 2016 +0200 - - pseudotcp: Implement NewReno timestamp heuristic - - This allows the sender to enter fast retransmit after a timeout because - it can now detect that three duplicate acks are caused by a packet loss. - - As specific in RFC 6582 section 4.2. - -commit 1f532aeb6bf5b5b3042c445e677988f3327b1cb5 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Apr 6 10:46:46 2016 +0300 - - pseudotcp: Set min RTO to 1 second - - This is recommended by RFC 6298 - -commit b5952012bc5b403550f8dd9945d92747323acfc4 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Apr 6 01:59:36 2016 +0300 - - pseudotcp: Implement full NewReno - -commit e31932bdf25ce545a88fe6078b9557bc4d9e6365 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Feb 2 16:59:18 2016 -0500 - - pseudotcp: Make debug more useful - -commit 8ccb2c1711a2e5cdebf9411764fd92d5a089ffbf -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Jan 12 20:14:48 2016 -0500 - - pseudotcp: Separate default and maximum MTU - - Accept packets much beyond the default MTU, but - set a reasonable default MTU for sending of 1400 - -commit cb644b2baa681f510a79e158cd50c490dcfa5186 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Dec 24 01:15:59 2015 -0500 - - pseudotcp: close local socket on initial transmission error - - This is required as no retransmissions will happen - -commit 23331ff2add5a60d611eee2093614d1fb8749164 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Sep 17 21:26:36 2015 -0400 - - pseudotcp: Export more symbols for PseudoTCP - -commit 026c15a838554c30ea96a59b08a2064b61d62736 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Sep 17 15:00:27 2015 -0400 - - pseudotcp: Make structs definitions private - -commit 11d4bb9783a69363de80ff49638030ba892a93fe -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Jun 23 15:42:33 2015 +0100 - - pseudotcp: Correct behaviour of buffer size methods when part-closed - - Correct the behaviour of pseudo_tcp_socket_get_available_bytes() and - pseudo_tcp_get_available_send_space() when the socket is not in - TCP_ESTABLISHED state. It’s still permissible to send and receive up - until the local side calls pseudo_tcp_socket_close(), which means we - may be in state TCP_ESTABLISHED *or TCP_CLOSE_WAIT*. - -commit a9a149f529b3165543b52260d40a7855401841da -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Fri Jul 31 14:28:51 2015 +0100 - - pseudotcp: Fix EOS checks in high packet loss situations - - The state tracking previously assumed that if a FIN packet was sent, the - other side received it and the preceding packets, and hence it was - correct to sent an RST if an unexpected packet (such as a delayed - SYN-ACK) was received. - - In cases where there is high packet loss, this won’t work. For example, - peer A sends a SYN, it is received and peer B replies with a SYN-ACK - which is also received; then peer A sends its data and a FIN, which are - both dropped. Since it hasn’t received anything since the original SYN, - peer B resends its SYN-ACK. If that is received, peer A was incorrectly - treating it as an erroneous packet, and would then send a RST. In actual - fact, it should take this as a signal that the data and FIN packets were - dropped, and should resend them. - - TODO: Add unit tests - -commit 4dc2b5d9a01e3314d229fb9aa80884d84c45c1f0 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Fri Jul 31 14:19:30 2015 +0100 - - pseudotcp: Propagate error codes from transmit() to callers - - Otherwise we can’t easily differentiate between different transmission - failures; for example: underlying socket failures, versus retransmission - timeouts. - -commit 8a6bc000a5d7395bd9c4ff9942be26bd4f7d2e44 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Jun 23 15:40:13 2015 +0100 - - pseudotcp: Add more debug info on closing down a pseudo-TCP socket - -commit a72a93e51dba5d239e0607380bb4799cf1b0caca -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Jun 24 14:06:05 2015 +0100 - - pseudotcp: Fix pseudo_tcp_socket_recv() in state TCP_CLOSE_WAIT - - Previously, pseudo_tcp_socket_recv() would start returning 0 (EOS) as - soon as a FIN segment was received from the peer, even if there was - unread data already in the receive buffer. - - Instead, the unread data should all be accessible before - pseudo_tcp_socket_recv() starts returning 0. - -commit 02699917641922c9f1d337e3102f13a1ea1d83c4 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Jun 24 13:52:16 2015 +0100 - - pseudotcp: Fix retransmission of segments before handling a FIN - - Previously, if peer A transmitted one or more data segments (1), - followed by a FIN segment (2) to peer B, and segments 1 were - dropped, peer B would not request retransmission of them and would - instead continue with the FIN handshake. This effectively meant - segments 1 were lost without peer B realising. - - Fix this by only handling the FIN segment once its sequence number is - acknowledged in the receive window. - -commit b58e852de6183f2bda4e7d322a35d18edf5cbbed -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Jun 2 19:22:50 2016 -0400 - - socket: Assert trying to use free'd socket - - Cleanly returnign makes no sense and may hide - worse problems. - -commit baab2c3c7049f984cdca6ed622059c62ce8cebf7 -Author: Misha Uliutin mishau@microsoft.com -Date: Mon Apr 25 09:59:48 2016 +0300 - - component: Fix set TCP selected remote candidate - - https://phabricator.freedesktop.org/T7407 - -commit 6329509b86f3a6877a39fb59b7a1b535408db0ce -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Jun 2 19:00:17 2016 -0400 - - agent: Parse TURN packet on the right socket - - https://phabricator.freedesktop.org/T99 - -commit 2f0daa030a69ebb2dea4c1a6fc47699d0f6828aa -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Jun 2 17:34:27 2016 -0400 - - tests: Add TURN test - - This test depends on rfc5766-turn-server which must - be installed for this test to run. - -commit 75d332cc4b7d9ee76bdf92b38f9cc3f6dd94b796 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Tue May 31 11:42:44 2016 +0000 - - conncheck: mark discovered pairs with TCP passive as valid - - Doing so similarly to priv_process_response_check_for_reflexive(), - which also sets valid flag on discovered peer reflexive pairs. - - Fixes a regression in previously working scenario. - Differential Revision: https://phabricator.freedesktop.org/D1035 - -commit 1a23476513d487bb09afbc7fb4853169399312d7 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Jun 1 08:52:41 2016 +0000 - - test-icetcp: don't be sensitive to the signal order - - "new-selected-pair" may be emitted after "component-state-changed" - to READY, by which time the main loop might have gotten quit in - cb_component_state_changed(). Consequently, cb_new_selected_pair() could - miss to register the selected pair, ultimately leading to an assertion - failure in main(). - - We should wait for both selected pair and state change events to occur - before stopping the main loop. - - Differential Revision: https://phabricator.freedesktop.org/D1044 - -commit b559384734deb9ec934f5ff69814f3d90c6a36c1 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue May 31 17:31:18 2016 -0400 - - Revert "WIP" - - This reverts commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a. - -commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue May 31 17:31:12 2016 -0400 - - WIP - -commit 955323915c43b1a066399e61d0ab091f0b1d112b -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Tue May 31 09:27:03 2016 +0000 - - conncheck: fix pruning conn checks with TCP active sockets - - TCP active socket makes a NiceSocket for each peer in conn_check_send() - and this new socket is then stored as CandidateCheckPair's 'sockptr'. - We thus have to look also at the 'sockptr' value when eliminating - sockets which have received HUP from connection checks. - Differential Revision: https://phabricator.freedesktop.org/D1034 - -commit 2112ebba886d15fd96cc36b9fe3196834acaa892 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Mar 8 15:37:05 2016 -0500 - - agent: Remove socket on read error - - If a socket returned an error, remove it. - -commit 1949b89f3de6e45616187e86f542d26a003ea7a6 -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri Jan 15 22:40:27 2016 -0500 - - component: Add API to cleanly remove a base socket - -commit 93330f1f97e4f2a9ff09b602765e620cb279574b -Author: Olivier Crête olivier.crete@collabora.com -Date: Sat Feb 27 03:35:27 2016 -0500 - - agent: Fix udp-turn-over-tcp - - The TCP-based turns don't come pre-parsed unlike - the UDP variants! - -commit 7f6ddac880ee07530ae59f4c64a0a17417fb9e24 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Jan 27 18:56:13 2016 -0500 - - agent: Add force-relay property to force messages through the relay - - This allows implementing WebRTC privacy mode. - -commit c69d479edfaeb461ff2bc61cf7257ce0c2d273da -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Feb 10 16:29:57 2016 -0500 - - conncheck: Start conncheck on server reply if needed - - This only really applies in the force relay mode where there are - no local candidates. - -commit 1513ce23ff4279dad16e177a3fc779cb61074fa1 -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 8 16:44:47 2016 -0500 - - Replace g_malloc/g_new with g_alloca where possible - - This should reduce the overhead a bit. - -commit 524c1090cc2813bcb1be6f7f29a894c742a608f7 -Author: Fabrice Bellet fabrice@bellet.info -Date: Wed Apr 20 10:17:05 2016 +0000 - - conncheck: explain some corner cases - - This patch give details why some exceptions to the ICE spec are needed. - - Differential Revision: https://phabricator.freedesktop.org/D876 - -commit b05debeb95c13d162286c0a5c4076eee2ae7cf51 -Author: Fabrice Bellet fabrice@bellet.info -Date: Fri May 27 19:15:39 2016 -0400 - - conncheck: add a debug dump of the whole stream check list - - https://phabricator.freedesktop.org/D814 - -commit 71e271095032bd50ac2be2b5d60f4beb15801fa0 -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri May 27 18:50:59 2016 -0400 - - conncheck: fix the replay of early incoming connchecks - - This patch fixes a bug in the way the list of incoming checks - is handled. The code purges this list too early, before all ichecks - for a given component are processed. It happens because the component - is computed from each pair of the check list, instead of being passed - as a fixed parameter of the function. - - Differential Revision: https://phabricator.freedesktop.org/D882 - -commit acdc0b8bb3cd2d48afe82c8dd8396a3c245191d9 -Author: Fabrice Bellet fabrice@bellet.info -Date: Wed Apr 20 09:23:14 2016 +0000 - - stun: fix ice role conflict handling - - This patch fixes the role conflict handling in stun ICE usage, - according to RFC 5245, by adding including missing cases in the - test. The role switch not only depends of the comparison of the - stun ice-controlling/controlled attrib with the agent tie breaker - value, but it also depends on the current role of the agent. - - This patch also changes the value returned by - stun_usage_ice_conncheck_create_reply() when a role conflict exists - but doesn't change the role of the agent, causing an error stun - response. Previously, this case could not be differenciated by the - caller from a case with no role conflict. Now by examinating the - return value, and whether the control param changed, the caller - can check the four possibles situations. The stun test suite is - updated to match this change. - - Differential Revision: https://phabricator.freedesktop.org/D873 - -commit c90f93838db6a315ab2cbedaa92fed3f277b2103 -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri May 27 17:26:06 2016 -0400 - - conncheck: Make previous commit compile - - https://phabricator.freedesktop.org/T3324 - -commit d252feb553a423ee81482ff6b87e0033d9c046a6 -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 8 18:49:42 2016 -0500 - - discovery: Make sure each candidate has a unique priority - - This should fix compliance with RFC 5245 Section 4.1.2 - - https://phabricator.freedesktop.org/T3324 - -commit b72b9153d91a93a550c4ec40fce5f9a18e7eaac6 -Author: Olivier Crête olivier.crete@collabora.com -Date: Sun Sep 20 16:53:26 2015 -0400 - - agent: Restrict transitions to gathering - - Only allow transitions to gathering from disconnected or - failed states. - -commit 059a0e33c973a54c44f7c4fd1e766155f6078f80 -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri May 27 14:06:24 2016 -0400 - - conncheck: fix TCP active relay handling - - TCP active relay candidates use UDP TURN for their underlying socket. - Since 0a6c779f1f, socket->fileno of UDP TURN sockets is always NULL, - which caused a wrong code path to be chosen in conn_check_send(). - - We have to update the if-expression accordingly. - - Maniphest Tasks: T7442 - Differential Revision: https://phabricator.freedesktop.org/D1017 - -commit fc4d3aab5392f855dbda7ad0225bf0ad4e5fafb6 -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri May 27 11:29:22 2016 -0400 - - agent: ignore gathering failures on auto-generated IPs - - Candidate gathering is stopped when discovery_add_local_host_candidate() - returns HOST_CANDIDATE_CANT_CREATE_SOCKET. This may be too radical - a measure when other local addresses can still be able to generate - usable candidates. - - The issue was observed by a user who had an IPv6 address with tentative - flag on one of the interfaces. That single failing address was causing - the whole gathering process to end with no candidates found. - - Still, don't do this if nice_agent_add_local_address() has been called. - In that case, the user really cares about the addresses and if there's - any problem, the process should fail. - - https://phabricator.freedesktop.org/D1016 - -commit 1fb6401d9d5dbee8ba28a20f3d787a95f13d1883 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Mar 1 15:27:46 2016 -0500 - - candidate: Give lower priority to TCP relayed candidates - -commit 8ee6d1bbed87fda37ade7b5c5d9483f41037b06a -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 29 16:11:18 2016 -0500 - - udp-turn: Fix binding timeout leak - -commit 8f1f615e92cd56ad4d8487457c2fde2c4aaa51d9 -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Feb 24 22:53:08 2016 -0500 - - conncheck: Update selected pair if necessary - -commit 65f2eda04c1c73cc7ebc3df2032d528eedc236e1 -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 22 19:36:58 2016 -0500 - - conncheck: Don't reset keepalive timer on next keepalive - - If the keepalive is still being re-send, just let the retries do their - job. If they don't get a reply, then declare the attempt failed. - -commit 1ab9d7c104978ea1904aaaad708c1c8c23c77592 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu May 26 16:05:36 2016 -0400 - - conncheck: Separate valid and succeded states - - RFC 5245 specifies that when a mapped-address differs from the address - from the request was sent, the mapped-address is used to select the - valid pair, but the source address of the check is used to select the - pair that succeeded, so they are not the same. - -commit 0a6c779f1f24099db2c1cd34cd339e240682525d -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri Feb 19 20:47:08 2016 -0500 - - udp-turn: Don't expose GSocket - - UDP turn sockets should never be read frm directly. - Because they may share the same socket with the non-relay, - so the incoming data may not be relayed and then the NiceSocket - API doesn't allow returning the base socket as the source. - -commit 5b27b028d8ad89214dc7b1ecd018f56aa0333b9c -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Feb 18 14:20:52 2016 -0500 - - conncheck: Make very frequent debug verbose-only - -commit b7c2eabfd0bd8c1321d9e8450caa8fa6b6ecb5ab -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 15 19:09:58 2016 -0500 - - debug: Enable based on G_MESSAGES_DEBUG - -commit acfe2b1f366d5f33314db7e9878225f2a70358ef -Author: Olivier Crête olivier.crete@collabora.com -Date: Fri Feb 12 01:01:37 2016 -0500 - - agent: Don't emit signal in the middle of recv call - -commit 716c5805d5b73e94bc6af3637dfd50266150734e -Author: Olivier Crête olivier.crete@collabora.com -Date: Wed Feb 10 19:38:52 2016 -0500 - - agent: Update type of peer-reflexive candidate on trickled candidate - - If a remote candidate matches an already discovered peer-reflexive candidate, - then the type can be updated to the real type and the foundation - can be set correctly. - -commit fc0d3744ebc03f8137866170594968ba61e6be30 -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Feb 9 12:52:45 2016 -0500 - - agent: Only try to use the address of the same family to connect to TURN - - Using a IPv6 local address to connect to a IPv4 relay just creates an - extra discovery attempt that will not provide something useful. - -commit c129b05a469b59b576f4700fe9bfe3adca0a48dc -Author: Olivier Crête olivier.crete@collabora.com -Date: Sun Feb 7 19:48:07 2016 -0500 - - stun turn usage: Only send the username if short term creds or nonce present - - This is recommended by the STUN RFC 5389. - -commit 501f9a82e47076cda0deab8cf54758b608e899aa -Author: Olivier Crête olivier.crete@collabora.com -Date: Sun Feb 7 19:41:52 2016 -0500 - - turn: Cache the nonce & realm to remove useless round trips - - Instead of re-discovering the nonce and realm for every request, cache them - in th socket. - -commit 82ea4d71728af95cf0c7bff478f69342a461134b -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Feb 9 11:18:30 2016 -0500 - - conncheck: Stay READY if a new nominated pairs comes in - -commit 729bd3bb215f0a9a67293dea6df3f8f234eea0ac -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 8 21:04:24 2016 -0500 - - conncheck: Deduplicate conncheck stopping code - -commit f122e4174d19c60bc434f3986f5c08f8673344bd -Author: Olivier Crête olivier.crete@collabora.com -Date: Mon Feb 8 19:41:28 2016 -0500 - - Reset to connecting if reconnected after failed - -commit 9c1a41b06ab459ce33f32d25ce258fb21ba49047 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Jan 14 17:59:16 2016 -0500 - - agent: Add warning on ignored result - -commit a357b17f5f3415320b9ec7122738396ccd998521 -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Apr 4 23:02:52 2016 +0100 - - conncheck: display controlling mode of stun requests - - This patch makes the debug log more explicit about the agent - controlling role for each stun request sent. It helps to debug - role conflict resolution. - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D877 - -commit b986d6e5f2ee0b7b0e09031c1a369bf89153e4c5 -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Apr 4 22:38:07 2016 +0100 - - agent: remove newline from debug output - - Just a cosmetic fix. - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D872 - -commit e0ed4fb3a236710846d9129438e0077782569633 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Mon Apr 4 21:52:29 2016 +0100 - - socket: refactor nice_socket_is_base_of() - - • rename to nice_socket_is_based_on() and swap the order of arguments - accordingly; the implementation doesn't have to use the confusing - 'return other->is_base_of()' pattern anymore - • fix potential NULL dereferences - - The argument order in agent_recv_message_unlocked() was already wrongly - swapped in 1732c7d6 and thus this commit isn't changing it back because - that order has become the correct one. - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D866 - -commit 38268e53fde8cd97055d88d2066c0016fe04b31b -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Mon Apr 4 21:46:05 2016 +0100 - - socket: fix wrong function called in nice_socket_is_base_of() - - We have to call is_base_of "virtual function pointer" of 'other' - object, not 'sock', since 'other' is the structure whose base - NiceSocket we need to get from its private data. - - For instance calling nice_socket_is_base_of() with 'sock' and 'other' - being respectively pseudo-SSL and UDP-TURN-over-TCP invoked is_base_of - variant for pseudo-SSL, casting other->priv into PseudoSSLPriv *, but - other->priv is actually TurnTcpPriv *. It must be called the other way - around. - - https://phabricator.freedesktop.org/T7335 - https://phabricator.freedesktop.org/T7336 - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Reviewed-by: José Antonio Santos Cadenas santoscadenas@gmail.com - Reviewed-by: Philip Withnall philip@tecnocode.co.uk - Reviewed-by: José Antonio Santos Cadenas santoscadenas@gmail.com - Differential Revision: https://phabricator.freedesktop.org/D785 - -commit 7037ab4cf384edd9f700bc221a9d980b30d9c64f -Author: Fabrice Bellet fabrice@bellet.info -Date: Mon Apr 4 21:38:59 2016 +0100 - - conncheck: implement a "triggered queue" list - - The checks should not be sent immediately in priv_conn_check_initiate(), - but be put into the "triggered queue", see "7.2.1.4 Triggered Checks". - This patch implements this triggered checks list, and uses it to enforce a - pacing of STUN transactions, no more than one per Ta ms, according to - "B.1. Pacing of STUN Transactions". - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Reviewed-by: Philip Withnall philip@tecnocode.co.uk - Differential Revision: https://phabricator.freedesktop.org/D802 - -commit 1732c7d6a7a104438412309373818e493a2504c9 -Author: Olivier Crête olivier.crete@collabora.com -Date: Sun Mar 6 15:16:18 2016 -0500 - - agent: Fix argument order - - Fixes crash reported on https://phabricator.freedesktop.org/D786 - -commit aac283e0fa75b226fe2431403761ebd45e4f5614 -Author: Fabrice Bellet fabrice@bellet.info -Date: Sat Mar 5 18:46:48 2016 +0000 - - ice: fix the debug of the presence of the controlling/controlled attrib - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D807 - -commit ed75d55cf279613bb736f7646d3010d816797ddf -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Mar 2 00:01:19 2016 +0000 - - agent: fix relay candidate discovery on hosts having several IPs - - When a message is received from a TURN server and we manage to find a - local relay candidate with matching stream and component IDs, we should - also check whether the message came from the candidate's respective - socket. - - We should do this because there might still be some pending TURN - candidate discovery with the same server from a different local host IP - and the message may be a response to our allocate request. If - nice_udp_turn_socket_parse_recv_message() is passed such request, it can - make some wrong assumptions and modify it like in the case of reliable - UDP-TURN-OVER-TCP by removing (supposed) RFC4571 framing, which in turn - causes the reply to be unrecognized and discarded. - - Because of this, any subsequent replies following the first successful - allocate response from that server couldn't create any additional relay - candidates. - - Maniphest Tasks: https://phabricator.freedesktop.org/T7336 - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D786 - -commit 1493a381d5bf6e15348c2bc17270f45b69cb70d2 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 23:50:11 2016 +0000 - - build: Update autogen.sh from GNOME template - - https://wiki.gnome.org/Projects/GnomeCommon/Migration#autogen.sh - -commit bc620c0de966b47d761bbcb1279dac6221a5a30e -Author: Olivier Crête olivier.crete@collabora.com -Date: Tue Mar 1 23:29:10 2016 +0000 - - component.c: Fix memory leak - - If nicesocket is not added to a component it will be leaked. - - This is the case of active tcp sockets - - Change-Id: I57fefffef71d35ce9871139ee1064181f6fe125b - Reviewed-by: José Antonio Santos Cadenas santoscadenas@gmail.com - Differential Revision: https://phabricator.freedesktop.org/D822 - -commit 38c5e66886cb8138d6be57b8a4721d9b42a358b7 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Mar 1 23:23:14 2016 +0000 - - agent: Use provided CandidatePair rather than re-finding a pair - - In priv_update_selected_pair(), commit 57393333 changed the code to - re-find a CandidatePair matching the given lfoundation and rfoundation. - However, the foundation does not uniquely identify candidate pairs, - and if we’re aiming to set a specific candidate pair as the selected - pair, this could result in the wrong pair being selected. - - This can happen when handling multiple similar candidate pairs, such as - when generating peer reflexive candidates from multiple sources. - - See https://tools.ietf.org/html/rfc5245#section-2.4. - - Originally spotted by Fabrice Bellet in - https://phabricator.freedesktop.org/T3557. - - Reviewed-by: José Antonio Santos Cadenas santoscadenas@gmail.com - Differential Revision: https://phabricator.freedesktop.org/D742 - -commit 70981d41edf46a09393017f3de34748fcecea046 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 23:05:20 2016 +0000 - - simple-example: transmission can begin earlier than in ready state - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D819 - -commit 47eaf50a99d91c4a666f05c4de24613706847c88 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 23:04:14 2016 +0000 - - test-new-dribble: wait until ragent reaches state completed - - The test didn't let enough time for ragent to reach the completed state - after obtaining its remote candidates and switching to connecting state. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D817 - -commit 4e68244a51698aefdf44dd1ceb17b95275e655bf -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 23:02:52 2016 +0000 - - conncheck: reorder the connection list when priorities are updated - - The update of pairs priorities due to agent role change requires the - conncheck list to be reordered to reflect this modification. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D806 - -commit 0f1e6c64515871298b115b497b019506b8065235 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 23:01:14 2016 +0000 - - conncheck: fix keepalive stun agent initialisation - - With this patch, we send keepalive binding requests using agent - compatibility flags, instead of RFC 3489 classic stun. The peer stun - agent will known how to handle it, and won't be confused by the - uncompatible RFC 3489 message, causing "no cookie" errors in the debug - log. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D804 - -commit 17488a20d8db8ea1f8fa2d7a44090070407d6db8 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 22:58:15 2016 +0000 - - conncheck: foundations are shared across streams - - This patch fixes a bug where the foundation definition shouldn't take - into account the stream the pair belongs to. This is important, because - the ordinary checks algorithm will change pair state from Frozen to - Waiting, by selecting pairs from other streams sharing the same - foundation than already succeeded pairs. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D815 - -commit 3ce45c25af238fb4d9a040abb44597531140db2d -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 22:37:33 2016 +0000 - - test-priority: ignore the local preference - - The local preference depends on the rank of the IP address in the list - of all IP addresses available of the box running the test. As this value - is not fixed we ignore it in the test. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D818 - -commit c309905ff446bed2dc47811023b98bc586a02d63 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 22:33:51 2016 +0000 - - conncheck: nominate only one matching pair - - This patch fixes a bug in priv_mark_pair_nominated(), where the local - candidate was not passed to the function, so removing the possibility to - find which local candidate the check was sent to. - - Reviewed-by: Philip Withnall philip@tecnocode.co.uk - Differential Revision: https://phabricator.freedesktop.org/D808 - -commit 80973c096de872983bc60cd28a84653931f8d601 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Tue Mar 1 22:28:35 2016 +0000 - - conncheck: add more debug information - - Add a more debug details, specifically in some places, it is interesting - to have the src and dst IP addresses of the pairs being checked, and - also to make the difference between log related to different stream ids. - - Reviewed-by: Philip Withnall philip@tecnocode.co.uk - Differential Revision: https://phabricator.freedesktop.org/D803 - -commit 41ab61def82aa275649afd5f4a3fcb43e75fb360 -Author: Mike Ruprecht cmaiku@gmail.com -Date: Mon Jan 18 12:42:46 2016 +0000 - - agent: Fix not setting UPnP timeout on second gather_candidates() - - If the first call to nice_agent_gather_candidates() partially succeeds - (setting a UPnP agent and timeout), then fails before starting - gathering, a second call to nice_agent_gather_candidates() would fail to - set a new UPnP timeout because the UPnP initialisation block would be - skipped. That means gathering would never succeed due to timing out on - UPnP. - - Fix that by setting the UPnP timeout whenever a new pending UPnP mapping - is added. - - https://phabricator.freedesktop.org/T3534 - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - -commit 9638bc132f909177f6ecfb86cdd17af557a35c56 -Author: Jose Antonio Santos Cadenas santoscadenas@gmail.com -Date: Thu Dec 3 15:01:35 2015 +0100 - - configure.ac: Update glib version - - As udp-bsd.ccode is using G_IO_ERROR_CONNECTION_CLOSED glib 2.44 - is required. - - Change-Id: I1bb63f2484c513c58eeec312ba0835164604c40c - Reviewed-by: Philip Withnall philip@tecnocode.co.uk - https://phabricator.freedesktop.org/T3492 - -commit 3e71f42c8b15790e252d850ba42a7ae7e7cc69a9 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Oct 7 19:03:58 2015 +0100 - - pseudotcp: Use labs() rather than abs() for handling long integers - - This fixes a compiler warning and prevents a possible truncation. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D345 - -commit 53283c218a5eb7a29e7019ac320e74f9dbe4b3fc -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Mon Jun 22 11:30:31 2015 +0100 - - tests: Enable G_MESSAGES_DEBUG for all unit tests - - Now that we’re using automake’s parallel test harness, it automatically - redirects all the debug log spew away from the console, so we should - always have it enabled. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D292 - -commit aef748ec7d0b06067312e5cc761a6375cd0f699c -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Thu Oct 1 17:52:08 2015 +0100 - - build: Set repository callsign in .arcconfig - - This fixes `arc diff` to select the right repository when submitting - patches. - -commit 008739a5a60e591629e38da9b8b7065dbc2c746f -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 14:57:10 2015 +0100 - - agent: Correctly namespace Component and its methods - - Remove all references to the old, unnamespaced versions. This should - cause no functional changes. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D309 - -commit 529bc193d56522b10a4de83409396b3316863934 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 14:34:34 2015 +0100 - - agent: Correctly namespace Stream and its methods - - Remove all references to the old, unnamespaced versions. This should - cause no functional changes. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D308 - -commit ef2b58f64887546e426dd8cda382f2908f84caca -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 14:11:14 2015 +0100 - - agent: Turn Component into a GObject - - This makes it reference-counted. This will be useful for allowing - GDatagramBased and GIOStream objects to hold references to the stream - and component they are interested in, allowing removal of the global - NiceAgent lock previously needed to look up the component for every I/O - operation. - - Deprecate all the old methods until it’s properly namespaced. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D307 - -commit 1f08419382c52c7e796da06c9271a362aa60333d -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 14:10:32 2015 +0100 - - agent: Turn Stream into a GObject - - This makes it reference-counted. This will be useful for allowing - GDatagramBased and GIOStream objects to hold references to the stream - and component they are interested in, allowing removal of the global - NiceAgent lock previously needed to look up the component for every I/O - operation. - - It also means that nice_stream_close() could eventually become - asynchronous, which would fix a few race conditions. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D306 - -commit da70716162b2085ca4db6e7efd446fcda7bd488d -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 17:59:04 2015 +0100 - - tests: Update expected priority values in test-priority - - This is a follow up to T3324, to update the test case to match the new - values generated. - - Bug: https://phabricator.freedesktop.org/T3324 - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D301 - -commit 66e47aa39f9cd3666e610fab78caa0c7d8f9c410 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 17:54:21 2015 +0100 - - tests: Use g_assert_cmpuint() to make test failures easier to diagnose - - Now we can actually see the priority numbers which are unequal. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D300 - -commit 1ae15f66af2af17e991ab028ca16a1200fd5f4e5 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 17:53:14 2015 +0100 - - tests: Set candidate addresses in test-priority - - This avoids an assertion failure in nice_address_to_string() when the - addresses are compared for priority. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D299 - -commit ea3348020da586f20e1a64c9732405e730563616 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 17:46:47 2015 +0100 - - agent: Remove redundant GLIB_CHECK_VERSION macros - - We depend on GLib 2.36.0, which is a higher version than any of these - version checks cared about, so they were all trivially true or false. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D298 - -commit 9f10231fc787e4683e896bf35f086906a5b16c03 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 17:44:44 2015 +0100 - - tests: Remove g_thread_init() calls - - We depend on GLib 2.36.0; g_thread_init() has been deprecated since - 2.32.0, when thread initialisation was changed to happen automatically. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D297 - -commit dac3e280d6f3fd792f1d79313debd03c0df282c4 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 30 17:41:36 2015 +0100 - - tests: Remove g_type_init() calls - - We depend on GLib 2.36.0, which deprecated g_type_init() since GType - initialisation is now done automatically. - - Reviewed-by: Olivier Crête olivier.crete@collabora.com - Differential Revision: https://phabricator.freedesktop.org/D296 - -commit fae0f9d37c6e34f34c92dee85384c29e565bdbce -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Fri Sep 11 11:57:54 2015 +0100 - - conncheck: rename priv_process_response_check_for_peer_reflexive() - - Renamed the function to priv_process_response_check_for_reflexive() - because it now checks also for server reflexive candidates. - - Updated the documentation to indicate that the function never returns - NULL. - - Maniphest Tasks: https://phabricator.freedesktop.org/T115 - Differential Revision: https://phabricator.freedesktop.org/D243 - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - -commit 02852728ef347f5cb4c9227848b266c72b5fe38b -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Fri Sep 11 11:56:02 2015 +0100 - - conncheck: generate candidate pair for valid srflx candidate - - In priv_process_response_check_for_peer_reflexive(), mere presence of a candidate in local_candidates doesn't mean there's also some candidate - pair in conncheck_list using it - for instance that candidate may be server reflexive, for which no check pairs are initially created (see - conn_check_add_for_candidate_pair()). - - If we fail to find corresponding pair upon receiving such candidate's IP in a conncheck response's XOR-MAPPED-ADDRESS attribute, we shall add a - new one in a similar way we would add a new pair for a just discovered peer reflexive candidate. - - Previous priv_process_response_check_for_peer_reflexive() implementation would return NULL, causing a CandidateCheckPair with local candidate of - type HOST to be wrongly selected even though the local host IP might not be directly accessible by the remote counterpart (e.g. it's an address - on a private network segment). In practice this was coming through as a duplex connection that libnice was reporting as properly established, - but only one direction of the communication was actually working. - - Maniphest Tasks: https://phabricator.freedesktop.org/T115 - Differential Revision: https://phabricator.freedesktop.org/D242 - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - -commit 84eaa12b0b1a76c45ce2d77294e0477c10552cd4 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Fri Sep 11 11:33:51 2015 +0100 - - agent: check for base socket in _tcp_sock_is_writable() - - The argument passed into the callback is always a base (TCP/UDP) socket, - which can't be directly compared with local candidate's sockptr (may be - TURN, http, or other socket wrapping another one). We're in fact - interested whether sock is a base socket of sockptr. - - Maniphest Tasks: T114 - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D241 - -commit 837c8953fe87bdd5d5bccc444e72739100578ef8 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Fri Sep 11 11:29:39 2015 +0100 - - socket: add nice_socket_is_base_of() - - This will be used in the next commit. - - Maniphest Tasks: T114 - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Differential Revision: https://phabricator.freedesktop.org/D240 - -commit c6bc33c031493e0db11a1f57055a656f6428c60a -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Thu Jul 10 08:32:04 2014 +0100 - - build: Bump GLib dependency to 2.36 - - This is needed for G_IO_ERROR_BROKEN_PIPE, which is used in the I/O - stream code. - - Reported by Emanuele Bizzarri emabiz76@gmail.com on the mailing list. - -commit 757f8aecdb4fda86b2bbac828a3acc528d8eb8bc -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Fri Sep 4 08:14:08 2015 +0100 - - stun: Disable debug by default - - To match debug_enable in agent/debug.c. Debug can still be enabled by - calling stun_debug_enable() or nice_debug_enable(). - - Spotted on the mailing list by Tom Chen. - -commit 2eaa8b3277f4f39515ff5dc7b512a44fd79e7275 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Mon Jun 29 16:30:12 2015 +0100 - - agent: Add assertions to check component state transitions are valid - - There is no point in the NiceComponents having a state machine if the - state transition graph is not documented or enforced. Document and - enforce it. - - http://phabricator.freedesktop.org/T120 - -commit 3f54b333525e2a4ae35e0be439062900fb8ab7c3 -Merge: 1034832 181ad3a -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 2 16:44:45 2015 +0100 - - ms-ice: ensure distinct candidate priority for multihomed hosts - - Summary: - Offering multiple host candidates with equal priorities could lead to unpredictable candidate pair selection by our counterparty. - - Fixes call disconnection by MS Lync client after 30 seconds while VPN (2nd IP) was active on libnice host. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D234 - -commit 181ad3a9bf54b9d6c4e0921ae148bab6d9fd0b3a -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Sep 2 16:43:02 2015 +0100 - - candidate: use distinct priority also for non-MS compatibilities - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D239 - -commit 799b322d6c654b864b5442cdaaa62aaee81d4901 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Sep 2 16:41:49 2015 +0100 - - conncheck: give temporary candidate_priority a base_addr - - Summary: - Fixes "(nice_address_to_string): should not be reached" errors when calling nice_candidate_ms_ice_priority() because of invalid NiceAddress. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D238 - -commit 85cd01c1396ef244f90e0d9c995fab29ed121f23 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Sep 2 16:41:49 2015 +0100 - - ms-ice: ensure distinct candidate priority for multihomed hosts - - Summary: - Offering multiple host candidates with equal priorities could lead to unpredictable candidate pair selection by our counterparty. - - Fixes call disconnection by MS Lync client after 30 seconds while VPN (2nd IP) was active on libnice host. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D234 - -commit 88ed42619049ac1e3fe3a6e481df8aeb95033ac0 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Sep 2 16:41:48 2015 +0100 - - discovery: assign candidate priority after base_addr is set - - Summary: So that we can take the base address into account in the calculation. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D235 - -commit 10348322a960258043363e7c84e78c4821c90412 -Merge: abdab05 ab4ced5 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Sep 2 16:34:01 2015 +0100 - - ms-turn: don't wait for a reply to STUN_SEND request - - Maniphest Tasks: T126 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D223 - -commit ab4ced5a46a7edba7cd49c0495bc41cd8fff7cc2 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Wed Sep 2 16:32:05 2015 +0100 - - ms-turn: don't wait for a reply to STUN_SEND request - - As per [MS-TURN] Section 2.2.1, TURN message type 0x0104 "Send request - response" isn't supported and the TURN server MUST NOT send them. Thus, - libnice should not remember Send requests in agent->sent_ids because - without replies coming, the number of allowed pending transaction gets - quickly exhausted, causing our data packets to be dropped until a - request timeout frees some space in the queue. - - This behavior resulted in choppy reception of our audio on a Lync client - when connected via Lync Edge (TURN) Server. - - Maniphest Tasks: T126 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision: https://phabricator.freedesktop.org/D223 - -commit abdab053af41068406caf95e80a64c512fd3db90 -Merge: 03d11b4 490b16f -Author: Philip Withnall philip@tecnocode.co.uk -Date: Sat Aug 29 23:26:00 2015 +0100 - - Creating TCP sockets with TCP_NODELAY option set to TRUE - - Summary: - Disable Nagling for underlying TCP sockets used by libnice, because they - are typically used for streaming applications, or for pseudo-TCP; the - bandwidth in both cases is harmed by Nagling. - - Based on a patch by Vadim Genkin. - - Maniphest Tasks: T3317 - - Reviewers: vadimgenkin, pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall, vadimgenkin - - Differential Revision: https://phabricator.freedesktop.org/D230 - -commit 490b16f400284c5df6508fd095d592efdfbc62ae -Author: Philip Withnall philip@tecnocode.co.uk -Date: Sat Aug 29 22:39:56 2015 +0100 - - Creating TCP sockets with TCP_NODELAY option set to TRUE - - Disable Nagling for underlying TCP sockets used by libnice, because they - are typically used for streaming applications, or for pseudo-TCP; the - bandwidth in both cases is harmed by Nagling. - - Based on a patch by Vadim Genkin. - - Maniphest Tasks: T3317 - - Reviewers: pwithnall - - Projects: #libnice - - Subscribers: pwithnall, vadimgenkin - - Differential Revision: https://phabricator.freedesktop.org/D230 - -commit 03d11b49b0ac14ff320192562df57898ea9f94b4 -Merge: dddca10 1c34734 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Sat Aug 29 20:05:00 2015 +0100 - - Fix agent leak in case component socket is reset remotely - - Summary: The patch fixes the issue where agent reference count is not properly decremented causing instance leak in cases where component's socket is reset remotely. - - Reviewers: #libnice, pwithnall - - Projects: #libnice - - Reviewed By: #libnice, pwithnall - - Subscribers: pwithnall, maximgolunov - - Differential Revision: https://phabricator.freedesktop.org/D236 - -commit 1c34734cd105c6cca0ec8bbb908002c4bfb007b3 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Sat Aug 29 20:04:03 2015 +0100 - - Fix agent leak in case component socket is reset remotely - - Summary: The patch fixes the issue where agent reference count is not properly decremented causing instance leak in cases where component's socket is reset remotely. - - Reviewers: #libnice, pwithnall - - Projects: #libnice - - Reviewed By: #libnice, pwithnall - - Subscribers: pwithnall, maximgolunov - - Differential Revision: https://phabricator.freedesktop.org/D236 - -commit dddca10ceff20e3578fc901b9919c3d20f87b7b6 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Aug 19 09:24:27 2015 +0100 - - build: Update .gitignore - - Add stun/usages/.dirstamp. - -commit 6c9afb4db35168cc699ff0aa2e56b127f3706083 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Aug 19 09:22:49 2015 +0100 - - build: Fix multiple definition of CLEANFILES - - It’s already defined in common.mk. - -commit 041158c4fe36c4af6d56cd4445946d7f5e5be57d -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Aug 19 09:20:01 2015 +0100 - - build: Add .arcconfig file - - This completes the transition to Phabricator; everyone should be using - the same project settings now. - - https://phabricator.freedesktop.org/tag/libnice/ - -commit 8f2a14b1d6af73e91c5712898e8a3e97308920a6 -Merge: e5e77b6 ad0003b -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Aug 19 09:19:18 2015 +0100 - - socket: Handle ECONNRESET as EWOULDBLOCK on Windows - - Summary: - Some versions of Windows can return ECONNRESET for UDP recvmsg() calls - if they would otherwise block. Hence, handle the two equivalently; this - should not affect behaviour on Linux, which apparently does not return - ECONNRESET for UDP recvmsg() calls at all. - - https://phabricator.freedesktop.org/T121 - - Maniphest Tasks: T121 - - Reviewers: ocrete - - Projects: #libnice - - Reviewed By: ocrete - - Subscribers: stwiname, felixSchl - - Differential Revision: https://phabricator.freedesktop.org/D227 - -commit e5e77b67fb6152dd9fb0af3d7410a428299504d3 -Merge: 6835e7b a2e25cf -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Wed Aug 19 09:16:53 2015 +0100 - - socket: Close base socket for a TCP passive socket when closing parent - - Summary: - Otherwise the base socket will leak. Spotted by Vadim Genkin. - - https://phabricator.freedesktop.org/T125 - - Maniphest Tasks: T125 - - Reviewers: ocrete - - Projects: #libnice - - Reviewed By: ocrete - - Subscribers: vadimgenkin - - Differential Revision: https://phabricator.freedesktop.org/D228 - -commit a2e25cf49b24c2012e8e9058ecc7e62f1e027cb3 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Aug 18 14:58:23 2015 +0100 - - socket: Close base socket for a TCP passive socket when closing parent - - Otherwise the base socket will leak. Spotted by Vadim Genkin. - - https://phabricator.freedesktop.org/T125 - -commit 6835e7b7f4a20078d508444659c636fc98e680fc -Merge: e1f748c 6b3e59c -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Aug 18 14:03:41 2015 +0100 - - agent: Remove unused inet_pton() function - - Summary: - As spotted by Felix felixschlitter@gmail.com. This is a static - function which is totally unused in this compilation unit and is causing - build failures with `-Werror=unused-function`. - - Maniphest Tasks: T123 - - Reviewers: felixSchl, ocrete - - Projects: #libnice - - Differential Revision: https://phabricator.freedesktop.org/D221 - -commit 6b3e59c5a670d5117ed293ae612082dcd047ba8a -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Jun 30 14:39:51 2015 +0100 - - agent: Remove unused inet_pton() function - - As spotted by Felix felixschlitter@gmail.com. This is a static - function which is totally unused in this compilation unit and is causing - build failures with -Werror=unused-function. - - http://phabricator.freedesktop.org/T123 - -commit ad0003b48414c789a1191fd5f44fec41e694dfa0 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Aug 18 13:33:23 2015 +0100 - - socket: Handle ECONNRESET as EWOULDBLOCK on Windows - - Some versions of Windows can return ECONNRESET for UDP recvmsg() calls - if they would otherwise block. Hence, handle the two equivalently; this - should not affect behaviour on Linux, which apparently does not return - ECONNRESET for UDP recvmsg() calls at all. - - https://phabricator.freedesktop.org/T121 - -commit e1f748cccacd81cce4db338d0203dc49bc915a30 -Author: Jakub Adam jakub.adam@ktknet.cz -Date: Thu Jun 18 09:05:21 2015 +0200 - - conncheck: set writable callback to socket from TCP active connect - - A new socket created in nice_tcp_active_socket_connect() should have its - writable callback set, because it's possible for it to become a base - socket of a peer reflexive candidate, if some is discovered by - connection checks on that TCP active candidate. - - Previously, when such prflx candidate became selected, without write_cb - on the socket the agent was never notified about it becoming writable - again after the socket's buffer got filled up. This caused the data flow - to hang permanently. - - Reviewed-by: Philip Withnall philip.withnall@collabora.co.uk - Reviewed-by: Olivier Crête olivier.crete@collabora.com - - http://phabricator.freedesktop.org/T117 - -commit cd61af3114a51df53619fb0460ace2b3660fc5da -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Tue Jun 30 14:29:43 2015 +0100 - - pseudotcp: Only define errnos on Windows if not already defined - - Recent versions of MinGW define at least ECONNABORTED and EAFNOSUPPORT, - so only define the various socket errnos if they are not defined - already. - - Based on a patch by Alexey Pawlow alexey.pawlow@gmail.com and Felix - felixschlitter@gmail.com. - - Reviewed-by: Olivier Crete olivier.crete@collabora.com - Reviewed-by: Felix Schlitter felixschlitter@gmail.com - - http://phabricator.freedesktop.org/T122 - -commit 3cccc311b1becf4307f5a4004734d8f7b2cf84f6 -Author: Felix Schlitter felixschlitter@gmail.com -Date: Tue Jun 30 07:37:31 2015 +1200 - - Use G_GSIZE_FORMAT instead of %zu - - The C runtime on windows does not implement a printf that understands - %zu and other C99 format specifiers. Use G_GSIZE_FORMAT instead. This - is further consistent with the rest of the code base that makes use of - G_GSIZE_FORMAT throughout. - - https://github.com/libnice/libnice/pull/3 - -commit c4d5ec572ae0ede14d13d9ce5b193cdcb36b07a1 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Sep 18 19:33:10 2014 -0400 - - Split "verbose" on-every-packet messages to a verbose log - - This way, the regular log will only contain connection-time information. - -commit 2d24ef532d1b6ec684f28d52aa0592dfdfb7e067 -Author: Olivier Crête olivier.crete@collabora.com -Date: Thu Sep 18 19:33:06 2014 -0400 - - stun: Remove annoying non-error on non-STUN packet - -commit 81a929ac141aae66b6450e8ce93cb357ed404cda -Author: Timo Gurr timo.gurr@gmail.com -Date: Mon Jun 1 16:10:16 2015 +0200 - - configure: Fix configure failure when building without gstreamer support - - Error introduced in 20ea22e0a11a9bdfe4d8125b68083249b694338a, resulting in a - configure/build error when building without gstreamer: - - configure: error: conditional "HAVE_GST_CHECK" was never defined. - Usually this means the macro was only invoked conditionally. - - https://bugs.freedesktop.org/show_bug.cgi?id=90801 - -commit d3a7b315ec708ac9ecb8df53bcc8d108016bd508 -Author: Philip Withnall philip.withnall@collabora.co.uk -Date: Fri May 8 10:13:39 2015 +0100 - - build: Auto-generate win32 .def file from libnice.sym - - We’ve neglected to manually update this file once too often — it’s been - out of date for important new symbols (for example, - nice_agent_get_io_stream()) since at least 0.1.11. - - Since the format is a simple extension of libnice.sym, we might as well - automatically generate it at dist time. - -commit 6a8c63219c632c27707267b6510dca096c6fd511 -Author: Youness Alaoui kakaroto@kakaroto.homelinux.net -Date: Tue May 5 15:07:10 2015 -0400 - - Removing no-op assignment - -commit 91a7b9324244844baf35d8fcef019a4ea3872d30 -Author: Youness Alaoui kakaroto@kakaroto.homelinux.net -Date: Tue May 5 15:00:30 2015 -0400 - - Do not compare scope for IPv6 address when scope is 0 - - This caused issues with thinking local host candidates were peer-reflexive - candidates because the nice_address_equal would fail since the scope - would be 6 (or some other value) but locally created NiceAddress from - a stun response would have the scope set to 0. - We ignore the scope when comparing ipv6 candidates when scope is 0 - to avoid these kinds of issues. - Thanks to ikonst_ for finding these issues - -commit 93862c1e1940618e06143d4788f54bffd4d1c5da -Author: Youness Alaoui kakaroto@kakaroto.homelinux.net -Date: Tue May 5 14:24:15 2015 -0400 - - Do not update a remote candidate's type - - When adding a remote candidate, if it's the same ip:port, we should - also check its type, otherwise it's a new candidate. We can't allow - a candidate type to be updated. This caused issues to ikonst_ on IRC - where for some reason a host candidate appeared as both host and prflx - and the update caused a remote host candidate to be updated to prflx - causing a crash when the sockptr was being accessed. - -commit 7b7d2d986876fc53a23af7b516d78f82f2a546e9 -Author: Philip Withnall philip@tecnocode.co.uk -Date: Sun May 3 16:05:30 2015 +0100 - - agent: Remove unnecessary NULL check - - With the changes in commit 483bdcf8, @name is now guaranteed to be - non-NULL. Spotted by Coverity. - - CID: #109878 -diff --git a/.arcconfig b/.arcconfig -new file mode 100644 -index 0000000..ba3d1ea ---- /dev/null -+++ b/.arcconfig -@@ -0,0 +1,6 @@ -+{ -+ "phabricator.uri" : "https://phabricator.freedesktop.org/", -+ "history.immutable" : true, -+ "project.name" : "libnice", -+ "repository.callsign" : "LIBNICE" -+} -diff --git a/Makefile.am b/Makefile.am -index 432a14d..896c751 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -33,6 +33,7 @@ EXTRA_DIST = \ - scripts/lcov.sh \ - scripts/valgrind.sh \ - win32 \ -+ win32/vs9/libnice.def \ - m4/introspection.m4 - - MAINTAINERCLEANFILES = ar-lib -@@ -41,6 +42,22 @@ dist_check_SCRIPTS = \ - scripts/check-symbols.sh \ - scripts/make-symbol-list.sh - -+# Generate the win32 DLL symbol export file. -+# The stun_*() symbols at the end have historically been exported on Windows -+# but not Linux, for no particular reason. They can’t be removed without -+# breaking ABI. FIXME: Remove them when we next break ABI. -+win32/vs9/libnice.def: nice/libnice.sym -+ $(AM_V_GEN)(echo "LIBRARY libnice"; \ -+ echo ""; \ -+ echo "EXPORTS"; \ -+ echo ""; \ -+ cat $<; \ -+ echo "stun_debug"; \ -+ echo "stun_debug_bytes"; \ -+ echo "stun_hash_creds") > $@ -+ -+CLEANFILES += win32/vs9/libnice.def -+ - lcov: - find -name '*.gcda' -delete - $(MAKE) $(AM_MAKEFLAGS) check -diff --git a/agent/address.c b/agent/address.c -index a8d9c76..3c20220 100644 ---- a/agent/address.c -+++ b/agent/address.c -@@ -51,7 +51,6 @@ - #include "address.h" - - #ifdef G_OS_WIN32 --#define inet_pton inet_pton_win32 - #define inet_ntop inet_ntop_win32 - - /* Defined in recent versions of mingw: -@@ -86,36 +85,6 @@ inet_ntop_win32 (int af, const void *src, char *dst, socklen_t cnt) - return NULL; - } - --static int --inet_pton_win32(int af, const char *src, void *dst) --{ -- struct addrinfo hints, *res, *ressave; -- -- memset(&hints, 0, sizeof(struct addrinfo)); -- hints.ai_family = af; -- -- if (getaddrinfo(src, NULL, &hints, &res) != 0) { -- return 0; -- } -- -- ressave = res; -- -- while (res) { -- if( res->ai_addr->sa_family == AF_INET) { -- memcpy(dst, &((struct sockaddr_in *) res->ai_addr)->sin_addr, -- sizeof(struct in_addr)); -- res = res->ai_next; -- } else if(res->ai_addr->sa_family == AF_INET6) { -- memcpy(dst, &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr, -- sizeof(struct in_addr6)); -- res = res->ai_next; -- } -- } -- -- freeaddrinfo(ressave); -- return 1; --} -- - #endif - - -@@ -297,7 +266,8 @@ nice_address_equal (const NiceAddress *a, const NiceAddress *b) - case AF_INET6: - return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr) - && (a->s.ip6.sin6_port == b->s.ip6.sin6_port) -- && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id); -+ && (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 || -+ (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id)); - - default: - g_return_val_if_reached (FALSE); -@@ -412,7 +382,8 @@ nice_address_equal_no_port (const NiceAddress *a, const NiceAddress *b) - - case AF_INET6: - return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr) -- && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id); -+ && (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 || -+ (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id)); - - default: - g_return_val_if_reached (FALSE); -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index c413bc1..d66a9ef 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -130,6 +130,7 @@ struct _NiceAgent - gboolean 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 */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -@@ -139,6 +140,7 @@ struct _NiceAgent - guint next_stream_id; /* id of next created candidate */ - NiceRNG *rng; /* random number generator */ - GSList *discovery_list; /* list of CandidateDiscovery items */ -+ GSList *triggered_check_queue; /* pairs in the triggered check list */ - guint discovery_unsched_items; /* number of discovery items unscheduled */ - GSource *discovery_timer_source; /* source of discovery timer */ - GSource *conncheck_timer_source; /* source of conncheck timer */ -@@ -171,10 +173,10 @@ agent_find_component ( - NiceAgent *agent, - guint stream_id, - guint component_id, -- Stream **stream, -- Component **component); -+ NiceStream **stream, -+ NiceComponent **component) G_GNUC_WARN_UNUSED_RESULT; - --Stream *agent_find_stream (NiceAgent *agent, guint stream_id); -+NiceStream *agent_find_stream (NiceAgent *agent, guint stream_id); - - void agent_gathering_done (NiceAgent *agent); - void agent_signal_gathering_done (NiceAgent *agent); -@@ -202,7 +204,7 @@ void agent_signal_new_candidate ( - - void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate); - --void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream); -+void agent_signal_initial_binding_request_received (NiceAgent *agent, NiceStream *stream); - - guint64 agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote); - -@@ -220,6 +222,8 @@ void nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent); - - void _priv_set_socket_tos (NiceAgent *agent, NiceSocket *sock, gint tos); - -+void _tcp_sock_is_writable (NiceSocket *sock, gpointer user_data); -+ - gboolean - component_io_cb ( - GSocket *gsocket, -@@ -274,10 +278,14 @@ void nice_debug_init (void); - - #ifdef NDEBUG - static inline gboolean nice_debug_is_enabled (void) { return FALSE; } -+static inline gboolean nice_debug_is_verbose (void) { return FALSE; } - static inline void nice_debug (const char *fmt, ...) { } -+static inline void nice_debug_verbose (const char *fmt, ...) { } - #else - gboolean nice_debug_is_enabled (void); -+gboolean nice_debug_is_verbose (void); - void nice_debug (const char *fmt, ...) G_GNUC_PRINTF (1, 2); -+void nice_debug_verbose (const char *fmt, ...) G_GNUC_PRINTF (1, 2); - #endif - - #endif /*_NICE_AGENT_PRIV_H */ -diff --git a/agent/agent.c b/agent/agent.c -index 259fdc9..f6c7a36 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -86,6 +86,7 @@ - static void - nice_debug_input_message_composition (const NiceInputMessage *messages, - guint n_messages); -+static const gchar *_cand_type_to_sdp (NiceCandidateType type); - - G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT); - -@@ -110,7 +111,8 @@ enum - PROP_ICE_UDP, - PROP_ICE_TCP, - PROP_BYTESTREAM_TCP, -- PROP_KEEPALIVE_CONNCHECK -+ PROP_KEEPALIVE_CONNCHECK, -+ PROP_FORCE_RELAY, - }; - - -@@ -133,11 +135,7 @@ enum - - static guint signals[N_SIGNALS]; - --#if GLIB_CHECK_VERSION(2,31,8) - static GMutex agent_mutex; /* Mutex used for thread-safe lib */ --#else --static GStaticMutex agent_mutex = G_STATIC_MUTEX_INIT; --#endif - - static void priv_stop_upnp (NiceAgent *agent); - -@@ -148,7 +146,7 @@ static void pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err, - gpointer user_data); - static PseudoTcpWriteResult pseudo_tcp_socket_write_packet (PseudoTcpSocket *sock, - const gchar *buffer, guint32 len, gpointer user_data); --static void adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component); -+static void adjust_tcp_clock (NiceAgent *agent, NiceStream *stream, NiceComponent *component); - - static void nice_agent_dispose (GObject *object); - static void nice_agent_get_property (GObject *object, -@@ -156,7 +154,6 @@ static void nice_agent_get_property (GObject *object, - static void nice_agent_set_property (GObject *object, - guint property_id, const GValue *value, GParamSpec *pspec); - --#if GLIB_CHECK_VERSION(2,31,8) - void agent_lock (void) - { - g_mutex_lock (&agent_mutex); -@@ -167,19 +164,6 @@ void agent_unlock (void) - g_mutex_unlock (&agent_mutex); - } - --#else --void agent_lock(void) --{ -- g_static_mutex_lock (&agent_mutex); --} -- --void agent_unlock(void) --{ -- g_static_mutex_unlock (&agent_mutex); --} -- --#endif -- - static GType _nice_agent_stream_ids_get_type (void); - - G_DEFINE_POINTER_TYPE (_NiceAgentStreamIds, _nice_agent_stream_ids); -@@ -314,13 +298,13 @@ agent_to_turn_socket_compatibility (NiceAgent *agent) - NICE_TURN_SOCKET_COMPATIBILITY_RFC5766; - } - --Stream *agent_find_stream (NiceAgent *agent, guint stream_id) -+NiceStream *agent_find_stream (NiceAgent *agent, guint stream_id) - { - GSList *i; - - for (i = agent->streams; i; i = i->next) - { -- Stream *s = i->data; -+ NiceStream *s = i->data; - - if (s->id == stream_id) - return s; -@@ -335,18 +319,18 @@ agent_find_component ( - NiceAgent *agent, - guint stream_id, - guint component_id, -- Stream **stream, -- Component **component) -+ NiceStream **stream, -+ NiceComponent **component) - { -- Stream *s; -- Component *c; -+ NiceStream *s; -+ NiceComponent *c; - - s = agent_find_stream (agent, stream_id); - - if (s == NULL) - return FALSE; - -- c = stream_find_component_by_id (s, component_id); -+ c = nice_stream_find_component_by_id (s, component_id); - - if (c == NULL) - return FALSE; -@@ -706,6 +690,24 @@ nice_agent_class_init (NiceAgentClass *klass) - FALSE, - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:force-relay -+ * -+ * Force all traffic to go through a relay for added privacy, this -+ * allows hiding the local IP address. When this is enabled, so -+ * local candidates are available before relay servers have been set -+ * with nice_agent_set_relay_info(). -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (gobject_class, PROP_FORCE_RELAY, -+ g_param_spec_boolean ( -+ "force-relay", -+ "Force Relay", -+ "Force all traffic to go through a relay for added privacy.", -+ FALSE, -+ G_PARAM_READWRITE)); -+ - /* install signals */ - - /** -@@ -713,9 +715,12 @@ nice_agent_class_init (NiceAgentClass *klass) - * @agent: The #NiceAgent object - * @stream_id: The ID of the stream - * @component_id: The ID of the component -- * @state: The #NiceComponentState of the component -+ * @state: The new #NiceComponentState of the component -+ * -+ * This signal is fired whenever a component’s state changes. There are many -+ * valid state transitions. - * -- * This signal is fired whenever a component's state changes -+ *  - */ - signals[SIGNAL_COMPONENT_STATE_CHANGED] = - g_signal_new ( -@@ -1178,6 +1183,10 @@ nice_agent_get_property ( - g_value_set_boolean (value, agent->keepalive_conncheck); - break; - -+ case PROP_FORCE_RELAY: -+ g_value_set_boolean (value, agent->force_relay); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1231,11 +1240,11 @@ nice_agent_reset_all_stun_agents (NiceAgent *agent, gboolean only_software) - - for (stream_item = agent->streams; stream_item; - stream_item = stream_item->next) { -- Stream *stream = stream_item->data; -+ NiceStream *stream = stream_item->data; - - for (component_item = stream->components; component_item; - component_item = component_item->next) { -- Component *component = component_item->data; -+ NiceComponent *component = component_item->data; - - if (only_software) - stun_agent_set_software (&component->stun_agent, -@@ -1361,6 +1370,10 @@ nice_agent_set_property ( - agent->keepalive_conncheck = g_value_get_boolean (value); - break; - -+ case PROP_FORCE_RELAY: -+ agent->force_relay = g_value_get_boolean (value); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1371,7 +1384,7 @@ nice_agent_set_property ( - - - static void -- agent_signal_socket_writable (NiceAgent *agent, Component *component) -+ agent_signal_socket_writable (NiceAgent *agent, NiceComponent *component) - { - g_cancellable_cancel (component->tcp_writable_cancellable); - -@@ -1380,7 +1393,7 @@ static void - } - - static void --pseudo_tcp_socket_create (NiceAgent *agent, Stream *stream, Component *component) -+pseudo_tcp_socket_create (NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { - PseudoTcpCallbacks tcp_callbacks = {component, - pseudo_tcp_socket_opened, -@@ -1394,8 +1407,8 @@ pseudo_tcp_socket_create (NiceAgent *agent, Stream *stream, Component *component - agent, component->id); - } - --static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream, -- Component *component) -+static void priv_pseudo_tcp_error (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component) - { - if (component->tcp_writable_cancellable) { - g_cancellable_cancel (component->tcp_writable_cancellable); -@@ -1405,7 +1418,7 @@ static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream, - if (component->tcp) { - agent_signal_component_state_change (agent, stream->id, - component->id, NICE_COMPONENT_STATE_FAILED); -- component_detach_all_sockets (component); -+ nice_component_detach_all_sockets (component); - pseudo_tcp_socket_close (component->tcp, TRUE); - } - -@@ -1419,9 +1432,9 @@ static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream, - static void - pseudo_tcp_socket_opened (PseudoTcpSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - - nice_debug ("Agent %p: s%d:%d pseudo Tcp socket Opened", agent, - stream->id, component->id); -@@ -1533,7 +1546,7 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, - (gchar *) buffer->buffer + iter->offset, - buffer->size - iter->offset); - -- nice_debug ("%s: Received %" G_GSSIZE_FORMAT " bytes into " -+ nice_debug_verbose ("%s: Received %" G_GSSIZE_FORMAT " bytes into " - "buffer %p (offset %" G_GSIZE_FORMAT ", length %" G_GSIZE_FORMAT - ").", G_STRFUNC, len, buffer->buffer, iter->offset, buffer->size); - -@@ -1580,25 +1593,25 @@ done: - static void - pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - gboolean has_io_callback; - guint stream_id = stream->id; - guint component_id = component->id; - - g_object_ref (agent); - -- nice_debug ("Agent %p: s%d:%d pseudo Tcp socket readable", agent, -+ nice_debug_verbose ("Agent %p: s%d:%d pseudo Tcp socket readable", agent, - stream->id, component->id); - - component->tcp_readable = TRUE; - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - - /* Only dequeue pseudo-TCP data if we can reliably inform the client. The - * agent lock is held here, so has_io_callback can only change during -- * component_emit_io_callback(), after which it’s re-queried. This ensures -+ * nice_component_emit_io_callback(), after which it’s re-queried. This ensures - * no data loss of packets already received and dequeued. */ - if (has_io_callback) { - do { -@@ -1641,7 +1654,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - break; - } - -- component_emit_io_callback (component, buf, len); -+ nice_component_emit_io_callback (component, buf, len); - - if (!agent_find_component (agent, stream_id, component_id, - &stream, &component)) { -@@ -1653,7 +1666,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - goto out; - } - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - } while (has_io_callback); - } else if (component->recv_messages != NULL) { - gint n_valid_messages; -@@ -1667,7 +1680,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - component->recv_messages, component->n_recv_messages, - &component->recv_messages_iter, &child_error); - -- nice_debug ("%s: Client buffers case: Received %d valid messages:", -+ nice_debug_verbose ("%s: Client buffers case: Received %d valid messages:", - G_STRFUNC, n_valid_messages); - nice_debug_input_message_composition (component->recv_messages, - component->n_recv_messages); -@@ -1700,17 +1713,16 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - out: - - g_object_unref (agent); -- - } - - static void - pseudo_tcp_socket_writable (PseudoTcpSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - -- nice_debug ("Agent %p: s%d:%d pseudo Tcp socket writable", agent, -+ nice_debug_verbose ("Agent %p: s%d:%d pseudo Tcp socket writable", agent, - stream->id, component->id); - - agent_signal_socket_writable (agent, component); -@@ -1720,9 +1732,9 @@ static void - pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err, - gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - - nice_debug ("Agent %p: s%d:%d pseudo Tcp socket closed. " - "Calling priv_pseudo_tcp_error().", agent, stream->id, component->id); -@@ -1734,7 +1746,7 @@ static PseudoTcpWriteResult - pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket, - const gchar *buffer, guint32 len, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - - if (component->selected_pair.local != NULL) { - NiceSocket *sock; -@@ -1747,7 +1759,7 @@ pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket, - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (addr, tmpbuf); - -- nice_debug ( -+ nice_debug_verbose ( - "Agent %p : s%d:%d: sending %d bytes on socket %p (FD %d) to [%s]:%d", - component->agent, component->stream->id, component->id, len, - sock->fileno, g_socket_get_fd (sock->fileno), tmpbuf, -@@ -1775,8 +1787,8 @@ pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket, - static gboolean - notify_pseudo_tcp_socket_clock (gpointer user_data) - { -- Component *component = user_data; -- Stream *stream; -+ NiceComponent *component = user_data; -+ NiceStream *stream; - NiceAgent *agent; - - agent_lock(); -@@ -1800,7 +1812,7 @@ notify_pseudo_tcp_socket_clock (gpointer user_data) - } - - static void --adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component) -+adjust_tcp_clock (NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { - if (!pseudo_tcp_socket_is_closed (component->tcp)) { - guint64 timeout = component->last_clock_timeout; -@@ -1809,13 +1821,7 @@ adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component) - if (timeout != component->last_clock_timeout) { - component->last_clock_timeout = timeout; - if (component->tcp_clock) { --#if GLIB_CHECK_VERSION (2, 36, 0) - g_source_set_ready_time (component->tcp_clock, timeout * 1000); --#else -- g_source_destroy (component->tcp_clock); -- g_source_unref (component->tcp_clock); -- component->tcp_clock = NULL; --#endif - } - if (!component->tcp_clock) { - long interval = timeout - (guint32) (g_get_monotonic_time () / 1000); -@@ -1837,19 +1843,19 @@ adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component) - } - } - --static void -+void - _tcp_sock_is_writable (NiceSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - - agent_lock (); - - /* Don't signal writable if the socket that has become writable is not - * the selected pair */ - if (component->selected_pair.local == NULL || -- component->selected_pair.local->sockptr != sock) { -+ !nice_socket_is_based_on (component->selected_pair.local->sockptr, sock)) { - agent_unlock (); - return; - } -@@ -1883,12 +1889,17 @@ void agent_gathering_done (NiceAgent *agent) - GSList *i, *j, *k, *l, *m; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *local_candidate = k->data; -+ -+ if (agent->force_relay && -+ local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (&local_candidate->addr, tmpbuf); -@@ -1933,7 +1944,7 @@ void agent_signal_gathering_done (NiceAgent *agent) - GSList *i; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - if (stream->gathering) { - stream->gathering = FALSE; - agent_queue_signal (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], -@@ -1942,7 +1953,9 @@ void agent_signal_gathering_done (NiceAgent *agent) - } - } - --void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream) -+void -+agent_signal_initial_binding_request_received (NiceAgent *agent, -+ NiceStream *stream) - { - if (stream->initial_binding_request_received != TRUE) { - stream->initial_binding_request_received = TRUE; -@@ -1957,8 +1970,8 @@ void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *st - * - * Must be called with the agent lock held. */ - static void --process_queued_tcp_packets (NiceAgent *agent, Stream *stream, -- Component *component) -+process_queued_tcp_packets (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component) - { - GOutputVector *vec; - guint stream_id = stream->id; -@@ -1972,7 +1985,7 @@ process_queued_tcp_packets (NiceAgent *agent, Stream *stream, - return; - } - -- nice_debug ("%s: Sending outstanding packets for agent %p.", G_STRFUNC, -+ nice_debug_verbose ("%s: Sending outstanding packets for agent %p.", G_STRFUNC, - agent); - - while ((vec = g_queue_peek_head (&component->queued_tcp_packets)) != NULL) { -@@ -2011,8 +2024,8 @@ process_queued_tcp_packets (NiceAgent *agent, Stream *stream, - void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id, - guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - - if (!agent_find_component (agent, stream_id, component_id, - &stream, &component)) -@@ -2122,28 +2135,67 @@ nice_component_state_to_string (NiceComponentState state) - } - } - --void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state) -+void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState new_state) - { -- Component *component; -- Stream *stream; -+ NiceComponentState old_state; -+ NiceComponent *component; -+ NiceStream *stream; -+ -+ g_return_if_fail (new_state < NICE_COMPONENT_STATE_LAST); - - if (!agent_find_component (agent, stream_id, component_id, - &stream, &component)) - return; - -- if (component->state != state && state < NICE_COMPONENT_STATE_LAST) { -- nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %s -> %s.", agent, -- stream_id, component_id, nice_component_state_to_string (component->state), -- nice_component_state_to_string (state)); -+ /* Validate the state change. */ -+ old_state = component->state; - -- component->state = state; -+ if (new_state == old_state) { -+ return; -+ } - -- if (agent->reliable) -- process_queued_tcp_packets (agent, stream, component); -+ nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %s -> %s.", agent, -+ stream_id, component_id, nice_component_state_to_string (old_state), -+ nice_component_state_to_string (new_state)); -+ -+ /* Check whether it’s a valid state transition. */ -+#define TRANSITION(OLD, NEW) \ -+ (old_state == NICE_COMPONENT_STATE_##OLD && \ -+ new_state == NICE_COMPONENT_STATE_##NEW) -+ -+ g_assert (/* Can (almost) always transition to FAILED (including -+ * DISCONNECTED → FAILED which happens if one component fails -+ * before another leaves DISCONNECTED): */ -+ TRANSITION (DISCONNECTED, FAILED) || -+ TRANSITION (GATHERING, FAILED) || -+ TRANSITION (CONNECTING, FAILED) || -+ TRANSITION (CONNECTED, FAILED) || -+ TRANSITION (READY, FAILED) || -+ /* Standard progression towards a ready connection: */ -+ TRANSITION (DISCONNECTED, GATHERING) || -+ TRANSITION (GATHERING, CONNECTING) || -+ TRANSITION (CONNECTING, CONNECTED) || -+ TRANSITION (CONNECTED, READY) || -+ /* priv_conn_check_add_for_candidate_pair_matched(): */ -+ TRANSITION (READY, CONNECTED) || -+ /* If set_remote_candidates() is called with new candidates after -+ * reaching FAILED: */ -+ TRANSITION (FAILED, CONNECTING) || -+ /* if new relay servers are added to a failed connection */ -+ TRANSITION (FAILED, GATHERING) || -+ /* Possible by calling set_remote_candidates() without calling -+ * nice_agent_gather_candidates(): */ -+ TRANSITION (DISCONNECTED, CONNECTING)); -+ -+#undef TRANSITION -+ -+ component->state = new_state; - -- agent_queue_signal (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], -- stream_id, component_id, state); -- } -+ if (agent->reliable) -+ process_queued_tcp_packets (agent, stream, component); -+ -+ agent_queue_signal (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], -+ stream_id, component_id, new_state); - } - - guint64 -@@ -2158,7 +2210,7 @@ agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandi - static void - priv_add_new_candidate_discovery_stun (NiceAgent *agent, - NiceSocket *nicesock, NiceAddress server, -- Stream *stream, guint component_id) -+ NiceStream *stream, guint component_id) - { - CandidateDiscovery *cdisco; - -@@ -2171,7 +2223,7 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent, - cdisco->nicesock = nicesock; - cdisco->server = server; - cdisco->stream = stream; -- cdisco->component = stream_find_component_by_id (stream, component_id); -+ cdisco->component = nice_stream_find_component_by_id (stream, component_id); - cdisco->agent = agent; - stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES, - STUN_COMPATIBILITY_RFC3489, -@@ -2179,7 +2231,7 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent, - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ? - STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES : 0); - -- nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n", -+ nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p", - agent, cdisco); - - agent->discovery_list = g_slist_append (agent->discovery_list, cdisco); -@@ -2189,10 +2241,10 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent, - static void - priv_add_new_candidate_discovery_turn (NiceAgent *agent, - NiceSocket *nicesock, TurnServer *turn, -- Stream *stream, guint component_id, gboolean turn_tcp) -+ NiceStream *stream, guint component_id, gboolean turn_tcp) - { - CandidateDiscovery *cdisco; -- Component *component = stream_find_component_by_id (stream, component_id); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, component_id); - NiceAddress local_address; - - /* note: no need to check for redundant candidates, as this is -@@ -2214,7 +2266,7 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - new_socket = nice_udp_bsd_socket_new (&addr); - if (new_socket) { - _priv_set_socket_tos (agent, new_socket, stream->tos); -- component_attach_socket (component, new_socket); -+ nice_component_attach_socket (component, new_socket); - nicesock = new_socket; - } - } -@@ -2308,14 +2360,14 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - cdisco->nicesock = nice_udp_turn_over_tcp_socket_new (nicesock, - agent_to_turn_socket_compatibility (agent)); - -- component_attach_socket (component, cdisco->nicesock); -+ nice_component_attach_socket (component, cdisco->nicesock); - } - - cdisco->turn = turn_server_ref (turn); - cdisco->server = turn->server; - - cdisco->stream = stream; -- cdisco->component = stream_find_component_by_id (stream, component_id); -+ cdisco->component = nice_stream_find_component_by_id (stream, component_id); - cdisco->agent = agent; - - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { -@@ -2342,7 +2394,7 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - } - stun_agent_set_software (&cdisco->stun_agent, agent->software_attribute); - -- nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p\n", -+ nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p", - agent, cdisco); - agent->discovery_list = g_slist_append (agent->discovery_list, cdisco); - ++agent->discovery_unsched_items; -@@ -2353,7 +2405,7 @@ nice_agent_add_stream ( - NiceAgent *agent, - guint n_components) - { -- Stream *stream; -+ NiceStream *stream; - guint ret = 0; - guint i; - -@@ -2361,7 +2413,7 @@ nice_agent_add_stream ( - g_return_val_if_fail (n_components >= 1, 0); - - agent_lock(); -- stream = stream_new (n_components, agent); -+ stream = nice_stream_new (n_components, agent); - - agent->streams = g_slist_append (agent->streams, stream); - stream->id = agent->next_stream_id++; -@@ -2369,7 +2421,7 @@ nice_agent_add_stream ( - if (agent->reliable) { - nice_debug ("Agent %p : reliable stream", agent); - for (i = 0; i < n_components; i++) { -- Component *component = stream_find_component_by_id (stream, i + 1); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, i + 1); - if (component) { - pseudo_tcp_socket_create (agent, stream, component); - } else { -@@ -2378,7 +2430,7 @@ nice_agent_add_stream ( - } - } - -- stream_initialize_credentials (stream, agent->rng); -+ nice_stream_initialize_credentials (stream, agent->rng); - - ret = stream->id; - -@@ -2395,8 +2447,8 @@ nice_agent_set_relay_info(NiceAgent *agent, - NiceRelayType type) - { - -- Component *component = NULL; -- Stream *stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *stream = NULL; - gboolean ret = TRUE; - TurnServer *turn; - -@@ -2439,7 +2491,9 @@ nice_agent_set_relay_info(NiceAgent *agent, - NiceCandidate *candidate = i->data; - - if (candidate->type == NICE_CANDIDATE_TYPE_HOST && -- candidate->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -+ candidate->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && -+ nice_address_ip_version (&candidate->addr) == -+ nice_address_ip_version (&turn->server)) - priv_add_new_candidate_discovery_turn (agent, - candidate->sockptr, turn, stream, component_id, - candidate->transport != NICE_CANDIDATE_TRANSPORT_UDP); -@@ -2547,12 +2601,16 @@ static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto, - nice_address_set_port (&externaddr, external_port); - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *local_candidate = k->data; - -+ if (agent->force_relay && -+ local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - if (nice_address_equal (&localaddr, &local_candidate->base_addr)) { - discovery_add_server_reflexive_candidate ( - agent, -@@ -2613,7 +2671,7 @@ nice_agent_gather_candidates ( - { - guint cid; - GSList *i; -- Stream *stream; -+ NiceStream *stream; - GSList *local_addresses = NULL; - gboolean ret = TRUE; - -@@ -2638,13 +2696,10 @@ nice_agent_gather_candidates ( - agent->full_mode ? "ICE-FULL" : "ICE-LITE"); - - #ifdef HAVE_GUPNP -- if (agent->upnp_enabled && agent->upnp == NULL) { -+ if (agent->upnp_enabled && agent->upnp == NULL && !agent->force_relay) { - agent->upnp = gupnp_simple_igd_thread_new (); - - if (agent->upnp) { -- agent_timeout_add_with_context (agent, &agent->upnp_timer_source, -- "UPnP timeout", agent->upnp_timeout, priv_upnp_timeout_cb, agent); -- - g_signal_connect (agent->upnp, "mapped-external-port", - G_CALLBACK (_upnp_mapped_external_port), agent); - g_signal_connect (agent->upnp, "error-mapping-port", -@@ -2698,7 +2753,7 @@ nice_agent_gather_candidates ( - #endif - - for (cid = 1; cid <= stream->n_components; cid++) { -- Component *component = stream_find_component_by_id (stream, cid); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, cid); - enum { - ADD_HOST_MIN = 0, - ADD_HOST_UDP = ADD_HOST_MIN, -@@ -2769,6 +2824,10 @@ nice_agent_gather_candidates ( - " s%d:%d. Invalid interface?", agent, ip, stream->id, - component->id); - } -+ if (agent->local_addresses == NULL) { -+ /* Ignore when an auto-generated address fails. */ -+ continue; -+ } - ret = FALSE; - goto error; - } -@@ -2791,11 +2850,14 @@ nice_agent_gather_candidates ( - 0, local_ip, nice_address_get_port (base_addr), - 0, PACKAGE_STRING); - agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, base_addr); -+ -+ agent_timeout_add_with_context (agent, &agent->upnp_timer_source, -+ "UPnP timeout", agent->upnp_timeout, priv_upnp_timeout_cb, agent); - } - #endif - - /* TODO: Add server-reflexive support for TCP candidates */ -- if (agent->full_mode && agent->stun_server_ip && -+ if (agent->full_mode && agent->stun_server_ip && !agent->force_relay && - transport == NICE_CANDIDATE_TRANSPORT_UDP) { - NiceAddress stun_server; - if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) { -@@ -2834,9 +2896,13 @@ nice_agent_gather_candidates ( - /* Only signal the new candidates after we're sure that the gathering was - * succesfful. But before sending gathering-done */ - for (cid = 1; cid <= stream->n_components; cid++) { -- Component *component = stream_find_component_by_id (stream, cid); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, cid); - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; -+ -+ if (agent->force_relay && candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - agent_signal_new_candidate (agent, candidate); - } - } -@@ -2863,9 +2929,9 @@ nice_agent_gather_candidates ( - if (ret == FALSE) { - priv_stop_upnp (agent); - for (cid = 1; cid <= stream->n_components; cid++) { -- Component *component = stream_find_component_by_id (stream, cid); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, cid); - -- component_free_socket_sources (component); -+ nice_component_free_socket_sources (component); - - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; -@@ -2938,7 +3004,7 @@ nice_agent_remove_stream ( - - /* note that streams/candidates can be in use by other threads */ - -- Stream *stream; -+ NiceStream *stream; - - g_return_if_fail (NICE_IS_AGENT (agent)); - g_return_if_fail (stream_id >= 1); -@@ -2958,7 +3024,7 @@ nice_agent_remove_stream ( - - /* Remove the stream and signal its removal. */ - agent->streams = g_slist_remove (agent->streams, stream); -- stream_close (stream); -+ nice_stream_close (stream); - - if (!agent->streams) - priv_remove_keepalive_timer (agent); -@@ -2971,7 +3037,7 @@ nice_agent_remove_stream ( - /* Actually free the stream. This should be done with the lock released, as - * it could end up disposing of a NiceIOStream, which tries to take the - * agent lock itself. */ -- stream_free (stream); -+ g_object_unref (stream); - - return; - } -@@ -2980,8 +3046,8 @@ NICEAPI_EXPORT void - nice_agent_set_port_range (NiceAgent *agent, guint stream_id, guint component_id, - guint min_port, guint max_port) - { -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - - g_return_if_fail (NICE_IS_AGENT (agent)); - g_return_if_fail (stream_id >= 1); -@@ -3033,15 +3099,28 @@ static gboolean priv_add_remote_candidate ( - const gchar *password, - const gchar *foundation) - { -- Component *component; -+ NiceComponent *component; - NiceCandidate *candidate; - - if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) - return FALSE; - - /* step: check whether the candidate already exists */ -- candidate = component_find_remote_candidate(component, addr, transport); -- if (candidate) { -+ candidate = nice_component_find_remote_candidate (component, addr, transport); -+ -+ /* 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) { -+ nice_debug ("Agent %p : Updating existing peer-rfx remote candidate to %s", -+ agent, _cand_type_to_sdp (type)); -+ candidate->type = type; -+ /* If it got there, the next one will also be ran, so the foundation -+ * will be set. -+ */ -+ } -+ -+ if (candidate && candidate->type == type) { - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (addr, tmpbuf); -@@ -3051,7 +3130,6 @@ static gboolean priv_add_remote_candidate ( - username, password, priority); - } - /* case 1: an existing candidate, update the attributes */ -- candidate->type = type; - if (base_addr) - candidate->base_addr = *base_addr; - candidate->priority = priority; -@@ -3136,7 +3214,7 @@ nice_agent_set_remote_credentials ( - guint stream_id, - const gchar *ufrag, const gchar *pwd) - { -- Stream *stream; -+ NiceStream *stream; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -3167,7 +3245,7 @@ nice_agent_set_local_credentials ( - const gchar *ufrag, - const gchar *pwd) - { -- Stream *stream; -+ NiceStream *stream; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -3198,7 +3276,7 @@ nice_agent_get_local_credentials ( - guint stream_id, - gchar **ufrag, gchar **pwd) - { -- Stream *stream; -+ NiceStream *stream; - gboolean ret = TRUE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -3226,8 +3304,8 @@ nice_agent_get_local_credentials ( - } - - static int --_set_remote_candidates_locked (NiceAgent *agent, Stream *stream, -- Component *component, const GSList *candidates) -+_set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component, const GSList *candidates) - { - const GSList *i; - int added = 0; -@@ -3253,12 +3331,10 @@ _set_remote_candidates_locked (NiceAgent *agent, Stream *stream, - } - } - -- conn_check_remote_candidates_set(agent); -+ conn_check_remote_candidates_set(agent, stream, component); - - if (added > 0) { -- gboolean res = conn_check_schedule_next (agent); -- if (res != TRUE) -- nice_debug ("Agent %p : Warning: unable to schedule any conn checks!", agent); -+ conn_check_schedule_next (agent); - } - - return added; -@@ -3269,8 +3345,8 @@ NICEAPI_EXPORT int - nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint component_id, const GSList *candidates) - { - int added = 0; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - - g_return_val_if_fail (NICE_IS_AGENT (agent), 0); - g_return_val_if_fail (stream_id >= 1, 0); -@@ -3329,14 +3405,15 @@ typedef enum { - static RecvStatus - agent_recv_message_unlocked ( - NiceAgent *agent, -- Stream *stream, -- Component *component, -+ NiceStream *stream, -+ NiceComponent *component, - NiceSocket *nicesock, - NiceInputMessage *message) - { - NiceAddress from; - GList *item; - gint retval; -+ gboolean is_turn = FALSE; - - /* We need an address for packet parsing, below. */ - if (message->from == NULL) { -@@ -3383,7 +3460,7 @@ agent_recv_message_unlocked ( - n_bufs = message->n_buffers; - } - -- local_bufs = g_malloc_n (n_bufs + 1, sizeof (GInputVector)); -+ local_bufs = g_alloca ((n_bufs + 1) * sizeof (GInputVector)); - local_message.buffers = local_bufs; - local_message.n_buffers = n_bufs + 1; - local_message.from = message->from; -@@ -3400,7 +3477,6 @@ agent_recv_message_unlocked ( - if (retval == 1) { - message->length = ntohs (rfc4571_frame); - } -- g_free (local_bufs); - } else { - if (nicesock->type == NICE_SOCKET_TYPE_TCP_PASSIVE) { - NiceSocket *new_socket; -@@ -3411,7 +3487,7 @@ agent_recv_message_unlocked ( - new_socket = nice_tcp_passive_socket_accept (nicesock); - if (new_socket) { - _priv_set_socket_tos (agent, new_socket, stream->tos); -- component_attach_socket (component, new_socket); -+ nice_component_attach_socket (component, new_socket); - } - retval = 0; - } else { -@@ -3481,7 +3557,7 @@ agent_recv_message_unlocked ( - n_bufs = message->n_buffers; - } - -- local_bufs = g_malloc_n (n_bufs, sizeof (GInputVector)); -+ local_bufs = g_alloca (n_bufs * sizeof (GInputVector)); - local_message.buffers = local_bufs; - local_message.from = message->from; - local_message.length = 0; -@@ -3508,7 +3584,6 @@ agent_recv_message_unlocked ( - message->length = local_message.length; - agent->rfc4571_expecting_length -= local_message.length; - } -- g_free (local_bufs); - } - } - } -@@ -3516,7 +3591,7 @@ agent_recv_message_unlocked ( - retval = nice_socket_recv_messages (nicesock, message, 1); - } - -- nice_debug ("%s: Received %d valid messages of length %" G_GSIZE_FORMAT -+ nice_debug_verbose ("%s: Received %d valid messages of length %" G_GSIZE_FORMAT - " from base socket %p.", G_STRFUNC, retval, message->length, nicesock); - - if (retval == 0) { -@@ -3535,30 +3610,45 @@ agent_recv_message_unlocked ( - goto done; - } - -- if (nice_debug_is_enabled ()) { -+ if (nice_debug_is_verbose ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (message->from, tmpbuf); -- nice_debug ("Agent %p : Packet received on local socket %d from [%s]:%u (%" G_GSSIZE_FORMAT " octets).", agent, -+ nice_debug_verbose ("Agent %p : Packet received on local socket %d from [%s]:%u (%" G_GSSIZE_FORMAT " octets).", agent, - g_socket_get_fd (nicesock->fileno), tmpbuf, - nice_address_get_port (message->from), message->length); - } - -- for (item = component->turn_servers; item; item = g_list_next (item)) { -+ if (nicesock->type == NICE_SOCKET_TYPE_UDP_TURN) -+ is_turn = TRUE; -+ -+ if (!is_turn && component->turn_candidate && -+ nice_socket_is_based_on (component->turn_candidate->sockptr, nicesock) && -+ nice_address_equal (message->from, -+ &component->turn_candidate->turn->server)) { -+ is_turn = TRUE; -+ retval = nice_udp_turn_socket_parse_recv_message ( -+ component->turn_candidate->sockptr, &nicesock, message); -+ } -+ -+ for (item = component->turn_servers; item && !is_turn; -+ item = g_list_next (item)) { - TurnServer *turn = item->data; - GSList *i = NULL; - - if (!nice_address_equal (message->from, &turn->server)) - continue; - -- nice_debug ("Agent %p : Packet received from TURN server candidate.", -+ nice_debug_verbose ("Agent %p : Packet received from TURN server candidate.", - agent); -+ is_turn = TRUE; - - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *cand = i->data; - - if (cand->type == NICE_CANDIDATE_TYPE_RELAYED && -+ cand->turn == turn && - cand->stream_id == stream->id && -- cand->component_id == component->id) { -+ nice_socket_is_based_on (cand->sockptr, nicesock)) { - retval = nice_udp_turn_socket_parse_recv_message (cand->sockptr, &nicesock, - message); - break; -@@ -3567,6 +3657,12 @@ agent_recv_message_unlocked ( - break; - } - -+ if (agent->force_relay && !is_turn) { -+ /* Ignore messages not from TURN if TURN is required */ -+ retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ -+ goto done; -+ } -+ - if (retval == RECV_OOB) - goto done; - -@@ -3638,7 +3734,7 @@ agent_recv_message_unlocked ( - - /* Received data on a reliable connection. */ - -- nice_debug ("%s: notifying pseudo-TCP of packet, length %" G_GSIZE_FORMAT, -+ nice_debug_verbose ("%s: notifying pseudo-TCP of packet, length %" G_GSIZE_FORMAT, - G_STRFUNC, message->length); - pseudo_tcp_socket_notify_message (component->tcp, message); - -@@ -3672,14 +3768,14 @@ nice_debug_input_message_composition (const NiceInputMessage *messages, - { - guint i; - -- if (!nice_debug_is_enabled ()) -+ if (!nice_debug_is_verbose ()) - return; - - for (i = 0; i < n_messages; i++) { - const NiceInputMessage *message = &messages[i]; - guint j; - -- nice_debug ("Message %p (from: %p, length: %" G_GSIZE_FORMAT ")", message, -+ nice_debug_verbose ("Message %p (from: %p, length: %" G_GSIZE_FORMAT ")", message, - message->from, message->length); - - for (j = 0; -@@ -3688,7 +3784,7 @@ nice_debug_input_message_composition (const NiceInputMessage *messages, - j++) { - GInputVector *buffer = &message->buffers[j]; - -- nice_debug ("\tBuffer %p (length: %" G_GSIZE_FORMAT ")", buffer->buffer, -+ nice_debug_verbose ("\tBuffer %p (length: %" G_GSIZE_FORMAT ")", buffer->buffer, - buffer->size); - } - } -@@ -3724,7 +3820,7 @@ compact_message (const NiceOutputMessage *message, gsize buffer_length) - guint8 * - compact_input_message (const NiceInputMessage *message, gsize *buffer_length) - { -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - nice_debug_input_message_composition (message, 1); - - /* This works as long as NiceInputMessage is a subset of eNiceOutputMessage */ -@@ -3742,7 +3838,7 @@ memcpy_buffer_to_input_message (NiceInputMessage *message, - { - guint i; - -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - - message->length = 0; - -@@ -3912,7 +4008,7 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - * - * Must be called with the io_mutex held. */ - static gint --pending_io_messages_recv_messages (Component *component, gboolean reliable, -+pending_io_messages_recv_messages (NiceComponent *component, gboolean reliable, - NiceInputMessage *messages, guint n_messages, NiceInputMessageIter *iter) - { - gsize len; -@@ -3986,8 +4082,8 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - GCancellable *cancellable, GError **error) - { - GMainContext *context; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - gint n_valid_messages = -1; - GSource *cancellable_source = NULL; - gboolean received_enough = FALSE, error_reported = FALSE; -@@ -4041,7 +4137,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - goto done; - } - -- nice_debug ("%s: %p: (%s):", G_STRFUNC, agent, -+ nice_debug_verbose ("%s: %p: (%s):", G_STRFUNC, agent, - blocking ? "blocking" : "non-blocking"); - nice_debug_input_message_composition (messages, n_messages); - -@@ -4050,8 +4146,8 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - component->recv_messages == NULL); - - /* Set the component’s receive buffer. */ -- context = component_dup_io_context (component); -- component_set_io_callback (component, NULL, NULL, messages, n_messages, -+ context = nice_component_dup_io_context (component); -+ nice_component_set_io_callback (component, NULL, NULL, messages, n_messages, - &child_error); - - /* Add the cancellable as a source. */ -@@ -4074,7 +4170,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - component->recv_messages, component->n_recv_messages, - &component->recv_messages_iter); - -- nice_debug ("%s: %p: Received %d valid messages from pending I/O buffer.", -+ nice_debug_verbose ("%s: %p: Received %d valid messages from pending I/O buffer.", - G_STRFUNC, agent, - nice_input_message_iter_get_n_valid_messages ( - &component->recv_messages_iter)); -@@ -4095,7 +4191,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - &component->recv_messages_iter, &child_error); - adjust_tcp_clock (agent, stream, component); - -- nice_debug ("%s: %p: Received %d valid messages from pseudo-TCP read " -+ nice_debug_verbose ("%s: %p: Received %d valid messages from pseudo-TCP read " - "buffer.", G_STRFUNC, agent, - nice_input_message_iter_get_n_valid_messages ( - &component->recv_messages_iter)); -@@ -4125,7 +4221,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - memcpy (&prev_recv_messages_iter, &component->recv_messages_iter, - sizeof (NiceInputMessageIter)); - -- agent_unlock_and_emit (agent); -+ agent_unlock (); - g_main_context_iteration (context, blocking); - agent_lock (); - -@@ -4159,7 +4255,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - nice_input_message_iter_get_n_valid_messages ( - &component->recv_messages_iter); /* grab before resetting the iter */ - -- component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); -+ nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); - - recv_error: - /* Tidy up. Below this point, @component may be %NULL. */ -@@ -4179,7 +4275,7 @@ recv_error: - n_valid_messages = -1; - } - -- nice_debug ("%s: %p: n_valid_messages: %d, n_messages: %u", G_STRFUNC, agent, -+ nice_debug_verbose ("%s: %p: n_valid_messages: %d, n_messages: %u", G_STRFUNC, agent, - n_valid_messages, n_messages); - - done: -@@ -4311,8 +4407,8 @@ nice_agent_send_messages_nonblocking_internal ( - gboolean allow_partial, - GError **error) - { -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - gint n_sent = -1; /* is in bytes if allow_partial is TRUE, - otherwise in messages */ - GError *child_error = NULL; -@@ -4335,7 +4431,7 @@ nice_agent_send_messages_nonblocking_internal ( - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (&component->selected_pair.remote->addr, tmpbuf); - -- nice_debug ("Agent %p : s%d:%d: sending %u messages to " -+ nice_debug_verbose ("Agent %p : s%d:%d: sending %u messages to " - "[%s]:%d", agent, stream_id, component_id, n_messages, tmpbuf, - nice_address_get_port (&component->selected_pair.remote->addr)); - } -@@ -4485,7 +4581,7 @@ nice_agent_send_messages_nonblocking_internal ( - n_sent = -1; - } - -- nice_debug ("%s: n_sent: %d, n_messages: %u", G_STRFUNC, -+ nice_debug_verbose ("%s: n_sent: %d, n_messages: %u", G_STRFUNC, - n_sent, n_messages); - - done: -@@ -4558,7 +4654,7 @@ nice_agent_get_local_candidates ( - guint stream_id, - guint component_id) - { -- Component *component; -+ NiceComponent *component; - GSList * ret = NULL; - GSList * item = NULL; - -@@ -4572,8 +4668,14 @@ nice_agent_get_local_candidates ( - goto done; - } - -- for (item = component->local_candidates; item; item = item->next) -- ret = g_slist_append (ret, nice_candidate_copy (item->data)); -+ for (item = component->local_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (agent->force_relay && cand->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ -+ ret = g_slist_append (ret, nice_candidate_copy (cand)); -+ } - - done: - agent_unlock_and_emit (agent); -@@ -4587,7 +4689,7 @@ nice_agent_get_remote_candidates ( - guint stream_id, - guint component_id) - { -- Component *component; -+ NiceComponent *component; - GSList *ret = NULL, *item = NULL; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); -@@ -4620,11 +4722,11 @@ nice_agent_restart ( - priv_generate_tie_breaker (agent); - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - /* step: reset local credentials for the stream and - * clean up the list of remote candidates */ -- stream_restart (agent, stream); -+ nice_stream_restart (stream, agent); - } - - agent_unlock_and_emit (agent); -@@ -4637,7 +4739,7 @@ nice_agent_restart_stream ( - guint stream_id) - { - gboolean res = FALSE; -- Stream *stream; -+ NiceStream *stream; - - agent_lock(); - -@@ -4649,7 +4751,7 @@ nice_agent_restart_stream ( - - /* step: reset local credentials for the stream and - * clean up the list of remote candidates */ -- stream_restart (agent, stream); -+ nice_stream_restart (stream, agent); - - res = TRUE; - done: -@@ -4688,10 +4790,10 @@ nice_agent_dispose (GObject *object) - - for (i = agent->streams; i; i = i->next) - { -- Stream *s = i->data; -+ NiceStream *s = i->data; - -- stream_close (s); -- stream_free (s); -+ nice_stream_close (s); -+ g_object_unref (s); - } - - g_slist_free (agent->streams); -@@ -4739,9 +4841,9 @@ gboolean - component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - { - SocketSource *socket_source = user_data; -- Component *component; -+ NiceComponent *component; - NiceAgent *agent; -- Stream *stream; -+ NiceStream *stream; - gboolean has_io_callback; - gboolean remove_source = FALSE; - -@@ -4774,20 +4876,22 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - stream->id, component->id, NICE_COMPONENT_STATE_FAILED); - } - -- component_detach_socket (component, socket_source->socket); -+ nice_component_remove_socket (component, socket_source->socket); - agent_unlock (); -+ g_object_unref (agent); - return G_SOURCE_REMOVE; - } - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - - /* Choose which receive buffer to use. If we’re reading for - * nice_agent_attach_recv(), use a local static buffer. If we’re reading for - * nice_agent_recv_messages(), use the buffer provided by the client. - * - * has_io_callback cannot change throughout this function, as we operate -- * entirely with the agent lock held, and component_set_io_callback() would -- * need to take the agent lock to change the Component’s io_callback. */ -+ * entirely with the agent lock held, and nice_component_set_io_callback() -+ * would need to take the agent lock to change the Component’s -+ * io_callback. */ - g_assert (!has_io_callback || component->recv_messages == NULL); - - if (agent->reliable && !nice_socket_is_reliable (socket_source->socket)) { -@@ -4835,7 +4939,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - retval = agent_recv_message_unlocked (agent, stream, component, - socket_source->socket, &local_message); - -- nice_debug ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT -+ nice_debug_verbose ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT - " bytes", G_STRFUNC, agent, retval, local_message.length); - - /* Don’t expect any valid messages to escape pseudo_tcp_socket_readable() -@@ -4852,7 +4956,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - break; - } - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - } - } else if (has_io_callback) { - while (has_io_callback) { -@@ -4865,7 +4969,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - retval = agent_recv_message_unlocked (agent, stream, component, - socket_source->socket, &local_message); - -- nice_debug ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT -+ nice_debug_verbose ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT - " bytes", G_STRFUNC, agent, retval, local_message.length); - - if (retval == RECV_WOULD_BLOCK) { -@@ -4879,13 +4983,13 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - } - - if (retval == RECV_SUCCESS && local_message.length > 0) -- component_emit_io_callback (component, local_buf, local_message.length); -+ nice_component_emit_io_callback (component, local_buf, local_message.length); - - if (g_source_is_destroyed (g_main_current_source ())) { - nice_debug ("Component IO source disappeared during the callback"); - goto out; - } -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - } - } else if (component->recv_messages != NULL) { - RecvStatus retval; -@@ -4906,7 +5010,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - socket_source->socket, - &component->recv_messages[component->recv_messages_iter.message]); - -- nice_debug ("%s: %p: received %d valid messages", G_STRFUNC, agent, -+ nice_debug_verbose ("%s: %p: received %d valid messages", G_STRFUNC, agent, - retval); - - if (retval == RECV_SUCCESS) { -@@ -4931,6 +5035,10 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - } - - done: -+ -+ if (remove_source) -+ nice_component_remove_socket (component, socket_source->socket); -+ - /* If we’re in the middle of a read, don’t emit any signals, or we could cause - * re-entrancy by (e.g.) emitting component-state-changed and having the - * client perform a read. */ -@@ -4946,6 +5054,7 @@ done: - - out: - g_object_unref (agent); -+ - agent_unlock_and_emit (agent); - return G_SOURCE_REMOVE; - } -@@ -4959,8 +5068,8 @@ nice_agent_attach_recv ( - NiceAgentRecvFunc func, - gpointer data) - { -- Component *component = NULL; -- Stream *stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *stream = NULL; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -4982,8 +5091,8 @@ nice_agent_attach_recv ( - ctx = g_main_context_default (); - - /* Set the component’s I/O context. */ -- component_set_io_context (component, ctx); -- component_set_io_callback (component, func, data, NULL, 0, NULL); -+ nice_component_set_io_context (component, ctx); -+ nice_component_set_io_callback (component, func, data, NULL, 0, NULL); - ret = TRUE; - - if (func) { -@@ -5011,8 +5120,8 @@ nice_agent_set_selected_pair ( - const gchar *lfoundation, - const gchar *rfoundation) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - CandidatePair pair; - gboolean ret = FALSE; - -@@ -5029,7 +5138,7 @@ nice_agent_set_selected_pair ( - goto done; - } - -- if (!component_find_pair (component, agent, lfoundation, rfoundation, &pair)){ -+ if (!nice_component_find_pair (component, agent, lfoundation, rfoundation, &pair)){ - goto done; - } - -@@ -5044,11 +5153,22 @@ nice_agent_set_selected_pair ( - goto done; - } - -- /* step: change component state */ -- agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY); -+ /* step: change component state; we could be in STATE_DISCONNECTED; skip -+ * STATE_GATHERING and continue through the states to give client code a nice -+ * logical progression. See http://phabricator.freedesktop.org/D218 for -+ * discussion. */ -+ if (component->state < NICE_COMPONENT_STATE_CONNECTING || -+ 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_CONNECTED) -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_CONNECTED); -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_READY); - - /* step: set the selected pair */ -- component_update_selected_pair (component, &pair); -+ nice_component_update_selected_pair (component, &pair); - agent_signal_new_selected_pair (agent, stream_id, component_id, - pair.local, pair.remote); - -@@ -5063,8 +5183,8 @@ NICEAPI_EXPORT gboolean - nice_agent_get_selected_pair (NiceAgent *agent, guint stream_id, - guint component_id, NiceCandidate **local, NiceCandidate **remote) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -5096,8 +5216,8 @@ NICEAPI_EXPORT GSocket * - nice_agent_get_selected_socket (NiceAgent *agent, guint stream_id, - guint component_id) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceSocket *nice_socket; - GSocket *g_socket = NULL; - -@@ -5176,8 +5296,8 @@ nice_agent_set_selected_remote_candidate ( - guint component_id, - NiceCandidate *candidate) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceCandidate *lcandidate = NULL; - gboolean ret = FALSE; - NiceCandidate *local = NULL, *remote = NULL; -@@ -5204,7 +5324,7 @@ nice_agent_set_selected_remote_candidate ( - priority = component->selected_pair.priority; - - /* step: set the selected pair */ -- lcandidate = component_set_selected_remote_candidate (agent, component, -+ lcandidate = nice_component_set_selected_remote_candidate (component, agent, - candidate); - if (!lcandidate) - goto done; -@@ -5222,8 +5342,19 @@ nice_agent_set_selected_remote_candidate ( - goto done; - } - -- /* step: change component state */ -- agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY); -+ /* step: change component state; we could be in STATE_DISCONNECTED; skip -+ * STATE_GATHERING and continue through the states to give client code a nice -+ * logical progression. See http://phabricator.freedesktop.org/D218 for -+ * discussion. */ -+ if (component->state < NICE_COMPONENT_STATE_CONNECTING || -+ 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_CONNECTED) -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_CONNECTED); -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_READY); - - agent_signal_new_selected_pair (agent, stream_id, component_id, - lcandidate, candidate); -@@ -5261,7 +5392,7 @@ nice_agent_set_stream_tos (NiceAgent *agent, - guint stream_id, gint tos) - { - GSList *i, *j; -- Stream *stream; -+ NiceStream *stream; - - g_return_if_fail (NICE_IS_AGENT (agent)); - g_return_if_fail (stream_id >= 1); -@@ -5274,7 +5405,7 @@ nice_agent_set_stream_tos (NiceAgent *agent, - - stream->tos = tos; - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - - for (j = component->local_candidates; j; j = j->next) { - NiceCandidate *local_candidate = j->data; -@@ -5308,7 +5439,7 @@ NICEAPI_EXPORT gboolean - nice_agent_set_stream_name (NiceAgent *agent, guint stream_id, - const gchar *name) - { -- Stream *stream_to_name = NULL; -+ NiceStream *stream_to_name = NULL; - GSList *i; - gboolean ret = FALSE; - -@@ -5329,16 +5460,14 @@ nice_agent_set_stream_name (NiceAgent *agent, guint stream_id, - - agent_lock(); - -- if (name != NULL) { -- for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ for (i = agent->streams; i; i = i->next) { -+ NiceStream *stream = i->data; - -- if (stream->id != stream_id && -- g_strcmp0 (stream->name, name) == 0) -- goto done; -- else if (stream->id == stream_id) -- stream_to_name = stream; -- } -+ if (stream->id != stream_id && -+ g_strcmp0 (stream->name, name) == 0) -+ goto done; -+ else if (stream->id == stream_id) -+ stream_to_name = stream; - } - - if (stream_to_name == NULL) -@@ -5358,7 +5487,7 @@ nice_agent_set_stream_name (NiceAgent *agent, guint stream_id, - NICEAPI_EXPORT const gchar * - nice_agent_get_stream_name (NiceAgent *agent, guint stream_id) - { -- Stream *stream; -+ NiceStream *stream; - gchar *name = NULL; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); -@@ -5379,14 +5508,14 @@ nice_agent_get_stream_name (NiceAgent *agent, guint stream_id) - - static NiceCandidate * - _get_default_local_candidate_locked (NiceAgent *agent, -- Stream *stream, Component *component) -+ NiceStream *stream, NiceComponent *component) - { - GSList *i; - NiceCandidate *default_candidate = NULL; - NiceCandidate *default_rtp_candidate = NULL; - - if (component->id != NICE_COMPONENT_TYPE_RTP) { -- Component *rtp_component; -+ NiceComponent *rtp_component; - - if (!agent_find_component (agent, stream->id, NICE_COMPONENT_TYPE_RTP, - NULL, &rtp_component)) -@@ -5402,6 +5531,10 @@ _get_default_local_candidate_locked (NiceAgent *agent, - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *local_candidate = i->data; - -+ if (agent->force_relay && -+ local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - /* Only check for ipv4 candidates */ - if (nice_address_ip_version (&local_candidate->addr) != 4) - continue; -@@ -5426,8 +5559,8 @@ NICEAPI_EXPORT NiceCandidate * - nice_agent_get_default_local_candidate (NiceAgent *agent, - guint stream_id, guint component_id) - { -- Stream *stream = NULL; -- Component *component = NULL; -+ NiceStream *stream = NULL; -+ NiceComponent *component = NULL; - NiceCandidate *default_candidate = NULL; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); -@@ -5526,7 +5659,7 @@ _generate_candidate_sdp (NiceAgent *agent, - } - - static void --_generate_stream_sdp (NiceAgent *agent, Stream *stream, -+_generate_stream_sdp (NiceAgent *agent, NiceStream *stream, - GString *sdp, gboolean include_non_ice) - { - GSList *i, *j; -@@ -5542,7 +5675,7 @@ _generate_stream_sdp (NiceAgent *agent, Stream *stream, - - /* Find default candidates */ - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - NiceCandidate *default_candidate; - - if (component->id == NICE_COMPONENT_TYPE_RTP) { -@@ -5571,11 +5704,14 @@ _generate_stream_sdp (NiceAgent *agent, Stream *stream, - g_string_append_printf (sdp, "a=ice-pwd:%s\n", stream->local_password); - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - - for (j = component->local_candidates; j; j = j->next) { - NiceCandidate *candidate = j->data; - -+ if (agent->force_relay && candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - _generate_candidate_sdp (agent, candidate, sdp); - g_string_append (sdp, "\n"); - } -@@ -5593,7 +5729,7 @@ nice_agent_generate_local_sdp (NiceAgent *agent) - agent_lock(); - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - _generate_stream_sdp (agent, stream, sdp, TRUE); - } -@@ -5609,7 +5745,7 @@ nice_agent_generate_local_stream_sdp (NiceAgent *agent, guint stream_id, - { - GString *sdp = NULL; - gchar *ret = NULL; -- Stream *stream; -+ NiceStream *stream; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); - g_return_val_if_fail (stream_id >= 1, NULL); -@@ -5652,7 +5788,7 @@ nice_agent_generate_local_candidate_sdp (NiceAgent *agent, - NICEAPI_EXPORT gint - nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp) - { -- Stream *current_stream = NULL; -+ NiceStream *current_stream = NULL; - gchar **sdp_lines = NULL; - GSList *l, *stream_item = NULL; - gint i; -@@ -5664,7 +5800,7 @@ nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp) - agent_lock(); - - for (l = agent->streams; l; l = l->next) { -- Stream *stream = l->data; -+ NiceStream *stream = l->data; - - if (stream->name == NULL) { - ret = -1; -@@ -5701,7 +5837,7 @@ nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp) - NICE_STREAM_MAX_PWD); - } else if (g_str_has_prefix (sdp_lines[i], "a=candidate:")) { - NiceCandidate *candidate = NULL; -- Component *component = NULL; -+ NiceComponent *component = NULL; - GSList *cands = NULL; - gint added; - -@@ -5744,7 +5880,7 @@ NICEAPI_EXPORT GSList * - nice_agent_parse_remote_stream_sdp (NiceAgent *agent, guint stream_id, - const gchar *sdp, gchar **ufrag, gchar **pwd) - { -- Stream *stream = NULL; -+ NiceStream *stream = NULL; - gchar **sdp_lines = NULL; - GSList *candidates = NULL; - gint i; -@@ -5924,7 +6060,7 @@ nice_agent_get_io_stream (NiceAgent *agent, guint stream_id, - guint component_id) - { - GIOStream *iostream = NULL; -- Component *component; -+ NiceComponent *component; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); - g_return_val_if_fail (stream_id >= 1, NULL); -@@ -5951,7 +6087,7 @@ nice_agent_get_io_stream (NiceAgent *agent, guint stream_id, - NICEAPI_EXPORT gboolean - nice_agent_forget_relays (NiceAgent *agent, guint stream_id, guint component_id) - { -- Component *component; -+ NiceComponent *component; - gboolean ret = TRUE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -5965,7 +6101,7 @@ nice_agent_forget_relays (NiceAgent *agent, guint stream_id, guint component_id) - goto done; - } - -- component_clean_turn_servers (component); -+ nice_component_clean_turn_servers (component); - - done: - agent_unlock_and_emit (agent); -@@ -6013,7 +6149,7 @@ nice_agent_get_component_state (NiceAgent *agent, - guint stream_id, guint component_id) - { - NiceComponentState state = NICE_COMPONENT_STATE_FAILED; -- Component *component; -+ NiceComponent *component; - - agent_lock (); - -diff --git a/agent/candidate.c b/agent/candidate.c -index a472c4a..68c7714 100644 ---- a/agent/candidate.c -+++ b/agent/candidate.c -@@ -52,6 +52,7 @@ - - #include "agent.h" - #include "component.h" -+#include "interfaces.h" - - G_DEFINE_BOXED_TYPE (NiceCandidate, nice_candidate, nice_candidate_copy, - nice_candidate_free); -@@ -144,6 +145,43 @@ nice_candidate_ice_local_preference_full (guint direction_preference, - other_preference); - } - -+static guint8 -+nice_candidate_ip_local_preference (const NiceCandidate *candidate) -+{ -+ guint8 preference = 0; -+ gchar ip_string[INET6_ADDRSTRLEN]; -+ GList/*<owned gchar*>*/ *ips = NULL; -+ GList/*<unowned gchar*>*/ *iter; -+ -+ /* Ensure otherwise identical host candidates with only different IP addresses -+ * (multihomed host) get assigned different priorities. Position of the IP in -+ * the list obtained from nice_interfaces_get_local_ips() serves here as the -+ * distinguishing value of other_preference. Reflexive and relayed candidates -+ * are likewise differentiated by their base address. -+ * -+ * This is required by RFC 5245 Section 4.1.2.1: -+ * https://tools.ietf.org/html/rfc5245#section-4.1.2.1 -+ */ -+ if (candidate->type == NICE_CANDIDATE_TYPE_HOST) { -+ nice_address_to_string (&candidate->addr, ip_string); -+ } else { -+ nice_address_to_string (&candidate->base_addr, ip_string); -+ } -+ -+ ips = nice_interfaces_get_local_ips (TRUE); -+ -+ for (iter = ips; iter; iter = g_list_next (iter)) { -+ if (g_strcmp0 (ip_string, iter->data) == 0) { -+ break; -+ } -+ ++preference; -+ } -+ -+ g_list_free_full (ips, g_free); -+ -+ return preference; -+} -+ - static guint16 - nice_candidate_ice_local_preference (const NiceCandidate *candidate) - { -@@ -178,7 +216,8 @@ nice_candidate_ice_local_preference (const NiceCandidate *candidate) - break; - } - -- return nice_candidate_ice_local_preference_full (direction_preference, 1); -+ return nice_candidate_ice_local_preference_full (direction_preference, -+ nice_candidate_ip_local_preference (candidate)); - } - - static guint32 -@@ -214,7 +253,7 @@ nice_candidate_ms_ice_local_preference (const NiceCandidate *candidate) - } - - return nice_candidate_ms_ice_local_preference_full(transport_preference, -- direction_preference, 0); -+ direction_preference, nice_candidate_ip_local_preference (candidate)); - } - - static guint8 -@@ -238,7 +277,10 @@ nice_candidate_ice_type_preference (const NiceCandidate *candidate, - type_preference = NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE; - break; - case NICE_CANDIDATE_TYPE_RELAYED: -- type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED; -+ if (candidate->turn->type == NICE_RELAY_TYPE_TURN_UDP) -+ type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP; -+ else -+ type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED; - break; - default: - type_preference = 0; -diff --git a/agent/candidate.h b/agent/candidate.h -index 5e0eaf3..fadfce3 100644 ---- a/agent/candidate.h -+++ b/agent/candidate.h -@@ -64,8 +64,8 @@ G_BEGIN_DECLS - #define NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE 110 - #define NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED 105 - #define NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE 100 --#define NICE_CANDIDATE_TYPE_PREF_UDP_TUNNELED 75 --#define NICE_CANDIDATE_TYPE_PREF_RELAYED 10 -+#define NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP 30 -+#define NICE_CANDIDATE_TYPE_PREF_RELAYED 20 - - /* Priority preference constants for MS-ICE compatibility */ - #define NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP 15 -diff --git a/agent/component.c b/agent/component.c -index 1a1f84a..d38d3e5 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -59,11 +59,33 @@ static volatile unsigned int n_components_destroyed = 0; - #include "discovery.h" - #include "agent-priv.h" - -+G_DEFINE_TYPE (NiceComponent, nice_component, G_TYPE_OBJECT); - -+typedef enum { -+ PROP_ID = 1, -+ PROP_AGENT, -+ PROP_STREAM, -+} NiceComponentProperty; -+ -+static void -+nice_component_constructed (GObject *obj); - static void --component_schedule_io_callback (Component *component); -+nice_component_get_property (GObject *obj, -+ guint property_id, GValue *value, GParamSpec *pspec); - static void --component_deschedule_io_callback (Component *component); -+nice_component_set_property (GObject *obj, -+ guint property_id, const GValue *value, GParamSpec *pspec); -+static void -+nice_component_finalize (GObject *obj); -+ -+static void -+nice_component_schedule_io_callback (NiceComponent *component); -+static void -+nice_component_deschedule_io_callback (NiceComponent *component); -+static void -+nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock); -+static void -+nice_component_clear_selected_pair (NiceComponent *component); - - - void -@@ -74,12 +96,15 @@ incoming_check_free (IncomingCheck *icheck) - } - - /* Must *not* take the agent lock, since it’s called from within -- * component_set_io_context(), which holds the Component’s I/O lock. */ -+ * nice_component_set_io_context(), which holds the Component’s I/O lock. */ - static void - socket_source_attach (SocketSource *socket_source, GMainContext *context) - { - GSource *source; - -+ if (socket_source->socket->fileno == NULL) -+ return; -+ - /* Create a source. */ - source = g_socket_create_source (socket_source->socket->fileno, - G_IO_IN, NULL); -@@ -121,50 +146,57 @@ socket_source_free (SocketSource *source) - g_slice_free (SocketSource, source); - } - --Component * --component_new (guint id, NiceAgent *agent, Stream *stream) -+NiceComponent * -+nice_component_new (guint id, NiceAgent *agent, NiceStream *stream) - { -- Component *component; -- -- g_atomic_int_inc (&n_components_created); -- nice_debug ("Created NiceComponent (%u created, %u destroyed)", -- n_components_created, n_components_destroyed); -+ return g_object_new (NICE_TYPE_COMPONENT, -+ "id", id, -+ "agent", agent, -+ "stream", stream, -+ NULL); -+} - -- component = g_slice_new0 (Component); -- component->id = id; -- component->state = NICE_COMPONENT_STATE_DISCONNECTED; -- component->restart_candidate = NULL; -- component->tcp = NULL; -- component->agent = agent; -- component->stream = stream; -+void -+nice_component_remove_socket (NiceComponent *cmp, NiceSocket *nsocket) -+{ -+ GSList *i; - -- nice_agent_init_stun_agent (agent, &component->stun_agent); -+ for (i = cmp->local_candidates; i;) { -+ NiceCandidate *candidate = i->data; -+ GSList *next = i->next; - -- g_mutex_init (&component->io_mutex); -- g_queue_init (&component->pending_io_messages); -- component->io_callback_id = 0; -+ if (!nice_socket_is_based_on (candidate->sockptr, nsocket)) { -+ i = next; -+ continue; -+ } - -- component->own_ctx = g_main_context_new (); -- 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); -- g_source_attach (component->stop_cancellable_source, component->own_ctx); -- component->ctx = g_main_context_ref (component->own_ctx); -+ if (candidate == cmp->selected_pair.local) { -+ nice_component_clear_selected_pair (cmp); -+ agent_signal_component_state_change (cmp->agent, cmp->stream->id, -+ cmp->id, NICE_COMPONENT_STATE_FAILED); -+ } - -- /* Start off with a fresh main context and all I/O paused. This -- * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages() -- * are called. */ -- component_set_io_context (component, NULL); -- component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); -+ refresh_prune_candidate (cmp->agent, candidate); -+ if (candidate->sockptr != nsocket) { -+ discovery_prune_socket (cmp->agent, candidate->sockptr); -+ conn_check_prune_socket (cmp->agent, cmp->stream, cmp, -+ candidate->sockptr); -+ nice_component_detach_socket (cmp, candidate->sockptr); -+ } -+ agent_remove_local_candidate (cmp->agent, candidate); -+ nice_candidate_free (candidate); - -- g_queue_init (&component->queued_tcp_packets); -+ cmp->local_candidates = g_slist_delete_link (cmp->local_candidates, i); -+ i = next; -+ } - -- return component; -+ discovery_prune_socket (cmp->agent, nsocket); -+ conn_check_prune_socket (cmp->agent, cmp->stream, cmp, nsocket); -+ nice_component_detach_socket (cmp, nsocket); - } - - void --component_clean_turn_servers (Component *cmp) -+nice_component_clean_turn_servers (NiceComponent *cmp) - { - GSList *i; - -@@ -195,7 +227,7 @@ component_clean_turn_servers (Component *cmp) - discovery_prune_socket (cmp->agent, cmp->turn_candidate->sockptr); - conn_check_prune_socket (cmp->agent, cmp->stream, cmp, - cmp->turn_candidate->sockptr); -- component_detach_socket (cmp, cmp->turn_candidate->sockptr); -+ nice_component_detach_socket (cmp, cmp->turn_candidate->sockptr); - nice_candidate_free (cmp->turn_candidate); - } - /* Bring the priority down to 0, so that it will be replaced -@@ -208,7 +240,7 @@ component_clean_turn_servers (Component *cmp) - discovery_prune_socket (cmp->agent, candidate->sockptr); - conn_check_prune_socket (cmp->agent, cmp->stream, cmp, - candidate->sockptr); -- component_detach_socket (cmp, candidate->sockptr); -+ nice_component_detach_socket (cmp, candidate->sockptr); - agent_remove_local_candidate (cmp->agent, candidate); - nice_candidate_free (candidate); - } -@@ -218,7 +250,7 @@ component_clean_turn_servers (Component *cmp) - } - - static void --component_clear_selected_pair (Component *component) -+nice_component_clear_selected_pair (NiceComponent *component) - { - if (component->selected_pair.keepalive.tick_source != NULL) { - g_source_destroy (component->selected_pair.keepalive.tick_source); -@@ -232,7 +264,7 @@ component_clear_selected_pair (Component *component) - /* Must be called with the agent lock held as it touches internal Component - * state. */ - void --component_close (Component *cmp) -+nice_component_close (NiceComponent *cmp) - { - IOCallbackData *data; - GOutputVector *vec; -@@ -240,13 +272,13 @@ component_close (Component *cmp) - /* Start closing the pseudo-TCP socket first. FIXME: There is a very big and - * reliably triggerable race here. pseudo_tcp_socket_close() does not block - * on the socket closing — it only sends the first packet of the FIN -- * handshake. component_close() will immediately afterwards close the -+ * handshake. nice_component_close() will immediately afterwards close the - * underlying component sockets, aborting the handshake. - * - * On the principle that starting the FIN handshake is better than not - * starting it, even if it’s later truncated, call pseudo_tcp_socket_close(). -- * A long-term fix is needed in the form of making component_close() (and all -- * its callers) async, so we can properly block on closure. */ -+ * A long-term fix is needed in the form of making nice_component_close() (and -+ * all its callers) async, so we can properly block on closure. */ - if (cmp->tcp) { - pseudo_tcp_socket_close (cmp->tcp, TRUE); - } -@@ -269,12 +301,12 @@ component_close (Component *cmp) - g_slist_free_full (cmp->remote_candidates, - (GDestroyNotify) nice_candidate_free); - cmp->remote_candidates = NULL; -- component_free_socket_sources (cmp); -+ nice_component_free_socket_sources (cmp); - g_slist_free_full (cmp->incoming_checks, - (GDestroyNotify) incoming_check_free); - cmp->incoming_checks = NULL; - -- component_clean_turn_servers (cmp); -+ nice_component_clean_turn_servers (cmp); - - if (cmp->tcp_clock) { - g_source_destroy (cmp->tcp_clock); -@@ -289,7 +321,7 @@ component_close (Component *cmp) - while ((data = g_queue_pop_head (&cmp->pending_io_messages)) != NULL) - io_callback_data_free (data); - -- component_deschedule_io_callback (cmp); -+ nice_component_deschedule_io_callback (cmp); - - g_cancellable_cancel (cmp->stop_cancellable); - -@@ -299,47 +331,13 @@ component_close (Component *cmp) - } - } - --/* Must be called with the agent lock released as it could dispose of -- * NiceIOStreams. */ --void --component_free (Component *cmp) --{ -- /* Component should have been closed already. */ -- g_warn_if_fail (cmp->local_candidates == NULL); -- g_warn_if_fail (cmp->remote_candidates == NULL); -- g_warn_if_fail (cmp->incoming_checks == NULL); -- -- g_clear_object (&cmp->tcp); -- g_clear_object (&cmp->stop_cancellable); -- g_clear_object (&cmp->iostream); -- g_mutex_clear (&cmp->io_mutex); -- -- if (cmp->stop_cancellable_source != NULL) { -- g_source_destroy (cmp->stop_cancellable_source); -- g_source_unref (cmp->stop_cancellable_source); -- } -- -- if (cmp->ctx != NULL) { -- g_main_context_unref (cmp->ctx); -- cmp->ctx = NULL; -- } -- -- g_main_context_unref (cmp->own_ctx); -- -- g_slice_free (Component, cmp); -- -- g_atomic_int_inc (&n_components_destroyed); -- nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)", -- n_components_created, n_components_destroyed); --} -- - /* - * Finds a candidate pair that has matching foundation ids. - * - * @return TRUE if pair found, pointer to pair stored at 'pair' - */ - gboolean --component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair) -+nice_component_find_pair (NiceComponent *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair) - { - GSList *i; - CandidatePair result = { 0, }; -@@ -375,7 +373,7 @@ component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, - * session. - */ - void --component_restart (Component *cmp) -+nice_component_restart (NiceComponent *cmp) - { - GSList *i; - -@@ -410,7 +408,8 @@ component_restart (Component *cmp) - * Changes the selected pair for the component to 'pair'. Does not - * emit the "selected-pair-changed" signal. - */ --void component_update_selected_pair (Component *component, const CandidatePair *pair) -+void -+nice_component_update_selected_pair (NiceComponent *component, const CandidatePair *pair) - { - g_assert (component); - g_assert (pair); -@@ -425,17 +424,17 @@ void component_update_selected_pair (Component *component, const CandidatePair * - component->turn_candidate->sockptr); - conn_check_prune_socket (component->agent, component->stream, component, - component->turn_candidate->sockptr); -- component_detach_socket (component, component->turn_candidate->sockptr); -+ nice_component_detach_socket (component, component->turn_candidate->sockptr); - nice_candidate_free (component->turn_candidate); - component->turn_candidate = NULL; - } - -- component_clear_selected_pair (component); -+ nice_component_clear_selected_pair (component); - - component->selected_pair.local = pair->local; - component->selected_pair.remote = pair->remote; - component->selected_pair.priority = pair->priority; -- -+ component->selected_pair.prflx_priority = pair->prflx_priority; - } - - /* -@@ -445,7 +444,7 @@ void component_update_selected_pair (Component *component, const CandidatePair * - * @return pointer to candidate or NULL if not found - */ - NiceCandidate * --component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport) -+nice_component_find_remote_candidate (NiceComponent *component, const NiceAddress *addr, NiceCandidateTransport transport) - { - GSList *i; - -@@ -469,8 +468,8 @@ component_find_remote_candidate (const Component *component, const NiceAddress * - */ - - NiceCandidate * --component_set_selected_remote_candidate (NiceAgent *agent, Component *component, -- NiceCandidate *candidate) -+nice_component_set_selected_remote_candidate (NiceComponent *component, -+ NiceAgent *agent, NiceCandidate *candidate) - { - NiceCandidate *local = NULL; - NiceCandidate *remote = NULL; -@@ -483,7 +482,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component, - NiceCandidate *tmp = item->data; - guint64 tmp_prio = 0; - -- if (tmp->transport != candidate->transport || -+ if (tmp->transport != conn_check_match_transport(candidate->transport) || - tmp->addr.s.addr.sa_family != candidate->addr.s.addr.sa_family || - tmp->type != NICE_CANDIDATE_TYPE_HOST) - continue; -@@ -499,7 +498,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component, - if (local == NULL) - return NULL; - -- remote = component_find_remote_candidate (component, &candidate->addr, -+ remote = nice_component_find_remote_candidate (component, &candidate->addr, - candidate->transport); - - if (!remote) { -@@ -509,7 +508,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component, - agent_signal_new_remote_candidate (agent, remote); - } - -- component_clear_selected_pair (component); -+ nice_component_clear_selected_pair (component); - - component->selected_pair.local = local; - component->selected_pair.remote = remote; -@@ -530,7 +529,7 @@ _find_socket_source (gconstpointer a, gconstpointer b) - /* This takes ownership of the socket. - * It creates and attaches a source to the component’s context. */ - void --component_attach_socket (Component *component, NiceSocket *nicesock) -+nice_component_attach_socket (NiceComponent *component, NiceSocket *nicesock) - { - GSList *l; - SocketSource *socket_source; -@@ -540,9 +539,6 @@ component_attach_socket (Component *component, NiceSocket *nicesock) - - g_assert (component->ctx != NULL); - -- if (nicesock->fileno == NULL) -- return; -- - /* Find an existing SocketSource in the component which contains @socket, or - * create a new one. - * -@@ -559,7 +555,8 @@ component_attach_socket (Component *component, NiceSocket *nicesock) - socket_source->component = component; - component->socket_sources = - g_slist_prepend (component->socket_sources, socket_source); -- component->socket_sources_age++; -+ if (nicesock->fileno != NULL) -+ component->socket_sources_age++; - } - - /* Create and attach a source */ -@@ -571,9 +568,9 @@ component_attach_socket (Component *component, NiceSocket *nicesock) - /* Reattaches socket handles of @component to the main context. - * - * Must *not* take the agent lock, since it’s called from within -- * component_set_io_context(), which holds the Component’s I/O lock. */ -+ * nice_component_set_io_context(), which holds the Component’s I/O lock. */ - static void --component_reattach_all_sockets (Component *component) -+nice_component_reattach_all_sockets (NiceComponent *component) - { - GSList *i; - -@@ -586,8 +583,8 @@ component_reattach_all_sockets (Component *component) - } - - /** -- * component_detach_socket: -- * @component: a #Component -+ * nice_component_detach_socket: -+ * @component: a #NiceComponent - * @socket: the socket to detach the source for - * - * Detach the #GSource for the single specified @socket. It also closes it -@@ -595,8 +592,8 @@ component_reattach_all_sockets (Component *component) - * - * If the @socket doesn’t exist in this @component, do nothing. - */ --void --component_detach_socket (Component *component, NiceSocket *nicesock) -+static void -+nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock) - { - GSList *l; - SocketSource *socket_source; -@@ -637,10 +634,10 @@ component_detach_socket (Component *component, NiceSocket *nicesock) - * sockets themselves untouched. - * - * Must *not* take the agent lock, since it’s called from within -- * component_set_io_context(), which holds the Component’s I/O lock. -+ * nice_component_set_io_context(), which holds the Component’s I/O lock. - */ - void --component_detach_all_sockets (Component *component) -+nice_component_detach_all_sockets (NiceComponent *component) - { - GSList *i; - -@@ -653,7 +650,7 @@ component_detach_all_sockets (Component *component) - } - - void --component_free_socket_sources (Component *component) -+nice_component_free_socket_sources (NiceComponent *component) - { - nice_debug ("Free socket sources for component %p.", component); - -@@ -662,11 +659,11 @@ component_free_socket_sources (Component *component) - component->socket_sources = NULL; - component->socket_sources_age++; - -- component_clear_selected_pair (component); -+ nice_component_clear_selected_pair (component); - } - - GMainContext * --component_dup_io_context (Component *component) -+nice_component_dup_io_context (NiceComponent *component) - { - return g_main_context_ref (component->own_ctx); - } -@@ -674,7 +671,7 @@ component_dup_io_context (Component *component) - /* If @context is %NULL, it's own context is used, so component->ctx is always - * guaranteed to be non-%NULL. */ - void --component_set_io_context (Component *component, GMainContext *context) -+nice_component_set_io_context (NiceComponent *component, GMainContext *context) - { - g_mutex_lock (&component->io_mutex); - -@@ -684,11 +681,11 @@ component_set_io_context (Component *component, GMainContext *context) - else - g_main_context_ref (context); - -- component_detach_all_sockets (component); -+ nice_component_detach_all_sockets (component); - g_main_context_unref (component->ctx); - - component->ctx = context; -- component_reattach_all_sockets (component); -+ nice_component_reattach_all_sockets (component); - } - - g_mutex_unlock (&component->io_mutex); -@@ -705,7 +702,7 @@ component_set_io_context (Component *component, GMainContext *context) - * emitted for it (which could cause data loss if the I/O callback function was - * unset in that time). */ - void --component_set_io_callback (Component *component, -+nice_component_set_io_callback (NiceComponent *component, - NiceAgentRecvFunc func, gpointer user_data, - NiceInputMessage *recv_messages, guint n_recv_messages, - GError **error) -@@ -722,14 +719,14 @@ component_set_io_callback (Component *component, - component->recv_messages = NULL; - component->n_recv_messages = 0; - -- component_schedule_io_callback (component); -+ nice_component_schedule_io_callback (component); - } else { - component->io_callback = NULL; - component->io_user_data = NULL; - component->recv_messages = recv_messages; - component->n_recv_messages = n_recv_messages; - -- component_deschedule_io_callback (component); -+ nice_component_deschedule_io_callback (component); - } - - nice_input_message_iter_reset (&component->recv_messages_iter); -@@ -739,7 +736,7 @@ component_set_io_callback (Component *component, - } - - gboolean --component_has_io_callback (Component *component) -+nice_component_has_io_callback (NiceComponent *component) - { - gboolean has_io_callback; - -@@ -775,7 +772,7 @@ io_callback_data_free (IOCallbackData *data) - static gboolean - emit_io_callback_cb (gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - IOCallbackData *data; - NiceAgentRecvFunc io_callback; - gpointer io_user_data; -@@ -792,7 +789,7 @@ emit_io_callback_cb (gpointer user_data) - g_mutex_lock (&component->io_mutex); - - /* The members of Component are guaranteed not to have changed since this -- * GSource was attached in component_emit_io_callback(). The Component’s agent -+ * GSource was attached in nice_component_emit_io_callback(). The Component’s agent - * and stream are immutable after construction, as are the stream and - * component IDs. The callback and its user data may have changed, but are - * guaranteed to be non-%NULL at the start as the idle source is removed when -@@ -802,7 +799,7 @@ emit_io_callback_cb (gpointer user_data) - * - * If the component is destroyed (which happens if the agent or stream are - * destroyed) between attaching the GSource and firing it, the GSource is -- * detached in component_free() and this callback is never invoked. If the -+ * detached during dispose and this callback is never invoked. If the - * agent is destroyed during an io_callback, its weak pointer will be - * nullified. Similarly, the Component needs to be re-queried for after every - * iteration, just in case the client has removed the stream in the -@@ -845,7 +842,7 @@ emit_io_callback_cb (gpointer user_data) - - /* This must be called with the agent lock *held*. */ - void --component_emit_io_callback (Component *component, -+nice_component_emit_io_callback (NiceComponent *component, - const guint8 *buf, gsize buf_len) - { - NiceAgent *agent; -@@ -897,7 +894,7 @@ component_emit_io_callback (Component *component, - - nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - -- component_schedule_io_callback (component); -+ nice_component_schedule_io_callback (component); - - g_mutex_unlock (&component->io_mutex); - } -@@ -905,7 +902,7 @@ component_emit_io_callback (Component *component, - - /* Note: Must be called with the io_mutex held. */ - static void --component_schedule_io_callback (Component *component) -+nice_component_schedule_io_callback (NiceComponent *component) - { - GSource *source; - -@@ -928,7 +925,7 @@ component_schedule_io_callback (Component *component) - - /* Note: Must be called with the io_mutex held. */ - static void --component_deschedule_io_callback (Component *component) -+nice_component_deschedule_io_callback (NiceComponent *component) - { - /* Already descheduled? */ - if (component->io_callback_id == 0) -@@ -938,6 +935,202 @@ component_deschedule_io_callback (Component *component) - component->io_callback_id = 0; - } - -+static void -+nice_component_class_init (NiceComponentClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ -+ object_class->constructed = nice_component_constructed; -+ object_class->get_property = nice_component_get_property; -+ object_class->set_property = nice_component_set_property; -+ object_class->finalize = nice_component_finalize; -+ -+ /** -+ * NiceComponent:id: -+ * -+ * The unique numeric ID of the component. -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (object_class, PROP_ID, -+ g_param_spec_uint ( -+ "id", -+ "ID", -+ "The unique numeric ID of the component.", -+ 1, G_MAXUINT, 1, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ -+ /** -+ * NiceComponent:agent: -+ * -+ * The #NiceAgent this component belongs to. -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (object_class, PROP_AGENT, -+ g_param_spec_object ( -+ "agent", -+ "Agent", -+ "The NiceAgent this component belongs to.", -+ NICE_TYPE_AGENT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ -+ /** -+ * NiceComponent:stream: -+ * -+ * The #NiceStream this component belongs to. -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (object_class, PROP_STREAM, -+ g_param_spec_object ( -+ "stream", -+ "Stream", -+ "The NiceStream this component belongs to.", -+ NICE_TYPE_STREAM, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+} -+ -+static void -+nice_component_init (NiceComponent *component) -+{ -+ g_atomic_int_inc (&n_components_created); -+ nice_debug ("Created NiceComponent (%u created, %u destroyed)", -+ n_components_created, n_components_destroyed); -+ -+ component->id = 0; -+ component->state = NICE_COMPONENT_STATE_DISCONNECTED; -+ component->restart_candidate = NULL; -+ component->tcp = NULL; -+ component->agent = NULL; -+ component->stream = NULL; -+ -+ g_mutex_init (&component->io_mutex); -+ g_queue_init (&component->pending_io_messages); -+ component->io_callback_id = 0; -+ -+ component->own_ctx = g_main_context_new (); -+ 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); -+ g_source_attach (component->stop_cancellable_source, component->own_ctx); -+ component->ctx = g_main_context_ref (component->own_ctx); -+ -+ /* Start off with a fresh main context and all I/O paused. This -+ * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages() -+ * are called. */ -+ nice_component_set_io_context (component, NULL); -+ nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); -+ -+ g_queue_init (&component->queued_tcp_packets); -+} -+ -+static void -+nice_component_constructed (GObject *obj) -+{ -+ NiceComponent *component; -+ -+ component = NICE_COMPONENT (obj); -+ -+ g_assert (component->agent != NULL); -+ nice_agent_init_stun_agent (component->agent, &component->stun_agent); -+ -+ G_OBJECT_CLASS (nice_component_parent_class)->constructed (obj); -+} -+ -+static void -+nice_component_get_property (GObject *obj, -+ guint property_id, GValue *value, GParamSpec *pspec) -+{ -+ NiceComponent *component; -+ -+ component = NICE_COMPONENT (obj); -+ -+ switch ((NiceComponentProperty) property_id) -+ { -+ case PROP_ID: -+ g_value_set_uint (value, component->id); -+ break; -+ -+ case PROP_AGENT: -+ g_value_set_object (value, component->agent); -+ break; -+ -+ case PROP_STREAM: -+ g_value_set_object (value, component->stream); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); -+ } -+} -+ -+static void -+nice_component_set_property (GObject *obj, -+ guint property_id, const GValue *value, GParamSpec *pspec) -+{ -+ NiceComponent *component; -+ -+ component = NICE_COMPONENT (obj); -+ -+ switch ((NiceComponentProperty) property_id) -+ { -+ case PROP_ID: -+ component->id = g_value_get_uint (value); -+ break; -+ -+ case PROP_AGENT: -+ component->agent = g_value_get_object (value); -+ break; -+ -+ case PROP_STREAM: -+ component->stream = g_value_get_object (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); -+ } -+} -+ -+/* Must be called with the agent lock released as it could dispose of -+ * NiceIOStreams. */ -+static void -+nice_component_finalize (GObject *obj) -+{ -+ NiceComponent *cmp; -+ -+ cmp = NICE_COMPONENT (obj); -+ -+ /* Component should have been closed already. */ -+ g_warn_if_fail (cmp->local_candidates == NULL); -+ g_warn_if_fail (cmp->remote_candidates == NULL); -+ g_warn_if_fail (cmp->incoming_checks == NULL); -+ -+ g_clear_object (&cmp->tcp); -+ g_clear_object (&cmp->stop_cancellable); -+ g_clear_object (&cmp->iostream); -+ g_mutex_clear (&cmp->io_mutex); -+ -+ if (cmp->stop_cancellable_source != NULL) { -+ g_source_destroy (cmp->stop_cancellable_source); -+ g_source_unref (cmp->stop_cancellable_source); -+ } -+ -+ if (cmp->ctx != NULL) { -+ g_main_context_unref (cmp->ctx); -+ cmp->ctx = NULL; -+ } -+ -+ g_main_context_unref (cmp->own_ctx); -+ -+ g_atomic_int_inc (&n_components_destroyed); -+ nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)", -+ n_components_created, n_components_destroyed); -+ -+ G_OBJECT_CLASS (nice_component_parent_class)->finalize (obj); -+} -+ - /** - * ComponentSource: - * -@@ -980,7 +1173,7 @@ component_source_prepare (GSource *source, gint *timeout_) - { - ComponentSource *component_source = (ComponentSource *) source; - NiceAgent *agent; -- Component *component; -+ NiceComponent *component; - GSList *parentl, *childl; - - agent = g_weak_ref_get (&component_source->agent_ref); -@@ -1011,6 +1204,9 @@ component_source_prepare (GSource *source, gint *timeout_) - SocketSource *parent_socket_source = parentl->data; - SocketSource *child_socket_source; - -+ if (parent_socket_source->socket->fileno == NULL) -+ continue; -+ - /* Iterating the list of socket sources every time isn't a big problem - * because the number of pairs is limited ~100 normally, so there will - * rarely be more than 10. -@@ -1128,7 +1324,7 @@ static GSourceFuncs component_source_funcs = { - }; - - /** -- * component_source_new: -+ * nice_component_source_new: - * @agent: a #NiceAgent - * @stream_id: The stream's id - * @component_id: The component's number -@@ -1150,7 +1346,7 @@ static GSourceFuncs component_source_funcs = { - * Returns: (transfer full): a new #ComponentSource; unref with g_source_unref() - */ - GSource * --component_input_source_new (NiceAgent *agent, guint stream_id, -+nice_component_input_source_new (NiceAgent *agent, guint stream_id, - guint component_id, GPollableInputStream *pollable_istream, - GCancellable *cancellable) - { -diff --git a/agent/component.h b/agent/component.h -index 7ded710..6712794 100644 ---- a/agent/component.h -+++ b/agent/component.h -@@ -42,7 +42,7 @@ - - #include <glib.h> - --typedef struct _Component Component; -+typedef struct _NiceComponent NiceComponent; - - #include "agent.h" - #include "agent-priv.h" -@@ -83,6 +83,7 @@ struct _CandidatePair - NiceCandidate *local; - NiceCandidate *remote; - guint64 priority; /* candidate pair priority */ -+ guint32 prflx_priority; - CandidatePairKeepalive keepalive; - }; - -@@ -110,7 +111,7 @@ incoming_check_free (IncomingCheck *icheck); - typedef struct { - NiceSocket *socket; - GSource *source; -- Component *component; -+ NiceComponent *component; - } SocketSource; - - -@@ -137,9 +138,22 @@ io_callback_data_new (const guint8 *buf, gsize buf_len); - void - io_callback_data_free (IOCallbackData *data); - -+#define NICE_TYPE_COMPONENT nice_component_get_type() -+#define NICE_COMPONENT(obj) \ -+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), NICE_TYPE_COMPONENT, NiceComponent)) -+#define NICE_COMPONENT_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_CAST ((klass), NICE_TYPE_COMPONENT, NiceComponentClass)) -+#define NICE_IS_COMPONENT(obj) \ -+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NICE_TYPE_COMPONENT)) -+#define NICE_IS_COMPONENT_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_TYPE ((klass), NICE_TYPE_COMPONENT)) -+#define NICE_COMPONENT_GET_CLASS(obj) \ -+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_COMPONENT, NiceComponentClass)) -+ -+struct _NiceComponent { -+ /*< private >*/ -+ GObject parent; - --struct _Component --{ - NiceComponentType type; - guint id; /* component id */ - NiceComponentState state; -@@ -186,8 +200,8 @@ struct _Component - - NiceAgent *agent; /* unowned, immutable: can be accessed without holding the - * agent lock */ -- Stream *stream; /* unowned, immutable: can be accessed without holding the -- * agent lock */ -+ NiceStream *stream; /* unowned, immutable: can be accessed without holding -+ * the agent lock */ - - StunAgent stun_agent; /* This stun agent is used to validate all stun requests */ - -@@ -212,63 +226,69 @@ struct _Component - GQueue queued_tcp_packets; - }; - --Component * --component_new (guint component_id, NiceAgent *agent, Stream *stream); -+typedef struct { -+ GObjectClass parent_class; -+} NiceComponentClass; -+ -+GType nice_component_get_type (void); - --void --component_close (Component *cmp); -+NiceComponent * -+nice_component_new (guint component_id, NiceAgent *agent, NiceStream *stream); - - void --component_free (Component *cmp); -+nice_component_close (NiceComponent *component); - - gboolean --component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair); -+nice_component_find_pair (NiceComponent *component, NiceAgent *agent, -+ const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair); - - void --component_restart (Component *cmp); -+nice_component_restart (NiceComponent *component); - - void --component_update_selected_pair (Component *component, const CandidatePair *pair); -+nice_component_update_selected_pair (NiceComponent *component, -+ const CandidatePair *pair); - - NiceCandidate * --component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport); -+nice_component_find_remote_candidate (NiceComponent *component, -+ const NiceAddress *addr, NiceCandidateTransport transport); - - NiceCandidate * --component_set_selected_remote_candidate (NiceAgent *agent, Component *component, -- NiceCandidate *candidate); -+nice_component_set_selected_remote_candidate (NiceComponent *component, -+ NiceAgent *agent, NiceCandidate *candidate); - - void --component_attach_socket (Component *component, NiceSocket *nsocket); -+nice_component_attach_socket (NiceComponent *component, NiceSocket *nsocket); -+ - void --component_detach_socket (Component *component, NiceSocket *nsocket); -+nice_component_remove_socket (NiceComponent *component, NiceSocket *nsocket); - void --component_detach_all_sockets (Component *component); -+nice_component_detach_all_sockets (NiceComponent *component); -+ - void --component_free_socket_sources (Component *component); -+nice_component_free_socket_sources (NiceComponent *component); - - GSource * --component_input_source_new (NiceAgent *agent, guint stream_id, -+nice_component_input_source_new (NiceAgent *agent, guint stream_id, - guint component_id, GPollableInputStream *pollable_istream, - GCancellable *cancellable); - - GMainContext * --component_dup_io_context (Component *component); -+nice_component_dup_io_context (NiceComponent *component); - void --component_set_io_context (Component *component, GMainContext *context); -+nice_component_set_io_context (NiceComponent *component, GMainContext *context); - void --component_set_io_callback (Component *component, -+nice_component_set_io_callback (NiceComponent *component, - NiceAgentRecvFunc func, gpointer user_data, - NiceInputMessage *recv_messages, guint n_recv_messages, - GError **error); - void --component_emit_io_callback (Component *component, -+nice_component_emit_io_callback (NiceComponent *component, - const guint8 *buf, gsize buf_len); -- - gboolean --component_has_io_callback (Component *component); -- -+nice_component_has_io_callback (NiceComponent *component); - void --component_clean_turn_servers (Component *component); -+nice_component_clean_turn_servers (NiceComponent *component); - - - TurnServer * -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 057fc81..7e03985 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -61,18 +61,20 @@ - #include "stun/usages/bind.h" - #include "stun/usages/turn.h" - --static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream); --static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component); --static guint priv_prune_pending_checks (Stream *stream, guint component_id); --static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); --static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand); --static size_t priv_create_username (NiceAgent *agent, Stream *stream, -+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 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, - uint8_t *dest, guint dest_len, gboolean inbound); --static size_t priv_get_password (NiceAgent *agent, Stream *stream, -+static size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - NiceCandidate *remote, uint8_t **password); - static void conn_check_free_item (gpointer data); --static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); -+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 int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - { -@@ -81,6 +83,140 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - now->tv_sec >= timer->tv_sec; - } - -+static gchar -+priv_state_to_gchar (NiceCheckState state) -+{ -+ switch (state) { -+ case NICE_CHECK_WAITING: -+ return 'W'; -+ case NICE_CHECK_IN_PROGRESS: -+ return 'I'; -+ case NICE_CHECK_SUCCEEDED: -+ return 'S'; -+ case NICE_CHECK_FAILED: -+ return 'F'; -+ case NICE_CHECK_FROZEN: -+ return 'Z'; -+ case NICE_CHECK_CANCELLED: -+ return 'C'; -+ case NICE_CHECK_DISCOVERED: -+ return 'D'; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * -+priv_candidate_type_to_string (NiceCandidateType type) -+{ -+ switch (type) { -+ case NICE_CANDIDATE_TYPE_HOST: -+ return "host"; -+ case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: -+ return "srflx"; -+ case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: -+ return "prflx"; -+ case NICE_CANDIDATE_TYPE_RELAYED: -+ return "relay"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+/* -+ * Dump the conncheck lists of the agent -+ */ -+static void -+priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) -+{ -+ GSList *i, *k; -+ guint j; -+ -+ if (!nice_debug_is_verbose ()) -+ return; -+ -+#define PRIORITY_LEN 32 -+ -+ nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", -+ agent, where, detail ? detail : ""); -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ for (j = 1; j <= stream->n_components; j++) { -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *pair = k->data; -+ if (pair->component_id == j) { -+ gchar priority[PRIORITY_LEN]; -+ guint p1, p2, p3; -+ gchar local_addr[INET6_ADDRSTRLEN]; -+ gchar remote_addr[INET6_ADDRSTRLEN]; -+ -+ p1 = (pair->priority >> 32); -+ p2 = (pair->priority >> 1) & 0x7fffffff; -+ p3 = (pair->priority & 1); -+ -+ g_snprintf (priority, PRIORITY_LEN, -+ "%02x:%04x:%02x:%02x:%04x:%02x:%1x", -+ (p1 >> 24) & 0x7f, (p1 >> 8) & 0xffff, (p1 & 0xff), -+ (p2 >> 24) & 0x7f, (p2 >> 8) & 0xffff, (p2 & 0xff), -+ p3); -+ -+ 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 p=%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), -+ priority, -+ 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" : ""); -+ } -+ } -+ } -+ } -+} -+ -+/* Add the pair to the triggered checks list, if not already present -+ */ -+static void -+priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) -+{ -+ g_assert (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); -+} -+ -+/* Remove the pair from the triggered checks list -+ */ -+static void -+priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) -+{ -+ g_assert (pair); -+ agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair); -+} -+ -+/* Get the pair from the triggered checks list -+ */ -+static CandidateCheckPair * -+priv_get_pair_from_triggered_check_queue (NiceAgent *agent) -+{ -+ CandidateCheckPair *pair = NULL; -+ -+ if (agent->triggered_check_queue) { -+ pair = (CandidateCheckPair *)agent->triggered_check_queue->data; -+ priv_remove_pair_from_triggered_check_queue (agent, pair); -+ } -+ return pair; -+} -+ - /* - * Finds the next connectivity check in WAITING state. - */ -@@ -107,10 +243,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - */ - static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) - { -- /* XXX: from ID-16 onwards, the checks should not be sent -- * immediately, but be put into the "triggered queue", -- * see "7.2.1.4 Triggered Checks" -- */ - g_get_current_time (&pair->next_tick); - g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); - pair->state = NICE_CHECK_IN_PROGRESS; -@@ -142,7 +274,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - */ - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - guint64 max_frozen_priority = 0; - - -@@ -185,7 +317,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - * - * @return TRUE on success, and FALSE if no frozen candidates were found. - */ --static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check) -+static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) - { - GSList *i, *j; - guint unfrozen = 0; -@@ -212,10 +344,10 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, - - /* step: perform the step (2) of 'Updating Pair States' */ - stream = agent_find_stream (agent, ok_check->stream_id); -- if (stream_all_components_ready (stream)) { -+ if (nice_stream_all_components_ready (stream)) { - /* step: unfreeze checks from other streams */ - for (i = agent->streams; i ; i = i->next) { -- Stream *s = i->data; -+ NiceStream *s = i->data; - for (j = stream->conncheck_list; j ; j = j->next) { - CandidateCheckPair *p = j->data; - -@@ -242,12 +374,12 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, - } - - static void --candidate_check_pair_fail (Stream *stream, NiceAgent *agent, CandidateCheckPair *p) -+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) - { - StunTransactionId id; -- Component *component; -+ NiceComponent *component; - -- component = stream_find_component_by_id (stream, p->component_id); -+ 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); -@@ -269,14 +401,16 @@ candidate_check_pair_fail (Stream *stream, NiceAgent *agent, CandidateCheckPair - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) - { - gboolean keep_timer_going = FALSE; - guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, -- s_nominated = 0, s_waiting_for_nomination = 0; -+ s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; - guint frozen = 0, waiting = 0; - GSList *i, *k; - -+ priv_print_conn_check_lists (agent, G_STRFUNC, NULL); -+ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -290,7 +424,13 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - { - /* case: error, abort processing */ -+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ 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); - - break; -@@ -299,8 +439,9 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - { - /* case: not ready, so schedule a new timeout */ - unsigned int timeout = stun_timer_remainder (&p->timer); -- nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).", -- agent, 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), -@@ -311,8 +452,8 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - p->next_tick = *now; - g_time_val_add (&p->next_tick, timeout * 1000); - -- keep_timer_going = TRUE; -- break; -+ *stun_transmitted = TRUE; -+ return TRUE; - } - case STUN_USAGE_TIMER_RETURN_SUCCESS: - { -@@ -342,6 +483,8 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - ++s_succeeded; - else if (p->state == NICE_CHECK_DISCOVERED) - ++s_discovered; -+ if (p->valid) -+ ++s_valid; - - if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED) - && p->nominated) -@@ -365,7 +508,7 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - - for (component_item = stream->components; component_item; - component_item = component_item->next) { -- Component *component = component_item->data; -+ NiceComponent *component = component_item->data; - - for (k = stream->conncheck_list; k ; k = k->next) { - CandidateCheckPair *p = k->data; -@@ -375,7 +518,7 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -- priv_conn_check_initiate (agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ - } - } -@@ -385,17 +528,28 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - { - static int tick_counter = 0; - if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE) -- nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, " -+ nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, %u in-progress, " - "%u waiting, %u succeeded, %u discovered, %u nominated, " -- "%u waiting-for-nom.", agent, -+ "%u waiting-for-nom, %u valid.", agent, stream->id, - tick_counter, frozen, s_inprogress, waiting, s_succeeded, -- s_discovered, s_nominated, s_waiting_for_nomination); -+ s_discovered, s_nominated, s_waiting_for_nomination, s_valid); - } - - return keep_timer_going; - - } - -+static void -+conn_check_stop (NiceAgent *agent) -+{ -+ if (agent->conncheck_timer_source == NULL) -+ return; -+ -+ g_source_destroy (agent->conncheck_timer_source); -+ g_source_unref (agent->conncheck_timer_source); -+ agent->conncheck_timer_source = NULL; -+} -+ - - /* - * Timer callback that handles initiating and managing connectivity -@@ -409,15 +563,39 @@ 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); - -- /* step: find the highest priority waiting check and send it */ -+ 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_conn_check_initiate (agent, pair); -+ 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) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) -@@ -426,27 +604,37 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - - if (pair) { - priv_conn_check_initiate (agent, pair); -- keep_timer_going = TRUE; -- } else { -- keep_timer_going = priv_conn_check_unfreeze_next (agent); -+ return TRUE; - } - -- for (j = agent->streams; j; j = j->next) { -- Stream *stream = j->data; -- gboolean res = -- priv_conn_check_tick_stream (stream, agent, &now); -- if (res) -- keep_timer_going = res; -+ /* step: when there's no pair in the Waiting state, -+ * unfreeze a new pair and check it -+ */ -+ res = 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; - } - - /* step: stop timer if no work left */ - if (keep_timer_going != TRUE) { - nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - priv_update_check_list_failed_components (agent, stream); - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - priv_update_check_list_state_for_ready (agent, stream, component); - } - } -@@ -454,11 +642,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - /* Stopping the timer so destroy the source.. this will allow - the timer to be reset if we get a set_remote_candidates after this - point */ -- if (agent->conncheck_timer_source != NULL) { -- g_source_destroy (agent->conncheck_timer_source); -- g_source_unref (agent->conncheck_timer_source); -- agent->conncheck_timer_source = NULL; -- } -+ conn_check_stop (agent); - - /* XXX: what to signal, is all processing now really done? */ - nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); -@@ -512,7 +696,7 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer) - { - /* Time out */ - StunTransactionId id; -- Component *component; -+ NiceComponent *component; - - if (!agent_find_component (pair->keepalive.agent, - pair->keepalive.stream_id, pair->keepalive.component_id, -@@ -525,13 +709,12 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer) - - stun_message_id (&pair->keepalive.stun_message, id); - stun_agent_forget_transaction (&component->stun_agent, id); -+ pair->keepalive.stun_message.buffer = NULL; - - if (pair->keepalive.agent->media_after_tick) { - nice_debug ("Agent %p : Keepalive conncheck timed out!! " - "but media was received. Suspecting keepalive lost because of " - "network bottleneck", pair->keepalive.agent); -- -- pair->keepalive.stun_message.buffer = NULL; - } else { - nice_debug ("Agent %p : Keepalive conncheck timed out!! " - "peer probably lost connection", pair->keepalive.agent); -@@ -561,7 +744,7 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer) - priv_conn_keepalive_retransmissions_tick, pair); - break; - default: -- /* Nothing to do. */ -+ g_assert_not_reached(); - break; - } - -@@ -579,6 +762,7 @@ static guint32 peer_reflexive_candidate_priority (NiceAgent *agent, - - candidate_priority->transport = local_candidate->transport; - candidate_priority->component_id = local_candidate->component_id; -+ candidate_priority->base_addr = local_candidate->addr; - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - priority = nice_candidate_jingle_priority (candidate_priority); - } else if (agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -617,9 +801,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - * (ref ICE sect 10 "Keepalives" ID-19) */ - for (i = agent->streams; i; i = i->next) { - -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - if (component->selected_pair.local != NULL) { - CandidatePair *p = &component->selected_pair; - -@@ -629,7 +813,6 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || - agent->keepalive_conncheck) { -- guint32 priority; - uint8_t uname[NICE_STREAM_MAX_UNAME]; - size_t uname_len = - priv_create_username (agent, agent_find_stream (agent, stream->id), -@@ -639,25 +822,31 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - size_t password_len = priv_get_password (agent, - agent_find_stream (agent, stream->id), p->remote, &password); - -- priority = peer_reflexive_candidate_priority (agent, p->local); -+ if (p->keepalive.stun_message.buffer != NULL) { -+ nice_debug ("Agent %p: Keepalive for s%u:c%u still" -+ " retransmitting, not restarting", agent, stream->id, -+ component->id); -+ continue; -+ } - - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (&p->remote->addr, tmpbuf); - nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', " -- "socket=%u (c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), " -+ "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), " - "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%u.", agent, - tmpbuf, nice_address_get_port (&p->remote->addr), -- g_socket_get_fd(((NiceSocket *)p->local->sockptr)->fileno), - component->id, (int) uname_len, uname, uname_len, -- (int) password_len, password, password_len, priority); -+ (int) password_len, password, password_len, -+ p->prflx_priority); - } - if (uname_len > 0) { - buf_len = stun_usage_ice_conncheck_create (&component->stun_agent, - &p->keepalive.stun_message, p->keepalive.stun_buffer, - sizeof(p->keepalive.stun_buffer), - uname, uname_len, password, password_len, -- agent->controlling_mode, agent->controlling_mode, priority, -+ agent->controlling_mode, agent->controlling_mode, -+ p->prflx_priority, - agent->tie_breaker, - NULL, - agent_to_ice_compatibility (agent)); -@@ -709,9 +898,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - /* case 2: connectivity establishment ongoing - * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */ - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - if (component->state < NICE_COMPONENT_STATE_READY && - agent->stun_server_ip) { - NiceAddress stun_server; -@@ -723,12 +912,7 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - - nice_address_set_port (&stun_server, agent->stun_server_port); - -- /* FIXME: This will cause the stun response to arrive on the socket -- * but the stun agent will not be able to parse it due to an invalid -- * stun message since RFC3489 will not be compatible, and the response -- * will be forwarded to the application as user data */ -- stun_agent_init (&stun_agent, STUN_ALL_KNOWN_ATTRIBUTES, -- STUN_COMPATIBILITY_RFC3489, 0); -+ nice_agent_init_stun_agent (agent, &stun_agent); - - buffer_len = stun_usage_bind_create (&stun_agent, - &stun_message, stun_buffer, sizeof(stun_buffer)); -@@ -937,23 +1121,14 @@ static gboolean priv_turn_allocate_refresh_tick (gpointer pointer) - - /* - * Initiates the next pending connectivity check. -- * -- * @return TRUE if a pending check was scheduled - */ --gboolean conn_check_schedule_next (NiceAgent *agent) -+void conn_check_schedule_next (NiceAgent *agent) - { -- gboolean res = priv_conn_check_unfreeze_next (agent); -- nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res); -- - if (agent->discovery_unsched_items > 0) - nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent); - -- /* step: call once imediately */ -- res = priv_conn_check_tick_unlocked (agent); -- nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res); -- - /* step: schedule timer if not running yet */ -- if (res && agent->conncheck_timer_source == NULL) { -+ if (agent->conncheck_timer_source == NULL) { - agent_timeout_add_with_context (agent, &agent->conncheck_timer_source, - "Connectivity check schedule", agent->timer_ta, - priv_conn_check_tick, agent); -@@ -965,9 +1140,6 @@ gboolean conn_check_schedule_next (NiceAgent *agent) - "Connectivity keepalive timeout", NICE_AGENT_TIMER_TR_DEFAULT, - priv_conn_keepalive_tick, agent); - } -- -- nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res); -- return res; - } - - /* -@@ -995,7 +1167,7 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair * - * @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, Stream *stream, Component *component, CandidateCheckPair *pair) -+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) { -@@ -1004,7 +1176,7 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *s - 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->remote); -+ 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); - } - } -@@ -1039,15 +1211,13 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list) - * reaches us. The special case is documented in sect 7.2 - * if ICE spec (ID-19). - */ --void conn_check_remote_candidates_set(NiceAgent *agent) -+void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { -- GSList *i, *j, *k, *l, *m, *n; -+ GSList *j, *k, *l, *m, *n; - -- for (i = agent->streams; i ; i = i->next) { -- Stream *stream = i->data; -- for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *pair = j->data; -- Component *component = stream_find_component_by_id (stream, pair->component_id); -+ 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, -@@ -1148,24 +1318,24 @@ void conn_check_remote_candidates_set(NiceAgent *agent) - conn_check_add_for_candidate (agent, stream->id, component, candidate); - - if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, 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); - } - } - } - } -- -- /* 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; - } -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); - } -+ -+ /* 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; -+ -+ stream->conncheck_list = -+ prune_cancelled_conn_check (stream->conncheck_list); - } - - /* -@@ -1204,20 +1374,23 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper - * and has higher priority than the currently selected pair. See - * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19). - */ --static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair) -+static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *component, CandidateCheckPair *pair) - { -- CandidatePair cpair; -+ CandidatePair cpair = { 0, }; - - g_assert (component); - g_assert (pair); -- if (pair->priority > component->selected_pair.priority && -- component_find_pair (component, agent, pair->local->foundation, -- pair->remote->foundation, &cpair)) { -+ if (pair->priority > component->selected_pair.priority) { - nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s " - "(prio:%" G_GUINT64_FORMAT ").", agent, component->id, - pair->local->foundation, pair->remote->foundation, pair->priority); - -- component_update_selected_pair (component, &cpair); -+ cpair.local = pair->local; -+ cpair.remote = pair->remote; -+ cpair.priority = pair->priority; -+ /* cpair.keepalive is not used by nice_component_update_selected_pair() */ -+ -+ nice_component_update_selected_pair (component, &cpair); - - priv_conn_keepalive_tick_unlocked (agent); - -@@ -1239,7 +1412,7 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, Component *componen - * - * Sends a component state changesignal via 'agent'. - */ --static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream) -+static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) - { - GSList *i; - /* note: emitting a signal might cause the client -@@ -1261,7 +1434,7 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, Stream * - - /* note: iterate the conncheck list for each component separately */ - for (c = 0; c < components; c++) { -- Component *comp = NULL; -+ NiceComponent *comp = NULL; - if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) - continue; - -@@ -1299,10 +1472,10 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, Stream * - * - * Sends a component state changesignal via 'agent'. - */ --static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component) -+static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { - GSList *i; -- guint succeeded = 0, nominated = 0; -+ guint valid = 0, nominated = 0; - - g_assert (component); - -@@ -1310,26 +1483,36 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *st - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->component_id == component->id) { -- if (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) { -- ++succeeded; -+ if (p->valid) { -+ ++valid; - if (p->nominated == TRUE) { - ++nominated; -+ priv_update_selected_pair (agent, component, p); - } - } - } - } - -- if (nominated > 0) { -+ if (valid > 0) { - /* 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) { -+ /* Continue through the states to give client code a nice -+ * logical progression. See http://phabricator.freedesktop.org/D218 for -+ * discussion. */ -+ if (component->state < NICE_COMPONENT_STATE_CONNECTING || -+ 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_CONNECTED) -+ agent_signal_component_state_change (agent, stream->id, component->id, -+ NICE_COMPONENT_STATE_CONNECTED); - agent_signal_component_state_change (agent, stream->id, - component->id, NICE_COMPONENT_STATE_READY); - } - } -- nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id); -+ nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id); - } - - /* -@@ -1337,7 +1520,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *st - * described by 'component' and 'remotecand' is nominated - * for use. - */ --static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand) -+static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand) - { - GSList *i; - -@@ -1346,27 +1529,66 @@ static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Componen - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; -- /* XXX: hmm, how to figure out to which local candidate the -- * check was sent to? let's mark all matching pairs -- * as nominated instead */ -- if (pair->remote == remotecand) { -+ 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->state == NICE_CHECK_SUCCEEDED || -- pair->state == NICE_CHECK_DISCOVERED) -+ 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) { -+ /* 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); - } - } - } - -+guint32 -+ensure_unique_priority (NiceComponent *component, guint32 priority) -+{ -+ GSList *item; -+ -+ again: -+ if (priority == 0) -+ priority--; -+ -+ for (item = component->local_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (cand->priority == priority) { -+ priority--; -+ goto again; -+ } -+ } -+ -+ for (item = component->stream->conncheck_list; item; item = item->next) { -+ CandidateCheckPair *p = item->data; -+ -+ if (p->component_id == component->id && -+ p->prflx_priority == priority) { -+ priority--; -+ goto again; -+ } -+ } -+ -+ return priority; -+} -+ -+ - /* - * Creates a new connectivity check pair and adds it to - * the agent's list of checks. - */ --static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) -+static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, -+ guint stream_id, NiceComponent *component, NiceCandidate *local, -+ NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) - { -- Stream *stream; -+ NiceStream *stream; - CandidateCheckPair *pair; - - g_assert (local != NULL); -@@ -1389,8 +1611,19 @@ static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Componen - 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); -+ { -+ gchar tmpbuf1[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&pair->local->addr, tmpbuf1); -+ nice_address_to_string (&pair->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair, -+ tmpbuf1, nice_address_get_port (&pair->local->addr), -+ 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)); - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -1402,6 +1635,8 @@ static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Componen - if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { - priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); - } -+ -+ return pair; - } - - NiceCandidateTransport -@@ -1422,13 +1657,16 @@ conn_check_match_transport (NiceCandidateTransport transport) - } - } - --static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, -- guint stream_id, Component *component, NiceCandidate *local, -- NiceCandidate *remote, NiceCheckState initial_state) -+static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( -+ NiceAgent *agent, guint stream_id, NiceComponent *component, -+ NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state) - { -- nice_debug ("Agent %p, Adding check pair between %s and %s", agent, -- local->foundation, remote->foundation); -- priv_add_new_check_pair (agent, stream_id, component, local, remote, -+ 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, FALSE); - if (component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY) { -@@ -1442,10 +1680,12 @@ static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, - component->id, - NICE_COMPONENT_STATE_CONNECTING); - } -+ -+ return pair; - } - - gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, -- guint stream_id, Component *component, NiceCandidate *local, -+ guint stream_id, NiceComponent *component, NiceCandidate *local, - NiceCandidate *remote) - { - gboolean ret = FALSE; -@@ -1491,7 +1731,7 @@ gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, - * - * @return number of checks added, negative on fatal errors - */ --int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote) -+int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote) - { - GSList *i; - int added = 0; -@@ -1500,8 +1740,11 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component * - g_assert (remote != NULL); - - for (i = component->local_candidates; i ; i = i->next) { -- - NiceCandidate *local = i->data; -+ -+ if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote); - - if (ret) { -@@ -1522,7 +1765,7 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component * - * - * @return number of checks added, negative on fatal errors - */ --int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local) -+int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local) - { - GSList *i; - int added = 0; -@@ -1551,22 +1794,13 @@ static void conn_check_free_item (gpointer data) - { - CandidateCheckPair *pair = 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; - g_slice_free (CandidateCheckPair, pair); - } - --static void --conn_check_stop (NiceAgent *agent) --{ -- if (agent->conncheck_timer_source == NULL) -- return; -- -- g_source_destroy (agent->conncheck_timer_source); -- g_source_unref (agent->conncheck_timer_source); -- agent->conncheck_timer_source = NULL; --} -- - /* - * Frees all resources of all connectivity checks. - */ -@@ -1574,7 +1808,7 @@ void conn_check_free (NiceAgent *agent) - { - GSList *i; - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - if (stream->conncheck_list) { - nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, -@@ -1593,7 +1827,7 @@ void conn_check_free (NiceAgent *agent) - * - * @return TRUE on success, FALSE on a fatal error - */ --void conn_check_prune_stream (NiceAgent *agent, Stream *stream) -+void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream) - { - GSList *i; - gboolean keep_going = FALSE; -@@ -1606,7 +1840,7 @@ void conn_check_prune_stream (NiceAgent *agent, Stream *stream) - } - - for (i = agent->streams; i; i = i->next) { -- Stream *s = i->data; -+ NiceStream *s = i->data; - if (s->conncheck_list) { - keep_going = TRUE; - break; -@@ -1717,7 +1951,7 @@ size_t priv_gen_username (NiceAgent *agent, guint component_id, - * NULL) is ever written to the 'dest'. - */ - static --size_t priv_create_username (NiceAgent *agent, Stream *stream, -+size_t priv_create_username (NiceAgent *agent, NiceStream *stream, - guint component_id, NiceCandidate *remote, NiceCandidate *local, - uint8_t *dest, guint dest_len, gboolean inbound) - { -@@ -1760,7 +1994,7 @@ size_t priv_create_username (NiceAgent *agent, Stream *stream, - * check. - */ - static --size_t priv_get_password (NiceAgent *agent, Stream *stream, -+size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - NiceCandidate *remote, uint8_t **password) - { - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) -@@ -1781,26 +2015,30 @@ size_t priv_get_password (NiceAgent *agent, Stream *stream, - - /* Implement the computation specific in RFC 5245 section 16 */ - --static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, -- Stream *stream) -+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) - { -- GSList *item; -+ GSList *item1, *item2; - guint waiting_and_in_progress = 0; - unsigned int rto = 0; - -- for (item = stream->conncheck_list; item; item = item->next) { -- CandidateCheckPair *pair = item->data; - -- if (pair->state == NICE_CHECK_IN_PROGRESS || -- pair->state == NICE_CHECK_WAITING) -- waiting_and_in_progress++; -+ 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++; -+ } - } - -- /* FIXME: This should also be multiple by "N", which I believe is the -- * number of Streams currently in the conncheck state. */ - rto = agent->timer_ta * 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)", -+ agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), -+ waiting_and_in_progress); - if (agent->reliable) - return MAX (rto, 500); - else -@@ -1822,11 +2060,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts) - * - USE-CANDIDATE (if sent by the controlling agent) - */ -- guint32 priority; - - uint8_t uname[NICE_STREAM_MAX_UNAME]; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - gsize uname_len; - uint8_t *password = NULL; - gsize password_len; -@@ -1844,8 +2081,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - pair->remote, pair->local, uname, sizeof (uname), FALSE); - password_len = priv_get_password (agent, stream, pair->remote, &password); - -- priority = peer_reflexive_candidate_priority (agent, pair->local); -- - if (password != NULL && - (agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007)) { -@@ -1853,20 +2088,21 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - } - - if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- nice_address_to_string (&pair->remote->addr, tmpbuf); -- nice_debug ("Agent %p : STUN-CC REQ to '%s:%u', socket=%u, " -+ gchar tmpbuf1[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ 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 "), " -- "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%u.", agent, -- tmpbuf, -- nice_address_get_port (&pair->remote->addr), -+ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", 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, - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -- priority); -- -+ pair->prflx_priority, controlling); - } - - if (cand_use) -@@ -1876,7 +2112,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - 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, priority, -+ cand_use, controlling, pair->prflx_priority, - agent->tie_breaker, - pair->local->foundation, - agent_to_ice_compatibility (agent)); -@@ -1894,7 +2130,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); - } else { - stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent, stream), -+ priv_compute_conncheck_timer (agent), - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); - } - -@@ -1902,9 +2138,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * 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) { -- Stream *stream2 = NULL; -- Component *component2 = NULL; -+ NiceStream *stream2 = NULL; -+ NiceComponent *component2 = NULL; - NiceSocket *new_socket; - - if (agent_find_component (agent, pair->stream_id, pair->component_id, -@@ -1914,7 +2151,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - if (new_socket) { - pair->sockptr = new_socket; - _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -- component_attach_socket (component2, new_socket); -+ -+ if (agent->reliable) { -+ nice_socket_set_writable_callback (pair->sockptr, -+ _tcp_sock_is_writable, component2); -+ } -+ -+ nice_component_attach_socket (component2, new_socket); - } - } - } -@@ -1948,7 +2191,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - * @see priv_update_check_list_state_failed_components() - */ --static guint priv_prune_pending_checks (Stream *stream, guint component_id) -+static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - { - GSList *i; - guint64 highest_nominated_priority = 0; -@@ -1959,10 +2202,8 @@ static guint priv_prune_pending_checks (Stream *stream, guint component_id) - - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -- if (p->component_id == component_id && -- (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) && -- p->nominated == TRUE){ -+ if (p->component_id == component_id && p->valid == TRUE && -+ p->nominated == TRUE) { - if (p->priority > highest_nominated_priority) { - highest_nominated_priority = p->priority; - } -@@ -2015,7 +2256,7 @@ static guint priv_prune_pending_checks (Stream *stream, guint component_id) - * @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, Stream *stream, Component *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, gboolean use_candidate) - { - GSList *i; - NiceCandidate *local = NULL; -@@ -2038,7 +2279,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - - if (p->state == NICE_CHECK_WAITING || - p->state == NICE_CHECK_FROZEN) -- priv_conn_check_initiate (agent, p); -+ 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 -@@ -2049,7 +2290,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - 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), -+ priv_compute_conncheck_timer (agent), - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); - p->timer_restarted = TRUE; - } -@@ -2059,23 +2300,36 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - 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 */ -+ /* 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); - -- /* note: to take care of the controlling-controlling case in -- * aggressive nomination mode, send a new triggered -- * check to nominate the pair */ -+ /* 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_conn_check_initiate (agent, p); -+ 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 - 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_conn_check_initiate (agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -2121,7 +2375,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - * - * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE) - */ --static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len, uint8_t *rbuf, gboolean use_candidate) -+static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len, uint8_t *rbuf, gboolean use_candidate) - { - g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE); - -@@ -2144,7 +2398,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Componen - priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); - - if (use_candidate) -- priv_mark_pair_nominated (agent, stream, component, rcand); -+ priv_mark_pair_nominated (agent, stream, component, lcand, rcand); - } - } - -@@ -2156,7 +2410,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Componen - * - * @return non-zero on error, zero on success - */ --static int priv_store_pending_check (NiceAgent *agent, Component *component, -+static int priv_store_pending_check (NiceAgent *agent, NiceComponent *component, - const NiceAddress *from, NiceSocket *sockptr, uint8_t *username, - uint16_t username_len, uint32_t priority, gboolean use_candidate) - { -@@ -2190,19 +2444,28 @@ static int priv_store_pending_check (NiceAgent *agent, Component *component, - * - * @return created pair, or NULL on fatal (memory allocation) errors - */ --static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair) -+static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local_cand, CandidateCheckPair *parent_pair) - { - CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair); -- Stream *stream = agent_find_stream (agent, stream_id); -+ NiceStream *stream = agent_find_stream (agent, stream_id); - - pair->agent = agent; - pair->stream_id = stream_id; -- pair->component_id = component_id;; -+ pair->component_id = component->id;; - pair->local = local_cand; - pair->remote = parent_pair->remote; - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; -- nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair); -+ nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); -+ { -+ gchar tmpbuf1[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&pair->local->addr, tmpbuf1); -+ nice_address_to_string (&pair->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair, -+ tmpbuf1, nice_address_get_port (&pair->local->addr), -+ tmpbuf2, nice_address_get_port (&pair->remote->addr)); -+ } - g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", - local_cand->foundation, parent_pair->remote->foundation); - if (agent->controlling_mode == TRUE) -@@ -2213,6 +2476,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - 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); - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, -@@ -2230,11 +2495,13 @@ static void priv_recalculate_pair_priorities (NiceAgent *agent) - GSList *i, *j; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->conncheck_list; j; j = j->next) { - CandidateCheckPair *p = j->data; - p->priority = agent_candidate_pair_priority (agent, p->local, p->remote); - } -+ stream->conncheck_list = g_slist_sort (stream->conncheck_list, -+ (GCompareFunc)conn_check_compare); - } - } - -@@ -2269,21 +2536,21 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) - * @param socketptr socket used to send the reply - * @param mapped_sockaddr mapped address in the response - * -- * @return pointer to a new pair if one was created, otherwise NULL -+ * @return pointer to a candidate pair, found in conncheck list or newly created - */ --static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate) -+static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate) - { - CandidateCheckPair *new_pair = NULL; - NiceAddress mapped; - GSList *i, *j; -- gboolean local_cand_matches = FALSE; -+ NiceCandidate *local_cand = NULL; - - nice_address_set_from_sockaddr (&mapped, mapped_sockaddr); - - for (j = component->local_candidates; j; j = j->next) { - NiceCandidate *cand = j->data; - if (nice_address_equal (&mapped, &cand->addr)) { -- local_cand_matches = TRUE; -+ local_cand = cand; - - /* We always need to select the peer-reflexive Candidate Pair in the case - * of a TCP-ACTIVE local candidate, so we find it even if an incoming -@@ -2300,31 +2567,38 @@ static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAg - } - } - -- if (local_cand_matches == TRUE) { -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -+ if (new_pair) { - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); - } - else { -- NiceCandidate *cand = -- discovery_add_peer_reflexive_candidate (agent, -- stream->id, -- component->id, -- &mapped, -- sockptr, -- local_candidate, -- remote_candidate); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ if (!local_cand) { -+ if (!agent->force_relay) -+ local_cand = discovery_add_peer_reflexive_candidate (agent, -+ stream->id, -+ component->id, -+ &mapped, -+ sockptr, -+ local_candidate, -+ remote_candidate); -+ p->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ } - - /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 - "Constructing a Valid Pair") */ -- new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p); -+ 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); - } - -+ /* note: this is same as "adding to VALID LIST" in the spec -+ text */ -+ if (new_pair) -+ new_pair->valid = TRUE; -+ - return new_pair; - } - -@@ -2335,7 +2609,7 @@ static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAg - * - * @return TRUE if a matching transaction is found - */ --static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp) -+static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp) - { - union { - struct sockaddr_storage storage; -@@ -2404,11 +2678,13 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream * - /* 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); - priv_conn_check_unfreeze_related (agent, stream, p); - } else { -- ok_pair = priv_process_response_check_for_peer_reflexive (agent, -+ ok_pair = priv_process_response_check_for_reflexive (agent, - stream, component, p, sockptr, &sockaddr.addr, - local_candidate, remote_candidate); - } -@@ -2431,6 +2707,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream * - } - } - -+ priv_print_conn_check_lists (agent, G_STRFUNC, 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); -@@ -2447,6 +2725,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream * - 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 { -@@ -2516,25 +2795,27 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa - d->pending = FALSE; - } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { - /* case: successful binding discovery, create a new local candidate */ -- NiceAddress niceaddr; -- nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr); -- -- discovery_add_server_reflexive_candidate ( -- d->agent, -- d->stream->id, -- d->component->id, -- &niceaddr, -- NICE_CANDIDATE_TRANSPORT_UDP, -- d->nicesock, -- FALSE); -- if (d->agent->use_ice_tcp) -- discovery_discover_tcp_server_reflexive_candidates ( -+ -+ if (!agent->force_relay) { -+ NiceAddress niceaddr; -+ -+ nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr); -+ discovery_add_server_reflexive_candidate ( - d->agent, - d->stream->id, - d->component->id, - &niceaddr, -- d->nicesock); -- -+ 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); -+ } - d->stun_message.buffer = NULL; - d->stun_message.buffer_len = 0; - d->done = TRUE; -@@ -2667,7 +2948,8 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - * on a TCP connection, which cannot be used for server-reflexive - * discovery of candidates. - */ -- if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP) { -+ if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP && -+ !agent->force_relay) { - discovery_add_server_reflexive_candidate ( - d->agent, - d->stream->id, -@@ -2729,6 +3011,9 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - } - - if (relay_cand) { -+ if (d->stun_resp_msg.buffer) -+ nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr, -+ &d->stun_resp_msg); - if (agent->compatibility == NICE_COMPATIBILITY_OC2007 || - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { - /* These data are needed on TURN socket when sending requests, -@@ -2744,6 +3029,9 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - } else { - priv_add_new_turn_refresh (d, relay_cand, lifetime); - } -+ -+ /* In case a new candidate has been added */ -+ conn_check_schedule_next (agent); - } - - d->stun_message.buffer = NULL; -@@ -2889,7 +3177,7 @@ static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage * - - - static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent, -- Component *component, StunMessage *resp) -+ NiceComponent *component, StunMessage *resp) - { - StunTransactionId conncheck_id; - StunTransactionId response_id; -@@ -2917,8 +3205,8 @@ static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent, - - typedef struct { - NiceAgent *agent; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - uint8_t *password; - } conncheck_validater_data; - -@@ -2957,7 +3245,7 @@ static bool conncheck_stun_validater (StunAgent *agent, - if (ufrag == NULL) - continue; - -- stun_debug ("Comparing username/ufrag of len %d and %zu, equal=%d", -+ stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d", - username_len, ufrag_len, username_len >= ufrag_len ? - memcmp (username, ufrag, ufrag_len) : 0); - stun_debug_bytes (" username: ", username, username_len); -@@ -3014,8 +3302,8 @@ static bool conncheck_stun_validater (StunAgent *agent, - * - * @return XXX (what FALSE means exactly?) - */ --gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, -- Component *component, NiceSocket *nicesock, const NiceAddress *from, -+gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from, - gchar *buf, guint len) - { - union { -@@ -3080,9 +3368,11 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, - valid == STUN_VALIDATION_UNMATCHED_RESPONSE) { - for (i = agent->refresh_list; i; i = i->next) { - CandidateRefresh *r = i->data; -- nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream, -- stream, r->component, component, r->nicesock, r->candidate->sockptr, -- nicesock); -+ -+ nice_debug_verbose ("Comparing %p to %p, %p to %p and %p and %p to %p", -+ r->stream, stream, r->component, component, r->nicesock, -+ r->candidate->sockptr, nicesock); -+ - if (r->stream == stream && r->component == component && - (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) { - valid = stun_agent_validate (&r->stun_agent, &req, -@@ -3294,16 +3584,22 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, - remote_candidate2 ? remote_candidate2 : remote_candidate); - if(remote_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, remote_candidate, NICE_CHECK_DISCOVERED); -- else -+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { -+ CandidateCheckPair *pair; -+ -+ pair = priv_conn_check_add_for_candidate_pair_matched (agent, -+ stream->id, component, local_candidate, remote_candidate, -+ NICE_CHECK_DISCOVERED); -+ if (pair) { -+ pair->valid = TRUE; -+ } -+ } else - conn_check_add_for_candidate (agent, stream->id, component, remote_candidate); - } - } - -- priv_reply_to_conn_check (agent, stream, component, remote_candidate, -- from, nicesock, rbuf_len, rbuf, use_candidate); -+ priv_reply_to_conn_check (agent, stream, component, local_candidate, -+ remote_candidate, from, nicesock, rbuf_len, rbuf, use_candidate); - - if (component->remote_candidates == NULL) { - /* case: We've got a valid binding request to a local candidate -@@ -3360,7 +3656,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, - /* Remove all pointers to the given @sock from the connection checking process. - * These are entirely NiceCandidates pointed to from various places. */ - void --conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, -+conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, - NiceSocket *sock) - { - GSList *l; -@@ -3375,14 +3671,20 @@ conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, - } - - /* Prune from the candidate check pairs. */ -- for (l = stream->conncheck_list; l != NULL; l = l->next) { -+ for (l = stream->conncheck_list; l != NULL;) { - CandidateCheckPair *p = l->data; -+ GSList *next = l->next; - - if ((p->local != NULL && p->local->sockptr == sock) || -- (p->remote != NULL && p->remote->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); - candidate_check_pair_fail (stream, agent, p); -+ conn_check_free_item (p); -+ stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l); - } -+ -+ l = next; - } - } -diff --git a/agent/conncheck.h b/agent/conncheck.h -index e6c2c62..431c606 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -87,26 +87,30 @@ struct _CandidateCheckPair - gboolean nominated; - gboolean controlling; - gboolean timer_restarted; -+ gboolean valid; - 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; - }; - --int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote); --int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local); --gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote); -+int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); -+int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local); -+gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local, NiceCandidate *remote); - void conn_check_free (NiceAgent *agent); --gboolean conn_check_schedule_next (NiceAgent *agent); -+void conn_check_schedule_next (NiceAgent *agent); - int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); --void conn_check_prune_stream (NiceAgent *agent, Stream *stream); --gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); -+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); -+void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); - NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); - void --conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, -+conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, - NiceSocket *sock); - -+guint32 ensure_unique_priority (NiceComponent *component, guint32 priority); -+ - #endif /*_NICE_CONNCHECK_H */ -diff --git a/agent/debug.c b/agent/debug.c -index 6e69f9b..e1a298c 100644 ---- a/agent/debug.c -+++ b/agent/debug.c -@@ -48,17 +48,29 @@ - #include "agent-priv.h" - - static int debug_enabled = 0; -+static int debug_verbose_enabled = 0; - - #define NICE_DEBUG_STUN 1 - #define NICE_DEBUG_NICE 2 - #define NICE_DEBUG_PSEUDOTCP 4 - #define NICE_DEBUG_PSEUDOTCP_VERBOSE 8 -+#define NICE_DEBUG_NICE_VERBOSE 16 - - static const GDebugKey keys[] = { - { (gchar *)"stun", NICE_DEBUG_STUN }, - { (gchar *)"nice", NICE_DEBUG_NICE }, - { (gchar *)"pseudotcp", NICE_DEBUG_PSEUDOTCP }, - { (gchar *)"pseudotcp-verbose", NICE_DEBUG_PSEUDOTCP_VERBOSE }, -+ { (gchar *)"nice-verbose", NICE_DEBUG_NICE_VERBOSE }, -+ { NULL, 0}, -+}; -+ -+static const GDebugKey gkeys[] = { -+ { (gchar *)"libnice-stun", NICE_DEBUG_STUN }, -+ { (gchar *)"libnice", NICE_DEBUG_NICE }, -+ { (gchar *)"libnice-pseudotcp", NICE_DEBUG_PSEUDOTCP }, -+ { (gchar *)"libnice-pseudotcp-verbose", NICE_DEBUG_PSEUDOTCP_VERBOSE }, -+ { (gchar *)"libnice-verbose", NICE_DEBUG_NICE_VERBOSE }, - { NULL, 0}, - }; - -@@ -86,18 +98,30 @@ void nice_debug_init (void) - - if (flags_string) - flags = g_parse_debug_string (flags_string, keys, 4); -+ if (gflags_string) -+ 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")) { -+ flags |= NICE_DEBUG_NICE_VERBOSE; -+ } - - stun_set_debug_handler (stun_handler); -- nice_debug_enable (TRUE); -+ debug_enabled = !!(flags & NICE_DEBUG_NICE); -+ if (flags & NICE_DEBUG_STUN) -+ stun_debug_enable (); -+ else -+ stun_debug_disable (); -+ -+ if (flags & NICE_DEBUG_NICE_VERBOSE) -+ debug_verbose_enabled = TRUE; - - /* Set verbose before normal so that if we use 'all', then only - normal debug is enabled, we'd need to set pseudotcp-verbose without the - pseudotcp flag in order to actually enable verbose pseudotcp */ - if (flags & NICE_DEBUG_PSEUDOTCP_VERBOSE) - pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE); -- else -+ else if (flags & NICE_DEBUG_PSEUDOTCP) - pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_NORMAL); - } - } -@@ -107,6 +131,10 @@ gboolean nice_debug_is_enabled (void) - { - return debug_enabled; - } -+gboolean nice_debug_is_verbose (void) -+{ -+ return debug_verbose_enabled; -+} - #else - /* Defined in agent-priv.h. */ - #endif -@@ -136,6 +164,15 @@ void nice_debug (const char *fmt, ...) - va_end (ap); - } - } -+void nice_debug_verbose (const char *fmt, ...) -+{ -+ va_list ap; -+ if (debug_verbose_enabled) { -+ va_start (ap, fmt); -+ g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fmt, ap); -+ va_end (ap); -+ } -+} - #else - /* Defined in agent-priv.h. */ - #endif -diff --git a/agent/discovery.c b/agent/discovery.c -index f3a702d..7a890a0 100644 ---- a/agent/discovery.c -+++ b/agent/discovery.c -@@ -305,7 +305,7 @@ void refresh_cancel (CandidateRefresh *refresh) - * defined in ICE spec section 4.1.3 "Eliminating Redundant - * Candidates" (ID-19). - */ --static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *candidate) -+static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *candidate) - { - GSList *i; - -@@ -329,7 +329,7 @@ static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_ - return TRUE; - } - --static guint priv_highest_remote_foundation (Component *component) -+static guint priv_highest_remote_foundation (NiceComponent *component) - { - GSList *i; - guint highest = 1; -@@ -382,9 +382,9 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate) - GSList *i, *j, *k; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *n = k->data; - -@@ -393,7 +393,6 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate) - - if (candidate->type == n->type && - candidate->transport == n->transport && -- candidate->stream_id == n->stream_id && - nice_address_equal_no_port (&candidate->base_addr, &n->base_addr) && - (candidate->type != NICE_CANDIDATE_TYPE_RELAYED || - priv_compare_turn_servers (candidate->turn, n->turn)) && -@@ -427,12 +426,12 @@ static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *cand - { - GSList *i, *j, *k; - guint next_remote_id; -- Component *component = NULL; -+ NiceComponent *component = NULL; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *c = j->data; -+ NiceComponent *c = j->data; - - if (c->id == candidate->component_id) - component = c; -@@ -523,8 +522,8 @@ HostCandidateResult discovery_add_local_host_candidate ( - NiceCandidate **outcandidate) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceSocket *nicesock = NULL; - HostCandidateResult res = HOST_CANDIDATE_FAILED; - -@@ -550,6 +549,8 @@ HostCandidateResult discovery_add_local_host_candidate ( - agent->reliable, FALSE); - } - -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_generate_candidate_credentials (agent, candidate); - priv_assign_foundation (agent, candidate); - -@@ -580,7 +581,7 @@ HostCandidateResult discovery_add_local_host_candidate ( - } - - _priv_set_socket_tos (agent, nicesock, stream->tos); -- component_attach_socket (component, nicesock); -+ nice_component_attach_socket (component, nicesock); - - *outcandidate = candidate; - -@@ -610,8 +611,8 @@ discovery_add_server_reflexive_candidate ( - gboolean nat_assisted) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - gboolean result = FALSE; - - if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) -@@ -623,6 +624,10 @@ discovery_add_server_reflexive_candidate ( - candidate->component_id = component_id; - candidate->addr = *address; - -+ /* step: link to the base candidate+socket */ -+ candidate->sockptr = base_socket; -+ candidate->base_addr = base_socket->addr; -+ - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - candidate->priority = nice_candidate_jingle_priority (candidate); - } else if (agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -636,10 +641,8 @@ discovery_add_server_reflexive_candidate ( - agent->reliable, nat_assisted); - } - -- /* step: link to the base candidate+socket */ -- candidate->sockptr = base_socket; -- candidate->base_addr = base_socket->addr; -- -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_generate_candidate_credentials (agent, candidate); - priv_assign_foundation (agent, candidate); - -@@ -670,8 +673,8 @@ discovery_discover_tcp_server_reflexive_candidates ( - NiceAddress *address, - NiceSocket *base_socket) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceAddress base_addr = base_socket->addr; - GSList *i; - -@@ -718,8 +721,8 @@ discovery_add_relay_candidate ( - TurnServer *turn) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceSocket *relay_socket = NULL; - - if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) -@@ -732,6 +735,17 @@ discovery_add_relay_candidate ( - candidate->addr = *address; - candidate->turn = turn_server_ref (turn); - -+ /* step: link to the base candidate+socket */ -+ relay_socket = nice_udp_turn_socket_new (agent->main_context, address, -+ base_socket, &turn->server, -+ turn->username, turn->password, -+ agent_to_turn_socket_compatibility (agent)); -+ if (!relay_socket) -+ goto errors; -+ -+ candidate->sockptr = relay_socket; -+ candidate->base_addr = base_socket->addr; -+ - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - candidate->priority = nice_candidate_jingle_priority (candidate); - } else if (agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -745,17 +759,8 @@ discovery_add_relay_candidate ( - agent->reliable, FALSE); - } - -- /* step: link to the base candidate+socket */ -- relay_socket = nice_udp_turn_socket_new (agent->main_context, address, -- base_socket, &turn->server, -- turn->username, turn->password, -- agent_to_turn_socket_compatibility (agent)); -- if (!relay_socket) -- goto errors; -- -- candidate->sockptr = relay_socket; -- candidate->base_addr = base_socket->addr; -- -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_generate_candidate_credentials (agent, candidate); - - /* Google uses the turn username as the candidate username */ -@@ -769,7 +774,7 @@ discovery_add_relay_candidate ( - if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate)) - goto errors; - -- component_attach_socket (component, relay_socket); -+ nice_component_attach_socket (component, relay_socket); - agent_signal_new_candidate (agent, candidate); - - return candidate; -@@ -798,8 +803,8 @@ discovery_add_peer_reflexive_candidate ( - NiceCandidate *remote) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - gboolean result; - - if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) -@@ -836,6 +841,8 @@ discovery_add_peer_reflexive_candidate ( - agent->reliable, FALSE); - } - -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_assign_foundation (agent, candidate); - - if ((agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -891,8 +898,8 @@ discovery_add_peer_reflexive_candidate ( - */ - NiceCandidate *discovery_learn_remote_peer_reflexive_candidate ( - NiceAgent *agent, -- Stream *stream, -- Component *component, -+ NiceStream *stream, -+ NiceComponent *component, - guint32 priority, - const NiceAddress *remote_address, - NiceSocket *nicesock, -@@ -1023,10 +1030,12 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) - (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || - cand->type == NICE_CANDIDATE_TYPE_RELAYED)) { - -- agent_signal_component_state_change (agent, -- cand->stream->id, -- cand->component->id, -- NICE_COMPONENT_STATE_GATHERING); -+ if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED || -+ cand->component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ cand->stream->id, -+ cand->component->id, -+ NICE_COMPONENT_STATE_GATHERING); - - if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) { - buffer_len = stun_usage_bind_create (&cand->stun_agent, -diff --git a/agent/discovery.h b/agent/discovery.h -index c22ea6a..67e2186 100644 ---- a/agent/discovery.h -+++ b/agent/discovery.h -@@ -53,8 +53,8 @@ typedef struct - GTimeVal next_tick; /* next tick timestamp */ - gboolean pending; /* is discovery in progress? */ - gboolean done; /* is discovery complete? */ -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - TurnServer *turn; - StunAgent stun_agent; - StunTimer timer; -@@ -70,8 +70,8 @@ typedef struct - NiceSocket *nicesock; /* existing socket to use */ - NiceAddress server; /* STUN/TURN server address */ - NiceCandidate *candidate; /* candidate to refresh */ -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - StunAgent stun_agent; - GSource *timer_source; - GSource *tick_source; -@@ -151,8 +151,8 @@ discovery_add_peer_reflexive_candidate ( - NiceCandidate * - discovery_learn_remote_peer_reflexive_candidate ( - NiceAgent *agent, -- Stream *stream, -- Component *component, -+ NiceStream *stream, -+ NiceComponent *component, - guint32 priority, - const NiceAddress *remote_address, - NiceSocket *udp_socket, -diff --git a/agent/inputstream.c b/agent/inputstream.c -index b9c5369..58a4a0d 100644 ---- a/agent/inputstream.c -+++ b/agent/inputstream.c -@@ -332,8 +332,8 @@ nice_input_stream_close (GInputStream *stream, GCancellable *cancellable, - GError **error) - { - NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - NiceAgent *agent; /* owned */ - - /* Has the agent disappeared? */ -@@ -361,8 +361,8 @@ static gboolean - nice_input_stream_is_readable (GPollableInputStream *stream) - { - NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - gboolean retval = FALSE; - GSList *i; - NiceAgent *agent; /* owned */ -@@ -458,7 +458,7 @@ nice_input_stream_create_source (GPollableInputStream *stream, - if (agent == NULL) - goto dummy_source; - -- component_source = component_input_source_new (agent, priv->stream_id, -+ component_source = nice_component_input_source_new (agent, priv->stream_id, - priv->component_id, stream, cancellable); - - g_object_unref (agent); -diff --git a/agent/outputstream.c b/agent/outputstream.c -index d479aa5..4c918a7 100644 ---- a/agent/outputstream.c -+++ b/agent/outputstream.c -@@ -476,8 +476,8 @@ nice_output_stream_close (GOutputStream *stream, GCancellable *cancellable, - GError **error) - { - NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - NiceAgent *agent; /* owned */ - - /* Has the agent disappeared? */ -@@ -505,8 +505,8 @@ static gboolean - nice_output_stream_is_writable (GPollableOutputStream *stream) - { - NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - gboolean retval = FALSE; - NiceAgent *agent; /* owned */ - -@@ -595,8 +595,8 @@ nice_output_stream_create_source (GPollableOutputStream *stream, - { - NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; - GSource *component_source = NULL; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - NiceAgent *agent; /* owned */ - - component_source = g_pollable_source_new (G_OBJECT (stream)); -diff --git a/agent/pseudotcp.c b/agent/pseudotcp.c -index eb91e3c..3160c34 100644 ---- a/agent/pseudotcp.c -+++ b/agent/pseudotcp.c -@@ -77,8 +77,19 @@ - #include "pseudotcp.h" - #include "agent-priv.h" - --G_DEFINE_TYPE (PseudoTcpSocket, pseudo_tcp_socket, G_TYPE_OBJECT); -+struct _PseudoTcpSocketClass { -+ GObjectClass parent_class; -+}; -+ -+typedef struct _PseudoTcpSocketPrivate PseudoTcpSocketPrivate; -+ -+ -+struct _PseudoTcpSocket { -+ GObject parent; -+ PseudoTcpSocketPrivate *priv; -+}; - -+G_DEFINE_TYPE (PseudoTcpSocket, pseudo_tcp_socket, G_TYPE_OBJECT); - - ////////////////////////////////////////////////////////////////////// - // Network Constants -@@ -107,7 +118,9 @@ const guint16 PACKET_MAXIMUMS[] = { - 0, // End of list marker - }; - --#define MAX_PACKET 65535 -+// FIXME: This is a reasonable MTU, but we should get it from the lower layer -+#define DEF_MTU 1400 -+#define MAX_PACKET 65532 - // Note: we removed lowest level because packet overhead was larger! - #define MIN_PACKET 296 - -@@ -151,8 +164,8 @@ const guint16 PACKET_MAXIMUMS[] = { - #define PACKET_OVERHEAD (HEADER_SIZE + UDP_HEADER_SIZE + \ - IP_HEADER_SIZE + JINGLE_HEADER_SIZE) - --// MIN_RTO = 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second") --#define MIN_RTO 250 -+// MIN_RTO = 1 second (RFC6298, Sec 2.4) -+#define MIN_RTO 1000 - #define DEF_RTO 1000 /* 1 seconds (RFC 6298 sect 2.1) */ - #define MAX_RTO 60000 /* 60 seconds */ - #define DEFAULT_ACK_DELAY 100 /* 100 milliseconds */ -@@ -416,6 +429,7 @@ typedef enum { - sfImmediateAck, - sfFin, - sfRst, -+ sfDuplicateAck, - } SendFlags; - - typedef struct { -@@ -471,6 +485,7 @@ struct _PseudoTcpSocketPrivate { - guint32 rbuf_len, rcv_nxt, rcv_wnd, lastrecv; - guint8 rwnd_scale; // Window scale factor - PseudoTcpFifo rbuf; -+ guint32 rcv_fin; /* sequence number of the received FIN octet, or 0 */ - - // Outgoing data - GQueue slist; -@@ -495,7 +510,9 @@ struct _PseudoTcpSocketPrivate { - guint32 ssthresh, cwnd; - guint8 dup_acks; - guint32 recover; -+ gboolean fast_recovery; - guint32 t_ack; /* time a delayed ack was scheduled; 0 if no acks scheduled */ -+ guint32 last_acked_ts; - - gboolean use_nagling; - guint32 ack_delay; -@@ -550,7 +567,7 @@ static gboolean parse (PseudoTcpSocket *self, - const guint8 *_header_buf, gsize header_buf_len, - const guint8 *data_buf, gsize data_buf_len); - static gboolean process(PseudoTcpSocket *self, Segment *seg); --static gboolean transmit(PseudoTcpSocket *self, SSegment *sseg, guint32 now); -+static int transmit(PseudoTcpSocket *self, SSegment *sseg, guint32 now); - static void attempt_send(PseudoTcpSocket *self, SendFlags sflags); - static void closedown (PseudoTcpSocket *self, guint32 err, - ClosedownSource source); -@@ -566,6 +583,7 @@ static void set_state_closed (PseudoTcpSocket *self, guint32 err); - static const gchar *pseudo_tcp_state_get_name (PseudoTcpState state); - static gboolean pseudo_tcp_state_has_sent_fin (PseudoTcpState state); - static gboolean pseudo_tcp_state_has_received_fin (PseudoTcpState state); -+static gboolean pseudo_tcp_state_has_received_fin_ack (PseudoTcpState state); - - // The following logging is for detailed (packet-level) pseudotcp analysis only. - static PseudoTcpDebugLevel debug_level = PSEUDO_TCP_DEBUG_NONE; -@@ -809,12 +827,14 @@ pseudo_tcp_socket_init (PseudoTcpSocket *obj) - priv->snd_una = priv->rcv_nxt = 0; - priv->bReadEnable = TRUE; - priv->bWriteEnable = FALSE; -+ priv->rcv_fin = 0; -+ - priv->t_ack = 0; - - priv->msslevel = 0; - priv->largest = 0; - priv->mss = MIN_PACKET - PACKET_OVERHEAD; -- priv->mtu_advise = MAX_PACKET; -+ priv->mtu_advise = DEF_MTU; - - priv->rto_base = 0; - -@@ -825,6 +845,7 @@ pseudo_tcp_socket_init (PseudoTcpSocket *obj) - - priv->dup_acks = 0; - priv->recover = 0; -+ priv->last_acked_ts = 0; - - priv->ts_recent = priv->ts_lastack = 0; - -@@ -959,18 +980,24 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self) - // retransmit segments - guint32 nInFlight; - guint32 rto_limit; -+ int transmit_status; - - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "timeout retransmit (rto: %u) " - "(rto_base: %u) (now: %u) (dup_acks: %u)", - priv->rx_rto, priv->rto_base, now, (guint) priv->dup_acks); - -- if (!transmit(self, g_queue_peek_head (&priv->slist), now)) { -- closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); -+ transmit_status = transmit(self, g_queue_peek_head (&priv->slist), now); -+ if (transmit_status != 0) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Error transmitting segment. Closing down."); -+ closedown (self, transmit_status, CLOSEDOWN_LOCAL); - return; - } - - nInFlight = priv->snd_nxt - priv->snd_una; - priv->ssthresh = max(nInFlight / 2, 2 * priv->mss); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "ssthresh: %u = (nInFlight: %u / 2) + " -+ "2 * mss: %u", priv->ssthresh, nInFlight, priv->mss); - //LOG(LS_INFO) << "priv->ssthresh: " << priv->ssthresh << " nInFlight: " << nInFlight << " priv->mss: " << priv->mss; - priv->cwnd = priv->mss; - -@@ -978,6 +1005,13 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self) - rto_limit = (priv->state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO; - priv->rx_rto = min(rto_limit, priv->rx_rto * 2); - priv->rto_base = now; -+ -+ priv->recover = priv->snd_nxt; -+ if (priv->dup_acks >= 3) { -+ priv->dup_acks = 0; -+ priv->fast_recovery = FALSE; -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery on timeout"); -+ } - } - } - -@@ -985,6 +1019,7 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self) - if ((priv->snd_wnd == 0) - && (time_diff(priv->lastsend + priv->rx_rto, now) <= 0)) { - if (time_diff(now, priv->lastrecv) >= 15000) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Receive window closed. Closing down."); - closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); - return; - } -@@ -1012,9 +1047,11 @@ pseudo_tcp_socket_notify_packet(PseudoTcpSocket *self, - - if (len > MAX_PACKET) { - //LOG_F(WARNING) << "packet too large"; -+ self->priv->error = EMSGSIZE; - return FALSE; - } else if (len < HEADER_SIZE) { - //LOG_F(WARNING) << "packet too small"; -+ self->priv->error = EINVAL; - return FALSE; - } - -@@ -1149,9 +1186,7 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len) - gsize available_space; - - /* Received a FIN from the peer, so return 0. RFC 793, §3.5, Case 2. */ -- if (priv->support_fin_ack && -- (priv->shutdown_reads || -- pseudo_tcp_state_has_received_fin (priv->state))) { -+ if (priv->support_fin_ack && priv->shutdown_reads) { - return 0; - } - -@@ -1173,7 +1208,9 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len) - bytesread = pseudo_tcp_fifo_read (&priv->rbuf, (guint8 *) buffer, len); - - // If there's no data in |m_rbuf|. -- if (bytesread == 0) { -+ if (bytesread == 0 && -+ !(pseudo_tcp_state_has_received_fin (priv->state) || -+ pseudo_tcp_state_has_received_fin_ack (priv->state))) { - priv->bReadEnable = TRUE; - priv->error = EWOULDBLOCK; - return -1; -@@ -1407,7 +1444,7 @@ packet(PseudoTcpSocket *self, guint32 seq, TcpFlags flags, - g_assert (bytes_read == len); - } - -- DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "<-- <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Sending <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" - "<WND=%u><TS=%u><TSR=%u><LEN=%u>", - priv->conv, (unsigned)flags, seq, seq + len, priv->rcv_nxt, priv->rcv_wnd, - now % 10000, priv->ts_recent % 10000, len); -@@ -1460,7 +1497,8 @@ parse (PseudoTcpSocket *self, const guint8 *_header_buf, gsize header_buf_len, - seg.data = (const gchar *) data_buf; - seg.len = data_buf_len; - -- DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "--> <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, -+ "Received <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" - "<WND=%u><TS=%u><TSR=%u><LEN=%u>", - seg.conv, (unsigned)seg.flags, seg.seq, seg.seq + seg.len, seg.ack, - seg.wnd, seg.tsval % 10000, seg.tsecr % 10000, seg.len); -@@ -1516,6 +1554,30 @@ pseudo_tcp_state_has_received_fin (PseudoTcpState state) - } - } - -+/* True iff the @state requires that a FIN-ACK has already been received from -+ * the peer. */ -+static gboolean -+pseudo_tcp_state_has_received_fin_ack (PseudoTcpState state) -+{ -+ switch (state) { -+ case TCP_LISTEN: -+ case TCP_SYN_SENT: -+ case TCP_SYN_RECEIVED: -+ case TCP_ESTABLISHED: -+ case TCP_FIN_WAIT_1: -+ case TCP_FIN_WAIT_2: -+ case TCP_CLOSING: -+ case TCP_CLOSE_WAIT: -+ case TCP_LAST_ACK: -+ return FALSE; -+ case TCP_CLOSED: -+ case TCP_TIME_WAIT: -+ return TRUE; -+ default: -+ return FALSE; -+ } -+} -+ - static gboolean - process(PseudoTcpSocket *self, Segment *seg) - { -@@ -1529,6 +1591,7 @@ process(PseudoTcpSocket *self, Segment *seg) - gsize available_space; - guint32 kIdealRefillSize; - gboolean is_valuable_ack, is_duplicate_ack, is_fin_ack = FALSE; -+ gboolean received_fin = FALSE; - - /* If this is the wrong conversation, send a reset!?! - (with the correct conversation?) */ -@@ -1545,17 +1608,23 @@ process(PseudoTcpSocket *self, Segment *seg) - priv->bOutgoing = FALSE; - - if (priv->state == TCP_CLOSED || -- (pseudo_tcp_state_has_sent_fin (priv->state) && seg->len > 0)) { -- /* Send an RST segment. See: RFC 1122, §4.2.2.13. */ -+ (pseudo_tcp_state_has_received_fin_ack (priv->state) && seg->len > 0)) { -+ /* Send an RST segment. See: RFC 1122, §4.2.2.13; RFC 793, §3.4, point 3, -+ * page 37. We can only send RST if we know the peer knows we’re closed; -+ * otherwise this could be a timeout retransmit from them, due to our -+ * packets from data through to FIN being dropped. */ -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Segment received while closed; sending RST."); - if ((seg->flags & FLAG_RST) == 0) { - closedown (self, 0, CLOSEDOWN_LOCAL); - } -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Segment received while closed; sent RST."); -+ - return FALSE; - } - - // Check if this is a reset segment - if (seg->flags & FLAG_RST) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Received RST segment; closing down."); - closedown (self, ECONNRESET, CLOSEDOWN_REMOTE); - return FALSE; - } -@@ -1607,18 +1676,20 @@ process(PseudoTcpSocket *self, Segment *seg) - priv->rx_rttvar = rtt / 2; - } else { - priv->rx_rttvar = (3 * priv->rx_rttvar + -- abs((long)(rtt - priv->rx_srtt))) / 4; -+ labs((long)(rtt - priv->rx_srtt))) / 4; - priv->rx_srtt = (7 * priv->rx_srtt + rtt) / 8; - } - priv->rx_rto = bound(MIN_RTO, - priv->rx_srtt + max(1LU, 4 * priv->rx_rttvar), MAX_RTO); - -- DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "rtt: %ld srtt: %u rto: %u", -- rtt, priv->rx_srtt, priv->rx_rto); -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "rtt: %ld srtt: %u rttvar: %u rto: %u", -+ rtt, priv->rx_srtt, priv->rx_rttvar, priv->rx_rto); - } else { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Invalid RTT: %ld", rtt); - return FALSE; - } -+ -+ priv->last_acked_ts = seg->tsecr; - } - - priv->snd_wnd = seg->wnd << priv->swnd_scale; -@@ -1663,16 +1734,24 @@ process(PseudoTcpSocket *self, Segment *seg) - if (LARGER_OR_EQUAL (priv->snd_una, priv->recover)) { // NewReno - guint32 nInFlight = priv->snd_nxt - priv->snd_una; - // (Fast Retransmit) -- priv->cwnd = min(priv->ssthresh, nInFlight + priv->mss); -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery"); -+ priv->cwnd = min(priv->ssthresh, -+ max (nInFlight, priv->mss) + priv->mss); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery cwnd=%d ssthresh=%d nInFlight=%d mss: %d", priv->cwnd, priv->ssthresh, nInFlight, priv->mss); -+ priv->fast_recovery = FALSE; - priv->dup_acks = 0; - } else { -+ int transmit_status; -+ - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit"); -- if (!transmit(self, g_queue_peek_head (&priv->slist), now)) { -- closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); -+ transmit_status = transmit(self, g_queue_peek_head (&priv->slist), now); -+ if (transmit_status != 0) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Error transmitting recovery retransmit segment. Closing down."); -+ closedown (self, transmit_status, CLOSEDOWN_LOCAL); - return FALSE; - } -- priv->cwnd += priv->mss - min(nAcked, priv->cwnd); -+ priv->cwnd += (nAcked > priv->mss ? priv->mss : 0) - -+ min(nAcked, priv->cwnd); - } - } else { - priv->dup_acks = 0; -@@ -1695,20 +1774,43 @@ process(PseudoTcpSocket *self, Segment *seg) - guint32 nInFlight; - - priv->dup_acks += 1; -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Received dup ack (dups: %u)", -+ priv->dup_acks); - if (priv->dup_acks == 3) { // (Fast Retransmit) -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "enter recovery"); -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit"); -- if (!transmit(self, g_queue_peek_head (&priv->slist), now)) { -- closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); -- return FALSE; -+ int transmit_status; -+ -+ -+ if (LARGER_OR_EQUAL (priv->snd_una, priv->recover) || -+ seg->tsecr == priv->last_acked_ts) { /* NewReno */ -+ /* Invoke fast retransmit RFC3782 section 3 step 1A*/ -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "enter recovery"); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit"); -+ -+ transmit_status = transmit(self, g_queue_peek_head (&priv->slist), -+ now); -+ if (transmit_status != 0) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Error transmitting recovery retransmit segment. Closing down."); -+ -+ closedown (self, transmit_status, CLOSEDOWN_LOCAL); -+ return FALSE; -+ } -+ priv->recover = priv->snd_nxt; -+ nInFlight = priv->snd_nxt - priv->snd_una; -+ priv->ssthresh = max(nInFlight / 2, 2 * priv->mss); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "ssthresh: %u = max((nInFlight: %u / 2), 2 * mss: %u)", -+ priv->ssthresh, nInFlight, priv->mss); -+ priv->cwnd = priv->ssthresh + 3 * priv->mss; -+ priv->fast_recovery = TRUE; -+ } else { -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, -+ "Skipping fast recovery: recover: %u snd_una: %u", priv->recover, -+ priv->snd_una); - } -- priv->recover = priv->snd_nxt; -- nInFlight = priv->snd_nxt - priv->snd_una; -- priv->ssthresh = max(nInFlight / 2, 2 * priv->mss); -- //LOG(LS_INFO) << "priv->ssthresh: " << priv->ssthresh << " nInFlight: " << nInFlight << " priv->mss: " << priv->mss; -- priv->cwnd = priv->ssthresh + 3 * priv->mss; - } else if (priv->dup_acks > 3) { -- priv->cwnd += priv->mss; -+ if (priv->fast_recovery) -+ priv->cwnd += priv->mss; - } - } else { - priv->dup_acks = 0; -@@ -1720,19 +1822,34 @@ process(PseudoTcpSocket *self, Segment *seg) - set_state_established (self); - } - -- /* Check for connection closure. */ -+ /* Check for connection closure. Only pay attention to FIN segments if they -+ * are in sequence; otherwise we’ve missed a packet earlier in the stream and -+ * need to request retransmission first. */ - if (priv->support_fin_ack) { -+ /* @received_fin is set when, and only when, all segments preceding the FIN -+ * have been acknowledged. This is to handle the case where the FIN arrives -+ * out of order with a preceding data segment. */ -+ if (seg->flags & FLAG_FIN && priv->rcv_fin == 0) { -+ priv->rcv_fin = seg->seq; -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Setting rcv_fin = %u", priv->rcv_fin); -+ } else if (seg->flags & FLAG_FIN && seg->seq != priv->rcv_fin) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Second FIN segment received; ignored"); -+ return FALSE; -+ } -+ - /* For the moment, FIN segments must not contain data. */ - if (seg->flags & FLAG_FIN && seg->len != 0) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "FIN segment contained data; ignored"); - return FALSE; - } - -+ received_fin = (priv->rcv_nxt != 0 && priv->rcv_nxt + seg->len == priv->rcv_fin); -+ - /* Update the state machine, implementing all transitions on ‘rcv FIN’ or - * ‘rcv ACK of FIN’ from RFC 793, Figure 6; and RFC 1122, §4.2.2.8. */ - switch (priv->state) { - case TCP_ESTABLISHED: -- if (seg->flags & FLAG_FIN) { -+ if (received_fin) { - /* Received a FIN from the network, RFC 793, §3.5, Case 2. - * The code below will send an ACK for the FIN. */ - set_state (self, TCP_CLOSE_WAIT); -@@ -1751,20 +1868,20 @@ process(PseudoTcpSocket *self, Segment *seg) - } - break; - case TCP_FIN_WAIT_1: -- if (is_fin_ack && seg->flags & FLAG_FIN) { -+ if (is_fin_ack && received_fin) { - /* Simultaneous close with an ACK for a FIN previously sent, - * RFC 793, §3.5, Case 3. */ - set_state (self, TCP_TIME_WAIT); - } else if (is_fin_ack) { - /* Handle the ACK of a locally-sent FIN flag. RFC 793, §3.5, Case 1. */ - set_state (self, TCP_FIN_WAIT_2); -- } else if (seg->flags & FLAG_FIN) { -+ } else if (received_fin) { - /* Simultaneous close, RFC 793, §3.5, Case 3. */ - set_state (self, TCP_CLOSING); - } - break; - case TCP_FIN_WAIT_2: -- if (seg->flags & FLAG_FIN) { -+ if (received_fin) { - /* Local user closed the connection, RFC 793, §3.5, Case 1. */ - set_state (self, TCP_TIME_WAIT); - } -@@ -1776,7 +1893,7 @@ process(PseudoTcpSocket *self, Segment *seg) - case TCP_CLOSED: - case TCP_CLOSE_WAIT: - /* Shouldn’t ever hit these cases. */ -- if (seg->flags & FLAG_FIN) { -+ if (received_fin) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, - "Unexpected state %u when FIN received", priv->state); - } else if (is_fin_ack) { -@@ -1820,19 +1937,20 @@ process(PseudoTcpSocket *self, Segment *seg) - * see RFC 793, §3.3. Also see: RFC 793, §3.5. - */ - if (seg->seq != priv->rcv_nxt) { -- sflags = sfImmediateAck; // (Fast Recovery) -+ sflags = sfDuplicateAck; // (Fast Recovery) - } else if (seg->len != 0) { - if (priv->ack_delay == 0) { - sflags = sfImmediateAck; - } else { - sflags = sfDelayedAck; - } -- } else if (seg->flags & FLAG_FIN) { -+ } else if (received_fin) { -+ /* FIN flags have a sequence number. Only acknowledge them after all -+ * preceding octets have been acknowledged. */ - sflags = sfImmediateAck; -- priv->rcv_nxt += 1; - } - -- if (sflags == sfImmediateAck) { -+ if (sflags == sfDuplicateAck) { - if (seg->seq > priv->rcv_nxt) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "too new"); - } else if (SMALLER_OR_EQUAL(seg->seq + seg->len, priv->rcv_nxt)) { -@@ -1869,12 +1987,7 @@ process(PseudoTcpSocket *self, Segment *seg) - - bNewData = FALSE; - -- if (seg->flags & FLAG_FIN) { -- /* FIN flags have a sequence number. */ -- if (seg->seq == priv->rcv_nxt) { -- priv->rcv_nxt++; -- } -- } else if (seg->len > 0) { -+ if (seg->len > 0) { - if (bIgnoreData) { - if (seg->seq == priv->rcv_nxt) { - priv->rcv_nxt += seg->len; -@@ -1929,6 +2042,12 @@ process(PseudoTcpSocket *self, Segment *seg) - } - } - -+ if (received_fin) { -+ /* FIN flags have a sequence number. */ -+ priv->rcv_nxt++; -+ } -+ -+ - attempt_send(self, sflags); - - // If we have new data, notify the user -@@ -1952,7 +2071,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - - if (segment->xmit >= ((priv->state == TCP_ESTABLISHED) ? 15 : 30)) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "too many retransmits"); -- return FALSE; -+ return ETIMEDOUT; - } - - while (TRUE) { -@@ -1972,7 +2091,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - - if (wres == WR_FAIL) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "packet failed"); -- return FALSE; -+ return ECONNABORTED; /* FIXME: This error code doesn’t quite seem right */ - } - - g_assert(wres == WR_TOO_LARGE); -@@ -1980,7 +2099,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - while (TRUE) { - if (PACKET_MAXIMUMS[priv->msslevel + 1] == 0) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "MTU too small"); -- return FALSE; -+ return EMSGSIZE; - } - /* !?! We need to break up all outstanding and pending packets - and then retransmit!?! */ -@@ -2029,7 +2148,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - priv->rto_base = now; - } - -- return TRUE; -+ return 0; - } - - static void -@@ -2039,6 +2158,8 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - guint32 now = get_current_time (self); - gboolean bFirst = TRUE; - -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Attempting send with flags %u.", sflags); -+ - if (time_diff(now, priv->lastsend) > (long) priv->rx_rto) { - priv->cwnd = priv->mss; - } -@@ -2053,6 +2174,7 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - gsize snd_buffered; - GList *iter; - SSegment *sseg; -+ int transmit_status; - - cwnd = priv->cwnd; - if ((priv->dup_acks == 1) || (priv->dup_acks == 2)) { // Limited Transmit -@@ -2078,12 +2200,19 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - - if (bFirst) { - gsize available_space = pseudo_tcp_fifo_get_write_remaining (&priv->sbuf); -+ - bFirst = FALSE; - DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "[cwnd: %u nWindow: %u nInFlight: %u " - "nAvailable: %u nQueued: %" G_GSIZE_FORMAT " nEmpty: %" G_GSIZE_FORMAT -- " ssthresh: %u]", -+ " nWaiting: %zu ssthresh: %u]", - priv->cwnd, nWindow, nInFlight, nAvailable, snd_buffered, -- available_space, priv->ssthresh); -+ available_space, snd_buffered - nInFlight, priv->ssthresh); -+ } -+ -+ if (sflags == sfDuplicateAck) { -+ packet(self, priv->snd_nxt, 0, 0, 0, now); -+ sflags = sfNone; -+ continue; - } - - if (nAvailable == 0 && sflags != sfFin && sflags != sfRst) { -@@ -2091,7 +2220,8 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - return; - - // If this is an immediate ack, or the second delayed ack -- if ((sflags == sfImmediateAck) || priv->t_ack) { -+ if ((sflags == sfImmediateAck || sflags == sfDuplicateAck) || -+ priv->t_ack) { - packet(self, priv->snd_nxt, 0, 0, 0, now); - } else { - priv->t_ack = now; -@@ -2128,9 +2258,12 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - subseg); - } - -- if (!transmit(self, sseg, now)) { -+ transmit_status = transmit(self, sseg, now); -+ if (transmit_status != 0) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "transmit failed"); -- // TODO: consider closing socket -+ -+ // TODO: Is this the right thing ? -+ closedown (self, transmit_status, CLOSEDOWN_REMOTE); - return; - } - -@@ -2147,6 +2280,9 @@ closedown (PseudoTcpSocket *self, guint32 err, ClosedownSource source) - { - PseudoTcpSocketPrivate *priv = self->priv; - -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Closing down socket %p with %s error %u.", -+ self, (source == CLOSEDOWN_LOCAL) ? "local" : "remote", err); -+ - if (source == CLOSEDOWN_LOCAL && priv->support_fin_ack) { - queue_rst_message (self); - attempt_send (self, sfRst); -@@ -2211,6 +2347,7 @@ apply_window_scale_option (PseudoTcpSocket *self, guint8 scale_factor) - PseudoTcpSocketPrivate *priv = self->priv; - - priv->swnd_scale = scale_factor; -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Setting scale factor to %u", scale_factor); - } - - static void -@@ -2375,10 +2512,6 @@ pseudo_tcp_socket_get_available_bytes (PseudoTcpSocket *self) - { - PseudoTcpSocketPrivate *priv = self->priv; - -- if (priv->state != TCP_ESTABLISHED) { -- return -1; -- } -- - return pseudo_tcp_fifo_get_buffered (&priv->rbuf); - } - -@@ -2394,11 +2527,11 @@ pseudo_tcp_socket_get_available_send_space (PseudoTcpSocket *self) - PseudoTcpSocketPrivate *priv = self->priv; - gsize ret; - -- -- if (priv->state == TCP_ESTABLISHED) -+ if (!pseudo_tcp_state_has_sent_fin (priv->state)) { - ret = pseudo_tcp_fifo_get_write_remaining (&priv->sbuf); -- else -+ } else { - ret = 0; -+ } - - if (ret == 0) - priv->bWriteEnable = TRUE; -diff --git a/agent/pseudotcp.h b/agent/pseudotcp.h -index 879276e..e7c8eaa 100644 ---- a/agent/pseudotcp.h -+++ b/agent/pseudotcp.h -@@ -62,12 +62,24 @@ - #ifndef __GTK_DOC_IGNORE__ - #ifdef G_OS_WIN32 - # include <winsock2.h> -+ -+#ifndef ECONNABORTED - # define ECONNABORTED WSAECONNABORTED -+#endif -+ -+#ifndef ENOTCONN - # define ENOTCONN WSAENOTCONN -+#endif -+ -+#ifndef EWOULDBLOCK - # define EWOULDBLOCK WSAEWOULDBLOCK -+#endif -+ -+#ifndef ECONNRESET - # define ECONNRESET WSAECONNRESET - #endif - #endif -+#endif - - #include "agent.h" - -@@ -103,17 +115,6 @@ GType pseudo_tcp_socket_get_type (void); - (G_TYPE_INSTANCE_GET_CLASS ((obj), PSEUDO_TCP_SOCKET_TYPE, \ - PseudoTcpSocketClass)) - --struct _PseudoTcpSocketClass { -- GObjectClass parent_class; --}; -- --typedef struct _PseudoTcpSocketPrivate PseudoTcpSocketPrivate; -- --struct _PseudoTcpSocket { -- GObject parent; -- PseudoTcpSocketPrivate *priv; --}; -- - /** - * PseudoTcpDebugLevel: - * @PSEUDO_TCP_DEBUG_NONE: Disable debug messages -diff --git a/agent/stream.c b/agent/stream.c -index 09f79b5..8121e12 100644 ---- a/agent/stream.c -+++ b/agent/stream.c -@@ -48,63 +48,54 @@ - static volatile unsigned int n_streams_created = 0; - static volatile unsigned int n_streams_destroyed = 0; - -+G_DEFINE_TYPE (NiceStream, nice_stream, G_TYPE_OBJECT); -+ -+static void -+nice_stream_finalize (GObject *obj); -+ - /* - * @file stream.c - * @brief ICE stream functionality - */ --Stream * --stream_new (guint n_components, NiceAgent *agent) -+NiceStream * -+nice_stream_new (guint n_components, NiceAgent *agent) - { -- Stream *stream; -+ NiceStream *stream = NULL; - guint n; -- Component *component; - -- g_atomic_int_inc (&n_streams_created); -- nice_debug ("Created NiceStream (%u created, %u destroyed)", -- n_streams_created, n_streams_destroyed); -+ stream = g_object_new (NICE_TYPE_STREAM, NULL); - -- stream = g_slice_new0 (Stream); -+ /* Create the components. */ - for (n = 0; n < n_components; n++) { -- component = component_new (n + 1, agent, stream); -+ NiceComponent *component = NULL; -+ -+ component = nice_component_new (n + 1, agent, stream); - stream->components = g_slist_append (stream->components, component); - } - - stream->n_components = n_components; -- stream->initial_binding_request_received = FALSE; - - return stream; - } - - void --stream_close (Stream *stream) -+nice_stream_close (NiceStream *stream) - { - GSList *i; - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -- component_close (component); -+ NiceComponent *component = i->data; -+ nice_component_close (component); - } - } - --void --stream_free (Stream *stream) --{ -- g_free (stream->name); -- g_slist_free_full (stream->components, (GDestroyNotify) component_free); -- g_slice_free (Stream, stream); -- -- g_atomic_int_inc (&n_streams_destroyed); -- nice_debug ("Destroyed NiceStream (%u created, %u destroyed)", -- n_streams_created, n_streams_destroyed); --} -- --Component * --stream_find_component_by_id (const Stream *stream, guint id) -+NiceComponent * -+nice_stream_find_component_by_id (NiceStream *stream, guint id) - { - GSList *i; - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - if (component && component->id == id) - return component; - } -@@ -117,12 +108,12 @@ stream_find_component_by_id (const Stream *stream, guint id) - * 'CONNECTED' or 'READY' (connected plus nominated). - */ - gboolean --stream_all_components_ready (const Stream *stream) -+nice_stream_all_components_ready (NiceStream *stream) - { - GSList *i; - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - if (component && - !(component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY)) -@@ -136,7 +127,8 @@ stream_all_components_ready (const Stream *stream) - /* - * Initialized the local crendentials for the stream. - */ --void stream_initialize_credentials (Stream *stream, NiceRNG *rng) -+void -+nice_stream_initialize_credentials (NiceStream *stream, NiceRNG *rng) - { - /* note: generate ufrag/pwd for the stream (see ICE 15.4. - * '"ice-ufrag" and "ice-pwd" Attributes', ID-19) */ -@@ -149,7 +141,7 @@ void stream_initialize_credentials (Stream *stream, NiceRNG *rng) - * session. - */ - void --stream_restart (NiceAgent *agent, Stream *stream) -+nice_stream_restart (NiceStream *stream, NiceAgent *agent) - { - GSList *i; - -@@ -158,12 +150,49 @@ stream_restart (NiceAgent *agent, Stream *stream) - - stream->initial_binding_request_received = FALSE; - -- stream_initialize_credentials (stream, agent->rng); -+ nice_stream_initialize_credentials (stream, agent->rng); - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - -- component_restart (component); -+ nice_component_restart (component); - } - } - -+static void -+nice_stream_class_init (NiceStreamClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ -+ object_class->finalize = nice_stream_finalize; -+} -+ -+static void -+nice_stream_init (NiceStream *stream) -+{ -+ g_atomic_int_inc (&n_streams_created); -+ nice_debug ("Created NiceStream (%u created, %u destroyed)", -+ n_streams_created, n_streams_destroyed); -+ -+ stream->n_components = 0; -+ stream->initial_binding_request_received = FALSE; -+} -+ -+/* Must be called with the agent lock released as it could dispose of -+ * NiceIOStreams. */ -+static void -+nice_stream_finalize (GObject *obj) -+{ -+ NiceStream *stream; -+ -+ stream = NICE_STREAM (obj); -+ -+ g_free (stream->name); -+ g_slist_free_full (stream->components, (GDestroyNotify) g_object_unref); -+ -+ g_atomic_int_inc (&n_streams_destroyed); -+ nice_debug ("Destroyed NiceStream (%u created, %u destroyed)", -+ n_streams_created, n_streams_destroyed); -+ -+ G_OBJECT_CLASS (nice_stream_parent_class)->finalize (obj); -+} -diff --git a/agent/stream.h b/agent/stream.h -index e220f43..e524f62 100644 ---- a/agent/stream.h -+++ b/agent/stream.h -@@ -42,7 +42,7 @@ - - #include <glib.h> - --typedef struct _Stream Stream; -+typedef struct _NiceStream NiceStream; - - #include "component.h" - #include "random.h" -@@ -59,13 +59,27 @@ G_BEGIN_DECLS - #define NICE_STREAM_DEF_UFRAG 4 + 1 /* ufrag + NULL */ - #define NICE_STREAM_DEF_PWD 22 + 1 /* pwd + NULL */ - --struct _Stream --{ -+#define NICE_TYPE_STREAM nice_stream_get_type() -+#define NICE_STREAM(obj) \ -+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), NICE_TYPE_STREAM, NiceStream)) -+#define NICE_STREAM_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_CAST ((klass), NICE_TYPE_STREAM, NiceStreamClass)) -+#define NICE_IS_STREAM(obj) \ -+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NICE_TYPE_STREAM)) -+#define NICE_IS_STREAM_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_TYPE ((klass), NICE_TYPE_STREAM)) -+#define NICE_STREAM_GET_CLASS(obj) \ -+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_STREAM, NiceStreamClass)) -+ -+struct _NiceStream { -+ /*< private >*/ -+ GObject parent; -+ - gchar *name; - guint id; - guint n_components; - gboolean initial_binding_request_received; -- GSList *components; /* list of 'Component' structs */ -+ GSList *components; /* list of 'NiceComponent' objects */ - GSList *conncheck_list; /* list of CandidateCheckPair items */ - gchar local_ufrag[NICE_STREAM_MAX_UFRAG]; - gchar local_password[NICE_STREAM_MAX_PWD]; -@@ -76,27 +90,29 @@ struct _Stream - gint tos; - }; - -+typedef struct { -+ GObjectClass parent_class; -+} NiceStreamClass; - --Stream * --stream_new (guint n_components, NiceAgent *agent); -+GType nice_stream_get_type (void); - --void --stream_close (Stream *stream); -+NiceStream * -+nice_stream_new (guint n_components, NiceAgent *agent); - - void --stream_free (Stream *stream); -+nice_stream_close (NiceStream *stream); - - gboolean --stream_all_components_ready (const Stream *stream); -+nice_stream_all_components_ready (NiceStream *stream); - --Component * --stream_find_component_by_id (const Stream *stream, guint id); -+NiceComponent * -+nice_stream_find_component_by_id (NiceStream *stream, guint id); - - void --stream_initialize_credentials (Stream *stream, NiceRNG *rng); -+nice_stream_initialize_credentials (NiceStream *stream, NiceRNG *rng); - - void --stream_restart (NiceAgent *agent, Stream *stream); -+nice_stream_restart (NiceStream *stream, NiceAgent *agent); - - G_END_DECLS - -diff --git a/autogen.sh b/autogen.sh -index 2f58146..b6efba6 100755 ---- a/autogen.sh -+++ b/autogen.sh -@@ -1,27 +1,38 @@ - #!/bin/sh --set -e -- --test -d m4 || mkdir m4 --gtkdocize || exit 1 -- --autoreconf -fi -- --# Honor NOCONFIGURE for compatibility with gnome-autogen.sh --if test x"$NOCONFIGURE" = x; then -- run_configure=true -- for arg in $*; do -- case $arg in -- --no-configure) -- run_configure=false -- ;; -- *) -- ;; -- esac -- done --else -- run_configure=false -+# Run this to generate all the initial makefiles, etc. -+test -n "$srcdir" || srcdir=$(dirname "$0") -+test -n "$srcdir" || srcdir=. -+ -+olddir=$(pwd) -+ -+cd $srcdir -+ -+(test -f configure.ac) || { -+ echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***" -+ exit 1 -+} -+ -+# shellcheck disable=SC2016 -+PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac) -+ -+if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then -+ echo "*** WARNING: I am going to run 'configure' with no arguments." >&2 -+ echo "*** If you wish to pass any to it, please specify them on the" >&2 -+ echo "*** '$0' command line." >&2 -+ echo "" >&2 - fi - --if test $run_configure = true; then -- ./configure "$@" -+aclocal --install || exit 1 -+gtkdocize --copy || exit 1 -+autoreconf --verbose --force --install || exit 1 -+ -+cd "$olddir" -+if [ "$NOCONFIGURE" = "" ]; then -+ $srcdir/configure "$@" || exit 1 -+ -+ if [ "$1" = "--help" ]; then exit 0 else -+ echo "Now type 'make' to compile $PKG_NAME" || exit 1 -+ fi -+else -+ echo "Skipping configure process." - fi -diff --git a/configure.ac b/configure.ac -index 6031cec..6be4010 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -93,9 +93,9 @@ AC_CHECK_HEADERS([ifaddrs.h], \ - AC_CHECK_TYPES([size_t, ssize_t]) - - # Also put matching version in LIBNICE_CFLAGS --GLIB_REQ=2.30 -+GLIB_REQ=2.44 - --LIBNICE_CFLAGS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_30 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36" -+LIBNICE_CFLAGS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_44 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_44" - - dnl Support different levels of compiler error reporting. - dnl This configure flag is designed to mimic one from gnome-common, -@@ -231,9 +231,6 @@ AS_IF([test "$with_gstreamer" != no], [ - [ - have_gst_check=no - ]) -- -- AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes) -- - ]) - - AS_IF([test "$with_gstreamer010" != no], [ -@@ -260,6 +257,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(WITH_GSTREAMER010, test "$with_gstreamer010" = yes) - - GUPNP_IGD_REQUIRED=0.2.4 -diff --git a/docs/design.txt b/docs/design.txt -index 4f43724..6a3bf12 100644 ---- a/docs/design.txt -+++ b/docs/design.txt -@@ -90,7 +90,6 @@ NiceAgent GObject interface defined in 'nice/agent.h'. - - The rough order of control follow is as follows: - --- client should initialize glib with g_type_init() - - creation of NiceAgent object instance - - setting agent properties such as STUN and TURN server addresses - - connecting the GObject signals with g_signal_connect() to application -diff --git a/docs/reference/libnice/Makefile.am b/docs/reference/libnice/Makefile.am -index 1d53e3b..19e479e 100644 ---- a/docs/reference/libnice/Makefile.am -+++ b/docs/reference/libnice/Makefile.am -@@ -62,7 +62,7 @@ IGNORE_HFILES= conncheck.h discovery.h stream.h component.h agent-priv.h \ - - # Images to copy into HTML directory. - # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png --HTML_IMAGES= -+HTML_IMAGES = states.png - - # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). - # e.g. content_files=running.sgml building.sgml changes-2.0.sgml -@@ -94,13 +94,19 @@ include $(top_srcdir)/gtk-doc.make - - # Other files to distribute - # e.g. EXTRA_DIST += version.xml.in --#EXTRA_DIST += -+EXTRA_DIST += states.gv - - # Files not to distribute - # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types - # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt - #DISTCLEANFILES += - -+# If we ever need to regenerate this diagram. -+# Since it’s not expected to change much, let’s not depend on GraphViz to -+# build the docs. -+states.png: states.gv -+ dot -Tpng -Gsize=9.6,2.9! -Gdpi=200 $^ > $@ -+ - if ENABLE_GTK_DOC - TESTS_ENVIRONMENT = cd $(builddir) && - TESTS = $(GTKDOC_CHECK) -diff --git a/docs/reference/libnice/states.gv b/docs/reference/libnice/states.gv -new file mode 100644 -index 0000000..609be2e ---- /dev/null -+++ b/docs/reference/libnice/states.gv -@@ -0,0 +1,25 @@ -+/* libnice state transition diagram for NiceComponentState. */ -+digraph NiceComponentState { -+ rankdir=TB; -+ node [shape = doublecircle]; DISCONNECTED; -+ node [shape = circle]; -+ -+ /* Colour the normal control flow in green. */ -+ DISCONNECTED -> GATHERING [ label = "nice_agent_gather_candidates()", color = chartreuse3 ]; -+ GATHERING -> CONNECTING [ label = "nice_agent_set_remote_candidates()", color = chartreuse3 ]; -+ CONNECTING -> CONNECTED [ label = "At least one candidate pair succeeds", color = chartreuse3 ]; -+ CONNECTED -> READY [ label = "All candidate pairs checks finished", color = chartreuse3 ]; -+ -+ READY -> CONNECTED [ label = "Selected candidate pair fails" ]; -+ -+ FAILED -> CONNECTING [ label = "nice_agent_set_remote_candidates()" ]; -+ -+ DISCONNECTED -> CONNECTING [ label = "nice_agent_set_remote_candidates()" ]; -+ -+ /* Colour the failure paths in grey. */ -+ DISCONNECTED -> FAILED [ label = "Failure", color = gray ]; -+ GATHERING -> FAILED [ label = "Failure", color = gray ]; -+ CONNECTING -> FAILED [ label = "Failure", color = gray ]; -+ CONNECTED -> FAILED [ label = "Failure", color = gray ]; -+ READY -> FAILED [ label = "Failure", color = gray ]; -+} -diff --git a/docs/reference/libnice/states.png b/docs/reference/libnice/states.png -new file mode 100644 -index 0000000..ba23739 -Binary files /dev/null and b/docs/reference/libnice/states.png differ -diff --git a/examples/sdp-example.c b/examples/sdp-example.c -index 246341e..b6dd80a 100644 ---- a/examples/sdp-example.c -+++ b/examples/sdp-example.c -@@ -44,9 +44,7 @@ - - #include <agent.h> - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include <gio/gnetworking.h> --#endif - - static GMainLoop *gloop; - static gchar *stun_addr = NULL; -@@ -95,11 +93,7 @@ main(int argc, char *argv[]) - g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port); - } - --#if GLIB_CHECK_VERSION(2, 36, 0) - g_networking_init(); --#else -- g_type_init(); --#endif - - gloop = g_main_loop_new(NULL, FALSE); - -diff --git a/examples/simple-example.c b/examples/simple-example.c -index 6e13dc6..a511d29 100644 ---- a/examples/simple-example.c -+++ b/examples/simple-example.c -@@ -44,9 +44,7 @@ - - #include <agent.h> - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include <gio/gnetworking.h> --#endif - - static GMainLoop *gloop; - static GIOChannel* io_stdin; -@@ -105,11 +103,7 @@ main(int argc, char *argv[]) - g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port); - } - --#if GLIB_CHECK_VERSION(2, 36, 0) - g_networking_init(); --#else -- g_type_init(); --#endif - - gloop = g_main_loop_new(NULL, FALSE); - #ifdef G_OS_WIN32 -@@ -226,7 +220,7 @@ cb_component_state_changed(NiceAgent *agent, guint _stream_id, - g_debug("SIGNAL: state changed %d %d %s[%d]\n", - _stream_id, component_id, state_name[state], state); - -- if (state == NICE_COMPONENT_STATE_READY) { -+ if (state == NICE_COMPONENT_STATE_CONNECTED) { - NiceCandidate *local, *remote; - - // Get current selected candidate pair and print IP address used -diff --git a/examples/threaded-example.c b/examples/threaded-example.c -index 79eda8d..575b4dc 100644 ---- a/examples/threaded-example.c -+++ b/examples/threaded-example.c -@@ -44,9 +44,7 @@ - - #include <agent.h> - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include <gio/gnetworking.h> --#endif - - static GMainLoop *gloop; - static gchar *stun_addr = NULL; -@@ -104,11 +102,6 @@ main(int argc, char *argv[]) - g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port); - } - --#if GLIB_CHECK_VERSION(2, 36, 0) -- g_networking_init(); --#else -- g_type_init(); --#endif - g_networking_init(); - - gloop = g_main_loop_new(NULL, FALSE); -diff --git a/nice/libnice.sym b/nice/libnice.sym -index efcfdc3..b04bb95 100644 ---- a/nice/libnice.sym -+++ b/nice/libnice.sym -@@ -74,12 +74,16 @@ pseudo_tcp_socket_close - pseudo_tcp_socket_connect - pseudo_tcp_socket_get_error - pseudo_tcp_socket_get_next_clock -+pseudo_tcp_socket_get_type -+pseudo_tcp_socket_is_closed -+pseudo_tcp_socket_is_closed_remotely - pseudo_tcp_socket_new - pseudo_tcp_socket_notify_clock - pseudo_tcp_socket_notify_mtu - pseudo_tcp_socket_notify_packet - pseudo_tcp_socket_recv - pseudo_tcp_socket_send -+pseudo_tcp_socket_shutdown - stun_agent_build_unknown_attributes_error - stun_agent_default_validater - stun_agent_finish_message -diff --git a/socket/http.c b/socket/http.c -index 404d378..96ddfd8 100644 ---- a/socket/http.c -+++ b/socket/http.c -@@ -95,6 +95,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - NiceSocket * - nice_http_socket_new (NiceSocket *base_socket, -@@ -126,6 +127,7 @@ nice_http_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - /* Send HTTP CONNECT */ -@@ -281,9 +283,8 @@ socket_recv_messages (NiceSocket *sock, - HttpPriv *priv = sock->priv; - gint ret = -1; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->state == HTTP_STATE_CONNECTED) { - guint i; -@@ -576,9 +577,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - HttpPriv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->state == HTTP_STATE_CONNECTED) { - /* Fast path. */ -@@ -642,3 +642,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ HttpPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/pseudossl.c b/socket/pseudossl.c -index 5ad4f97..052725c 100644 ---- a/socket/pseudossl.c -+++ b/socket/pseudossl.c -@@ -116,6 +116,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - NiceSocket * - nice_pseudossl_socket_new (NiceSocket *base_socket, -@@ -152,6 +153,7 @@ nice_pseudossl_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - /* We send 'to' NULL because it will always be to an already connected -@@ -204,9 +206,8 @@ socket_recv_messages (NiceSocket *sock, - { - PseudoSSLPriv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->handshaken) { - if (priv->base_socket) { -@@ -256,9 +257,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - PseudoSSLPriv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->handshaken) { - /* Fast path: pass directly through to the base socket once the handshake is -@@ -319,3 +319,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ PseudoSSLPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/socket.c b/socket/socket.c -index 9c0d978..08ae31a 100644 ---- a/socket/socket.c -+++ b/socket/socket.c -@@ -265,6 +265,14 @@ nice_socket_set_writable_callback (NiceSocket *sock, - sock->set_writable_callback (sock, callback, user_data); - } - -+gboolean -+nice_socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ if (sock->is_based_on) -+ return sock->is_based_on (sock, other); -+ return (sock == other); -+} -+ - void - nice_socket_free (NiceSocket *sock) - { -diff --git a/socket/socket.h b/socket/socket.h -index 41ea07b..fadcbc1 100644 ---- a/socket/socket.h -+++ b/socket/socket.h -@@ -88,6 +88,7 @@ struct _NiceSocket - gboolean (*can_send) (NiceSocket *sock, NiceAddress *addr); - void (*set_writable_callback) (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+ gboolean (*is_based_on) (NiceSocket *sock, NiceSocket *other); - void (*close) (NiceSocket *sock); - void *priv; - }; -@@ -124,6 +125,23 @@ void - nice_socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); - -+/** -+ * nice_socket_is_based_on: -+ * @sock: a #NiceSocket -+ * @other: another #NiceSocket -+ * -+ * Checks whether @sock wraps @other as a source and destination of its read and -+ * write operations. The function traverses the whole chain of @sock's base -+ * sockets until @other is found or the end is reached. -+ * -+ * Returns: %TRUE if @sock is based on @other or if @sock and @other are -+ * the same socket, %FALSE otherwise. -+ * -+ * Since: UNRELEASED -+ */ -+gboolean -+nice_socket_is_based_on (NiceSocket *sock, NiceSocket *other); -+ - void - nice_socket_free (NiceSocket *sock); - -diff --git a/socket/socks5.c b/socket/socks5.c -index 46d17fb..d15fc29 100644 ---- a/socket/socks5.c -+++ b/socket/socks5.c -@@ -81,6 +81,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - - NiceSocket * -@@ -108,6 +109,7 @@ nice_socks5_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - /* Send SOCKS5 handshake */ -@@ -167,9 +169,8 @@ socket_recv_messages (NiceSocket *sock, - guint i; - gint ret = -1; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - switch (priv->state) { - case SOCKS_STATE_CONNECTED: -@@ -423,9 +424,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - Socks5Priv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->state == SOCKS_STATE_CONNECTED) { - /* Fast path: pass through to the base socket once connected. */ -@@ -488,3 +488,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ Socks5Priv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/tcp-active.c b/socket/tcp-active.c -index 5144678..5402806 100644 ---- a/socket/tcp-active.c -+++ b/socket/tcp-active.c -@@ -50,6 +50,11 @@ - #include <unistd.h> - #endif - -+/* FIXME: This should be defined in gio/gnetworking.h, which we should include; -+ * but we cannot do that without refactoring. -+ * (See: https://phabricator.freedesktop.org/D230). */ -+#define TCP_NODELAY 1 -+ - typedef struct { - GSocketAddress *local_addr; - GMainContext *context; -@@ -225,6 +230,9 @@ nice_tcp_active_socket_connect (NiceSocket *sock, NiceAddress *addr) - /* GSocket: All socket file descriptors are set to be close-on-exec. */ - g_socket_set_blocking (gsock, false); - -+ /* setting TCP_NODELAY to TRUE in order to avoid packet batching */ -+ g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); -+ - /* Allow g_socket_bind to fail */ - g_socket_bind (gsock, priv->local_addr, FALSE, NULL); - -diff --git a/socket/tcp-bsd.c b/socket/tcp-bsd.c -index 20dd698..3e5f5a8 100644 ---- a/socket/tcp-bsd.c -+++ b/socket/tcp-bsd.c -@@ -54,6 +54,11 @@ - #include <unistd.h> - #endif - -+/* FIXME: This should be defined in gio/gnetworking.h, which we should include; -+ * but we cannot do that without refactoring. -+ * (See: https://phabricator.freedesktop.org/D230). */ -+#define TCP_NODELAY 1 -+ - typedef struct { - NiceAddress remote_addr; - GQueue send_queue; -@@ -168,6 +173,9 @@ nice_tcp_bsd_socket_new (GMainContext *ctx, NiceAddress *local_addr, - /* GSocket: All socket file descriptors are set to be close-on-exec. */ - g_socket_set_blocking (gsock, false); - -+ /* setting TCP_NODELAY to TRUE in order to avoid packet batching */ -+ g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); -+ - gret = g_socket_connect (gsock, gaddr, NULL, &gerr); - g_object_unref (gaddr); - -@@ -229,9 +237,8 @@ socket_recv_messages (NiceSocket *sock, - TcpPriv *priv = sock->priv; - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Don't try to access the socket if it had an error */ - if (priv->error) -@@ -283,9 +290,8 @@ socket_send_message (NiceSocket *sock, - GError *gerr = NULL; - gsize message_len; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Don't try to access the socket if it had an error, otherwise we risk a - * crash with SIGPIPE (Broken pipe) */ -@@ -344,9 +350,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -diff --git a/socket/tcp-passive.c b/socket/tcp-passive.c -index 30bfba8..131ff4b 100644 ---- a/socket/tcp-passive.c -+++ b/socket/tcp-passive.c -@@ -50,6 +50,11 @@ - #include <unistd.h> - #endif - -+/* FIXME: This should be defined in gio/gnetworking.h, which we should include; -+ * but we cannot do that without refactoring. -+ * (See: https://phabricator.freedesktop.org/D230). */ -+#define TCP_NODELAY 1 -+ - typedef struct { - GMainContext *context; - GHashTable *connections; -@@ -176,6 +181,12 @@ socket_close (NiceSocket *sock) - { - TcpPassivePriv *priv = sock->priv; - -+ if (sock->fileno != NULL) { -+ g_socket_close (sock->fileno, NULL); -+ g_object_unref (sock->fileno); -+ sock->fileno = NULL; -+ } -+ - if (priv->context) - g_main_context_unref (priv->context); - g_hash_table_unref (priv->connections); -@@ -278,6 +289,9 @@ nice_tcp_passive_socket_accept (NiceSocket *sock) - /* GSocket: All socket file descriptors are set to be close-on-exec. */ - g_socket_set_blocking (gsock, false); - -+ /* setting TCP_NODELAY to TRUE in order to avoid packet batching */ -+ g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); -+ - gaddr = g_socket_get_remote_address (gsock, NULL); - if (gaddr == NULL || - !g_socket_address_to_native (gaddr, &name.addr, sizeof (name), NULL)) { -diff --git a/socket/udp-bsd.c b/socket/udp-bsd.c -index d56f093..3fac544 100644 ---- a/socket/udp-bsd.c -+++ b/socket/udp-bsd.c -@@ -183,9 +183,8 @@ socket_recv_messages (NiceSocket *sock, - guint i; - gboolean error = FALSE; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Read messages into recv_messages until one fails or would block, or we - * reach the end. */ -@@ -204,7 +203,10 @@ socket_recv_messages (NiceSocket *sock, - recv_message->length = MAX (recvd, 0); - - if (recvd < 0) { -- if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) -+ /* Handle ECONNRESET here as if it were EWOULDBLOCK; see -+ * https://phabricator.freedesktop.org/T121 */ -+ if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || -+ g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED)) - recvd = 0; - else - error = TRUE; -@@ -245,9 +247,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - GError *child_error = NULL; - gssize len; - -- /* Socket has been closed: */ -- if (priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (!nice_address_is_valid (&priv->niceaddr) || - !nice_address_equal (&priv->niceaddr, to)) { -@@ -289,9 +290,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -diff --git a/socket/udp-turn-over-tcp.c b/socket/udp-turn-over-tcp.c -index d97fa04..2b91f92 100644 ---- a/socket/udp-turn-over-tcp.c -+++ b/socket/udp-turn-over-tcp.c -@@ -86,6 +86,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - NiceSocket * - nice_udp_turn_over_tcp_socket_new (NiceSocket *base_socket, -@@ -107,6 +108,7 @@ nice_udp_turn_over_tcp_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - return sock; -@@ -134,9 +136,8 @@ socket_recv_message (NiceSocket *sock, NiceInputMessage *recv_message) - GInputVector local_recv_buf; - NiceInputMessage local_recv_message; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->expecting_len == 0) { - guint headerlen = 0; -@@ -241,9 +242,8 @@ socket_recv_messages (NiceSocket *nicesock, - guint i; - gboolean error = FALSE; - -- /* Socket has been closed: */ -- if (nicesock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (nicesock->priv != NULL); - - for (i = 0; i < n_recv_messages; i++) { - gssize len; -@@ -285,9 +285,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - } header_buf; - guint offset = 0; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Count the number of buffers. */ - if (message->n_buffers == -1) { -@@ -301,7 +300,7 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - - /* Allocate a new array of buffers, covering all the buffers in the input - * @message, but with an additional one for a header and one for a footer. */ -- local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector)); -+ local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector)); - local_message.buffers = local_bufs; - local_message.n_buffers = n_bufs + 1; - -@@ -377,8 +376,6 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - if (ret == 1) - ret = output_message_get_size (&local_message); - -- g_free (local_bufs); -- - return ret; - } - -@@ -388,9 +385,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -@@ -458,3 +454,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ TurnTcpPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index e640363..617e4f3 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -98,6 +98,11 @@ typedef struct { - GHashTable *send_data_queues; /* stores a send data queue for per peer */ - GSource *permission_timeout_source; /* timer used to invalidate - permissions */ -+ -+ guint8 *cached_realm; -+ uint16_t cached_realm_len; -+ guint8 *cached_nonce; -+ uint16_t cached_nonce_len; - } UdpTurnPriv; - - -@@ -125,15 +130,16 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - static void priv_process_pending_bindings (UdpTurnPriv *priv); - static gboolean priv_retransmissions_tick_unlocked (UdpTurnPriv *priv); - static gboolean priv_retransmissions_tick (gpointer pointer); - static void priv_schedule_tick (UdpTurnPriv *priv); - static void priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg); --static gboolean priv_send_create_permission (UdpTurnPriv *priv, StunMessage *resp, -+static gboolean priv_send_create_permission (UdpTurnPriv *priv, - const NiceAddress *peer); --static gboolean priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp, -+static gboolean priv_send_channel_bind (UdpTurnPriv *priv, - uint16_t channel, - const NiceAddress *peer); - static gboolean priv_add_channel_binding (UdpTurnPriv *priv, -@@ -235,7 +241,7 @@ nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, - priv_send_data_queue_destroy); - - sock->type = NICE_SOCKET_TYPE_UDP_TURN; -- sock->fileno = base_socket->fileno; -+ sock->fileno = NULL; - sock->addr = *addr; - sock->send_messages = socket_send_messages; - sock->send_messages_reliable = socket_send_messages_reliable; -@@ -243,6 +249,7 @@ nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - sock->priv = (void *) priv; - -@@ -317,6 +324,8 @@ socket_close (NiceSocket *sock) - g_list_free(priv->pending_permissions); - g_free (priv->username); - g_free (priv->password); -+ g_free (priv->cached_realm); -+ g_free (priv->cached_nonce); - g_free (priv); - - sock->priv = NULL; -@@ -332,11 +341,10 @@ socket_recv_messages (NiceSocket *sock, - gboolean error = FALSE; - guint n_valid_messages; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - -- nice_debug ("received message on TURN socket"); -+ nice_debug_verbose ("received message on TURN socket"); - - n_messages = nice_socket_recv_messages (priv->base_socket, - recv_messages, n_recv_messages); -@@ -374,7 +382,7 @@ socket_recv_messages (NiceSocket *sock, - buffer = message->buffers[0].buffer; - buffer_length = message->length; - } else { -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - - buffer = compact_input_message (message, &buffer_length); - allocated_buffer = TRUE; -@@ -575,7 +583,7 @@ _socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to, - n_bufs = message->n_buffers; - } - -- local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector)); -+ local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector)); - local_message.buffers = local_bufs; - local_message.n_buffers = n_bufs + 1; - -@@ -598,8 +606,6 @@ _socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to, - if (ret == 1) - ret = message_len; - -- g_free (local_bufs); -- - return ret; - } - } -@@ -663,7 +669,7 @@ socket_dequeue_all_data (UdpTurnPriv *priv, const NiceAddress *to) - SendData *data = - (SendData *) g_queue_pop_head(send_queue); - -- nice_debug ("dequeuing data"); -+ nice_debug_verbose ("dequeuing data"); - _socket_send_wrapped (priv->base_socket, &priv->server_addr, - data->data_len, data->data, data->reliable); - -@@ -693,9 +699,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - ChannelBinding *binding = NULL; - gint ret; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = priv->channels; i; i = i->next) { - ChannelBinding *b = i->data; -@@ -816,7 +821,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - /* Finish the message. */ - msg_len = stun_agent_finish_message (&priv->agent, &msg, - priv->password, priv->password_len); -- if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) { -+ if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST && -+ priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_OC2007) { - SendRequest *req = g_slice_new0 (SendRequest); - - req->priv = priv; -@@ -831,11 +837,11 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 && - !priv_has_permission_for_peer (priv, to)) { - if (!priv_has_sent_permission_for_peer (priv, to)) { -- priv_send_create_permission (priv, NULL, to); -+ priv_send_create_permission (priv, to); - } - - /* enque data */ -- nice_debug ("enqueuing data"); -+ nice_debug_verbose ("enqueuing data"); - socket_enqueue_data(priv, to, msg_len, (gchar *)buffer, reliable); - - return msg_len; -@@ -868,9 +874,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -@@ -951,6 +956,15 @@ socket_set_writable_callback (NiceSocket *sock, - } - - static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ UdpTurnPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -+ -+static gboolean - priv_forget_send_request (gpointer pointer) - { - SendRequest *req = pointer; -@@ -1079,13 +1093,20 @@ priv_binding_timeout (gpointer data) - ChannelBinding *b = i->data; - if (b->timeout_source == source) { - b->renew = TRUE; -+ -+ /* Remove any existing timer */ -+ if (b->timeout_source) { -+ g_source_destroy (b->timeout_source); -+ g_source_unref (b->timeout_source); -+ } -+ - /* Install timer to expire the permission */ - b->timeout_source = priv_timeout_add_with_context (priv, - STUN_EXPIRE_TIMEOUT, TRUE, priv_binding_expired_timeout, priv); - - /* Send renewal */ - if (!priv->current_binding_msg) -- priv_send_channel_bind (priv, NULL, b->channel, &b->peer); -+ priv_send_channel_bind (priv, b->channel, &b->peer); - break; - } - } -@@ -1095,6 +1116,31 @@ priv_binding_timeout (gpointer data) - return FALSE; - } - -+void -+nice_udp_turn_socket_cache_realm_nonce (NiceSocket *sock, StunMessage *msg) -+{ -+ UdpTurnPriv *priv = sock->priv; -+ gconstpointer tmp; -+ -+ g_assert (sock->type == NICE_SOCKET_TYPE_UDP_TURN); -+ -+ g_free (priv->cached_realm); -+ priv->cached_realm = NULL; -+ priv->cached_realm_len = 0; -+ -+ g_free (priv->cached_nonce); -+ priv->cached_nonce = NULL; -+ priv->cached_nonce_len = 0; -+ -+ tmp = stun_message_find (msg, STUN_ATTRIBUTE_REALM, &priv->cached_realm_len); -+ if (tmp && priv->cached_realm_len < 764) -+ priv->cached_realm = g_memdup (tmp, priv->cached_realm_len); -+ -+ tmp = stun_message_find (msg, STUN_ATTRIBUTE_NONCE, &priv->cached_nonce_len); -+ if (tmp && priv->cached_nonce_len < 764) -+ priv->cached_nonce = g_memdup (tmp, priv->cached_nonce_len); -+} -+ - guint - nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_sock, - NiceInputMessage *message) -@@ -1121,7 +1167,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - } - - /* Slow path. */ -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - - buf = compact_input_message (message, &buf_len); - len = nice_udp_turn_socket_parse_recv (sock, from_sock, -@@ -1298,8 +1344,9 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - - g_free (priv->current_binding_msg); - priv->current_binding_msg = NULL; -+ nice_udp_turn_socket_cache_realm_nonce (sock, &msg); - if (binding) -- priv_send_channel_bind (priv, &msg, binding->channel, -+ priv_send_channel_bind (priv, binding->channel, - &binding->peer); - } else { - g_free (priv->current_binding); -@@ -1357,12 +1404,17 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - } peer; - socklen_t peer_len = sizeof(peer); - NiceAddress to; -+ gchar tmpbuf[INET6_ADDRSTRLEN]; - -- nice_debug ("got response for CreatePermission"); - stun_message_find_xor_addr ( - ¤t_create_permission_msg->message, - STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &peer.storage, &peer_len); - nice_address_set_from_sockaddr (&to, &peer.addr); -+ nice_address_to_string (&to, tmpbuf); -+ nice_debug ("TURN: got response for CreatePermission " -+ "with XOR_PEER_ADDRESS=[%s]:%u : %s", -+ tmpbuf, nice_address_get_port (&to), -+ stun_message_get_class (&msg) == STUN_ERROR ? "unauthorized" : "ok"); - - /* unathorized => resend with realm and nonce */ - if (stun_message_get_class (&msg) == STUN_ERROR) { -@@ -1395,8 +1447,10 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - priv->pending_permissions, i); - g_free (current_create_permission_msg); - current_create_permission_msg = NULL; -+ -+ nice_udp_turn_socket_cache_realm_nonce (sock, &msg); - /* resend CreatePermission */ -- priv_send_create_permission (priv, &msg, &to); -+ priv_send_create_permission (priv, &to); - return 0; - } - } -@@ -1463,7 +1517,7 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 && - !priv_has_permission_for_peer (priv, from)) { - if (!priv_has_sent_permission_for_peer (priv, from)) { -- priv_send_create_permission (priv, NULL, from); -+ priv_send_create_permission (priv, from); - } - } - -@@ -1549,7 +1603,7 @@ priv_process_pending_bindings (UdpTurnPriv *priv) - for (i = priv->channels ; i; i = i->next) { - ChannelBinding *b = i->data; - if (b->renew) { -- priv_send_channel_bind (priv, NULL, b->channel, &b->peer); -+ priv_send_channel_bind (priv, b->channel, &b->peer); - break; - } - } -@@ -1804,7 +1858,7 @@ priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg) - } - - static gboolean --priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, -+priv_send_create_permission(UdpTurnPriv *priv, - const NiceAddress *peer) - { - guint msg_buf_len; -@@ -1814,17 +1868,6 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, - struct sockaddr_storage storage; - struct sockaddr addr; - } addr; -- uint8_t *realm = NULL; -- uint16_t realm_len = 0; -- uint8_t *nonce = NULL; -- uint16_t nonce_len = 0; -- -- if (resp) { -- realm = (uint8_t *) stun_message_find (resp, -- STUN_ATTRIBUTE_REALM, &realm_len); -- nonce = (uint8_t *) stun_message_find (resp, -- STUN_ATTRIBUTE_NONCE, &nonce_len); -- } - - /* register this peer as being pening a permission (if not already pending) */ - if (!priv_has_sent_permission_for_peer (priv, peer)) { -@@ -1841,8 +1884,8 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, - priv->username_len, - priv->password, - priv->password_len, -- realm, realm_len, -- nonce, nonce_len, -+ priv->cached_realm, priv->cached_realm_len, -+ priv->cached_nonce, priv->cached_nonce_len, - &addr.storage, - STUN_USAGE_TURN_COMPATIBILITY_RFC5766); - -@@ -1876,8 +1919,8 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, - } - - static gboolean --priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp, -- uint16_t channel, const NiceAddress *peer) -+priv_send_channel_bind (UdpTurnPriv *priv, uint16_t channel, -+ const NiceAddress *peer) - { - uint32_t channel_attr = channel << 16; - size_t stun_len; -@@ -1910,37 +1953,29 @@ priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp, - return FALSE; - } - -- if (priv->username != NULL && priv->username_len > 0) { -+ if (priv->username != NULL && priv->username_len > 0 && -+ priv->cached_realm != NULL && priv->cached_realm_len > 0 && -+ priv->cached_nonce != NULL && priv->cached_nonce_len > 0) { -+ - if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME, - priv->username, priv->username_len) - != STUN_MESSAGE_RETURN_SUCCESS) { - g_free (msg); - return FALSE; - } -- } -- -- if (resp) { -- uint8_t *realm; -- uint8_t *nonce; -- uint16_t len; - -- realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len); -- if (realm != NULL) { -- if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM, -- realm, len) -- != STUN_MESSAGE_RETURN_SUCCESS) { -- g_free (msg); -- return 0; -- } -+ if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM, -+ priv->cached_realm, priv->cached_realm_len) -+ != STUN_MESSAGE_RETURN_SUCCESS) { -+ g_free (msg); -+ return 0; - } -- nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len); -- if (nonce != NULL) { -- if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE, -- nonce, len) -- != STUN_MESSAGE_RETURN_SUCCESS) { -- g_free (msg); -- return 0; -- } -+ -+ if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE, -+ priv->cached_nonce, priv->cached_nonce_len) -+ != STUN_MESSAGE_RETURN_SUCCESS) { -+ g_free (msg); -+ return 0; - } - } - -@@ -1988,7 +2023,7 @@ priv_add_channel_binding (UdpTurnPriv *priv, const NiceAddress *peer) - } - - if (channel >= 0x4000 && channel < 0xffff) { -- gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer); -+ gboolean ret = priv_send_channel_bind (priv, channel, peer); - if (ret) { - priv->current_binding = g_new0 (ChannelBinding, 1); - priv->current_binding->channel = channel; -diff --git a/socket/udp-turn.h b/socket/udp-turn.h -index ba31636..b1eeeb4 100644 ---- a/socket/udp-turn.h -+++ b/socket/udp-turn.h -@@ -75,6 +75,9 @@ nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); - void - nice_udp_turn_socket_set_ms_connection_id (NiceSocket *sock, StunMessage *msg); - -+void -+nice_udp_turn_socket_cache_realm_nonce (NiceSocket *sock, StunMessage *msg); -+ - - G_END_DECLS - -diff --git a/stun/debug.c b/stun/debug.c -index 2b4ec59..8efb576 100644 ---- a/stun/debug.c -+++ b/stun/debug.c -@@ -46,7 +46,7 @@ - #include "debug.h" - - --static int debug_enabled = 1; -+static int debug_enabled = 0; - - void stun_debug_enable (void) { - debug_enabled = 1; -diff --git a/stun/stunagent.c b/stun/stunagent.c -index 2abcc29..bd243cb 100644 ---- a/stun/stunagent.c -+++ b/stun/stunagent.c -@@ -513,8 +513,20 @@ size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg, - uint32_t fpr; - int saved_id_idx = 0; - uint8_t md5[16]; -+ bool remember_transaction; - -- if (stun_message_get_class (msg) == STUN_REQUEST) { -+ remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST); -+ -+ if (agent->compatibility == STUN_COMPATIBILITY_OC2007 && -+ stun_message_get_method (msg) == STUN_SEND) { -+ /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to -+ * STUN_SEND requests, so don't bother waiting for them. More details at -+ * https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx. -+ */ -+ remember_transaction = FALSE; -+ } -+ -+ if (remember_transaction) { - for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) { - if (agent->sent_ids[saved_id_idx].valid == FALSE) { - break; -@@ -620,7 +632,7 @@ size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg, - } - - -- if (stun_message_get_class (msg) == STUN_REQUEST) { -+ if (remember_transaction) { - stun_message_id (msg, agent->sent_ids[saved_id_idx].id); - agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg); - agent->sent_ids[saved_id_idx].key = (uint8_t *) key; -diff --git a/stun/stunmessage.c b/stun/stunmessage.c -index 9faa64b..558fe5e 100644 ---- a/stun/stunmessage.c -+++ b/stun/stunmessage.c -@@ -550,7 +550,6 @@ ssize_t stun_message_validate_buffer_length_fast (StunInputVector *buffers, - - if (buffers[0].buffer[0] >> 6) - { -- stun_debug ("STUN error: RTP or other non-protocol packet!"); - return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet - } - -diff --git a/stun/tests/test-bind.c b/stun/tests/test-bind.c -index 0c7646f..2cf4feb 100644 ---- a/stun/tests/test-bind.c -+++ b/stun/tests/test-bind.c -@@ -438,7 +438,7 @@ static void keepalive (void) - - static void test (void (*func) (void), const char *name) - { -- alarm (20); -+ alarm (30); - - printf ("%s test... ", name); - func (); -diff --git a/stun/tests/test-conncheck.c b/stun/tests/test-conncheck.c -index 610d43a..92b947c 100644 ---- a/stun/tests/test-conncheck.c -+++ b/stun/tests/test-conncheck.c -@@ -213,7 +213,7 @@ int main (void) - - addr.ip4.sin_family = AF_INET; - -- /* Lost role conflict */ -+ /* Role conflict, controlling + ICE-CONTROLLING, switching controlled */ - assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); - val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLING, tie + 1); - assert (val == STUN_MESSAGE_RETURN_SUCCESS); -@@ -223,7 +223,6 @@ int main (void) - rlen = stun_agent_finish_message (&agent, &req, pass, pass_len); - assert (rlen > 0); - -- - len = sizeof (resp_buf); - control = true; - val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, -@@ -236,7 +235,7 @@ int main (void) - stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); - assert (stun_message_get_class (&resp) == STUN_RESPONSE); - -- /* Won role conflict */ -+ /* Role conflict, controlled + ICE-CONTROLLED, switching controlling */ - assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); - val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLED, tie - 1); - assert (val == STUN_MESSAGE_RETURN_SUCCESS); -@@ -251,15 +250,60 @@ int main (void) - val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, - &resp, resp_buf, &len, &addr.storage, - sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245); -- assert (val2 == STUN_USAGE_ICE_RETURN_SUCCESS); -+ assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT); - assert (len > 0); -- assert (control == false); -+ assert (control == true); -+ assert (stun_agent_validate (&agent, &resp, resp_buf, len, -+ stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); -+ assert (stun_message_get_class (&resp) == STUN_RESPONSE); -+ -+ /* Role conflict, controlling + ICE-CONTROLLING, staying controlling */ -+ assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); -+ val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLING, tie - 1); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ val = stun_message_append_string (&req, STUN_ATTRIBUTE_USERNAME, -+ (char *) ufrag); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ rlen = stun_agent_finish_message (&agent, &req, pass, pass_len); -+ assert (rlen > 0); -+ -+ len = sizeof (resp_buf); -+ control = true; -+ val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, -+ &resp, resp_buf, &len, &addr.storage, -+ sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245); -+ assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT); -+ assert (len > 0); -+ assert (control == true); - assert (stun_agent_validate (&agent, &resp, resp_buf, len, - stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); - assert (stun_message_get_class (&resp) == STUN_ERROR); - stun_message_find_error (&resp, &code); - assert (code == STUN_ERROR_ROLE_CONFLICT); - -+ /* Role conflict, controlled + ICE-CONTROLLED, staying controlling */ -+ assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); -+ val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLED, tie + 1); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ val = stun_message_append_string (&req, STUN_ATTRIBUTE_USERNAME, -+ (char *) ufrag); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ rlen = stun_agent_finish_message (&agent, &req, pass, pass_len); -+ assert (rlen > 0); -+ -+ len = sizeof (resp_buf); -+ control = false; -+ val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, -+ &resp, resp_buf, &len, &addr.storage, -+ sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245); -+ assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT); -+ assert (len > 0); -+ assert (control == false); -+ assert (stun_agent_validate (&agent, &resp, resp_buf, len, -+ stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); -+ assert (stun_message_get_class (&resp) == STUN_ERROR); -+ stun_message_find_error (&resp, &code); -+ assert (code == STUN_ERROR_ROLE_CONFLICT); - - return 0; - } -diff --git a/stun/usages/ice.c b/stun/usages/ice.c -index a628791..a7d0d19 100644 ---- a/stun/usages/ice.c -+++ b/stun/usages/ice.c -@@ -265,9 +265,17 @@ stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, - if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING - : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS) - { -+ /* we have the ice-controlling/controlled attribute, -+ * and there's a role conflict -+ */ - stun_debug ("STUN Role Conflict detected:"); - -- if (tie < q) -+ /* According to ICE RFC 5245, section 7.2.1.1, we consider the four -+ * possible cases when a role conflict is detected: two cases are -+ * resolved by switching role locally, and the two other cases are -+ * handled by responding with a STUN error. -+ */ -+ if ((tie < q && *control) || (tie >= q && !*control)) - { - stun_debug (" switching role from "controll%s" to "controll%s"", - *control ? "ing" : "ed", *control ? "ed" : "ing"); -@@ -279,10 +287,21 @@ stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, - stun_debug (" staying "controll%s" (sending error)", - *control ? "ing" : "ed"); - err (STUN_ERROR_ROLE_CONFLICT); -- return STUN_USAGE_ICE_RETURN_SUCCESS; -+ return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; - } - } else { -- stun_debug ("STUN Role not specified by peer!"); -+ if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED -+ : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS) -+ { -+ /* we don't have the expected ice-controlling/controlled -+ * attribute -+ */ -+ if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 || -+ compatibility == STUN_USAGE_ICE_COMPATIBILITY_WLM2009) -+ { -+ stun_debug ("STUN Role not specified by peer!"); -+ } -+ } - } - - if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) { -diff --git a/stun/usages/timer.c b/stun/usages/timer.c -index 82f3ea2..2862ab8 100644 ---- a/stun/usages/timer.c -+++ b/stun/usages/timer.c -@@ -104,7 +104,7 @@ void stun_timer_start (StunTimer *timer, unsigned int initial_timeout, - unsigned int max_retransmissions) - { - stun_gettime (&timer->deadline); -- timer->retransmissions = 0; -+ timer->retransmissions = 1; - timer->delay = initial_timeout; - timer->max_retransmissions = max_retransmissions; - add_delay (&timer->deadline, timer->delay); -diff --git a/stun/usages/timer.h b/stun/usages/timer.h -index e6501cb..e74353b 100644 ---- a/stun/usages/timer.h -+++ b/stun/usages/timer.h -@@ -130,15 +130,18 @@ struct stun_timer_s { - * STUN_TIMER_DEFAULT_TIMEOUT: - * - * 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. - */ --#define STUN_TIMER_DEFAULT_TIMEOUT 600 -+#define STUN_TIMER_DEFAULT_TIMEOUT 200 - - /** - * STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS: - * - * The default maximum retransmissions allowed before a timer decides to timeout - */ --#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 3 -+#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 7 - - /** - * STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT: -diff --git a/stun/usages/turn.c b/stun/usages/turn.c -index f242650..3b94959 100644 ---- a/stun/usages/turn.c -+++ b/stun/usages/turn.c -@@ -152,7 +152,9 @@ size_t stun_usage_turn_create (StunAgent *agent, StunMessage *msg, - } - } - -- if (username != NULL && username_len > 0) { -+ if (username != NULL && username_len > 0 && -+ (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS || -+ previous_response)) { - if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, - username, username_len) != STUN_MESSAGE_RETURN_SUCCESS) - return 0; -@@ -205,7 +207,9 @@ size_t stun_usage_turn_create_refresh (StunAgent *agent, StunMessage *msg, - } - - -- if (username != NULL && username_len > 0) { -+ if (username != NULL && username_len > 0 && -+ (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS || -+ previous_response)) { - if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, - username, username_len) != STUN_MESSAGE_RETURN_SUCCESS) - return 0; -@@ -251,7 +255,9 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, - } - - /* username */ -- if (username != NULL) { -+ if (username != NULL && -+ (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS || -+ (nonce != NULL && realm != NULL))) { - if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, - username, username_len) != STUN_MESSAGE_RETURN_SUCCESS) - return 0; -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 3644091..7bfe075 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -22,6 +22,10 @@ AM_CFLAGS = \ - -I $(top_srcdir)/stun - AM_CPPFLAGS = -DG_LOG_DOMAIN="libnice-tests" - -+AM_TESTS_ENVIRONMENT = \ -+ G_MESSAGES_DEBUG=all \ -+ NICE_DEBUG=all; -+ - COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) - - check_PROGRAMS = \ -@@ -39,6 +43,7 @@ check_PROGRAMS = \ - test-io-stream-cancelling \ - test-io-stream-pollable \ - test-send-recv \ -+ test-socket-is-based-on \ - test-priority \ - test-mainloop \ - test-fullmode \ -@@ -49,7 +54,8 @@ check_PROGRAMS = \ - test-new-dribble \ - test-tcp \ - test-icetcp \ -- test-credentials -+ test-credentials \ -+ test-turn - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -99,6 +105,8 @@ test_io_stream_pollable_LDADD = $(COMMON_LDADD) - test_send_recv_SOURCES = test-send-recv.c test-io-stream-common.c - test_send_recv_LDADD = $(COMMON_LDADD) - -+test_socket_is_based_on_LDADD = $(COMMON_LDADD) -+ - test_priority_LDADD = $(COMMON_LDADD) - - test_mainloop_LDADD = $(COMMON_LDADD) -@@ -119,6 +127,8 @@ test_icetcp_LDADD = $(COMMON_LDADD) - - test_credentials_LDADD = $(COMMON_LDADD) - -+test_turn_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-add-remove-stream.c b/tests/test-add-remove-stream.c -index 5ed3463..c97bc7b 100644 ---- a/tests/test-add-remove-stream.c -+++ b/tests/test-add-remove-stream.c -@@ -54,8 +54,6 @@ main (void) - WSAStartup(0x0202, &w); - #endif - nice_address_init (&addr); -- g_type_init (); -- g_thread_init (NULL); - - if (!nice_address_set_from_string (&addr, "127.0.0.1")) - g_assert_not_reached (); -diff --git a/tests/test-bsd.c b/tests/test-bsd.c -index 6b747d0..c1ddf53 100644 ---- a/tests/test-bsd.c -+++ b/tests/test-bsd.c -@@ -368,8 +368,6 @@ test_multi_message_recv (guint n_sends, guint n_receives, - int - main (void) - { -- g_type_init (); -- - test_socket_initial_properties (); - test_socket_address_properties (); - test_simple_send_recv (); -diff --git a/tests/test-build-io-stream.c b/tests/test-build-io-stream.c -index 0c9c593..a3478ed 100644 ---- a/tests/test-build-io-stream.c -+++ b/tests/test-build-io-stream.c -@@ -440,8 +440,6 @@ main (void) - WSAStartup (0x0202, &w); - #endif - nice_address_init (&addr); -- g_type_init (); -- g_thread_init (NULL); - - g_assert (nice_address_set_from_string (&addr, "127.0.0.1")); - -diff --git a/tests/test-credentials.c b/tests/test-credentials.c -index f678afa..1de4e49 100644 ---- a/tests/test-credentials.c -+++ b/tests/test-credentials.c -@@ -172,8 +172,6 @@ int main (void) - WSADATA w; - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - loop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-dribble.c b/tests/test-dribble.c -index 6603129..d9de07b 100644 ---- a/tests/test-dribble.c -+++ b/tests/test-dribble.c -@@ -215,9 +215,6 @@ int main (void) - WSAStartup(0x0202, &w); - #endif - -- g_type_init (); -- g_thread_init (NULL); -- - global_mainloop = g_main_loop_new (NULL, FALSE); - - /* step: create the agents L and R */ -diff --git a/tests/test-fallback.c b/tests/test-fallback.c -index f97cb0d..34fd1d1 100644 ---- a/tests/test-fallback.c -+++ b/tests/test-fallback.c -@@ -491,8 +491,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-fullmode.c b/tests/test-fullmode.c -index 03778f7..b599a24 100644 ---- a/tests/test-fullmode.c -+++ b/tests/test-fullmode.c -@@ -834,8 +834,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init(NULL); - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-icetcp.c b/tests/test-icetcp.c -index 7ab3563..5b2b4b2 100644 ---- a/tests/test-icetcp.c -+++ b/tests/test-icetcp.c -@@ -125,6 +125,14 @@ static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpoin - (void)agent; - } - -+static void check_loop_quit_condition (void) -+{ -+ if (global_ready_reached && -+ global_lagent_cands >= 2 && global_ragent_cands >= 2) { -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ - static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) - { - gboolean ready_to_connected = FALSE; -@@ -158,10 +166,10 @@ static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint - global_ready_reached == FALSE) { - g_debug ("Components ready/failed achieved. Stopping mailoop"); - global_ready_reached = TRUE; -- g_main_loop_quit (global_mainloop); -- return; - } - -+ check_loop_quit_condition (); -+ - #if 0 - /* signal status via a global variable */ - if (global_components_failed == global_components_failed_exit) { -@@ -184,6 +192,8 @@ static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint compon - else if (GPOINTER_TO_UINT (data) == 2) - ++global_ragent_cands; - -+ check_loop_quit_condition (); -+ - /* XXX: dear compiler, these are for you: */ - (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; - } -@@ -397,10 +407,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); --#if !GLIB_CHECK_VERSION(2,31,8) -- g_thread_init(NULL); --#endif - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-io-stream-cancelling.c b/tests/test-io-stream-cancelling.c -index 55fc1f3..d1b9a89 100644 ---- a/tests/test-io-stream-cancelling.c -+++ b/tests/test-io-stream-cancelling.c -@@ -112,8 +112,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - l_data.cancellable = g_cancellable_new (); - l_data.blocking = FALSE; -diff --git a/tests/test-io-stream-closing-read.c b/tests/test-io-stream-closing-read.c -index ec434dd..5acddec 100644 ---- a/tests/test-io-stream-closing-read.c -+++ b/tests/test-io-stream-closing-read.c -@@ -127,8 +127,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL); - -diff --git a/tests/test-io-stream-closing-write.c b/tests/test-io-stream-closing-write.c -index 97e8347..6f92f28 100644 ---- a/tests/test-io-stream-closing-write.c -+++ b/tests/test-io-stream-closing-write.c -@@ -127,8 +127,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL); - -diff --git a/tests/test-io-stream-common.c b/tests/test-io-stream-common.c -index bd0f3b5..efa6160 100644 ---- a/tests/test-io-stream-common.c -+++ b/tests/test-io-stream-common.c -@@ -335,12 +335,7 @@ spawn_thread (const gchar *thread_name, GThreadFunc thread_func, - { - GThread *thread; - --#if !GLIB_CHECK_VERSION(2, 31, 8) -- thread = g_thread_create (thread_func, user_data, TRUE, NULL); --#else - thread = g_thread_new (thread_name, thread_func, user_data); --#endif -- - g_assert (thread); - - return thread; -diff --git a/tests/test-io-stream-pollable.c b/tests/test-io-stream-pollable.c -index 614a546..a543f88 100644 ---- a/tests/test-io-stream-pollable.c -+++ b/tests/test-io-stream-pollable.c -@@ -159,8 +159,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - l_data = g_malloc0 (sizeof (ThreadData)); - r_data = g_malloc0 (sizeof (ThreadData)); -diff --git a/tests/test-io-stream-thread.c b/tests/test-io-stream-thread.c -index db14fe4..73ecd76 100644 ---- a/tests/test-io-stream-thread.c -+++ b/tests/test-io-stream-thread.c -@@ -124,8 +124,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - l_data = g_malloc0 (sizeof (ThreadData)); - r_data = g_malloc0 (sizeof (ThreadData)); -diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c -index b4e4d05..7c52daa 100644 ---- a/tests/test-mainloop.c -+++ b/tests/test-mainloop.c -@@ -72,8 +72,6 @@ main (void) - guint stream; - - nice_address_init (&addr); -- g_type_init (); -- g_thread_init(NULL); - - loop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-new-dribble.c b/tests/test-new-dribble.c -index 395e275..3e60ae3 100644 ---- a/tests/test-new-dribble.c -+++ b/tests/test-new-dribble.c -@@ -56,21 +56,14 @@ - #define LEFT_AGENT GINT_TO_POINTER(1) - #define RIGHT_AGENT GINT_TO_POINTER(2) - --#if !GLIB_CHECK_VERSION(2,31,8) -- static GMutex *stun_mutex_ptr = NULL; -- static GCond *stun_signal_ptr = NULL; -- static GMutex *stun_thread_mutex_ptr = NULL; -- static GCond *stun_thread_signal_ptr = NULL --#else -- static GMutex stun_mutex; -- static GMutex *stun_mutex_ptr = &stun_mutex; -- static GCond stun_signal; -- static GCond *stun_signal_ptr = &stun_signal; -- static GMutex stun_thread_mutex; -- static GMutex *stun_thread_mutex_ptr = &stun_thread_mutex; -- static GCond stun_thread_signal; -- static GCond *stun_thread_signal_ptr = &stun_thread_signal; --#endif -+static GMutex stun_mutex; -+static GMutex *stun_mutex_ptr = &stun_mutex; -+static GCond stun_signal; -+static GCond *stun_signal_ptr = &stun_signal; -+static GMutex stun_thread_mutex; -+static GMutex *stun_thread_mutex_ptr = &stun_thread_mutex; -+static GCond stun_thread_signal; -+static GCond *stun_thread_signal_ptr = &stun_thread_signal; - - static NiceComponentState global_lagent_state = NICE_COMPONENT_STATE_LAST; - static NiceComponentState global_ragent_state = NICE_COMPONENT_STATE_LAST; -@@ -530,6 +523,10 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent) - - g_assert (lagent_candidate_gathering_done); - -+ while (global_ragent_state < NICE_COMPONENT_STATE_CONNECTED) -+ g_main_context_iteration (NULL, TRUE); -+ g_cancellable_reset (global_cancellable); -+ - g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY); - g_assert (global_ragent_state >= NICE_COMPONENT_STATE_CONNECTED); - -@@ -725,8 +722,6 @@ int main(void) - GSource *src; - int sock; - -- g_type_init(); -- - global_cancellable = g_cancellable_new (); - src = g_cancellable_source_new (global_cancellable); - g_source_set_dummy_callback (src); -@@ -739,16 +734,8 @@ int main(void) - } - - --#if !GLIB_CHECK_VERSION(2,31,8) -- g_thread_init (NULL); -- stun_thread = g_thread_create (stun_thread_func, GINT_TO_POINTER (sock), -- TRUE, NULL); -- stun_mutex_ptr = g_mutex_new (); -- stun_signal_ptr = g_cond_new (); --#else - stun_thread = g_thread_new ("listen for STUN requests", - stun_thread_func, GINT_TO_POINTER (sock)); --#endif - - // Once the the thread is forked, we want to listen for a signal - // that the socket was opened successfully -@@ -803,10 +790,6 @@ int main(void) - g_object_unref (ragent); - - g_thread_join (stun_thread); --#if !GLIB_CHECK_VERSION(2,31,8) -- g_mutex_free (stun_mutex_ptr); -- g_cond_free (stun_signal_ptr); --#endif - g_object_unref (global_cancellable); - - g_source_destroy (src); -diff --git a/tests/test-priority.c b/tests/test-priority.c -index f7d3273..1700dd0 100644 ---- a/tests/test-priority.c -+++ b/tests/test-priority.c -@@ -47,38 +47,41 @@ main (void) - { - NiceCandidate *candidate; - -- /* test 1 */ - candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST); -- g_assert (nice_candidate_jingle_priority (candidate) == 1000); -+ nice_address_set_from_string (&candidate->addr, "127.0.0.1"); -+ nice_address_set_from_string (&candidate->base_addr, "127.0.0.1"); -+ -+ /* test 1 */ -+ g_assert_cmpuint (nice_candidate_jingle_priority (candidate), ==, 1000); - /* Host UDP */ - candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP; - candidate->component_id = 1; -- g_assert (nice_candidate_ice_priority (candidate, FALSE, FALSE) == 0x780001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, FALSE, FALSE), ==, 0x780001FF); - /* Host UDP reliable */ -- g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x3C0001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE), ==, 0x3C0001FF); - /* Host tcp-active unreliable */ - candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE; -- g_assert (nice_candidate_ice_priority (candidate, FALSE, FALSE) == 0x3CC001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, FALSE, FALSE) & 0xFFE000FF, ==, 0x3CC000FF); - /* Host tcp-active reliable */ - candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE; - /* Host tcp-active reliable */ -- g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x78C001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE) & 0xFFE000FF, ==, 0x78C000FF); - /* srv-reflexive tcp-active reliable */ - candidate->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE; - candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE; -- g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x648001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE) & 0xFFE000FF, ==, 0x648000FF); - /* nat-assisted srv-reflexive tcp-active reliable */ -- g_assert (nice_candidate_ice_priority (candidate, TRUE, TRUE) == 0x698001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, TRUE) & 0xFFE000FF, ==, 0x698000FF); - nice_candidate_free (candidate); - - /* test 2 */ - /* 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0) - = 2^32*1 + 2*5000 + 0 - = 4294977296 */ -- g_assert (nice_candidate_pair_priority (1,5000) == 4294977296LL); -+ g_assert_cmpuint (nice_candidate_pair_priority (1,5000), ==, 4294977296LL); - - /* 2^32*1 + 2*5000 + 1 = 4294977297 */ -- g_assert (nice_candidate_pair_priority (5000, 1) == 4294977297LL); -+ g_assert_cmpuint (nice_candidate_pair_priority (5000, 1), ==, 4294977297LL); - - return 0; - } -diff --git a/tests/test-pseudotcp-fin.c b/tests/test-pseudotcp-fin.c -index b769161..d240c96 100644 ---- a/tests/test-pseudotcp-fin.c -+++ b/tests/test-pseudotcp-fin.c -@@ -1,7 +1,7 @@ - /* - * This file is part of the Nice GLib ICE library. - * -- * (C) 2014 Collabora Ltd. -+ * © 2014, 2015 Collabora Ltd. - * Contact: Philip Withnall - * - * The contents of this file are subject to the Mozilla Public License Version -@@ -269,6 +269,13 @@ expect_syn_received (Data *data) - expect_segment (data->right, data->right_sent, 0, 7, 7, FLAG_SYN); - } - -+static void -+assert_empty_queues (Data *data) -+{ -+ g_assert_cmpuint (g_queue_get_length (data->left_sent), ==, 0); -+ g_assert_cmpuint (g_queue_get_length (data->right_sent), ==, 0); -+} -+ - /* Return whether the socket accepted the packet. */ - static gboolean - forward_segment (GQueue/*<owned GBytes>*/ *from, PseudoTcpSocket *to) -@@ -453,6 +460,8 @@ establish_connection (Data *data) - expect_ack (data->left, data->left_sent, 7, 7); - forward_segment_ltr (data); - expect_sockets_connected (data); -+ -+ assert_empty_queues (data); - } - - /* Helper to close the LHS of a socket pair which has not transmitted any -@@ -638,7 +647,7 @@ pseudotcp_close_normal_recovery1 (void) - expect_fin (data.left, data.left_sent, 7, 7); - drop_segment (data.left, data.left_sent); - -- increment_time_both (&data, 300); /* retransmit timeout */ -+ increment_time_both (&data, 1100); /* retransmit timeout */ - - expect_fin (data.left, data.left_sent, 7, 7); - forward_segment_ltr (&data); -@@ -673,7 +682,7 @@ pseudotcp_close_normal_recovery2 (void) - - expect_ack (data.right, data.right_sent, 7, 8); - drop_segment (data.right, data.right_sent); -- increment_time_both (&data, 300); /* retransmit timeout */ -+ increment_time_both (&data, 1100); /* retransmit timeout */ - expect_fin (data.left, data.left_sent, 7, 7); - forward_segment_ltr (&data); - expect_ack (data.right, data.right_sent, 7, 8); -@@ -753,6 +762,76 @@ pseudotcp_close_normal_recovery4 (void) - data_clear (&data); - } - -+/* Check that closing a connection recovers from a data segment being dropped -+ * immediately before the first FIN is sent. Based on: RFC 793, Figure 13. */ -+static void -+pseudotcp_close_normal_recovery_data (void) -+{ -+ Data data = { 0, }; -+ -+ /* Establish a connection. */ -+ establish_connection (&data); -+ -+ /* Send some data from LHS to RHS, but drop the segment. */ -+ g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3); -+ expect_data (data.left, data.left_sent, 7, 7, 3); -+ drop_segment (data.left, data.left_sent); -+ -+ assert_empty_queues(&data); -+ -+ /* Close the LHS. */ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 0); -+ close_socket (data.left); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_1); -+ expect_fin (data.left, data.left_sent, 10, 7); -+ forward_segment_ltr (&data); -+ -+ expect_socket_state (data.right, TCP_ESTABLISHED); -+ expect_ack (data.right, data.right_sent, 7, 7); -+ forward_segment_rtl (&data); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_1); -+ -+ assert_empty_queues(&data); -+ -+ /* Close the RHS. */ -+ close_socket (data.right); -+ -+ expect_socket_state (data.right, TCP_FIN_WAIT_1); -+ -+ expect_fin (data.right, data.right_sent, 7, 7); -+ forward_segment_rtl (&data); -+ -+ expect_socket_state (data.left, TCP_CLOSING); -+ -+ expect_ack (data.left, data.left_sent, 11, 8); -+ forward_segment_ltr (&data); -+ -+ expect_socket_state (data.right, TCP_FIN_WAIT_2); -+ -+ expect_data (data.right, data.right_sent, 8, 7, 0); -+ forward_segment_rtl (&data); -+ expect_socket_state (data.left, TCP_CLOSING); -+ -+ expect_data (data.left, data.left_sent, 7, 8, 3); -+ forward_segment_ltr (&data); -+ expect_socket_state (data.right, TCP_TIME_WAIT); -+ -+ increment_time_both (&data, 100); /* Delayed ACK */ -+ -+ expect_ack (data.right, data.right_sent, 8, 11); -+ forward_segment_rtl (&data); -+ expect_socket_state (data.left, TCP_TIME_WAIT); -+ -+ increment_time_both (&data, 10); /* TIME-WAIT */ -+ -+ expect_sockets_closed (&data); -+ -+ data_clear (&data); -+} -+ - /* Check that if both FIN segments from a simultaneous FIN handshake are - * dropped, the handshake recovers and completes successfully. - * See: RFC 793, Figure 14. */ -@@ -773,7 +852,7 @@ pseudotcp_close_simultaneous_recovery1 (void) - drop_segment (data.left, data.left_sent); - drop_segment (data.right, data.right_sent); - -- increment_time_both (&data, 400); /* retransmit timeout */ -+ increment_time_both (&data, 1200); /* retransmit timeout */ - - expect_fin (data.left, data.left_sent, 7, 7); - expect_fin (data.right, data.right_sent, 7, 7); -@@ -817,7 +896,7 @@ pseudotcp_close_simultaneous_recovery2 (void) - drop_segment (data.left, data.left_sent); - drop_segment (data.right, data.right_sent); - -- increment_time_both (&data, 400); /* retransmit timeout */ -+ increment_time_both (&data, 1200); /* retransmit timeout */ - - expect_fin (data.left, data.left_sent, 7, 8); - expect_fin (data.right, data.right_sent, 7, 8); -@@ -977,11 +1056,14 @@ pseudotcp_close_rst_afterwards (void) - - /* Close the LHS. */ - g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ pseudo_tcp_socket_close (data.left, TRUE); - close_socket (data.left); - -- expect_fin (data.left, data.left_sent, 7, 7); -+ expect_rst (data.left, data.left_sent, 7, 7); - drop_segment (data.left, data.left_sent); /* just to get it out of the way */ - -+ assert_empty_queues(&data); -+ - /* Send some data from RHS to LHS, which should result in an RST. */ - g_assert_cmpint (pseudo_tcp_socket_send (data.right, "foo", 3), ==, 3); - expect_data (data.right, data.right_sent, 7, 7, 3); -@@ -1059,6 +1141,63 @@ pseudotcp_compatibility (void) - data_clear (&data); - } - -+ -+/* Check that after receiving a FIN, queued data can still be read */ -+static void -+pseudotcp_close_recv_queued (void) -+{ -+ Data data = { 0, }; -+ guint8 buf[100]; -+ -+ /* Establish a connection. */ -+ establish_connection (&data); -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.right), >, -+ 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.left), >, -+ 0); -+ -+ g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3); -+ expect_data (data.left, data.left_sent, 7, 7, 3); -+ forward_segment_ltr (&data); -+ -+ increment_time_both (&data, 100); /* Delayed ACK */ -+ expect_ack (data.right, data.right_sent, 7, 10); -+ forward_segment_rtl (&data); -+ -+ close_socket (data.left); -+ expect_fin (data.left, data.left_sent, 10, 7); -+ forward_segment_ltr (&data); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_1); -+ expect_socket_state (data.right, TCP_CLOSE_WAIT); -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.left), ==, -+ 0); -+ -+ expect_ack (data.right, data.right_sent, 7, 11); -+ forward_segment_rtl (&data); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_2); -+ -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 3); -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.right), >, -+ 0); -+ -+ /* Check that the data can be read */ -+ g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 3); -+ -+ /* Now the socket should be empty */ -+ g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 0); -+ -+ data_clear (&data); -+} -+ - int - main (int argc, char *argv[]) - { -@@ -1109,6 +1248,8 @@ main (int argc, char *argv[]) - pseudotcp_close_normal_recovery3); - g_test_add_func ("/pseudotcp/close/normal/recovery4", - pseudotcp_close_normal_recovery4); -+ g_test_add_func ("/pseudotcp/close/normal/recovery-data", -+ pseudotcp_close_normal_recovery_data); - g_test_add_func ("/pseudotcp/close/simultaneous/recovery1", - pseudotcp_close_simultaneous_recovery1); - g_test_add_func ("/pseudotcp/close/simultaneous/recovery2", -@@ -1125,6 +1266,9 @@ main (int argc, char *argv[]) - g_test_add_func ("/pseudotcp/close/rst-afterwards", - pseudotcp_close_rst_afterwards); - -+ g_test_add_func ("/pseudotcp/close/recv-queued", -+ pseudotcp_close_recv_queued); -+ - g_test_add_func ("/pseudotcp/compatibility", - pseudotcp_compatibility); - -diff --git a/tests/test-pseudotcp-fuzzy.c b/tests/test-pseudotcp-fuzzy.c -index fdee222..4211248 100644 ---- a/tests/test-pseudotcp-fuzzy.c -+++ b/tests/test-pseudotcp-fuzzy.c -@@ -395,7 +395,6 @@ int main (int argc, char *argv[]) - GError *error = NULL; - - setlocale (LC_ALL, ""); -- g_type_init (); - - /* Configuration. */ - context = g_option_context_new ("— fuzz-test the pseudotcp socket"); -diff --git a/tests/test-pseudotcp.c b/tests/test-pseudotcp.c -index e4dd613..1a8391a 100644 ---- a/tests/test-pseudotcp.c -+++ b/tests/test-pseudotcp.c -@@ -259,8 +259,6 @@ int main (int argc, char *argv[]) - - mainloop = g_main_loop_new (NULL, FALSE); - -- g_type_init (); -- - pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE); - - left_closed = right_closed = FALSE; -diff --git a/tests/test-restart.c b/tests/test-restart.c -index c7f2f25..c2cbe9a 100644 ---- a/tests/test-restart.c -+++ b/tests/test-restart.c -@@ -400,8 +400,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init(NULL); - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-send-recv.c b/tests/test-send-recv.c -index 55e6002..5841639 100644 ---- a/tests/test-send-recv.c -+++ b/tests/test-send-recv.c -@@ -1202,8 +1202,6 @@ main (int argc, char *argv[]) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - if (!long_mode) { - /* Quick mode. Just test each of the stream APIs in reliable and -diff --git a/tests/test-socket-is-based-on.c b/tests/test-socket-is-based-on.c -new file mode 100644 -index 0000000..6080fe3 ---- /dev/null -+++ b/tests/test-socket-is-based-on.c -@@ -0,0 +1,122 @@ -+/* -+ * This file is part of the Nice GLib ICE library. -+ * -+ * (C) 2016 Jakub Adam jakub.adam@ktknet.cz -+ * -+ * 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. -+ * -+ * 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 "config.h" -+#endif -+ -+#include <locale.h> -+ -+#include "socket.h" -+ -+static NiceSocket *udp_bsd; -+static NiceSocket *tcp_active; -+static NiceSocket *pseudossl; -+static NiceSocket *udp_turn_over_tcp; -+ -+static void -+socket_base_udp_bsd (void) -+{ -+ g_assert (nice_socket_is_based_on (udp_bsd, udp_bsd)); -+ g_assert (!nice_socket_is_based_on (udp_bsd, tcp_active)); -+ g_assert (!nice_socket_is_based_on (udp_bsd, pseudossl)); -+ g_assert (!nice_socket_is_based_on (udp_bsd, udp_turn_over_tcp)); -+} -+ -+static void -+socket_base_tcp_active (void) -+{ -+ g_assert (!nice_socket_is_based_on (tcp_active, udp_bsd)); -+ g_assert (nice_socket_is_based_on (tcp_active, tcp_active)); -+ g_assert (!nice_socket_is_based_on (tcp_active, pseudossl)); -+ g_assert (!nice_socket_is_based_on (tcp_active, udp_turn_over_tcp)); -+} -+ -+static void -+socket_base_pseudossl (void) -+{ -+ g_assert (!nice_socket_is_based_on (pseudossl, udp_bsd)); -+ g_assert (nice_socket_is_based_on (pseudossl, tcp_active)); -+ g_assert (nice_socket_is_based_on (pseudossl, pseudossl)); -+ g_assert (!nice_socket_is_based_on (pseudossl, udp_turn_over_tcp)); -+} -+ -+static void -+socket_base_udp_turn_over_tcp (void) -+{ -+ g_assert (!nice_socket_is_based_on (udp_turn_over_tcp, udp_bsd)); -+ g_assert (nice_socket_is_based_on (udp_turn_over_tcp, tcp_active)); -+ g_assert (nice_socket_is_based_on (udp_turn_over_tcp, pseudossl)); -+ g_assert (nice_socket_is_based_on (udp_turn_over_tcp, udp_turn_over_tcp)); -+} -+ -+int -+main (int argc, char *argv[]) -+{ -+ GMainLoop *mainloop = NULL; -+ -+ NiceAddress addr; -+ -+ setlocale (LC_ALL, ""); -+ g_test_init (&argc, &argv, NULL); -+ -+ mainloop = g_main_loop_new (NULL, TRUE); -+ -+ nice_address_set_from_string (&addr, "127.0.0.1"); -+ -+ /* Standalone socket */ -+ udp_bsd = nice_udp_bsd_socket_new (&addr); -+ -+ /* tcp_passive -> pseudossl -> udp_turn_over_tcp */ -+ tcp_active = nice_tcp_active_socket_new (g_main_loop_get_context (mainloop), -+ &addr); -+ pseudossl = nice_pseudossl_socket_new (tcp_active, -+ NICE_PSEUDOSSL_SOCKET_COMPATIBILITY_GOOGLE); -+ udp_turn_over_tcp = nice_udp_turn_over_tcp_socket_new (pseudossl, -+ NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE); -+ -+ g_test_add_func ("/socket/is-base-of/udp-bsd", -+ socket_base_udp_bsd); -+ g_test_add_func ("/socket/is-base-of/tcp-active", -+ socket_base_tcp_active); -+ g_test_add_func ("/socket/is-base-of/pseudossl", -+ socket_base_pseudossl); -+ g_test_add_func ("/socket/is-base-of/udp-turn-over-tcp", -+ socket_base_udp_turn_over_tcp); -+ -+ g_test_run (); -+ -+ nice_socket_free (udp_bsd); -+ nice_socket_free (udp_turn_over_tcp); -+ -+ g_main_loop_unref (mainloop); -+ -+ return 0; -+} -diff --git a/tests/test-tcp.c b/tests/test-tcp.c -index dd259a4..e2a1bfd 100644 ---- a/tests/test-tcp.c -+++ b/tests/test-tcp.c -@@ -88,8 +88,6 @@ main (void) - NiceAddress active_bind_addr, passive_bind_addr; - GSource *srv_listen_source, *srv_input_source, *cli_input_source; - -- g_type_init (); -- - mainloop = g_main_loop_new (NULL, FALSE); - - nice_address_init (&active_bind_addr); -diff --git a/tests/test-thread.c b/tests/test-thread.c -index df0b145..7493f97 100644 ---- a/tests/test-thread.c -+++ b/tests/test-thread.c -@@ -211,8 +211,6 @@ int main (void) - WSADATA w; - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init(NULL); - - lmainctx = g_main_context_new (); - rmainctx = g_main_context_new (); -@@ -291,13 +289,8 @@ int main (void) - /* step: run test the first time */ - g_debug ("test-thread: TEST STARTS / running test for the 1st time"); - --#if !GLIB_CHECK_VERSION(2,31,8) -- lthread = g_thread_create (mainloop_thread, lmainloop, TRUE, NULL); -- rthread = g_thread_create (mainloop_thread, rmainloop, TRUE, NULL); --#else - lthread = g_thread_new ("lthread libnice", mainloop_thread, lmainloop); - rthread = g_thread_new ("rthread libnice", mainloop_thread, rmainloop); --#endif - - g_assert (lthread); - g_assert (rthread); -@@ -318,13 +311,9 @@ int main (void) - nice_agent_attach_recv (ragent, rs_id, 1, rdmainctx, cb_nice_recv, - GUINT_TO_POINTER (2)); - --#if !GLIB_CHECK_VERSION(2,31,8) -- ldthread = g_thread_create (mainloop_thread, ldmainloop, TRUE, NULL); -- rdthread = g_thread_create (mainloop_thread, rdmainloop, TRUE, NULL); --#else - ldthread = g_thread_new ("ldthread libnice", mainloop_thread, ldmainloop); - rdthread = g_thread_new ("rdthread libnice", mainloop_thread, rdmainloop); --#endif -+ - g_assert (ldthread); - g_assert (rdthread); - -diff --git a/tests/test-turn.c b/tests/test-turn.c -new file mode 100644 -index 0000000..46d1bf2 ---- /dev/null -+++ b/tests/test-turn.c -@@ -0,0 +1,379 @@ -+#include <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <gio/gio.h> -+#include <agent.h> -+ -+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; -+ -+#define TURN_USER "toto" -+#define TURN_PASS "password" -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-turn:%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-fullmode:%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; -+ -+#if 0 -+ if (GPOINTER_TO_UINT (user_data) == 2) { -+ global_ragent_read += len; -+ } -+#endif -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-fullmode:%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-fullmode:%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-turn: 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-turn:%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, gboolean remove_non_relay, -+ gboolean force_relay) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ if (remove_non_relay) { -+ restart: -+ for (i = cands; i; i = i->next) { -+ NiceCandidate *cand = i->data; -+ if (force_relay) -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_RELAYED); -+ if (cand->type != NICE_CANDIDATE_TYPE_RELAYED) { -+ cands = g_slist_remove (cands, cand); -+ nice_candidate_free (cand); -+ goto restart; -+ } -+ } -+ } -+ 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(guint turn_port, gboolean is_ipv6, -+ gboolean ice_udp, gboolean ice_tcp, gboolean force_relay, -+ gboolean remove_non_relay, -+ NiceRelayType turn_type) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ const gchar *localhost; -+ NiceAddress localaddr; -+ guint ls_id, rs_id; -+ gulong timer_id; -+ -+ if (is_ipv6) -+ localhost = "::1"; -+ else -+ 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 (NULL, NICE_COMPATIBILITY_RFC5245); -+ ragent = nice_agent_new (NULL, NICE_COMPATIBILITY_RFC5245); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", ice_tcp, "ice-udp", ice_udp, -+ "force-relay", force_relay, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", ice_tcp, "ice-udp", ice_udp, -+ "force-relay", force_relay, 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-turn, Left Agent"); -+ nice_agent_set_software (ragent, "Test-turn, 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); -+ nice_agent_set_relay_info(lagent, ls_id, 1, -+ localhost, turn_port, TURN_USER, TURN_PASS, turn_type); -+ nice_agent_set_relay_info(ragent, rs_id, 1, -+ localhost, turn_port, TURN_USER, TURN_PASS, turn_type); -+ -+ /* 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_assert (global_lagent_gathering_done == FALSE); -+ g_assert (global_ragent_gathering_done == FALSE); -+ g_debug ("test-turn: Added streams, running context until 'candidate-gathering-done'..."); -+ while (!global_lagent_gathering_done && !global_ragent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_gathering_done == 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, -+ remove_non_relay, force_relay); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ remove_non_relay, force_relay); -+ -+ 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); -+} -+ -+guint global_turn_port; -+ -+static void -+udp_no_force_no_remove_udp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_UDP); -+} -+ -+static void -+udp_no_force_remove_udp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ TRUE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_UDP); -+} -+ -+static void -+udp_force_no_remove_udp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ TRUE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_UDP); -+} -+ -+static void -+udp_no_force_no_remove_tcp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_TCP); -+} -+ -+static void -+udp_no_force_remove_tcp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ TRUE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_TCP); -+} -+ -+static void -+udp_force_no_remove_tcp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ TRUE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_TCP); -+} -+ -+ -+ -+ -+ -+int -+main (int argc, char **argv) -+{ -+ GSubprocess *sp; -+ GError *error = NULL; -+ gchar portstr[10]; -+ int ret; -+ gchar *out_str = NULL; -+ gchar *err_str = NULL; -+ -+ g_test_init (&argc, &argv, NULL); -+ -+ global_turn_port = g_random_int_range (10000, 60000); -+ snprintf(portstr, 9, "%u", global_turn_port); -+ -+ if (g_spawn_command_line_sync ("turnserver --help", &out_str, &err_str, NULL, -+ NULL) && err_str) { -+ if (!strstr(err_str, "--user")) { -+ g_print ("rfc5766-turn-server not installed, skipping turn test\n"); -+ return 0; -+ } -+ } else { -+ g_print ("rfc5766-turn-server not installed, skipping turn test\n"); -+ return 0; -+ } -+ g_free (err_str); -+ g_free (out_str); -+ -+ sp = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, &error, -+ "turnserver", -+ "--user", "toto:0xaae440b3348d50265b63703117c7bfd5", -+ "--realm", "realm", -+ "--listening-port", portstr, -+ NULL); -+ -+ g_test_add_func ("/nice/turn/udp", udp_no_force_no_remove_udp); -+ g_test_add_func ("/nice/turn/udp/remove_non_turn", -+ udp_no_force_remove_udp); -+ g_test_add_func ("/nice/turn/udp/force_relay", -+ udp_force_no_remove_udp); -+ g_test_add_func ("/nice/turn/udp/over-tcp", udp_no_force_no_remove_tcp); -+ g_test_add_func ("/nice/turn/udp/over-tcp/remove_non_turn", -+ udp_no_force_remove_tcp); -+ g_test_add_func ("/nice/turn/udp/over-tcp/force_relay", -+ udp_force_no_remove_tcp); -+ -+ ret = g_test_run (); -+ -+ g_subprocess_force_exit (sp); -+ g_subprocess_wait (sp, NULL, NULL); -+ g_clear_object (&sp); -+ -+ return ret; -+} -diff --git a/tests/test.c b/tests/test.c -index 31a9fc7..a92b33c 100644 ---- a/tests/test.c -+++ b/tests/test.c -@@ -75,9 +75,6 @@ main (void) - - nice_address_init (&addr_local); - nice_address_init (&addr_remote); -- g_type_init (); -- -- g_thread_init(NULL); - - g_assert (nice_address_set_from_string (&addr_local, "127.0.0.1")); - g_assert (nice_address_set_from_string (&addr_remote, "127.0.0.1")); -diff --git a/win32/vs9/libnice.def b/win32/vs9/libnice.def -deleted file mode 100644 -index 7065330..0000000 ---- a/win32/vs9/libnice.def -+++ /dev/null -@@ -1,137 +0,0 @@ --LIBRARY libnice -- --EXPORTS -- --nice_address_copy_to_sockaddr --nice_address_dup --nice_address_equal --nice_address_equal_no_port --nice_address_free --nice_address_get_port --nice_address_init --nice_address_ip_version --nice_address_is_private --nice_address_is_valid --nice_address_new --nice_address_set_from_sockaddr --nice_address_set_from_string --nice_address_set_ipv4 --nice_address_set_ipv6 --nice_address_set_port --nice_address_to_string --nice_agent_add_local_address --nice_agent_add_stream --nice_agent_attach_recv --nice_agent_forget_relays --nice_agent_gather_candidates --nice_agent_generate_local_candidate_sdp --nice_agent_generate_local_sdp --nice_agent_generate_local_stream_sdp --nice_agent_get_default_local_candidate --nice_agent_get_local_candidates --nice_agent_get_local_credentials --nice_agent_get_remote_candidates --nice_agent_get_selected_pair --nice_agent_get_selected_socket --nice_agent_get_stream_name --nice_agent_get_type --nice_agent_new --nice_agent_new_reliable --nice_agent_parse_remote_candidate_sdp --nice_agent_parse_remote_sdp --nice_agent_parse_remote_stream_sdp --nice_agent_remove_stream --nice_agent_restart --nice_agent_send --nice_agent_set_port_range --nice_agent_set_relay_info --nice_agent_set_remote_candidates --nice_agent_set_remote_credentials --nice_agent_set_local_credentials --nice_agent_set_selected_pair --nice_agent_set_selected_remote_candidate --nice_agent_set_software --nice_agent_set_stream_name --nice_agent_set_stream_tos --nice_candidate_copy --nice_candidate_free --nice_candidate_new --nice_component_state_to_string --nice_debug_disable --nice_debug_enable --nice_interfaces_get_ip_for_interface --nice_interfaces_get_local_interfaces --nice_interfaces_get_local_ips --pseudo_tcp_set_debug_level --pseudo_tcp_socket_close --pseudo_tcp_socket_connect --pseudo_tcp_socket_get_error --pseudo_tcp_socket_get_next_clock --pseudo_tcp_socket_new --pseudo_tcp_socket_notify_clock --pseudo_tcp_socket_notify_mtu --pseudo_tcp_socket_notify_packet --pseudo_tcp_socket_recv --pseudo_tcp_socket_send --stun_agent_build_unknown_attributes_error --stun_agent_default_validater --stun_agent_finish_message --stun_agent_forget_transaction --stun_agent_init --stun_agent_init_error --stun_agent_init_indication --stun_agent_init_request --stun_agent_init_response --stun_agent_set_software --stun_agent_validate --stun_debug_disable --stun_debug_enable --stun_debug --stun_debug_bytes --stun_hash_creds --stun_message_append --stun_message_append32 --stun_message_append64 --stun_message_append_addr --stun_message_append_bytes --stun_message_append_error --stun_message_append_flag --stun_message_append_string --stun_message_append_xor_addr --stun_message_append_xor_addr_full --stun_message_find --stun_message_find32 --stun_message_find64 --stun_message_find_addr --stun_message_find_error --stun_message_find_flag --stun_message_find_string --stun_message_find_xor_addr --stun_message_find_xor_addr_full --stun_message_get_class --stun_message_get_method --stun_message_has_attribute --stun_message_has_cookie --stun_message_id --stun_message_init --stun_message_length --stun_message_validate_buffer_length --stun_optional --stun_strerror --stun_timer_refresh --stun_timer_remainder --stun_timer_start --stun_timer_start_reliable --stun_usage_bind_create --stun_usage_bind_keepalive --stun_usage_bind_process --stun_usage_bind_run --stun_usage_ice_conncheck_create --stun_usage_ice_conncheck_create_reply --stun_usage_ice_conncheck_priority --stun_usage_ice_conncheck_process --stun_usage_ice_conncheck_use_candidate --stun_usage_turn_create --stun_usage_turn_create_refresh --stun_usage_turn_process --stun_usage_turn_refresh_process diff --git a/libnice.spec b/libnice.spec index 8231dc4..f69c824 100644 --- a/libnice.spec +++ b/libnice.spec @@ -2,17 +2,17 @@ %bcond_with gst010
Name: libnice -Version: 0.1.13 -Release: 11%{?dist} +Version: 0.1.14 +Release: 1%{?dist} Summary: GLib ICE implementation
Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz -Patch1: libnice-0.1.13-20160610.patch
BuildRequires: glib2-devel +BuildRequires: gnutls-devel >= 2.12.0 BuildRequires: gobject-introspection-devel %if %{with gst010} BuildRequires: gstreamer-devel @@ -75,7 +75,6 @@ developing applications that use %{name}.
%prep %setup -q -%patch1 -p1
%check #make check @@ -133,6 +132,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog +* Mon Jan 29 2018 Stefan Becker chemobejk@gmail.com - 0.1.14-1 +- Update to 0.1.14 + * Wed Jan 24 2018 Tomas Hoger thoger@redhat.com - 0.1.13-11 - Add conditional for building with(out) gst010 / GStreamer 0.10 support. - Disable gst010 plugin by default. diff --git a/sources b/sources index 111369c..d14a570 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -3226faeaf48a9150ada00da2e2865959 libnice-0.1.13.tar.gz +SHA512 (libnice-0.1.14.tar.gz) = 58bd0e0f630f4f14fe4765f2dab24215d71d61a4e7dc260cdb34eb0254b3dcdc9ce3b6fb010800250fb85f91e06b1c48cfcdd6e18867324609ead17a9106d4dd
commit dbce58ce3ca1376450d4aab31e5107a8c1f062aa Author: Stefan Becker chemobejk@gmail.com Date: Thu May 4 09:40:36 2017 +0300
expand tabs in libnice.spec
diff --git a/libnice.spec b/libnice.spec index d9fbb06..8231dc4 100644 --- a/libnice.spec +++ b/libnice.spec @@ -12,15 +12,15 @@ URL: https://nice.freedesktop.org/wiki/ Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz Patch1: libnice-0.1.13-20160610.patch
-BuildRequires: glib2-devel -BuildRequires: gobject-introspection-devel +BuildRequires: glib2-devel +BuildRequires: gobject-introspection-devel %if %{with gst010} BuildRequires: gstreamer-devel -BuildRequires: gstreamer-plugins-base-devel +BuildRequires: gstreamer-plugins-base-devel %endif BuildRequires: gstreamer1-devel >= 0.11.91 -BuildRequires: gstreamer1-plugins-base-devel >= 0.11.91 -BuildRequires: gupnp-igd-devel >= 0.1.2 +BuildRequires: gstreamer1-plugins-base-devel >= 0.11.91 +BuildRequires: gupnp-igd-devel >= 0.1.2
%description @@ -65,8 +65,8 @@ The %{name}-examples package contains usage (simple, threaded and sdp) examples. Summary: Development files for %{name} Group: Development/Libraries Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: glib2-devel -Requires: pkgconfig +Requires: glib2-devel +Requires: pkgconfig
%description devel The %{name}-devel package contains libraries and header files for
commit a4ee9cf78ac3fe4c30a7e5f3d58de87864d5ccd0 Author: Kamil Dudka kdudka@redhat.com Date: Mon Jan 29 16:10:57 2018 +0100
use https:// URLs in specfile
... and a more generic glob in .gitignore
diff --git a/.gitignore b/.gitignore index def842a..97f428b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1 @@ -libnice-0.0.12.tar.gz -libnice-0.0.13.tar.gz -/libnice-0.1.0.tar.gz -/libnice-0.1.1.tar.gz -/libnice-0.1.2.tar.gz -/libnice-0.1.3.tar.gz -/libnice-0.1.4.tar.gz -/libnice-0.1.8.tar.gz -/libnice-0.1.10.tar.gz -/libnice-0.1.11.tar.gz -/libnice-0.1.13.tar.gz +/libnice-0.1.[0-9][0-9].tar.gz diff --git a/libnice.spec b/libnice.spec index 9abb00a..d9fbb06 100644 --- a/libnice.spec +++ b/libnice.spec @@ -8,8 +8,8 @@ Summary: GLib ICE implementation
Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 -URL: http://nice.freedesktop.org/wiki/ -Source0: http://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz +URL: https://nice.freedesktop.org/wiki/ +Source0: https://nice.freedesktop.org/releases/%%7Bname%7D-%%7Bversion%7D.tar.gz Patch1: libnice-0.1.13-20160610.patch
BuildRequires: glib2-devel
commit 746455c9c6e7b6e406e588b4f7e7a3429b50e8ed Author: Tomas Hoger thoger@redhat.com Date: Wed Jan 24 17:14:05 2018 +0100
Disable gst010 support everywhere
GStreamer 0.10 plugin is not required by any other package any more, so stop building it even in Fedora.
diff --git a/libnice.spec b/libnice.spec index 3097910..9abb00a 100644 --- a/libnice.spec +++ b/libnice.spec @@ -1,9 +1,5 @@ -# enable/disable building of plugin for gstreamer 0.10 -%if 0%{?rhel} > 7 +# disable building of plugin for gstreamer 0.10 %bcond_with gst010 -%else -%bcond_without gst010 -%endif
Name: libnice Version: 0.1.13 @@ -138,7 +134,8 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
%changelog * Wed Jan 24 2018 Tomas Hoger thoger@redhat.com - 0.1.13-11 -- Add conditional for building with(out) gst010 / GStreamer 0.10 support +- Add conditional for building with(out) gst010 / GStreamer 0.10 support. +- Disable gst010 plugin by default.
* Thu Aug 03 2017 Fedora Release Engineering releng@fedoraproject.org - 0.1.13-10 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
arch-excludes@lists.fedoraproject.org