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=cb05dbf215...
https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=368c412d96...
https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=729f8ecea8....
Change:
-%ifarch armv7hl
+%ifarch x86_64 %{ix86}
+%ifarch armv7hl
Thanks.
Full change:
============
commit 617b0abe3745d5595d7af6e4cf21a433dafe130f
Merge: 7a9cb02 a04a646
Author: Kamil Dudka <kdudka(a)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(a)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(a)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(a)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(a)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(a)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(a)fedoraproject.org> -
0.1.14-2
+ - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
* Mon Jan 29 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-1
- Update to 0.1.14
commit a04a646f78d6c1834cc1a32d154a8a34c42f226a
Author: Kamil Dudka <kdudka(a)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(a)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(a)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(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7
commit 9cda08004416b0d369badbae81066eeb25e0039c
Author: Kamil Dudka <kdudka(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)gnu.org> or send patches to
-+# <automake-patches(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
-Reviewed-by: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
-Reviewed-by: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)gnu.org> or send patches to
++# <automake-patches(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
+Reviewed-by: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
+Reviewed-by: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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/%{name}-%{version}.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(a)redhat.com> - 0.1.14-7.20180504git34d6044
+- update to 0.1.14-85-g34d6044 (#1541646)
+
* Mon Apr 16 2018 Kamil Dudka <kdudka(a)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(a)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(a)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(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7
commit d274df7d77937ce5004f9c1c7d3e86fe565e0f28
Author: Kamil Dudka <kdudka(a)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(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7
+- disable test-send-recv, which fails in Koji
+
* Fri Mar 16 2018 Kamil Dudka <kdudka(a)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(a)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(a)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(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7
commit 288dc3b59279d38e0d188f838efaa9fe3c466aad
Author: Kamil Dudka <kdudka(a)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(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7
+- make the build more verbose
+
* Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7
- enable make check again
- make tests pass in Koji
commit 63a783842195268faa634e86d09505769c01db10
Author: Kamil Dudka <kdudka(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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/%{name}-%{version}.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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)gmail.com> - 0.1.14-3
commit 794e7ad7d94e48b6a20f923c99c68daf642f6095
Author: Kamil Dudka <kdudka(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
- Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D1819
-
-commit 4c4834ab634f735145c8f758a22cbdd9cab79bac
-Author: Philip Withnall <withnall(a)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(a)endlessm.com>
-
-commit 1a1803a45778000720c93d91060cedeb19124a27
-Author: Philip Withnall <withnall(a)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(a)endlessm.com>
-
-commit 14102d44449d2eb4148588ce54fa897fa13b87ad
-Author: Fabrice Bellet <fabrice(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
- Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D1737
-
-commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342
-Author: Fabrice Bellet <fabrice(a)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(a)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(a)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(a)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(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D1717
-
-commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b
-Author: Olivier Crête <olivier.crete(a)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(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D1715
-
-commit 10c557f23f8337f1304fff27bd85d2eb713cb249
-Author: Olivier Crête <olivier.crete(a)collabora.com>
-Date: Wed Apr 5 17:01:35 2017 -0400
-
- test-credentials: Fix leak
-
-commit ae6d939e48366b80570d713b83334191b0982e71
-Author: Olivier Crête <olivier.crete(a)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(a)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(a)collabora.com>
-Date: Tue Apr 4 16:16:46 2017 -0400
-
- agent: Remove impossible case
-
-commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad
-Author: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)gnu.org> or send patches to
-+# <automake-patches(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)gnu.org> or send patches to
++# <automake-patches(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
+Reviewed-by: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
+Reviewed-by: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)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/%{name}-%{version}.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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
+ Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com>
+ Differential Revision:
https://phabricator.freedesktop.org/D1819
+
+commit 4c4834ab634f735145c8f758a22cbdd9cab79bac
+Author: Philip Withnall <withnall(a)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(a)endlessm.com>
+
+commit 1a1803a45778000720c93d91060cedeb19124a27
+Author: Philip Withnall <withnall(a)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(a)endlessm.com>
+
+commit 14102d44449d2eb4148588ce54fa897fa13b87ad
+Author: Fabrice Bellet <fabrice(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)endlessm.com>
+ Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com>
+ Differential Revision:
https://phabricator.freedesktop.org/D1737
+
+commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342
+Author: Fabrice Bellet <fabrice(a)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(a)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(a)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(a)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(a)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(a)collabora.co.uk>
+ Differential Revision:
https://phabricator.freedesktop.org/D1717
+
+commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b
+Author: Olivier Crête <olivier.crete(a)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(a)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(a)collabora.co.uk>
+ Differential Revision:
https://phabricator.freedesktop.org/D1715
+
+commit 10c557f23f8337f1304fff27bd85d2eb713cb249
+Author: Olivier Crête <olivier.crete(a)collabora.com>
+Date: Wed Apr 5 17:01:35 2017 -0400
+
+ test-credentials: Fix leak
+
+commit ae6d939e48366b80570d713b83334191b0982e71
+Author: Olivier Crête <olivier.crete(a)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(a)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(a)collabora.com>
+Date: Tue Apr 4 16:16:46 2017 -0400
+
+ agent: Remove impossible case
+
+commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad
+Author: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)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(a)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(a)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(a)gnu.org> or send patches to
++# <automake-patches(a)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(a)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/%{name}-%{version}.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(a)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(a)fedoraproject.org> - 0.1.14-2
- Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
commit b17a790f177dfb96d128af068bb7b391da8162df
Author: Kamil Dudka <kdudka(a)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(a)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(a)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(a)fedoraproject.org> - 0.1.14-2
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
* Mon Jan 29 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-1
- Update to 0.1.14
commit 3feabe3b2d1f84cb307dff64326af6681d69f183
Author: Stefan Becker <chemobejk(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)collabora.com>
-Date: Wed Apr 6 01:59:36 2016 +0300
-
- pseudotcp: Implement full NewReno
-
-commit e31932bdf25ce545a88fe6078b9557bc4d9e6365
-Author: Olivier Crête <olivier.crete(a)collabora.com>
-Date: Tue Feb 2 16:59:18 2016 -0500
-
- pseudotcp: Make debug more useful
-
-commit 8ccb2c1711a2e5cdebf9411764fd92d5a089ffbf
-Author: Olivier Crête <olivier.crete(a)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(a)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(a)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(a)collabora.com>
-Date: Thu Sep 17 15:00:27 2015 -0400
-
- pseudotcp: Make structs definitions private
-
-commit 11d4bb9783a69363de80ff49638030ba892a93fe
-Author: Philip Withnall <philip.withnall(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)collabora.com>
-Date: Tue May 31 17:31:12 2016 -0400
-
- WIP
-
-commit 955323915c43b1a066399e61d0ab091f0b1d112b
-Author: Jakub Adam <jakub.adam(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)collabora.com>
-Date: Mon Feb 8 21:04:24 2016 -0500
-
- conncheck: Deduplicate conncheck stopping code
-
-commit f122e4174d19c60bc434f3986f5c08f8673344bd
-Author: Olivier Crête <olivier.crete(a)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(a)collabora.com>
-Date: Thu Jan 14 17:59:16 2016 -0500
-
- agent: Add warning on ignored result
-
-commit a357b17f5f3415320b9ec7122738396ccd998521
-Author: Fabrice Bellet <fabrice(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D877
-
-commit b986d6e5f2ee0b7b0e09031c1a369bf89153e4c5
-Author: Fabrice Bellet <fabrice(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D872
-
-commit e0ed4fb3a236710846d9129438e0077782569633
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D866
-
-commit 38268e53fde8cd97055d88d2066c0016fe04b31b
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.co.uk>
- Reviewed-by: José Antonio Santos Cadenas <santoscadenas(a)gmail.com>
- Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk>
- Reviewed-by: José Antonio Santos Cadenas <santoscadenas(a)gmail.com>
- Differential Revision:
https://phabricator.freedesktop.org/D785
-
-commit 7037ab4cf384edd9f700bc221a9d980b30d9c64f
-Author: Fabrice Bellet <fabrice(a)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(a)collabora.co.uk>
- Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D802
-
-commit 1732c7d6a7a104438412309373818e493a2504c9
-Author: Olivier Crête <olivier.crete(a)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(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D807
-
-commit ed75d55cf279613bb736f7646d3010d816797ddf
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D786
-
-commit 1493a381d5bf6e15348c2bc17270f45b69cb70d2
-Author: Philip Withnall <philip(a)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(a)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(a)gmail.com>
- Differential Revision:
https://phabricator.freedesktop.org/D822
-
-commit 38c5e66886cb8138d6be57b8a4721d9b42a358b7
-Author: Philip Withnall <philip.withnall(a)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(a)gmail.com>
- Differential Revision:
https://phabricator.freedesktop.org/D742
-
-commit 70981d41edf46a09393017f3de34748fcecea046
-Author: Philip Withnall <philip(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D819
-
-commit 47eaf50a99d91c4a666f05c4de24613706847c88
-Author: Philip Withnall <philip(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D817
-
-commit 4e68244a51698aefdf44dd1ceb17b95275e655bf
-Author: Philip Withnall <philip(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D806
-
-commit 0f1e6c64515871298b115b497b019506b8065235
-Author: Philip Withnall <philip(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D804
-
-commit 17488a20d8db8ea1f8fa2d7a44090070407d6db8
-Author: Philip Withnall <philip(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D815
-
-commit 3ce45c25af238fb4d9a040abb44597531140db2d
-Author: Philip Withnall <philip(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D818
-
-commit c309905ff446bed2dc47811023b98bc586a02d63
-Author: Philip Withnall <philip(a)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(a)tecnocode.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D808
-
-commit 80973c096de872983bc60cd28a84653931f8d601
-Author: Philip Withnall <philip(a)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(a)tecnocode.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D803
-
-commit 41ab61def82aa275649afd5f4a3fcb43e75fb360
-Author: Mike Ruprecht <cmaiku(a)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(a)collabora.co.uk>
-
-commit 9638bc132f909177f6ecfb86cdd17af557a35c56
-Author: Jose Antonio Santos Cadenas <santoscadenas(a)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(a)tecnocode.co.uk>
-
https://phabricator.freedesktop.org/T3492
-
-commit 3e71f42c8b15790e252d850ba42a7ae7e7cc69a9
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D345
-
-commit 53283c218a5eb7a29e7019ac320e74f9dbe4b3fc
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D292
-
-commit aef748ec7d0b06067312e5cc761a6375cd0f699c
-Author: Philip Withnall <philip.withnall(a)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(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D309
-
-commit 529bc193d56522b10a4de83409396b3316863934
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D308
-
-commit ef2b58f64887546e426dd8cda382f2908f84caca
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D307
-
-commit 1f08419382c52c7e796da06c9271a362aa60333d
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D306
-
-commit da70716162b2085ca4db6e7efd446fcda7bd488d
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D301
-
-commit 66e47aa39f9cd3666e610fab78caa0c7d8f9c410
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D300
-
-commit 1ae15f66af2af17e991ab028ca16a1200fd5f4e5
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D299
-
-commit ea3348020da586f20e1a64c9732405e730563616
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D298
-
-commit 9f10231fc787e4683e896bf35f086906a5b16c03
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D297
-
-commit dac3e280d6f3fd792f1d79313debd03c0df282c4
-Author: Philip Withnall <philip.withnall(a)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(a)collabora.com>
- Differential Revision:
https://phabricator.freedesktop.org/D296
-
-commit fae0f9d37c6e34f34c92dee85384c29e565bdbce
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.co.uk>
-
-commit 02852728ef347f5cb4c9227848b266c72b5fe38b
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.co.uk>
-
-commit 84eaa12b0b1a76c45ce2d77294e0477c10552cd4
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D241
-
-commit 837c8953fe87bdd5d5bccc444e72739100578ef8
-Author: Jakub Adam <jakub.adam(a)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(a)collabora.co.uk>
- Differential Revision:
https://phabricator.freedesktop.org/D240
-
-commit c6bc33c031493e0db11a1f57055a656f6428c60a
-Author: Philip Withnall <philip.withnall(a)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(a)gmail.com> on the mailing list.
-
-commit 757f8aecdb4fda86b2bbac828a3acc528d8eb8bc
-Author: Philip Withnall <philip.withnall(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)collabora.co.uk>
-Date: Tue Aug 18 14:03:41 2015 +0100
-
- agent: Remove unused inet_pton() function
-
- Summary:
- As spotted by Felix <felixschlitter(a)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(a)collabora.co.uk>
-Date: Tue Jun 30 14:39:51 2015 +0100
-
- agent: Remove unused inet_pton() function
-
- As spotted by Felix <felixschlitter(a)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(a)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(a)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(a)collabora.co.uk>
- Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com>
-
-
http://phabricator.freedesktop.org/T117
-
-commit cd61af3114a51df53619fb0460ace2b3660fc5da
-Author: Philip Withnall <philip.withnall(a)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(a)gmail.com> and Felix
- <felixschlitter(a)gmail.com>.
-
- Reviewed-by: Olivier Crete <olivier.crete(a)collabora.com>
- Reviewed-by: Felix Schlitter <felixschlitter(a)gmail.com>
-
-
http://phabricator.freedesktop.org/T122
-
-commit 3cccc311b1becf4307f5a4004734d8f7b2cf84f6
-Author: Felix Schlitter <felixschlitter(a)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(a)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(a)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(a)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(a)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(a)kakaroto.homelinux.net>
-Date: Tue May 5 15:07:10 2015 -0400
-
- Removing no-op assignment
-
-commit 91a7b9324244844baf35d8fcef019a4ea3872d30
-Author: Youness Alaoui <kakaroto(a)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(a)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(a)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
-+ * ![State transition diagram](states.png)
- */
- 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(a)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/%{name}-%{version}.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(a)gmail.com> - 0.1.14-1
+- Update to 0.1.14
+
* Wed Jan 24 2018 Tomas Hoger <thoger(a)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(a)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/%{name}-%{version}.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(a)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/%{name}-%{version}.tar.gz
+URL:
https://nice.freedesktop.org/wiki/
+Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
Patch1: libnice-0.1.13-20160610.patch
BuildRequires: glib2-devel
commit 746455c9c6e7b6e406e588b4f7e7a3429b50e8ed
Author: Tomas Hoger <thoger(a)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(a)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(a)fedoraproject.org> -
0.1.13-10
- Rebuilt for
https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild