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=729f8ecea8....
Change:
+%ifarch armv7hl
Thanks.
Full change:
============
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