Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
List overview
Download
Arch-excludes
June 2018
----- 2025 -----
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
arch-excludes@lists.fedoraproject.org
2 participants
531 discussions
Start a n
N
ew thread
Architecture specific change in rpms/golang-github-russross-blackfriday.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/golang-github-russross-blackfriday.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/golang-github-russross-blackfriday.…
. Change: -%ifarch 0%{?gccgo_arches} Thanks. Full change: ============ commit a404f8e3bdfb527d1318af518d79e590dc1fe2c1 Author: Jan Chaloupka <jchaloup(a)redhat.com> Date: Thu Jun 28 12:38:48 2018 +0200 Upload glide files diff --git a/.gitignore b/.gitignore index 911ebeb..ad07b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/blackfriday-4048872.tar.gz +/blackfriday-4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c.tar.gz diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..407f28e --- /dev/null +++ b/glide.lock @@ -0,0 +1,4 @@ +hash: 514603a8ede9edb7244340baf29ffacc8aca929e08541c4e5348b294d9997228 +imports: [] +updated: '2018-06-28T10:37:44.703749+00:00' + diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..d0758fd --- /dev/null +++ b/glide.yaml @@ -0,0 +1,3 @@ +import: [] +package:
github.com/russross/blackfriday
+ diff --git a/golang-github-russross-blackfriday.spec b/golang-github-russross-blackfriday.spec index ad76132..861d8aa 100644 --- a/golang-github-russross-blackfriday.spec +++ b/golang-github-russross-blackfriday.spec @@ -1,18 +1,20 @@ #
http://github.com/russross/blackfriday
-%global provider_prefix
github.com/russross/blackfriday
-%global gobaseipath %{provider_prefix} + +%global goipath
github.com/russross/blackfriday
%global commit 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c -%global commitdate 20170728 -%gocraftmeta -i + +%gometa -i Name: %{goname} Version: 1.5 -Release: 3%{?dist} +Release: 4%{?dist} Summary: Markdown processor implemented in Go License: BSD URL: %{gourl} Source0: %{gosource} +Source1: glide.yaml +Source2: glide.yaml %description %{summary} @@ -26,13 +28,14 @@ BuildArch: noarch This package contains library source intended for building other packages which use import path with -%{gobaseipath} prefix. +%{goipath} prefix. %prep -%gosetup - +%gosetup -q +cp %{SOURCE1} %{SOURCE2} . %install -%goinstall $(find . -iname "testdata" -type d) +files="$(find . -iname 'testdata' -type d)" +%goinstall glide.lock glide.yaml ${files} %check %gochecks @@ -45,6 +48,9 @@ building other packages which use import path with %doc README.md %changelog +* Thu Jun 28 2018 Jan Chaloupka <jchaloup(a)redhat.com> - 1.5-4.git4048872 +- Upload glide files + * Thu Mar 01 2018 Jan Chaloupka <jchaloup(a)redhat.com> - Autogenerate some parts using the new macros diff --git a/sources b/sources index 6d72ff2..ee16b26 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (blackfriday-4048872.tar.gz) = 2c358bb8453030d432cf75b4457a9c13ae8b46f206dccdf8a764727faa78fb3a3ecaece7b074ce479856c6dd116e66187768c176501babc2a5713acab2d19276 +SHA512 (blackfriday-4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c.tar.gz) = 2c358bb8453030d432cf75b4457a9c13ae8b46f206dccdf8a764727faa78fb3a3ecaece7b074ce479856c6dd116e66187768c176501babc2a5713acab2d19276 commit fd2416533135a482b48daf762e58df49addcae05 Author: Jan Chaloupka <jchaloup(a)redhat.com> Date: Thu Mar 1 23:25:33 2018 +0100 Autogenerate some parts using the new macros diff --git a/golang-github-russross-blackfriday.spec b/golang-github-russross-blackfriday.spec index fc40ab7..ad76132 100644 --- a/golang-github-russross-blackfriday.spec +++ b/golang-github-russross-blackfriday.spec @@ -1,161 +1,53 @@ -# If any of the following macros should be set otherwise, -# you can wrap any of them with the following conditions: -# - %%if 0%%{centos} == 7 -# - %%if 0%%{?rhel} == 7 -# - %%if 0%%{?fedora} == 23 -# Or just test for particular distribution: -# - %%if 0%%{centos} -# - %%if 0%%{?rhel} -# - %%if 0%%{?fedora} -# -# Be aware, on centos, both %%rhel and %%centos are set. If you want to test -# rhel specific macros, you can use %%if 0%%{?rhel} && 0%%{?centos} == 0 condition. -# (Don't forget to replace double percentage symbol with single one in order to apply a condition) - -# Generate devel rpm -%global with_devel 1 -# Build project from bundled dependencies -%global with_bundled 0 -# Build with debug info rpm -%global with_debug 0 -# Run tests in check section -%global with_check 1 -# Generate unit-test rpm -%global with_unit_test 1 - -%if 0%{?with_debug} -%global _dwz_low_mem_die_limit 0 -%else -%global debug_package %{nil} -%endif - -%global provider github -%global provider_tld com -%global project russross -%global repo blackfriday -#
https://github.com/russross/blackfriday
-%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo} -%global import_path %{provider_prefix} +#
http://github.com/russross/blackfriday
+%global provider_prefix
github.com/russross/blackfriday
+%global gobaseipath %{provider_prefix} %global commit 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c -%global shortcommit %(c=%{commit}; echo ${c:0:7}) +%global commitdate 20170728 -Name: golang-%{provider}-%{project}-%{repo} +%gocraftmeta -i + +Name: %{goname} Version: 1.5 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Markdown processor implemented in Go License: BSD -URL: https://%{provider_prefix} -Source0: https://%{provider_prefix}/archive/%{commit}/%{repo}-%{shortcommit}.tar.gz - -# e.g. el6 has ppc64 arch without gcc-go, so EA tag is required -ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}} -# If go_compiler is not set to 1, there is no virtual provide. Use golang instead. -BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} +URL: %{gourl} +Source0: %{gosource} %description %{summary} -%if 0%{?with_devel} %package devel Summary: %{summary} BuildArch: noarch -%if 0%{?with_check} -%endif - -Provides: golang(%{import_path}) = %{version}-%{release} - %description devel %{summary} This package contains library source intended for building other packages which use import path with -%{import_path} prefix. -%endif - -%if 0%{?with_unit_test} -%package unit-test -Summary: Unit tests for %{name} package - -%if 0%{?with_check} -#Here comes all BuildRequires: PACKAGE the unit tests -#in %%check section need for running -%endif - -# test subpackage tests code from devel subpackage -Requires: %{name}-devel = %{version}-%{release} - -%description unit-test -%{summary} - -This package contains unit tests for project -providing packages with %{import_path} prefix. -%endif +%{gobaseipath} prefix. %prep -%setup -q -n %{repo}-%{commit} - -%build +%gosetup %install -# source codes for building projects -%if 0%{?with_devel} -install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ -# find all *.go but no *_test.go files and generate devel.file-list -for file in $(find . -iname "*.go" \! -iname "*_test.go") ; do - install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) - cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file - echo "%%{gopath}/src/%%{import_path}/$file" >> devel.file-list -done -%endif - -# testing files for this project -%if 0%{?with_unit_test} -install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ -# find all *_test.go files and generate unit-test.file-list -for file in $(find . -iname "*_test.go"); do - install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) - cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file - echo "%%{gopath}/src/%%{import_path}/$file" >> unit-test.file-list -done -cp -r testdata %{buildroot}/%{gopath}/src/%{import_path}/. -echo "%%{gopath}/src/%%{import_path}/testdata" >> unit-test.file-list -%endif +%goinstall $(find . -iname "testdata" -type d) %check -%if 0%{?with_check} && 0%{?with_unit_test} && 0%{?with_devel} -%ifarch 0%{?gccgo_arches} -function gotest { %{gcc_go_test} "$@"; } -%else -%if 0%{?golang_test:1} -function gotest { %{golang_test} "$@"; } -%else -function gotest { go test "$@"; } -%endif -%endif - -export GOPATH=%{buildroot}/%{gopath}:%{gopath} -gotest %{import_path} -%endif +%gochecks #define license tag if not already defined %{!?_licensedir:%global license %doc} -%if 0%{?with_devel} %files devel -f devel.file-list %license LICENSE.txt %doc README.md -%dir %{gopath}/src/%{provider}.%{provider_tld}/%{project} -%dir %{gopath}/src/%{import_path} -%endif - -%if 0%{?with_unit_test} -%files unit-test -f unit-test.file-list -%license LICENSE.txt -%doc README.md -%endif %changelog +* Thu Mar 01 2018 Jan Chaloupka <jchaloup(a)redhat.com> +- Autogenerate some parts using the new macros + * Wed Feb 07 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.5-2 - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
commit 66bf08f41d857464a9f87627fcbe1c324dfc13bf Author: Fedora Release Engineering <releng(a)fedoraproject.org> Date: Wed Feb 7 14:50:26 2018 +0000 - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org> diff --git a/golang-github-russross-blackfriday.spec b/golang-github-russross-blackfriday.spec index c8a6d39..fc40ab7 100644 --- a/golang-github-russross-blackfriday.spec +++ b/golang-github-russross-blackfriday.spec @@ -41,7 +41,7 @@ Name: golang-%{provider}-%{project}-%{repo} Version: 1.5 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Markdown processor implemented in Go License: BSD URL: https://%{provider_prefix} @@ -156,6 +156,9 @@ gotest %{import_path} %endif %changelog +* Wed Feb 07 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.5-2 +- Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+ * Tue Sep 12 2017 Athos Ribeiro <athoscr(a)fedoraproject.org> - 1.5-1 - Update to v1.5 related: #1222338
1
0
0
0
Architecture specific change in rpms/webkit2-sharp.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/webkit2-sharp.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/webkit2-sharp.git/commit/?id=7359ca…
. Change: +ExclusiveArch: %mono_arches Thanks. Full change: ============ commit 7359ca3aa6e288222d50c73a407d269618baedb9 Author: Robert-André Mauchin <zebob.m(a)gmail.com> Date: Thu Jun 28 12:53:16 2018 +0200 Remove ldconfig diff --git a/webkit2-sharp.spec b/webkit2-sharp.spec index 6ca9aff..e4309c8 100644 --- a/webkit2-sharp.spec +++ b/webkit2-sharp.spec @@ -1,50 +1,48 @@ -%global commit a59fd76dd730432c76b12ee6347ea66567107ab9 -%global commitdate 20170219 -%global shortcommit %(c=%{commit}; echo ${c:0:7}) +%global commit a59fd76dd730432c76b12ee6347ea66567107ab9 +%global snapshotdate 20170219 +%global shortcommit %(c=%{commit}; echo ${c:0:7}) -Name: webkit2-sharp -Version: 0 -Release: 0.6%{?commitdate:.%{commitdate}git%{shortcommit}}%{?dist} -Summary: C# bindings for WebKit 2 with GTK+ 3 +Name: webkit2-sharp +Version: 0 +Release: 0.7%{?snapshotdate:.%{snapshotdate}git%{shortcommit}}%{?dist} +Summary: C# bindings for WebKit 2 with GTK+ 3 -License: MIT -URL:
https://github.com/hbons/%{name}
+License: MIT +URL:
https://github.com/hbons/%{name}
%{?shortcommit: -Source0: %url/archive/%{commit}/%{name}-%{shortcommit}.tar.gz} +Source0: %url/archive/%{commit}/%{name}-%{shortcommit}.tar.gz} %{!?shortcommit: -Source0: %url/archive/%{commit}/%{name}-%{version}.tar.gz} - -Patch0: %{name}-a59fd76-fix_libdir.patch - -Requires: webkit2gtk3 -BuildRequires: pkgconfig(webkit2gtk-4.0) -BuildRequires: pkgconfig(mono) -BuildRequires: pkgconfig(gtk-sharp-3.0) -BuildRequires: pkgconfig(gapi-3.0) -BuildRequires: pkgconfig(monodoc) -BuildRequires: libxslt -BuildRequires: dos2unix -BuildRequires: autoconf -BuildRequires: automake -BuildRequires: intltool -BuildRequires: libtool -BuildRequires: gettext - -ExclusiveArch: %mono_arches +Source0: %url/archive/%{commit}/%{name}-%{version}.tar.gz} + +Patch0: %{name}-a59fd76-fix_libdir.patch + +Requires: webkit2gtk3 +BuildRequires: pkgconfig(webkit2gtk-4.0) +BuildRequires: pkgconfig(mono) +BuildRequires: pkgconfig(gtk-sharp-3.0) +BuildRequires: pkgconfig(gapi-3.0) +BuildRequires: pkgconfig(monodoc) +BuildRequires: libxslt +BuildRequires: dos2unix +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: intltool +BuildRequires: libtool +BuildRequires: gettext + +ExclusiveArch: %mono_arches #https://fedoraproject.org/wiki/Packaging:Mono#Empty_debuginfo %global debug_package %{nil} - %description C# bindings for WebKit 2 with GTK+ 3 %package devel -Summary: Development files for WebKit2-sharp -Requires: %{name} = %{version}-%{release} -Requires: pkgconfig - +Summary: Development files for WebKit2-sharp +Requires: %{name} = %{version}-%{release} +Requires: pkgconfig %description devel Development files for WebKit2-sharp @@ -56,6 +54,7 @@ Development files for WebKit2-sharp %{!?shortcommit: %autosetup -n %{name}-%{version}} + %build ./autogen.sh %configure --disable-static @@ -64,14 +63,12 @@ sed -i 's/\r$//' COPYING # No parallel make, race condition with monodoc make + %install %make_install find %{buildroot} -name '*.la' -delete -%post -p /sbin/ldconfig - -%postun -p /sbin/ldconfig %files %license COPYING @@ -80,11 +77,16 @@ find %{buildroot} -name '*.la' -delete %{_datadir}/gapi-3.0/webkit2-sharp-api.xml %{_libdir}/libwebkit2sharpglue-2.10.9.so + %files devel %{_libdir}/pkgconfig/webkit2-sharp-4.0.pc %{_prefix}/lib/monodoc/sources/webkit2-sharp* + %changelog +* Thu Jun 28 2018 Robert-André Mauchin <zebob.m(a)gmail.com> - 0-0.7.20170219gita59fd76 +- Remove ldconfig + * Fri Feb 09 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 0-0.6.20170219gita59fd76 - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
1
0
0
0
Architecture specific change in rpms/libnice.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
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=cb05dbf2151a…
https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=368c412d96e9…
https://src.fedoraproject.org/cgit/rpms/libnice.git/commit/?id=729f8ecea839…
. Change: -%ifarch armv7hl +%ifarch x86_64 %{ix86} +%ifarch armv7hl Thanks. Full change: ============ commit 617b0abe3745d5595d7af6e4cf21a433dafe130f Merge: 7a9cb02 a04a646 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Thu Jun 28 12:15:48 2018 +0200 Merge branch 'f28' into f27 diff --cc libnice.spec index 98da5b7,f27e02f..8201309 --- a/libnice.spec +++ b/libnice.spec @@@ -1,13 -1,13 +1,17 @@@ -# disable building of plugin for gstreamer 0.10 +# enable/disable building of plugin for gstreamer 0.10 +%if 0%{?rhel} > 7 %bcond_with gst010 +%else +%bcond_without gst010 +%endif + %global upstream_date 20180504 + %global upstream_rnum 85 + %global upstream_hash 34d6044 + Name: libnice Version: 0.1.14 - Release: 1%{?dist} + Release: 7.%{upstream_date}git%{upstream_hash}%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries @@@ -121,12 -149,6 +162,8 @@@ make chec %{_libdir}/gstreamer-1.0/libgstnice.so +%files examples - %{_bindir}/sdp-example - %{_bindir}/simple-example - %{_bindir}/threaded-example - + %files devel %{_includedir}/* %{_libdir}/*.so @@@ -136,6 -158,37 +173,36 @@@ %changelog + * Mon May 07 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-7.20180504git34d6044 + - component: accept TURN in nice_component_verify_remote_candidate() (#1541646) + - update to 0.1.14-85-g34d6044 (#1541646) + + * Mon Apr 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7 + - temporarily make the upstream test-suite run on Intel arches only + - disable test-send-recv, which fails in Koji + + * Fri Mar 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7 + - do not build with -Werror by default + - make the build more verbose + + * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 + - enable make check again + - make tests pass in Koji + - disable test-new-dribble that sometimes hangs indefinitely + - make tests compile on i686 + - make the package build on armv7hl + - make the package build on Fedora 28 + - avoid build failure if gstreamer-plugins-base-devel is installed + - move autoreconf invocation to %%prep + - use Name Version Release that explicitly identifies an SCM snapshot (#1541646) + + * Fri Feb 09 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-3 + - update to 0.1.14-70-gfb2f1f7 with alternate server fixes for SIPE + - add autoreconf build step -- remove examples subpackage as examples are no longer installed + + * Wed Feb 07 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 0.1.14-2 + - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+ * Mon Jan 29 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-1 - Update to 0.1.14 commit a04a646f78d6c1834cc1a32d154a8a34c42f226a Author: Kamil Dudka <kdudka(a)redhat.com> Date: Mon May 7 17:37:42 2018 +0200 Resolves: #1541646 - component: accept TURN in nice_component_verify_remote_candidate() diff --git a/libnice-0.1.14-turn-verify.patch b/libnice-0.1.14-turn-verify.patch new file mode 100644 index 0000000..2ce2f86 --- /dev/null +++ b/libnice-0.1.14-turn-verify.patch @@ -0,0 +1,32 @@ +From e4d92bf96d0bb64df35790e5b49c58bfa6e9fbcc Mon Sep 17 00:00:00 2001 +From: Jakub Adam <jakub.adam(a)ktknet.cz> +Date: Thu, 20 Apr 2017 06:47:00 +0200 +Subject: [PATCH] component: accept TURN in + nice_component_verify_remote_candidate() + +When TURN is in operation, agent_recv_message_unlocked() may receive from +NiceSocket with type = NICE_SOCKET_TYPE_UDP_TURN. Such messages were always +dropped due to failed nice_component_verify_remote_candidate(). + +Bug:
https://phabricator.freedesktop.org/D1727
+--- + agent/component.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/agent/component.c b/agent/component.c +index 6eee90e..3e8a7a6 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -1510,7 +1510,8 @@ nice_component_verify_remote_candidate (NiceComponent *component, + (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || + cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || + cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || +- cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && ++ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP || ++ nicesock->type == NICE_SOCKET_TYPE_UDP_TURN) && + nice_address_equal (address, &cand->addr)) { + /* fast return if it's already the first */ + if (item == component->valid_candidates) +-- +2.14.3 + diff --git a/libnice.spec b/libnice.spec index 26f66dc..f27e02f 100644 --- a/libnice.spec +++ b/libnice.spec @@ -22,6 +22,9 @@ Patch2: libnice-0.1.14-tests-i686.patch # make tests pass in Koji Patch3: libnice-0.1.14-tests-koji.patch +# component: accept TURN in nice_component_verify_remote_candidate() (#1541646) +Patch4: libnice-0.1.14-turn-verify.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: glib2-devel @@ -82,6 +85,7 @@ developing applications that use %{name}. %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 chmod 0755 scripts/valgrind-test-driver # disable test-new-dribble, which sometimes hangs indefinitely, and @@ -155,6 +159,7 @@ make check %changelog * Mon May 07 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-7.20180504git34d6044 +- component: accept TURN in nice_component_verify_remote_candidate() (#1541646) - update to 0.1.14-85-g34d6044 (#1541646) * Mon Apr 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7 commit 9cda08004416b0d369badbae81066eeb25e0039c Author: Kamil Dudka <kdudka(a)redhat.com> Date: Mon May 7 15:57:12 2018 +0200 Resolves: #1541646 - update to 0.1.14-85-g34d6044 diff --git a/libnice-0.1.14-70-gfb2f1f7.patch b/libnice-0.1.14-70-gfb2f1f7.patch deleted file mode 100644 index db148cb..0000000 --- a/libnice-0.1.14-70-gfb2f1f7.patch +++ /dev/null @@ -1,10531 +0,0 @@ -From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Mon, 3 Apr 2017 14:30:10 -0400 -Subject: [PATCH 01/70] Version 0.1.14.1 - ---- - configure.ac | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 5fabdb4..b39bfe3 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -5,8 +5,8 @@ dnl Always compile with -Wall; if --enable-compile-warnings=error is passed, - dnl also use -Werror. git and pre-releases default to -Werror - - dnl use a three digit version number for releases, and four for cvs/prerelease --AC_INIT([libnice],[0.1.14]) --LIBNICE_RELEASE="yes" -+AC_INIT([libnice],[0.1.14.1]) -+LIBNICE_RELEASE="no" - - AC_CANONICAL_TARGET - --- -2.13.6 - - -From 59ce41dfb837adf4222b25490cde2e394384ad15 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= <mparisdiaz(a)gmail.com> -Date: Fri, 31 Mar 2017 20:20:38 -0400 -Subject: [PATCH 02/70] conncheck: consider answer received when remote - credentials are set -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Consider that the answer is received when remote credentials -are set instead of when a remote candidate is set, -which could not happen or could cause more delay for the -connection establishment. - -Ported to git master by Olivier Crête - -Differential Revision:
https://phabricator.freedesktop.org/D1704
---- - agent/agent.c | 4 +- - agent/conncheck.c | 225 +++++++++++++++++++++++++++--------------------------- - agent/conncheck.h | 2 +- - 3 files changed, 117 insertions(+), 114 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 555fd16..4d9381c 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3240,6 +3240,8 @@ nice_agent_set_remote_credentials ( - g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG); - g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD); - -+ conn_check_remote_credentials_set(agent, stream); -+ - ret = TRUE; - goto done; - } -@@ -3342,8 +3344,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, - } - } - -- conn_check_remote_candidates_set(agent, stream, component); -- - if (added > 0) { - conn_check_schedule_next (agent); - } -diff --git a/agent/conncheck.c b/agent/conncheck.c -index dda2f2f..2abbc5e 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1243,124 +1243,124 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list) - - /* - * Handle any processing steps for connectivity checks after -- * remote candidates have been set. This function handles -+ * remote credentials have been set. This function handles - * the special case where answerer has sent us connectivity -- * checks before the answer (containing candidate information), -+ * checks before the answer (containing credentials information), - * reaches us. The special case is documented in sect 7.2 - * if ICE spec (ID-19). - */ --void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) -+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - { -- GSList *j, *k, *l, *m, *n; -+ GSList *j, *k, *l, *m, *n, *o; - - for (j = stream->conncheck_list; j ; j = j->next) { - CandidateCheckPair *pair = j->data; -- if (pair->component_id == component->id) { -- gboolean match = FALSE; -- -- /* performn delayed processing of spec steps section 7.2.1.4, -- and section 7.2.1.5 */ -- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -- -- for (k = component->incoming_checks; k; k = k->next) { -- IncomingCheck *icheck = k->data; -- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to -- * be handled separately */ -- for (l = component->remote_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&icheck->from, &cand->addr)) { -- match = TRUE; -- break; -- } -- } -- if (match != TRUE) { -- /* note: we have gotten an incoming connectivity check from -- * an address that is not a known remote candidate */ -- -- NiceCandidate *local_candidate = NULL; -- NiceCandidate *remote_candidate = NULL; -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -- agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- /* We need to find which local candidate was used */ -- uint8_t uname[NICE_STREAM_MAX_UNAME]; -- guint uname_len; -- -- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -- "stored pending check", agent); -- -- for (m = component->remote_candidates; -- m != NULL && remote_candidate == NULL; m = m->next) { -- for (n = component->local_candidates; n; n = n->next) { -- NiceCandidate *rcand = m->data; -- NiceCandidate *lcand = n->data; -- -- uname_len = priv_create_username (agent, stream, -- component->id, rcand, lcand, -- uname, sizeof (uname), TRUE); -- -- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -- icheck->username_len, uname_len, -- icheck->username && uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0); -- stun_debug_bytes (" first username: ", -- icheck->username, -- icheck->username? icheck->username_len : 0); -- stun_debug_bytes (" second username: ", uname, uname_len); -- -- if (icheck->username && -- uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0) { -- local_candidate = lcand; -- remote_candidate = rcand; -- break; -- } -- } -- } -- } else { -- for (l = component->local_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -- local_candidate = cand; -+ NiceComponent *component = -+ nice_stream_find_component_by_id (stream, pair->component_id); -+ gboolean match = FALSE; -+ -+ /* performn delayed processing of spec steps section 7.2.1.4, -+ and section 7.2.1.5 */ -+ priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -+ -+ for (k = component->incoming_checks; k; k = k->next) { -+ IncomingCheck *icheck = k->data; -+ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to -+ * be handled separately */ -+ for (l = component->remote_candidates; l; l = l->next) { -+ NiceCandidate *cand = l->data; -+ if (nice_address_equal (&icheck->from, &cand->addr)) { -+ match = TRUE; -+ break; -+ } -+ } -+ if (match != TRUE) { -+ /* note: we have gotten an incoming connectivity check from -+ * an address that is not a known remote candidate */ -+ -+ NiceCandidate *local_candidate = NULL; -+ NiceCandidate *remote_candidate = NULL; -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -+ agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ /* We need to find which local candidate was used */ -+ uint8_t uname[NICE_STREAM_MAX_UNAME]; -+ guint uname_len; -+ -+ nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -+ "stored pending check", agent); -+ -+ for (m = component->remote_candidates; -+ m != NULL && remote_candidate == NULL; m = m->next) { -+ for (n = component->local_candidates; n; n = n->next) { -+ NiceCandidate *rcand = m->data; -+ NiceCandidate *lcand = n->data; -+ -+ uname_len = priv_create_username (agent, stream, -+ component->id, rcand, lcand, -+ uname, sizeof (uname), TRUE); -+ -+ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -+ icheck->username_len, uname_len, -+ icheck->username && uname_len == icheck->username_len && -+ memcmp (uname, icheck->username, icheck->username_len) == 0); -+ stun_debug_bytes (" first username: ", -+ icheck->username, -+ icheck->username? icheck->username_len : 0); -+ stun_debug_bytes (" second username: ", uname, uname_len); -+ -+ if (icheck->username && -+ uname_len == icheck->username_len && -+ memcmp (uname, icheck->username, icheck->username_len) == 0) { -+ local_candidate = lcand; -+ remote_candidate = rcand; - break; - } - } - } -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -- local_candidate == NULL) { -- /* if we couldn't match the username, then the matching remote -- * candidate hasn't been received yet.. we must wait */ -- nice_debug ("Agent %p : Username check failed. pending check has " -- "to wait to be processed", agent); -- } else { -- NiceCandidate *candidate; -- -- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -- agent); -- candidate = -- discovery_learn_remote_peer_reflexive_candidate (agent, -- stream, -- component, -- icheck->priority, -- &icheck->from, -- icheck->local_socket, -- local_candidate, remote_candidate); -- if (candidate) { -- if (local_candidate && -- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -- priv_conn_check_add_for_candidate_pair_matched (agent, -- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -- else -- conn_check_add_for_candidate (agent, stream->id, component, candidate); -- -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ } else { -+ for (l = component->local_candidates; l; l = l->next) { -+ NiceCandidate *cand = l->data; -+ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -+ local_candidate = cand; -+ break; - } - } - } -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -+ local_candidate == NULL) { -+ /* if we couldn't match the username, then the matching remote -+ * candidate hasn't been received yet.. we must wait */ -+ nice_debug ("Agent %p : Username check failed. pending check has " -+ "to wait to be processed", agent); -+ } else { -+ NiceCandidate *candidate; -+ -+ nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -+ agent); -+ candidate = -+ discovery_learn_remote_peer_reflexive_candidate (agent, -+ stream, -+ component, -+ icheck->priority, -+ &icheck->from, -+ icheck->local_socket, -+ local_candidate, remote_candidate); -+ if (candidate) { -+ if (local_candidate && -+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -+ priv_conn_check_add_for_candidate_pair_matched (agent, -+ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -+ else -+ conn_check_add_for_candidate (agent, stream->id, component, candidate); -+ -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -+ priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ } -+ } - } - } - } -@@ -1368,9 +1368,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice - /* Once we process the pending checks, we should free them to avoid - * reprocessing them again if a dribble-mode set_remote_candidates - * is called */ -- g_slist_free_full (component->incoming_checks, -- (GDestroyNotify) incoming_check_free); -- component->incoming_checks = NULL; -+ for (o = stream->components; o; o = o->next) { -+ NiceComponent *component = o->data; -+ g_slist_free_full (component->incoming_checks, -+ (GDestroyNotify) incoming_check_free); -+ component->incoming_checks = NULL; -+ } - - stream->conncheck_list = - prune_cancelled_conn_check (stream->conncheck_list); -@@ -3628,14 +3631,14 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - if (stream->initial_binding_request_received != TRUE) - agent_signal_initial_binding_request_received (agent, stream); - -- if (component->remote_candidates && remote_candidate == NULL) { -+ if (remote_candidate == NULL) { - nice_debug ("Agent %p : No matching remote candidate for incoming check ->" - "peer-reflexive candidate.", agent); - remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( - agent, stream, component, priority, from, nicesock, - local_candidate, - remote_candidate2 ? remote_candidate2 : remote_candidate); -- if(remote_candidate) { -+ if(remote_candidate && stream->remote_ufrag != NULL) { - if (local_candidate && - local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { - CandidateCheckPair *pair; -@@ -3654,10 +3657,10 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -- if (component->remote_candidates == NULL) { -+ if (stream->remote_ufrag == NULL) { - /* case: We've got a valid binding request to a local candidate -- * but we do not yet know remote credentials nor -- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply -+ * but we do not yet know remote credentials. -+ * As per sect 7.2 of ICE (ID-19), we send a reply - * immediately but postpone all other processing until - * we get information about the remote candidates */ - -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 431c606..10319cc 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -105,7 +105,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); - void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); - gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); - gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); --void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); -+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream); - NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); - void - conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, --- -2.13.6 - - -From 0de1657e4d15d4f1911ab1fad84ea23e7013070f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 12:25:50 -0400 -Subject: [PATCH 03/70] conncheck: Use the right test for empty remote_frag - -It's now an array, not a pointer, so needs to test to emptyness. - -It's a bugfix on the previous commit, 59ce41df ---- - agent/conncheck.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 2abbc5e..7096b42 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3638,7 +3638,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - agent, stream, component, priority, from, nicesock, - local_candidate, - remote_candidate2 ? remote_candidate2 : remote_candidate); -- if(remote_candidate && stream->remote_ufrag != NULL) { -+ if(remote_candidate && stream->remote_ufrag[0]) { - if (local_candidate && - local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { - CandidateCheckPair *pair; -@@ -3657,7 +3657,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -- if (stream->remote_ufrag == NULL) { -+ if (stream->remote_ufrag[0] == 0) { - /* case: We've got a valid binding request to a local candidate - * but we do not yet know remote credentials. - * As per sect 7.2 of ICE (ID-19), we send a reply --- -2.13.6 - - -From 0672758b9621801c8f0d9e3c920370983b267a68 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 12:29:29 -0400 -Subject: [PATCH 04/70] agent: Don't set variable that won't be used - -It exits the loop immediately, so no point to set the variable. -And it makes the clang static analyzer happy. ---- - agent/agent.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 4d9381c..8ba99bc 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -4245,7 +4245,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - "Component removed during call."); - - component = NULL; -- error_reported = TRUE; - - goto recv_error; - } --- -2.13.6 - - -From db05e8b0fdc713df93cd6a4c3914e5aee38b2391 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 12:30:27 -0400 -Subject: [PATCH 05/70] Make clang-analyzer happy - -Various little things, none of which should make a functional difference. ---- - agent/agent.c | 1 - - agent/conncheck.c | 2 +- - socket/udp-turn.c | 5 ++--- - stun/usages/bind.c | 4 +++- - 4 files changed, 6 insertions(+), 6 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 8ba99bc..28d7ccf 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -1552,7 +1552,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, - - if (len == 0) { - /* Reached EOS. */ -- len = 0; - goto done; - } else if (len < 0 && - pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) { -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 7096b42..1dc13dd 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -606,7 +606,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - /* step: when there's no pair in the Waiting state, - * unfreeze a new pair and check it - */ -- res = priv_conn_check_unfreeze_next (agent); -+ priv_conn_check_unfreeze_next (agent); - - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index cc3409b..a9c57e5 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock, - - /* Split up the monolithic buffer again into the caller-provided buffers. */ - if (parsed_buffer_length > 0 && allocated_buffer) { -- parsed_buffer_length = -- memcpy_buffer_to_input_message (message, buffer, -- parsed_buffer_length); -+ memcpy_buffer_to_input_message (message, buffer, -+ parsed_buffer_length); - } - - if (allocated_buffer) -diff --git a/stun/usages/bind.c b/stun/usages/bind.c -index 8dd7afc..d56790f 100644 ---- a/stun/usages/bind.c -+++ b/stun/usages/bind.c -@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - size_t len; - StunUsageTransReturn ret; - int val; -- struct sockaddr_storage alternate_server; -+ struct sockaddr_storage alternate_server = { AF_UNSPEC } ; - socklen_t alternate_server_len = sizeof (alternate_server); - StunUsageBindReturn bind_ret; - -@@ -548,6 +548,8 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) { - stun_trans_deinit (&trans); - -+ assert (alternate_server.ss_family != AF_UNSPEC); -+ - ret = stun_trans_create (&trans, SOCK_DGRAM, 0, - (struct sockaddr *) &alternate_server, alternate_server_len); - --- -2.13.6 - - -From cd255bddc7fa0ddae056b5358a22b380c4eefc42 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 15:24:43 -0400 -Subject: [PATCH 06/70] udp-turn: Add some const to internal APIs - ---- - agent/component.c | 2 +- - socket/udp-turn.c | 10 +++++----- - socket/udp-turn.h | 7 ++++--- - 3 files changed, 10 insertions(+), 9 deletions(-) - -diff --git a/agent/component.c b/agent/component.c -index 32f7463..a679b30 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -380,7 +380,7 @@ nice_component_restart (NiceComponent *cmp) - for (i = cmp->remote_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; - -- /* note: do not remove the local candidate that is -+ /* note: do not remove the remote candidate that is - * currently part of the 'selected pair', see ICE - * 9.1.1.1. "ICE Restarts" (ID-19) */ - if (candidate == cmp->selected_pair.remote) { -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index a9c57e5..190a9ea 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data) - - NiceSocket * - nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, -- NiceSocket *base_socket, NiceAddress *server_addr, -- gchar *username, gchar *password, -+ NiceSocket *base_socket, const NiceAddress *server_addr, -+ const gchar *username, const gchar *password, - NiceTurnSocketCompatibility compatibility) - { - UdpTurnPriv *priv; -@@ -1184,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - gsize - nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - NiceAddress *from, gsize len, guint8 *buf, -- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len) -+ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len) - { - - UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv; -@@ -1194,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - ChannelBinding *binding = NULL; - - union { -- guint8 *u8; -- guint16 *u16; -+ const guint8 *u8; -+ const guint16 *u16; - } recv_buf; - - /* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN) -diff --git a/socket/udp-turn.h b/socket/udp-turn.h -index b1eeeb4..df10a1c 100644 ---- a/socket/udp-turn.h -+++ b/socket/udp-turn.h -@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - gsize - nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - NiceAddress *from, gsize len, guint8 *buf, -- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len); -+ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len); - - gboolean - nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer); - - NiceSocket * - nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, -- NiceSocket *base_socket, NiceAddress *server_addr, -- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility); -+ NiceSocket *base_socket, const NiceAddress *server_addr, -+ const gchar *username, const gchar *password, -+ NiceTurnSocketCompatibility compatibility); - - void - nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); --- -2.13.6 - - -From e56b910d2d8b70f5677bbd4be579d5b95aff33ad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 16:16:05 -0400 -Subject: [PATCH 07/70] agent: Separate return from NiceSocket and internal - enum - -The same variable was used for return values from NiceSocket and -for the internal enum, but 0 and -1 have different meanings in both. ---- - agent/agent.c | 35 +++++++++++++++++++---------------- - 1 file changed, 19 insertions(+), 16 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 28d7ccf..7b8a9fc 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3422,7 +3422,8 @@ agent_recv_message_unlocked ( - { - NiceAddress from; - GList *item; -- gint retval; -+ RecvStatus retval; -+ gint sockret; - gboolean is_turn = FALSE; - - /* We need an address for packet parsing, below. */ -@@ -3483,8 +3484,8 @@ agent_recv_message_unlocked ( - local_bufs[i + 1].buffer = message->buffers[i].buffer; - local_bufs[i + 1].size = message->buffers[i].size; - } -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - message->length = ntohs (rfc4571_frame); - } - } else { -@@ -3499,7 +3500,7 @@ agent_recv_message_unlocked ( - _priv_set_socket_tos (agent, new_socket, stream->tos); - nice_component_attach_socket (component, new_socket); - } -- retval = 0; -+ sockret = 0; - } else { - /* In the case of a real ICE-TCP connection, we can use the socket as a - * bytestream and do the read here with caching of data being read -@@ -3508,9 +3509,9 @@ agent_recv_message_unlocked ( - - /* TODO: Support bytestream reads */ - message->length = 0; -- retval = 0; -+ sockret = 0; - if (available <= 0) { -- retval = available; -+ sockret = available; - - /* If we don't call check_connect_result on an outbound connection, - * then is_connected will always return FALSE. That's why we check -@@ -3523,7 +3524,7 @@ agent_recv_message_unlocked ( - * not connected, it means that it failed to connect, so we must - * return an error to make the socket fail/closed - */ -- retval = -1; -+ sockret = -1; - } else { - gint flags = G_SOCKET_MSG_PEEK; - -@@ -3536,7 +3537,7 @@ agent_recv_message_unlocked ( - */ - if (g_socket_receive_message (nicesock->fileno, NULL, - NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0) -- retval = -1; -+ sockret = -1; - } - } else if (agent->rfc4571_expecting_length == 0) { - if ((gsize) available >= sizeof(guint16)) { -@@ -3544,8 +3545,8 @@ agent_recv_message_unlocked ( - GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)}; - NiceInputMessage local_message = { &local_buf, 1, message->from, 0}; - -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - agent->rfc4571_expecting_length = ntohs (rfc4571_frame); - available = g_socket_get_available_bytes (nicesock->fileno); - } -@@ -3589,8 +3590,8 @@ agent_recv_message_unlocked ( - off += local_bufs[i].size; - } - } -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - message->length = local_message.length; - agent->rfc4571_expecting_length -= local_message.length; - } -@@ -3598,20 +3599,22 @@ agent_recv_message_unlocked ( - } - } - } else { -- retval = nice_socket_recv_messages (nicesock, message, 1); -+ sockret = nice_socket_recv_messages (nicesock, message, 1); - } - -- if (retval == 0) { -+ if (sockret == 0) { - retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ - nice_debug_verbose ("%s: Agent %p: no message available on read attempt", - G_STRFUNC, agent); - goto done; -- } else if (retval < 0) { -+ } else if (sockret < 0) { - nice_debug ("Agent %p: %s returned %d, errno (%d) : %s", -- agent, G_STRFUNC, retval, errno, g_strerror (errno)); -+ agent, G_STRFUNC, sockret, errno, g_strerror (errno)); - - retval = RECV_ERROR; - goto done; -+ } else { -+ retval = sockret; - } - - if (retval == RECV_OOB || message->length == 0) { --- -2.13.6 - - -From 4e605885c9dcaeb3ee443ec902c9c9189b19043f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 16:16:46 -0400 -Subject: [PATCH 08/70] agent: Remove impossible case - ---- - agent/agent.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 7b8a9fc..77f27e3 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3617,7 +3617,8 @@ agent_recv_message_unlocked ( - retval = sockret; - } - -- if (retval == RECV_OOB || message->length == 0) { -+ g_assert (retval != RECV_OOB); -+ if (message->length == 0) { - retval = RECV_OOB; - nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC, - agent); --- -2.13.6 - - -From efc6a9be8cb34c899f0454c32e8a1e62b38df474 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 18:42:57 -0400 -Subject: [PATCH 09/70] tests: Use automake test-driver for valgrind - -This fixes the valgrind integration with the new test drivers. ---- - common.mk | 4 +- - scripts/valgrind-test-driver | 162 +++++++++++++++++++++++++++++++++++++++++++ - scripts/valgrind.sh | 28 -------- - 3 files changed, 165 insertions(+), 29 deletions(-) - create mode 100755 scripts/valgrind-test-driver - delete mode 100755 scripts/valgrind.sh - -diff --git a/common.mk b/common.mk -index e2ca3f4..b16380d 100644 ---- a/common.mk -+++ b/common.mk -@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice - - - check-valgrind: -- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check -+ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check -+ -+LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver - - .PHONY: check-valgrind -diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver -new file mode 100755 -index 0000000..5b660ee ---- /dev/null -+++ b/scripts/valgrind-test-driver -@@ -0,0 +1,162 @@ -+#! /bin/sh -+# test-driver - basic testsuite driver script. -+ -+scriptversion=2017-04-04.22; # UTC -+ -+# Copyright (C) 2011-2014 Free Software Foundation, Inc. -+# -+# This program is free software; you can redistribute it and/or modify -+# it under the terms of the GNU General Public License as published by -+# the Free Software Foundation; either version 2, or (at your option) -+# any later version. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see <
http://www.gnu.org/licenses/
>. -+ -+# As a special exception to the GNU General Public License, if you -+# distribute this file as part of a program that contains a -+# configuration script generated by Autoconf, you may include it under -+# the same distribution terms that you use for the rest of that program. -+ -+# This file is maintained in Automake, please report -+# bugs to <bug-automake(a)gnu.org> or send patches to -+# <automake-patches(a)gnu.org>. -+ -+# Make unconditional expansion of undefined variables an error. This -+# helps a lot in preventing typo-related bugs. -+set -u -+ -+usage_error () -+{ -+ echo "$0: $*" >&2 -+ print_usage >&2 -+ exit 2 -+} -+ -+print_usage () -+{ -+ cat <<END -+Usage: -+ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH -+ [--expect-failure={yes|no}] [--color-tests={yes|no}] -+ [--enable-hard-errors={yes|no}] [--] -+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] -+The '--test-name', '--log-file' and '--trs-file' options are mandatory. -+END -+} -+ -+test_name= # Used for reporting. -+log_file= # Where to save the output of the test script. -+trs_file= # Where to save the metadata of the test run. -+expect_failure=no -+color_tests=no -+enable_hard_errors=yes -+while test $# -gt 0; do -+ case $1 in -+ --help) print_usage; exit $?;; -+ --version) echo "test-driver $scriptversion"; exit $?;; -+ --test-name) test_name=$2; shift;; -+ --log-file) log_file=$2; shift;; -+ --trs-file) trs_file=$2; shift;; -+ --color-tests) color_tests=$2; shift;; -+ --expect-failure) expect_failure=$2; shift;; -+ --enable-hard-errors) enable_hard_errors=$2; shift;; -+ --) shift; break;; -+ -*) usage_error "invalid option: '$1'";; -+ *) break;; -+ esac -+ shift -+done -+ -+missing_opts= -+test x"$test_name" = x && missing_opts="$missing_opts --test-name" -+test x"$log_file" = x && missing_opts="$missing_opts --log-file" -+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" -+if test x"$missing_opts" != x; then -+ usage_error "the following mandatory options are missing:$missing_opts" -+fi -+ -+if test $# -eq 0; then -+ usage_error "missing argument" -+fi -+ -+if test $color_tests = yes; then -+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. -+ red='[0;31m' # Red. -+ grn='[0;32m' # Green. -+ lgn='[1;32m' # Light green. -+ blu='[1;34m' # Blue. -+ mgn='[0;35m' # Magenta. -+ std='[m' # No color. -+else -+ red= grn= lgn= blu= mgn= std= -+fi -+ -+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' -+trap "st=129; $do_exit" 1 -+trap "st=130; $do_exit" 2 -+trap "st=141; $do_exit" 13 -+trap "st=143; $do_exit" 15 -+ -+# Test script is run here. -+top_srcdir="`dirname $0`/.." -+tests_dir="${top_srcdir}/tests" -+ -+USE_VALGRIND="`printenv USE_VALGRIND`" -+ -+if test "x${USE_VALGRIND}" = "x1"; then -+ ${top_srcdir}/libtool --mode=execute valgrind \ -+ --leak-check=full \ -+ --show-reachable=no \ -+ --error-exitcode=1 \ -+ --suppressions=$tests_dir/libnice.supp \ -+ --num-callers=30 "$@" >$log_file 2>&1 -+else -+ "$@" >$log_file 2>&1 -+fi -+estatus=$? -+ -+if test $enable_hard_errors = no && test $estatus -eq 99; then -+ tweaked_estatus=1 -+else -+ tweaked_estatus=$estatus -+fi -+ -+case $tweaked_estatus:$expect_failure in -+ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; -+ 0:*) col=$grn res=PASS recheck=no gcopy=no;; -+ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; -+ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; -+ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; -+ *:*) col=$red res=FAIL recheck=yes gcopy=yes;; -+esac -+ -+# Report the test outcome and exit status in the logs, so that one can -+# know whether the test passed or failed simply by looking at the '.log' -+# file, without the need of also peaking into the corresponding '.trs' -+# file (automake bug#11814). -+echo "$res $test_name (exit status: $estatus)" >>$log_file -+ -+# Report outcome to console. -+echo "${col}${res}${std}: $test_name" -+ -+# Register the test result, and other relevant metadata. -+echo ":test-result: $res" > $trs_file -+echo ":global-test-result: $res" >> $trs_file -+echo ":recheck: $recheck" >> $trs_file -+echo ":copy-in-global-log: $gcopy" >> $trs_file -+ -+# Local Variables: -+# mode: shell-script -+# sh-indentation: 2 -+# eval: (add-hook 'write-file-hooks 'time-stamp) -+# time-stamp-start: "scriptversion=" -+# time-stamp-format: "%:y-%02m-%02d.%02H" -+# time-stamp-time-zone: "UTC" -+# time-stamp-end: "; # UTC" -+# End: -diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh -deleted file mode 100755 -index 2864b6f..0000000 ---- a/scripts/valgrind.sh -+++ /dev/null -@@ -1,28 +0,0 @@ --#!/bin/sh -- --export G_SLICE=always-malloc --export G_DEBUG=gc-friendly -- --tests_dir="`dirname $0`/../tests" -- --report=`libtool --mode=execute valgrind \ -- --leak-check=full \ -- --show-reachable=no \ -- --error-exitcode=1 \ -- --suppressions=$tests_dir/libnice.supp \ -- --num-callers=30 \ -- $1 2>&1` -- --#if echo "$report" | grep -q ==; then --if test $? != 0; then -- echo "$report" -- exit 1 --fi -- --if echo "$report" | grep -q "definitely lost"; then -- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then -- echo "$report" -- exit 1 -- fi --fi -- --- -2.13.6 - - -From ae6d939e48366b80570d713b83334191b0982e71 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 20:34:05 -0400 -Subject: [PATCH 10/70] debug: Use libnice-verbose, not libnice-nice-verbose - ---- - agent/debug.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/debug.c b/agent/debug.c -index e1a298c..84fee94 100644 ---- a/agent/debug.c -+++ b/agent/debug.c -@@ -102,7 +102,7 @@ void nice_debug_init (void) - flags |= g_parse_debug_string (gflags_string, gkeys, 4); - if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) - flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; -- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { -+ if (gflags_string && strstr (gflags_string, "libnice-verbose")) { - flags |= NICE_DEBUG_NICE_VERBOSE; - } - --- -2.13.6 - - -From 10c557f23f8337f1304fff27bd85d2eb713cb249 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Wed, 5 Apr 2017 17:01:35 -0400 -Subject: [PATCH 11/70] test-credentials: Fix leak - ---- - tests/test-credentials.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/tests/test-credentials.c b/tests/test-credentials.c -index 1de4e49..f1ea89d 100644 ---- a/tests/test-credentials.c -+++ b/tests/test-credentials.c -@@ -184,6 +184,8 @@ int main (void) - nice_agent_get_local_credentials (lagent, 1, &ufrag, &password); - g_assert (g_strcmp0("unicorns", ufrag) == 0); - g_assert (g_strcmp0("awesome", password) == 0); -+ g_free (ufrag); -+ g_free (password); - - nice_agent_gather_candidates (lagent, 1); - nice_agent_gather_candidates (ragent, 1); --- -2.13.6 - - -From 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 14:41:51 -0400 -Subject: [PATCH 12/70] candidate: Add equality check function - -Add a function that can check if two candidates point to the same place. - -https://phabricator.freedesktop.org/T104 - -Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> -Differential Revision:
https://phabricator.freedesktop.org/D1715
---- - agent/candidate.c | 11 +++++++++++ - agent/candidate.h | 18 +++++++++++++++++- - docs/reference/libnice/libnice-docs.xml | 4 ++++ - docs/reference/libnice/libnice-sections.txt | 1 + - nice/libnice.sym | 1 + - 5 files changed, 34 insertions(+), 1 deletion(-) - -diff --git a/agent/candidate.c b/agent/candidate.c -index 27966ef..85f8f65 100644 ---- a/agent/candidate.c -+++ b/agent/candidate.c -@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate) - - return copy; - } -+ -+NICEAPI_EXPORT gboolean -+nice_candidate_equal_target (const NiceCandidate *candidate1, -+ const NiceCandidate *candidate2) -+{ -+ g_return_val_if_fail (candidate1 != NULL, FALSE); -+ g_return_val_if_fail (candidate2 != NULL, FALSE); -+ -+ return (candidate1->transport == candidate2->transport && -+ nice_address_equal (&candidate1->addr, &candidate2->addr)); -+} -diff --git a/agent/candidate.h b/agent/candidate.h -index fadfce3..e556c16 100644 ---- a/agent/candidate.h -+++ b/agent/candidate.h -@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate); - NiceCandidate * - nice_candidate_copy (const NiceCandidate *candidate); - --GType nice_candidate_get_type (void); -+/** -+ * nice_candidate_equal_target: -+ * @candidate1: A candidate -+ * @candidate2: A candidate -+ * -+ * Verifies that the candidates point to the same place, meaning they have -+ * the same transport and the same address. It ignores all other aspects. -+ * -+ * Returns: %TRUE if the candidates point to the same place -+ * -+ * Since: 0.1.15 -+ */ -+gboolean -+nice_candidate_equal_target (const NiceCandidate *candidate1, -+ const NiceCandidate *candidate2); -+ -+ GType nice_candidate_get_type (void); - - /** - * NICE_TYPE_CANDIDATE: -diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml -index 53487bc..391be1a 100644 ---- a/docs/reference/libnice/libnice-docs.xml -+++ b/docs/reference/libnice/libnice-docs.xml -@@ -105,6 +105,10 @@ - <title>Index of new symbols in 0.1.14</title> - <xi:include href="xml/api-index-0.1.14.xml"><xi:fallback/></xi:include> - </index> -+ <index role="0.1.15"> -+ <title>Index of new symbols in 0.1.15</title> -+ <xi:include href="xml/api-index-0.1.15.xml"><xi:fallback/></xi:include> -+ </index> - <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> - </part> - </book> -diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt -index d377257..88a6cd2 100644 ---- a/docs/reference/libnice/libnice-sections.txt -+++ b/docs/reference/libnice/libnice-sections.txt -@@ -76,6 +76,7 @@ NICE_CANDIDATE_MAX_FOUNDATION - nice_candidate_new - nice_candidate_free - nice_candidate_copy -+nice_candidate_equal_target - <SUBSECTION Standard> - NICE_TYPE_CANDIDATE - nice_candidate_get_type -diff --git a/nice/libnice.sym b/nice/libnice.sym -index b04bb95..1e522ad 100644 ---- a/nice/libnice.sym -+++ b/nice/libnice.sym -@@ -58,6 +58,7 @@ nice_agent_set_software - nice_agent_set_stream_name - nice_agent_set_stream_tos - nice_candidate_copy -+nice_candidate_equal_target - nice_candidate_free - nice_candidate_new - nice_component_state_to_string --- -2.13.6 - - -From ffc7fddac42728bac6e4753a17bc52e5e610ae8b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 4 Apr 2017 21:27:39 -0400 -Subject: [PATCH 13/70] agent: Drop packets not from validated addresses - -This is required by the WebRTC spec. - -Remove test-mainloop as it doesnt even try to do -a negotiation. - -https://phabricator.freedesktop.org/T104 - -Differential Revision:
https://phabricator.freedesktop.org/D1716
---- - agent/agent-priv.h | 2 + - agent/agent.c | 20 +++++++++- - agent/component.c | 90 +++++++++++++++++++++++++++++++++++++++++ - agent/component.h | 10 +++++ - agent/conncheck.c | 8 +++- - tests/Makefile.am | 1 - - tests/test-mainloop.c | 108 -------------------------------------------------- - 7 files changed, 127 insertions(+), 112 deletions(-) - delete mode 100644 tests/test-mainloop.c - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 4d8c9b8..ada3630 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -114,6 +114,8 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - * MTU and estimated typical sizes of ICE STUN packet */ - #define MAX_STUN_DATAGRAM_PAYLOAD 1300 - -+#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -diff --git a/agent/agent.c b/agent/agent.c -index 77f27e3..eff62f0 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3682,8 +3682,6 @@ agent_recv_message_unlocked ( - if (retval == RECV_OOB) - goto done; - -- agent->media_after_tick = TRUE; -- - /* If the message’s stated length is equal to its actual length, it’s probably - * a STUN message; otherwise it’s probably data. */ - if (stun_message_validate_buffer_length_fast ( -@@ -3715,6 +3713,7 @@ agent_recv_message_unlocked ( - nice_debug ("%s: Valid STUN packet received.", G_STRFUNC); - retval = RECV_OOB; - g_free (big_buf); -+ agent->media_after_tick = TRUE; - goto done; - } - } -@@ -3725,6 +3724,23 @@ agent_recv_message_unlocked ( - g_free (big_buf); - } - -+ if (!nice_component_verify_remote_candidate (component, -+ message->from, nicesock)) { -+ if (nice_debug_is_verbose ()) { -+ gchar str[INET6_ADDRSTRLEN]; -+ -+ nice_address_to_string (message->from, str); -+ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" -+ " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, -+ nice_address_get_port (message->from), nicesock->type); -+ } -+ -+ retval = RECV_OOB; -+ goto done; -+ } -+ -+ agent->media_after_tick = TRUE; -+ - /* Unhandled STUN; try handling TCP data, then pass to the client. */ - if (message->length > 0 && agent->reliable) { - if (!nice_socket_is_reliable (nicesock) && -diff --git a/agent/component.c b/agent/component.c -index a679b30..ba28ffa 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -435,6 +435,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa - component->selected_pair.remote = pair->remote; - component->selected_pair.priority = pair->priority; - component->selected_pair.prflx_priority = pair->prflx_priority; -+ -+ nice_component_add_valid_candidate (component, pair->remote); - } - - /* -@@ -514,6 +516,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, - component->selected_pair.remote = remote; - component->selected_pair.priority = priority; - -+ /* Get into fallback mode where packets from any source is accepted once -+ * this has been called. This is the expected behavior of pre-ICE SIP. -+ */ -+ component->fallback_mode = TRUE; -+ - return local; - } - -@@ -1107,6 +1114,9 @@ nice_component_finalize (GObject *obj) - g_warn_if_fail (cmp->remote_candidates == NULL); - g_warn_if_fail (cmp->incoming_checks == NULL); - -+ g_list_free_full (cmp->valid_candidates, -+ (GDestroyNotify) nice_candidate_free); -+ - g_clear_object (&cmp->tcp); - g_clear_object (&cmp->stop_cancellable); - g_clear_object (&cmp->iostream); -@@ -1421,3 +1431,83 @@ turn_server_unref (TurnServer *turn) - g_slice_free (TurnServer, turn); - } - } -+ -+void -+nice_component_add_valid_candidate (NiceComponent *component, -+ const NiceCandidate *candidate) -+{ -+ guint count = 0; -+ GList *item, *last = NULL; -+ -+ for (item = component->valid_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ last = item; -+ count++; -+ if (nice_candidate_equal_target (cand, candidate)) -+ return; -+ } -+ -+ /* New candidate */ -+ -+ if (nice_debug_is_enabled ()) { -+ char str[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&candidate->addr, str); -+ nice_debug ("Agent %p : %d:%d Adding valid source" -+ " candidate: %s:%d trans: %d\n", component->agent, -+ candidate->stream_id, candidate->component_id, str, -+ nice_address_get_port (&candidate->addr), candidate->transport); -+ } -+ -+ component->valid_candidates = g_list_prepend ( -+ component->valid_candidates, nice_candidate_copy (candidate)); -+ -+ /* Delete the last one to make sure we don't have a list that is too long, -+ * the candidates are not freed on ICE restart as this would be more complex, -+ * we just keep the list not too long. -+ */ -+ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) { -+ NiceCandidate *cand = last->data; -+ -+ component->valid_candidates = g_list_delete_link ( -+ component->valid_candidates, last); -+ nice_candidate_free (cand); -+ } -+} -+ -+gboolean -+nice_component_verify_remote_candidate (NiceComponent *component, -+ const NiceAddress *address, NiceSocket *nicesock) -+{ -+ GList *item; -+ -+ if (component->fallback_mode) -+ return TRUE; -+ -+ for (item = component->valid_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD && -+ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && -+ nice_address_equal (address, &cand->addr)) { -+ /* fast return if it's already the first */ -+ if (item == component->valid_candidates) -+ return TRUE; -+ -+ /* Put the current candidate at the top so that in the normal use-case, -+ * this function becomes O(1). -+ */ -+ component->valid_candidates = g_list_remove_link ( -+ component->valid_candidates, item); -+ component->valid_candidates = g_list_concat (item, -+ component->valid_candidates); -+ -+ return TRUE; -+ } -+ } -+ -+ return FALSE; -+} -diff --git a/agent/component.h b/agent/component.h -index 6712794..a8a1222 100644 ---- a/agent/component.h -+++ b/agent/component.h -@@ -159,12 +159,14 @@ struct _NiceComponent { - NiceComponentState state; - GSList *local_candidates; /* list of NiceCandidate objs */ - GSList *remote_candidates; /* list of NiceCandidate objs */ -+ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */ - GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */ - guint socket_sources_age; /* incremented when socket_sources changes */ - GSList *incoming_checks; /* list of IncomingCheck objs */ - GList *turn_servers; /* List of TurnServer objs */ - CandidatePair selected_pair; /* independent from checklists, - see ICE 11.1. "Sending Media" (ID-19) */ -+ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */ - NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */ - NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */ - /* I/O handling. The main context must always be non-NULL, and is used for all -@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn); - void - turn_server_unref (TurnServer *turn); - -+void -+nice_component_add_valid_candidate (NiceComponent *component, -+ const NiceCandidate *candidate); -+ -+gboolean -+nice_component_verify_remote_candidate (NiceComponent *component, -+ const NiceAddress *address, NiceSocket *nicesock); -+ - - G_END_DECLS - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 1dc13dd..7ffa3db 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2627,6 +2627,7 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); -+ nice_component_add_valid_candidate (component, remote_candidate); - } - else { - if (!local_cand) { -@@ -2652,8 +2653,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - - /* note: this is same as "adding to VALID LIST" in the spec - text */ -- if (new_pair) -+ if (new_pair) { - new_pair->valid = TRUE; -+ nice_component_add_valid_candidate (component, remote_candidate); -+ } - - return new_pair; - } -@@ -2739,6 +2742,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - nice_debug ("Agent %p : Mapped address not found." - " conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); -+ nice_component_add_valid_candidate (component, p->remote); - } else { - ok_pair = priv_process_response_check_for_reflexive (agent, - stream, component, p, sockptr, &sockaddr.addr, -@@ -3654,6 +3658,8 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - } - } - -+ nice_component_add_valid_candidate (component, remote_candidate); -+ - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 7bfe075..d24a2aa 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -45,7 +45,6 @@ check_PROGRAMS = \ - test-send-recv \ - test-socket-is-based-on \ - test-priority \ -- test-mainloop \ - test-fullmode \ - test-restart \ - test-fallback \ -diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c -deleted file mode 100644 -index 7c52daa..0000000 ---- a/tests/test-mainloop.c -+++ /dev/null -@@ -1,108 +0,0 @@ --/* -- * This file is part of the Nice GLib ICE library. -- * -- * (C) 2006, 2007 Collabora Ltd. -- * Contact: Dafydd Harries -- * (C) 2006, 2007 Nokia Corporation. All rights reserved. -- * Contact: Kai Vehmanen -- * -- * The contents of this file are subject to the Mozilla Public License Version -- * 1.1 (the "License"); you may not use this file except in compliance with -- * the License. You may obtain a copy of the License at -- *
http://www.mozilla.org/MPL/
-- * -- * Software distributed under the License is distributed on an "AS IS" basis, -- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -- * for the specific language governing rights and limitations under the -- * License. -- * -- * The Original Code is the Nice GLib ICE library. -- * -- * The Initial Developers of the Original Code are Collabora Ltd and Nokia -- * Corporation. All Rights Reserved. -- * -- * Contributors: -- * Dafydd Harries, Collabora Ltd. -- * Kai Vehmanen, Nokia -- * -- * Alternatively, the contents of this file may be used under the terms of the -- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -- * case the provisions of LGPL are applicable instead of those above. If you -- * wish to allow use of your version of this file only under the terms of the -- * LGPL and not to allow others to use your version of this file under the -- * MPL, indicate your decision by deleting the provisions above and replace -- * them with the notice and other provisions required by the LGPL. If you do -- * not delete the provisions above, a recipient may use your version of this -- * file under either the MPL or the LGPL. -- */ --#ifdef HAVE_CONFIG_H --# include <config.h> --#endif -- --#include <string.h> -- --#include <nice/nice.h> --#include "socket/socket.h" -- --static GMainLoop *loop = NULL; -- --static void --recv_cb ( -- NiceAgent *agent, -- guint stream_id, -- guint component_id, -- guint len, -- gchar *buf, -- gpointer data) --{ -- g_assert (agent != NULL); -- g_assert (stream_id == 1); -- g_assert (component_id == 1); -- g_assert (len == 6); -- g_assert (0 == strncmp (buf, "\x80hello", len)); -- g_assert (42 == GPOINTER_TO_UINT (data)); -- g_main_loop_quit (loop); --} -- --int --main (void) --{ -- NiceAgent *agent; -- NiceAddress addr; -- guint stream; -- -- nice_address_init (&addr); -- -- loop = g_main_loop_new (NULL, FALSE); -- -- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245); -- nice_address_set_ipv4 (&addr, 0x7f000001); -- nice_agent_add_local_address (agent, &addr); -- stream = nice_agent_add_stream (agent, 1); -- nice_agent_gather_candidates (agent, stream); -- -- // attach to default main context -- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP, -- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42)); -- -- { -- NiceCandidate *candidate; -- GSList *candidates, *i; -- -- candidates = nice_agent_get_local_candidates (agent, 1, 1); -- candidate = candidates->data; -- -- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello"); -- for (i = candidates; i; i = i->next) -- nice_candidate_free ((NiceCandidate *) i->data); -- g_slist_free (candidates); -- } -- -- g_main_loop_run (loop); -- -- nice_agent_remove_stream (agent, stream); -- g_object_unref (agent); -- -- return 0; --} -- --- -2.13.6 - - -From 8fc22b0034d04cbc222e0637152b1cee2879eef3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Wed, 5 Apr 2017 17:43:26 -0400 -Subject: [PATCH 14/70] tests_: Add test to verify that only packets from - validated addresses pass - -https://phabricator.freedesktop.org/T104 - -Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> -Differential Revision:
https://phabricator.freedesktop.org/D1717
---- - tests/Makefile.am | 5 +- - tests/test-drop-invalid.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 521 insertions(+), 1 deletion(-) - create mode 100644 tests/test-drop-invalid.c - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index d24a2aa..62d5d64 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -54,7 +54,8 @@ check_PROGRAMS = \ - test-tcp \ - test-icetcp \ - test-credentials \ -- test-turn -+ test-turn \ -+ test-drop-invalid - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -128,6 +129,8 @@ test_credentials_LDADD = $(COMMON_LDADD) - - test_turn_LDADD = $(COMMON_LDADD) - -+test_drop_invalid_LDADD = $(COMMON_LDADD) -+ - test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) - test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) - -diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c -new file mode 100644 -index 0000000..97e3586 ---- /dev/null -+++ b/tests/test-drop-invalid.c -@@ -0,0 +1,517 @@ -+/* -+ * This file is part of the Nice GLib ICE library. -+ * -+ * Unit test for ICE full-mode related features. -+ * -+ * (C) 2007 Nokia Corporation. All rights reserved. -+ * Contact: Kai Vehmanen -+ * (C) 2017 Collabora Ltd -+ * Contact: Olivier Crete <olivier.crete(a)collabora.com> -+ * -+ * The contents of this file are subject to the Mozilla Public License Version -+ * 1.1 (the "License"); you may not use this file except in compliance with -+ * the License. You may obtain a copy of the License at -+ *
http://www.mozilla.org/MPL/
-+ * -+ * Software distributed under the License is distributed on an "AS IS" basis, -+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -+ * for the specific language governing rights and limitations under the -+ * License. -+ * -+ * The Original Code is the Nice GLib ICE library. -+ * -+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia -+ * Corporation. All Rights Reserved. -+ * -+ * Contributors: -+ * Kai Vehmanen, Nokia -+ * -+ * Alternatively, the contents of this file may be used under the terms of the -+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -+ * case the provisions of LGPL are applicable instead of those above. If you -+ * wish to allow use of your version of this file only under the terms of the -+ * LGPL and not to allow others to use your version of this file under the -+ * MPL, indicate your decision by deleting the provisions above and replace -+ * them with the notice and other provisions required by the LGPL. If you do -+ * not delete the provisions above, a recipient may use your version of this -+ * file under either the MPL or the LGPL. -+ */ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include "socket/socket.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+ -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static guint global_components_ready_exit = 0; -+static guint global_components_failed = 0; -+static guint global_components_failed_exit = 0; -+static GMainLoop *global_mainloop = NULL; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static gboolean global_lagent_ibr_received = FALSE; -+static gboolean global_ragent_ibr_received = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+static gint global_ragent_read = 0; -+static guint global_exit_when_ibr_received = 0; -+ -+static void priv_print_global_status (void) -+{ -+ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done); -+ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]); -+ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]); -+ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands); -+} -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* Core of the test -+ * Assert on any unreleated packet received. This would include anything -+ * send before the negotiation is over. -+ */ -+ g_assert (len == 16); -+ g_assert (strncmp ("1234567812345678", buf, 16) == 0); -+ -+ if (component_id == 2) -+ return; -+ -+ if (GPOINTER_TO_UINT (user_data) == 2) { -+ g_debug ("right agent received %d bytes, stopping mainloop", len); -+ global_ragent_read = len; -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+ -+ if (global_lagent_gathering_done && -+ global_ragent_gathering_done) -+ g_main_loop_quit (global_mainloop); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; -+} -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ if (state == NICE_COMPONENT_STATE_FAILED) -+ global_components_failed++; -+ -+ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit); -+ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit); -+ -+ /* signal status via a global variable */ -+ if (global_components_ready == global_components_ready_exit && -+ global_components_failed == global_components_failed_exit) { -+ g_debug ("Components ready/failed achieved. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ return; -+ } -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; (void)component_id; -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, -+ gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; -+} -+ -+static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, -+ gchar *foundation, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation; -+} -+ -+static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_ibr_received = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_ibr_received = TRUE; -+ -+ if (global_exit_when_ibr_received) { -+ g_debug ("Received initial binding request. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ } -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL; -+ GSList *peer_cands = NULL; -+ GSList *item1, *item2; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ peer_cands = nice_agent_get_local_candidates (to, to_stream, component); -+ -+ /* -+ * Core of the test: -+ * -+ * Send packets that shoudl be dropped. -+ */ -+ -+ for (item1 = cands; item1; item1 = item1->next) { -+ NiceCandidate *cand = item1->data; -+ NiceSocket *nicesock = cand->sockptr; -+ -+ g_assert (nicesock); -+ -+ for (item2 = peer_cands; item2; item2 = item2->next) { -+ NiceCandidate *target_cand = item2->data; -+ -+ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB"); -+ } -+ -+ } -+ -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free); -+ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed) -+{ -+ guint ls_id, rs_id; -+ gint ret; -+ -+ /* XXX: dear compiler, this is for you */ -+ (void)baseaddr; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_components_ready_exit = ready; -+ global_components_failed = 0; -+ global_components_failed_exit = failed; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_ibr_received = -+ global_ragent_ibr_received = FALSE; -+ global_lagent_cands = -+ global_ragent_cands = 0; -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ /* step: add one stream, with RTP+RTCP components, to each agent */ -+ ls_id = nice_agent_add_stream (lagent, 2); -+ -+ rs_id = nice_agent_add_stream (ragent, 2); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000); -+ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001); -+ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345); -+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001); -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE); -+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL); -+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL); -+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ { -+ GSList *cands = NULL, *i; -+ NiceCandidate *cand = NULL; -+ -+ cands = nice_agent_get_local_candidates (lagent, ls_id, 1); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10000); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (lagent, ls_id, 2); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10001); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (ragent, rs_id, 1); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 12345); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (ragent, rs_id, 2); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10002); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ } -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (2)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (2)); -+ -+ /* step: run mainloop until local candidates are ready -+ * (see timer_cb() above) */ -+ if (global_lagent_gathering_done != TRUE || -+ global_ragent_gathering_done != TRUE) { -+ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'..."); -+ g_main_loop_run (global_mainloop); -+ g_assert (global_lagent_gathering_done == TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ } -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ /* step: pass the remote candidates to agents */ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP); -+ -+ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed..."); -+ -+ /* step: run the mainloop until connectivity checks succeed -+ * (see timer_cb() above) */ -+ g_main_loop_run (global_mainloop); -+ -+ /* note: verify that STUN binding requests were sent */ -+ g_assert (global_lagent_ibr_received == TRUE); -+ g_assert (global_ragent_ibr_received == TRUE); -+ -+ /* note: Send a packet from another address */ -+ /* These should also be ignored */ -+ { -+ NiceCandidate *local_cand = NULL; -+ NiceCandidate *remote_cand = NULL; -+ NiceSocket *tmpsock; -+ -+ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand, -+ &remote_cand)); -+ g_assert (local_cand); -+ g_assert (remote_cand); -+ -+ tmpsock = nice_udp_bsd_socket_new (NULL); -+ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD"); -+ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE"); -+ nice_socket_free (tmpsock); -+ } -+ -+ /* note: test payload send and receive */ -+ global_ragent_read = 0; -+ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"); -+ g_assert (ret != -1); -+ g_debug ("Sent %d bytes", ret); -+ g_assert (ret == 16); -+ while (global_ragent_read != 16) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_ragent_read == 16); -+ -+ g_debug ("test-drop-invalid: Ran mainloop, removing streams..."); -+ -+ /* step: clean up resources and exit */ -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ return 0; -+} -+ -+int main (void) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ NiceAddress baseaddr; -+ int result; -+ guint timer_id; -+ -+#ifdef G_OS_WIN32 -+ WSADATA w; -+ -+ WSAStartup(0x0202, &w); -+#endif -+ -+ global_mainloop = g_main_loop_new (NULL, FALSE); -+ -+ /* step: create the agents L and R */ -+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_RFC5245); -+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_RFC5245); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ -+ -+ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent"); -+ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent"); -+ -+ /* step: add a timer to catch state changes triggered by signals */ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ /* step: specify which local interface to use */ -+ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1")) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &baseaddr); -+ nice_agent_add_local_address (ragent, &baseaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-candidate", -+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "new-candidate", -+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received", -+ G_CALLBACK (cb_initial_binding_request_received), -+ GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received", -+ G_CALLBACK (cb_initial_binding_request_received), -+ GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ -+ -+ /* step: run test the first time */ -+ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time"); -+ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0); -+ priv_print_global_status (); -+ g_assert (result == 0); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY); -+ /* When using TURN, we get peer reflexive candidates for the host cands -+ that we removed so we can get another new_selected_pair signal later -+ depending on timing/racing, we could double (or not) the amount we expected -+ */ -+ -+ /* note: verify that correct number of local candidates were reported */ -+ g_assert (global_lagent_cands == 2); -+ g_assert (global_ragent_cands == 2); -+ -+ g_object_unref (lagent); -+ g_object_unref (ragent); -+ -+ g_main_loop_unref (global_mainloop); -+ global_mainloop = NULL; -+ -+ g_source_remove (timer_id); -+#ifdef G_OS_WIN32 -+ WSACleanup(); -+#endif -+ return result; -+} --- -2.13.6 - - -From f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 11 Apr 2017 16:42:55 -0400 -Subject: [PATCH 15/70] conncheck: Check the controlling state when the req was - sent - -It was checking when the pair was created, but the role may have -already changed when the request is sent. ---- - agent/conncheck.c | 30 +++++++++++++++++++----------- - agent/conncheck.h | 1 - - 2 files changed, 19 insertions(+), 12 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 7ffa3db..5501c2b 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1666,7 +1666,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - tmpbuf2, nice_address_get_port (&pair->remote->addr)); - } - pair->nominated = use_candidate; -- pair->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); - -@@ -2531,7 +2530,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->priority = nice_candidate_pair_priority (pair->remote->priority, - pair->local->priority); - pair->nominated = FALSE; -- pair->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local_cand)); - nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); -@@ -2777,16 +2775,26 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { - /* case: role conflict error, need to restart with new role */ - nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- /* note: our role might already have changed due to an -- * incoming request, but if not, change role now; -- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ -- priv_check_for_role_conflict (agent, !p->controlling); - -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_WAITING; -- priv_add_pair_to_triggered_check_queue (agent, p); -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ if (p->stun_message.buffer != NULL) { -+ guint64 tie; -+ gboolean controlled_mode; -+ -+ /* note: our role might already have changed due to an -+ * incoming request, but if not, change role now; -+ * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ -+ controlled_mode = (stun_message_find64 (&p->stun_message, -+ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -+ STUN_MESSAGE_RETURN_SUCCESS); -+ -+ priv_check_for_role_conflict (agent, controlled_mode); -+ -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; -+ p->state = NICE_CHECK_WAITING; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ } - trans_found = TRUE; - } else { - /* case: STUN error, the check STUN context was freed */ -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 10319cc..c204475 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -85,7 +85,6 @@ struct _CandidateCheckPair - gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; - NiceCheckState state; - gboolean nominated; -- gboolean controlling; - gboolean timer_restarted; - gboolean valid; - guint64 priority; --- -2.13.6 - - -From b0538d8c51f65019867b56a45cf90a70bef38f01 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 11 Apr 2017 18:31:21 -0400 -Subject: [PATCH 16/70] agent: Ignore remote candidate of non-accepted types - -If we disable ice-tcp or ice-udp, ignore the remote -candidates for those types. ---- - agent/agent.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/agent/agent.c b/agent/agent.c -index eff62f0..77fb1eb 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3112,6 +3112,13 @@ static gboolean priv_add_remote_candidate ( - NiceComponent *component; - NiceCandidate *candidate; - -+ if (transport == NICE_CANDIDATE_TRANSPORT_UDP && -+ !agent->use_ice_udp) -+ return FALSE; -+ if (transport != NICE_CANDIDATE_TRANSPORT_UDP && -+ !agent->use_ice_tcp) -+ return FALSE; -+ - if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) - return FALSE; - --- -2.13.6 - - -From f6f704c5e8d2193bc67ba2b697c77694e1698c43 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu, 9 Jun 2016 22:22:33 +0200 -Subject: [PATCH 17/70] stun timer: fix timeout of the last retransmission - -According to RFC 5389, section 7.2.1, a special timeout is applied to -the last retransmission (Rm * RTO), with Rm default value of 16, instead -of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7. - -As spotted by Olivier Crete, stun_timer_* is a public API, that cannot -be changed, and the initial delay (RTO) is not preserved in the -stun_timer_s struct. So we use a hack that implicitely guess Rm from the -number of transmissions Rc, by generalizing the default value of the -spec for Rm and Rc to other values of Rc passed in stun_timer_start( - -According to the spec, with the default value of Rc=7, the last delay -should be (64 * RTO), and it is instead (16 * RTO). So the last delay -can be computed by dividing the penultimate delay by two, instead of -multiplying it by two. - -Differential Revision:
https://phabricator.freedesktop.org/D1108
---- - stun/usages/timer.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/stun/usages/timer.c b/stun/usages/timer.c -index 2862ab8..5370cba 100644 ---- a/stun/usages/timer.c -+++ b/stun/usages/timer.c -@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer) - if (timer->retransmissions >= timer->max_retransmissions) - return STUN_USAGE_TIMER_RETURN_TIMEOUT; - -- add_delay (&timer->deadline, timer->delay *= 2); -+ if (timer->retransmissions == timer->max_retransmissions - 1) -+ timer->delay = timer->delay / 2; -+ else -+ timer->delay = timer->delay * 2; -+ add_delay (&timer->deadline, timer->delay); - timer->retransmissions++; - return STUN_USAGE_TIMER_RETURN_RETRANSMIT; - } --- -2.13.6 - - -From 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 5 Apr 2016 21:32:39 +0200 -Subject: [PATCH 18/70] agent: do not create a GSource for UDP TURN socket - -With this patch, we don't create a new GSource for udp-turn socket, -because it would duplicate the packets already received on the base UDP -socket, as the underlying GSocket is the same. This is a race condition, -because an UDP packet arriving on the base socket, may randomly be -handled by the GSource callback created for the base socket (udp-bsd) of -the callback created for the udp-turn socket. Moreover this callback -already knows how to parse UDP datagrams received from a known turn -server. - -This patch also prevents a subtle bug, when a STUN request is received -directly from a peer, is handled by the udp turn socket. If the agent -already has a valid permission for this remote candidate, established -for another pair, it will happily send the STUN reply through the turn -relay. This generates a source address mismatch on the peer agent, when -it'll receive the STUN response from the turn relay instead of the -initial address the request has been sent to. - -Differential Revision:
https://phabricator.freedesktop.org/D932
---- - agent/component.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/agent/component.c b/agent/component.c -index ba28ffa..ab665b6 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context) - if (socket_source->socket->fileno == NULL) - return; - -+ /* Do not create a GSource for UDP turn socket, because it -+ * would duplicate the packets already received on the base -+ * UDP socket. -+ */ -+ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN) -+ return; -+ - /* Create a source. */ - source = g_socket_create_source (socket_source->socket->fileno, - G_IO_IN, NULL); --- -2.13.6 - - -From d5446a72233eab8501be0b3fb9060c8be3ba034b Mon Sep 17 00:00:00 2001 -From: Philip Withnall <withnall(a)endlessm.com> -Date: Mon, 1 May 2017 08:51:40 +0100 -Subject: [PATCH 19/70] examples: Stop installing the examples -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -There’s no point in installing them; their benefit is in providing -example code to developers. - -Debian doesn’t package them; Fedora packages them in a separate -subpackage which will have to disappear. - -Signed-off-by: Philip Withnall <withnall(a)endlessm.com> -Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> -Differential Revision:
https://phabricator.freedesktop.org/D1737
---- - examples/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/examples/Makefile.am b/examples/Makefile.am -index 1e7decf..9c80854 100644 ---- a/examples/Makefile.am -+++ b/examples/Makefile.am -@@ -18,7 +18,7 @@ AM_CFLAGS = \ - $(GLIB_CFLAGS) \ - $(GUPNP_CFLAGS) - --bin_PROGRAMS = simple-example threaded-example sdp-example -+noinst_PROGRAMS = simple-example threaded-example sdp-example - - simple_example_SOURCES = simple-example.c - simple_example_LDADD = $(top_builddir)/agent/libagent.la \ --- -2.13.6 - - -From b4abda09c79e4ce372a3771300abf568c85c7ff5 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu, 21 Apr 2016 18:18:59 +0200 -Subject: [PATCH 20/70] interfaces: ignore predefined network interfaces - -Some interfaces, like the one managed by libvirtd to provide a network -bridge to locally hosted virtual machines, can be completely ignored -when gathering ICE candidates. The motivation for adding this -possibility is that, ignoring them doesn't remove capabilities, and -improves the overall speed of the connection check method, by reducing -the number of pairs to be tested. This patch adds the possibility to -define such interfaces in the configuration script. - -Differential Revision:
https://phabricator.freedesktop.org/D948
---- - agent/interfaces.c | 6 ++++++ - configure.ac | 14 ++++++++++++++ - 2 files changed, 20 insertions(+) - -diff --git a/agent/interfaces.c b/agent/interfaces.c -index 0fa2fd7..a81888e 100644 ---- a/agent/interfaces.c -+++ b/agent/interfaces.c -@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback) - nice_debug ("Ignoring loopback interface"); - g_free (addr_string); - } -+#ifdef IGNORED_IFACE_PREFIX -+ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) { -+ nice_debug ("Ignoring interface %s as it matches prefix %s", -+ ifa->ifa_name, IGNORED_IFACE_PREFIX); -+ g_free (addr_string); -+#endif - } else { - if (nice_interfaces_is_private_ip (ifa->ifa_addr)) - ips = add_ip_to_list (ips, addr_string, TRUE); -diff --git a/configure.ac b/configure.ac -index b39bfe3..98bbc08 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false) - # GObject introspection - GOBJECT_INTROSPECTION_CHECK([1.30.0]) - -+dnl Ignore a specific network interface name prefix from the connection check -+AC_MSG_CHECKING([whether to ignore a specific network interface name prefix]) -+AC_ARG_WITH([ignored-network-interface-prefix], -+ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string], -+ [Ignore network interfaces whose name starts with "string" from the ICE connection -+ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge -+ handled by libvirtd, do not help in finding connectivity.])], -+ [interface_prefix="$withval"]) -+AS_IF([test -n "$interface_prefix"], -+ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"], -+ [Ignore this network interface prefix from the connection check]) -+ AC_MSG_RESULT([yes, $interface_prefix])], -+ [AC_MSG_RESULT([no])]) -+ - AC_CONFIG_MACRO_DIR(m4) - - AC_OUTPUT --- -2.13.6 - - -From 80c613699786567fd93db74377138600794a86e0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Thu, 8 Jun 2017 16:34:21 -0400 -Subject: [PATCH 21/70] agent: Use base_addr to generate rport in SDP - -Reported by Capricornus (zhushengliang) - -https://phabricator.freedesktop.org/T7763 ---- - agent/agent.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 77fb1eb..1ff09af 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -5690,7 +5690,7 @@ _generate_candidate_sdp (NiceAgent *agent, - g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type)); - if (nice_address_is_valid (&candidate->base_addr) && - !nice_address_equal (&candidate->addr, &candidate->base_addr)) { -- port = nice_address_get_port (&candidate->addr); -+ port = nice_address_get_port (&candidate->base_addr); - nice_address_to_string (&candidate->base_addr, ip4); - g_string_append_printf (sdp, " raddr %s rport %d", ip4, - port == 0 ? 9 : port); --- -2.13.6 - - -From 8bb210c5af4bcaf342d7fa4fef6034269e976532 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu, 9 Jun 2016 23:28:43 +0200 -Subject: [PATCH 22/70] stun timer: make properties for stun timer tunables - -Three STUN binding request properties should be customisable. RFC 5245 -describes the retransmission timer of the STUN transaction 'RTO', and -RFC 5389 describes the number of retransmissions to send until a -response is received 'Rc'. The third property is the 'RTO' when -a reliable connection is used. - -RFC 5389 introduces a supplementary property 'Rm' as a multiplier used -to compute the final timeout RTO * Rm. However, this property is not -added in libnice, because this would require breaking the public API for -STUN. Currently, our STUN implementation hardcodes a division by two for -this final timeout. - -Differential Revision:
https://phabricator.freedesktop.org/D1109
---- - agent/agent-priv.h | 4 ++- - agent/agent.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ - agent/conncheck.c | 16 +++++---- - agent/discovery.c | 8 ++--- - stun/usages/timer.h | 6 +++- - 5 files changed, 118 insertions(+), 13 deletions(-) - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index ada3630..162ea63 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -106,7 +106,6 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - - #define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */ - #define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */ --#define NICE_AGENT_TIMER_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */ - #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */ - - -@@ -132,6 +131,9 @@ struct _NiceAgent - guint timer_ta; /* property: timer Ta */ - guint max_conn_checks; /* property: max connectivity checks */ - gboolean force_relay; /* property: force relay */ -+ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ -+ guint stun_initial_timeout; /* property: stun initial timeout, RTO */ -+ guint stun_reliable_timeout; /* property: stun reliable timeout */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -diff --git a/agent/agent.c b/agent/agent.c -index 1ff09af..25d7886 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -113,6 +113,9 @@ enum - PROP_BYTESTREAM_TCP, - PROP_KEEPALIVE_CONNCHECK, - PROP_FORCE_RELAY, -+ PROP_STUN_MAX_RETRANSMISSIONS, -+ PROP_STUN_INITIAL_TIMEOUT, -+ PROP_STUN_RELIABLE_TIMEOUT, - }; - - -@@ -708,6 +711,76 @@ nice_agent_class_init (NiceAgentClass *klass) - FALSE, - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:stun-max-retransmissions -+ * -+ * The maximum number of retransmissions of the STUN binding requests -+ * used in the gathering stage, to find our local candidates, and used -+ * in the connection check stage, to test the validity of each -+ * constructed pair. This property is described as 'Rc' in the RFC -+ * 5389, with a default value of 7. The timeout of each STUN request -+ * is doubled for each retransmission, so the choice of this value has -+ * a direct impact on the time needed to move from the CONNECTED state -+ * to the READY state, and on the time needed to complete the GATHERING -+ * state. -+ * -+ * Since: UNRELEASED -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, -+ g_param_spec_uint ( -+ "stun-max-retransmissions", -+ "STUN Max Retransmissions", -+ "Maximum number of STUN binding requests retransmissions " -+ "described as 'Rc' in the STUN specification.", -+ 1, 99, -+ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * NiceAgent:stun-initial-timeout -+ * -+ * The initial timeout (msecs) of the STUN binding requests -+ * used in the gathering stage, to find our local candidates. -+ * This property is described as 'RTO' in the RFC 5389 and RFC 5245. -+ * This timeout is doubled for each retransmission, until -+ * #NiceAgent:stun-max-retransmissions have been done, -+ * with an exception for the last restransmission, where the timeout is -+ * divided by two instead (RFC 5389 indicates that a customisable -+ * multiplier 'Rm' to 'RTO' should be used). -+ * -+ * Since: UNRELEASED -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, -+ g_param_spec_uint ( -+ "stun-initial-timeout", -+ "STUN Initial Timeout", -+ "STUN timeout in msecs of the initial binding requests used in the " -+ "gathering state, described as 'RTO' in the ICE specification.", -+ 20, 9999, -+ STUN_TIMER_DEFAULT_TIMEOUT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * NiceAgent:stun-reliable-timeout -+ * -+ * The initial timeout of the STUN binding requests used -+ * for a reliable timer. -+ * -+ * Since: UNRELEASED -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, -+ g_param_spec_uint ( -+ "stun-reliable-timeout", -+ "STUN Reliable Timeout", -+ "STUN timeout in msecs of the initial binding requests used for " -+ "a reliable timer.", -+ 20, 99999, -+ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ - /* install signals */ - - /** -@@ -1187,6 +1260,18 @@ nice_agent_get_property ( - g_value_set_boolean (value, agent->force_relay); - break; - -+ case PROP_STUN_MAX_RETRANSMISSIONS: -+ g_value_set_uint (value, agent->stun_max_retransmissions); -+ break; -+ -+ case PROP_STUN_INITIAL_TIMEOUT: -+ g_value_set_uint (value, agent->stun_initial_timeout); -+ break; -+ -+ case PROP_STUN_RELIABLE_TIMEOUT: -+ g_value_set_uint (value, agent->stun_reliable_timeout); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1374,6 +1459,18 @@ nice_agent_set_property ( - agent->force_relay = g_value_get_boolean (value); - break; - -+ case PROP_STUN_MAX_RETRANSMISSIONS: -+ agent->stun_max_retransmissions = g_value_get_uint (value); -+ break; -+ -+ case PROP_STUN_INITIAL_TIMEOUT: -+ agent->stun_initial_timeout = g_value_get_uint (value); -+ break; -+ -+ case PROP_STUN_RELIABLE_TIMEOUT: -+ agent->stun_reliable_timeout = g_value_get_uint (value); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 5501c2b..14fdcd9 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -888,8 +888,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - agent, buf_len, p->keepalive.stun_message.buffer); - - if (buf_len > 0) { -- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&p->keepalive.timer, -+ agent->stun_initial_timeout, -+ agent->stun_max_retransmissions); - - agent->media_after_tick = FALSE; - -@@ -1116,8 +1117,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand) - } - - if (buffer_len > 0) { -- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&cand->timer, -+ cand->agent->stun_initial_timeout, -+ cand->agent->stun_max_retransmissions); - - /* send the refresh */ - agent_socket_send (cand->nicesock, &cand->server, -@@ -2171,11 +2173,11 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - - if (buffer_len > 0) { - if (nice_socket_is_reliable(pair->sockptr)) { -- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -+ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); - } else { - stun_timer_start (&pair->timer, - priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ agent->stun_max_retransmissions); - } - - /* TCP-ACTIVE candidate must create a new socket before sending -@@ -2340,7 +2342,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { - stun_timer_start (&p->timer, - priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ agent->stun_max_retransmissions); - p->timer_restarted = TRUE; - } - } -diff --git a/agent/discovery.c b/agent/discovery.c -index 7a890a0..4cc99c2 100644 ---- a/agent/discovery.c -+++ b/agent/discovery.c -@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) - - if (buffer_len > 0) { - if (nice_socket_is_reliable (cand->nicesock)) { -- stun_timer_start_reliable (&cand->timer, -- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -+ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); - } else { -- stun_timer_start (&cand->timer, 200, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&cand->timer, -+ agent->stun_initial_timeout, -+ agent->stun_max_retransmissions); - } - - /* send the conncheck */ -diff --git a/stun/usages/timer.h b/stun/usages/timer.h -index e74353b..097e75b 100644 ---- a/stun/usages/timer.h -+++ b/stun/usages/timer.h -@@ -132,7 +132,11 @@ struct stun_timer_s { - * The default intial timeout to use for the timer - * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most - * cases as it is also what is used by SIP style VoIP when sending A-Law and -- * mu-Law audio, so 200ms should be hyper safe. -+ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout -+ * of 200ms, a default of 7 transmissions, the last timeout will be -+ * 16 * 200ms, and we expect to receive a response from the stun server -+ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial -+ * stun request has been sent. - */ - #define STUN_TIMER_DEFAULT_TIMEOUT 200 - --- -2.13.6 - - -From 7a2c1edf502849a868b6f1026e8e2c343dee4ded Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 6 Jun 2016 22:24:50 +0200 -Subject: [PATCH 23/70] conncheck: update selected pair when nominated flag is - set - -This modifies commit 8f1f615. It is better focused to update the -selected pair just after its nominated flag has been set. We also keep -the code homogeneous with other places, where the call to -priv_update_selected_pair() immediately follows the setting of -pair->nominated. Moreover in priv_update_check_list_state_for_ready(), -we would call priv_update_selected_pair() more times that necessary when -iterating on all nominated pairs. - -Differential Revision:
https://phabricator.freedesktop.org/D1125
---- - agent/conncheck.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 14fdcd9..8c55269 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data); - static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - NiceAgent *agent, guint stream_id, NiceComponent *component, - NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); -+static gboolean priv_update_selected_pair (NiceAgent *agent, -+ NiceComponent *component, CandidateCheckPair *pair); - - static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - { -@@ -515,6 +517,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -+ priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ - } -@@ -1530,7 +1533,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream - ++valid; - if (p->nominated == TRUE) { - ++nominated; -- priv_update_selected_pair (agent, component, p); - } - } - } --- -2.13.6 - - -From 3a58ba6120b188d78c5709e0349c0346bfa21c1a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 1 Feb 2016 11:10:21 +0100 -Subject: [PATCH 24/70] conncheck: peer reflexive candidates are not paired - -This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning -Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive -Candidates", where discovered candidates do not cause the creation -of new pairs to be checked. - -Differential Revision:
https://phabricator.freedesktop.org/D805
---- - agent/conncheck.c | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 8c55269..cdf1025 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1787,6 +1787,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone - - g_assert (remote != NULL); - -+ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates", -+ * the agent does not pair this candidate with any local candidates. -+ */ -+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && -+ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) -+ { -+ return added; -+ } -+ - for (i = component->local_candidates; i ; i = i->next) { - NiceCandidate *local = i->data; - -@@ -1821,6 +1830,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC - - g_assert (local != NULL); - -+ /* -+ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive -+ * Candidates", the peer reflexive candidate is not paired -+ * with other remote candidates -+ */ -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && -+ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) -+ { -+ return added; -+ } -+ - for (i = component->remote_candidates; i ; i = i->next) { - - NiceCandidate *remote = i->data; --- -2.13.6 - - -From a602ff57aae6a6afdeab843954c48e6fb5d82d31 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 13:02:45 +0200 -Subject: [PATCH 25/70] conncheck: fix pair state transition when successful - response is received - -According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a -successful check should go to state succeeded, not only the valid -pair built in section 7.1.3.2.2. - -Differential Revision:
https://phabricator.freedesktop.org/D810
---- - agent/conncheck.c | 17 +++++++++++++---- - 1 file changed, 13 insertions(+), 4 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index cdf1025..7fc2a1d 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2654,7 +2654,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - } - else { - if (!local_cand) { -- if (!agent->force_relay) -+ if (!agent->force_relay) { -+ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1. -+ * "Discovering Peer Reflexive Candidates" -+ */ - local_cand = discovery_add_peer_reflexive_candidate (agent, - stream->id, - component->id, -@@ -2662,8 +2665,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - sockptr, - local_candidate, - remote_candidate); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p", -+ agent, local_cand); -+ } - } - - /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 -@@ -2671,7 +2675,12 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (local_cand) - new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, - local_cand, p); -- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); -+ /* step: The agent sets the state of the pair that *generated* the check to -+ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" -+ */ -+ p->state = NICE_CHECK_SUCCEEDED; -+ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", -+ agent, p, new_pair); - } - - /* note: this is same as "adding to VALID LIST" in the spec --- -2.13.6 - - -From 0636f9addc041cf93c4ff4eaa351b1768d48a32e Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 19 Apr 2016 13:12:48 +0200 -Subject: [PATCH 26/70] conncheck: implement ice regular nomination method - -This patch implements Regular Nomation as described in RFC5245 -8.1.1.1. The controlling agent lets valid pairs accumulate, and -decides which pair to recheck with the use-candidate attribute set. -priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated -pair when acting as a STUN server, and -priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to -update the nominated pair when acting as a STUN client. A new -property is also added to the agent to control the nomination -mode, which can be regular of aggressive, with default value -set to aggressive. - -Two new flags are introduced in the CandidateCheckPair structure: - -- use_candidate_on_next_check indicates the STUN client to add the - use-candidate attribute when the pair will be checked. At this - time, the nominated flag has not been set on this pair yet. - -- mark_nominated_on_response_arrival indicates the STUN server - to nominate the pair when its succesfull response to a - previous triggered check will arrive (7.2.1.5, item #2) - -Differential Revision:
https://phabricator.freedesktop.org/D811
---- - agent/Makefile.am | 23 ++++ - agent/agent-priv.h | 8 ++ - agent/agent.c | 59 +++++++++ - agent/agent.h | 43 ++++++- - agent/conncheck.c | 178 +++++++++++++++++++++++++++- - agent/conncheck.h | 2 + - configure.ac | 1 + - docs/reference/libnice/libnice-sections.txt | 2 + - 8 files changed, 309 insertions(+), 7 deletions(-) - -diff --git a/agent/Makefile.am b/agent/Makefile.am -index b585393..915f312 100644 ---- a/agent/Makefile.am -+++ b/agent/Makefile.am -@@ -22,6 +22,12 @@ if WINDOWS - AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP - endif - -+BUILT_SOURCES = \ -+ agent-enum-types.h \ -+ agent-enum-types.c -+ -+CLEANFILES += $(BUILT_SOURCES) -+ - noinst_LTLIBRARIES = libagent.la - - libagent_la_SOURCES = \ -@@ -54,6 +60,23 @@ libagent_la_SOURCES = \ - outputstream.c \ - $(BUILT_SOURCES) - -+agent-enum-types.h: agent.h Makefile -+ $(AM_V_GEN)$(GLIB_MKENUMS) \ -+ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ -+ --fprod "/* enumerations from \"@filename@\" */\n" \ -+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ -+ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \ -+ $(addprefix $(srcdir)/,agent.h) > $@ -+ -+agent-enum-types.c: agent.h Makefile agent-enum-types.h -+ $(AM_V_GEN)$(GLIB_MKENUMS) \ -+ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \ -+ --fprod "\n/* enumerations from \"@filename@\" */" \ -+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \ -+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ -+ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \ -+ $(addprefix $(srcdir)/,agent.h) > $@ -+ - libagent_la_LIBADD = \ - $(top_builddir)/random/libnice-random.la \ - $(top_builddir)/socket/libsocket.la \ -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 162ea63..3384180 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -115,6 +115,13 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - - #define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ - -+/* A convenient macro to test if the agent is compatible with RFC5245 -+ * or OC2007R2. Specifically these two modes share the support -+ * of the regular or aggressive nomination mode */ -+#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \ -+ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ -+ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -@@ -134,6 +141,7 @@ struct _NiceAgent - guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ - guint stun_initial_timeout; /* property: stun initial timeout, RTO */ - guint stun_reliable_timeout; /* property: stun reliable timeout */ -+ NiceNominationMode nomination_mode; /* property: Nomination mode */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -diff --git a/agent/agent.c b/agent/agent.c -index 25d7886..577a7e0 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -73,6 +73,7 @@ - #include "interfaces.h" - - #include "pseudotcp.h" -+#include "agent-enum-types.h" - - /* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b - * wide. */ -@@ -116,6 +117,7 @@ enum - PROP_STUN_MAX_RETRANSMISSIONS, - PROP_STUN_INITIAL_TIMEOUT, - PROP_STUN_RELIABLE_TIMEOUT, -+ PROP_NOMINATION_MODE, - }; - - -@@ -440,6 +442,24 @@ nice_agent_class_init (NiceAgentClass *klass) - G_PARAM_READWRITE)); - - /** -+ * NiceAgent:nomination-mode: -+ * -+ * The nomination mode used in the ICE specification for describing -+ * the selection of valid pairs to be used upstream. -+ * <para> See also: #NiceNominationMode </para> -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, -+ g_param_spec_enum ( -+ "nomination-mode", -+ "ICE nomination mode", -+ "Nomination mode used in the ICE specification for describing " -+ "the selection of valid pairs to be used upstream", -+ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ -+ /** - * NiceAgent:proxy-ip: - * - * The proxy server IP used to bypass a proxy firewall -@@ -1096,6 +1116,7 @@ nice_agent_init (NiceAgent *agent) - agent->stun_server_port = DEFAULT_STUN_PORT; - agent->controlling_mode = TRUE; - agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; -+ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; - - agent->discovery_list = NULL; - agent->discovery_unsched_items = 0; -@@ -1144,6 +1165,23 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) - } - - -+NICEAPI_EXPORT NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ gboolean reliable, -+ NiceNominationMode nomination) -+{ -+ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, -+ "compatibility", compat, -+ "main-context", ctx, -+ "reliable", reliable, -+ "nomination-mode", nomination, -+ NULL); -+ -+ return agent; -+} -+ -+ - static void - nice_agent_get_property ( - GObject *object, -@@ -1190,6 +1228,10 @@ nice_agent_get_property ( - /* XXX: should we prune the list of already existing checks? */ - break; - -+ case PROP_NOMINATION_MODE: -+ g_value_set_enum (value, agent->nomination_mode); -+ break; -+ - case PROP_PROXY_IP: - g_value_set_string (value, agent->proxy_ip); - break; -@@ -1394,6 +1436,10 @@ nice_agent_set_property ( - agent->max_conn_checks = g_value_get_uint (value); - break; - -+ case PROP_NOMINATION_MODE: -+ agent->nomination_mode = g_value_get_enum (value); -+ break; -+ - case PROP_PROXY_IP: - g_free (agent->proxy_ip); - agent->proxy_ip = g_value_dup_string (value); -@@ -3298,6 +3344,19 @@ static gboolean priv_add_remote_candidate ( - username, password, priority); - } - -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ /* note: If there are TCP candidates for a media stream, -+ * a controlling agent MUST use the regular selection algorithm, -+ * RFC 6544, sect 8, "Concluding ICE Processing" -+ */ -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE && -+ transport != NICE_CANDIDATE_TRANSPORT_UDP) { -+ nice_debug ("Agent %p : we have TCP candidates, switching back " -+ "to regular nomination mode", agent); -+ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR; -+ } -+ } -+ - if (base_addr) - candidate->base_addr = *base_addr; - -diff --git a/agent/agent.h b/agent/agent.h -index 47c4d5a..6e233c6 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -377,6 +377,26 @@ typedef enum - NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP, - } NiceProxyType; - -+/** -+ * NiceNominationMode: -+ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode -+ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode -+ * -+ * An enum to specity the kind of nomination mode to use by -+ * the agent, as described in RFC 5245. Two modes exists, -+ * regular and aggressive. They differ by the way the controlling -+ * agent chooses to put the USE-CANDIDATE attribute in its STUN -+ * messages. The aggressive mode is supposed to nominate a pair -+ * faster, than the regular mode, potentially causing the nominated -+ * pair to change until the connection check completes. -+ * -+ * Since: UNRELEASED -+ */ -+typedef enum -+{ -+ NICE_NOMINATION_MODE_REGULAR = 0, -+ NICE_NOMINATION_MODE_AGGRESSIVE, -+} NiceNominationMode; - - /** - * NiceAgentRecvFunc: -@@ -429,6 +449,28 @@ NiceAgent * - nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - - /** -+ * nice_agent_new_full: -+ * @ctx: The Glib Mainloop Context to use for timers -+ * @compat: The compatibility mode of the agent -+ * @reliable: The reliability mode of the agent -+ * @nomination: The nomination mode of the agent -+ * -+ * Create a new #NiceAgent with parameters that must be be defined at -+ * construction time. -+ * The returned object must be freed with g_object_unref() -+ * <para> See also: #NiceNominationMode </para> -+ * -+ * Since: UNRELEASED -+ * -+ * Returns: The new agent GObject -+ */ -+NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ gboolean reliable, -+ NiceNominationMode nomination); -+ -+/** - * nice_agent_add_local_address: - * @agent: The #NiceAgent Object - * @addr: The address to listen to -@@ -447,7 +489,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - gboolean - nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr); - -- - /** - * nice_agent_add_stream: - * @agent: The #NiceAgent Object -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 7fc2a1d..6827e6e 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -502,7 +502,62 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - if (s_nominated < stream->n_components && - s_waiting_for_nomination) { - keep_timer_going = TRUE; -- if (agent->controlling_mode) { -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && -+ agent->controlling_mode && -+ ((waiting == 0 && s_inprogress == 0) || -+ (s_succeeded + s_discovered) >= 5 * stream->n_components)){ -+ /* ICE 8.1.1.1 Regular nomination -+ * we choose to nominate the valid pair if -+ * there is no pair left waiting or in-progress or -+ * if there are at least 5 valid pairs per stream on average. -+ * -+ * This is the "stopping criterion" described in 8.1.1.1, and is -+ * a "local optimization" between accumulating more valid pairs, -+ * and limiting the time spent waiting for in-progress connections -+ * checks until they finally fail. -+ */ -+ GSList *component_item; -+ -+ for (component_item = stream->components; component_item; -+ component_item = component_item->next) { -+ NiceComponent *component = component_item->data; -+ gboolean already_done = FALSE; -+ -+ /* verify that the choice of the pair to be nominated -+ * has not already been done -+ */ -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ if (p->component_id == component->id && -+ p->use_candidate_on_next_check) { -+ already_done = TRUE; -+ break; -+ } -+ } -+ -+ /* choose a pair to be nominated in the list of valid -+ * pairs, and add it to the triggered checks list -+ */ -+ if (!already_done) { -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ /* note: highest priority item selected (list always sorted) */ -+ if (p->component_id == component->id && -+ !p->nominated && -+ !p->use_candidate_on_next_check && -+ p->valid) { -+ nice_debug ("Agent %p : restarting check %p with " -+ "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->use_candidate_on_next_check = TRUE; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ break; /* move to the next component */ -+ } -+ } -+ } -+ } -+ } -+ } else if (agent->controlling_mode) { - GSList *component_item; - - for (component_item = stream->components; component_item; -@@ -1571,10 +1626,40 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - - g_assert (component); - -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && -+ agent->controlling_mode) -+ return; -+ - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; -- if (pair->local == localcand && pair->remote == remotecand) { -+ if (pair->local == localcand && pair->remote == remotecand && -+ NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ /* ICE, 7.2.1.5. Updating the Nominated Flag */ -+ /* note: TCP candidates typically produce peer reflexive -+ * candidate, generating a "discovered" pair that can be -+ * nominated. -+ */ -+ if (pair->valid) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated", -+ agent, pair, pair->foundation); -+ pair->nominated = TRUE; -+ priv_update_selected_pair (agent, component, pair); -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) { -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (pair->state == NICE_CHECK_IN_PROGRESS) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is in-progress, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } -+ } else if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; - if (pair->valid) { -@@ -2174,7 +2259,35 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - pair->prflx_priority, controlling); - } - -- if (cand_use) -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ { -+ /* We are doing regular nomination, so we set the use-candidate -+ * attrib, when the controlling agent decided which valid pair to -+ * resend with this flag in priv_conn_check_tick_stream() -+ */ -+ cand_use = pair->use_candidate_on_next_check; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(regular nomination).", agent, G_STRFUNC, cand_use); -+ break; -+ } -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ { -+ /* We are doing aggressive nomination, we set the use-candidate -+ * attrib in every check we send, when we are the controlling -+ * agent, RFC 5245, 8.1.1.2 -+ */ -+ cand_use = controlling; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(aggressive nomination).", agent, G_STRFUNC, cand_use); -+ break; -+ } -+ default: -+ /* Nothing to do. */ -+ break; -+ } -+ } else if (cand_use) - pair->nominated = controlling; - - if (uname_len > 0) { -@@ -2781,12 +2894,66 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - local_candidate, remote_candidate); - } - -- -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ - if (!ok_pair) - ok_pair = p; - - /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the - Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, control=1, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(aggressive nomination, control=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ default: -+ /* Nothing to do */ -+ break; -+ } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, control=0, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; -+ } -+ } -+ } -+ - if (ok_pair->nominated == TRUE) { - priv_update_selected_pair (agent, component, ok_pair); - priv_print_conn_check_lists (agent, G_STRFUNC, -@@ -3668,8 +3835,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - stun_usage_ice_conncheck_use_candidate (&req); - uint32_t priority = stun_usage_ice_conncheck_priority (&req); - -- if (agent->controlling_mode || -- agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || - agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007) - use_candidate = TRUE; -diff --git a/agent/conncheck.h b/agent/conncheck.h -index c204475..0f988de 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -87,6 +87,8 @@ struct _CandidateCheckPair - gboolean nominated; - gboolean timer_restarted; - gboolean valid; -+ gboolean use_candidate_on_next_check; -+ gboolean mark_nominated_on_response_arrival; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ -diff --git a/configure.ac b/configure.ac -index 98bbc08..6c106ff 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -57,6 +57,7 @@ AC_PROG_CC - AM_PROG_AR - LT_PREREQ([2.2.6]) - LT_INIT([dlopen win32-dll disable-static]) -+AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) - - # Check Operating System - AC_MSG_CHECKING([operating system]) -diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt -index 88a6cd2..a481106 100644 ---- a/docs/reference/libnice/libnice-sections.txt -+++ b/docs/reference/libnice/libnice-sections.txt -@@ -5,6 +5,7 @@ NiceAgent - NiceComponentState - NiceComponentType - NiceProxyType -+NiceNominationMode - NiceCompatibility - NiceAgentRecvFunc - NiceInputMessage -@@ -12,6 +13,7 @@ NiceOutputMessage - NICE_AGENT_MAX_REMOTE_CANDIDATES - nice_agent_new - nice_agent_new_reliable -+nice_agent_new_full - nice_agent_add_local_address - nice_agent_set_port_range - nice_agent_add_stream --- -2.13.6 - - -From 4497d9b7afaaea7124db4a2cd13546d9366b5986 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Wed, 22 Jun 2016 15:44:39 +0200 -Subject: [PATCH 27/70] test-nomination: added a new test for the nomination - mode - -Differential Revision:
https://phabricator.freedesktop.org/D1107
---- - tests/Makefile.am | 5 +- - tests/test-nomination.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 267 insertions(+), 1 deletion(-) - create mode 100644 tests/test-nomination.c - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 62d5d64..b623764 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -55,7 +55,8 @@ check_PROGRAMS = \ - test-icetcp \ - test-credentials \ - test-turn \ -- test-drop-invalid -+ test-drop-invalid \ -+ test-nomination - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -131,6 +132,8 @@ test_turn_LDADD = $(COMMON_LDADD) - - test_drop_invalid_LDADD = $(COMMON_LDADD) - -+test_nomination_LDADD = $(COMMON_LDADD) -+ - test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) - test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) - -diff --git a/tests/test-nomination.c b/tests/test-nomination.c -new file mode 100644 -index 0000000..b5a5e5f ---- /dev/null -+++ b/tests/test-nomination.c -@@ -0,0 +1,263 @@ -+#include <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <gio/gio.h> -+#include <agent.h> -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* -+ * Lets ignore stun packets that got through -+ */ -+ if (len < 8) -+ return; -+ if (strncmp ("12345678", buf, 8)) -+ return; -+ -+ if (component_id != 1) -+ return; -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+} -+ -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ g_assert (state != NICE_COMPONENT_STATE_FAILED); -+ -+ g_debug ("test-nomination: checks READY %u.", global_components_ready); -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, -+ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static void -+run_test(NiceNominationMode l_nomination_mode, -+ NiceNominationMode r_nomination_mode) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ const gchar *localhost; -+ NiceAddress localaddr; -+ guint ls_id, rs_id; -+ gulong timer_id; -+ -+ localhost = "127.0.0.1"; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_cands = global_ragent_cands = 0; -+ -+ lagent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ FALSE, /* reliable */ -+ l_nomination_mode); -+ -+ ragent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ FALSE, /* reliable */ -+ r_nomination_mode); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ nice_agent_set_software (lagent, "Test-nomination, Left Agent"); -+ nice_agent_set_software (ragent, "Test-nomination, Right Agent"); -+ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ if (!nice_address_set_from_string (&localaddr, localhost)) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &localaddr); -+ nice_agent_add_local_address (ragent, &localaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 1); -+ rs_id = nice_agent_add_stream (ragent, 1); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); -+ -+ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'..."); -+ while (!global_lagent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_gathering_done == TRUE); -+ while (!global_ragent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); -+ -+ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || -+ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ g_source_remove (timer_id); -+ -+ g_clear_object(&lagent); -+ g_clear_object(&ragent); -+} -+ -+static void -+regular (void) -+{ -+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR); -+} -+ -+static void -+aggressive (void) -+{ -+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE); -+} -+ -+static void -+mixed_ra (void) -+{ -+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE); -+} -+ -+static void -+mixed_ar (void) -+{ -+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR); -+} -+ -+int -+main (int argc, char **argv) -+{ -+ int ret; -+ -+ g_test_init (&argc, &argv, NULL); -+ -+ g_test_add_func ("/nice/nomination/regular", regular); -+ g_test_add_func ("/nice/nomination/aggressive", aggressive); -+ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra); -+ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar); -+ -+ ret = g_test_run (); -+ -+ return ret; -+} --- -2.13.6 - - -From 58d061df8f5425dc1add9c6030a2f891ebda4616 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 7 Mar 2016 16:35:09 +0100 -Subject: [PATCH 28/70] conncheck: update pair valid property selectively - -With this patch, we fix a corner case when the succeeded pair is a -peer-reflexive candidate pair, that already has been discovered -previously, In this case, the current pair -p- should not be marked -valid, because the valid flag is already set on the discovered pair. - -Differential Revision:
https://phabricator.freedesktop.org/D1124
---- - agent/conncheck.c | 18 +++++++++++++----- - 1 file changed, 13 insertions(+), 5 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 6827e6e..ef8df68 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2760,6 +2760,13 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - } - - if (new_pair) { -+ /* note: when new_pair is distinct from p, it means new_pair is a -+ * previously discovered peer-reflexive candidate pair, so we don't -+ * set the valid flag on p in this case, because the valid flag is -+ * already set on the discovered pair. -+ */ -+ if (new_pair == p) -+ p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); -@@ -2788,6 +2795,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (local_cand) - new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, - local_cand, p); -+ /* note: this is same as "adding to VALID LIST" in the spec -+ text */ -+ if (new_pair) -+ new_pair->valid = TRUE; - /* step: The agent sets the state of the pair that *generated* the check to - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ -@@ -2796,12 +2807,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - agent, p, new_pair); - } - -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -- if (new_pair) { -- new_pair->valid = TRUE; -+ if (new_pair && new_pair->valid) - nice_component_add_valid_candidate (component, remote_candidate); -- } -+ - - return new_pair; - } --- -2.13.6 - - -From 15c0546f624113b8c0546a1f883a48bff7020f1b Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 19 Apr 2016 17:06:32 +0200 -Subject: [PATCH 29/70] conncheck: improve the selection of the pairs to be - checked - -This patch aims to implement more closely the algorithm described -in RFC 5245 indicating how pairs are transitionned from state Frozen -to Waiting. This is described in 7.1.3.2 when a check succeeded, and -correspond to modifications in function priv_conn_check_unfreeze_related(). -This is also described in 5.7.4 when defining the initial state of the -pairs in a conncheck, and correspond to modifications in function -priv_conn_check_unfreeze_next(). - -This patch introduces the notion of active and frozen check list. It -allows us to define the timer restranmission delay as described in 16.1. - -Another modification in priv_conn_check_tick_unlocked() is that every -stream in handled consecutively, and in an independant way. The pacing -was previously of a single STUN request emitted per callback, it is now -of a triggered check per callback OR a single STUN per callback AND per -stream per callback. - -The description of ordinary checks per stream in 5.8 is detailled in -function priv_conn_check_tick_stream(), and a remaining of the code -used to nominate a pair by the controlling agent is put in a dedicated -function priv_conn_check_tick_stream_nominate() - -Differential Revision:
https://phabricator.freedesktop.org/D813
---- - agent/conncheck.c | 535 ++++++++++++++++++++++++++++++++++++++---------------- - agent/stream.c | 21 --- - agent/stream.h | 3 - - 3 files changed, 381 insertions(+), 178 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index ef8df68..6b1b7e3 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -212,6 +212,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) - } - - /* -+ * Check if the conncheck list if Active according to -+ * ICE spec, 5.7.4 (Computing States) -+ * -+ * note: the ICE spec in unclear about that, but the check list should -+ * be considered active when there is at least a pair in Waiting state -+ * OR a pair in In-Progress state. -+ */ -+static gboolean -+priv_is_checklist_active (NiceStream *stream) -+{ -+ GSList *i; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS) -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* -+ * Check if the conncheck list if Frozen according to -+ * ICE spec, 5.7.4 (Computing States) -+ */ -+static gboolean -+priv_is_checklist_frozen (NiceStream *stream) -+{ -+ GSList *i; -+ -+ if (stream->conncheck_list == NULL) -+ return FALSE; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state != NICE_CHECK_FROZEN) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* -+ * Check if all components of the stream have -+ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.) -+ */ -+static gboolean -+priv_all_components_have_valid_pair (NiceStream *stream) -+{ -+ guint i; -+ GSList *j; -+ -+ for (i = 1; i <= stream->n_components; i++) { -+ for (j = stream->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (p->component_id == i && p->valid) -+ break; -+ } -+ if (j == NULL) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* -+ * Check if the foundation in parameter matches the foundation -+ * of a valid pair in the conncheck list [of stream] (used for ICE spec, -+ * 7.1.3.2.3, point 2.) -+ */ -+static gboolean -+priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream) -+{ -+ GSList *i; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->valid && -+ strncmp (p->foundation, foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* - * Finds the next connectivity check in WAITING state. - */ - static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list) -@@ -220,7 +303,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - - /* note: list is sorted in priority order to first waiting check has - * the highest priority */ -- - for (i = conn_check_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->state == NICE_CHECK_WAITING) -@@ -231,6 +313,74 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - } - - /* -+ * Finds the next connectivity check in FROZEN state. -+ */ -+static CandidateCheckPair * -+priv_conn_check_find_next_frozen (GSList *conn_check_list) -+{ -+ GSList *i; -+ -+ /* note: list is sorted in priority order to first frozen check has -+ * the highest priority */ -+ for (i = conn_check_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_FROZEN) -+ return p; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Returns the number of check lists of the agent -+ */ -+static guint -+priv_number_of_check_lists (NiceAgent *agent) -+{ -+ guint n = 0; -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (stream->conncheck_list != NULL) -+ n++; -+ } -+ return n; -+} -+ -+/* -+ * Returns the number of active check lists of the agent -+ */ -+static guint -+priv_number_of_active_check_lists (NiceAgent *agent) -+{ -+ guint n = 0; -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) -+ if (priv_is_checklist_active (i->data)) -+ n++; -+ return n; -+} -+ -+/* -+ * Returns the first stream of the agent having a Frozen -+ * connection check list -+ */ -+static NiceStream * -+priv_find_first_frozen_check_list (NiceAgent *agent) -+{ -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (priv_is_checklist_frozen (stream)) -+ return stream; -+ } -+ return NULL; -+} -+ -+/* - * Initiates a new connectivity check for a ICE candidate pair. - * - * @return TRUE on success, FALSE on error -@@ -248,58 +398,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * - /* - * Unfreezes the next connectivity check in the list. Follows the - * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec -- * (ID-19), with some exceptions (see comments in code). -+ * (RFC5245) - * - * See also sect 7.1.2.2.3 (Updating Pair States), and - * priv_conn_check_unfreeze_related(). - * - * @return TRUE on success, and FALSE if no frozen candidates were found. - */ --static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) -+static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream) - { -- CandidateCheckPair *pair = NULL; - GSList *i, *j; -- -- /* XXX: the unfreezing is implemented a bit differently than in the -- * current ICE spec, but should still be interoperate: -- * - checks are not grouped by foundation -- * - one frozen check is unfrozen (lowest component-id, highest -- * priority) -- */ -+ GSList *found_list = NULL; -+ gboolean result = FALSE; - - priv_print_conn_check_lists (agent, G_STRFUNC, NULL); - -- for (i = agent->streams; i; i = i->next) { -- NiceStream *stream = i->data; -- guint64 max_frozen_priority = 0; -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p1 = i->data; -+ CandidateCheckPair *pair = NULL; -+ guint lowest_component_id = stream->n_components + 1; -+ guint64 highest_priority = 0; - -+ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp)) -+ continue; -+ found_list = g_slist_prepend (found_list, p1->foundation); - - for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *p = j->data; -- -- /* XXX: the prio check could be removed as the pairs are sorted -- * already */ -- -- if (p->state == NICE_CHECK_FROZEN) { -- if (p->priority > max_frozen_priority) { -- max_frozen_priority = p->priority; -- pair = p; -- } -+ CandidateCheckPair *p2 = i->data; -+ if (strncmp (p2->foundation, p1->foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { -+ if (p2->component_id < lowest_component_id || -+ (p2->component_id == lowest_component_id && -+ p2->priority > highest_priority)) { -+ pair = p2; -+ lowest_component_id = p2->component_id; -+ highest_priority = p2->priority; -+ } - } - } - -- if (pair) -- break; -- } -- -- if (pair) { -- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -- return TRUE; -+ if (pair) { -+ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", -+ agent, pair, pair->stream_id, pair->component_id, pair->foundation); -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ result = TRUE; -+ } - } -- -- return FALSE; -+ g_slist_free (found_list); -+ return result; - } - - /* -@@ -316,7 +463,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) - { - GSList *i, *j; -- guint unfrozen = 0; - - g_assert (ok_check); - g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); -@@ -336,40 +482,59 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); - p->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- ++unfrozen; - } - } - } - - /* step: perform the step (2) of 'Updating Pair States' */ - stream = agent_find_stream (agent, ok_check->stream_id); -- if (nice_stream_all_components_ready (stream)) { -- /* step: unfreeze checks from other streams */ -+ if (priv_all_components_have_valid_pair (stream)) { - for (i = agent->streams; i ; i = i->next) { -+ /* the agent examines the check list for each other -+ * media stream in turn -+ */ - NiceStream *s = i->data; -- for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *p = j->data; -- -- if (p->stream_id == s->id && -- p->stream_id != ok_check->stream_id) { -- if (p->state == NICE_CHECK_FROZEN && -- strcmp (p->foundation, ok_check->foundation) == 0) { -+ if (s->id == ok_check->stream_id) -+ continue; -+ if (priv_is_checklist_active (s)) { -+ /* checklist is Active -+ */ -+ for (j = s->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (p->state == NICE_CHECK_FROZEN && -+ priv_foundation_matches_a_valid_pair (p->foundation, stream)) { - nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); - p->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- ++unfrozen; -- -- } -- } -+ } -+ } -+ } else if (priv_is_checklist_frozen (s)) { -+ /* checklist is Frozen -+ */ -+ gboolean match_found = FALSE; -+ /* check if there is one pair in the check list whose -+ * foundation matches a pair in the valid list under -+ * consideration -+ */ -+ for (j = s->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { -+ match_found = TRUE; -+ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); -+ p->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ } -+ } -+ -+ if (!match_found) { -+ /* set the pair with the lowest component ID -+ * and highest priority to Waiting -+ */ -+ priv_conn_check_unfreeze_next (agent, s); -+ } - } -- /* note: only unfreeze check from one stream at a time */ -- if (unfrozen) -- break; - } - } -- -- if (unfrozen == 0) -- priv_conn_check_unfreeze_next (agent); - } - - static void -@@ -400,14 +565,13 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) - { - gboolean keep_timer_going = FALSE; -- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, -- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; -- guint frozen = 0, waiting = 0; -- GSList *i, *k; -+ GSList *i; -+ CandidateCheckPair *pair; - -+ /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -451,7 +615,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - p->next_tick = *now; - g_time_val_add (&p->next_tick, timeout * 1000); - -- *stun_transmitted = TRUE; - return TRUE; - } - case STUN_USAGE_TIMER_RETURN_SUCCESS: -@@ -471,7 +634,57 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - } - } - } -+ } - -+ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" -+ * note: This code is executed when the triggered checks list is -+ * empty, and when no STUN message has been sent (pacing constraint) -+ */ -+ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -+ if (pair) { -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ -+ /* note: this is unclear in the ICE spec, but a check list in Frozen -+ * state (where all pairs are in Frozen state) is not supposed to -+ * change its state by an ordinary check, but only by the success of -+ * checks in other check lists, in priv_conn_check_unfreeze_related(). -+ * The underlying idea is to concentrate the checks on a single check -+ * list initially. -+ */ -+ if (priv_is_checklist_frozen (stream)) -+ return keep_timer_going; -+ -+ /* step: ordinary check continued, if there's no pair in the waiting -+ * state, pick a pair in the frozen state -+ */ -+ pair = priv_conn_check_find_next_frozen (stream->conncheck_list); -+ if (pair) { -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ return keep_timer_going; -+} -+ -+static gboolean -+priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) -+{ -+ gboolean keep_timer_going = FALSE; -+ guint s_inprogress = 0; -+ guint s_succeeded = 0; -+ guint s_discovered = 0; -+ guint s_nominated = 0; -+ guint s_waiting_for_nomination = 0; -+ guint s_valid = 0; -+ guint frozen = 0; -+ guint waiting = 0; -+ GSList *i, *k; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; - if (p->state == NICE_CHECK_FROZEN) - ++frozen; - else if (p->state == NICE_CHECK_IN_PROGRESS) -@@ -504,13 +717,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - keep_timer_going = TRUE; - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { - if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && -- agent->controlling_mode && -- ((waiting == 0 && s_inprogress == 0) || -- (s_succeeded + s_discovered) >= 5 * stream->n_components)){ -+ agent->controlling_mode) { -+#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2 - /* ICE 8.1.1.1 Regular nomination -- * we choose to nominate the valid pair if -- * there is no pair left waiting or in-progress or -- * if there are at least 5 valid pairs per stream on average. -+ * we choose to nominate the valid pair of a component if -+ * - there is no pair left frozen, waiting or in-progress, or -+ * - if there are at least two valid pairs, or -+ * - if there is at least one valid pair of type HOST-HOST - * - * This is the "stopping criterion" described in 8.1.1.1, and is - * a "local optimization" between accumulating more valid pairs, -@@ -523,36 +736,63 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - component_item = component_item->next) { - NiceComponent *component = component_item->data; - gboolean already_done = FALSE; -+ gboolean stopping_criterion = FALSE; -+ guint p_valid = 0; -+ guint p_frozen = 0; -+ guint p_waiting = 0; -+ guint p_inprogress = 0; -+ guint p_host_host_valid = 0; - - /* verify that the choice of the pair to be nominated - * has not already been done - */ - for (k = stream->conncheck_list; k ; k = k->next) { - CandidateCheckPair *p = k->data; -- if (p->component_id == component->id && -- p->use_candidate_on_next_check) { -- already_done = TRUE; -- break; -+ if (p->component_id == component->id) { -+ if (p->use_candidate_on_next_check) -+ already_done = TRUE; -+ if (p->state == NICE_CHECK_FROZEN) -+ p_frozen++; -+ else if (p->state == NICE_CHECK_WAITING) -+ p_waiting++; -+ else if (p->state == NICE_CHECK_IN_PROGRESS) -+ p_inprogress++; -+ if (p->valid) -+ p_valid++; -+ if (p->valid && -+ p->local->type == NICE_CANDIDATE_TYPE_HOST && -+ p->remote->type == NICE_CANDIDATE_TYPE_HOST) -+ p_host_host_valid++; - } - } - -- /* choose a pair to be nominated in the list of valid -- * pairs, and add it to the triggered checks list -+ if (already_done) -+ continue; -+ -+ stopping_criterion = -+ (p_host_host_valid > 0 || -+ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS || -+ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0)); -+ -+ if (!stopping_criterion) -+ continue; -+ -+ /* when the stopping criterion is satisfied, we choose -+ * a pair to be nominated in the list of valid pairs, -+ * and add it to the triggered checks list - */ -- if (!already_done) { -- for (k = stream->conncheck_list; k ; k = k->next) { -- CandidateCheckPair *p = k->data; -- /* note: highest priority item selected (list always sorted) */ -- if (p->component_id == component->id && -- !p->nominated && -- !p->use_candidate_on_next_check && -- p->valid) { -- nice_debug ("Agent %p : restarting check %p with " -- "USE-CANDIDATE attrib (regular nomination)", agent, p); -- p->use_candidate_on_next_check = TRUE; -- priv_add_pair_to_triggered_check_queue (agent, p); -- break; /* move to the next component */ -- } -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ /* note: highest priority item selected (list always sorted) */ -+ if (p->component_id == component->id && -+ !p->nominated && -+ !p->use_candidate_on_next_check && -+ p->valid) { -+ nice_debug ("Agent %p : restarting check %p with " -+ "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->use_candidate_on_next_check = TRUE; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ break; /* move to the next component */ - } - } - } -@@ -615,70 +855,55 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - { - CandidateCheckPair *pair = NULL; - gboolean keep_timer_going = FALSE; -- gboolean res; -- /* note: we try to only generate a single stun transaction per timer -- * callback, to respect some pacing of STUN transaction, as per -- * appendix B.1 of ICE spec. -- */ -- gboolean stun_transmitted = FALSE; - GSList *i, *j; - GTimeVal now; - -- /* step: process ongoing STUN transactions */ - g_get_current_time (&now); - -- for (j = agent->streams; j; j = j->next) { -- NiceStream *stream = j->data; -- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); -- if (res) -- keep_timer_going = res; -- if (stun_transmitted) -- return TRUE; -- } -- -- /* step: first initiate a conncheck with a pair from the triggered list */ -- pair = priv_get_pair_from_triggered_check_queue (agent); -- -- if (pair) { -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a pair from triggered check list"); -- priv_conn_check_initiate (agent, pair); -+ /* the conncheck really starts when we have built -+ * a connection check list for each stream -+ */ -+ if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) - return TRUE; -- } - -- /* step: when the triggered list is empty, -- * find the highest priority waiting check and send it */ -- for (i = agent->streams; i ; i = i->next) { -- NiceStream *stream = i->data; -- -- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -- if (pair) -- break; -+ /* configure the initial state of the check lists of the agent -+ * as described in ICE spec, 5.7.4 -+ * -+ * if all pairs in all check lists are in frozen state, then -+ * we are in the initial state (5.7.4, point 1.) -+ */ -+ if (priv_number_of_active_check_lists (agent) == 0) { -+ /* set some pairs of the first stream in the waiting state -+ * ICE spec, 5.7.4, point 2. -+ * -+ * note: we adapt the ICE spec here, by selecting the first -+ * frozen check list, which is not necessarily the check -+ * list of the first stream (the first stream may be completed) -+ */ -+ NiceStream *stream = priv_find_first_frozen_check_list (agent); -+ if (stream) -+ priv_conn_check_unfreeze_next (agent, stream); - } - -+ /* step: perform a test from the triggered checks list, -+ * ICE spec, 5.8 "Scheduling Checks" -+ */ -+ pair = priv_get_pair_from_triggered_check_queue (agent); -+ - if (pair) { - priv_conn_check_initiate (agent, pair); - return TRUE; - } - -- /* step: when there's no pair in the Waiting state, -- * unfreeze a new pair and check it -+ /* step: process ongoing STUN transactions and -+ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" - */ -- priv_conn_check_unfreeze_next (agent); -- - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -- -- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -- if (pair) -- break; -- } -- -- if (pair) { -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a pair in Waiting state"); -- priv_conn_check_initiate (agent, pair); -- return TRUE; -+ if (priv_conn_check_tick_stream (stream, agent, &now)) -+ keep_timer_going = TRUE; -+ if (priv_conn_check_tick_stream_nominate (stream, agent)) -+ keep_timer_going = TRUE; - } - - /* step: stop timer if no work left */ -@@ -2169,30 +2394,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - - /* Implement the computation specific in RFC 5245 section 16 */ - --static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) -+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream) - { -- GSList *item1, *item2; -+ GSList *i; - guint waiting_and_in_progress = 0; -+ guint n = 0; - unsigned int rto = 0; - -- -- for (item1 = agent->streams; item1; item1 = item1->next) { -- NiceStream *stream = item1->data;; -- for (item2 = stream->conncheck_list; item2; item2 = item2->next) { -- CandidateCheckPair *pair = item2->data; -- -- if (pair->state == NICE_CHECK_IN_PROGRESS || -- pair->state == NICE_CHECK_WAITING) -- waiting_and_in_progress++; -- } -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_IN_PROGRESS || -+ p->state == NICE_CHECK_WAITING) -+ waiting_and_in_progress++; - } - -- rto = agent->timer_ta * waiting_and_in_progress; -+ n = priv_number_of_active_check_lists (agent); -+ rto = agent->timer_ta * n * waiting_and_in_progress; - - /* We assume non-reliable streams are RTP, so we use 100 as the max */ -- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", -+ nice_debug ("Agent %p : timer set to %dms, " -+ "waiting+in_progress=%d, nb_active=%d", - agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), -- waiting_and_in_progress); -+ waiting_and_in_progress, n); - if (agent->reliable) - return MAX (rto, 500); - else -@@ -2312,7 +2535,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); - } else { - stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent), -+ priv_compute_conncheck_timer (agent, stream), - agent->stun_max_retransmissions); - } - -@@ -2477,7 +2700,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p->timer_restarted ? "no" : "yes"); - if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { - stun_timer_start (&p->timer, -- priv_compute_conncheck_timer (agent), -+ priv_compute_conncheck_timer (agent, stream), - agent->stun_max_retransmissions); - p->timer_restarted = TRUE; - } -@@ -2769,7 +2992,6 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); -- priv_conn_check_unfreeze_related (agent, stream, p); - nice_component_add_valid_candidate (component, remote_candidate); - } - else { -@@ -2894,7 +3116,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - g_assert_not_reached (); - nice_debug ("Agent %p : Mapped address not found." - " conncheck %p SUCCEEDED.", agent, p); -- priv_conn_check_unfreeze_related (agent, stream, p); - nice_component_add_valid_candidate (component, p->remote); - } else { - ok_pair = priv_process_response_check_for_reflexive (agent, -@@ -2902,6 +3123,12 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - local_candidate, remote_candidate); - } - -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ - /* Note: this assignment helps to reduce the numbers of cases - * to be tested. If ok_pair and p refer to distinct pairs, it - * means that ok_pair is a discovered peer reflexive one, -diff --git a/agent/stream.c b/agent/stream.c -index 8121e12..533ff15 100644 ---- a/agent/stream.c -+++ b/agent/stream.c -@@ -104,27 +104,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) - } - - /* -- * Returns true if all components of the stream are either -- * 'CONNECTED' or 'READY' (connected plus nominated). -- */ --gboolean --nice_stream_all_components_ready (NiceStream *stream) --{ -- GSList *i; -- -- for (i = stream->components; i; i = i->next) { -- NiceComponent *component = i->data; -- if (component && -- !(component->state == NICE_COMPONENT_STATE_CONNECTED || -- component->state == NICE_COMPONENT_STATE_READY)) -- return FALSE; -- } -- -- return TRUE; --} -- -- --/* - * Initialized the local crendentials for the stream. - */ - void -diff --git a/agent/stream.h b/agent/stream.h -index f9188cb..954ba66 100644 ---- a/agent/stream.h -+++ b/agent/stream.h -@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); - void - nice_stream_close (NiceStream *stream); - --gboolean --nice_stream_all_components_ready (NiceStream *stream); -- - NiceComponent * - nice_stream_find_component_by_id (NiceStream *stream, guint id); - --- -2.13.6 - - -From ead3453d04fc70865d176ab073636f8b9078cbbc Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 13:20:38 +0200 -Subject: [PATCH 30/70] conncheck: invoke the debug dump in more places - -Differential Revision:
https://phabricator.freedesktop.org/D1123
---- - agent/conncheck.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 6b1b7e3..2d2224d 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -642,6 +642,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - */ - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Waiting state"); - priv_conn_check_initiate (agent, pair); - return TRUE; - } -@@ -661,6 +663,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - */ - pair = priv_conn_check_find_next_frozen (stream->conncheck_list); - if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Frozen state"); - pair->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, pair); - priv_conn_check_initiate (agent, pair); -@@ -891,6 +895,8 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - pair = priv_get_pair_from_triggered_check_queue (agent); - - if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair from triggered check list"); - priv_conn_check_initiate (agent, pair); - return TRUE; - } --- -2.13.6 - - -From 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 19 Apr 2016 17:59:27 +0200 -Subject: [PATCH 31/70] conncheck: improve triggered check of in-progress pairs - -This patch update the way triggered checks of in-progress pairs are -handled, according to ICE spec, section 7.2.1.4. Previously the same -connection check was retransmitted with an updated timeout. This causes -problems when a controlling role switch occurs in this time frame. -This is the reason why a new connection check must be generated -reflecting the updated role. We introduce a new flag "recheck_on_timeout" -in the pair indicating that the pair must be rechecked at the next timer -expiration. - -Differential Revision:
https://phabricator.freedesktop.org/D875
---- - agent/conncheck.c | 88 +++++++++++++++++++++++++++++++++++++++++++++---------- - agent/conncheck.h | 2 +- - 2 files changed, 74 insertions(+), 16 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 2d2224d..3a489fe 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -558,6 +558,37 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP - } - - /* -+ * Function that resubmits a new connection check, after a previous -+ * check in in-progress state got cancelled due to an incoming stun -+ * request matching this same pair -+ * -+ * @return will return TRUE if the pair is scheduled for recheck -+ */ -+static gboolean -+priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) -+{ -+ if (p->recheck_on_timeout) { -+ g_assert (p->state == NICE_CHECK_IN_PROGRESS); -+ /* this cancelled pair may have the flag 'mark nominated -+ * on response arrival' set, we want to keep it, because -+ * this is needed to nominate this pair in aggressive -+ * nomination, when the agent is in controlled mode. -+ * -+ * this cancelled pair may also have the flag 'use candidate -+ * on next check' set, that we want to preserve too. -+ */ -+ nice_debug ("Agent %p : pair %p was cancelled, " -+ "triggering a new connection check", agent, p); -+ p->recheck_on_timeout = FALSE; -+ p->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* - * Helper function for connectivity check timer callback that - * runs through the stream specific part of the state machine. - * -@@ -584,8 +615,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - switch (stun_timer_refresh (&p->timer)) { - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - { -- /* case: error, abort processing */ - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; -+ -+ /* case: error, abort processing */ - nice_address_to_string (&p->local->addr, tmpbuf1); - nice_address_to_string (&p->remote->addr, tmpbuf2); - nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -@@ -600,8 +640,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - } - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: - { -- /* case: not ready, so schedule a new timeout */ - unsigned int timeout = stun_timer_remainder (&p->timer); -+ -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; -+ -+ /* case: not ready, so schedule a new timeout */ - nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " - "(timeout %dms, delay=%dms, retrans=%d).", - agent, p, timeout, p->timer.delay, p->timer.retransmissions); -@@ -642,6 +691,12 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - */ - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) { -+ /* remove the pair from the triggered check list if needed. This -+ * may happen with the cancelled pair, that's just been added -+ * in state waiting to the triggered check list above in the -+ * same function. -+ */ -+ priv_remove_pair_from_triggered_check_queue (agent, pair); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair in Waiting state"); - priv_conn_check_initiate (agent, pair); -@@ -794,6 +849,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->valid) { - nice_debug ("Agent %p : restarting check %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->recheck_on_timeout = FALSE; - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ -@@ -816,6 +872,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -+ p->recheck_on_timeout = FALSE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ -@@ -2697,19 +2754,20 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p->state == NICE_CHECK_FROZEN) - priv_add_pair_to_triggered_check_queue (agent, p); - else if (p->state == NICE_CHECK_IN_PROGRESS) { -- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), -- * we should cancel the existing one, instead we reset our timer, so -- * we'll resend the exiting transactions faster if needed...? :P -- */ -- nice_debug ("Agent %p : check already in progress, " -- "restarting the timer again?: %s ..", agent, -- p->timer_restarted ? "no" : "yes"); -- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { -- stun_timer_start (&p->timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -- p->timer_restarted = TRUE; -- } -+ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -+ * we cancel the in-progress transaction, and after the -+ * retransmission timeout, we create a new connectivity check -+ * for that pair. The controlling role of this new check may -+ * be different from the role of this cancelled check. -+ */ -+ if (!nice_socket_is_reliable (p->sockptr)) { -+ nice_debug ("Agent %p : check already in progress, " -+ "cancelling this check..", agent); -+ /* this flag will determine the action at the retransmission -+ * timeout of the stun timer -+ */ -+ p->recheck_on_timeout = TRUE; -+ } - } - else if (p->state == NICE_CHECK_SUCCEEDED || - p->state == NICE_CHECK_DISCOVERED) { -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 0f988de..785a6cd 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -85,10 +85,10 @@ struct _CandidateCheckPair - gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; - NiceCheckState state; - gboolean nominated; -- gboolean timer_restarted; - gboolean valid; - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; -+ gboolean recheck_on_timeout; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ --- -2.13.6 - - -From 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 13:25:16 +0200 -Subject: [PATCH 32/70] conncheck: link succeeded and discovered pairs - -When the agent has the role of the stun server, is in controlled mode, -and receives a pair with the "use-candidate" attribute set, it must find -a matching succeded or discovered pair in its conncheck list. This is -described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1. -When a matching pair is in succeeded state, the agent must nominate the -valid pair (a discovered pair) constructed from section 7.1.3.2.2, -that's been created from this succeeded one. To make this lookup, we -introduce a new "discovered_pair" member of the CandidateCheckPair -struct, that links the succeeded pair, and its discovered pair -if any. - -Differential Revision:
https://phabricator.freedesktop.org/D878
---- - agent/conncheck.c | 7 +++++++ - agent/conncheck.h | 1 + - 2 files changed, 8 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 3a489fe..99cb6d2 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1928,6 +1928,12 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - * candidate, generating a "discovered" pair that can be - * nominated. - */ -+ if (pair->state == NICE_CHECK_SUCCEEDED && -+ pair->discovered_pair != NULL) { -+ pair = pair->discovered_pair; -+ g_assert (pair->state == NICE_CHECK_DISCOVERED); -+ } -+ - if (pair->valid) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", - agent, pair, pair->foundation); -@@ -2936,6 +2942,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->remote = parent_pair->remote; - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; -+ parent_pair->discovered_pair = pair; - nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 785a6cd..dd47ebe 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -89,6 +89,7 @@ struct _CandidateCheckPair - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; - gboolean recheck_on_timeout; -+ struct _CandidateCheckPair *discovered_pair; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ --- -2.13.6 - - -From 9103a5f2e184211fc160d1d3070ce4d043c71ff0 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 19 Apr 2016 18:16:26 +0200 -Subject: [PATCH 33/70] conncheck: use the right pair when retriggering a check - -This patch completes the previous patch by adding a link back from the -discovered pair, to the parent pair that generated this check. This link -is needed by the ICE spec, to comply with section 8.1.1.1, "Regular -nomination", where the check to be retriggered is the initial check that -caused the discovery of the valid pair. When the valid pair is a -peer-reflexive pair, the retriggered check must target the succeeded -pair, and not the valid discovered pair. - -Differential Revision:
https://phabricator.freedesktop.org/D879
---- - agent/conncheck.c | 21 ++++++++++++++++++--- - agent/conncheck.h | 1 + - 2 files changed, 19 insertions(+), 3 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 99cb6d2..79685df 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -847,6 +847,16 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - !p->nominated && - !p->use_candidate_on_next_check && - p->valid) { -+ /* According a ICE spec, sect 8.1.1.1. "Regular -+ * Nomination", we enqueue the check that produced this -+ * valid pair. When this pair has been discovered, we want -+ * to test its parent pair instead. -+ */ -+ if (p->succeeded_pair != NULL) { -+ g_assert (p->state == NICE_CHECK_DISCOVERED); -+ p = p->succeeded_pair; -+ } -+ g_assert (p->state == NICE_CHECK_SUCCEEDED); - nice_debug ("Agent %p : restarting check %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); - p->recheck_on_timeout = FALSE; -@@ -2754,6 +2764,11 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * tcp-active we don't want to retrigger a check on a pair that - * was FAILED when a peer-reflexive pair was created */ - -+ if (p->succeeded_pair != NULL) { -+ g_assert (p->state == NICE_CHECK_DISCOVERED); -+ p = p->succeeded_pair; -+ } -+ - nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); - - if (p->state == NICE_CHECK_WAITING || -@@ -2775,8 +2790,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p->recheck_on_timeout = TRUE; - } - } -- else if (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) { -+ else if (p->state == NICE_CHECK_SUCCEEDED) { - nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); - /* note: this is a bit unsure corner-case -- let's do the - same state update as for processing responses to our own checks */ -@@ -2943,6 +2957,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; - parent_pair->discovered_pair = pair; -+ pair->succeeded_pair = parent_pair; - nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; -@@ -4163,7 +4178,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - - pair = priv_conn_check_add_for_candidate_pair_matched (agent, - stream->id, component, local_candidate, remote_candidate, -- NICE_CHECK_DISCOVERED); -+ NICE_CHECK_SUCCEEDED); - if (pair) { - pair->valid = TRUE; - } -diff --git a/agent/conncheck.h b/agent/conncheck.h -index dd47ebe..c07fb22 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -90,6 +90,7 @@ struct _CandidateCheckPair - gboolean mark_nominated_on_response_arrival; - gboolean recheck_on_timeout; - struct _CandidateCheckPair *discovered_pair; -+ struct _CandidateCheckPair *succeeded_pair; - guint64 priority; - guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ --- -2.13.6 - - -From 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 13:30:04 +0200 -Subject: [PATCH 34/70] conncheck: fix a nomination corner case - -This patch add two supplementary cases, not covered by the ICE spec, -sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent -receives a STUN request with the USE-CANDIDATE flag, for a pair that is -in the waiting state. We consider that this case is similar to the -in-progress state, and should be handled in the same way. We also accept -when the pair is in frozen state. This latter case happens in the -new-dribble test, when an agent replays incoming early connchecks. - -Differential Revision:
https://phabricator.freedesktop.org/D880
---- - agent/conncheck.c | 35 +++++++++++++++++++++++++++++++++-- - 1 file changed, 33 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 79685df..4f4af40 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1963,6 +1963,29 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - "will be nominated on response receipt.", - agent, pair, pair->foundation); - } -+ /* note: this case is not covered by the ICE spec, 7.2.1.5, -+ * "Updating the Nominated Flag", but a pair in waiting state -+ * deserves the same treatment than a pair in-progress. -+ */ -+ if (pair->state == NICE_CHECK_WAITING) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is waiting, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } -+ /* note: this case is not covered by the ICE spec, 7.2.1.5, -+ * "Updating the Nominated Flag" either, but a pair in frozen -+ * state, and in the triggered check list should also be -+ * considered like a pair in-progress. This case happens with -+ * the new-dribble test, when an agent replays incoming early -+ * connchecks. -+ */ -+ if (pair->state == NICE_CHECK_FROZEN) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is frozen, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } - } else if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; -@@ -2703,17 +2726,25 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - "is %" G_GUINT64_FORMAT, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ -+ /* note: this case is not covered by the ICE spec, 8.1.2 -+ * "Updating States", but a pair in waiting state which will be -+ * nominated on response receipt should be treated the same way -+ * that an in-progress pair. -+ */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->component_id == component_id) { - if (p->state == NICE_CHECK_FROZEN || -- p->state == NICE_CHECK_WAITING) { -+ (p->state == NICE_CHECK_WAITING && -+ !p->mark_nominated_on_response_arrival)) { - p->state = NICE_CHECK_CANCELLED; - nice_debug ("Agent XXX : pair %p state CANCELED", p); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- if (p->state == NICE_CHECK_IN_PROGRESS) { -+ if (p->state == NICE_CHECK_IN_PROGRESS || -+ (p->state == NICE_CHECK_WAITING && -+ p->mark_nominated_on_response_arrival)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { - p->stun_message.buffer = NULL; --- -2.13.6 - - -From afd8d41bb34afb3864e838ef79026ae4ef15c0d4 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 13:32:49 +0200 -Subject: [PATCH 35/70] conncheck: new pairs never have the nominated flag - preset - -This patch disables the possibility to set the nominated flag of a -candidate pair at creation time. This possibility was used when a new -pair is created from a new peer reflexive remote candidate, when the -agent is in controlled mode, and an stun request with USE-CANDIDATE is -received. In this case, since previous commit "conncheck: fix a -nomination corner case", we set the nominated flag when the stun -response of this new pair will arrive, and not before. Consequently, -this flag is no longer required when the pair is created. - -Differential Revision:
https://phabricator.freedesktop.org/D881
---- - agent/conncheck.c | 21 +++++++++++---------- - 1 file changed, 11 insertions(+), 10 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 4f4af40..3cd0424 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -65,7 +65,7 @@ - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); - static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); - static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); --static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); - static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); - static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, - guint component_id, NiceCandidate *remote, NiceCandidate *local, -@@ -1573,7 +1573,8 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); - if (icheck->use_candidate) - priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, pair->remote); - } - } - } -@@ -1716,7 +1717,8 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - - if (icheck->use_candidate) - priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, candidate); - } - } - } -@@ -2043,7 +2045,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority) - */ - static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - guint stream_id, NiceComponent *component, NiceCandidate *local, -- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) -+ NiceCandidate *remote, NiceCheckState initial_state) - { - NiceStream *stream; - CandidateCheckPair *pair; -@@ -2081,7 +2083,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr)); - } -- pair->nominated = use_candidate; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); - -@@ -2127,7 +2128,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - agent, local->foundation, remote->foundation, - stream_id, component->id); - pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, -- initial_state, FALSE); -+ initial_state); - if (component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY) { - agent_signal_component_state_change (agent, -@@ -2774,9 +2775,8 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - * @param component the check is related to - * @param local_socket socket from which the inbound check was received - * @param remote_cand remote candidate from which the inbound check was sent -- * @param use_candidate whether the original check had USE-CANDIDATE attribute set - */ --static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand) - { - GSList *i; - NiceCandidate *local = NULL; -@@ -2872,7 +2872,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - - if (i) { - nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); -- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate); -+ priv_add_new_check_pair (agent, stream->id, component, -+ local, remote_cand, NICE_CHECK_WAITING); - return TRUE; - } - else { -@@ -2926,7 +2927,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, - - if (rcand) { - /* note: upon successful check, make the reserve check immediately */ -- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); -+ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); - - if (use_candidate) - priv_mark_pair_nominated (agent, stream, component, lcand, rcand); --- -2.13.6 - - -From 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 12:56:28 +0200 -Subject: [PATCH 36/70] conncheck: update the pair state in triggered check - list - -With this patch, we update the state of the pair to waiting when -it is put in the triggered check queue. We also take care to call -priv_schedule_triggered_check() before priv_mark_pair_nominated() -so a pair to be rechecked and put on the triggered check queue -will have a unique state to be tested in the following call to -priv_mark_pair_nominated() when evaluating its nomination attributes. - -Differential Revision:
https://phabricator.freedesktop.org/D883
---- - agent/conncheck.c | 33 +++++++++------------------------ - 1 file changed, 9 insertions(+), 24 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 3cd0424..9950970 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -183,6 +183,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa - { - g_assert (pair); - -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); - if (agent->triggered_check_queue == NULL || - g_slist_find (agent->triggered_check_queue, pair) == NULL) - agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -@@ -580,8 +582,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - nice_debug ("Agent %p : pair %p was cancelled, " - "triggering a new connection check", agent, p); - p->recheck_on_timeout = FALSE; -- p->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } -@@ -1571,10 +1571,10 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - if (nice_address_equal (&icheck->from, &pair->remote->addr) && - icheck->local_socket == pair->sockptr) { - nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); - priv_schedule_triggered_check (agent, stream, component, - icheck->local_socket, pair->remote); -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); - } - } - } -@@ -1715,10 +1715,10 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - else - conn_check_add_for_candidate (agent, stream->id, component, candidate); - -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); - priv_schedule_triggered_check (agent, stream, component, - icheck->local_socket, candidate); -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); - } - } - } -@@ -1967,7 +1967,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - } - /* note: this case is not covered by the ICE spec, 7.2.1.5, - * "Updating the Nominated Flag", but a pair in waiting state -- * deserves the same treatment than a pair in-progress. -+ * deserves the same treatment than a pair in-progress. A pair -+ * can be in waiting state as the result of being enqueued in -+ * the triggered check list for example. - */ - if (pair->state == NICE_CHECK_WAITING) { - pair->mark_nominated_on_response_arrival = TRUE; -@@ -1975,19 +1977,6 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - "will be nominated on response receipt.", - agent, pair, pair->foundation); - } -- /* note: this case is not covered by the ICE spec, 7.2.1.5, -- * "Updating the Nominated Flag" either, but a pair in frozen -- * state, and in the triggered check list should also be -- * considered like a pair in-progress. This case happens with -- * the new-dribble test, when an agent replays incoming early -- * connchecks. -- */ -- if (pair->state == NICE_CHECK_FROZEN) { -- pair->mark_nominated_on_response_arrival = TRUE; -- nice_debug ("Agent %p : pair %p (%s) is frozen, " -- "will be nominated on response receipt.", -- agent, pair, pair->foundation); -- } - } else if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; -@@ -2926,9 +2915,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, - } - - if (rcand) { -- /* note: upon successful check, make the reserve check immediately */ - priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); -- - if (use_candidate) - priv_mark_pair_nominated (agent, stream, component, lcand, rcand); - } -@@ -3345,9 +3332,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - - p->stun_message.buffer = NULL; - p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_WAITING; - priv_add_pair_to_triggered_check_queue (agent, p); -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); - } - trans_found = TRUE; - } else { --- -2.13.6 - - -From 11d4e37a030eb144a355dc26c705ef5aa5a975a7 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Fri, 1 Apr 2016 17:31:44 +0200 -Subject: [PATCH 37/70] conncheck: remove a useless pair recheck - -This exception to the ICE spec is no longer needed: when a pair is in -the succeeded state, there is no needed to recheck it again upon -reception of an incoming stun request on it. - -Differential Revision:
https://phabricator.freedesktop.org/D884
---- - agent/conncheck.c | 17 ----------------- - 1 file changed, 17 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 9950970..95e2556 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2820,23 +2820,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * that causes the ready -> connected transition. - */ - priv_update_check_list_state_for_ready (agent, stream, component); -- -- /* note: this new check is required by the new-dribble test, -- * when early icheck on the peer controlled agent causes an -- * incoming stun request to an already succeeded (and -- * nominated) pair on the controlling agent. If the -- * controlling agent doesn't retrigger a check with -- * USE-CANDIDATE=1, the peer agent has no way to nominate it. -- * -- * This behavior differs from ICE spec 7.2.1.4 -- */ -- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || -- agent->compatibility == NICE_COMPATIBILITY_WLM2009 || -- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -- agent->controlling_mode) { -- priv_add_pair_to_triggered_check_queue (agent, p); -- conn_check_schedule_next(agent); -- } - } else if (p->state == NICE_CHECK_FAILED) { - /* 7.2.1.4 Triggered Checks - * If the state of the pair is Failed, it is changed to Waiting --- -2.13.6 - - -From 8fa648a15a6700d08165fe97a09f5c068abae1e6 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 11 Apr 2016 13:13:51 +0200 -Subject: [PATCH 38/70] conncheck: dont cancel a pair for triggered check - -This patch adds another supplementary "corner" case, not covered by the -ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in -the triggered check list should be considered like an in-progress pair, -and cancelled only if its priority is lower than the priority of the -nominated pair. This is required in some aggressive nomination -situations for both peers to select the same pair, having the highest -priority. - -Differential Revision:
https://phabricator.freedesktop.org/D933
---- - agent/conncheck.c | 48 ++++++++++++++++++++++++++++++++---------------- - 1 file changed, 32 insertions(+), 16 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 95e2556..79f678a 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -64,7 +64,7 @@ - - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); - static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); --static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); -+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id); - static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); - static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); - static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, -@@ -176,6 +176,16 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - } - } - -+/* Verify if the pair is in the triggered checks list -+ */ -+ -+static gboolean -+priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) -+{ -+ g_assert (pair); -+ return (g_slist_find (agent->triggered_check_queue, pair) != NULL); -+} -+ - /* Add the pair to the triggered checks list, if not already present - */ - static void -@@ -1897,7 +1907,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream - /* Only go to READY if no checks are left in progress. If there are - * any that are kept, then this function will be called again when the - * conncheck tick timer finishes them all */ -- if (priv_prune_pending_checks (stream, component->id) == 0) { -+ if (priv_prune_pending_checks (agent, stream, component->id) == 0) { - /* Continue through the states to give client code a nice - * logical progression. See
http://phabricator.freedesktop.org/D218
for - * discussion. */ -@@ -2693,14 +2703,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - * @see priv_update_check_list_state_failed_components() - */ --static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) -+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id) - { - GSList *i; - guint64 highest_nominated_priority = 0; - guint in_progress = 0; - -- nice_debug ("Agent XXX: Finding highest priority for component %d", -- component_id); -+ nice_debug ("Agent %p: Finding highest priority for component %d", -+ agent, component_id); - - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -@@ -2712,41 +2722,47 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - } - } - -- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority " -- "is %" G_GUINT64_FORMAT, highest_nominated_priority); -+ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority " -+ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ - /* note: this case is not covered by the ICE spec, 8.1.2 - * "Updating States", but a pair in waiting state which will be - * nominated on response receipt should be treated the same way -- * that an in-progress pair. -+ * that an in-progress pair. A pair in waiting state and in -+ * the triggered check list should also be treated like an in-progress -+ * pair. - */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -+ - if (p->component_id == component_id) { -+ gboolean like_in_progress = -+ p->mark_nominated_on_response_arrival || -+ priv_is_pair_in_triggered_check_queue (agent, p); -+ - if (p->state == NICE_CHECK_FROZEN || -- (p->state == NICE_CHECK_WAITING && -- !p->mark_nominated_on_response_arrival)) { -+ (p->state == NICE_CHECK_WAITING && !like_in_progress)) { - p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ - if (p->state == NICE_CHECK_IN_PROGRESS || -- (p->state == NICE_CHECK_WAITING && -- p->mark_nominated_on_response_arrival)) { -+ (p->state == NICE_CHECK_WAITING && like_in_progress)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { - p->stun_message.buffer = NULL; - p->stun_message.buffer_len = 0; - p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); - } else { - /* We must keep the higher priority pairs running because if a udp - * packet was lost, we might end up using a bad candidate */ -- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %" -+ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %" - G_GUINT64_FORMAT " is higher than currently nominated pair %" -- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority); -+ G_GUINT64_FORMAT, agent, -+ p, p->priority, highest_nominated_priority); - in_progress++; - } - } --- -2.13.6 - - -From 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 14 Jun 2016 21:04:49 +0200 -Subject: [PATCH 39/70] conncheck: try to change earlier to state ready - -We check if we can move from state connected to ready just -after a pair expired its retransmission count. This pair -will be marked failed, and will no longer be in-progress. -The number of in-progress dropping down to zero is one -of the conditions needed to make the transition to ready, -per component (and not globally as it's the case in other -locations where this check function is called). - -Differential Revision:
https://phabricator.freedesktop.org/D1117
---- - agent/conncheck.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 79f678a..d31b77f 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -626,6 +626,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - { - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ NiceComponent *component; - - /* case: conncheck cancelled due to in-progress incoming - * check, requeing the pair, ICE spec, sect 7.2.1.4 -@@ -646,6 +647,16 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - priv_print_conn_check_lists (agent, G_STRFUNC, - ", retransmission failed"); - -+ /* perform a check if a transition state from connected to -+ * ready can be performed. This may happen here, when the last -+ * in-progress pair has expired its retransmission count -+ * in priv_conn_check_tick_stream(), which is a condition to -+ * make the transition connected to ready. -+ */ -+ if (agent_find_component (agent, p->stream_id, p->component_id, -+ NULL, &component)) -+ priv_update_check_list_state_for_ready (agent, stream, -+ component); - break; - } - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: --- -2.13.6 - - -From 59fe48517c0b7db77b99183d31fdd84b55adb5d4 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 14 Jun 2016 21:12:16 +0200 -Subject: [PATCH 40/70] conncheck: fix a state transition case - -When a new stun request hits a valid pair, of a failed component, we may -have a transition from state failed to connected. In this situation, we -do a logical progression failed -> connecting -> connected, like we do -in function priv_update_check_list_state_for_ready() - -Similarily, when a new stun request hits a failed pair, of a failed -component, triggering a new conncheck for this pair may also cause the -component state to move back from failed to connecting state. - -Differential Revision:
https://phabricator.freedesktop.org/D1118
---- - agent/conncheck.c | 21 ++++++++++++++++----- - 1 file changed, 16 insertions(+), 5 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index d31b77f..e1a5cf1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -1973,12 +1973,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - pair->nominated = TRUE; - priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -+ if (component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } - priv_update_check_list_state_for_ready (agent, stream, component); - } else if (pair->state == NICE_CHECK_IN_PROGRESS) { - pair->mark_nominated_on_response_arrival = TRUE; -@@ -2004,13 +2006,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - if (pair->valid) { - priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -+ if (component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -- - } - priv_update_check_list_state_for_ready (agent, stream, component); - } -@@ -2854,6 +2857,14 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - pair (representing a new STUN Binding request transaction), by - enqueueing the pair in the triggered check queue. */ - priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } - } - - /* note: the spec says the we SHOULD retransmit in-progress --- -2.13.6 - - -From f19d209decac432a1597d84c3d5809d2208f7457 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 14 Jun 2016 21:20:49 +0200 -Subject: [PATCH 41/70] conncheck: do not recheck a just succeeded pair - -We cancel the potential in-progress transaction cancellation, caused by -sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before -transmission timeout, or just after timeout, when the pair is -temporarily put on the triggered check list on the way to be -rechecked. This situation is not covered by the RFC 5245. - -Differential Revision:
https://phabricator.freedesktop.org/D1119
---- - agent/conncheck.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index e1a5cf1..4b785b5 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3117,6 +3117,16 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (new_pair == p) - p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; -+ /* note: we cancel the potential in-progress transaction -+ * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if -+ * we receive a valid reply before transmission timeout... -+ */ -+ p->recheck_on_timeout = FALSE; -+ /* ... or just after the transmission timeout, while the pair is -+ * temporarily put on the triggered check list on the way to be -+ * be rechecked: we remove it from the list too. -+ */ -+ priv_remove_pair_from_triggered_check_queue (agent, p); - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - nice_component_add_valid_candidate (component, remote_candidate); - } -@@ -3151,6 +3161,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ - p->state = NICE_CHECK_SUCCEEDED; -+ p->recheck_on_timeout = FALSE; -+ priv_remove_pair_from_triggered_check_queue (agent, p); - nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", - agent, p, new_pair); - } --- -2.13.6 - - -From d516fca1b0e0a6606afec797bdc0690104e779a9 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 14 Jun 2016 21:32:26 +0200 -Subject: [PATCH 42/70] conncheck: adjust recheck on timeout strategy - -The pair recheck on timeout can easily cause repetitive rechecks in -a ping-pong effect, if both peers with the same behaviour try to -check the same pair almost simultaneously, and if the network rtt -is greater than the initial timer rto. The reply to the initial -stun request may arrive after the in-progress conncheck -cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -creates a new stun request, and forgets the initial one. -The conncheck timer is restarted with the same initial value, -so the same situation happens again later. - -We choose to avoid resetting the timer in such situation. After enough -retransmissions, the timeout delay, that doubles after each timeout, -becomes longer than the rtt, and the stun reply can be handled. - -Differential Revision:
https://phabricator.freedesktop.org/D1115
---- - agent/conncheck.c | 30 ++++++++++++++++++++++++++---- - 1 file changed, 26 insertions(+), 4 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 4b785b5..88d2534 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -591,7 +591,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - */ - nice_debug ("Agent %p : pair %p was cancelled, " - "triggering a new connection check", agent, p); -- p->recheck_on_timeout = FALSE; - priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } -@@ -2650,9 +2649,32 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - if (nice_socket_is_reliable(pair->sockptr)) { - stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); - } else { -- stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -+ StunTimer *timer = &pair->timer; -+ -+ if (pair->recheck_on_timeout) -+ /* The pair recheck on timeout can easily cause repetitive rechecks in -+ * a ping-pong effect, if both peers with the same behaviour try to -+ * check the same pair almost simultaneously, and if the network rtt -+ * is greater than the initial timer rto. The reply to the initial -+ * stun request may arrive after the in-progress conncheck -+ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -+ * creates a new stun request, and forgets the initial one. -+ * The conncheck timer is restarted with the same initial value, -+ * so the same situation happens again later. -+ * -+ * We choose to avoid resetting the timer in such situation. -+ * After enough retransmissions, the timeout delay becomes -+ * longer than the rtt, and the stun reply can be handled. -+ */ -+ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -+ agent, pair, -+ timer->retransmissions, timer->max_retransmissions, -+ timer->delay - stun_timer_remainder (timer), timer->delay); -+ else -+ stun_timer_start (timer, -+ priv_compute_conncheck_timer (agent, stream), -+ agent->stun_max_retransmissions); -+ pair->recheck_on_timeout = FALSE; - } - - /* TCP-ACTIVE candidate must create a new socket before sending --- -2.13.6 - - -From 95f8805eb7b77755337e28daf1f134587d42b35f Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu, 16 Jun 2016 17:32:39 +0200 -Subject: [PATCH 43/70] conncheck: remove cancelled pair state - -Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for -removal after the nomination of a pair with an higher priority, -described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They -include also pairs that overflow the conncheck list size, but this is a -somewhat more marginal situation. So we are mainly interested in the -first use case of this state. - -This state mixes two different situations, that deserve a distinct -handling : on one side, there are waiting or frozen pairs that must be -removed, this is an immediate action that doesn't need a dedicated state -for that. And on the other side, there are in-progress pairs that -should no longer be retransmitted, because another pair with a higher -priority has already been nominated. - -This patch removes the cancelled state, and adds a flag -retransmit_on_timeout to deal with this last situation. Note that this -case should not generate a triggered check, as per described in section -7.2.1.4, when the state of the pair is In-Progress or Failed, since this -pair of lower priority has no hope to replace the nominated one. - -Differential Revision:
https://phabricator.freedesktop.org/D1114
---- - agent/conncheck.c | 142 +++++++++++++++++++++++++++++------------------------- - agent/conncheck.h | 3 +- - 2 files changed, 77 insertions(+), 68 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 88d2534..b0e2222 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -100,8 +100,6 @@ priv_state_to_gchar (NiceCheckState state) - return 'F'; - case NICE_CHECK_FROZEN: - return 'Z'; -- case NICE_CHECK_CANCELLED: -- return 'C'; - case NICE_CHECK_DISCOVERED: - return 'D'; - default: -@@ -627,6 +625,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; - NiceComponent *component; - -+timer_timeout: - /* case: conncheck cancelled due to in-progress incoming - * check, requeing the pair, ICE spec, sect 7.2.1.4 - * "Triggered Checks", "If the state of that pair is -@@ -662,6 +661,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - { - unsigned int timeout = stun_timer_remainder (&p->timer); - -+ /* case: retransmission stopped, due to the nomination of -+ * a pair with a higher priority than this in-progress pair, -+ * ICE spec, sect 8.1.2 "Updating States", item 2.2 -+ */ -+ if (!p->retransmit_on_timeout) -+ goto timer_timeout; -+ - /* case: conncheck cancelled due to in-progress incoming - * check, requeing the pair, ICE spec, sect 7.2.1.4 - * "Triggered Checks", "If the state of that pair is -@@ -1600,26 +1606,6 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - } - - --static GSList *prune_cancelled_conn_check (GSList *conncheck_list) --{ -- GSList *item = conncheck_list; -- -- while (item) { -- CandidateCheckPair *pair = item->data; -- GSList *next = item->next; -- -- if (pair->state == NICE_CHECK_CANCELLED) { -- conn_check_free_item (pair); -- conncheck_list = g_slist_delete_link (conncheck_list, item); -- } -- -- item = next; -- } -- -- return conncheck_list; --} -- -- - /* - * Handle any processing steps for connectivity checks after - * remote credentials have been set. This function handles -@@ -1754,9 +1740,6 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - (GDestroyNotify) incoming_check_free); - component->incoming_checks = NULL; - } -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); - } - - /* -@@ -1764,7 +1747,7 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - * in ICE spec section 5.7.3 (ID-19). See also - * conn_check_add_for_candidate(). - */ --static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) -+static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) - { - guint valid = 0; - guint cancelled = 0; -@@ -1772,22 +1755,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper - - while (item) { - CandidateCheckPair *pair = item->data; -+ GSList *next = item->next; - -- if (pair->state != NICE_CHECK_CANCELLED) { -- valid++; -- if (valid > upper_limit) { -- pair->state = NICE_CHECK_CANCELLED; -+ valid++; -+ if (valid > upper_limit) { -+ conn_check_free_item (pair); -+ conncheck_list = g_slist_delete_link (conncheck_list, item); - cancelled++; -- } - } -- -- item = item->next; -+ item = next; - } - - if (cancelled > 0) - nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements" - " left. Maximum connchecks allowed : %d", cancelled, valid, - upper_limit); -+ return conncheck_list; - } - - /* -@@ -2097,6 +2080,7 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - } - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); -+ pair->retransmit_on_timeout = TRUE; - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -2106,7 +2090,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - /* implement the hard upper limit for number of - checks (see sect 5.7.3 ICE ID-19): */ - if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { -- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); -+ stream->conncheck_list = priv_limit_conn_check_list_size ( -+ stream->conncheck_list, agent->max_conn_checks); - } - - return pair; -@@ -2769,8 +2754,10 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - * the triggered check list should also be treated like an in-progress - * pair. - */ -- for (i = stream->conncheck_list; i; i = i->next) { -+ i = stream->conncheck_list; -+ while (i) { - CandidateCheckPair *p = i->data; -+ GSList *next = i->next; - - if (p->component_id == component_id) { - gboolean like_in_progress = -@@ -2779,19 +2766,20 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - - if (p->state == NICE_CHECK_FROZEN || - (p->state == NICE_CHECK_WAITING && !like_in_progress)) { -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); -+ nice_debug ("Agent %p : pair %p removed.", agent, p); -+ conn_check_free_item (p); -+ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- if (p->state == NICE_CHECK_IN_PROGRESS || -+ else if (p->state == NICE_CHECK_IN_PROGRESS || - (p->state == NICE_CHECK_WAITING && like_in_progress)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); -+ p->retransmit_on_timeout = FALSE; -+ p->recheck_on_timeout = FALSE; -+ nice_debug ("Agent %p : pair %p will not be retransmitted.", -+ agent, p); - } else { - /* We must keep the higher priority pairs running because if a udp - * packet was lost, we might end up using a bad candidate */ -@@ -2803,6 +2791,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - } - } - } -+ i = next; - } - - return in_progress; -@@ -2841,29 +2830,42 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p = p->succeeded_pair; - } - -- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); -+ nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", -+ agent, p, p->foundation, priv_state_to_gchar (p->state)); - - if (p->state == NICE_CHECK_WAITING || -- p->state == NICE_CHECK_FROZEN) -+ p->state == NICE_CHECK_FROZEN) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -+ } - else if (p->state == NICE_CHECK_IN_PROGRESS) { - /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" - * we cancel the in-progress transaction, and after the - * retransmission timeout, we create a new connectivity check - * for that pair. The controlling role of this new check may - * be different from the role of this cancelled check. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * so there's no reason to recheck this pair, since it can in -+ * no way replace the nominated one. - */ - if (!nice_socket_is_reliable (p->sockptr)) { -- nice_debug ("Agent %p : check already in progress, " -- "cancelling this check..", agent); -- /* this flag will determine the action at the retransmission -- * timeout of the stun timer -- */ -- p->recheck_on_timeout = TRUE; -+ if (p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p will be rechecked " -+ "on stun timer timeout.", agent, p); -+ /* this flag will determine the action at the retransmission -+ * timeout of the stun timer -+ */ -+ p->recheck_on_timeout = TRUE; -+ } else -+ nice_debug ("Agent %p : pair %p won't be retransmitted.", -+ agent, p); - } - } - else if (p->state == NICE_CHECK_SUCCEEDED) { -- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); -+ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); - /* note: this is a bit unsure corner-case -- let's do the - same state update as for processing responses to our own checks */ - /* note: this update is required by the dribble test, to -@@ -2875,18 +2877,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - } else if (p->state == NICE_CHECK_FAILED) { - /* 7.2.1.4 Triggered Checks - * If the state of the pair is Failed, it is changed to Waiting -- and the agent MUST create a new connectivity check for that -- pair (representing a new STUN Binding request transaction), by -- enqueueing the pair in the triggered check queue. */ -- priv_add_pair_to_triggered_check_queue (agent, p); -- /* If the component for this pair is in failed state, move it -- * back to connecting, and reinitiate the timers -+ * and the agent MUST create a new connectivity check for that -+ * pair (representing a new STUN Binding request transaction), by -+ * enqueueing the pair in the triggered check queue. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * we apply the same strategy than with an in-progress pair -+ * above. - */ -- if (component->state == NICE_COMPONENT_STATE_FAILED) { -- agent_signal_component_state_change (agent, stream->id, -- component->id, NICE_COMPONENT_STATE_CONNECTING); -- conn_check_schedule_next (agent); -- } -+ if (p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } -+ } else -+ nice_debug ("Agent %p : pair %p won't be retransmitted.", -+ agent, p); - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -3401,10 +3415,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - } - } - -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); -- - return trans_found; - } - -diff --git a/agent/conncheck.h b/agent/conncheck.h -index c07fb22..909d469 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -56,7 +56,6 @@ - * @NICE_CHECK_SUCCEEDED: Connection successfully checked. - * @NICE_CHECK_FAILED: No connectivity; retransmissions ceased. - * @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING. -- * @NICE_CHECK_CANCELLED: Check cancelled. - * @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list. - * - * States for checking a candidate pair. -@@ -68,7 +67,6 @@ typedef enum - NICE_CHECK_SUCCEEDED, - NICE_CHECK_FAILED, - NICE_CHECK_FROZEN, -- NICE_CHECK_CANCELLED, - NICE_CHECK_DISCOVERED, - } NiceCheckState; - -@@ -89,6 +87,7 @@ struct _CandidateCheckPair - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; - gboolean recheck_on_timeout; -+ gboolean retransmit_on_timeout; - struct _CandidateCheckPair *discovered_pair; - struct _CandidateCheckPair *succeeded_pair; - guint64 priority; --- -2.13.6 - - -From 07366a5bca7e4818b8df29d9c7c220da8f752547 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 21 Jun 2016 21:47:42 +0200 -Subject: [PATCH 44/70] conncheck: fix the component failed transition - -This patch fixes the transition of a component from connecting to -failed, that previously occured due to the propagation of the -keep_timer_going variable, and to the final call to function -priv_update_check_list_failed_components(), after the global agent -timer was stopped. - -Previously, the code almost never entered to failed state, because the -timer was going one, as long as the number of nominated pair was not -enough, and as long as there were valid pairs not yet nominated. Even -if all pair timers were over. - -The definition of the Failed state of a conncheck list is somewhat -contradictory in the spec, depending on weather you read : - - * sect 5.7.4. "Computing States", - "Failed: In this state, the ICE checks have not completed successfully - for this media stream." - - or - - * sect 7.1.3.3. "Check List and Timer State Updates", - "If all of the pairs in the check list are now either in the Failed or - Succeeded state: If there is not a pair in the valid list for each - component of the media stream, the state of the check list is set to - Failed." - -Our understanding of the ICE spec is that, the proper way to enter failed -state instead in when all connchecks have no longer in-progress pairs. -All pairs are either in state succeeded, discovered, or failed. No timer -is still running, and we have no hope that the conncheck list changes -again, except if an unexpected STUN packet arrives later. All pairs in -frozen state is a special case, that is handled separately (sect -7.1.3.3). - -A special grace delay is added before declaring a component in state -Failed. This delay is not part of the RFC, and it is aimed to limit the -cases when a conncheck list is reactivated just after it's been declared -failed, causing a user visible transition from connecting to failed, and -back from failed to connecting again. This is also required by the test -suite, that counts exactly the number of time each state is entered, and -doesn't expect these transcient failed states to happen (frequent due to -the nature of the testsuite, less frequent in real life). - -Differential Revision:
https://phabricator.freedesktop.org/D1111
---- - agent/agent-priv.h | 14 +++++++++++ - agent/conncheck.c | 71 +++++++++++++++++++++++++++++++++++++++++++----------- - 2 files changed, 71 insertions(+), 14 deletions(-) - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 3384180..714ecff 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -122,6 +122,18 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ - (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) - -+/* A grace period before declaring a component as failed, in msecs. This -+ * delay is added to reduce the chance to see the agent receiving new -+ * stun activity just after the conncheck list has been declared failed, -+ * reactiviting conncheck activity, and causing a (valid) state -+ * transitions like that: connecting -> failed -> connecting -> -+ * connected -> ready. -+ * Such transitions are not buggy per-se, but may break the -+ * test-suite, that counts precisely the number of time each state -+ * has been set, and doesnt expect these transcient failed states. -+ */ -+#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000 -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -@@ -176,6 +188,8 @@ struct _NiceAgent - guint16 rfc4571_expecting_length; - gboolean use_ice_udp; - gboolean use_ice_tcp; -+ -+ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ - /* XXX: add pointer to internal data struct for ABI-safe extensions */ - }; - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index b0e2222..63db471 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -709,7 +709,7 @@ timer_timeout: - } - } - } -- } -+ } - - /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" - * note: This code is executed when the triggered checks list is -@@ -795,11 +795,8 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - if (s_inprogress) - keep_timer_going = TRUE; - -- /* note: if some components have established connectivity, -- * but yet no nominated pair, keep timer going */ - if (s_nominated < stream->n_components && - s_waiting_for_nomination) { -- keep_timer_going = TRUE; - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { - if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && - agent->controlling_mode) { -@@ -888,6 +885,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->recheck_on_timeout = FALSE; - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); -+ keep_timer_going = TRUE; - break; /* move to the next component */ - } - } -@@ -911,6 +909,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->recheck_on_timeout = FALSE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); -+ keep_timer_going = TRUE; - break; /* move to the next component */ - } - } -@@ -937,6 +936,7 @@ conn_check_stop (NiceAgent *agent) - g_source_destroy (agent->conncheck_timer_source); - g_source_unref (agent->conncheck_timer_source); - agent->conncheck_timer_source = NULL; -+ agent->conncheck_timer_grace_period = 0; - } - - -@@ -1005,9 +1005,39 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - keep_timer_going = TRUE; - } - -+ /* step: if no work left and a conncheck list of a stream is still -+ * frozen, set the pairs to waiting, according to ICE SPEC, sect -+ * 7.1.3.3. "Check List and Timer State Updates" -+ */ -+ if (!keep_timer_going) { -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (priv_is_checklist_frozen (stream)) { -+ nice_debug ("Agent %p : stream %d conncheck list is still " -+ "frozen, while other lists are completed. Unfreeze it.", -+ agent, stream->id); -+ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream); -+ } -+ } -+ } -+ -+ /* note: we provide a grace period before declaring a component as -+ * failed. Components marked connected, and then ready follow another -+ * code path, and are not concerned by this grace period. -+ */ -+ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0) -+ nice_debug ("Agent %p : waiting %d msecs before checking " -+ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD); -+ -+ if (keep_timer_going) -+ agent->conncheck_timer_grace_period = 0; -+ else -+ agent->conncheck_timer_grace_period += agent->timer_ta; -+ - /* step: stop timer if no work left */ -- if (keep_timer_going != TRUE) { -- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); -+ if (!keep_timer_going && -+ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) { -+ nice_debug ("Agent %p : checking for failed components now.", agent); - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; - priv_update_check_list_failed_components (agent, stream); -@@ -1017,6 +1047,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - } - } - -+ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", conncheck timer stopped"); - -@@ -1027,9 +1058,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - - /* XXX: what to signal, is all processing now really done? */ - nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); -+ return FALSE; - } - -- return keep_timer_going; -+ return TRUE; - } - - static gboolean priv_conn_check_tick (gpointer pointer) -@@ -1810,15 +1842,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp - * Updates the check list state. - * - * Implements parts of the algorithm described in -- * ICE sect 8.1.2. "Updating States" (ID-19): if for any -+ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any - * component, all checks have been completed and have -- * failed, mark that component's state to NICE_CHECK_FAILED. -+ * failed to produce a nominated pair, mark that component's -+ * state to NICE_CHECK_FAILED. - * - * Sends a component state changesignal via 'agent'. - */ - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) - { - GSList *i; -+ gboolean completed; -+ guint nominated; - /* note: emitting a signal might cause the client - * to remove the stream, thus the component count - * must be fetched before entering the loop*/ -@@ -1842,6 +1877,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) - continue; - -+ nominated = 0; -+ completed = TRUE; - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -1849,16 +1886,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - g_assert (p->stream_id == stream->id); - - if (p->component_id == (c + 1)) { -- if (p->state != NICE_CHECK_FAILED) -- break; -+ if (p->nominated) -+ ++nominated; -+ if (p->state != NICE_CHECK_FAILED && -+ p->state != NICE_CHECK_SUCCEEDED && -+ p->state != NICE_CHECK_DISCOVERED) -+ completed = FALSE; - } - } - -- /* note: all checks have failed -+ /* note: all pairs are either failed or succeeded, and the component -+ * has not produced a nominated pair. - * Set the component to FAILED only if it actually had remote candidates - * that failed.. */ -- if (i == NULL && comp != NULL && comp->remote_candidates != NULL) -- agent_signal_component_state_change (agent, -+ if (completed && nominated == 0 && -+ comp != NULL && comp->remote_candidates != NULL) -+ agent_signal_component_state_change (agent, - stream->id, - (c + 1), /* component-id */ - NICE_COMPONENT_STATE_FAILED); --- -2.13.6 - - -From 195db6b344fc4f9fadc39419dfeec2fc14b23fac Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Fri, 15 Jul 2016 23:31:42 +0200 -Subject: [PATCH 45/70] agent: add new pairs only for gathering streams - -At the end of the local candidate gathering process, we only create new -pairs for streams that are in gathering state. - -Other stream that may be in ready state for example, due to a -previously succeeded conncheck process, may have accumulated some -couples (local,remote) candidates that have not resulted in the creation -a new pair during this previous conncheck process, and we don't want -these new pairs to be added now, because it would generate unneeded -transition changes for a stream unconcerned by this gathering. - -Differential Revision:
https://phabricator.freedesktop.org/D1755
---- - agent/agent.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/agent/agent.c b/agent/agent.c -index 577a7e0..e3705ed 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -2032,6 +2032,17 @@ void agent_gathering_done (NiceAgent *agent) - - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; -+ -+ /* We ignore streams not in gathering state, typically already in -+ * ready state. Such streams may have couples (local,remote) -+ * candidates that have not resulted in the creation a new pair -+ * during a previous conncheck session, and we don't want these new -+ * pairs to be added now, because it would generate unneeded -+ * transition changes for a stream unconcerned by this gathering. -+ */ -+ if (!stream->gathering) -+ continue; -+ - for (j = stream->components; j; j = j->next) { - NiceComponent *component = j->data; - --- -2.13.6 - - -From b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sun, 28 May 2017 22:20:36 +0200 -Subject: [PATCH 46/70] stun: fix gcc7 implicit fallthrough warning - -Differential Revision:
https://phabricator.freedesktop.org/D1754
---- - stun/stunmessage.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/stun/stunmessage.c b/stun/stunmessage.c -index e8184c4..4cc3392 100644 ---- a/stun/stunmessage.c -+++ b/stun/stunmessage.c -@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type, - /* Only fingerprint may come after M-I */ - if (type == STUN_ATTRIBUTE_FINGERPRINT) - break; -+ return NULL; - - case STUN_ATTRIBUTE_FINGERPRINT: - /* Nothing may come after FPR */ --- -2.13.6 - - -From c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sun, 18 Jun 2017 10:12:58 +0200 -Subject: [PATCH 47/70] agent: remove spurious newlines - -Differential Revision:
https://phabricator.freedesktop.org/D1756
---- - agent/agent.c | 2 +- - agent/component.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index e3705ed..27e6193 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -3905,7 +3905,7 @@ agent_recv_message_unlocked ( - - nice_address_to_string (message->from, str); - nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" -- " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, -+ " %s:%d sock-type: %d", agent, stream->id, component->id, str, - nice_address_get_port (message->from), nicesock->type); - } - -diff --git a/agent/component.c b/agent/component.c -index ab665b6..6e207d3 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -1461,7 +1461,7 @@ nice_component_add_valid_candidate (NiceComponent *component, - char str[INET6_ADDRSTRLEN]; - nice_address_to_string (&candidate->addr, str); - nice_debug ("Agent %p : %d:%d Adding valid source" -- " candidate: %s:%d trans: %d\n", component->agent, -+ " candidate: %s:%d trans: %d", component->agent, - candidate->stream_id, candidate->component_id, str, - nice_address_get_port (&candidate->addr), candidate->transport); - } --- -2.13.6 - - -From e3ddaa285e389baf3f26cfb6964919718a8f6a00 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Wed, 21 Jun 2017 16:55:32 -0400 -Subject: [PATCH 48/70] agent: Adjust the nice_agent_new_full() to use flags - -This makes it easier to read and more extensible. ---- - agent/agent.c | 9 +++++---- - agent/agent.h | 27 ++++++++++++++++++++++----- - tests/test-nomination.c | 8 ++++---- - 3 files changed, 31 insertions(+), 13 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 27e6193..8fd8ead 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -1168,14 +1168,15 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) - NICEAPI_EXPORT NiceAgent * - nice_agent_new_full (GMainContext *ctx, - NiceCompatibility compat, -- gboolean reliable, -- NiceNominationMode nomination) -+ NiceAgentOption flags) - { - NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, - "compatibility", compat, - "main-context", ctx, -- "reliable", reliable, -- "nomination-mode", nomination, -+ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE, -+ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ? -+ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE, -+ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, - NULL); - - return agent; -diff --git a/agent/agent.h b/agent/agent.h -index 6e233c6..ed6f6e4 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -399,6 +399,25 @@ typedef enum - } NiceNominationMode; - - /** -+ * NiceAgentOption: -+ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default -+ * is aggrssive mode (see #NiceNominationMode). -+ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable(). -+ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode -+ * -+ * These are options that can be passed to nice_agent_new_full(). They set -+ * various properties on the agent. Not including them sets the property to -+ * the other value. -+ * -+ * Since: UNRELEASED -+ */ -+typedef enum { -+ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, -+ NICE_AGENT_OPTION_RELIABLE = 1 << 1, -+ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, -+} NiceAgentOption; -+ -+/** - * NiceAgentRecvFunc: - * @agent: The #NiceAgent Object - * @stream_id: The id of the stream -@@ -452,13 +471,12 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - * nice_agent_new_full: - * @ctx: The Glib Mainloop Context to use for timers - * @compat: The compatibility mode of the agent -- * @reliable: The reliability mode of the agent -- * @nomination: The nomination mode of the agent -+ * @flags: Flags to set the properties - * - * Create a new #NiceAgent with parameters that must be be defined at - * construction time. - * The returned object must be freed with g_object_unref() -- * <para> See also: #NiceNominationMode </para> -+ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> - * - * Since: UNRELEASED - * -@@ -467,8 +485,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - NiceAgent * - nice_agent_new_full (GMainContext *ctx, - NiceCompatibility compat, -- gboolean reliable, -- NiceNominationMode nomination); -+ NiceAgentOption flags); - - /** - * nice_agent_add_local_address: -diff --git a/tests/test-nomination.c b/tests/test-nomination.c -index b5a5e5f..bf21557 100644 ---- a/tests/test-nomination.c -+++ b/tests/test-nomination.c -@@ -140,13 +140,13 @@ run_test(NiceNominationMode l_nomination_mode, - - lagent = nice_agent_new_full (NULL, - NICE_COMPATIBILITY_RFC5245, -- FALSE, /* reliable */ -- l_nomination_mode); -+ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); - - ragent = nice_agent_new_full (NULL, - NICE_COMPATIBILITY_RFC5245, -- FALSE, /* reliable */ -- r_nomination_mode); -+ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); - - g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); - g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); --- -2.13.6 - - -From dcb0d647174416a292492f8deca86f83a2ef124c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Wed, 21 Jun 2017 17:07:17 -0400 -Subject: [PATCH 49/70] Repleace UNRELEASED with 0.1.15 - ---- - agent/agent.c | 8 ++++---- - agent/agent.h | 6 +++--- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 8fd8ead..15af9ed 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -448,7 +448,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * the selection of valid pairs to be used upstream. - * <para> See also: #NiceNominationMode </para> - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, - g_param_spec_enum ( -@@ -744,7 +744,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * to the READY state, and on the time needed to complete the GATHERING - * state. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - - g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, -@@ -769,7 +769,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * divided by two instead (RFC 5389 indicates that a customisable - * multiplier 'Rm' to 'RTO' should be used). - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - - g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, -@@ -788,7 +788,7 @@ nice_agent_class_init (NiceAgentClass *klass) - * The initial timeout of the STUN binding requests used - * for a reliable timer. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - - g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, -diff --git a/agent/agent.h b/agent/agent.h -index ed6f6e4..520c4c5 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -390,7 +390,7 @@ typedef enum - * faster, than the regular mode, potentially causing the nominated - * pair to change until the connection check completes. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - typedef enum - { -@@ -409,7 +409,7 @@ typedef enum - * various properties on the agent. Not including them sets the property to - * the other value. - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - */ - typedef enum { - NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, -@@ -478,7 +478,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - * The returned object must be freed with g_object_unref() - * <para> See also: #NiceNominationMode and #NiceAgentOption</para> - * -- * Since: UNRELEASED -+ * Since: 0.1.15 - * - * Returns: The new agent GObject - */ --- -2.13.6 - - -From 2c50d73b82f2ec2422a8e0ea393194486c193c64 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Wed, 10 Feb 2016 23:20:39 -0500 -Subject: [PATCH 50/70] agent: Don't crash if recv cancelled without a GError - ---- - agent/agent.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index 15af9ed..e48d7f3 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -4279,7 +4279,10 @@ static gboolean - nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) - { - GError **error = user_data; -- return !g_cancellable_set_error_if_cancelled (cancellable, error); -+ -+ if (error && *error) -+ g_cancellable_set_error_if_cancelled (cancellable, error); -+ return G_SOURCE_REMOVE; - } - - static gint --- -2.13.6 - - -From 63d273cea42def3567701ad9feab91f63cf9345f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Thu, 11 Feb 2016 22:16:48 -0500 -Subject: [PATCH 51/70] component: Use non-GClosure dummy callbacks - -GClosures are not that cheap to setup ---- - agent/component.c | 18 +++++++++++++++--- - 1 file changed, 15 insertions(+), 3 deletions(-) - -diff --git a/agent/component.c b/agent/component.c -index 6e207d3..6eee90e 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -1005,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass) - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - } - -+static gboolean -+dummy_callback (gpointer data) -+{ -+ return G_SOURCE_CONTINUE; -+} -+ -+static void -+source_set_dummy_callback (GSource *source) -+{ -+ g_source_set_callback (source, dummy_callback, NULL, NULL); -+} -+ - static void - nice_component_init (NiceComponent *component) - { -@@ -1027,7 +1039,7 @@ nice_component_init (NiceComponent *component) - component->stop_cancellable = g_cancellable_new (); - component->stop_cancellable_source = - g_cancellable_source_new (component->stop_cancellable); -- g_source_set_dummy_callback (component->stop_cancellable_source); -+ source_set_dummy_callback (component->stop_cancellable_source); - g_source_attach (component->stop_cancellable_source, component->own_ctx); - component->ctx = g_main_context_ref (component->own_ctx); - -@@ -1242,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_) - child_socket_source->source = - g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, - NULL); -- g_source_set_dummy_callback (child_socket_source->source); -+ source_set_dummy_callback (child_socket_source->source); - g_source_add_child_source (source, child_socket_source->source); - g_source_unref (child_socket_source->source); - component_source->socket_sources = -@@ -1387,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id, - GSource *cancellable_source; - - cancellable_source = g_cancellable_source_new (cancellable); -- g_source_set_dummy_callback (cancellable_source); -+ source_set_dummy_callback (cancellable_source); - g_source_add_child_source ((GSource *) component_source, - cancellable_source); - g_source_unref (cancellable_source); --- -2.13.6 - - -From 9f800d3597767855accccc592c34bc4e945f5bd5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Wed, 21 Jun 2017 20:42:57 -0400 -Subject: [PATCH 52/70] configure: Remove -Wswitch-enum - -Creates useless warnings when other libraries change. - -https://phabricator.freedesktop.org/T7770 ---- - configure.ac | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/configure.ac b/configure.ac -index 6c106ff..16988ad 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -154,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \ - ]) - AS_IF([test "$enable_compile_warnings" = "maximum" -o \ - "$enable_compile_warnings" = "error"],[ -- NICE_ADD_FLAG([-Wswitch-enum]) - NICE_ADD_FLAG([-Wswitch-default]) - NICE_ADD_FLAG([-Waggregate-return]) - ]) --- -2.13.6 - - -From dbaf8f5ccd76089e340883887c7e08e6c04de80a Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 12 Apr 2016 13:22:21 +0200 -Subject: [PATCH 53/70] conncheck: improve role conflict debug - -This patch displays explicitely the controlling or controlled -role of the agent. - -Differential Revision:
https://phabricator.freedesktop.org/D874
---- - agent/conncheck.c | 24 +++++++++++++++++++----- - 1 file changed, 19 insertions(+), 5 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 63db471..8945e0f 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3134,14 +3134,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) - { - /* role conflict, change mode; wait for a new conn. check */ - if (control != agent->controlling_mode) { -- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control); -+ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".", -+ agent, control ? "controlling" : "controlled"); - agent->controlling_mode = control; - /* the pair priorities depend on the roles, so recalculation - * is needed */ - priv_recalculate_pair_priorities (agent); - } - else -- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control); -+ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", -+ agent, control ? "controlling" : "controlled"); - } - - /* -@@ -3429,13 +3431,25 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - /* case: role conflict error, need to restart with new role */ - nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); - -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ -+ - if (p->stun_message.buffer != NULL) { - guint64 tie; - gboolean controlled_mode; - -- /* note: our role might already have changed due to an -- * incoming request, but if not, change role now; -- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ - controlled_mode = (stun_message_find64 (&p->stun_message, - STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == - STUN_MESSAGE_RETURN_SUCCESS); --- -2.13.6 - - -From 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> -Date: Tue, 5 Sep 2017 14:50:29 -0400 -Subject: [PATCH 54/70] agent: Set error if it isn't set - ---- - agent/agent.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/agent.c b/agent/agent.c -index e48d7f3..a4dcc0c 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -4280,7 +4280,7 @@ nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) - { - GError **error = user_data; - -- if (error && *error) -+ if (error && !*error) - g_cancellable_set_error_if_cancelled (cancellable, error); - return G_SOURCE_REMOVE; - } --- -2.13.6 - - -From 25be00271a4c8c684a2d435d29ae0811dbf5e21c Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 26 Jun 2017 20:36:35 +0200 -Subject: [PATCH 55/70] conncheck: reorder some chunks of code - -With this patch we simplify the levels of code indentation. - -Differential Revision:
https://phabricator.freedesktop.org/D1758
---- - agent/conncheck.c | 858 +++++++++++++++++++++++++++--------------------------- - 1 file changed, 422 insertions(+), 436 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 8945e0f..874f7b1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -608,106 +608,106 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - gboolean keep_timer_going = FALSE; - GSList *i; - CandidateCheckPair *pair; -+ unsigned int timeout; - - /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; -+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ NiceComponent *component; -+ -+ if (!agent_find_component (agent, p->stream_id, p->component_id, -+ NULL, &component)) -+ continue; -+ -+ if (p->state != NICE_CHECK_IN_PROGRESS) -+ continue; - -- if (p->state == NICE_CHECK_IN_PROGRESS) { -- if (p->stun_message.buffer == NULL) { -- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -- } else if (priv_timer_expired (&p->next_tick, now)) { -- switch (stun_timer_refresh (&p->timer)) { -- case STUN_USAGE_TIMER_RETURN_TIMEOUT: -- { -- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -- NiceComponent *component; -+ if (p->stun_message.buffer == NULL) { -+ nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -+ p->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ continue; -+ } - -+ if (!priv_timer_expired (&p->next_tick, now)) -+ continue; -+ -+ switch (stun_timer_refresh (&p->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: - timer_timeout: -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; - -- /* case: error, abort processing */ -- nice_address_to_string (&p->local->addr, tmpbuf1); -- nice_address_to_string (&p->remote->addr, tmpbuf2); -- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -- tmpbuf1, nice_address_get_port (&p->local->addr), -- tmpbuf2, nice_address_get_port (&p->remote->addr)); -- candidate_check_pair_fail (stream, agent, p); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", retransmission failed"); -- -- /* perform a check if a transition state from connected to -- * ready can be performed. This may happen here, when the last -- * in-progress pair has expired its retransmission count -- * in priv_conn_check_tick_stream(), which is a condition to -- * make the transition connected to ready. -- */ -- if (agent_find_component (agent, p->stream_id, p->component_id, -- NULL, &component)) -- priv_update_check_list_state_for_ready (agent, stream, -- component); -- break; -- } -- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -- { -- unsigned int timeout = stun_timer_remainder (&p->timer); -+ /* case: error, abort processing */ -+ nice_address_to_string (&p->local->addr, tmpbuf1); -+ nice_address_to_string (&p->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -+ tmpbuf1, nice_address_get_port (&p->local->addr), -+ tmpbuf2, nice_address_get_port (&p->remote->addr)); -+ candidate_check_pair_fail (stream, agent, p); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", retransmission failed"); -+ -+ /* perform a check if a transition state from connected to -+ * ready can be performed. This may happen here, when the last -+ * in-progress pair has expired its retransmission count -+ * in priv_conn_check_tick_stream(), which is a condition to -+ * make the transition connected to ready. -+ */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ break; -+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -+ timeout = stun_timer_remainder (&p->timer); - -- /* case: retransmission stopped, due to the nomination of -- * a pair with a higher priority than this in-progress pair, -- * ICE spec, sect 8.1.2 "Updating States", item 2.2 -- */ -- if (!p->retransmit_on_timeout) -- goto timer_timeout; -+ /* case: retransmission stopped, due to the nomination of -+ * a pair with a higher priority than this in-progress pair, -+ * ICE spec, sect 8.1.2 "Updating States", item 2.2 -+ */ -+ if (!p->retransmit_on_timeout) -+ goto timer_timeout; - -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ /* case: conncheck cancelled due to in-progress incoming -+ * check, requeing the pair, ICE spec, sect 7.2.1.4 -+ * "Triggered Checks", "If the state of that pair is -+ * In-Progress..." -+ */ -+ if (priv_conn_recheck_on_timeout (agent, p)) -+ break; - -- /* case: not ready, so schedule a new timeout */ -- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -- "(timeout %dms, delay=%dms, retrans=%d).", -- agent, p, timeout, p->timer.delay, p->timer.retransmissions); -+ /* case: not ready, so schedule a new timeout */ -+ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -+ "(timeout %dms, delay=%dms, retrans=%d).", -+ agent, p, timeout, p->timer.delay, p->timer.retransmissions); - -- agent_socket_send (p->sockptr, &p->remote->addr, -- stun_message_length (&p->stun_message), -- (gchar *)p->stun_buffer); -+ agent_socket_send (p->sockptr, &p->remote->addr, -+ stun_message_length (&p->stun_message), -+ (gchar *)p->stun_buffer); - - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ p->next_tick = *now; -+ g_time_val_add (&p->next_tick, timeout * 1000); - -- return TRUE; -- } -- case STUN_USAGE_TIMER_RETURN_SUCCESS: -- { -- unsigned int timeout = stun_timer_remainder (&p->timer); -+ return TRUE; -+ case STUN_USAGE_TIMER_RETURN_SUCCESS: -+ timeout = stun_timer_remainder (&p->timer); - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ p->next_tick = *now; -+ g_time_val_add (&p->next_tick, timeout * 1000); - -- keep_timer_going = TRUE; -- break; -- } -- default: -- /* Nothing to do. */ -- break; -- } -- } -+ keep_timer_going = TRUE; -+ break; -+ default: -+ /* Nothing to do. */ -+ break; - } - } - -@@ -2628,27 +2628,23 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { - switch (agent->nomination_mode) { - case NICE_NOMINATION_MODE_REGULAR: -- { -- /* We are doing regular nomination, so we set the use-candidate -- * attrib, when the controlling agent decided which valid pair to -- * resend with this flag in priv_conn_check_tick_stream() -- */ -- cand_use = pair->use_candidate_on_next_check; -- nice_debug ("Agent %p : %s: set cand_use=%d " -- "(regular nomination).", agent, G_STRFUNC, cand_use); -- break; -- } -+ /* We are doing regular nomination, so we set the use-candidate -+ * attrib, when the controlling agent decided which valid pair to -+ * resend with this flag in priv_conn_check_tick_stream() -+ */ -+ cand_use = pair->use_candidate_on_next_check; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(regular nomination).", agent, G_STRFUNC, cand_use); -+ break; - case NICE_NOMINATION_MODE_AGGRESSIVE: -- { -- /* We are doing aggressive nomination, we set the use-candidate -- * attrib in every check we send, when we are the controlling -- * agent, RFC 5245, 8.1.1.2 -- */ -- cand_use = controlling; -- nice_debug ("Agent %p : %s: set cand_use=%d " -- "(aggressive nomination).", agent, G_STRFUNC, cand_use); -- break; -- } -+ /* We are doing aggressive nomination, we set the use-candidate -+ * attrib in every check we send, when we are the controlling -+ * agent, RFC 5245, 8.1.1.2 -+ */ -+ cand_use = controlling; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(aggressive nomination).", agent, G_STRFUNC, cand_use); -+ break; - default: - /* Nothing to do. */ - break; -@@ -2656,107 +2652,105 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - } else if (cand_use) - pair->nominated = controlling; - -- if (uname_len > 0) { -- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -- uname, uname_len, password, password_len, -- cand_use, controlling, pair->prflx_priority, -- agent->tie_breaker, -- pair->local->foundation, -- agent_to_ice_compatibility (agent)); -+ if (uname_len == 0) { -+ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -+ pair->stun_message.buffer = NULL; -+ pair->stun_message.buffer_len = 0; -+ return -1; -+ } - -- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -- pair->stun_message.buffer); -+ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -+ &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -+ uname, uname_len, password, password_len, -+ cand_use, controlling, pair->prflx_priority, -+ agent->tie_breaker, -+ pair->local->foundation, -+ agent_to_ice_compatibility (agent)); - -- if (agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- g_free (password); -- } -+ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -+ pair->stun_message.buffer); - -- if (buffer_len > 0) { -- if (nice_socket_is_reliable(pair->sockptr)) { -- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); -- } else { -- StunTimer *timer = &pair->timer; -- -- if (pair->recheck_on_timeout) -- /* The pair recheck on timeout can easily cause repetitive rechecks in -- * a ping-pong effect, if both peers with the same behaviour try to -- * check the same pair almost simultaneously, and if the network rtt -- * is greater than the initial timer rto. The reply to the initial -- * stun request may arrive after the in-progress conncheck -- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -- * creates a new stun request, and forgets the initial one. -- * The conncheck timer is restarted with the same initial value, -- * so the same situation happens again later. -- * -- * We choose to avoid resetting the timer in such situation. -- * After enough retransmissions, the timeout delay becomes -- * longer than the rtt, and the stun reply can be handled. -- */ -- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -- agent, pair, -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay); -- else -- stun_timer_start (timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -- pair->recheck_on_timeout = FALSE; -- } -+ if (agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ g_free (password); -+ } - -- /* TCP-ACTIVE candidate must create a new socket before sending -- * by connecting to the peer. The new socket is stored in the candidate -- * check pair, until we discover a new local peer reflexive */ -- if (pair->sockptr->fileno == NULL && -- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && -- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -- NiceStream *stream2 = NULL; -- NiceComponent *component2 = NULL; -- NiceSocket *new_socket; -- -- if (agent_find_component (agent, pair->stream_id, pair->component_id, -- &stream2, &component2)) { -- new_socket = nice_tcp_active_socket_connect (pair->sockptr, -- &pair->remote->addr); -- if (new_socket) { -- pair->sockptr = new_socket; -- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -- -- if (agent->reliable) { -- nice_socket_set_writable_callback (pair->sockptr, -- _tcp_sock_is_writable, component2); -- } -+ if (buffer_len == 0) { -+ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -+ pair->stun_message.buffer = NULL; -+ pair->stun_message.buffer_len = 0; -+ return -1; -+ } - -- nice_component_attach_socket (component2, new_socket); -- } -- } -- } -- /* send the conncheck */ -- agent_socket_send (pair->sockptr, &pair->remote->addr, -- buffer_len, (gchar *)pair->stun_buffer); -+ if (nice_socket_is_reliable(pair->sockptr)) -+ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); -+ else { -+ StunTimer *timer = &pair->timer; -+ -+ if (pair->recheck_on_timeout) -+ /* The pair recheck on timeout can easily cause repetitive rechecks in -+ * a ping-pong effect, if both peers with the same behaviour try to -+ * check the same pair almost simultaneously, and if the network rtt -+ * is greater than the initial timer rto. The reply to the initial -+ * stun request may arrive after the in-progress conncheck -+ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -+ * creates a new stun request, and forgets the initial one. -+ * The conncheck timer is restarted with the same initial value, -+ * so the same situation happens again later. -+ * -+ * We choose to avoid resetting the timer in such situation. -+ * After enough retransmissions, the timeout delay becomes -+ * longer than the rtt, and the stun reply can be handled. -+ */ -+ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -+ agent, pair, -+ timer->retransmissions, timer->max_retransmissions, -+ timer->delay - stun_timer_remainder (timer), timer->delay); -+ else -+ stun_timer_start (timer, -+ priv_compute_conncheck_timer (agent, stream), -+ agent->stun_max_retransmissions); -+ pair->recheck_on_timeout = FALSE; -+ } - -- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { -- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -- &pair->remote->addr); -+ /* TCP-ACTIVE candidate must create a new socket before sending -+ * by connecting to the peer. The new socket is stored in the candidate -+ * check pair, until we discover a new local peer reflexive */ -+ if (pair->sockptr->fileno == NULL && -+ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && -+ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -+ NiceStream *stream2 = NULL; -+ NiceComponent *component2 = NULL; -+ NiceSocket *new_socket; -+ -+ if (agent_find_component (agent, pair->stream_id, pair->component_id, -+ &stream2, &component2)) { -+ new_socket = nice_tcp_active_socket_connect (pair->sockptr, -+ &pair->remote->addr); -+ if (new_socket) { -+ pair->sockptr = new_socket; -+ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -+ -+ if (agent->reliable) -+ nice_socket_set_writable_callback (pair->sockptr, -+ _tcp_sock_is_writable, component2); -+ -+ nice_component_attach_socket (component2, new_socket); - } -- -- timeout = stun_timer_remainder (&pair->timer); -- /* note: convert from milli to microseconds for g_time_val_add() */ -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -- } else { -- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -- return -1; - } -- } else { -- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -- return -1; - } -+ /* send the conncheck */ -+ agent_socket_send (pair->sockptr, &pair->remote->addr, -+ buffer_len, (gchar *)pair->stun_buffer); -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -+ &pair->remote->addr); -+ -+ timeout = stun_timer_remainder (&pair->timer); -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ g_get_current_time (&pair->next_tick); -+ g_time_val_add (&pair->next_tick, timeout * 1000); - - return 0; - } -@@ -2876,74 +2870,74 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", - agent, p, p->foundation, priv_state_to_gchar (p->state)); - -- if (p->state == NICE_CHECK_WAITING || -- p->state == NICE_CHECK_FROZEN) { -- nice_debug ("Agent %p : pair %p added for a triggered check.", -- agent, p); -- priv_add_pair_to_triggered_check_queue (agent, p); -- } -- else if (p->state == NICE_CHECK_IN_PROGRESS) { -- /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -- * we cancel the in-progress transaction, and after the -- * retransmission timeout, we create a new connectivity check -- * for that pair. The controlling role of this new check may -- * be different from the role of this cancelled check. -- * -- * note: the flag retransmit_on_timeout unset means that -- * another pair, with a higher priority is already nominated, -- * so there's no reason to recheck this pair, since it can in -- * no way replace the nominated one. -- */ -- if (!nice_socket_is_reliable (p->sockptr)) { -- if (p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p will be rechecked " -- "on stun timer timeout.", agent, p); -- /* this flag will determine the action at the retransmission -- * timeout of the stun timer -- */ -- p->recheck_on_timeout = TRUE; -- } else -- nice_debug ("Agent %p : pair %p won't be retransmitted.", -- agent, p); -- } -- } -- else if (p->state == NICE_CHECK_SUCCEEDED) { -- nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); -- /* note: this is a bit unsure corner-case -- let's do the -- same state update as for processing responses to our own checks */ -- /* note: this update is required by the dribble test, to -- * ensure the transition ready -> connected -> ready, because -- * an incoming stun request generates a discovered peer reflexive, -- * that causes the ready -> connected transition. -- */ -- priv_update_check_list_state_for_ready (agent, stream, component); -- } else if (p->state == NICE_CHECK_FAILED) { -- /* 7.2.1.4 Triggered Checks -- * If the state of the pair is Failed, it is changed to Waiting -- * and the agent MUST create a new connectivity check for that -- * pair (representing a new STUN Binding request transaction), by -- * enqueueing the pair in the triggered check queue. -- * -- * note: the flag retransmit_on_timeout unset means that -- * another pair, with a higher priority is already nominated, -- * we apply the same strategy than with an in-progress pair -- * above. -- */ -- if (p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p added for a triggered check.", -+ switch (p->state) { -+ case NICE_CHECK_WAITING: -+ case NICE_CHECK_FROZEN: -+ nice_debug ("Agent %p : pair %p added for a triggered check.", - agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -- /* If the component for this pair is in failed state, move it -- * back to connecting, and reinitiate the timers -+ break; -+ case NICE_CHECK_IN_PROGRESS: -+ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -+ * we cancel the in-progress transaction, and after the -+ * retransmission timeout, we create a new connectivity check -+ * for that pair. The controlling role of this new check may -+ * be different from the role of this cancelled check. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * so there's no reason to recheck this pair, since it can in -+ * no way replace the nominated one. - */ -- if (component->state == NICE_COMPONENT_STATE_FAILED) { -- agent_signal_component_state_change (agent, stream->id, -- component->id, NICE_COMPONENT_STATE_CONNECTING); -- conn_check_schedule_next (agent); -+ if (!nice_socket_is_reliable (p->sockptr) && -+ p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p will be rechecked " -+ "on stun timer timeout.", agent, p); -+ /* this flag will determine the action at the retransmission -+ * timeout of the stun timer -+ */ -+ p->recheck_on_timeout = TRUE; - } -- } else -- nice_debug ("Agent %p : pair %p won't be retransmitted.", -- agent, p); -+ break; -+ case NICE_CHECK_SUCCEEDED: -+ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); -+ /* note: this is a bit unsure corner-case -- let's do the -+ same state update as for processing responses to our own checks */ -+ /* note: this update is required by the dribble test, to -+ * ensure the transition ready -> connected -> ready, because -+ * an incoming stun request generates a discovered peer reflexive, -+ * that causes the ready -> connected transition. -+ */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ break; -+ case NICE_CHECK_FAILED: -+ /* 7.2.1.4 Triggered Checks -+ * If the state of the pair is Failed, it is changed to Waiting -+ * and the agent MUST create a new connectivity check for that -+ * pair (representing a new STUN Binding request transaction), by -+ * enqueueing the pair in the triggered check queue. -+ * -+ * note: the flag retransmit_on_timeout unset means that -+ * another pair, with a higher priority is already nominated, -+ * we apply the same strategy than with an in-progress pair -+ * above. -+ */ -+ if (p->retransmit_on_timeout) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } -+ } -+ break; -+ default: -+ break; - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -3271,208 +3265,200 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - socklen_t socklen = sizeof (sockaddr); - GSList *i; - StunUsageIceReturn res; -- gboolean trans_found = FALSE; - StunTransactionId discovery_id; - StunTransactionId response_id; - stun_message_id (resp, response_id); - -- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) { -+ for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -- if (p->stun_message.buffer) { -- stun_message_id (&p->stun_message, discovery_id); -+ if (p->stun_message.buffer == NULL) -+ continue; - -- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) { -- res = stun_usage_ice_conncheck_process (resp, -- &sockaddr.storage, &socklen, -- agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -- -- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* case: found a matching connectivity check request */ -- -- CandidateCheckPair *ok_pair = NULL; -- -- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- -- /* step: verify that response came from the same IP address we -- * sent the original request to (see 7.1.2.1. "Failure -- * Cases") */ -- if (nice_address_equal (from, &p->remote->addr) != TRUE) { -- -- p->state = NICE_CHECK_FAILED; -- if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- gchar tmpbuf2[INET6_ADDRSTRLEN]; -- nice_debug ("Agent %p : conncheck %p FAILED" -- " (mismatch of source address).", agent, p); -- nice_address_to_string (&p->remote->addr, tmpbuf); -- nice_address_to_string (from, tmpbuf2); -- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -- tmpbuf, nice_address_get_port (&p->remote->addr), -- tmpbuf2, nice_address_get_port (from)); -- } -- trans_found = TRUE; -- break; -- } -+ stun_message_id (&p->stun_message, discovery_id); - -- /* note: CONNECTED but not yet READY, see docs */ -- -- /* step: handle the possible case of a peer-reflexive -- * candidate where the mapped-address in response does -- * not match any local candidate, see 7.1.2.2.1 -- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -- -- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -- p->state = NICE_CHECK_SUCCEEDED; -- p->valid = TRUE; -- g_assert_not_reached (); -- nice_debug ("Agent %p : Mapped address not found." -- " conncheck %p SUCCEEDED.", agent, p); -- nice_component_add_valid_candidate (component, p->remote); -- } else { -- ok_pair = priv_process_response_check_for_reflexive (agent, -- stream, component, p, sockptr, &sockaddr.addr, -- local_candidate, remote_candidate); -- } -+ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -+ continue; - -- /* note: The success of this check might also -- * cause the state of other checks to change as well, ICE -- * spec 7.1.3.2.3 -- */ -- priv_conn_check_unfreeze_related (agent, stream, p); -- -- /* Note: this assignment helps to reduce the numbers of cases -- * to be tested. If ok_pair and p refer to distinct pairs, it -- * means that ok_pair is a discovered peer reflexive one, -- * caused by the check made on pair p. In that case, the -- * flags to be tested are on p, but the nominated flag will be -- * set on ok_pair. When there's no discovered pair, p and -- * ok_pair refer to the same pair. -- * To summarize : p is a SUCCEEDED pair, ok_pair is a -- * DISCOVERED, VALID, and eventually NOMINATED pair. -- */ -- if (!ok_pair) -- ok_pair = p; -- -- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -- Nominated Flag" (ID-19) */ -- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -- nice_debug ("Agent %p : Updating nominated flag (%s): " -- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -- "UDP" : "TCP", -- ok_pair, ok_pair->use_candidate_on_next_check, -- ok_pair->mark_nominated_on_response_arrival, -- p, p->use_candidate_on_next_check, -- p->mark_nominated_on_response_arrival); -- -- if (agent->controlling_mode) { -- switch (agent->nomination_mode) { -- case NICE_NOMINATION_MODE_REGULAR: -- if (p->use_candidate_on_next_check) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(regular nomination, control=1, " -- "use_cand_on_next_check=1).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- case NICE_NOMINATION_MODE_AGGRESSIVE: -- if (!p->nominated) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(aggressive nomination, control=1).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- default: -- /* Nothing to do */ -- break; -+ res = stun_usage_ice_conncheck_process (resp, -+ &sockaddr.storage, &socklen, -+ agent_to_ice_compatibility (agent)); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -+ "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -+ -+ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -+ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ /* case: found a matching connectivity check request */ -+ -+ CandidateCheckPair *ok_pair = NULL; -+ -+ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; -+ -+ /* step: verify that response came from the same IP address we -+ * sent the original request to (see 7.1.2.1. "Failure -+ * Cases") */ -+ if (nice_address_equal (from, &p->remote->addr) == FALSE) { -+ -+ p->state = NICE_CHECK_FAILED; -+ if (nice_debug_is_enabled ()) { -+ gchar tmpbuf[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_debug ("Agent %p : conncheck %p FAILED" -+ " (mismatch of source address).", agent, p); -+ nice_address_to_string (&p->remote->addr, tmpbuf); -+ nice_address_to_string (from, tmpbuf2); -+ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -+ tmpbuf, nice_address_get_port (&p->remote->addr), -+ tmpbuf2, nice_address_get_port (from)); -+ } -+ return TRUE; -+ } -+ -+ /* note: CONNECTED but not yet READY, see docs */ -+ -+ /* step: handle the possible case of a peer-reflexive -+ * candidate where the mapped-address in response does -+ * not match any local candidate, see 7.1.2.2.1 -+ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -+ -+ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ p->state = NICE_CHECK_SUCCEEDED; -+ p->valid = TRUE; -+ nice_debug ("Agent %p : Mapped address not found." -+ " conncheck %p SUCCEEDED.", agent, p); -+ nice_component_add_valid_candidate (component, p->remote); -+ } else -+ ok_pair = priv_process_response_check_for_reflexive (agent, -+ stream, component, p, sockptr, &sockaddr.addr, -+ local_candidate, remote_candidate); -+ -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ -+ if (!ok_pair) -+ ok_pair = p; -+ -+ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -+ Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, control=1, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; - } -- } else { -- if (p->mark_nominated_on_response_arrival) { -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(%s nomination, control=0, mark_on_response=1).", -- agent, ok_pair, ok_pair->foundation, -- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -- "aggressive" : "regular"); -+ "(aggressive nomination, control=1).", -+ agent, ok_pair, ok_pair->foundation); - ok_pair->nominated = TRUE; - } -- } -+ break; -+ default: -+ /* Nothing to do */ -+ break; - } -- -- if (ok_pair->nominated == TRUE) { -- priv_update_selected_pair (agent, component, ok_pair); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a nominated pair"); -- -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, control=0, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; - } -+ } -+ } - -- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -- states" and 8.1.2 "Updating States", ID-19) */ -- priv_update_check_list_state_for_ready (agent, stream, component); -+ if (ok_pair->nominated == TRUE) { -+ priv_update_selected_pair (agent, component, ok_pair); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a nominated pair"); - -- trans_found = TRUE; -- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -- /* case: role conflict error, need to restart with new role */ -- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- -- /* note: this res value indicates that the role of the peer -- * agent has not changed after the tie-breaker comparison, so -- * this is our role that must change. see ICE sect. 7.1.3.1 -- * "Failure Cases". Our role might already have changed due to -- * an earlier incoming request, but if not, change role now. -- * -- * Sect. 7.1.3.1 is not clear on this point, but we choose to -- * put the candidate pair in the triggered check list even -- * when the agent did not switch its role. The reason for this -- * interpretation is that the reception of the stun reply, even -- * an error reply, is a good sign that this pair will be -- * valid, if we retry the check after the role of both peers -- * has been fixed. -- */ -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } - -- if (p->stun_message.buffer != NULL) { -- guint64 tie; -- gboolean controlled_mode; -+ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -+ states" and 8.1.2 "Updating States", ID-19) */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -+ /* case: role conflict error, need to restart with new role */ -+ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -+ -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ - -- controlled_mode = (stun_message_find64 (&p->stun_message, -- STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -- STUN_MESSAGE_RETURN_SUCCESS); -+ if (p->stun_message.buffer != NULL) { -+ guint64 tie; -+ gboolean controlled_mode; - -- priv_check_for_role_conflict (agent, controlled_mode); -+ controlled_mode = (stun_message_find64 (&p->stun_message, -+ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -+ STUN_MESSAGE_RETURN_SUCCESS); - -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- priv_add_pair_to_triggered_check_queue (agent, p); -- } -- trans_found = TRUE; -- } else { -- /* case: STUN error, the check STUN context was freed */ -- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- trans_found = TRUE; -- } -+ priv_check_for_role_conflict (agent, controlled_mode); -+ -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; -+ priv_add_pair_to_triggered_check_queue (agent, p); - } -+ } else { -+ /* case: STUN error, the check STUN context was freed */ -+ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -+ p->stun_message.buffer = NULL; -+ p->stun_message.buffer_len = 0; - } -+ return TRUE; - } - -- return trans_found; -+ return FALSE; - } - - /* --- -2.13.6 - - -From ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 26 Jun 2017 20:41:49 +0200 -Subject: [PATCH 56/70] conncheck: make debug sentences more accurate - -We add a helper function to print the pair state in-extenso. - -Differential Revision:
https://phabricator.freedesktop.org/D1759
---- - agent/conncheck.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++------- - 1 file changed, 61 insertions(+), 9 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 874f7b1..9517ee1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -108,6 +108,54 @@ priv_state_to_gchar (NiceCheckState state) - } - - static const gchar * -+priv_state_to_string (NiceCheckState state) -+{ -+ switch (state) { -+ case NICE_CHECK_WAITING: -+ return "waiting"; -+ case NICE_CHECK_IN_PROGRESS: -+ return "in progress"; -+ case NICE_CHECK_SUCCEEDED: -+ return "succeeded"; -+ case NICE_CHECK_FAILED: -+ return "failed"; -+ case NICE_CHECK_FROZEN: -+ return "frozen"; -+ case NICE_CHECK_DISCOVERED: -+ return "discovered"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * -+priv_ice_return_to_string (StunUsageIceReturn ice_return) -+{ -+ switch (ice_return) { -+ case STUN_USAGE_ICE_RETURN_SUCCESS: -+ return "success"; -+ case STUN_USAGE_ICE_RETURN_ERROR: -+ return "error"; -+ case STUN_USAGE_ICE_RETURN_INVALID: -+ return "invalid"; -+ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT: -+ return "role conflict"; -+ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST: -+ return "invalid request"; -+ case STUN_USAGE_ICE_RETURN_INVALID_METHOD: -+ return "invalid method"; -+ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR: -+ return "memory error"; -+ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS: -+ return "invalid address"; -+ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS: -+ return "no mapped address"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * - priv_candidate_type_to_string (NiceCandidateType type) - { - switch (type) { -@@ -2614,7 +2662,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - nice_address_to_string (&pair->remote->addr, tmpbuf2); - nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " - "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, -+ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr), - pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, -@@ -2622,7 +2670,8 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -- pair->prflx_priority, controlling); -+ pair->prflx_priority, -+ controlling ? "controlling" : "controlled"); - } - - if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -@@ -2867,8 +2916,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - p = p->succeeded_pair; - } - -- nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", -- agent, p, p->foundation, priv_state_to_gchar (p->state)); -+ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", -+ agent, p, p->foundation, priv_state_to_string (p->state)); - - switch (p->state) { - case NICE_CHECK_WAITING: -@@ -3283,8 +3332,11 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - res = stun_usage_ice_conncheck_process (resp, - &sockaddr.storage, &socklen, - agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -+ "%s,res=%s.", -+ agent, p, -+ agent->controlling_mode ? "controlling" : "controlled", -+ priv_ice_return_to_string (res)); - - if (res == STUN_USAGE_ICE_RETURN_SUCCESS || - res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -@@ -3370,7 +3422,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - case NICE_NOMINATION_MODE_REGULAR: - if (p->use_candidate_on_next_check) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(regular nomination, control=1, " -+ "(regular nomination, controlling, " - "use_cand_on_next_check=1).", - agent, ok_pair, ok_pair->foundation); - ok_pair->nominated = TRUE; -@@ -3379,7 +3431,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - case NICE_NOMINATION_MODE_AGGRESSIVE: - if (!p->nominated) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(aggressive nomination, control=1).", -+ "(aggressive nomination, controlling).", - agent, ok_pair, ok_pair->foundation); - ok_pair->nominated = TRUE; - } -@@ -3391,7 +3443,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - } else { - if (p->mark_nominated_on_response_arrival) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(%s nomination, control=0, mark_on_response=1).", -+ "(%s nomination, controlled, mark_on_response=1).", - agent, ok_pair, ok_pair->foundation, - agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? - "aggressive" : "regular"); --- -2.13.6 - - -From e860948b5fe3a791119957f26045b8f5159baeff Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 26 Jun 2017 21:06:36 +0200 -Subject: [PATCH 57/70] conncheck: use stun_timer_remainder less frequently - -We try to use stun_timer_remainder() less frequently, particularily -in the debug messages, and favour of the next_tick value associated -to the pair. - -Differential Revision:
https://phabricator.freedesktop.org/D1760
---- - agent/conncheck.c | 77 ++++++++++++++++++++++++++++++++++--------------------- - 1 file changed, 48 insertions(+), 29 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 9517ee1..8075d4f 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -86,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - now->tv_sec >= timer->tv_sec; - } - -+static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now) -+{ -+ unsigned int delay; -+ if (now->tv_sec > timer->tv_sec || -+ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec)) -+ return 0; -+ delay = (timer->tv_sec - now->tv_sec) * 1000; -+ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000; -+ return delay; -+} -+ - static gchar - priv_state_to_gchar (NiceCheckState state) - { -@@ -180,10 +191,13 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - { - GSList *i, *k; - guint j; -+ GTimeVal now; - - if (!nice_debug_is_verbose ()) - return; - -+ g_get_current_time (&now); -+ - #define PRIORITY_LEN 32 - - nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", -@@ -209,7 +223,8 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - priv_candidate_type_to_string (pair->local->type), - priv_candidate_type_to_string (pair->remote->type), - timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay, -+ timer->delay - priv_timer_remainder (&pair->next_tick, &now), -+ timer->delay, - local_addr, nice_address_get_port (&pair->local->addr), - remote_addr, nice_address_get_port (&pair->remote->addr), - priv_state_to_gchar (pair->state), -@@ -445,8 +460,6 @@ priv_find_first_frozen_check_list (NiceAgent *agent) - */ - static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) - { -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); - pair->state = NICE_CHECK_IN_PROGRESS; - nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); - conn_check_send (agent, pair); -@@ -651,12 +664,15 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) - { - gboolean keep_timer_going = FALSE; - GSList *i; - CandidateCheckPair *pair; - unsigned int timeout; -+ GTimeVal now; -+ -+ g_get_current_time (&now); - - /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { -@@ -678,7 +694,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - continue; - } - -- if (!priv_timer_expired (&p->next_tick, now)) -+ if (!priv_timer_expired (&p->next_tick, &now)) - continue; - - switch (stun_timer_refresh (&p->timer)) { -@@ -712,8 +728,6 @@ timer_timeout: - priv_update_check_list_state_for_ready (agent, stream, component); - break; - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -- timeout = stun_timer_remainder (&p->timer); -- - /* case: retransmission stopped, due to the nomination of - * a pair with a higher priority than this in-progress pair, - * ICE spec, sect 8.1.2 "Updating States", item 2.2 -@@ -730,9 +744,13 @@ timer_timeout: - break; - - /* case: not ready, so schedule a new timeout */ -+ timeout = stun_timer_remainder (&p->timer); -+ - nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -- "(timeout %dms, delay=%dms, retrans=%d).", -- agent, p, timeout, p->timer.delay, p->timer.retransmissions); -+ "(timer=%d/%d %d/%dms).", -+ agent, p, -+ p->timer.retransmissions, p->timer.max_retransmissions, -+ p->timer.delay - timeout, p->timer.delay); - - agent_socket_send (p->sockptr, &p->remote->addr, - stun_message_length (&p->stun_message), -@@ -740,7 +758,7 @@ timer_timeout: - - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -+ p->next_tick = now; - g_time_val_add (&p->next_tick, timeout * 1000); - - return TRUE; -@@ -748,7 +766,7 @@ timer_timeout: - timeout = stun_timer_remainder (&p->timer); - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -+ p->next_tick = now; - g_time_val_add (&p->next_tick, timeout * 1000); - - keep_timer_going = TRUE; -@@ -1001,9 +1019,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - CandidateCheckPair *pair = NULL; - gboolean keep_timer_going = FALSE; - GSList *i, *j; -- GTimeVal now; -- -- g_get_current_time (&now); - - /* the conncheck really starts when we have built - * a connection check list for each stream -@@ -1047,7 +1062,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - */ - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -- if (priv_conn_check_tick_stream (stream, agent, &now)) -+ if (priv_conn_check_tick_stream (stream, agent)) - keep_timer_going = TRUE; - if (priv_conn_check_tick_stream_nominate (stream, agent)) - keep_timer_going = TRUE; -@@ -2731,12 +2746,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - return -1; - } - -- if (nice_socket_is_reliable(pair->sockptr)) -- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); -- else { -+ if (nice_socket_is_reliable(pair->sockptr)) { -+ timeout = agent->stun_reliable_timeout; -+ stun_timer_start_reliable(&pair->timer, timeout); -+ } else { - StunTimer *timer = &pair->timer; - -- if (pair->recheck_on_timeout) -+ if (pair->recheck_on_timeout) { -+ GTimeVal now; - /* The pair recheck on timeout can easily cause repetitive rechecks in - * a ping-pong effect, if both peers with the same behaviour try to - * check the same pair almost simultaneously, and if the network rtt -@@ -2751,17 +2768,24 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * After enough retransmissions, the timeout delay becomes - * longer than the rtt, and the stun reply can be handled. - */ -+ -+ g_get_current_time (&now); -+ timeout = priv_timer_remainder (&pair->next_tick, &now); - nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", - agent, pair, - timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay); -- else -- stun_timer_start (timer, -- priv_compute_conncheck_timer (agent, stream), -- agent->stun_max_retransmissions); -+ timer->delay - timeout, -+ timer->delay); -+ } else { -+ timeout = priv_compute_conncheck_timer (agent, stream); -+ stun_timer_start (timer, timeout, agent->stun_max_retransmissions); -+ } - pair->recheck_on_timeout = FALSE; - } - -+ g_get_current_time (&pair->next_tick); -+ g_time_val_add (&pair->next_tick, timeout * 1000); -+ - /* TCP-ACTIVE candidate must create a new socket before sending - * by connecting to the peer. The new socket is stored in the candidate - * check pair, until we discover a new local peer reflexive */ -@@ -2796,11 +2820,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, - &pair->remote->addr); - -- timeout = stun_timer_remainder (&pair->timer); -- /* note: convert from milli to microseconds for g_time_val_add() */ -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -- - return 0; - } - --- -2.13.6 - - -From 36f306f4a95f1c2b3e9c584b5a645a78e231c020 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon, 26 Jun 2017 21:41:44 +0200 -Subject: [PATCH 58/70] conncheck: support several stun requests per pair - -This patch should improve the reliabily of the connection check by -keeping the record of several simultaneous ongoing stun requests per -pair. A new stun request on an in-progress pair typically is caused by -in inbound stun request from the peer on this same pair. This is named -"Triggered Checks" in the spec. When this situation arises, it is fair -to handle these two stun requests simultaneously, the triggered check, -and the initial ordinary check, since both can potentially succeed. - -Differential Revision:
https://phabricator.freedesktop.org/D1761
---- - agent/conncheck.c | 701 +++++++++++++++++++++++++++--------------------------- - agent/conncheck.h | 21 +- - 2 files changed, 361 insertions(+), 361 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 8075d4f..2a85738 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -189,8 +189,8 @@ priv_candidate_type_to_string (NiceCandidateType type) - static void - priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) - { -- GSList *i, *k; -- guint j; -+ GSList *i, *k, *l; -+ guint j, m; - GTimeVal now; - - if (!nice_debug_is_verbose ()) -@@ -210,27 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - if (pair->component_id == j) { - gchar local_addr[INET6_ADDRSTRLEN]; - gchar remote_addr[INET6_ADDRSTRLEN]; -- StunTimer *timer = &pair->timer; - - nice_address_to_string (&pair->local->addr, local_addr); - nice_address_to_string (&pair->remote->addr, remote_addr); - - nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -- "f=%s t=%s:%s timer=%d/%d %d/%dms " -- "[%s]:%u > [%s]:%u state=%c%s%s%s", -+ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s", - agent, pair->stream_id, pair->component_id, pair, - pair->foundation, - priv_candidate_type_to_string (pair->local->type), - priv_candidate_type_to_string (pair->remote->type), -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - priv_timer_remainder (&pair->next_tick, &now), -- timer->delay, - local_addr, nice_address_get_port (&pair->local->addr), - remote_addr, nice_address_get_port (&pair->remote->addr), - priv_state_to_gchar (pair->state), - pair->valid ? "V" : "", - pair->nominated ? "N" : "", - g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); -+ -+ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) { -+ StunTransaction *stun = l->data; -+ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -+ "stun#=%d timer=%d/%d %d/%dms buf=%p %s", -+ agent, pair->stream_id, pair->component_id, pair, m, -+ stun->timer.retransmissions, stun->timer.max_retransmissions, -+ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now), -+ stun->timer.delay, -+ stun->message.buffer, -+ (m == 0 && pair->retransmit) ? "(R)" : ""); -+ } - } - } - } -@@ -608,52 +615,92 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - } - } - -+/* -+ * Create a new STUN transaction and add it to the list -+ * of ongoing stun transactions of a pair. -+ * -+ * @pair the pair the new stun transaction should be added to. -+ * @return the created stun transaction. -+ */ -+static StunTransaction * -+priv_add_stun_transaction (CandidateCheckPair *pair) -+{ -+ StunTransaction *stun = g_slice_new0 (StunTransaction); -+ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun); -+ pair->retransmit = TRUE; -+ return stun; -+} -+ -+/* -+ * Forget a STUN transaction. -+ * -+ * @data the stun transaction to be forgotten. -+ * @user_data the component contained the concerned stun agent. -+ */ - static void --candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) -+priv_forget_stun_transaction (gpointer data, gpointer user_data) - { -+ StunTransaction *stun = data; -+ NiceComponent *component = user_data; - StunTransactionId id; -- NiceComponent *component; -- -- component = nice_stream_find_component_by_id (stream, p->component_id); -- -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); - -- if (p->stun_message.buffer != NULL) { -- stun_message_id (&p->stun_message, id); -+ if (stun->message.buffer != NULL) { -+ stun_message_id (&stun->message, id); - stun_agent_forget_transaction (&component->stun_agent, id); - } -+} - -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+static void -+priv_free_stun_transaction (gpointer data) -+{ -+ g_slice_free (StunTransaction, data); - } - - /* -- * Function that resubmits a new connection check, after a previous -- * check in in-progress state got cancelled due to an incoming stun -- * request matching this same pair -+ * Remove a STUN transaction from a pair, and forget it -+ * from the related component stun agent. - * -- * @return will return TRUE if the pair is scheduled for recheck -+ * @pair the pair the stun transaction should be removed from. -+ * @stun the stun transaction to be removed. -+ * @component the component containing the stun agent used to -+ * forget the stun transaction. - */ --static gboolean --priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) -+static void -+priv_remove_stun_transaction (CandidateCheckPair *pair, -+ StunTransaction *stun, NiceComponent *component) - { -- if (p->recheck_on_timeout) { -- g_assert (p->state == NICE_CHECK_IN_PROGRESS); -- /* this cancelled pair may have the flag 'mark nominated -- * on response arrival' set, we want to keep it, because -- * this is needed to nominate this pair in aggressive -- * nomination, when the agent is in controlled mode. -- * -- * this cancelled pair may also have the flag 'use candidate -- * on next check' set, that we want to preserve too. -- */ -- nice_debug ("Agent %p : pair %p was cancelled, " -- "triggering a new connection check", agent, p); -- priv_add_pair_to_triggered_check_queue (agent, p); -- return TRUE; -- } -- return FALSE; -+ priv_forget_stun_transaction (stun, component); -+ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun); -+ priv_free_stun_transaction (stun); -+} -+ -+/* -+ * Remove all STUN transactions from a pair, and forget them -+ * from the related component stun agent. -+ * -+ * @pair the pair the stun list should be cleared. -+ * @component the component containing the stun agent used to -+ * forget the stun transactions. -+ */ -+static void -+priv_free_all_stun_transactions (CandidateCheckPair *pair, -+ NiceComponent *component) -+{ -+ if (component) -+ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component); -+ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction); -+ pair->stun_transactions = NULL; -+} -+ -+static void -+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) -+{ -+ NiceComponent *component; -+ -+ component = nice_stream_find_component_by_id (stream, p->component_id); -+ p->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ priv_free_all_stun_transactions (p, component); - } - - /* -@@ -667,7 +714,7 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) - static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) - { - gboolean keep_timer_going = FALSE; -- GSList *i; -+ GSList *i, *j; - CandidateCheckPair *pair; - unsigned int timeout; - GTimeVal now; -@@ -679,39 +726,59 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - CandidateCheckPair *p = i->data; - gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; - NiceComponent *component; -+ StunTransaction *stun; -+ -+ if (p->stun_transactions == NULL) -+ continue; - - if (!agent_find_component (agent, p->stream_id, p->component_id, - NULL, &component)) - continue; - -- if (p->state != NICE_CHECK_IN_PROGRESS) -- continue; -+ /* The first stun transaction of the list may eventually be -+ * retransmitted, other stun transactions just have their -+ * timer updated. -+ */ - -- if (p->stun_message.buffer == NULL) { -- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -- continue; -+ j = p->stun_transactions->next; -+ -+ /* process all stun transactions except the first one */ -+ while (j) { -+ StunTransaction *s = j->data; -+ GSList *next = j->next; -+ -+ if (priv_timer_expired (&s->next_tick, &now)) -+ switch (stun_timer_refresh (&s->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: -+ priv_remove_stun_transaction (p, s, component); -+ break; -+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -+ timeout = stun_timer_remainder (&s->timer); -+ s->next_tick = now; -+ g_time_val_add (&s->next_tick, timeout * 1000); -+ break; -+ default: -+ break; -+ } -+ j = next; - } - -- if (!priv_timer_expired (&p->next_tick, &now)) -+ if (p->state != NICE_CHECK_IN_PROGRESS) - continue; - -- switch (stun_timer_refresh (&p->timer)) { -- case STUN_USAGE_TIMER_RETURN_TIMEOUT: --timer_timeout: -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ /* process the first stun transaction of the list */ -+ stun = p->stun_transactions->data; -+ if (!priv_timer_expired (&stun->next_tick, &now)) -+ continue; - -+ switch (stun_timer_refresh (&stun->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: -+timer_return_timeout: - /* case: error, abort processing */ - nice_address_to_string (&p->local->addr, tmpbuf1); - nice_address_to_string (&p->remote->addr, tmpbuf2); -- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on " -+ "connectivity check %p", agent, p); - nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, - tmpbuf1, nice_address_get_port (&p->local->addr), - tmpbuf2, nice_address_get_port (&p->remote->addr)); -@@ -732,42 +799,33 @@ timer_timeout: - * a pair with a higher priority than this in-progress pair, - * ICE spec, sect 8.1.2 "Updating States", item 2.2 - */ -- if (!p->retransmit_on_timeout) -- goto timer_timeout; -- -- /* case: conncheck cancelled due to in-progress incoming -- * check, requeing the pair, ICE spec, sect 7.2.1.4 -- * "Triggered Checks", "If the state of that pair is -- * In-Progress..." -- */ -- if (priv_conn_recheck_on_timeout (agent, p)) -- break; -+ if (!p->retransmit) -+ goto timer_return_timeout; - - /* case: not ready, so schedule a new timeout */ -- timeout = stun_timer_remainder (&p->timer); -+ timeout = stun_timer_remainder (&stun->timer); - - nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " - "(timer=%d/%d %d/%dms).", - agent, p, -- p->timer.retransmissions, p->timer.max_retransmissions, -- p->timer.delay - timeout, p->timer.delay); -+ stun->timer.retransmissions, stun->timer.max_retransmissions, -+ stun->timer.delay - timeout, stun->timer.delay); - - agent_socket_send (p->sockptr, &p->remote->addr, -- stun_message_length (&p->stun_message), -- (gchar *)p->stun_buffer); -- -+ stun_message_length (&stun->message), -+ (gchar *)stun->buffer); - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); - - return TRUE; - case STUN_USAGE_TIMER_RETURN_SUCCESS: -- timeout = stun_timer_remainder (&p->timer); -+ timeout = stun_timer_remainder (&stun->timer); - - /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); - - keep_timer_going = TRUE; - break; -@@ -948,7 +1006,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - g_assert (p->state == NICE_CHECK_SUCCEEDED); - nice_debug ("Agent %p : restarting check %p with " - "USE-CANDIDATE attrib (regular nomination)", agent, p); -- p->recheck_on_timeout = FALSE; - p->use_candidate_on_next_check = TRUE; - priv_add_pair_to_triggered_check_queue (agent, p); - keep_timer_going = TRUE; -@@ -972,7 +1029,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -- p->recheck_on_timeout = FALSE; - priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); - keep_timer_going = TRUE; -@@ -2186,7 +2242,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - } - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); -- pair->retransmit_on_timeout = TRUE; - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -2381,8 +2436,7 @@ static void conn_check_free_item (gpointer data) - - if (pair->agent) - priv_remove_pair_from_triggered_check_queue (pair->agent, pair); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -+ priv_free_all_stun_transactions (pair, NULL); - g_slice_free (CandidateCheckPair, pair); - } - -@@ -2655,6 +2709,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - bool cand_use = controlling; - size_t buffer_len; - unsigned int timeout; -+ StunTransaction *stun; - - if (!agent_find_component (agent, pair->stream_id, pair->component_id, - &stream, &component)) -@@ -2718,13 +2773,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - - if (uname_len == 0) { - nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; - return -1; - } - -+ stun = priv_add_stun_transaction (pair); -+ - buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -+ &stun->message, stun->buffer, sizeof(stun->buffer), - uname, uname_len, password, password_len, - cand_use, controlling, pair->prflx_priority, - agent->tie_breaker, -@@ -2732,7 +2787,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - agent_to_ice_compatibility (agent)); - - nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -- pair->stun_message.buffer); -+ stun->message.buffer); - - if (agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007) { -@@ -2741,50 +2796,20 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - - if (buffer_len == 0) { - nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -+ priv_remove_stun_transaction (pair, stun, component); - return -1; - } - - if (nice_socket_is_reliable(pair->sockptr)) { - timeout = agent->stun_reliable_timeout; -- stun_timer_start_reliable(&pair->timer, timeout); -+ stun_timer_start_reliable(&stun->timer, timeout); - } else { -- StunTimer *timer = &pair->timer; -- -- if (pair->recheck_on_timeout) { -- GTimeVal now; -- /* The pair recheck on timeout can easily cause repetitive rechecks in -- * a ping-pong effect, if both peers with the same behaviour try to -- * check the same pair almost simultaneously, and if the network rtt -- * is greater than the initial timer rto. The reply to the initial -- * stun request may arrive after the in-progress conncheck -- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation -- * creates a new stun request, and forgets the initial one. -- * The conncheck timer is restarted with the same initial value, -- * so the same situation happens again later. -- * -- * We choose to avoid resetting the timer in such situation. -- * After enough retransmissions, the timeout delay becomes -- * longer than the rtt, and the stun reply can be handled. -- */ -- -- g_get_current_time (&now); -- timeout = priv_timer_remainder (&pair->next_tick, &now); -- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", -- agent, pair, -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - timeout, -- timer->delay); -- } else { -- timeout = priv_compute_conncheck_timer (agent, stream); -- stun_timer_start (timer, timeout, agent->stun_max_retransmissions); -- } -- pair->recheck_on_timeout = FALSE; -+ timeout = priv_compute_conncheck_timer (agent, stream); -+ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); - } - -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -+ g_get_current_time (&stun->next_tick); -+ g_time_val_add (&stun->next_tick, timeout * 1000); - - /* TCP-ACTIVE candidate must create a new socket before sending - * by connecting to the peer. The new socket is stored in the candidate -@@ -2814,10 +2839,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - } - /* send the conncheck */ - agent_socket_send (pair->sockptr, &pair->remote->addr, -- buffer_len, (gchar *)pair->stun_buffer); -+ buffer_len, (gchar *)stun->buffer); - - if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) -- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -+ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, - &pair->remote->addr); - - return 0; -@@ -2881,8 +2906,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - (p->state == NICE_CHECK_WAITING && like_in_progress)) { - if (highest_nominated_priority != 0 && - p->priority < highest_nominated_priority) { -- p->retransmit_on_timeout = FALSE; -- p->recheck_on_timeout = FALSE; -+ p->retransmit = FALSE; - nice_debug ("Agent %p : pair %p will not be retransmitted.", - agent, p); - } else { -@@ -2938,9 +2962,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", - agent, p, p->foundation, priv_state_to_string (p->state)); - -- switch (p->state) { -- case NICE_CHECK_WAITING: -- case NICE_CHECK_FROZEN: -+ switch (p->state) { -+ case NICE_CHECK_WAITING: -+ case NICE_CHECK_FROZEN: - nice_debug ("Agent %p : pair %p added for a triggered check.", - agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -@@ -2952,19 +2976,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * for that pair. The controlling role of this new check may - * be different from the role of this cancelled check. - * -- * note: the flag retransmit_on_timeout unset means that -+ * note: the flag retransmit unset means that - * another pair, with a higher priority is already nominated, - * so there's no reason to recheck this pair, since it can in - * no way replace the nominated one. - */ -- if (!nice_socket_is_reliable (p->sockptr) && -- p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p will be rechecked " -- "on stun timer timeout.", agent, p); -- /* this flag will determine the action at the retransmission -- * timeout of the stun timer -+ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ } -+ break; -+ case NICE_CHECK_FAILED: -+ if (p->retransmit) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers - */ -- p->recheck_on_timeout = TRUE; -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } - } - break; - case NICE_CHECK_SUCCEEDED: -@@ -2978,32 +3013,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - */ - priv_update_check_list_state_for_ready (agent, stream, component); - break; -- case NICE_CHECK_FAILED: -- /* 7.2.1.4 Triggered Checks -- * If the state of the pair is Failed, it is changed to Waiting -- * and the agent MUST create a new connectivity check for that -- * pair (representing a new STUN Binding request transaction), by -- * enqueueing the pair in the triggered check queue. -- * -- * note: the flag retransmit_on_timeout unset means that -- * another pair, with a higher priority is already nominated, -- * we apply the same strategy than with an in-progress pair -- * above. -- */ -- if (p->retransmit_on_timeout) { -- nice_debug ("Agent %p : pair %p added for a triggered check.", -- agent, p); -- priv_add_pair_to_triggered_check_queue (agent, p); -- /* If the component for this pair is in failed state, move it -- * back to connecting, and reinitiate the timers -- */ -- if (component->state == NICE_COMPONENT_STATE_FAILED) { -- agent_signal_component_state_change (agent, stream->id, -- component->id, NICE_COMPONENT_STATE_CONNECTING); -- conn_check_schedule_next (agent); -- } -- } -- break; - default: - break; - } -@@ -3260,16 +3269,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (new_pair == p) - p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; -- /* note: we cancel the potential in-progress transaction -- * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if -- * we receive a valid reply before transmission timeout... -- */ -- p->recheck_on_timeout = FALSE; -- /* ... or just after the transmission timeout, while the pair is -- * temporarily put on the triggered check list on the way to be -- * be rechecked: we remove it from the list too. -- */ - priv_remove_pair_from_triggered_check_queue (agent, p); -+ priv_free_all_stun_transactions (p, component); - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - nice_component_add_valid_candidate (component, remote_candidate); - } -@@ -3304,8 +3305,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" - */ - p->state = NICE_CHECK_SUCCEEDED; -- p->recheck_on_timeout = FALSE; - priv_remove_pair_from_triggered_check_queue (agent, p); -+ priv_free_all_stun_transactions (p, component); - nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", - agent, p, new_pair); - } -@@ -3331,7 +3332,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - struct sockaddr addr; - } sockaddr; - socklen_t socklen = sizeof (sockaddr); -- GSList *i; -+ GSList *i, *j; -+ guint k; - StunUsageIceReturn res; - StunTransactionId discovery_id; - StunTransactionId response_id; -@@ -3340,193 +3342,186 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -- if (p->stun_message.buffer == NULL) -- continue; -- -- stun_message_id (&p->stun_message, discovery_id); -- -- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -- continue; -- -- res = stun_usage_ice_conncheck_process (resp, -- &sockaddr.storage, &socklen, -- agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -- "%s,res=%s.", -- agent, p, -- agent->controlling_mode ? "controlling" : "controlled", -- priv_ice_return_to_string (res)); -- -- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* case: found a matching connectivity check request */ -- -- CandidateCheckPair *ok_pair = NULL; -- -- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- -- /* step: verify that response came from the same IP address we -- * sent the original request to (see 7.1.2.1. "Failure -- * Cases") */ -- if (nice_address_equal (from, &p->remote->addr) == FALSE) { -- -- p->state = NICE_CHECK_FAILED; -- if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- gchar tmpbuf2[INET6_ADDRSTRLEN]; -- nice_debug ("Agent %p : conncheck %p FAILED" -- " (mismatch of source address).", agent, p); -- nice_address_to_string (&p->remote->addr, tmpbuf); -- nice_address_to_string (from, tmpbuf2); -- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -- tmpbuf, nice_address_get_port (&p->remote->addr), -- tmpbuf2, nice_address_get_port (from)); -- } -- return TRUE; -- } -- -- /* note: CONNECTED but not yet READY, see docs */ -- -- /* step: handle the possible case of a peer-reflexive -- * candidate where the mapped-address in response does -- * not match any local candidate, see 7.1.2.2.1 -- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -- -- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- p->state = NICE_CHECK_SUCCEEDED; -- p->valid = TRUE; -- nice_debug ("Agent %p : Mapped address not found." -- " conncheck %p SUCCEEDED.", agent, p); -- nice_component_add_valid_candidate (component, p->remote); -- } else -- ok_pair = priv_process_response_check_for_reflexive (agent, -- stream, component, p, sockptr, &sockaddr.addr, -- local_candidate, remote_candidate); -- -- /* note: The success of this check might also -- * cause the state of other checks to change as well, ICE -- * spec 7.1.3.2.3 -- */ -- priv_conn_check_unfreeze_related (agent, stream, p); -- -- /* Note: this assignment helps to reduce the numbers of cases -- * to be tested. If ok_pair and p refer to distinct pairs, it -- * means that ok_pair is a discovered peer reflexive one, -- * caused by the check made on pair p. In that case, the -- * flags to be tested are on p, but the nominated flag will be -- * set on ok_pair. When there's no discovered pair, p and -- * ok_pair refer to the same pair. -- * To summarize : p is a SUCCEEDED pair, ok_pair is a -- * DISCOVERED, VALID, and eventually NOMINATED pair. -- */ -- if (!ok_pair) -- ok_pair = p; -- -- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -- Nominated Flag" (ID-19) */ -- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -- nice_debug ("Agent %p : Updating nominated flag (%s): " -- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -- "UDP" : "TCP", -- ok_pair, ok_pair->use_candidate_on_next_check, -- ok_pair->mark_nominated_on_response_arrival, -- p, p->use_candidate_on_next_check, -- p->mark_nominated_on_response_arrival); -- -- if (agent->controlling_mode) { -- switch (agent->nomination_mode) { -- case NICE_NOMINATION_MODE_REGULAR: -- if (p->use_candidate_on_next_check) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(regular nomination, controlling, " -- "use_cand_on_next_check=1).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- case NICE_NOMINATION_MODE_AGGRESSIVE: -- if (!p->nominated) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(aggressive nomination, controlling).", -- agent, ok_pair, ok_pair->foundation); -- ok_pair->nominated = TRUE; -- } -- break; -- default: -- /* Nothing to do */ -- break; -- } -- } else { -- if (p->mark_nominated_on_response_arrival) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated " -- "(%s nomination, controlled, mark_on_response=1).", -- agent, ok_pair, ok_pair->foundation, -- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -- "aggressive" : "regular"); -- ok_pair->nominated = TRUE; -- } -- } -- } -- -- if (ok_pair->nominated == TRUE) { -- priv_update_selected_pair (agent, component, ok_pair); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a nominated pair"); -+ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) { -+ StunTransaction *stun = j->data; -+ -+ stun_message_id (&stun->message, discovery_id); -+ -+ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -+ continue; -+ -+ res = stun_usage_ice_conncheck_process (resp, -+ &sockaddr.storage, &socklen, -+ agent_to_ice_compatibility (agent)); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -+ "%s,res=%s,stun#=%d.", -+ agent, p, -+ agent->controlling_mode ? "controlling" : "controlled", -+ priv_ice_return_to_string (res), k); -+ -+ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -+ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ /* case: found a matching connectivity check request */ -+ -+ CandidateCheckPair *ok_pair = NULL; -+ -+ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -+ priv_remove_stun_transaction (p, stun, component); -+ -+ /* step: verify that response came from the same IP address we -+ * sent the original request to (see 7.1.2.1. "Failure -+ * Cases") */ -+ if (nice_address_equal (from, &p->remote->addr) == FALSE) { -+ candidate_check_pair_fail (stream, agent, p); -+ if (nice_debug_is_enabled ()) { -+ gchar tmpbuf[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_debug ("Agent %p : conncheck %p FAILED" -+ " (mismatch of source address).", agent, p); -+ nice_address_to_string (&p->remote->addr, tmpbuf); -+ nice_address_to_string (from, tmpbuf2); -+ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -+ tmpbuf, nice_address_get_port (&p->remote->addr), -+ tmpbuf2, nice_address_get_port (from)); -+ } -+ return TRUE; -+ } - -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -+ /* note: CONNECTED but not yet READY, see docs */ -+ -+ /* step: handle the possible case of a peer-reflexive -+ * candidate where the mapped-address in response does -+ * not match any local candidate, see 7.1.2.2.1 -+ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -+ -+ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ p->state = NICE_CHECK_SUCCEEDED; -+ p->valid = TRUE; -+ nice_debug ("Agent %p : Mapped address not found." -+ " conncheck %p SUCCEEDED.", agent, p); -+ nice_component_add_valid_candidate (component, p->remote); -+ } else -+ ok_pair = priv_process_response_check_for_reflexive (agent, -+ stream, component, p, sockptr, &sockaddr.addr, -+ local_candidate, remote_candidate); -+ -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ -+ if (!ok_pair) -+ ok_pair = p; -+ -+ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -+ Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, controlling, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(aggressive nomination, controlling).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ default: -+ /* Nothing to do */ -+ break; -+ } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, controlled, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; -+ } -+ } -+ } - -- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -- states" and 8.1.2 "Updating States", ID-19) */ -- priv_update_check_list_state_for_ready (agent, stream, component); -- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -- /* case: role conflict error, need to restart with new role */ -- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- -- /* note: this res value indicates that the role of the peer -- * agent has not changed after the tie-breaker comparison, so -- * this is our role that must change. see ICE sect. 7.1.3.1 -- * "Failure Cases". Our role might already have changed due to -- * an earlier incoming request, but if not, change role now. -- * -- * Sect. 7.1.3.1 is not clear on this point, but we choose to -- * put the candidate pair in the triggered check list even -- * when the agent did not switch its role. The reason for this -- * interpretation is that the reception of the stun reply, even -- * an error reply, is a good sign that this pair will be -- * valid, if we retry the check after the role of both peers -- * has been fixed. -- */ -+ if (ok_pair->nominated == TRUE) { -+ priv_update_selected_pair (agent, component, ok_pair); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a nominated pair"); -+ -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } - -- if (p->stun_message.buffer != NULL) { -+ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -+ states" and 8.1.2 "Updating States", ID-19) */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { - guint64 tie; - gboolean controlled_mode; - -- controlled_mode = (stun_message_find64 (&p->stun_message, -+ /* case: role conflict error, need to restart with new role */ -+ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -+ -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ -+ controlled_mode = (stun_message_find64 (&stun->message, - STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == - STUN_MESSAGE_RETURN_SUCCESS); - - priv_check_for_role_conflict (agent, controlled_mode); -- -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+ priv_remove_stun_transaction (p, stun, component); - priv_add_pair_to_triggered_check_queue (agent, p); -+ } else { -+ /* case: STUN error, the check STUN context was freed */ -+ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -+ candidate_check_pair_fail (stream, agent, p); - } -- } else { -- /* case: STUN error, the check STUN context was freed */ -- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+ return TRUE; - } -- return TRUE; - } - - return FALSE; -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 909d469..e16dc67 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -71,6 +71,15 @@ typedef enum - } NiceCheckState; - - typedef struct _CandidateCheckPair CandidateCheckPair; -+typedef struct _StunTransaction StunTransaction; -+ -+struct _StunTransaction -+{ -+ GTimeVal next_tick; /* next tick timestamp */ -+ StunTimer timer; -+ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; -+ StunMessage message; -+}; - - struct _CandidateCheckPair - { -@@ -86,16 +95,12 @@ struct _CandidateCheckPair - gboolean valid; - gboolean use_candidate_on_next_check; - gboolean mark_nominated_on_response_arrival; -- gboolean recheck_on_timeout; -- gboolean retransmit_on_timeout; -- struct _CandidateCheckPair *discovered_pair; -- struct _CandidateCheckPair *succeeded_pair; -+ gboolean retransmit; /* if the first stun request must be retransmitted */ -+ CandidateCheckPair *discovered_pair; -+ CandidateCheckPair *succeeded_pair; - guint64 priority; - guint32 prflx_priority; -- GTimeVal next_tick; /* next tick timestamp */ -- StunTimer timer; -- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; -- StunMessage stun_message; -+ GSList *stun_transactions; /* a list of ongoing stun requests */ - }; - - int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); --- -2.13.6 - - -From 6fe64fdbc53ab87dffd79972f492665cff14c0a0 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 27 Jun 2017 11:01:14 +0200 -Subject: [PATCH 59/70] conncheck: update the state of triggered checks pairs - -With this patch, we fix an ambiguity of some parts of the spec, when -the document refers to in-progress pairs, that also concern pairs in -the triggered checks list. - -The first cast is in section 7.1.2.5, "Updating the Nominated Flag", -when the in-progress pair will be nominated on response arrival. This is -handled in function priv_mark_pair_nominated(), when a pair is put to -the triggered check list in reaction to a matching inbound stun request. -Such a pair in priv_mark_pair_nominated() will _always_ be in the -triggered check list, from the previously called function -priv_schedule_triggered_check(). - -The second case is in section 8.1.2, "Updating State" when an in-progress -pair stops its retransmission when another pair of higher priority is -already nominated. This is handled by function priv_prune_pending_checks(). - -Until now, pairs enqueued in the triggered check list move transiently -to state waiting, according to 7.2.1.4. But this state causes wrong -decisions in the two previous cases, because such pairs should in fact -rather be considered "like in-progress", to avoid discarding them -inadvertantly. - -This patch update the state of the triggered check list -pairs to in-progress. It allows to remove exception handling cited -above: the code is a bit more simple, and allows some refactoring -in priv_mark_pair_nominated() between RFC and compatibility modes. - -Differential Revision:
https://phabricator.freedesktop.org/D1762
---- - agent/conncheck.c | 96 +++++++++++++++---------------------------------------- - 1 file changed, 25 insertions(+), 71 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 2a85738..628c708 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -244,16 +244,6 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - } - } - --/* Verify if the pair is in the triggered checks list -- */ -- --static gboolean --priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) --{ -- g_assert (pair); -- return (g_slist_find (agent->triggered_check_queue, pair) != NULL); --} -- - /* Add the pair to the triggered checks list, if not already present - */ - static void -@@ -261,8 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa - { - g_assert (pair); - -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ pair->state = NICE_CHECK_IN_PROGRESS; -+ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); - if (agent->triggered_check_queue == NULL || - g_slist_find (agent->triggered_check_queue, pair) == NULL) - agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -@@ -841,12 +831,6 @@ timer_return_timeout: - */ - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) { -- /* remove the pair from the triggered check list if needed. This -- * may happen with the cancelled pair, that's just been added -- * in state waiting to the triggered check list above in the -- * same function. -- */ -- priv_remove_pair_from_triggered_check_queue (agent, pair); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair in Waiting state"); - priv_conn_check_initiate (agent, pair); -@@ -1109,7 +1093,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - if (pair) { - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair from triggered check list"); -- priv_conn_check_initiate (agent, pair); -+ conn_check_send (agent, pair); - return TRUE; - } - -@@ -2098,8 +2082,7 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; -- if (pair->local == localcand && pair->remote == remotecand && -- NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ if (pair->local == localcand && pair->remote == remotecand) { - /* ICE, 7.2.1.5. Updating the Nominated Flag */ - /* note: TCP candidates typically produce peer reflexive - * candidate, generating a "discovered" pair that can be -@@ -2111,44 +2094,27 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - g_assert (pair->state == NICE_CHECK_DISCOVERED); - } - -- if (pair->valid) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated", -- agent, pair, pair->foundation); -- pair->nominated = TRUE; -- priv_update_selected_pair (agent, component, pair); -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state == NICE_COMPONENT_STATE_FAILED) -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -- if (component->state == NICE_COMPONENT_STATE_CONNECTING) -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- priv_update_check_list_state_for_ready (agent, stream, component); -- } else if (pair->state == NICE_CHECK_IN_PROGRESS) { -+ /* If the state of this pair is In-Progress, [...] the resulting -+ * valid pair has its nominated flag set when the response -+ * arrives. -+ */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && -+ pair->state == NICE_CHECK_IN_PROGRESS) { - pair->mark_nominated_on_response_arrival = TRUE; - nice_debug ("Agent %p : pair %p (%s) is in-progress, " - "will be nominated on response receipt.", - agent, pair, pair->foundation); - } -- /* note: this case is not covered by the ICE spec, 7.2.1.5, -- * "Updating the Nominated Flag", but a pair in waiting state -- * deserves the same treatment than a pair in-progress. A pair -- * can be in waiting state as the result of being enqueued in -- * the triggered check list for example. -- */ -- if (pair->state == NICE_CHECK_WAITING) { -- pair->mark_nominated_on_response_arrival = TRUE; -- nice_debug ("Agent %p : pair %p (%s) is waiting, " -- "will be nominated on response receipt.", -+ -+ if (pair->valid || -+ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated", - agent, pair, pair->foundation); -+ pair->nominated = TRUE; - } -- } else if (pair->local == localcand && pair->remote == remotecand) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); -- pair->nominated = TRUE; -+ - if (pair->valid) { -- priv_update_selected_pair (agent, component, pair); -+ priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ - if (component->state == NICE_COMPONENT_STATE_FAILED) - agent_signal_component_state_change (agent, -@@ -2159,7 +2125,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); - } -- priv_update_check_list_state_for_ready (agent, stream, component); -+ -+ if (pair->nominated) -+ priv_update_check_list_state_for_ready (agent, stream, component); - } - } - } -@@ -2731,12 +2699,12 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - nice_address_to_string (&pair->local->addr, tmpbuf1); - nice_address_to_string (&pair->remote->addr, tmpbuf2); - nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " -- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -+ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " - "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr), - pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, -- pair->foundation, pair->component_id, -+ pair, pair->component_id, - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -@@ -2877,35 +2845,21 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu - "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ -- /* note: this case is not covered by the ICE spec, 8.1.2 -- * "Updating States", but a pair in waiting state which will be -- * nominated on response receipt should be treated the same way -- * that an in-progress pair. A pair in waiting state and in -- * the triggered check list should also be treated like an in-progress -- * pair. -- */ - i = stream->conncheck_list; - while (i) { - CandidateCheckPair *p = i->data; - GSList *next = i->next; - - if (p->component_id == component_id) { -- gboolean like_in_progress = -- p->mark_nominated_on_response_arrival || -- priv_is_pair_in_triggered_check_queue (agent, p); -- -- if (p->state == NICE_CHECK_FROZEN || -- (p->state == NICE_CHECK_WAITING && !like_in_progress)) { -+ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) { - nice_debug ("Agent %p : pair %p removed.", agent, p); - conn_check_free_item (p); - stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- else if (p->state == NICE_CHECK_IN_PROGRESS || -- (p->state == NICE_CHECK_WAITING && like_in_progress)) { -- if (highest_nominated_priority != 0 && -- p->priority < highest_nominated_priority) { -+ else if (p->state == NICE_CHECK_IN_PROGRESS) { -+ if (p->priority < highest_nominated_priority) { - p->retransmit = FALSE; - nice_debug ("Agent %p : pair %p will not be retransmitted.", - agent, p); --- -2.13.6 - - -From 72dd26a3368d3506fe8faca7067a02784fb5f0fd Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Wed, 28 Jun 2017 12:06:48 +0200 -Subject: [PATCH 60/70] conncheck: forgot to put a pair in triggered check list - -When a new pair is created from an unknown remote candidate, it -should be enqueue for a triggered check, to allow it to be marked -as nominated on response arrival in priv_mark_pair_nominated(). -Creating it in waiting state is not sufficient since the update -in priv_mark_pair_nominated() from previous commits. - -Differential Revision:
https://phabricator.freedesktop.org/D1763
---- - agent/conncheck.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 628c708..0e3ce88 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2893,11 +2893,12 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - { - GSList *i; - NiceCandidate *local = NULL; -+ CandidateCheckPair *p; - - g_assert (remote_cand != NULL); - - for (i = stream->conncheck_list; i ; i = i->next) { -- CandidateCheckPair *p = i->data; -+ p = i->data; - if (p->component_id == component->id && - p->remote == remote_cand && - ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && -@@ -2986,8 +2987,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - - if (i) { - nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); -- priv_add_new_check_pair (agent, stream->id, component, -+ p = priv_add_new_check_pair (agent, stream->id, component, - local, remote_cand, NICE_CHECK_WAITING); -+ priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } - else { --- -2.13.6 - - -From 14102d44449d2eb4148588ce54fa897fa13b87ad Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sun, 2 Jul 2017 16:02:09 +0200 -Subject: [PATCH 61/70] conncheck: change state before updating nominated pairs - -When a pair is nominated while in state failed, we first move -back to state connecting, then we update the selected pair, and -finally we move to state connected. ---- - agent/conncheck.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 0e3ce88..e584c0e 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -2114,11 +2114,11 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - } - - if (pair->valid) { -- priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ - if (component->state == NICE_COMPONENT_STATE_FAILED) - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ priv_update_selected_pair (agent, component, pair); - if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ --- -2.13.6 - - -From 1a1803a45778000720c93d91060cedeb19124a27 Mon Sep 17 00:00:00 2001 -From: Philip Withnall <withnall(a)endlessm.com> -Date: Tue, 12 Sep 2017 13:23:31 +0100 -Subject: [PATCH 62/70] tests: Fix copyright dates in test-gstreamer.c - -This code is not 1000 years old. - -Signed-off-by: Philip Withnall <withnall(a)endlessm.com> ---- - tests/test-gstreamer.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c -index 74d7133..a0be68e 100644 ---- a/tests/test-gstreamer.c -+++ b/tests/test-gstreamer.c -@@ -1,7 +1,7 @@ - /* - * This file is part of the Nice GLib ICE library. - * -- * (C) 1015 Kurento. -+ * (C) 2015 Kurento. - * Contact: Jose Antonio Santos Cadenas - * - * The contents of this file are subject to the Mozilla Public License Version --- -2.13.6 - - -From 4c4834ab634f735145c8f758a22cbdd9cab79bac Mon Sep 17 00:00:00 2001 -From: Philip Withnall <withnall(a)endlessm.com> -Date: Tue, 12 Sep 2017 13:23:53 +0100 -Subject: [PATCH 63/70] tests: Fix agent.h header inclusion in test-gstreamer.c - -Spotted by Lukas Gradl on the mailing list. - -Signed-off-by: Philip Withnall <withnall(a)endlessm.com> ---- - tests/test-gstreamer.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c -index a0be68e..f060efc 100644 ---- a/tests/test-gstreamer.c -+++ b/tests/test-gstreamer.c -@@ -34,7 +34,7 @@ - */ - - #include <gst/check/gstcheck.h> --#include <nice/agent.h> -+#include "agent.h" - - #define RTP_HEADER_SIZE 12 - #define RTP_PAYLOAD_SIZE 1024 --- -2.13.6 - - -From fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 Mon Sep 17 00:00:00 2001 -From: Philip Withnall <withnall(a)endlessm.com> -Date: Thu, 3 Aug 2017 12:20:32 +0100 -Subject: [PATCH 64/70] stun: Fix FD leak in test/utility code -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -https://phabricator.freedesktop.org/T7798 - -Signed-off-by: Philip Withnall <withnall(a)endlessm.com> -Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> -Differential Revision:
https://phabricator.freedesktop.org/D1819
---- - stun/usages/bind.c | 29 ++++++++++++++++++++++------- - 1 file changed, 22 insertions(+), 7 deletions(-) - -diff --git a/stun/usages/bind.c b/stun/usages/bind.c -index d56790f..ee600a0 100644 ---- a/stun/usages/bind.c -+++ b/stun/usages/bind.c -@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen); - if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { - stun_debug ("STUN transaction failed: couldn't create transport."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) { - stun_debug ("STUN transaction failed: couldn't send request."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, -@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - switch (stun_timer_refresh (&timer)) { - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - stun_debug ("STUN transaction failed: time out."); -- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! -+ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! -+ goto done; - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: - stun_debug ("STUN transaction retransmitted (timeout %dms).", - stun_timer_remainder (&timer)); - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) { - stun_debug ("STUN transaction failed: couldn't resend request."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - continue; - case STUN_USAGE_TIMER_RETURN_SUCCESS: -@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - - valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL); - if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE) -- return STUN_USAGE_BIND_RETURN_ERROR; -+ { -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; -+ } - - if (valid != STUN_VALIDATION_SUCCESS) { - ret = STUN_USAGE_TRANS_RETURN_RETRY; -@@ -554,12 +561,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - (struct sockaddr *) &alternate_server, alternate_server_len); - - if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) -- return STUN_USAGE_BIND_RETURN_ERROR; -+ { -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; -+ } - - stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -@@ -573,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - } - while (ret == STUN_USAGE_TRANS_RETURN_RETRY); - -+done: -+ if (trans.fd != -1) -+ stun_trans_deinit (&trans); -+ - return bind_ret; - } --- -2.13.6 - - -From 02216a6766caccb652387d5ee19686149eedbc93 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue, 21 Nov 2017 15:12:45 +0100 -Subject: [PATCH 65/70] agent: prevent external role change while conncheck is - running - -With this patch, we stash the controlling mode property change, and -apply it safely, when it won't interfere with an ongoing conncheck -running. According to RFC5245, sect 5.2. "Determining Role", the role -is determined for a session, and persists unless an ICE is restarted. - -Differential Revision:
https://phabricator.freedesktop.org/D1887
---- - agent/agent-priv.h | 4 +++- - agent/agent.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- - tests/test-restart.c | 15 ++++++++++++++ - 3 files changed, 74 insertions(+), 3 deletions(-) - -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 714ecff..7269be0 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -146,7 +146,7 @@ struct _NiceAgent - NiceProxyType proxy_type; /* property: Proxy type */ - gchar *proxy_username; /* property: Proxy username */ - gchar *proxy_password; /* property: Proxy password */ -- gboolean controlling_mode; /* property: controlling-mode */ -+ gboolean saved_controlling_mode;/* property: controlling-mode */ - guint timer_ta; /* property: timer Ta */ - guint max_conn_checks; /* property: max connectivity checks */ - gboolean force_relay; /* property: force relay */ -@@ -190,6 +190,8 @@ struct _NiceAgent - gboolean use_ice_tcp; - - guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ -+ gboolean controlling_mode; /* controlling mode used by the -+ conncheck */ - /* XXX: add pointer to internal data struct for ABI-safe extensions */ - }; - -diff --git a/agent/agent.c b/agent/agent.c -index a4dcc0c..0773c53 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -405,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass) - 1, /* not a construct property, ignored */ - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:controlling-mode: -+ * -+ * Whether the agent has the controlling role. This property should -+ * be modified before gathering candidates, any modification occuring -+ * later will be hold until ICE is restarted. -+ */ - g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE, - g_param_spec_boolean ( - "controlling-mode", -@@ -1107,6 +1114,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) - } - - static void -+priv_update_controlling_mode (NiceAgent *agent, gboolean value) -+{ -+ gboolean update_controlling_mode; -+ GSList *i, *j; -+ -+ agent->saved_controlling_mode = value; -+ /* It is safe to update the agent controlling mode when all -+ * components are still in state disconnected. When we leave -+ * this state, the role must stay under the control of the -+ * conncheck algorithm exclusively, until the conncheck is -+ * eventually restarted. See RFC5245, sect 5.2. Determining Role -+ */ -+ if (agent->controlling_mode != agent->saved_controlling_mode) { -+ update_controlling_mode = TRUE; -+ for (i = agent->streams; -+ i && update_controlling_mode; i = i->next) { -+ NiceStream *stream = i->data; -+ for (j = stream->components; -+ j && update_controlling_mode; j = j->next) { -+ NiceComponent *component = j->data; -+ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED) -+ update_controlling_mode = FALSE; -+ } -+ } -+ if (update_controlling_mode) { -+ agent->controlling_mode = agent->saved_controlling_mode; -+ nice_debug ("Agent %p : Property set, changing role to \"%s\".", -+ agent, agent->controlling_mode ? "controlling" : "controlled"); -+ } else { -+ nice_debug ("Agent %p : Property set, role switch requested " -+ "but conncheck already started.", agent); -+ nice_debug ("Agent %p : Property set, staying with role \"%s\" " -+ "until restart.", agent, -+ agent->controlling_mode ? "controlling" : "controlled"); -+ } -+ } else -+ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent, -+ agent->controlling_mode ? "controlling" : "controlled"); -+} -+ -+static void - nice_agent_init (NiceAgent *agent) - { - agent->next_candidate_id = 1; -@@ -1115,6 +1163,7 @@ nice_agent_init (NiceAgent *agent) - /* set defaults; not construct params, so set here */ - agent->stun_server_port = DEFAULT_STUN_PORT; - agent->controlling_mode = TRUE; -+ agent->saved_controlling_mode = TRUE; - agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; - agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; - -@@ -1213,7 +1262,7 @@ nice_agent_get_property ( - break; - - case PROP_CONTROLLING_MODE: -- g_value_set_boolean (value, agent->controlling_mode); -+ g_value_set_boolean (value, agent->saved_controlling_mode); - break; - - case PROP_FULL_MODE: -@@ -1422,7 +1471,7 @@ nice_agent_set_property ( - break; - - case PROP_CONTROLLING_MODE: -- agent->controlling_mode = g_value_get_boolean (value); -+ priv_update_controlling_mode (agent, g_value_get_boolean (value)); - break; - - case PROP_FULL_MODE: -@@ -4930,6 +4979,11 @@ nice_agent_restart ( - /* step: regenerate tie-breaker value */ - priv_generate_tie_breaker (agent); - -+ /* step: reset controlling mode from the property value */ -+ agent->controlling_mode = agent->saved_controlling_mode; -+ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".", -+ agent, agent->controlling_mode ? "controlling" : "controlled"); -+ - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; - -diff --git a/tests/test-restart.c b/tests/test-restart.c -index c2cbe9a..afc51b6 100644 ---- a/tests/test-restart.c -+++ b/tests/test-restart.c -@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands); - cdes.addr = laddr_rtcp; - nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands); -+ /* This role switch request will be effective after restart. We test -+ * here that the role cannot be externally modified after conncheck -+ * has started. */ -+ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL); -+ g_assert (ragent->controlling_mode == FALSE); - - g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed..."); - -@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - global_ragent_read = 0; - g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16); - -+ /* Both agent have a distinct role at the end of the conncheck */ -+ g_assert (lagent->controlling_mode == TRUE); -+ g_assert (ragent->controlling_mode == FALSE); - /* step: restart agents, exchange updated credentials */ - tie_breaker = ragent->tie_breaker; - nice_agent_restart (ragent); - g_assert (tie_breaker != ragent->tie_breaker); -+ /* This role switch of ragent should be done now, and both agents -+ * have now the same role, which should generate a role conflict -+ * resolution situation */ -+ g_assert (lagent->controlling_mode == TRUE); -+ g_assert (ragent->controlling_mode == TRUE); - nice_agent_restart (lagent); - { - gchar *ufrag = NULL, *password = NULL; -@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - /* note: verify binding requests were resent after restart */ - g_assert (global_lagent_ibr_received == TRUE); - g_assert (global_ragent_ibr_received == TRUE); -+ /* note: verify that a role switch occured for one of the agents */ -+ g_assert (ragent->controlling_mode != lagent->controlling_mode); - - g_debug ("test-restart: Ran mainloop, removing streams..."); - --- -2.13.6 - - -From c63349894b3fe974494453a883dfb5ad05df5a46 Mon Sep 17 00:00:00 2001 -From: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu, 23 Nov 2017 18:31:31 +0100 -Subject: [PATCH 66/70] Makefile: really enable debug for tests - -Differential Revision:
https://phabricator.freedesktop.org/D1888
---- - tests/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/Makefile.am b/tests/Makefile.am -index b623764..e94822d 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" - - AM_TESTS_ENVIRONMENT = \ - G_MESSAGES_DEBUG=all \ -- NICE_DEBUG=all; -+ NICE_DEBUG=all - - COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) - --- -2.13.6 - - -From 17f30e4465efe9533799b02d6f95feeaf0f2748c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miguel=20Par=C3=ADs?= <mparisdiaz(a)gmail.com> -Date: Wed, 8 Nov 2017 16:26:47 +0000 -Subject: [PATCH 67/70] conncheck: do not require that all streams have a - connection check list - -One or more streams might not have any connection check list if the -number of streams differs from the peer agent. -Differential Revision:
https://phabricator.freedesktop.org/D1880
---- - agent/conncheck.c | 23 ---- - tests/Makefile.am | 3 + - tests/test-different-number-streams.c | 208 ++++++++++++++++++++++++++++++++++ - 3 files changed, 211 insertions(+), 23 deletions(-) - create mode 100644 tests/test-different-number-streams.c - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index e584c0e..beb43c3 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -402,23 +402,6 @@ priv_conn_check_find_next_frozen (GSList *conn_check_list) - } - - /* -- * Returns the number of check lists of the agent -- */ --static guint --priv_number_of_check_lists (NiceAgent *agent) --{ -- guint n = 0; -- GSList *i; -- -- for (i = agent->streams; i ; i = i->next) { -- NiceStream *stream = i->data; -- if (stream->conncheck_list != NULL) -- n++; -- } -- return n; --} -- --/* - * Returns the number of active check lists of the agent - */ - static guint -@@ -1060,12 +1043,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - gboolean keep_timer_going = FALSE; - GSList *i, *j; - -- /* the conncheck really starts when we have built -- * a connection check list for each stream -- */ -- if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) -- return TRUE; -- - /* configure the initial state of the check lists of the agent - * as described in ICE spec, 5.7.4 - * -diff --git a/tests/Makefile.am b/tests/Makefile.am -index e94822d..30d6f8e 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -46,6 +46,7 @@ check_PROGRAMS = \ - test-socket-is-based-on \ - test-priority \ - test-fullmode \ -+ test-different-number-streams \ - test-restart \ - test-fallback \ - test-thread \ -@@ -114,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD) - - test_fullmode_LDADD = $(COMMON_LDADD) - -+test_different_number_streams_LDADD = $(COMMON_LDADD) -+ - test_restart_LDADD = $(COMMON_LDADD) - - test_fallback_LDADD = $(COMMON_LDADD) -diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c -new file mode 100644 -index 0000000..7fd4763 ---- /dev/null -+++ b/tests/test-different-number-streams.c -@@ -0,0 +1,208 @@ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+#define ADD_2_STREAMS TRUE -+#define USE_SECOND_STREAM TRUE -+ -+static GMainLoop *global_mainloop = NULL; -+ -+static guint global_components_ready = 0; -+static guint global_components_ready_exit = 0; -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id); -+} -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)", -+ agent, stream_id, component_id, nice_component_state_to_string (state)); -+ -+ if (state == NICE_COMPONENT_STATE_READY) { -+ global_components_ready++; -+ } -+ -+ /* signal status via a global variable */ -+ if (global_components_ready == global_components_ready_exit) { -+ g_debug ("Components ready/failed achieved. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id); -+} -+ -+int main (void) -+{ -+ NiceAgent *lagent, *ragent; -+ guint timer_id; -+ guint ls_id, rs_id_1, rs_id_2; -+ gchar *lufrag = NULL, *lpassword = NULL; -+ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL; -+ -+#ifdef G_OS_WIN32 -+ WSADATA w; -+ -+ WSAStartup(0x0202, &w); -+#endif -+ -+ global_mainloop = g_main_loop_new (NULL, FALSE); -+ -+ /* step: create the agents L and R */ -+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_GOOGLE); -+ g_debug ("lagent: %p", lagent); -+ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent"); -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), NULL); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), NULL); -+ -+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_GOOGLE); -+ g_debug ("ragent: %p", ragent); -+ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent"); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), NULL); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), NULL); -+ -+ /* step: add a timer to catch state changes triggered by signals */ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 2); -+ g_assert (ls_id > 0); -+ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword); -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ -+ global_components_ready_exit = 4; -+ -+ if (ADD_2_STREAMS) { -+ rs_id_1 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_1 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); -+ -+ rs_id_2 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_2 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2); -+ -+ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword); -+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2); -+ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); -+ -+ if (USE_SECOND_STREAM) { -+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP); -+ } else { -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); -+ } -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ } else { -+ rs_id_1 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_1 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); -+ -+ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword); -+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1); -+ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); -+ } -+ -+ /* step: run the mainloop until connectivity checks succeed -+ * (see timer_cb() above) */ -+ g_main_loop_run (global_mainloop); -+ -+ g_free (lufrag); -+ g_free (lpassword); -+ g_free (rufrag1); -+ g_free (rpassword1); -+ g_free (rufrag2); -+ g_free (rpassword2); -+ g_object_unref (lagent); -+ g_object_unref (ragent); -+ -+ g_main_loop_unref (global_mainloop); -+ global_mainloop = NULL; -+ -+ g_source_remove (timer_id); -+ -+#ifdef G_OS_WIN32 -+ WSACleanup(); -+#endif -+ -+ return 0; -+} --- -2.13.6 - - -From 59fcf95d505c3995f858b826d10cd48321ed383e Mon Sep 17 00:00:00 2001 -From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Mon, 27 Nov 2017 17:07:02 -0500 -Subject: [PATCH 68/70] turn: Add support for ALTERNATE_SERVER in OC2007 - Compatibility - -The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in -allocation responses, and if they are not handled, we end up using the -main turn server to send allocation requests that then get sent to the -alternate server which will return the XOR_MAPPED_ADDRESS containing -the IP address of the turn server that proxied the message instead of -our own actual external IP. ---- - agent/conncheck.c | 14 ++++++++++++++ - stun/usages/turn.c | 11 +++++++++++ - stun/usages/turn.h | 4 ++++ - 3 files changed, 29 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index beb43c3..229c8b1 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3764,6 +3764,20 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - recv_realm = (uint8_t *) stun_message_find (resp, - STUN_ATTRIBUTE_REALM, &recv_realm_len); - -+ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -+ alternatelen != sizeof(alternate)) { -+ NiceAddress alternate_addr; -+ -+ nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); -+ -+ if (!nice_address_equal (&alternate_addr, &d->server)) { -+ nice_address_set_from_sockaddr (&d->server, &alternate.addr); -+ nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ -+ d->pending = FALSE; -+ } -+ } - /* check for unauthorized error response */ - if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || - agent->compatibility == NICE_COMPATIBILITY_OC2007 || -diff --git a/stun/usages/turn.c b/stun/usages/turn.c -index 3b94959..ec12642 100644 ---- a/stun/usages/turn.c -+++ b/stun/usages/turn.c -@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, - stun_debug (" STUN error message received (code: %d)", code); - - /* ALTERNATE-SERVER mechanism */ -+ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 && -+ alternate_server && alternate_server_len && -+ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER, -+ alternate_server, -+ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) { -+ stun_debug ("Found alternate server"); -+ /* The ALTERNATE_SERVER will always be returned by the MS turn server. -+ * We need to check if the ALTERNATE_SERVER is the same as the current -+ * server to decide whether we need to switch servers or not. -+ */ -+ } - if ((code / 100) == 3) { - if (alternate_server && alternate_server_len) { - if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, -diff --git a/stun/usages/turn.h b/stun/usages/turn.h -index 7a2d4e6..83fa00a 100644 ---- a/stun/usages/turn.h -+++ b/stun/usages/turn.h -@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, - * Allocate request, in case the currently used TURN server is requesting the use - * of an alternate server. This argument will only be filled if the return value - * of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER -+ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the -+ * @alternate_server could be filled at any time, and should only be considered -+ * if the request was sent to a different server than the address returned -+ * in the @alternate_server field - * @alternate_server_len: The length of @alternate_server - * @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us - * @lifetime: A pointer to fill with the lifetime of the allocation --- -2.13.6 - - -From 4172d48852ecd1c86cc7bd4665b23697603d1eed Mon Sep 17 00:00:00 2001 -From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue, 28 Nov 2017 15:14:11 -0500 -Subject: [PATCH 69/70] discovery: Increase discovery_unsched_items whenever we - restart a check - -The discovery_unsched_items is decremented every time a DiscoveryCandidate -goes from non-pending to pending. So if we restart a check by setting -pending to FALSE, we should re-increase the discovery_unsched_items. ---- - agent/conncheck.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 229c8b1..5b08311 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3507,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa - d->server = niceaddr; - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { - /* case: successful binding discovery, create a new local candidate */ - -@@ -3648,6 +3649,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || - res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { - /* case: successful allocate, create a new local candidate */ -@@ -3776,6 +3778,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } - } - /* check for unauthorized error response */ -@@ -3798,6 +3801,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - d->stun_resp_msg.buffer = d->stun_resp_buffer; - d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer); - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else { - /* case: a real unauthorized error */ - d->stun_message.buffer = NULL; --- -2.13.6 - - -From fb2f1f77a31baa91968fc81c205f980b6913f403 Mon Sep 17 00:00:00 2001 -From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue, 28 Nov 2017 16:05:18 -0500 -Subject: [PATCH 70/70] conncheck: handle alternate-server for turn relays - differently - -If a relay gives us an alternate-server, we need to cancel and reset -every candidate discovery attempt that uses the same server, to avoid -ending up with one component on one server and the other component on -another server (causing relay candidates with mismatched foundations). ---- - agent/conncheck.c | 56 ++++++++++++++++++++++++++++++++++++++++++------------- - 1 file changed, 43 insertions(+), 13 deletions(-) - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 5b08311..c8a4edf 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -3592,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand - return cand; - } - -+static void priv_handle_turn_alternate_server (NiceAgent *agent, -+ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate) -+{ -+ /* We need to cancel and reset all candidate discovery turn for the same -+ stream and type if there is an alternate server. Otherwise, we might end up -+ with two relay components on different servers, creating candidates with -+ unique foundations that only contain one component. -+ */ -+ GSList *i; -+ -+ for (i = agent->discovery_list; i; i = i->next) { -+ CandidateDiscovery *d = i->data; -+ -+ if (!d->done && -+ d->type == disco->type && -+ d->stream == disco->stream && -+ d->turn->type == disco->turn->type && -+ nice_address_equal (&d->server, &server)) { -+ gchar ip[INET6_ADDRSTRLEN]; -+ // Cancel the pending request to avoid a race condition with another -+ // component responding with another altenrate-server -+ d->stun_message.buffer = NULL; -+ d->stun_message.buffer_len = 0; -+ -+ nice_address_to_string (&server, ip); -+ nice_debug ("Agent %p : Cancelling and setting alternate server %s for " -+ "CandidateDiscovery %p", agent, ip, d); -+ d->server = alternate; -+ d->turn->server = alternate; -+ d->pending = FALSE; -+ agent->discovery_unsched_items++; -+ } -+ } -+} -+ - /* - * Tries to match STUN reply in 'buf' to an existing STUN discovery - * transaction. If found, a reply is sent. -@@ -3644,12 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - agent, d, (int)res); - - if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) { -- /* handle alternate server */ -- nice_address_set_from_sockaddr (&d->server, &alternate.addr); -- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ NiceAddress addr; - -- d->pending = FALSE; -- agent->discovery_unsched_items++; -+ /* handle alternate server */ -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); - } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || - res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { - /* case: successful allocate, create a new local candidate */ -@@ -3769,16 +3803,12 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && - alternatelen != sizeof(alternate)) { -- NiceAddress alternate_addr; -- -- nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); -+ NiceAddress addr; - -- if (!nice_address_equal (&alternate_addr, &d->server)) { -- nice_address_set_from_sockaddr (&d->server, &alternate.addr); -- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); - -- d->pending = FALSE; -- agent->discovery_unsched_items++; -+ if (!nice_address_equal (&addr, &d->server)) { -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); - } - } - /* check for unauthorized error response */ --- -2.13.6 - diff --git a/libnice-0.1.14-85-g34d6044.patch b/libnice-0.1.14-85-g34d6044.patch new file mode 100644 index 0000000..541d010 --- /dev/null +++ b/libnice-0.1.14-85-g34d6044.patch @@ -0,0 +1,11963 @@ +From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Mon, 3 Apr 2017 14:30:10 -0400 +Subject: [PATCH 01/70] Version 0.1.14.1 + +--- + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 5fabdb4..b39bfe3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -5,8 +5,8 @@ dnl Always compile with -Wall; if --enable-compile-warnings=error is passed, + dnl also use -Werror. git and pre-releases default to -Werror + + dnl use a three digit version number for releases, and four for cvs/prerelease +-AC_INIT([libnice],[0.1.14]) +-LIBNICE_RELEASE="yes" ++AC_INIT([libnice],[0.1.14.1]) ++LIBNICE_RELEASE="no" + + AC_CANONICAL_TARGET + +-- +2.13.6 + + +From 59ce41dfb837adf4222b25490cde2e394384ad15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= <mparisdiaz(a)gmail.com> +Date: Fri, 31 Mar 2017 20:20:38 -0400 +Subject: [PATCH 02/70] conncheck: consider answer received when remote + credentials are set +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Consider that the answer is received when remote credentials +are set instead of when a remote candidate is set, +which could not happen or could cause more delay for the +connection establishment. + +Ported to git master by Olivier Crête + +Differential Revision:
https://phabricator.freedesktop.org/D1704
+--- + agent/agent.c | 4 +- + agent/conncheck.c | 225 +++++++++++++++++++++++++++--------------------------- + agent/conncheck.h | 2 +- + 3 files changed, 117 insertions(+), 114 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 555fd16..4d9381c 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3240,6 +3240,8 @@ nice_agent_set_remote_credentials ( + g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG); + g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD); + ++ conn_check_remote_credentials_set(agent, stream); ++ + ret = TRUE; + goto done; + } +@@ -3342,8 +3344,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, + } + } + +- conn_check_remote_candidates_set(agent, stream, component); +- + if (added > 0) { + conn_check_schedule_next (agent); + } +diff --git a/agent/conncheck.c b/agent/conncheck.c +index dda2f2f..2abbc5e 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1243,124 +1243,124 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list) + + /* + * Handle any processing steps for connectivity checks after +- * remote candidates have been set. This function handles ++ * remote credentials have been set. This function handles + * the special case where answerer has sent us connectivity +- * checks before the answer (containing candidate information), ++ * checks before the answer (containing credentials information), + * reaches us. The special case is documented in sect 7.2 + * if ICE spec (ID-19). + */ +-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) ++void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + { +- GSList *j, *k, *l, *m, *n; ++ GSList *j, *k, *l, *m, *n, *o; + + for (j = stream->conncheck_list; j ; j = j->next) { + CandidateCheckPair *pair = j->data; +- if (pair->component_id == component->id) { +- gboolean match = FALSE; +- +- /* performn delayed processing of spec steps section 7.2.1.4, +- and section 7.2.1.5 */ +- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); +- +- for (k = component->incoming_checks; k; k = k->next) { +- IncomingCheck *icheck = k->data; +- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to +- * be handled separately */ +- for (l = component->remote_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&icheck->from, &cand->addr)) { +- match = TRUE; +- break; +- } +- } +- if (match != TRUE) { +- /* note: we have gotten an incoming connectivity check from +- * an address that is not a known remote candidate */ +- +- NiceCandidate *local_candidate = NULL; +- NiceCandidate *remote_candidate = NULL; +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || +- agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- /* We need to find which local candidate was used */ +- uint8_t uname[NICE_STREAM_MAX_UNAME]; +- guint uname_len; +- +- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " +- "stored pending check", agent); +- +- for (m = component->remote_candidates; +- m != NULL && remote_candidate == NULL; m = m->next) { +- for (n = component->local_candidates; n; n = n->next) { +- NiceCandidate *rcand = m->data; +- NiceCandidate *lcand = n->data; +- +- uname_len = priv_create_username (agent, stream, +- component->id, rcand, lcand, +- uname, sizeof (uname), TRUE); +- +- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", +- icheck->username_len, uname_len, +- icheck->username && uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0); +- stun_debug_bytes (" first username: ", +- icheck->username, +- icheck->username? icheck->username_len : 0); +- stun_debug_bytes (" second username: ", uname, uname_len); +- +- if (icheck->username && +- uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0) { +- local_candidate = lcand; +- remote_candidate = rcand; +- break; +- } +- } +- } +- } else { +- for (l = component->local_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { +- local_candidate = cand; ++ NiceComponent *component = ++ nice_stream_find_component_by_id (stream, pair->component_id); ++ gboolean match = FALSE; ++ ++ /* performn delayed processing of spec steps section 7.2.1.4, ++ and section 7.2.1.5 */ ++ priv_preprocess_conn_check_pending_data (agent, stream, component, pair); ++ ++ for (k = component->incoming_checks; k; k = k->next) { ++ IncomingCheck *icheck = k->data; ++ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to ++ * be handled separately */ ++ for (l = component->remote_candidates; l; l = l->next) { ++ NiceCandidate *cand = l->data; ++ if (nice_address_equal (&icheck->from, &cand->addr)) { ++ match = TRUE; ++ break; ++ } ++ } ++ if (match != TRUE) { ++ /* note: we have gotten an incoming connectivity check from ++ * an address that is not a known remote candidate */ ++ ++ NiceCandidate *local_candidate = NULL; ++ NiceCandidate *remote_candidate = NULL; ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || ++ agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ /* We need to find which local candidate was used */ ++ uint8_t uname[NICE_STREAM_MAX_UNAME]; ++ guint uname_len; ++ ++ nice_debug ("Agent %p: We have a peer-reflexive candidate in a " ++ "stored pending check", agent); ++ ++ for (m = component->remote_candidates; ++ m != NULL && remote_candidate == NULL; m = m->next) { ++ for (n = component->local_candidates; n; n = n->next) { ++ NiceCandidate *rcand = m->data; ++ NiceCandidate *lcand = n->data; ++ ++ uname_len = priv_create_username (agent, stream, ++ component->id, rcand, lcand, ++ uname, sizeof (uname), TRUE); ++ ++ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", ++ icheck->username_len, uname_len, ++ icheck->username && uname_len == icheck->username_len && ++ memcmp (uname, icheck->username, icheck->username_len) == 0); ++ stun_debug_bytes (" first username: ", ++ icheck->username, ++ icheck->username? icheck->username_len : 0); ++ stun_debug_bytes (" second username: ", uname, uname_len); ++ ++ if (icheck->username && ++ uname_len == icheck->username_len && ++ memcmp (uname, icheck->username, icheck->username_len) == 0) { ++ local_candidate = lcand; ++ remote_candidate = rcand; + break; + } + } + } +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && +- local_candidate == NULL) { +- /* if we couldn't match the username, then the matching remote +- * candidate hasn't been received yet.. we must wait */ +- nice_debug ("Agent %p : Username check failed. pending check has " +- "to wait to be processed", agent); +- } else { +- NiceCandidate *candidate; +- +- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", +- agent); +- candidate = +- discovery_learn_remote_peer_reflexive_candidate (agent, +- stream, +- component, +- icheck->priority, +- &icheck->from, +- icheck->local_socket, +- local_candidate, remote_candidate); +- if (candidate) { +- if (local_candidate && +- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) +- priv_conn_check_add_for_candidate_pair_matched (agent, +- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); +- else +- conn_check_add_for_candidate (agent, stream->id, component, candidate); +- +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ } else { ++ for (l = component->local_candidates; l; l = l->next) { ++ NiceCandidate *cand = l->data; ++ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { ++ local_candidate = cand; ++ break; + } + } + } ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && ++ local_candidate == NULL) { ++ /* if we couldn't match the username, then the matching remote ++ * candidate hasn't been received yet.. we must wait */ ++ nice_debug ("Agent %p : Username check failed. pending check has " ++ "to wait to be processed", agent); ++ } else { ++ NiceCandidate *candidate; ++ ++ nice_debug ("Agent %p : Discovered peer reflexive from early i-check", ++ agent); ++ candidate = ++ discovery_learn_remote_peer_reflexive_candidate (agent, ++ stream, ++ component, ++ icheck->priority, ++ &icheck->from, ++ icheck->local_socket, ++ local_candidate, remote_candidate); ++ if (candidate) { ++ if (local_candidate && ++ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) ++ priv_conn_check_add_for_candidate_pair_matched (agent, ++ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); ++ else ++ conn_check_add_for_candidate (agent, stream->id, component, candidate); ++ ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); ++ priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ } ++ } + } + } + } +@@ -1368,9 +1368,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice + /* Once we process the pending checks, we should free them to avoid + * reprocessing them again if a dribble-mode set_remote_candidates + * is called */ +- g_slist_free_full (component->incoming_checks, +- (GDestroyNotify) incoming_check_free); +- component->incoming_checks = NULL; ++ for (o = stream->components; o; o = o->next) { ++ NiceComponent *component = o->data; ++ g_slist_free_full (component->incoming_checks, ++ (GDestroyNotify) incoming_check_free); ++ component->incoming_checks = NULL; ++ } + + stream->conncheck_list = + prune_cancelled_conn_check (stream->conncheck_list); +@@ -3628,14 +3631,14 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + if (stream->initial_binding_request_received != TRUE) + agent_signal_initial_binding_request_received (agent, stream); + +- if (component->remote_candidates && remote_candidate == NULL) { ++ if (remote_candidate == NULL) { + nice_debug ("Agent %p : No matching remote candidate for incoming check ->" + "peer-reflexive candidate.", agent); + remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( + agent, stream, component, priority, from, nicesock, + local_candidate, + remote_candidate2 ? remote_candidate2 : remote_candidate); +- if(remote_candidate) { ++ if(remote_candidate && stream->remote_ufrag != NULL) { + if (local_candidate && + local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { + CandidateCheckPair *pair; +@@ -3654,10 +3657,10 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +- if (component->remote_candidates == NULL) { ++ if (stream->remote_ufrag == NULL) { + /* case: We've got a valid binding request to a local candidate +- * but we do not yet know remote credentials nor +- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply ++ * but we do not yet know remote credentials. ++ * As per sect 7.2 of ICE (ID-19), we send a reply + * immediately but postpone all other processing until + * we get information about the remote candidates */ + +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 431c606..10319cc 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -105,7 +105,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); + void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); + gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); + gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); +-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); ++void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream); + NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); + void + conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, +-- +2.13.6 + + +From 0de1657e4d15d4f1911ab1fad84ea23e7013070f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 12:25:50 -0400 +Subject: [PATCH 03/70] conncheck: Use the right test for empty remote_frag + +It's now an array, not a pointer, so needs to test to emptyness. + +It's a bugfix on the previous commit, 59ce41df +--- + agent/conncheck.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 2abbc5e..7096b42 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3638,7 +3638,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + agent, stream, component, priority, from, nicesock, + local_candidate, + remote_candidate2 ? remote_candidate2 : remote_candidate); +- if(remote_candidate && stream->remote_ufrag != NULL) { ++ if(remote_candidate && stream->remote_ufrag[0]) { + if (local_candidate && + local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { + CandidateCheckPair *pair; +@@ -3657,7 +3657,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +- if (stream->remote_ufrag == NULL) { ++ if (stream->remote_ufrag[0] == 0) { + /* case: We've got a valid binding request to a local candidate + * but we do not yet know remote credentials. + * As per sect 7.2 of ICE (ID-19), we send a reply +-- +2.13.6 + + +From 0672758b9621801c8f0d9e3c920370983b267a68 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 12:29:29 -0400 +Subject: [PATCH 04/70] agent: Don't set variable that won't be used + +It exits the loop immediately, so no point to set the variable. +And it makes the clang static analyzer happy. +--- + agent/agent.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 4d9381c..8ba99bc 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -4245,7 +4245,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, + "Component removed during call."); + + component = NULL; +- error_reported = TRUE; + + goto recv_error; + } +-- +2.13.6 + + +From db05e8b0fdc713df93cd6a4c3914e5aee38b2391 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 12:30:27 -0400 +Subject: [PATCH 05/70] Make clang-analyzer happy + +Various little things, none of which should make a functional difference. +--- + agent/agent.c | 1 - + agent/conncheck.c | 2 +- + socket/udp-turn.c | 5 ++--- + stun/usages/bind.c | 4 +++- + 4 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 8ba99bc..28d7ccf 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -1552,7 +1552,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, + + if (len == 0) { + /* Reached EOS. */ +- len = 0; + goto done; + } else if (len < 0 && + pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) { +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 7096b42..1dc13dd 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -606,7 +606,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + /* step: when there's no pair in the Waiting state, + * unfreeze a new pair and check it + */ +- res = priv_conn_check_unfreeze_next (agent); ++ priv_conn_check_unfreeze_next (agent); + + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +diff --git a/socket/udp-turn.c b/socket/udp-turn.c +index cc3409b..a9c57e5 100644 +--- a/socket/udp-turn.c ++++ b/socket/udp-turn.c +@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock, + + /* Split up the monolithic buffer again into the caller-provided buffers. */ + if (parsed_buffer_length > 0 && allocated_buffer) { +- parsed_buffer_length = +- memcpy_buffer_to_input_message (message, buffer, +- parsed_buffer_length); ++ memcpy_buffer_to_input_message (message, buffer, ++ parsed_buffer_length); + } + + if (allocated_buffer) +diff --git a/stun/usages/bind.c b/stun/usages/bind.c +index 8dd7afc..d56790f 100644 +--- a/stun/usages/bind.c ++++ b/stun/usages/bind.c +@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + size_t len; + StunUsageTransReturn ret; + int val; +- struct sockaddr_storage alternate_server; ++ struct sockaddr_storage alternate_server = { AF_UNSPEC } ; + socklen_t alternate_server_len = sizeof (alternate_server); + StunUsageBindReturn bind_ret; + +@@ -548,6 +548,8 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) { + stun_trans_deinit (&trans); + ++ assert (alternate_server.ss_family != AF_UNSPEC); ++ + ret = stun_trans_create (&trans, SOCK_DGRAM, 0, + (struct sockaddr *) &alternate_server, alternate_server_len); + +-- +2.13.6 + + +From cd255bddc7fa0ddae056b5358a22b380c4eefc42 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 15:24:43 -0400 +Subject: [PATCH 06/70] udp-turn: Add some const to internal APIs + +--- + agent/component.c | 2 +- + socket/udp-turn.c | 10 +++++----- + socket/udp-turn.h | 7 ++++--- + 3 files changed, 10 insertions(+), 9 deletions(-) + +diff --git a/agent/component.c b/agent/component.c +index 32f7463..a679b30 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -380,7 +380,7 @@ nice_component_restart (NiceComponent *cmp) + for (i = cmp->remote_candidates; i; i = i->next) { + NiceCandidate *candidate = i->data; + +- /* note: do not remove the local candidate that is ++ /* note: do not remove the remote candidate that is + * currently part of the 'selected pair', see ICE + * 9.1.1.1. "ICE Restarts" (ID-19) */ + if (candidate == cmp->selected_pair.remote) { +diff --git a/socket/udp-turn.c b/socket/udp-turn.c +index a9c57e5..190a9ea 100644 +--- a/socket/udp-turn.c ++++ b/socket/udp-turn.c +@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data) + + NiceSocket * + nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, +- NiceSocket *base_socket, NiceAddress *server_addr, +- gchar *username, gchar *password, ++ NiceSocket *base_socket, const NiceAddress *server_addr, ++ const gchar *username, const gchar *password, + NiceTurnSocketCompatibility compatibility) + { + UdpTurnPriv *priv; +@@ -1184,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc + gsize + nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + NiceAddress *from, gsize len, guint8 *buf, +- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len) ++ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len) + { + + UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv; +@@ -1194,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + ChannelBinding *binding = NULL; + + union { +- guint8 *u8; +- guint16 *u16; ++ const guint8 *u8; ++ const guint16 *u16; + } recv_buf; + + /* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN) +diff --git a/socket/udp-turn.h b/socket/udp-turn.h +index b1eeeb4..df10a1c 100644 +--- a/socket/udp-turn.h ++++ b/socket/udp-turn.h +@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc + gsize + nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + NiceAddress *from, gsize len, guint8 *buf, +- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len); ++ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len); + + gboolean + nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer); + + NiceSocket * + nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, +- NiceSocket *base_socket, NiceAddress *server_addr, +- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility); ++ NiceSocket *base_socket, const NiceAddress *server_addr, ++ const gchar *username, const gchar *password, ++ NiceTurnSocketCompatibility compatibility); + + void + nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); +-- +2.13.6 + + +From e56b910d2d8b70f5677bbd4be579d5b95aff33ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 16:16:05 -0400 +Subject: [PATCH 07/70] agent: Separate return from NiceSocket and internal + enum + +The same variable was used for return values from NiceSocket and +for the internal enum, but 0 and -1 have different meanings in both. +--- + agent/agent.c | 35 +++++++++++++++++++---------------- + 1 file changed, 19 insertions(+), 16 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 28d7ccf..7b8a9fc 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3422,7 +3422,8 @@ agent_recv_message_unlocked ( + { + NiceAddress from; + GList *item; +- gint retval; ++ RecvStatus retval; ++ gint sockret; + gboolean is_turn = FALSE; + + /* We need an address for packet parsing, below. */ +@@ -3483,8 +3484,8 @@ agent_recv_message_unlocked ( + local_bufs[i + 1].buffer = message->buffers[i].buffer; + local_bufs[i + 1].size = message->buffers[i].size; + } +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + message->length = ntohs (rfc4571_frame); + } + } else { +@@ -3499,7 +3500,7 @@ agent_recv_message_unlocked ( + _priv_set_socket_tos (agent, new_socket, stream->tos); + nice_component_attach_socket (component, new_socket); + } +- retval = 0; ++ sockret = 0; + } else { + /* In the case of a real ICE-TCP connection, we can use the socket as a + * bytestream and do the read here with caching of data being read +@@ -3508,9 +3509,9 @@ agent_recv_message_unlocked ( + + /* TODO: Support bytestream reads */ + message->length = 0; +- retval = 0; ++ sockret = 0; + if (available <= 0) { +- retval = available; ++ sockret = available; + + /* If we don't call check_connect_result on an outbound connection, + * then is_connected will always return FALSE. That's why we check +@@ -3523,7 +3524,7 @@ agent_recv_message_unlocked ( + * not connected, it means that it failed to connect, so we must + * return an error to make the socket fail/closed + */ +- retval = -1; ++ sockret = -1; + } else { + gint flags = G_SOCKET_MSG_PEEK; + +@@ -3536,7 +3537,7 @@ agent_recv_message_unlocked ( + */ + if (g_socket_receive_message (nicesock->fileno, NULL, + NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0) +- retval = -1; ++ sockret = -1; + } + } else if (agent->rfc4571_expecting_length == 0) { + if ((gsize) available >= sizeof(guint16)) { +@@ -3544,8 +3545,8 @@ agent_recv_message_unlocked ( + GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)}; + NiceInputMessage local_message = { &local_buf, 1, message->from, 0}; + +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + agent->rfc4571_expecting_length = ntohs (rfc4571_frame); + available = g_socket_get_available_bytes (nicesock->fileno); + } +@@ -3589,8 +3590,8 @@ agent_recv_message_unlocked ( + off += local_bufs[i].size; + } + } +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + message->length = local_message.length; + agent->rfc4571_expecting_length -= local_message.length; + } +@@ -3598,20 +3599,22 @@ agent_recv_message_unlocked ( + } + } + } else { +- retval = nice_socket_recv_messages (nicesock, message, 1); ++ sockret = nice_socket_recv_messages (nicesock, message, 1); + } + +- if (retval == 0) { ++ if (sockret == 0) { + retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ + nice_debug_verbose ("%s: Agent %p: no message available on read attempt", + G_STRFUNC, agent); + goto done; +- } else if (retval < 0) { ++ } else if (sockret < 0) { + nice_debug ("Agent %p: %s returned %d, errno (%d) : %s", +- agent, G_STRFUNC, retval, errno, g_strerror (errno)); ++ agent, G_STRFUNC, sockret, errno, g_strerror (errno)); + + retval = RECV_ERROR; + goto done; ++ } else { ++ retval = sockret; + } + + if (retval == RECV_OOB || message->length == 0) { +-- +2.13.6 + + +From 4e605885c9dcaeb3ee443ec902c9c9189b19043f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 16:16:46 -0400 +Subject: [PATCH 08/70] agent: Remove impossible case + +--- + agent/agent.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 7b8a9fc..77f27e3 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3617,7 +3617,8 @@ agent_recv_message_unlocked ( + retval = sockret; + } + +- if (retval == RECV_OOB || message->length == 0) { ++ g_assert (retval != RECV_OOB); ++ if (message->length == 0) { + retval = RECV_OOB; + nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC, + agent); +-- +2.13.6 + + +From efc6a9be8cb34c899f0454c32e8a1e62b38df474 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 18:42:57 -0400 +Subject: [PATCH 09/70] tests: Use automake test-driver for valgrind + +This fixes the valgrind integration with the new test drivers. +--- + common.mk | 4 +- + scripts/valgrind-test-driver | 162 +++++++++++++++++++++++++++++++++++++++++++ + scripts/valgrind.sh | 28 -------- + 3 files changed, 165 insertions(+), 29 deletions(-) + create mode 100755 scripts/valgrind-test-driver + delete mode 100755 scripts/valgrind.sh + +diff --git a/common.mk b/common.mk +index e2ca3f4..b16380d 100644 +--- a/common.mk ++++ b/common.mk +@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice + + + check-valgrind: +- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check ++ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check ++ ++LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver + + .PHONY: check-valgrind +diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver +new file mode 100755 +index 0000000..5b660ee +--- /dev/null ++++ b/scripts/valgrind-test-driver +@@ -0,0 +1,162 @@ ++#! /bin/sh ++# test-driver - basic testsuite driver script. ++ ++scriptversion=2017-04-04.22; # UTC ++ ++# Copyright (C) 2011-2014 Free Software Foundation, Inc. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2, or (at your option) ++# any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <
http://www.gnu.org/licenses/
>. ++ ++# As a special exception to the GNU General Public License, if you ++# distribute this file as part of a program that contains a ++# configuration script generated by Autoconf, you may include it under ++# the same distribution terms that you use for the rest of that program. ++ ++# This file is maintained in Automake, please report ++# bugs to <bug-automake(a)gnu.org> or send patches to ++# <automake-patches(a)gnu.org>. ++ ++# Make unconditional expansion of undefined variables an error. This ++# helps a lot in preventing typo-related bugs. ++set -u ++ ++usage_error () ++{ ++ echo "$0: $*" >&2 ++ print_usage >&2 ++ exit 2 ++} ++ ++print_usage () ++{ ++ cat <<END ++Usage: ++ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH ++ [--expect-failure={yes|no}] [--color-tests={yes|no}] ++ [--enable-hard-errors={yes|no}] [--] ++ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] ++The '--test-name', '--log-file' and '--trs-file' options are mandatory. ++END ++} ++ ++test_name= # Used for reporting. ++log_file= # Where to save the output of the test script. ++trs_file= # Where to save the metadata of the test run. ++expect_failure=no ++color_tests=no ++enable_hard_errors=yes ++while test $# -gt 0; do ++ case $1 in ++ --help) print_usage; exit $?;; ++ --version) echo "test-driver $scriptversion"; exit $?;; ++ --test-name) test_name=$2; shift;; ++ --log-file) log_file=$2; shift;; ++ --trs-file) trs_file=$2; shift;; ++ --color-tests) color_tests=$2; shift;; ++ --expect-failure) expect_failure=$2; shift;; ++ --enable-hard-errors) enable_hard_errors=$2; shift;; ++ --) shift; break;; ++ -*) usage_error "invalid option: '$1'";; ++ *) break;; ++ esac ++ shift ++done ++ ++missing_opts= ++test x"$test_name" = x && missing_opts="$missing_opts --test-name" ++test x"$log_file" = x && missing_opts="$missing_opts --log-file" ++test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" ++if test x"$missing_opts" != x; then ++ usage_error "the following mandatory options are missing:$missing_opts" ++fi ++ ++if test $# -eq 0; then ++ usage_error "missing argument" ++fi ++ ++if test $color_tests = yes; then ++ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. ++ red='[0;31m' # Red. ++ grn='[0;32m' # Green. ++ lgn='[1;32m' # Light green. ++ blu='[1;34m' # Blue. ++ mgn='[0;35m' # Magenta. ++ std='[m' # No color. ++else ++ red= grn= lgn= blu= mgn= std= ++fi ++ ++do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' ++trap "st=129; $do_exit" 1 ++trap "st=130; $do_exit" 2 ++trap "st=141; $do_exit" 13 ++trap "st=143; $do_exit" 15 ++ ++# Test script is run here. ++top_srcdir="`dirname $0`/.." ++tests_dir="${top_srcdir}/tests" ++ ++USE_VALGRIND="`printenv USE_VALGRIND`" ++ ++if test "x${USE_VALGRIND}" = "x1"; then ++ ${top_srcdir}/libtool --mode=execute valgrind \ ++ --leak-check=full \ ++ --show-reachable=no \ ++ --error-exitcode=1 \ ++ --suppressions=$tests_dir/libnice.supp \ ++ --num-callers=30 "$@" >$log_file 2>&1 ++else ++ "$@" >$log_file 2>&1 ++fi ++estatus=$? ++ ++if test $enable_hard_errors = no && test $estatus -eq 99; then ++ tweaked_estatus=1 ++else ++ tweaked_estatus=$estatus ++fi ++ ++case $tweaked_estatus:$expect_failure in ++ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; ++ 0:*) col=$grn res=PASS recheck=no gcopy=no;; ++ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; ++ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; ++ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; ++ *:*) col=$red res=FAIL recheck=yes gcopy=yes;; ++esac ++ ++# Report the test outcome and exit status in the logs, so that one can ++# know whether the test passed or failed simply by looking at the '.log' ++# file, without the need of also peaking into the corresponding '.trs' ++# file (automake bug#11814). ++echo "$res $test_name (exit status: $estatus)" >>$log_file ++ ++# Report outcome to console. ++echo "${col}${res}${std}: $test_name" ++ ++# Register the test result, and other relevant metadata. ++echo ":test-result: $res" > $trs_file ++echo ":global-test-result: $res" >> $trs_file ++echo ":recheck: $recheck" >> $trs_file ++echo ":copy-in-global-log: $gcopy" >> $trs_file ++ ++# Local Variables: ++# mode: shell-script ++# sh-indentation: 2 ++# eval: (add-hook 'write-file-hooks 'time-stamp) ++# time-stamp-start: "scriptversion=" ++# time-stamp-format: "%:y-%02m-%02d.%02H" ++# time-stamp-time-zone: "UTC" ++# time-stamp-end: "; # UTC" ++# End: +diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh +deleted file mode 100755 +index 2864b6f..0000000 +--- a/scripts/valgrind.sh ++++ /dev/null +@@ -1,28 +0,0 @@ +-#!/bin/sh +- +-export G_SLICE=always-malloc +-export G_DEBUG=gc-friendly +- +-tests_dir="`dirname $0`/../tests" +- +-report=`libtool --mode=execute valgrind \ +- --leak-check=full \ +- --show-reachable=no \ +- --error-exitcode=1 \ +- --suppressions=$tests_dir/libnice.supp \ +- --num-callers=30 \ +- $1 2>&1` +- +-#if echo "$report" | grep -q ==; then +-if test $? != 0; then +- echo "$report" +- exit 1 +-fi +- +-if echo "$report" | grep -q "definitely lost"; then +- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then +- echo "$report" +- exit 1 +- fi +-fi +- +-- +2.13.6 + + +From ae6d939e48366b80570d713b83334191b0982e71 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 20:34:05 -0400 +Subject: [PATCH 10/70] debug: Use libnice-verbose, not libnice-nice-verbose + +--- + agent/debug.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/debug.c b/agent/debug.c +index e1a298c..84fee94 100644 +--- a/agent/debug.c ++++ b/agent/debug.c +@@ -102,7 +102,7 @@ void nice_debug_init (void) + flags |= g_parse_debug_string (gflags_string, gkeys, 4); + if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) + flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; +- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { ++ if (gflags_string && strstr (gflags_string, "libnice-verbose")) { + flags |= NICE_DEBUG_NICE_VERBOSE; + } + +-- +2.13.6 + + +From 10c557f23f8337f1304fff27bd85d2eb713cb249 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 5 Apr 2017 17:01:35 -0400 +Subject: [PATCH 11/70] test-credentials: Fix leak + +--- + tests/test-credentials.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/test-credentials.c b/tests/test-credentials.c +index 1de4e49..f1ea89d 100644 +--- a/tests/test-credentials.c ++++ b/tests/test-credentials.c +@@ -184,6 +184,8 @@ int main (void) + nice_agent_get_local_credentials (lagent, 1, &ufrag, &password); + g_assert (g_strcmp0("unicorns", ufrag) == 0); + g_assert (g_strcmp0("awesome", password) == 0); ++ g_free (ufrag); ++ g_free (password); + + nice_agent_gather_candidates (lagent, 1); + nice_agent_gather_candidates (ragent, 1); +-- +2.13.6 + + +From 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 14:41:51 -0400 +Subject: [PATCH 12/70] candidate: Add equality check function + +Add a function that can check if two candidates point to the same place. + +https://phabricator.freedesktop.org/T104 + +Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> +Differential Revision:
https://phabricator.freedesktop.org/D1715
+--- + agent/candidate.c | 11 +++++++++++ + agent/candidate.h | 18 +++++++++++++++++- + docs/reference/libnice/libnice-docs.xml | 4 ++++ + docs/reference/libnice/libnice-sections.txt | 1 + + nice/libnice.sym | 1 + + 5 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/agent/candidate.c b/agent/candidate.c +index 27966ef..85f8f65 100644 +--- a/agent/candidate.c ++++ b/agent/candidate.c +@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate) + + return copy; + } ++ ++NICEAPI_EXPORT gboolean ++nice_candidate_equal_target (const NiceCandidate *candidate1, ++ const NiceCandidate *candidate2) ++{ ++ g_return_val_if_fail (candidate1 != NULL, FALSE); ++ g_return_val_if_fail (candidate2 != NULL, FALSE); ++ ++ return (candidate1->transport == candidate2->transport && ++ nice_address_equal (&candidate1->addr, &candidate2->addr)); ++} +diff --git a/agent/candidate.h b/agent/candidate.h +index fadfce3..e556c16 100644 +--- a/agent/candidate.h ++++ b/agent/candidate.h +@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate); + NiceCandidate * + nice_candidate_copy (const NiceCandidate *candidate); + +-GType nice_candidate_get_type (void); ++/** ++ * nice_candidate_equal_target: ++ * @candidate1: A candidate ++ * @candidate2: A candidate ++ * ++ * Verifies that the candidates point to the same place, meaning they have ++ * the same transport and the same address. It ignores all other aspects. ++ * ++ * Returns: %TRUE if the candidates point to the same place ++ * ++ * Since: 0.1.15 ++ */ ++gboolean ++nice_candidate_equal_target (const NiceCandidate *candidate1, ++ const NiceCandidate *candidate2); ++ ++ GType nice_candidate_get_type (void); + + /** + * NICE_TYPE_CANDIDATE: +diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml +index 53487bc..391be1a 100644 +--- a/docs/reference/libnice/libnice-docs.xml ++++ b/docs/reference/libnice/libnice-docs.xml +@@ -105,6 +105,10 @@ + <title>Index of new symbols in 0.1.14</title> + <xi:include href="xml/api-index-0.1.14.xml"><xi:fallback/></xi:include> + </index> ++ <index role="0.1.15"> ++ <title>Index of new symbols in 0.1.15</title> ++ <xi:include href="xml/api-index-0.1.15.xml"><xi:fallback/></xi:include> ++ </index> + <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + </part> + </book> +diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt +index d377257..88a6cd2 100644 +--- a/docs/reference/libnice/libnice-sections.txt ++++ b/docs/reference/libnice/libnice-sections.txt +@@ -76,6 +76,7 @@ NICE_CANDIDATE_MAX_FOUNDATION + nice_candidate_new + nice_candidate_free + nice_candidate_copy ++nice_candidate_equal_target + <SUBSECTION Standard> + NICE_TYPE_CANDIDATE + nice_candidate_get_type +diff --git a/nice/libnice.sym b/nice/libnice.sym +index b04bb95..1e522ad 100644 +--- a/nice/libnice.sym ++++ b/nice/libnice.sym +@@ -58,6 +58,7 @@ nice_agent_set_software + nice_agent_set_stream_name + nice_agent_set_stream_tos + nice_candidate_copy ++nice_candidate_equal_target + nice_candidate_free + nice_candidate_new + nice_component_state_to_string +-- +2.13.6 + + +From ffc7fddac42728bac6e4753a17bc52e5e610ae8b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 21:27:39 -0400 +Subject: [PATCH 13/70] agent: Drop packets not from validated addresses + +This is required by the WebRTC spec. + +Remove test-mainloop as it doesnt even try to do +a negotiation. + +https://phabricator.freedesktop.org/T104 + +Differential Revision:
https://phabricator.freedesktop.org/D1716
+--- + agent/agent-priv.h | 2 + + agent/agent.c | 20 +++++++++- + agent/component.c | 90 +++++++++++++++++++++++++++++++++++++++++ + agent/component.h | 10 +++++ + agent/conncheck.c | 8 +++- + tests/Makefile.am | 1 - + tests/test-mainloop.c | 108 -------------------------------------------------- + 7 files changed, 127 insertions(+), 112 deletions(-) + delete mode 100644 tests/test-mainloop.c + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 4d8c9b8..ada3630 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -114,6 +114,8 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + * MTU and estimated typical sizes of ICE STUN packet */ + #define MAX_STUN_DATAGRAM_PAYLOAD 1300 + ++#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +diff --git a/agent/agent.c b/agent/agent.c +index 77f27e3..eff62f0 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3682,8 +3682,6 @@ agent_recv_message_unlocked ( + if (retval == RECV_OOB) + goto done; + +- agent->media_after_tick = TRUE; +- + /* If the message’s stated length is equal to its actual length, it’s probably + * a STUN message; otherwise it’s probably data. */ + if (stun_message_validate_buffer_length_fast ( +@@ -3715,6 +3713,7 @@ agent_recv_message_unlocked ( + nice_debug ("%s: Valid STUN packet received.", G_STRFUNC); + retval = RECV_OOB; + g_free (big_buf); ++ agent->media_after_tick = TRUE; + goto done; + } + } +@@ -3725,6 +3724,23 @@ agent_recv_message_unlocked ( + g_free (big_buf); + } + ++ if (!nice_component_verify_remote_candidate (component, ++ message->from, nicesock)) { ++ if (nice_debug_is_verbose ()) { ++ gchar str[INET6_ADDRSTRLEN]; ++ ++ nice_address_to_string (message->from, str); ++ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" ++ " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, ++ nice_address_get_port (message->from), nicesock->type); ++ } ++ ++ retval = RECV_OOB; ++ goto done; ++ } ++ ++ agent->media_after_tick = TRUE; ++ + /* Unhandled STUN; try handling TCP data, then pass to the client. */ + if (message->length > 0 && agent->reliable) { + if (!nice_socket_is_reliable (nicesock) && +diff --git a/agent/component.c b/agent/component.c +index a679b30..ba28ffa 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -435,6 +435,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa + component->selected_pair.remote = pair->remote; + component->selected_pair.priority = pair->priority; + component->selected_pair.prflx_priority = pair->prflx_priority; ++ ++ nice_component_add_valid_candidate (component, pair->remote); + } + + /* +@@ -514,6 +516,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, + component->selected_pair.remote = remote; + component->selected_pair.priority = priority; + ++ /* Get into fallback mode where packets from any source is accepted once ++ * this has been called. This is the expected behavior of pre-ICE SIP. ++ */ ++ component->fallback_mode = TRUE; ++ + return local; + } + +@@ -1107,6 +1114,9 @@ nice_component_finalize (GObject *obj) + g_warn_if_fail (cmp->remote_candidates == NULL); + g_warn_if_fail (cmp->incoming_checks == NULL); + ++ g_list_free_full (cmp->valid_candidates, ++ (GDestroyNotify) nice_candidate_free); ++ + g_clear_object (&cmp->tcp); + g_clear_object (&cmp->stop_cancellable); + g_clear_object (&cmp->iostream); +@@ -1421,3 +1431,83 @@ turn_server_unref (TurnServer *turn) + g_slice_free (TurnServer, turn); + } + } ++ ++void ++nice_component_add_valid_candidate (NiceComponent *component, ++ const NiceCandidate *candidate) ++{ ++ guint count = 0; ++ GList *item, *last = NULL; ++ ++ for (item = component->valid_candidates; item; item = item->next) { ++ NiceCandidate *cand = item->data; ++ ++ last = item; ++ count++; ++ if (nice_candidate_equal_target (cand, candidate)) ++ return; ++ } ++ ++ /* New candidate */ ++ ++ if (nice_debug_is_enabled ()) { ++ char str[INET6_ADDRSTRLEN]; ++ nice_address_to_string (&candidate->addr, str); ++ nice_debug ("Agent %p : %d:%d Adding valid source" ++ " candidate: %s:%d trans: %d\n", component->agent, ++ candidate->stream_id, candidate->component_id, str, ++ nice_address_get_port (&candidate->addr), candidate->transport); ++ } ++ ++ component->valid_candidates = g_list_prepend ( ++ component->valid_candidates, nice_candidate_copy (candidate)); ++ ++ /* Delete the last one to make sure we don't have a list that is too long, ++ * the candidates are not freed on ICE restart as this would be more complex, ++ * we just keep the list not too long. ++ */ ++ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) { ++ NiceCandidate *cand = last->data; ++ ++ component->valid_candidates = g_list_delete_link ( ++ component->valid_candidates, last); ++ nice_candidate_free (cand); ++ } ++} ++ ++gboolean ++nice_component_verify_remote_candidate (NiceComponent *component, ++ const NiceAddress *address, NiceSocket *nicesock) ++{ ++ GList *item; ++ ++ if (component->fallback_mode) ++ return TRUE; ++ ++ for (item = component->valid_candidates; item; item = item->next) { ++ NiceCandidate *cand = item->data; ++ ++ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD && ++ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && ++ nice_address_equal (address, &cand->addr)) { ++ /* fast return if it's already the first */ ++ if (item == component->valid_candidates) ++ return TRUE; ++ ++ /* Put the current candidate at the top so that in the normal use-case, ++ * this function becomes O(1). ++ */ ++ component->valid_candidates = g_list_remove_link ( ++ component->valid_candidates, item); ++ component->valid_candidates = g_list_concat (item, ++ component->valid_candidates); ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} +diff --git a/agent/component.h b/agent/component.h +index 6712794..a8a1222 100644 +--- a/agent/component.h ++++ b/agent/component.h +@@ -159,12 +159,14 @@ struct _NiceComponent { + NiceComponentState state; + GSList *local_candidates; /* list of NiceCandidate objs */ + GSList *remote_candidates; /* list of NiceCandidate objs */ ++ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */ + GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */ + guint socket_sources_age; /* incremented when socket_sources changes */ + GSList *incoming_checks; /* list of IncomingCheck objs */ + GList *turn_servers; /* List of TurnServer objs */ + CandidatePair selected_pair; /* independent from checklists, + see ICE 11.1. "Sending Media" (ID-19) */ ++ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */ + NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */ + NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */ + /* I/O handling. The main context must always be non-NULL, and is used for all +@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn); + void + turn_server_unref (TurnServer *turn); + ++void ++nice_component_add_valid_candidate (NiceComponent *component, ++ const NiceCandidate *candidate); ++ ++gboolean ++nice_component_verify_remote_candidate (NiceComponent *component, ++ const NiceAddress *address, NiceSocket *nicesock); ++ + + G_END_DECLS + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 1dc13dd..7ffa3db 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2627,6 +2627,7 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + p->state = NICE_CHECK_SUCCEEDED; + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + priv_conn_check_unfreeze_related (agent, stream, p); ++ nice_component_add_valid_candidate (component, remote_candidate); + } + else { + if (!local_cand) { +@@ -2652,8 +2653,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + + /* note: this is same as "adding to VALID LIST" in the spec + text */ +- if (new_pair) ++ if (new_pair) { + new_pair->valid = TRUE; ++ nice_component_add_valid_candidate (component, remote_candidate); ++ } + + return new_pair; + } +@@ -2739,6 +2742,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + nice_debug ("Agent %p : Mapped address not found." + " conncheck %p SUCCEEDED.", agent, p); + priv_conn_check_unfreeze_related (agent, stream, p); ++ nice_component_add_valid_candidate (component, p->remote); + } else { + ok_pair = priv_process_response_check_for_reflexive (agent, + stream, component, p, sockptr, &sockaddr.addr, +@@ -3654,6 +3658,8 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + } + } + ++ nice_component_add_valid_candidate (component, remote_candidate); ++ + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 7bfe075..d24a2aa 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -45,7 +45,6 @@ check_PROGRAMS = \ + test-send-recv \ + test-socket-is-based-on \ + test-priority \ +- test-mainloop \ + test-fullmode \ + test-restart \ + test-fallback \ +diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c +deleted file mode 100644 +index 7c52daa..0000000 +--- a/tests/test-mainloop.c ++++ /dev/null +@@ -1,108 +0,0 @@ +-/* +- * This file is part of the Nice GLib ICE library. +- * +- * (C) 2006, 2007 Collabora Ltd. +- * Contact: Dafydd Harries +- * (C) 2006, 2007 Nokia Corporation. All rights reserved. +- * Contact: Kai Vehmanen +- * +- * The contents of this file are subject to the Mozilla Public License Version +- * 1.1 (the "License"); you may not use this file except in compliance with +- * the License. You may obtain a copy of the License at +- *
http://www.mozilla.org/MPL/
+- * +- * Software distributed under the License is distributed on an "AS IS" basis, +- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +- * for the specific language governing rights and limitations under the +- * License. +- * +- * The Original Code is the Nice GLib ICE library. +- * +- * The Initial Developers of the Original Code are Collabora Ltd and Nokia +- * Corporation. All Rights Reserved. +- * +- * Contributors: +- * Dafydd Harries, Collabora Ltd. +- * Kai Vehmanen, Nokia +- * +- * Alternatively, the contents of this file may be used under the terms of the +- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which +- * case the provisions of LGPL are applicable instead of those above. If you +- * wish to allow use of your version of this file only under the terms of the +- * LGPL and not to allow others to use your version of this file under the +- * MPL, indicate your decision by deleting the provisions above and replace +- * them with the notice and other provisions required by the LGPL. If you do +- * not delete the provisions above, a recipient may use your version of this +- * file under either the MPL or the LGPL. +- */ +-#ifdef HAVE_CONFIG_H +-# include <config.h> +-#endif +- +-#include <string.h> +- +-#include <nice/nice.h> +-#include "socket/socket.h" +- +-static GMainLoop *loop = NULL; +- +-static void +-recv_cb ( +- NiceAgent *agent, +- guint stream_id, +- guint component_id, +- guint len, +- gchar *buf, +- gpointer data) +-{ +- g_assert (agent != NULL); +- g_assert (stream_id == 1); +- g_assert (component_id == 1); +- g_assert (len == 6); +- g_assert (0 == strncmp (buf, "\x80hello", len)); +- g_assert (42 == GPOINTER_TO_UINT (data)); +- g_main_loop_quit (loop); +-} +- +-int +-main (void) +-{ +- NiceAgent *agent; +- NiceAddress addr; +- guint stream; +- +- nice_address_init (&addr); +- +- loop = g_main_loop_new (NULL, FALSE); +- +- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245); +- nice_address_set_ipv4 (&addr, 0x7f000001); +- nice_agent_add_local_address (agent, &addr); +- stream = nice_agent_add_stream (agent, 1); +- nice_agent_gather_candidates (agent, stream); +- +- // attach to default main context +- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP, +- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42)); +- +- { +- NiceCandidate *candidate; +- GSList *candidates, *i; +- +- candidates = nice_agent_get_local_candidates (agent, 1, 1); +- candidate = candidates->data; +- +- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello"); +- for (i = candidates; i; i = i->next) +- nice_candidate_free ((NiceCandidate *) i->data); +- g_slist_free (candidates); +- } +- +- g_main_loop_run (loop); +- +- nice_agent_remove_stream (agent, stream); +- g_object_unref (agent); +- +- return 0; +-} +- +-- +2.13.6 + + +From 8fc22b0034d04cbc222e0637152b1cee2879eef3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 5 Apr 2017 17:43:26 -0400 +Subject: [PATCH 14/70] tests_: Add test to verify that only packets from + validated addresses pass + +https://phabricator.freedesktop.org/T104 + +Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> +Differential Revision:
https://phabricator.freedesktop.org/D1717
+--- + tests/Makefile.am | 5 +- + tests/test-drop-invalid.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 521 insertions(+), 1 deletion(-) + create mode 100644 tests/test-drop-invalid.c + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index d24a2aa..62d5d64 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -54,7 +54,8 @@ check_PROGRAMS = \ + test-tcp \ + test-icetcp \ + test-credentials \ +- test-turn ++ test-turn \ ++ test-drop-invalid + + dist_check_SCRIPTS = \ + check-test-fullmode-with-stun.sh \ +@@ -128,6 +129,8 @@ test_credentials_LDADD = $(COMMON_LDADD) + + test_turn_LDADD = $(COMMON_LDADD) + ++test_drop_invalid_LDADD = $(COMMON_LDADD) ++ + test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) + test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) + +diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c +new file mode 100644 +index 0000000..97e3586 +--- /dev/null ++++ b/tests/test-drop-invalid.c +@@ -0,0 +1,517 @@ ++/* ++ * This file is part of the Nice GLib ICE library. ++ * ++ * Unit test for ICE full-mode related features. ++ * ++ * (C) 2007 Nokia Corporation. All rights reserved. ++ * Contact: Kai Vehmanen ++ * (C) 2017 Collabora Ltd ++ * Contact: Olivier Crete <olivier.crete(a)collabora.com> ++ * ++ * The contents of this file are subject to the Mozilla Public License Version ++ * 1.1 (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ *
http://www.mozilla.org/MPL/
++ * ++ * Software distributed under the License is distributed on an "AS IS" basis, ++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ++ * for the specific language governing rights and limitations under the ++ * License. ++ * ++ * The Original Code is the Nice GLib ICE library. ++ * ++ * The Initial Developers of the Original Code are Collabora Ltd and Nokia ++ * Corporation. All Rights Reserved. ++ * ++ * Contributors: ++ * Kai Vehmanen, Nokia ++ * ++ * Alternatively, the contents of this file may be used under the terms of the ++ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which ++ * case the provisions of LGPL are applicable instead of those above. If you ++ * wish to allow use of your version of this file only under the terms of the ++ * LGPL and not to allow others to use your version of this file under the ++ * MPL, indicate your decision by deleting the provisions above and replace ++ * them with the notice and other provisions required by the LGPL. If you do ++ * not delete the provisions above, a recipient may use your version of this ++ * file under either the MPL or the LGPL. ++ */ ++#ifdef HAVE_CONFIG_H ++# include <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include "socket/socket.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++ ++ ++static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static guint global_components_ready = 0; ++static guint global_components_ready_exit = 0; ++static guint global_components_failed = 0; ++static guint global_components_failed_exit = 0; ++static GMainLoop *global_mainloop = NULL; ++static gboolean global_lagent_gathering_done = FALSE; ++static gboolean global_ragent_gathering_done = FALSE; ++static gboolean global_lagent_ibr_received = FALSE; ++static gboolean global_ragent_ibr_received = FALSE; ++static int global_lagent_cands = 0; ++static int global_ragent_cands = 0; ++static gint global_ragent_read = 0; ++static guint global_exit_when_ibr_received = 0; ++ ++static void priv_print_global_status (void) ++{ ++ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done); ++ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]); ++ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]); ++ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands); ++} ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)buf; ++ ++ /* Core of the test ++ * Assert on any unreleated packet received. This would include anything ++ * send before the negotiation is over. ++ */ ++ g_assert (len == 16); ++ g_assert (strncmp ("1234567812345678", buf, 16) == 0); ++ ++ if (component_id == 2) ++ return; ++ ++ if (GPOINTER_TO_UINT (user_data) == 2) { ++ g_debug ("right agent received %d bytes, stopping mainloop", len); ++ global_ragent_read = len; ++ g_main_loop_quit (global_mainloop); ++ } ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_gathering_done = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_gathering_done = TRUE; ++ ++ if (global_lagent_gathering_done && ++ global_ragent_gathering_done) ++ g_main_loop_quit (global_mainloop); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; ++} ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ gboolean ready_to_connected = FALSE; ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) { ++ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_lagent_state[component_id - 1] = state; ++ } else if (GPOINTER_TO_UINT (data) == 2) { ++ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_ragent_state[component_id - 1] = state; ++ } ++ ++ if (state == NICE_COMPONENT_STATE_READY) ++ global_components_ready++; ++ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) ++ global_components_ready--; ++ if (state == NICE_COMPONENT_STATE_FAILED) ++ global_components_failed++; ++ ++ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit); ++ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit); ++ ++ /* signal status via a global variable */ ++ if (global_components_ready == global_components_ready_exit && ++ global_components_failed == global_components_failed_exit) { ++ g_debug ("Components ready/failed achieved. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ return; ++ } ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; (void)component_id; ++} ++ ++static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, ++ gchar *lfoundation, gchar* rfoundation, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ ++global_lagent_cands; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ ++global_ragent_cands; ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; ++} ++ ++static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, ++ gchar *foundation, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation; ++} ++ ++static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_ibr_received = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_ibr_received = TRUE; ++ ++ if (global_exit_when_ibr_received) { ++ g_debug ("Received initial binding request. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ } ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL; ++ GSList *peer_cands = NULL; ++ GSList *item1, *item2; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ peer_cands = nice_agent_get_local_candidates (to, to_stream, component); ++ ++ /* ++ * Core of the test: ++ * ++ * Send packets that shoudl be dropped. ++ */ ++ ++ for (item1 = cands; item1; item1 = item1->next) { ++ NiceCandidate *cand = item1->data; ++ NiceSocket *nicesock = cand->sockptr; ++ ++ g_assert (nicesock); ++ ++ for (item2 = peer_cands; item2; item2 = item2->next) { ++ NiceCandidate *target_cand = item2->data; ++ ++ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB"); ++ } ++ ++ } ++ ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free); ++ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free); ++} ++ ++static void set_credentials (NiceAgent *lagent, guint lstream, ++ NiceAgent *ragent, guint rstream) ++{ ++ gchar *ufrag = NULL, *password = NULL; ++ ++ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++} ++ ++static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed) ++{ ++ guint ls_id, rs_id; ++ gint ret; ++ ++ /* XXX: dear compiler, this is for you */ ++ (void)baseaddr; ++ ++ /* step: initialize variables modified by the callbacks */ ++ global_components_ready = 0; ++ global_components_ready_exit = ready; ++ global_components_failed = 0; ++ global_components_failed_exit = failed; ++ global_lagent_gathering_done = FALSE; ++ global_ragent_gathering_done = FALSE; ++ global_lagent_ibr_received = ++ global_ragent_ibr_received = FALSE; ++ global_lagent_cands = ++ global_ragent_cands = 0; ++ ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ ++ /* step: add one stream, with RTP+RTCP components, to each agent */ ++ ls_id = nice_agent_add_stream (lagent, 2); ++ ++ rs_id = nice_agent_add_stream (ragent, 2); ++ g_assert (ls_id > 0); ++ g_assert (rs_id > 0); ++ ++ /* Gather candidates and test nice_agent_set_port_range */ ++ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000); ++ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001); ++ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345); ++ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001); ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE); ++ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL); ++ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL); ++ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); ++ ++ { ++ GSList *cands = NULL, *i; ++ NiceCandidate *cand = NULL; ++ ++ cands = nice_agent_get_local_candidates (lagent, ls_id, 1); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10000); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (lagent, ls_id, 2); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10001); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (ragent, rs_id, 1); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 12345); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (ragent, rs_id, 2); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10002); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ } ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (2)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (2)); ++ ++ /* step: run mainloop until local candidates are ready ++ * (see timer_cb() above) */ ++ if (global_lagent_gathering_done != TRUE || ++ global_ragent_gathering_done != TRUE) { ++ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'..."); ++ g_main_loop_run (global_mainloop); ++ g_assert (global_lagent_gathering_done == TRUE); ++ g_assert (global_ragent_gathering_done == TRUE); ++ } ++ ++ set_credentials (lagent, ls_id, ragent, rs_id); ++ ++ /* step: pass the remote candidates to agents */ ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP); ++ ++ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed..."); ++ ++ /* step: run the mainloop until connectivity checks succeed ++ * (see timer_cb() above) */ ++ g_main_loop_run (global_mainloop); ++ ++ /* note: verify that STUN binding requests were sent */ ++ g_assert (global_lagent_ibr_received == TRUE); ++ g_assert (global_ragent_ibr_received == TRUE); ++ ++ /* note: Send a packet from another address */ ++ /* These should also be ignored */ ++ { ++ NiceCandidate *local_cand = NULL; ++ NiceCandidate *remote_cand = NULL; ++ NiceSocket *tmpsock; ++ ++ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand, ++ &remote_cand)); ++ g_assert (local_cand); ++ g_assert (remote_cand); ++ ++ tmpsock = nice_udp_bsd_socket_new (NULL); ++ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD"); ++ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE"); ++ nice_socket_free (tmpsock); ++ } ++ ++ /* note: test payload send and receive */ ++ global_ragent_read = 0; ++ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"); ++ g_assert (ret != -1); ++ g_debug ("Sent %d bytes", ret); ++ g_assert (ret == 16); ++ while (global_ragent_read != 16) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_ragent_read == 16); ++ ++ g_debug ("test-drop-invalid: Ran mainloop, removing streams..."); ++ ++ /* step: clean up resources and exit */ ++ ++ nice_agent_remove_stream (lagent, ls_id); ++ nice_agent_remove_stream (ragent, rs_id); ++ ++ return 0; ++} ++ ++int main (void) ++{ ++ NiceAgent *lagent, *ragent; /* agent's L and R */ ++ NiceAddress baseaddr; ++ int result; ++ guint timer_id; ++ ++#ifdef G_OS_WIN32 ++ WSADATA w; ++ ++ WSAStartup(0x0202, &w); ++#endif ++ ++ global_mainloop = g_main_loop_new (NULL, FALSE); ++ ++ /* step: create the agents L and R */ ++ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_RFC5245); ++ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_RFC5245); ++ ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ ++ ++ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent"); ++ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent"); ++ ++ /* step: add a timer to catch state changes triggered by signals */ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ /* step: specify which local interface to use */ ++ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1")) ++ g_assert_not_reached (); ++ nice_agent_add_local_address (lagent, &baseaddr); ++ nice_agent_add_local_address (ragent, &baseaddr); ++ ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-candidate", ++ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "new-candidate", ++ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received", ++ G_CALLBACK (cb_initial_binding_request_received), ++ GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received", ++ G_CALLBACK (cb_initial_binding_request_received), ++ GUINT_TO_POINTER (2)); ++ ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ ++ ++ /* step: run test the first time */ ++ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time"); ++ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0); ++ priv_print_global_status (); ++ g_assert (result == 0); ++ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY); ++ /* When using TURN, we get peer reflexive candidates for the host cands ++ that we removed so we can get another new_selected_pair signal later ++ depending on timing/racing, we could double (or not) the amount we expected ++ */ ++ ++ /* note: verify that correct number of local candidates were reported */ ++ g_assert (global_lagent_cands == 2); ++ g_assert (global_ragent_cands == 2); ++ ++ g_object_unref (lagent); ++ g_object_unref (ragent); ++ ++ g_main_loop_unref (global_mainloop); ++ global_mainloop = NULL; ++ ++ g_source_remove (timer_id); ++#ifdef G_OS_WIN32 ++ WSACleanup(); ++#endif ++ return result; ++} +-- +2.13.6 + + +From f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 11 Apr 2017 16:42:55 -0400 +Subject: [PATCH 15/70] conncheck: Check the controlling state when the req was + sent + +It was checking when the pair was created, but the role may have +already changed when the request is sent. +--- + agent/conncheck.c | 30 +++++++++++++++++++----------- + agent/conncheck.h | 1 - + 2 files changed, 19 insertions(+), 12 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 7ffa3db..5501c2b 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1666,7 +1666,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + tmpbuf2, nice_address_get_port (&pair->remote->addr)); + } + pair->nominated = use_candidate; +- pair->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); + +@@ -2531,7 +2530,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->priority = nice_candidate_pair_priority (pair->remote->priority, + pair->local->priority); + pair->nominated = FALSE; +- pair->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local_cand)); + nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); +@@ -2777,16 +2775,26 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { + /* case: role conflict error, need to restart with new role */ + nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- /* note: our role might already have changed due to an +- * incoming request, but if not, change role now; +- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ +- priv_check_for_role_conflict (agent, !p->controlling); + +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_WAITING; +- priv_add_pair_to_triggered_check_queue (agent, p); +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ if (p->stun_message.buffer != NULL) { ++ guint64 tie; ++ gboolean controlled_mode; ++ ++ /* note: our role might already have changed due to an ++ * incoming request, but if not, change role now; ++ * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ ++ controlled_mode = (stun_message_find64 (&p->stun_message, ++ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == ++ STUN_MESSAGE_RETURN_SUCCESS); ++ ++ priv_check_for_role_conflict (agent, controlled_mode); ++ ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; ++ p->state = NICE_CHECK_WAITING; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ } + trans_found = TRUE; + } else { + /* case: STUN error, the check STUN context was freed */ +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 10319cc..c204475 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -85,7 +85,6 @@ struct _CandidateCheckPair + gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; + NiceCheckState state; + gboolean nominated; +- gboolean controlling; + gboolean timer_restarted; + gboolean valid; + guint64 priority; +-- +2.13.6 + + +From b0538d8c51f65019867b56a45cf90a70bef38f01 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 11 Apr 2017 18:31:21 -0400 +Subject: [PATCH 16/70] agent: Ignore remote candidate of non-accepted types + +If we disable ice-tcp or ice-udp, ignore the remote +candidates for those types. +--- + agent/agent.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/agent/agent.c b/agent/agent.c +index eff62f0..77fb1eb 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3112,6 +3112,13 @@ static gboolean priv_add_remote_candidate ( + NiceComponent *component; + NiceCandidate *candidate; + ++ if (transport == NICE_CANDIDATE_TRANSPORT_UDP && ++ !agent->use_ice_udp) ++ return FALSE; ++ if (transport != NICE_CANDIDATE_TRANSPORT_UDP && ++ !agent->use_ice_tcp) ++ return FALSE; ++ + if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) + return FALSE; + +-- +2.13.6 + + +From f6f704c5e8d2193bc67ba2b697c77694e1698c43 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 9 Jun 2016 22:22:33 +0200 +Subject: [PATCH 17/70] stun timer: fix timeout of the last retransmission + +According to RFC 5389, section 7.2.1, a special timeout is applied to +the last retransmission (Rm * RTO), with Rm default value of 16, instead +of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7. + +As spotted by Olivier Crete, stun_timer_* is a public API, that cannot +be changed, and the initial delay (RTO) is not preserved in the +stun_timer_s struct. So we use a hack that implicitely guess Rm from the +number of transmissions Rc, by generalizing the default value of the +spec for Rm and Rc to other values of Rc passed in stun_timer_start( + +According to the spec, with the default value of Rc=7, the last delay +should be (64 * RTO), and it is instead (16 * RTO). So the last delay +can be computed by dividing the penultimate delay by two, instead of +multiplying it by two. + +Differential Revision:
https://phabricator.freedesktop.org/D1108
+--- + stun/usages/timer.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/stun/usages/timer.c b/stun/usages/timer.c +index 2862ab8..5370cba 100644 +--- a/stun/usages/timer.c ++++ b/stun/usages/timer.c +@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer) + if (timer->retransmissions >= timer->max_retransmissions) + return STUN_USAGE_TIMER_RETURN_TIMEOUT; + +- add_delay (&timer->deadline, timer->delay *= 2); ++ if (timer->retransmissions == timer->max_retransmissions - 1) ++ timer->delay = timer->delay / 2; ++ else ++ timer->delay = timer->delay * 2; ++ add_delay (&timer->deadline, timer->delay); + timer->retransmissions++; + return STUN_USAGE_TIMER_RETURN_RETRANSMIT; + } +-- +2.13.6 + + +From 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 5 Apr 2016 21:32:39 +0200 +Subject: [PATCH 18/70] agent: do not create a GSource for UDP TURN socket + +With this patch, we don't create a new GSource for udp-turn socket, +because it would duplicate the packets already received on the base UDP +socket, as the underlying GSocket is the same. This is a race condition, +because an UDP packet arriving on the base socket, may randomly be +handled by the GSource callback created for the base socket (udp-bsd) of +the callback created for the udp-turn socket. Moreover this callback +already knows how to parse UDP datagrams received from a known turn +server. + +This patch also prevents a subtle bug, when a STUN request is received +directly from a peer, is handled by the udp turn socket. If the agent +already has a valid permission for this remote candidate, established +for another pair, it will happily send the STUN reply through the turn +relay. This generates a source address mismatch on the peer agent, when +it'll receive the STUN response from the turn relay instead of the +initial address the request has been sent to. + +Differential Revision:
https://phabricator.freedesktop.org/D932
+--- + agent/component.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/agent/component.c b/agent/component.c +index ba28ffa..ab665b6 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context) + if (socket_source->socket->fileno == NULL) + return; + ++ /* Do not create a GSource for UDP turn socket, because it ++ * would duplicate the packets already received on the base ++ * UDP socket. ++ */ ++ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN) ++ return; ++ + /* Create a source. */ + source = g_socket_create_source (socket_source->socket->fileno, + G_IO_IN, NULL); +-- +2.13.6 + + +From d5446a72233eab8501be0b3fb9060c8be3ba034b Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Mon, 1 May 2017 08:51:40 +0100 +Subject: [PATCH 19/70] examples: Stop installing the examples +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There’s no point in installing them; their benefit is in providing +example code to developers. + +Debian doesn’t package them; Fedora packages them in a separate +subpackage which will have to disappear. + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1737
+--- + examples/Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/examples/Makefile.am b/examples/Makefile.am +index 1e7decf..9c80854 100644 +--- a/examples/Makefile.am ++++ b/examples/Makefile.am +@@ -18,7 +18,7 @@ AM_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(GUPNP_CFLAGS) + +-bin_PROGRAMS = simple-example threaded-example sdp-example ++noinst_PROGRAMS = simple-example threaded-example sdp-example + + simple_example_SOURCES = simple-example.c + simple_example_LDADD = $(top_builddir)/agent/libagent.la \ +-- +2.13.6 + + +From b4abda09c79e4ce372a3771300abf568c85c7ff5 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 21 Apr 2016 18:18:59 +0200 +Subject: [PATCH 20/70] interfaces: ignore predefined network interfaces + +Some interfaces, like the one managed by libvirtd to provide a network +bridge to locally hosted virtual machines, can be completely ignored +when gathering ICE candidates. The motivation for adding this +possibility is that, ignoring them doesn't remove capabilities, and +improves the overall speed of the connection check method, by reducing +the number of pairs to be tested. This patch adds the possibility to +define such interfaces in the configuration script. + +Differential Revision:
https://phabricator.freedesktop.org/D948
+--- + agent/interfaces.c | 6 ++++++ + configure.ac | 14 ++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/agent/interfaces.c b/agent/interfaces.c +index 0fa2fd7..a81888e 100644 +--- a/agent/interfaces.c ++++ b/agent/interfaces.c +@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback) + nice_debug ("Ignoring loopback interface"); + g_free (addr_string); + } ++#ifdef IGNORED_IFACE_PREFIX ++ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) { ++ nice_debug ("Ignoring interface %s as it matches prefix %s", ++ ifa->ifa_name, IGNORED_IFACE_PREFIX); ++ g_free (addr_string); ++#endif + } else { + if (nice_interfaces_is_private_ip (ifa->ifa_addr)) + ips = add_ip_to_list (ips, addr_string, TRUE); +diff --git a/configure.ac b/configure.ac +index b39bfe3..98bbc08 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false) + # GObject introspection + GOBJECT_INTROSPECTION_CHECK([1.30.0]) + ++dnl Ignore a specific network interface name prefix from the connection check ++AC_MSG_CHECKING([whether to ignore a specific network interface name prefix]) ++AC_ARG_WITH([ignored-network-interface-prefix], ++ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string], ++ [Ignore network interfaces whose name starts with "string" from the ICE connection ++ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge ++ handled by libvirtd, do not help in finding connectivity.])], ++ [interface_prefix="$withval"]) ++AS_IF([test -n "$interface_prefix"], ++ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"], ++ [Ignore this network interface prefix from the connection check]) ++ AC_MSG_RESULT([yes, $interface_prefix])], ++ [AC_MSG_RESULT([no])]) ++ + AC_CONFIG_MACRO_DIR(m4) + + AC_OUTPUT +-- +2.13.6 + + +From 80c613699786567fd93db74377138600794a86e0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Thu, 8 Jun 2017 16:34:21 -0400 +Subject: [PATCH 21/70] agent: Use base_addr to generate rport in SDP + +Reported by Capricornus (zhushengliang) + +https://phabricator.freedesktop.org/T7763 +--- + agent/agent.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 77fb1eb..1ff09af 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -5690,7 +5690,7 @@ _generate_candidate_sdp (NiceAgent *agent, + g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type)); + if (nice_address_is_valid (&candidate->base_addr) && + !nice_address_equal (&candidate->addr, &candidate->base_addr)) { +- port = nice_address_get_port (&candidate->addr); ++ port = nice_address_get_port (&candidate->base_addr); + nice_address_to_string (&candidate->base_addr, ip4); + g_string_append_printf (sdp, " raddr %s rport %d", ip4, + port == 0 ? 9 : port); +-- +2.13.6 + + +From 8bb210c5af4bcaf342d7fa4fef6034269e976532 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 9 Jun 2016 23:28:43 +0200 +Subject: [PATCH 22/70] stun timer: make properties for stun timer tunables + +Three STUN binding request properties should be customisable. RFC 5245 +describes the retransmission timer of the STUN transaction 'RTO', and +RFC 5389 describes the number of retransmissions to send until a +response is received 'Rc'. The third property is the 'RTO' when +a reliable connection is used. + +RFC 5389 introduces a supplementary property 'Rm' as a multiplier used +to compute the final timeout RTO * Rm. However, this property is not +added in libnice, because this would require breaking the public API for +STUN. Currently, our STUN implementation hardcodes a division by two for +this final timeout. + +Differential Revision:
https://phabricator.freedesktop.org/D1109
+--- + agent/agent-priv.h | 4 ++- + agent/agent.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + agent/conncheck.c | 16 +++++---- + agent/discovery.c | 8 ++--- + stun/usages/timer.h | 6 +++- + 5 files changed, 118 insertions(+), 13 deletions(-) + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index ada3630..162ea63 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -106,7 +106,6 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + + #define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */ + #define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */ +-#define NICE_AGENT_TIMER_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */ + #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */ + + +@@ -132,6 +131,9 @@ struct _NiceAgent + guint timer_ta; /* property: timer Ta */ + guint max_conn_checks; /* property: max connectivity checks */ + gboolean force_relay; /* property: force relay */ ++ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ ++ guint stun_initial_timeout; /* property: stun initial timeout, RTO */ ++ guint stun_reliable_timeout; /* property: stun reliable timeout */ + + GSList *local_addresses; /* list of NiceAddresses for local + interfaces */ +diff --git a/agent/agent.c b/agent/agent.c +index 1ff09af..25d7886 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -113,6 +113,9 @@ enum + PROP_BYTESTREAM_TCP, + PROP_KEEPALIVE_CONNCHECK, + PROP_FORCE_RELAY, ++ PROP_STUN_MAX_RETRANSMISSIONS, ++ PROP_STUN_INITIAL_TIMEOUT, ++ PROP_STUN_RELIABLE_TIMEOUT, + }; + + +@@ -708,6 +711,76 @@ nice_agent_class_init (NiceAgentClass *klass) + FALSE, + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:stun-max-retransmissions ++ * ++ * The maximum number of retransmissions of the STUN binding requests ++ * used in the gathering stage, to find our local candidates, and used ++ * in the connection check stage, to test the validity of each ++ * constructed pair. This property is described as 'Rc' in the RFC ++ * 5389, with a default value of 7. The timeout of each STUN request ++ * is doubled for each retransmission, so the choice of this value has ++ * a direct impact on the time needed to move from the CONNECTED state ++ * to the READY state, and on the time needed to complete the GATHERING ++ * state. ++ * ++ * Since: UNRELEASED ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, ++ g_param_spec_uint ( ++ "stun-max-retransmissions", ++ "STUN Max Retransmissions", ++ "Maximum number of STUN binding requests retransmissions " ++ "described as 'Rc' in the STUN specification.", ++ 1, 99, ++ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * NiceAgent:stun-initial-timeout ++ * ++ * The initial timeout (msecs) of the STUN binding requests ++ * used in the gathering stage, to find our local candidates. ++ * This property is described as 'RTO' in the RFC 5389 and RFC 5245. ++ * This timeout is doubled for each retransmission, until ++ * #NiceAgent:stun-max-retransmissions have been done, ++ * with an exception for the last restransmission, where the timeout is ++ * divided by two instead (RFC 5389 indicates that a customisable ++ * multiplier 'Rm' to 'RTO' should be used). ++ * ++ * Since: UNRELEASED ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, ++ g_param_spec_uint ( ++ "stun-initial-timeout", ++ "STUN Initial Timeout", ++ "STUN timeout in msecs of the initial binding requests used in the " ++ "gathering state, described as 'RTO' in the ICE specification.", ++ 20, 9999, ++ STUN_TIMER_DEFAULT_TIMEOUT, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * NiceAgent:stun-reliable-timeout ++ * ++ * The initial timeout of the STUN binding requests used ++ * for a reliable timer. ++ * ++ * Since: UNRELEASED ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, ++ g_param_spec_uint ( ++ "stun-reliable-timeout", ++ "STUN Reliable Timeout", ++ "STUN timeout in msecs of the initial binding requests used for " ++ "a reliable timer.", ++ 20, 99999, ++ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ + /* install signals */ + + /** +@@ -1187,6 +1260,18 @@ nice_agent_get_property ( + g_value_set_boolean (value, agent->force_relay); + break; + ++ case PROP_STUN_MAX_RETRANSMISSIONS: ++ g_value_set_uint (value, agent->stun_max_retransmissions); ++ break; ++ ++ case PROP_STUN_INITIAL_TIMEOUT: ++ g_value_set_uint (value, agent->stun_initial_timeout); ++ break; ++ ++ case PROP_STUN_RELIABLE_TIMEOUT: ++ g_value_set_uint (value, agent->stun_reliable_timeout); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +@@ -1374,6 +1459,18 @@ nice_agent_set_property ( + agent->force_relay = g_value_get_boolean (value); + break; + ++ case PROP_STUN_MAX_RETRANSMISSIONS: ++ agent->stun_max_retransmissions = g_value_get_uint (value); ++ break; ++ ++ case PROP_STUN_INITIAL_TIMEOUT: ++ agent->stun_initial_timeout = g_value_get_uint (value); ++ break; ++ ++ case PROP_STUN_RELIABLE_TIMEOUT: ++ agent->stun_reliable_timeout = g_value_get_uint (value); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 5501c2b..14fdcd9 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -888,8 +888,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) + agent, buf_len, p->keepalive.stun_message.buffer); + + if (buf_len > 0) { +- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&p->keepalive.timer, ++ agent->stun_initial_timeout, ++ agent->stun_max_retransmissions); + + agent->media_after_tick = FALSE; + +@@ -1116,8 +1117,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand) + } + + if (buffer_len > 0) { +- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&cand->timer, ++ cand->agent->stun_initial_timeout, ++ cand->agent->stun_max_retransmissions); + + /* send the refresh */ + agent_socket_send (cand->nicesock, &cand->server, +@@ -2171,11 +2173,11 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + + if (buffer_len > 0) { + if (nice_socket_is_reliable(pair->sockptr)) { +- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); ++ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); + } else { + stun_timer_start (&pair->timer, + priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ agent->stun_max_retransmissions); + } + + /* TCP-ACTIVE candidate must create a new socket before sending +@@ -2340,7 +2342,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { + stun_timer_start (&p->timer, + priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ agent->stun_max_retransmissions); + p->timer_restarted = TRUE; + } + } +diff --git a/agent/discovery.c b/agent/discovery.c +index 7a890a0..4cc99c2 100644 +--- a/agent/discovery.c ++++ b/agent/discovery.c +@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) + + if (buffer_len > 0) { + if (nice_socket_is_reliable (cand->nicesock)) { +- stun_timer_start_reliable (&cand->timer, +- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); ++ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); + } else { +- stun_timer_start (&cand->timer, 200, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&cand->timer, ++ agent->stun_initial_timeout, ++ agent->stun_max_retransmissions); + } + + /* send the conncheck */ +diff --git a/stun/usages/timer.h b/stun/usages/timer.h +index e74353b..097e75b 100644 +--- a/stun/usages/timer.h ++++ b/stun/usages/timer.h +@@ -132,7 +132,11 @@ struct stun_timer_s { + * The default intial timeout to use for the timer + * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most + * cases as it is also what is used by SIP style VoIP when sending A-Law and +- * mu-Law audio, so 200ms should be hyper safe. ++ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout ++ * of 200ms, a default of 7 transmissions, the last timeout will be ++ * 16 * 200ms, and we expect to receive a response from the stun server ++ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial ++ * stun request has been sent. + */ + #define STUN_TIMER_DEFAULT_TIMEOUT 200 + +-- +2.13.6 + + +From 7a2c1edf502849a868b6f1026e8e2c343dee4ded Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 6 Jun 2016 22:24:50 +0200 +Subject: [PATCH 23/70] conncheck: update selected pair when nominated flag is + set + +This modifies commit 8f1f615. It is better focused to update the +selected pair just after its nominated flag has been set. We also keep +the code homogeneous with other places, where the call to +priv_update_selected_pair() immediately follows the setting of +pair->nominated. Moreover in priv_update_check_list_state_for_ready(), +we would call priv_update_selected_pair() more times that necessary when +iterating on all nominated pairs. + +Differential Revision:
https://phabricator.freedesktop.org/D1125
+--- + agent/conncheck.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 14fdcd9..8c55269 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data); + static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + NiceAgent *agent, guint stream_id, NiceComponent *component, + NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); ++static gboolean priv_update_selected_pair (NiceAgent *agent, ++ NiceComponent *component, CandidateCheckPair *pair); + + static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) + { +@@ -515,6 +517,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; ++ priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); + break; /* move to the next component */ + } +@@ -1530,7 +1533,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream + ++valid; + if (p->nominated == TRUE) { + ++nominated; +- priv_update_selected_pair (agent, component, p); + } + } + } +-- +2.13.6 + + +From 3a58ba6120b188d78c5709e0349c0346bfa21c1a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 1 Feb 2016 11:10:21 +0100 +Subject: [PATCH 24/70] conncheck: peer reflexive candidates are not paired + +This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning +Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive +Candidates", where discovered candidates do not cause the creation +of new pairs to be checked. + +Differential Revision:
https://phabricator.freedesktop.org/D805
+--- + agent/conncheck.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 8c55269..cdf1025 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1787,6 +1787,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone + + g_assert (remote != NULL); + ++ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates", ++ * the agent does not pair this candidate with any local candidates. ++ */ ++ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && ++ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) ++ { ++ return added; ++ } ++ + for (i = component->local_candidates; i ; i = i->next) { + NiceCandidate *local = i->data; + +@@ -1821,6 +1830,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC + + g_assert (local != NULL); + ++ /* ++ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive ++ * Candidates", the peer reflexive candidate is not paired ++ * with other remote candidates ++ */ ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && ++ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) ++ { ++ return added; ++ } ++ + for (i = component->remote_candidates; i ; i = i->next) { + + NiceCandidate *remote = i->data; +-- +2.13.6 + + +From a602ff57aae6a6afdeab843954c48e6fb5d82d31 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:02:45 +0200 +Subject: [PATCH 25/70] conncheck: fix pair state transition when successful + response is received + +According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a +successful check should go to state succeeded, not only the valid +pair built in section 7.1.3.2.2. + +Differential Revision:
https://phabricator.freedesktop.org/D810
+--- + agent/conncheck.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index cdf1025..7fc2a1d 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2654,7 +2654,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + } + else { + if (!local_cand) { +- if (!agent->force_relay) ++ if (!agent->force_relay) { ++ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1. ++ * "Discovering Peer Reflexive Candidates" ++ */ + local_cand = discovery_add_peer_reflexive_candidate (agent, + stream->id, + component->id, +@@ -2662,8 +2665,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + sockptr, + local_candidate, + remote_candidate); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p", ++ agent, local_cand); ++ } + } + + /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 +@@ -2671,7 +2675,12 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (local_cand) + new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, + local_cand, p); +- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); ++ /* step: The agent sets the state of the pair that *generated* the check to ++ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" ++ */ ++ p->state = NICE_CHECK_SUCCEEDED; ++ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", ++ agent, p, new_pair); + } + + /* note: this is same as "adding to VALID LIST" in the spec +-- +2.13.6 + + +From 0636f9addc041cf93c4ff4eaa351b1768d48a32e Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 13:12:48 +0200 +Subject: [PATCH 26/70] conncheck: implement ice regular nomination method + +This patch implements Regular Nomation as described in RFC5245 +8.1.1.1. The controlling agent lets valid pairs accumulate, and +decides which pair to recheck with the use-candidate attribute set. +priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated +pair when acting as a STUN server, and +priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to +update the nominated pair when acting as a STUN client. A new +property is also added to the agent to control the nomination +mode, which can be regular of aggressive, with default value +set to aggressive. + +Two new flags are introduced in the CandidateCheckPair structure: + +- use_candidate_on_next_check indicates the STUN client to add the + use-candidate attribute when the pair will be checked. At this + time, the nominated flag has not been set on this pair yet. + +- mark_nominated_on_response_arrival indicates the STUN server + to nominate the pair when its succesfull response to a + previous triggered check will arrive (7.2.1.5, item #2) + +Differential Revision:
https://phabricator.freedesktop.org/D811
+--- + agent/Makefile.am | 23 ++++ + agent/agent-priv.h | 8 ++ + agent/agent.c | 59 +++++++++ + agent/agent.h | 43 ++++++- + agent/conncheck.c | 178 +++++++++++++++++++++++++++- + agent/conncheck.h | 2 + + configure.ac | 1 + + docs/reference/libnice/libnice-sections.txt | 2 + + 8 files changed, 309 insertions(+), 7 deletions(-) + +diff --git a/agent/Makefile.am b/agent/Makefile.am +index b585393..915f312 100644 +--- a/agent/Makefile.am ++++ b/agent/Makefile.am +@@ -22,6 +22,12 @@ if WINDOWS + AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP + endif + ++BUILT_SOURCES = \ ++ agent-enum-types.h \ ++ agent-enum-types.c ++ ++CLEANFILES += $(BUILT_SOURCES) ++ + noinst_LTLIBRARIES = libagent.la + + libagent_la_SOURCES = \ +@@ -54,6 +60,23 @@ libagent_la_SOURCES = \ + outputstream.c \ + $(BUILT_SOURCES) + ++agent-enum-types.h: agent.h Makefile ++ $(AM_V_GEN)$(GLIB_MKENUMS) \ ++ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ ++ --fprod "/* enumerations from \"@filename@\" */\n" \ ++ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ ++ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \ ++ $(addprefix $(srcdir)/,agent.h) > $@ ++ ++agent-enum-types.c: agent.h Makefile agent-enum-types.h ++ $(AM_V_GEN)$(GLIB_MKENUMS) \ ++ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \ ++ --fprod "\n/* enumerations from \"@filename@\" */" \ ++ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \ ++ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ ++ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \ ++ $(addprefix $(srcdir)/,agent.h) > $@ ++ + libagent_la_LIBADD = \ + $(top_builddir)/random/libnice-random.la \ + $(top_builddir)/socket/libsocket.la \ +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 162ea63..3384180 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -115,6 +115,13 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + + #define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ + ++/* A convenient macro to test if the agent is compatible with RFC5245 ++ * or OC2007R2. Specifically these two modes share the support ++ * of the regular or aggressive nomination mode */ ++#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \ ++ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ ++ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +@@ -134,6 +141,7 @@ struct _NiceAgent + guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ + guint stun_initial_timeout; /* property: stun initial timeout, RTO */ + guint stun_reliable_timeout; /* property: stun reliable timeout */ ++ NiceNominationMode nomination_mode; /* property: Nomination mode */ + + GSList *local_addresses; /* list of NiceAddresses for local + interfaces */ +diff --git a/agent/agent.c b/agent/agent.c +index 25d7886..577a7e0 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -73,6 +73,7 @@ + #include "interfaces.h" + + #include "pseudotcp.h" ++#include "agent-enum-types.h" + + /* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b + * wide. */ +@@ -116,6 +117,7 @@ enum + PROP_STUN_MAX_RETRANSMISSIONS, + PROP_STUN_INITIAL_TIMEOUT, + PROP_STUN_RELIABLE_TIMEOUT, ++ PROP_NOMINATION_MODE, + }; + + +@@ -440,6 +442,24 @@ nice_agent_class_init (NiceAgentClass *klass) + G_PARAM_READWRITE)); + + /** ++ * NiceAgent:nomination-mode: ++ * ++ * The nomination mode used in the ICE specification for describing ++ * the selection of valid pairs to be used upstream. ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * Since: UNRELEASED ++ */ ++ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, ++ g_param_spec_enum ( ++ "nomination-mode", ++ "ICE nomination mode", ++ "Nomination mode used in the ICE specification for describing " ++ "the selection of valid pairs to be used upstream", ++ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** + * NiceAgent:proxy-ip: + * + * The proxy server IP used to bypass a proxy firewall +@@ -1096,6 +1116,7 @@ nice_agent_init (NiceAgent *agent) + agent->stun_server_port = DEFAULT_STUN_PORT; + agent->controlling_mode = TRUE; + agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; ++ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; + + agent->discovery_list = NULL; + agent->discovery_unsched_items = 0; +@@ -1144,6 +1165,23 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) + } + + ++NICEAPI_EXPORT NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ gboolean reliable, ++ NiceNominationMode nomination) ++{ ++ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, ++ "compatibility", compat, ++ "main-context", ctx, ++ "reliable", reliable, ++ "nomination-mode", nomination, ++ NULL); ++ ++ return agent; ++} ++ ++ + static void + nice_agent_get_property ( + GObject *object, +@@ -1190,6 +1228,10 @@ nice_agent_get_property ( + /* XXX: should we prune the list of already existing checks? */ + break; + ++ case PROP_NOMINATION_MODE: ++ g_value_set_enum (value, agent->nomination_mode); ++ break; ++ + case PROP_PROXY_IP: + g_value_set_string (value, agent->proxy_ip); + break; +@@ -1394,6 +1436,10 @@ nice_agent_set_property ( + agent->max_conn_checks = g_value_get_uint (value); + break; + ++ case PROP_NOMINATION_MODE: ++ agent->nomination_mode = g_value_get_enum (value); ++ break; ++ + case PROP_PROXY_IP: + g_free (agent->proxy_ip); + agent->proxy_ip = g_value_dup_string (value); +@@ -3298,6 +3344,19 @@ static gboolean priv_add_remote_candidate ( + username, password, priority); + } + ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ /* note: If there are TCP candidates for a media stream, ++ * a controlling agent MUST use the regular selection algorithm, ++ * RFC 6544, sect 8, "Concluding ICE Processing" ++ */ ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE && ++ transport != NICE_CANDIDATE_TRANSPORT_UDP) { ++ nice_debug ("Agent %p : we have TCP candidates, switching back " ++ "to regular nomination mode", agent); ++ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR; ++ } ++ } ++ + if (base_addr) + candidate->base_addr = *base_addr; + +diff --git a/agent/agent.h b/agent/agent.h +index 47c4d5a..6e233c6 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -377,6 +377,26 @@ typedef enum + NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP, + } NiceProxyType; + ++/** ++ * NiceNominationMode: ++ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode ++ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode ++ * ++ * An enum to specity the kind of nomination mode to use by ++ * the agent, as described in RFC 5245. Two modes exists, ++ * regular and aggressive. They differ by the way the controlling ++ * agent chooses to put the USE-CANDIDATE attribute in its STUN ++ * messages. The aggressive mode is supposed to nominate a pair ++ * faster, than the regular mode, potentially causing the nominated ++ * pair to change until the connection check completes. ++ * ++ * Since: UNRELEASED ++ */ ++typedef enum ++{ ++ NICE_NOMINATION_MODE_REGULAR = 0, ++ NICE_NOMINATION_MODE_AGGRESSIVE, ++} NiceNominationMode; + + /** + * NiceAgentRecvFunc: +@@ -429,6 +449,28 @@ NiceAgent * + nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + + /** ++ * nice_agent_new_full: ++ * @ctx: The Glib Mainloop Context to use for timers ++ * @compat: The compatibility mode of the agent ++ * @reliable: The reliability mode of the agent ++ * @nomination: The nomination mode of the agent ++ * ++ * Create a new #NiceAgent with parameters that must be be defined at ++ * construction time. ++ * The returned object must be freed with g_object_unref() ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * Since: UNRELEASED ++ * ++ * Returns: The new agent GObject ++ */ ++NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ gboolean reliable, ++ NiceNominationMode nomination); ++ ++/** + * nice_agent_add_local_address: + * @agent: The #NiceAgent Object + * @addr: The address to listen to +@@ -447,7 +489,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + gboolean + nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr); + +- + /** + * nice_agent_add_stream: + * @agent: The #NiceAgent Object +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 7fc2a1d..6827e6e 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -502,7 +502,62 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + if (s_nominated < stream->n_components && + s_waiting_for_nomination) { + keep_timer_going = TRUE; +- if (agent->controlling_mode) { ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && ++ agent->controlling_mode && ++ ((waiting == 0 && s_inprogress == 0) || ++ (s_succeeded + s_discovered) >= 5 * stream->n_components)){ ++ /* ICE 8.1.1.1 Regular nomination ++ * we choose to nominate the valid pair if ++ * there is no pair left waiting or in-progress or ++ * if there are at least 5 valid pairs per stream on average. ++ * ++ * This is the "stopping criterion" described in 8.1.1.1, and is ++ * a "local optimization" between accumulating more valid pairs, ++ * and limiting the time spent waiting for in-progress connections ++ * checks until they finally fail. ++ */ ++ GSList *component_item; ++ ++ for (component_item = stream->components; component_item; ++ component_item = component_item->next) { ++ NiceComponent *component = component_item->data; ++ gboolean already_done = FALSE; ++ ++ /* verify that the choice of the pair to be nominated ++ * has not already been done ++ */ ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ if (p->component_id == component->id && ++ p->use_candidate_on_next_check) { ++ already_done = TRUE; ++ break; ++ } ++ } ++ ++ /* choose a pair to be nominated in the list of valid ++ * pairs, and add it to the triggered checks list ++ */ ++ if (!already_done) { ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ /* note: highest priority item selected (list always sorted) */ ++ if (p->component_id == component->id && ++ !p->nominated && ++ !p->use_candidate_on_next_check && ++ p->valid) { ++ nice_debug ("Agent %p : restarting check %p with " ++ "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->use_candidate_on_next_check = TRUE; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ break; /* move to the next component */ ++ } ++ } ++ } ++ } ++ } ++ } else if (agent->controlling_mode) { + GSList *component_item; + + for (component_item = stream->components; component_item; +@@ -1571,10 +1626,40 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + + g_assert (component); + ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && ++ agent->controlling_mode) ++ return; ++ + /* step: search for at least one nominated pair */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *pair = i->data; +- if (pair->local == localcand && pair->remote == remotecand) { ++ if (pair->local == localcand && pair->remote == remotecand && ++ NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ /* ICE, 7.2.1.5. Updating the Nominated Flag */ ++ /* note: TCP candidates typically produce peer reflexive ++ * candidate, generating a "discovered" pair that can be ++ * nominated. ++ */ ++ if (pair->valid) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated", ++ agent, pair, pair->foundation); ++ pair->nominated = TRUE; ++ priv_update_selected_pair (agent, component, pair); ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) { ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (pair->state == NICE_CHECK_IN_PROGRESS) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is in-progress, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } ++ } else if (pair->local == localcand && pair->remote == remotecand) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); + pair->nominated = TRUE; + if (pair->valid) { +@@ -2174,7 +2259,35 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + pair->prflx_priority, controlling); + } + +- if (cand_use) ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ { ++ /* We are doing regular nomination, so we set the use-candidate ++ * attrib, when the controlling agent decided which valid pair to ++ * resend with this flag in priv_conn_check_tick_stream() ++ */ ++ cand_use = pair->use_candidate_on_next_check; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(regular nomination).", agent, G_STRFUNC, cand_use); ++ break; ++ } ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ { ++ /* We are doing aggressive nomination, we set the use-candidate ++ * attrib in every check we send, when we are the controlling ++ * agent, RFC 5245, 8.1.1.2 ++ */ ++ cand_use = controlling; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(aggressive nomination).", agent, G_STRFUNC, cand_use); ++ break; ++ } ++ default: ++ /* Nothing to do. */ ++ break; ++ } ++ } else if (cand_use) + pair->nominated = controlling; + + if (uname_len > 0) { +@@ -2781,12 +2894,66 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + local_candidate, remote_candidate); + } + +- ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ + if (!ok_pair) + ok_pair = p; + + /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the + Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, control=1, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(aggressive nomination, control=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ default: ++ /* Nothing to do */ ++ break; ++ } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, control=0, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; ++ } ++ } ++ } ++ + if (ok_pair->nominated == TRUE) { + priv_update_selected_pair (agent, component, ok_pair); + priv_print_conn_check_lists (agent, G_STRFUNC, +@@ -3668,8 +3835,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + stun_usage_ice_conncheck_use_candidate (&req); + uint32_t priority = stun_usage_ice_conncheck_priority (&req); + +- if (agent->controlling_mode || +- agent->compatibility == NICE_COMPATIBILITY_GOOGLE || ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || + agent->compatibility == NICE_COMPATIBILITY_MSN || + agent->compatibility == NICE_COMPATIBILITY_OC2007) + use_candidate = TRUE; +diff --git a/agent/conncheck.h b/agent/conncheck.h +index c204475..0f988de 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -87,6 +87,8 @@ struct _CandidateCheckPair + gboolean nominated; + gboolean timer_restarted; + gboolean valid; ++ gboolean use_candidate_on_next_check; ++ gboolean mark_nominated_on_response_arrival; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +diff --git a/configure.ac b/configure.ac +index 98bbc08..6c106ff 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -57,6 +57,7 @@ AC_PROG_CC + AM_PROG_AR + LT_PREREQ([2.2.6]) + LT_INIT([dlopen win32-dll disable-static]) ++AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) + + # Check Operating System + AC_MSG_CHECKING([operating system]) +diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt +index 88a6cd2..a481106 100644 +--- a/docs/reference/libnice/libnice-sections.txt ++++ b/docs/reference/libnice/libnice-sections.txt +@@ -5,6 +5,7 @@ NiceAgent + NiceComponentState + NiceComponentType + NiceProxyType ++NiceNominationMode + NiceCompatibility + NiceAgentRecvFunc + NiceInputMessage +@@ -12,6 +13,7 @@ NiceOutputMessage + NICE_AGENT_MAX_REMOTE_CANDIDATES + nice_agent_new + nice_agent_new_reliable ++nice_agent_new_full + nice_agent_add_local_address + nice_agent_set_port_range + nice_agent_add_stream +-- +2.13.6 + + +From 4497d9b7afaaea7124db4a2cd13546d9366b5986 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Wed, 22 Jun 2016 15:44:39 +0200 +Subject: [PATCH 27/70] test-nomination: added a new test for the nomination + mode + +Differential Revision:
https://phabricator.freedesktop.org/D1107
+--- + tests/Makefile.am | 5 +- + tests/test-nomination.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 267 insertions(+), 1 deletion(-) + create mode 100644 tests/test-nomination.c + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 62d5d64..b623764 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -55,7 +55,8 @@ check_PROGRAMS = \ + test-icetcp \ + test-credentials \ + test-turn \ +- test-drop-invalid ++ test-drop-invalid \ ++ test-nomination + + dist_check_SCRIPTS = \ + check-test-fullmode-with-stun.sh \ +@@ -131,6 +132,8 @@ test_turn_LDADD = $(COMMON_LDADD) + + test_drop_invalid_LDADD = $(COMMON_LDADD) + ++test_nomination_LDADD = $(COMMON_LDADD) ++ + test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) + test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) + +diff --git a/tests/test-nomination.c b/tests/test-nomination.c +new file mode 100644 +index 0000000..b5a5e5f +--- /dev/null ++++ b/tests/test-nomination.c +@@ -0,0 +1,263 @@ ++#include <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#include <gio/gio.h> ++#include <agent.h> ++ ++static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static guint global_components_ready = 0; ++static gboolean global_lagent_gathering_done = FALSE; ++static gboolean global_ragent_gathering_done = FALSE; ++static int global_lagent_cands = 0; ++static int global_ragent_cands = 0; ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)buf; ++ ++ /* ++ * Lets ignore stun packets that got through ++ */ ++ if (len < 8) ++ return; ++ if (strncmp ("12345678", buf, 8)) ++ return; ++ ++ if (component_id != 1) ++ return; ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_gathering_done = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_gathering_done = TRUE; ++} ++ ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ gboolean ready_to_connected = FALSE; ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) { ++ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_lagent_state[component_id - 1] = state; ++ } else if (GPOINTER_TO_UINT (data) == 2) { ++ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_ragent_state[component_id - 1] = state; ++ } ++ ++ if (state == NICE_COMPONENT_STATE_READY) ++ global_components_ready++; ++ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) ++ global_components_ready--; ++ g_assert (state != NICE_COMPONENT_STATE_FAILED); ++ ++ g_debug ("test-nomination: checks READY %u.", global_components_ready); ++} ++ ++static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, ++ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ ++global_lagent_cands; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ ++global_ragent_cands; ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL, *i; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++} ++ ++static void set_credentials (NiceAgent *lagent, guint lstream, ++ NiceAgent *ragent, guint rstream) ++{ ++ gchar *ufrag = NULL, *password = NULL; ++ ++ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++} ++ ++static void ++run_test(NiceNominationMode l_nomination_mode, ++ NiceNominationMode r_nomination_mode) ++{ ++ NiceAgent *lagent, *ragent; /* agent's L and R */ ++ const gchar *localhost; ++ NiceAddress localaddr; ++ guint ls_id, rs_id; ++ gulong timer_id; ++ ++ localhost = "127.0.0.1"; ++ ++ /* step: initialize variables modified by the callbacks */ ++ global_components_ready = 0; ++ global_lagent_gathering_done = FALSE; ++ global_ragent_gathering_done = FALSE; ++ global_lagent_cands = global_ragent_cands = 0; ++ ++ lagent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ FALSE, /* reliable */ ++ l_nomination_mode); ++ ++ ragent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ FALSE, /* reliable */ ++ r_nomination_mode); ++ ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ nice_agent_set_software (lagent, "Test-nomination, Left Agent"); ++ nice_agent_set_software (ragent, "Test-nomination, Right Agent"); ++ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ if (!nice_address_set_from_string (&localaddr, localhost)) ++ g_assert_not_reached (); ++ nice_agent_add_local_address (lagent, &localaddr); ++ nice_agent_add_local_address (ragent, &localaddr); ++ ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); ++ ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ ++ ls_id = nice_agent_add_stream (lagent, 1); ++ rs_id = nice_agent_add_stream (ragent, 1); ++ g_assert (ls_id > 0); ++ g_assert (rs_id > 0); ++ ++ /* Gather candidates and test nice_agent_set_port_range */ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); ++ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); ++ ++ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'..."); ++ while (!global_lagent_gathering_done) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_lagent_gathering_done == TRUE); ++ while (!global_ragent_gathering_done) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_ragent_gathering_done == TRUE); ++ ++ set_credentials (lagent, ls_id, ragent, rs_id); ++ ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); ++ ++ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || ++ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); ++ ++ nice_agent_remove_stream (lagent, ls_id); ++ nice_agent_remove_stream (ragent, rs_id); ++ ++ g_source_remove (timer_id); ++ ++ g_clear_object(&lagent); ++ g_clear_object(&ragent); ++} ++ ++static void ++regular (void) ++{ ++ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR); ++} ++ ++static void ++aggressive (void) ++{ ++ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE); ++} ++ ++static void ++mixed_ra (void) ++{ ++ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE); ++} ++ ++static void ++mixed_ar (void) ++{ ++ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int ret; ++ ++ g_test_init (&argc, &argv, NULL); ++ ++ g_test_add_func ("/nice/nomination/regular", regular); ++ g_test_add_func ("/nice/nomination/aggressive", aggressive); ++ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra); ++ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar); ++ ++ ret = g_test_run (); ++ ++ return ret; ++} +-- +2.13.6 + + +From 58d061df8f5425dc1add9c6030a2f891ebda4616 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 7 Mar 2016 16:35:09 +0100 +Subject: [PATCH 28/70] conncheck: update pair valid property selectively + +With this patch, we fix a corner case when the succeeded pair is a +peer-reflexive candidate pair, that already has been discovered +previously, In this case, the current pair -p- should not be marked +valid, because the valid flag is already set on the discovered pair. + +Differential Revision:
https://phabricator.freedesktop.org/D1124
+--- + agent/conncheck.c | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 6827e6e..ef8df68 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2760,6 +2760,13 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + } + + if (new_pair) { ++ /* note: when new_pair is distinct from p, it means new_pair is a ++ * previously discovered peer-reflexive candidate pair, so we don't ++ * set the valid flag on p in this case, because the valid flag is ++ * already set on the discovered pair. ++ */ ++ if (new_pair == p) ++ p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + priv_conn_check_unfreeze_related (agent, stream, p); +@@ -2788,6 +2795,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (local_cand) + new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, + local_cand, p); ++ /* note: this is same as "adding to VALID LIST" in the spec ++ text */ ++ if (new_pair) ++ new_pair->valid = TRUE; + /* step: The agent sets the state of the pair that *generated* the check to + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ +@@ -2796,12 +2807,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + agent, p, new_pair); + } + +- /* note: this is same as "adding to VALID LIST" in the spec +- text */ +- if (new_pair) { +- new_pair->valid = TRUE; ++ if (new_pair && new_pair->valid) + nice_component_add_valid_candidate (component, remote_candidate); +- } ++ + + return new_pair; + } +-- +2.13.6 + + +From 15c0546f624113b8c0546a1f883a48bff7020f1b Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 17:06:32 +0200 +Subject: [PATCH 29/70] conncheck: improve the selection of the pairs to be + checked + +This patch aims to implement more closely the algorithm described +in RFC 5245 indicating how pairs are transitionned from state Frozen +to Waiting. This is described in 7.1.3.2 when a check succeeded, and +correspond to modifications in function priv_conn_check_unfreeze_related(). +This is also described in 5.7.4 when defining the initial state of the +pairs in a conncheck, and correspond to modifications in function +priv_conn_check_unfreeze_next(). + +This patch introduces the notion of active and frozen check list. It +allows us to define the timer restranmission delay as described in 16.1. + +Another modification in priv_conn_check_tick_unlocked() is that every +stream in handled consecutively, and in an independant way. The pacing +was previously of a single STUN request emitted per callback, it is now +of a triggered check per callback OR a single STUN per callback AND per +stream per callback. + +The description of ordinary checks per stream in 5.8 is detailled in +function priv_conn_check_tick_stream(), and a remaining of the code +used to nominate a pair by the controlling agent is put in a dedicated +function priv_conn_check_tick_stream_nominate() + +Differential Revision:
https://phabricator.freedesktop.org/D813
+--- + agent/conncheck.c | 535 ++++++++++++++++++++++++++++++++++++++---------------- + agent/stream.c | 21 --- + agent/stream.h | 3 - + 3 files changed, 381 insertions(+), 178 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index ef8df68..6b1b7e3 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -212,6 +212,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) + } + + /* ++ * Check if the conncheck list if Active according to ++ * ICE spec, 5.7.4 (Computing States) ++ * ++ * note: the ICE spec in unclear about that, but the check list should ++ * be considered active when there is at least a pair in Waiting state ++ * OR a pair in In-Progress state. ++ */ ++static gboolean ++priv_is_checklist_active (NiceStream *stream) ++{ ++ GSList *i; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS) ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* ++ * Check if the conncheck list if Frozen according to ++ * ICE spec, 5.7.4 (Computing States) ++ */ ++static gboolean ++priv_is_checklist_frozen (NiceStream *stream) ++{ ++ GSList *i; ++ ++ if (stream->conncheck_list == NULL) ++ return FALSE; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state != NICE_CHECK_FROZEN) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* ++ * Check if all components of the stream have ++ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.) ++ */ ++static gboolean ++priv_all_components_have_valid_pair (NiceStream *stream) ++{ ++ guint i; ++ GSList *j; ++ ++ for (i = 1; i <= stream->n_components; i++) { ++ for (j = stream->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (p->component_id == i && p->valid) ++ break; ++ } ++ if (j == NULL) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* ++ * Check if the foundation in parameter matches the foundation ++ * of a valid pair in the conncheck list [of stream] (used for ICE spec, ++ * 7.1.3.2.3, point 2.) ++ */ ++static gboolean ++priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream) ++{ ++ GSList *i; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->valid && ++ strncmp (p->foundation, foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* + * Finds the next connectivity check in WAITING state. + */ + static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list) +@@ -220,7 +303,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + + /* note: list is sorted in priority order to first waiting check has + * the highest priority */ +- + for (i = conn_check_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; + if (p->state == NICE_CHECK_WAITING) +@@ -231,6 +313,74 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + } + + /* ++ * Finds the next connectivity check in FROZEN state. ++ */ ++static CandidateCheckPair * ++priv_conn_check_find_next_frozen (GSList *conn_check_list) ++{ ++ GSList *i; ++ ++ /* note: list is sorted in priority order to first frozen check has ++ * the highest priority */ ++ for (i = conn_check_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_FROZEN) ++ return p; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Returns the number of check lists of the agent ++ */ ++static guint ++priv_number_of_check_lists (NiceAgent *agent) ++{ ++ guint n = 0; ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (stream->conncheck_list != NULL) ++ n++; ++ } ++ return n; ++} ++ ++/* ++ * Returns the number of active check lists of the agent ++ */ ++static guint ++priv_number_of_active_check_lists (NiceAgent *agent) ++{ ++ guint n = 0; ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) ++ if (priv_is_checklist_active (i->data)) ++ n++; ++ return n; ++} ++ ++/* ++ * Returns the first stream of the agent having a Frozen ++ * connection check list ++ */ ++static NiceStream * ++priv_find_first_frozen_check_list (NiceAgent *agent) ++{ ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (priv_is_checklist_frozen (stream)) ++ return stream; ++ } ++ return NULL; ++} ++ ++/* + * Initiates a new connectivity check for a ICE candidate pair. + * + * @return TRUE on success, FALSE on error +@@ -248,58 +398,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * + /* + * Unfreezes the next connectivity check in the list. Follows the + * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec +- * (ID-19), with some exceptions (see comments in code). ++ * (RFC5245) + * + * See also sect 7.1.2.2.3 (Updating Pair States), and + * priv_conn_check_unfreeze_related(). + * + * @return TRUE on success, and FALSE if no frozen candidates were found. + */ +-static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) ++static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream) + { +- CandidateCheckPair *pair = NULL; + GSList *i, *j; +- +- /* XXX: the unfreezing is implemented a bit differently than in the +- * current ICE spec, but should still be interoperate: +- * - checks are not grouped by foundation +- * - one frozen check is unfrozen (lowest component-id, highest +- * priority) +- */ ++ GSList *found_list = NULL; ++ gboolean result = FALSE; + + priv_print_conn_check_lists (agent, G_STRFUNC, NULL); + +- for (i = agent->streams; i; i = i->next) { +- NiceStream *stream = i->data; +- guint64 max_frozen_priority = 0; ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p1 = i->data; ++ CandidateCheckPair *pair = NULL; ++ guint lowest_component_id = stream->n_components + 1; ++ guint64 highest_priority = 0; + ++ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp)) ++ continue; ++ found_list = g_slist_prepend (found_list, p1->foundation); + + for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *p = j->data; +- +- /* XXX: the prio check could be removed as the pairs are sorted +- * already */ +- +- if (p->state == NICE_CHECK_FROZEN) { +- if (p->priority > max_frozen_priority) { +- max_frozen_priority = p->priority; +- pair = p; +- } ++ CandidateCheckPair *p2 = i->data; ++ if (strncmp (p2->foundation, p1->foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { ++ if (p2->component_id < lowest_component_id || ++ (p2->component_id == lowest_component_id && ++ p2->priority > highest_priority)) { ++ pair = p2; ++ lowest_component_id = p2->component_id; ++ highest_priority = p2->priority; ++ } + } + } + +- if (pair) +- break; +- } +- +- if (pair) { +- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); +- return TRUE; ++ if (pair) { ++ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", ++ agent, pair, pair->stream_id, pair->component_id, pair->foundation); ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ result = TRUE; ++ } + } +- +- return FALSE; ++ g_slist_free (found_list); ++ return result; + } + + /* +@@ -316,7 +463,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) + static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) + { + GSList *i, *j; +- guint unfrozen = 0; + + g_assert (ok_check); + g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); +@@ -336,40 +482,59 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); + p->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- ++unfrozen; + } + } + } + + /* step: perform the step (2) of 'Updating Pair States' */ + stream = agent_find_stream (agent, ok_check->stream_id); +- if (nice_stream_all_components_ready (stream)) { +- /* step: unfreeze checks from other streams */ ++ if (priv_all_components_have_valid_pair (stream)) { + for (i = agent->streams; i ; i = i->next) { ++ /* the agent examines the check list for each other ++ * media stream in turn ++ */ + NiceStream *s = i->data; +- for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *p = j->data; +- +- if (p->stream_id == s->id && +- p->stream_id != ok_check->stream_id) { +- if (p->state == NICE_CHECK_FROZEN && +- strcmp (p->foundation, ok_check->foundation) == 0) { ++ if (s->id == ok_check->stream_id) ++ continue; ++ if (priv_is_checklist_active (s)) { ++ /* checklist is Active ++ */ ++ for (j = s->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (p->state == NICE_CHECK_FROZEN && ++ priv_foundation_matches_a_valid_pair (p->foundation, stream)) { + nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); + p->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- ++unfrozen; +- +- } +- } ++ } ++ } ++ } else if (priv_is_checklist_frozen (s)) { ++ /* checklist is Frozen ++ */ ++ gboolean match_found = FALSE; ++ /* check if there is one pair in the check list whose ++ * foundation matches a pair in the valid list under ++ * consideration ++ */ ++ for (j = s->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { ++ match_found = TRUE; ++ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); ++ p->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ } ++ } ++ ++ if (!match_found) { ++ /* set the pair with the lowest component ID ++ * and highest priority to Waiting ++ */ ++ priv_conn_check_unfreeze_next (agent, s); ++ } + } +- /* note: only unfreeze check from one stream at a time */ +- if (unfrozen) +- break; + } + } +- +- if (unfrozen == 0) +- priv_conn_check_unfreeze_next (agent); + } + + static void +@@ -400,14 +565,13 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP + * + * @return will return FALSE when no more pending timers. + */ +-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) ++static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) + { + gboolean keep_timer_going = FALSE; +- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, +- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; +- guint frozen = 0, waiting = 0; +- GSList *i, *k; ++ GSList *i; ++ CandidateCheckPair *pair; + ++ /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; + +@@ -451,7 +615,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + p->next_tick = *now; + g_time_val_add (&p->next_tick, timeout * 1000); + +- *stun_transmitted = TRUE; + return TRUE; + } + case STUN_USAGE_TIMER_RETURN_SUCCESS: +@@ -471,7 +634,57 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + } + } + } ++ } + ++ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" ++ * note: This code is executed when the triggered checks list is ++ * empty, and when no STUN message has been sent (pacing constraint) ++ */ ++ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); ++ if (pair) { ++ priv_conn_check_initiate (agent, pair); ++ return TRUE; ++ } ++ ++ /* note: this is unclear in the ICE spec, but a check list in Frozen ++ * state (where all pairs are in Frozen state) is not supposed to ++ * change its state by an ordinary check, but only by the success of ++ * checks in other check lists, in priv_conn_check_unfreeze_related(). ++ * The underlying idea is to concentrate the checks on a single check ++ * list initially. ++ */ ++ if (priv_is_checklist_frozen (stream)) ++ return keep_timer_going; ++ ++ /* step: ordinary check continued, if there's no pair in the waiting ++ * state, pick a pair in the frozen state ++ */ ++ pair = priv_conn_check_find_next_frozen (stream->conncheck_list); ++ if (pair) { ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ priv_conn_check_initiate (agent, pair); ++ return TRUE; ++ } ++ return keep_timer_going; ++} ++ ++static gboolean ++priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) ++{ ++ gboolean keep_timer_going = FALSE; ++ guint s_inprogress = 0; ++ guint s_succeeded = 0; ++ guint s_discovered = 0; ++ guint s_nominated = 0; ++ guint s_waiting_for_nomination = 0; ++ guint s_valid = 0; ++ guint frozen = 0; ++ guint waiting = 0; ++ GSList *i, *k; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; + if (p->state == NICE_CHECK_FROZEN) + ++frozen; + else if (p->state == NICE_CHECK_IN_PROGRESS) +@@ -504,13 +717,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + keep_timer_going = TRUE; + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { + if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && +- agent->controlling_mode && +- ((waiting == 0 && s_inprogress == 0) || +- (s_succeeded + s_discovered) >= 5 * stream->n_components)){ ++ agent->controlling_mode) { ++#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2 + /* ICE 8.1.1.1 Regular nomination +- * we choose to nominate the valid pair if +- * there is no pair left waiting or in-progress or +- * if there are at least 5 valid pairs per stream on average. ++ * we choose to nominate the valid pair of a component if ++ * - there is no pair left frozen, waiting or in-progress, or ++ * - if there are at least two valid pairs, or ++ * - if there is at least one valid pair of type HOST-HOST + * + * This is the "stopping criterion" described in 8.1.1.1, and is + * a "local optimization" between accumulating more valid pairs, +@@ -523,36 +736,63 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + component_item = component_item->next) { + NiceComponent *component = component_item->data; + gboolean already_done = FALSE; ++ gboolean stopping_criterion = FALSE; ++ guint p_valid = 0; ++ guint p_frozen = 0; ++ guint p_waiting = 0; ++ guint p_inprogress = 0; ++ guint p_host_host_valid = 0; + + /* verify that the choice of the pair to be nominated + * has not already been done + */ + for (k = stream->conncheck_list; k ; k = k->next) { + CandidateCheckPair *p = k->data; +- if (p->component_id == component->id && +- p->use_candidate_on_next_check) { +- already_done = TRUE; +- break; ++ if (p->component_id == component->id) { ++ if (p->use_candidate_on_next_check) ++ already_done = TRUE; ++ if (p->state == NICE_CHECK_FROZEN) ++ p_frozen++; ++ else if (p->state == NICE_CHECK_WAITING) ++ p_waiting++; ++ else if (p->state == NICE_CHECK_IN_PROGRESS) ++ p_inprogress++; ++ if (p->valid) ++ p_valid++; ++ if (p->valid && ++ p->local->type == NICE_CANDIDATE_TYPE_HOST && ++ p->remote->type == NICE_CANDIDATE_TYPE_HOST) ++ p_host_host_valid++; + } + } + +- /* choose a pair to be nominated in the list of valid +- * pairs, and add it to the triggered checks list ++ if (already_done) ++ continue; ++ ++ stopping_criterion = ++ (p_host_host_valid > 0 || ++ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS || ++ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0)); ++ ++ if (!stopping_criterion) ++ continue; ++ ++ /* when the stopping criterion is satisfied, we choose ++ * a pair to be nominated in the list of valid pairs, ++ * and add it to the triggered checks list + */ +- if (!already_done) { +- for (k = stream->conncheck_list; k ; k = k->next) { +- CandidateCheckPair *p = k->data; +- /* note: highest priority item selected (list always sorted) */ +- if (p->component_id == component->id && +- !p->nominated && +- !p->use_candidate_on_next_check && +- p->valid) { +- nice_debug ("Agent %p : restarting check %p with " +- "USE-CANDIDATE attrib (regular nomination)", agent, p); +- p->use_candidate_on_next_check = TRUE; +- priv_add_pair_to_triggered_check_queue (agent, p); +- break; /* move to the next component */ +- } ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ /* note: highest priority item selected (list always sorted) */ ++ if (p->component_id == component->id && ++ !p->nominated && ++ !p->use_candidate_on_next_check && ++ p->valid) { ++ nice_debug ("Agent %p : restarting check %p with " ++ "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->use_candidate_on_next_check = TRUE; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ break; /* move to the next component */ + } + } + } +@@ -615,70 +855,55 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + { + CandidateCheckPair *pair = NULL; + gboolean keep_timer_going = FALSE; +- gboolean res; +- /* note: we try to only generate a single stun transaction per timer +- * callback, to respect some pacing of STUN transaction, as per +- * appendix B.1 of ICE spec. +- */ +- gboolean stun_transmitted = FALSE; + GSList *i, *j; + GTimeVal now; + +- /* step: process ongoing STUN transactions */ + g_get_current_time (&now); + +- for (j = agent->streams; j; j = j->next) { +- NiceStream *stream = j->data; +- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); +- if (res) +- keep_timer_going = res; +- if (stun_transmitted) +- return TRUE; +- } +- +- /* step: first initiate a conncheck with a pair from the triggered list */ +- pair = priv_get_pair_from_triggered_check_queue (agent); +- +- if (pair) { +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a pair from triggered check list"); +- priv_conn_check_initiate (agent, pair); ++ /* the conncheck really starts when we have built ++ * a connection check list for each stream ++ */ ++ if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) + return TRUE; +- } + +- /* step: when the triggered list is empty, +- * find the highest priority waiting check and send it */ +- for (i = agent->streams; i ; i = i->next) { +- NiceStream *stream = i->data; +- +- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); +- if (pair) +- break; ++ /* configure the initial state of the check lists of the agent ++ * as described in ICE spec, 5.7.4 ++ * ++ * if all pairs in all check lists are in frozen state, then ++ * we are in the initial state (5.7.4, point 1.) ++ */ ++ if (priv_number_of_active_check_lists (agent) == 0) { ++ /* set some pairs of the first stream in the waiting state ++ * ICE spec, 5.7.4, point 2. ++ * ++ * note: we adapt the ICE spec here, by selecting the first ++ * frozen check list, which is not necessarily the check ++ * list of the first stream (the first stream may be completed) ++ */ ++ NiceStream *stream = priv_find_first_frozen_check_list (agent); ++ if (stream) ++ priv_conn_check_unfreeze_next (agent, stream); + } + ++ /* step: perform a test from the triggered checks list, ++ * ICE spec, 5.8 "Scheduling Checks" ++ */ ++ pair = priv_get_pair_from_triggered_check_queue (agent); ++ + if (pair) { + priv_conn_check_initiate (agent, pair); + return TRUE; + } + +- /* step: when there's no pair in the Waiting state, +- * unfreeze a new pair and check it ++ /* step: process ongoing STUN transactions and ++ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" + */ +- priv_conn_check_unfreeze_next (agent); +- + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +- +- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); +- if (pair) +- break; +- } +- +- if (pair) { +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a pair in Waiting state"); +- priv_conn_check_initiate (agent, pair); +- return TRUE; ++ if (priv_conn_check_tick_stream (stream, agent, &now)) ++ keep_timer_going = TRUE; ++ if (priv_conn_check_tick_stream_nominate (stream, agent)) ++ keep_timer_going = TRUE; + } + + /* step: stop timer if no work left */ +@@ -2169,30 +2394,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, + + /* Implement the computation specific in RFC 5245 section 16 */ + +-static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) ++static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream) + { +- GSList *item1, *item2; ++ GSList *i; + guint waiting_and_in_progress = 0; ++ guint n = 0; + unsigned int rto = 0; + +- +- for (item1 = agent->streams; item1; item1 = item1->next) { +- NiceStream *stream = item1->data;; +- for (item2 = stream->conncheck_list; item2; item2 = item2->next) { +- CandidateCheckPair *pair = item2->data; +- +- if (pair->state == NICE_CHECK_IN_PROGRESS || +- pair->state == NICE_CHECK_WAITING) +- waiting_and_in_progress++; +- } ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_IN_PROGRESS || ++ p->state == NICE_CHECK_WAITING) ++ waiting_and_in_progress++; + } + +- rto = agent->timer_ta * waiting_and_in_progress; ++ n = priv_number_of_active_check_lists (agent); ++ rto = agent->timer_ta * n * waiting_and_in_progress; + + /* We assume non-reliable streams are RTP, so we use 100 as the max */ +- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", ++ nice_debug ("Agent %p : timer set to %dms, " ++ "waiting+in_progress=%d, nb_active=%d", + agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), +- waiting_and_in_progress); ++ waiting_and_in_progress, n); + if (agent->reliable) + return MAX (rto, 500); + else +@@ -2312,7 +2535,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); + } else { + stun_timer_start (&pair->timer, +- priv_compute_conncheck_timer (agent), ++ priv_compute_conncheck_timer (agent, stream), + agent->stun_max_retransmissions); + } + +@@ -2477,7 +2700,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p->timer_restarted ? "no" : "yes"); + if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { + stun_timer_start (&p->timer, +- priv_compute_conncheck_timer (agent), ++ priv_compute_conncheck_timer (agent, stream), + agent->stun_max_retransmissions); + p->timer_restarted = TRUE; + } +@@ -2769,7 +2992,6 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); +- priv_conn_check_unfreeze_related (agent, stream, p); + nice_component_add_valid_candidate (component, remote_candidate); + } + else { +@@ -2894,7 +3116,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + g_assert_not_reached (); + nice_debug ("Agent %p : Mapped address not found." + " conncheck %p SUCCEEDED.", agent, p); +- priv_conn_check_unfreeze_related (agent, stream, p); + nice_component_add_valid_candidate (component, p->remote); + } else { + ok_pair = priv_process_response_check_for_reflexive (agent, +@@ -2902,6 +3123,12 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + local_candidate, remote_candidate); + } + ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ + /* Note: this assignment helps to reduce the numbers of cases + * to be tested. If ok_pair and p refer to distinct pairs, it + * means that ok_pair is a discovered peer reflexive one, +diff --git a/agent/stream.c b/agent/stream.c +index 8121e12..533ff15 100644 +--- a/agent/stream.c ++++ b/agent/stream.c +@@ -104,27 +104,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) + } + + /* +- * Returns true if all components of the stream are either +- * 'CONNECTED' or 'READY' (connected plus nominated). +- */ +-gboolean +-nice_stream_all_components_ready (NiceStream *stream) +-{ +- GSList *i; +- +- for (i = stream->components; i; i = i->next) { +- NiceComponent *component = i->data; +- if (component && +- !(component->state == NICE_COMPONENT_STATE_CONNECTED || +- component->state == NICE_COMPONENT_STATE_READY)) +- return FALSE; +- } +- +- return TRUE; +-} +- +- +-/* + * Initialized the local crendentials for the stream. + */ + void +diff --git a/agent/stream.h b/agent/stream.h +index f9188cb..954ba66 100644 +--- a/agent/stream.h ++++ b/agent/stream.h +@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); + void + nice_stream_close (NiceStream *stream); + +-gboolean +-nice_stream_all_components_ready (NiceStream *stream); +- + NiceComponent * + nice_stream_find_component_by_id (NiceStream *stream, guint id); + +-- +2.13.6 + + +From ead3453d04fc70865d176ab073636f8b9078cbbc Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:20:38 +0200 +Subject: [PATCH 30/70] conncheck: invoke the debug dump in more places + +Differential Revision:
https://phabricator.freedesktop.org/D1123
+--- + agent/conncheck.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 6b1b7e3..2d2224d 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -642,6 +642,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + */ + pair = priv_conn_check_find_next_waiting (stream->conncheck_list); + if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Waiting state"); + priv_conn_check_initiate (agent, pair); + return TRUE; + } +@@ -661,6 +663,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + */ + pair = priv_conn_check_find_next_frozen (stream->conncheck_list); + if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Frozen state"); + pair->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, pair); + priv_conn_check_initiate (agent, pair); +@@ -891,6 +895,8 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + pair = priv_get_pair_from_triggered_check_queue (agent); + + if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair from triggered check list"); + priv_conn_check_initiate (agent, pair); + return TRUE; + } +-- +2.13.6 + + +From 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 17:59:27 +0200 +Subject: [PATCH 31/70] conncheck: improve triggered check of in-progress pairs + +This patch update the way triggered checks of in-progress pairs are +handled, according to ICE spec, section 7.2.1.4. Previously the same +connection check was retransmitted with an updated timeout. This causes +problems when a controlling role switch occurs in this time frame. +This is the reason why a new connection check must be generated +reflecting the updated role. We introduce a new flag "recheck_on_timeout" +in the pair indicating that the pair must be rechecked at the next timer +expiration. + +Differential Revision:
https://phabricator.freedesktop.org/D875
+--- + agent/conncheck.c | 88 +++++++++++++++++++++++++++++++++++++++++++++---------- + agent/conncheck.h | 2 +- + 2 files changed, 74 insertions(+), 16 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 2d2224d..3a489fe 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -558,6 +558,37 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP + } + + /* ++ * Function that resubmits a new connection check, after a previous ++ * check in in-progress state got cancelled due to an incoming stun ++ * request matching this same pair ++ * ++ * @return will return TRUE if the pair is scheduled for recheck ++ */ ++static gboolean ++priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) ++{ ++ if (p->recheck_on_timeout) { ++ g_assert (p->state == NICE_CHECK_IN_PROGRESS); ++ /* this cancelled pair may have the flag 'mark nominated ++ * on response arrival' set, we want to keep it, because ++ * this is needed to nominate this pair in aggressive ++ * nomination, when the agent is in controlled mode. ++ * ++ * this cancelled pair may also have the flag 'use candidate ++ * on next check' set, that we want to preserve too. ++ */ ++ nice_debug ("Agent %p : pair %p was cancelled, " ++ "triggering a new connection check", agent, p); ++ p->recheck_on_timeout = FALSE; ++ p->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* + * Helper function for connectivity check timer callback that + * runs through the stream specific part of the state machine. + * +@@ -584,8 +615,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + switch (stun_timer_refresh (&p->timer)) { + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + { +- /* case: error, abort processing */ + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; ++ ++ /* case: error, abort processing */ + nice_address_to_string (&p->local->addr, tmpbuf1); + nice_address_to_string (&p->remote->addr, tmpbuf2); + nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); +@@ -600,8 +640,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + } + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: + { +- /* case: not ready, so schedule a new timeout */ + unsigned int timeout = stun_timer_remainder (&p->timer); ++ ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; ++ ++ /* case: not ready, so schedule a new timeout */ + nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " + "(timeout %dms, delay=%dms, retrans=%d).", + agent, p, timeout, p->timer.delay, p->timer.retransmissions); +@@ -642,6 +691,12 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + */ + pair = priv_conn_check_find_next_waiting (stream->conncheck_list); + if (pair) { ++ /* remove the pair from the triggered check list if needed. This ++ * may happen with the cancelled pair, that's just been added ++ * in state waiting to the triggered check list above in the ++ * same function. ++ */ ++ priv_remove_pair_from_triggered_check_queue (agent, pair); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair in Waiting state"); + priv_conn_check_initiate (agent, pair); +@@ -794,6 +849,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->valid) { + nice_debug ("Agent %p : restarting check %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->recheck_on_timeout = FALSE; + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); + break; /* move to the next component */ +@@ -816,6 +872,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; ++ p->recheck_on_timeout = FALSE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); + break; /* move to the next component */ +@@ -2697,19 +2754,20 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p->state == NICE_CHECK_FROZEN) + priv_add_pair_to_triggered_check_queue (agent, p); + else if (p->state == NICE_CHECK_IN_PROGRESS) { +- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), +- * we should cancel the existing one, instead we reset our timer, so +- * we'll resend the exiting transactions faster if needed...? :P +- */ +- nice_debug ("Agent %p : check already in progress, " +- "restarting the timer again?: %s ..", agent, +- p->timer_restarted ? "no" : "yes"); +- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { +- stun_timer_start (&p->timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); +- p->timer_restarted = TRUE; +- } ++ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" ++ * we cancel the in-progress transaction, and after the ++ * retransmission timeout, we create a new connectivity check ++ * for that pair. The controlling role of this new check may ++ * be different from the role of this cancelled check. ++ */ ++ if (!nice_socket_is_reliable (p->sockptr)) { ++ nice_debug ("Agent %p : check already in progress, " ++ "cancelling this check..", agent); ++ /* this flag will determine the action at the retransmission ++ * timeout of the stun timer ++ */ ++ p->recheck_on_timeout = TRUE; ++ } + } + else if (p->state == NICE_CHECK_SUCCEEDED || + p->state == NICE_CHECK_DISCOVERED) { +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 0f988de..785a6cd 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -85,10 +85,10 @@ struct _CandidateCheckPair + gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; + NiceCheckState state; + gboolean nominated; +- gboolean timer_restarted; + gboolean valid; + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; ++ gboolean recheck_on_timeout; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +-- +2.13.6 + + +From 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:25:16 +0200 +Subject: [PATCH 32/70] conncheck: link succeeded and discovered pairs + +When the agent has the role of the stun server, is in controlled mode, +and receives a pair with the "use-candidate" attribute set, it must find +a matching succeded or discovered pair in its conncheck list. This is +described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1. +When a matching pair is in succeeded state, the agent must nominate the +valid pair (a discovered pair) constructed from section 7.1.3.2.2, +that's been created from this succeeded one. To make this lookup, we +introduce a new "discovered_pair" member of the CandidateCheckPair +struct, that links the succeeded pair, and its discovered pair +if any. + +Differential Revision:
https://phabricator.freedesktop.org/D878
+--- + agent/conncheck.c | 7 +++++++ + agent/conncheck.h | 1 + + 2 files changed, 8 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 3a489fe..99cb6d2 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1928,6 +1928,12 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + * candidate, generating a "discovered" pair that can be + * nominated. + */ ++ if (pair->state == NICE_CHECK_SUCCEEDED && ++ pair->discovered_pair != NULL) { ++ pair = pair->discovered_pair; ++ g_assert (pair->state == NICE_CHECK_DISCOVERED); ++ } ++ + if (pair->valid) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", + agent, pair, pair->foundation); +@@ -2936,6 +2942,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->remote = parent_pair->remote; + pair->sockptr = local_cand->sockptr; + pair->state = NICE_CHECK_DISCOVERED; ++ parent_pair->discovered_pair = pair; + nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 785a6cd..dd47ebe 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -89,6 +89,7 @@ struct _CandidateCheckPair + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; + gboolean recheck_on_timeout; ++ struct _CandidateCheckPair *discovered_pair; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +-- +2.13.6 + + +From 9103a5f2e184211fc160d1d3070ce4d043c71ff0 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 18:16:26 +0200 +Subject: [PATCH 33/70] conncheck: use the right pair when retriggering a check + +This patch completes the previous patch by adding a link back from the +discovered pair, to the parent pair that generated this check. This link +is needed by the ICE spec, to comply with section 8.1.1.1, "Regular +nomination", where the check to be retriggered is the initial check that +caused the discovery of the valid pair. When the valid pair is a +peer-reflexive pair, the retriggered check must target the succeeded +pair, and not the valid discovered pair. + +Differential Revision:
https://phabricator.freedesktop.org/D879
+--- + agent/conncheck.c | 21 ++++++++++++++++++--- + agent/conncheck.h | 1 + + 2 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 99cb6d2..79685df 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -847,6 +847,16 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + !p->nominated && + !p->use_candidate_on_next_check && + p->valid) { ++ /* According a ICE spec, sect 8.1.1.1. "Regular ++ * Nomination", we enqueue the check that produced this ++ * valid pair. When this pair has been discovered, we want ++ * to test its parent pair instead. ++ */ ++ if (p->succeeded_pair != NULL) { ++ g_assert (p->state == NICE_CHECK_DISCOVERED); ++ p = p->succeeded_pair; ++ } ++ g_assert (p->state == NICE_CHECK_SUCCEEDED); + nice_debug ("Agent %p : restarting check %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); + p->recheck_on_timeout = FALSE; +@@ -2754,6 +2764,11 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * tcp-active we don't want to retrigger a check on a pair that + * was FAILED when a peer-reflexive pair was created */ + ++ if (p->succeeded_pair != NULL) { ++ g_assert (p->state == NICE_CHECK_DISCOVERED); ++ p = p->succeeded_pair; ++ } ++ + nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); + + if (p->state == NICE_CHECK_WAITING || +@@ -2775,8 +2790,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p->recheck_on_timeout = TRUE; + } + } +- else if (p->state == NICE_CHECK_SUCCEEDED || +- p->state == NICE_CHECK_DISCOVERED) { ++ else if (p->state == NICE_CHECK_SUCCEEDED) { + nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); + /* note: this is a bit unsure corner-case -- let's do the + same state update as for processing responses to our own checks */ +@@ -2943,6 +2957,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->sockptr = local_cand->sockptr; + pair->state = NICE_CHECK_DISCOVERED; + parent_pair->discovered_pair = pair; ++ pair->succeeded_pair = parent_pair; + nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; +@@ -4163,7 +4178,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + + pair = priv_conn_check_add_for_candidate_pair_matched (agent, + stream->id, component, local_candidate, remote_candidate, +- NICE_CHECK_DISCOVERED); ++ NICE_CHECK_SUCCEEDED); + if (pair) { + pair->valid = TRUE; + } +diff --git a/agent/conncheck.h b/agent/conncheck.h +index dd47ebe..c07fb22 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -90,6 +90,7 @@ struct _CandidateCheckPair + gboolean mark_nominated_on_response_arrival; + gboolean recheck_on_timeout; + struct _CandidateCheckPair *discovered_pair; ++ struct _CandidateCheckPair *succeeded_pair; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +-- +2.13.6 + + +From 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:30:04 +0200 +Subject: [PATCH 34/70] conncheck: fix a nomination corner case + +This patch add two supplementary cases, not covered by the ICE spec, +sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent +receives a STUN request with the USE-CANDIDATE flag, for a pair that is +in the waiting state. We consider that this case is similar to the +in-progress state, and should be handled in the same way. We also accept +when the pair is in frozen state. This latter case happens in the +new-dribble test, when an agent replays incoming early connchecks. + +Differential Revision:
https://phabricator.freedesktop.org/D880
+--- + agent/conncheck.c | 35 +++++++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 79685df..4f4af40 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1963,6 +1963,29 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + "will be nominated on response receipt.", + agent, pair, pair->foundation); + } ++ /* note: this case is not covered by the ICE spec, 7.2.1.5, ++ * "Updating the Nominated Flag", but a pair in waiting state ++ * deserves the same treatment than a pair in-progress. ++ */ ++ if (pair->state == NICE_CHECK_WAITING) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is waiting, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } ++ /* note: this case is not covered by the ICE spec, 7.2.1.5, ++ * "Updating the Nominated Flag" either, but a pair in frozen ++ * state, and in the triggered check list should also be ++ * considered like a pair in-progress. This case happens with ++ * the new-dribble test, when an agent replays incoming early ++ * connchecks. ++ */ ++ if (pair->state == NICE_CHECK_FROZEN) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is frozen, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } + } else if (pair->local == localcand && pair->remote == remotecand) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); + pair->nominated = TRUE; +@@ -2703,17 +2726,25 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + "is %" G_GUINT64_FORMAT, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ ++ /* note: this case is not covered by the ICE spec, 8.1.2 ++ * "Updating States", but a pair in waiting state which will be ++ * nominated on response receipt should be treated the same way ++ * that an in-progress pair. ++ */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + if (p->component_id == component_id) { + if (p->state == NICE_CHECK_FROZEN || +- p->state == NICE_CHECK_WAITING) { ++ (p->state == NICE_CHECK_WAITING && ++ !p->mark_nominated_on_response_arrival)) { + p->state = NICE_CHECK_CANCELLED; + nice_debug ("Agent XXX : pair %p state CANCELED", p); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- if (p->state == NICE_CHECK_IN_PROGRESS) { ++ if (p->state == NICE_CHECK_IN_PROGRESS || ++ (p->state == NICE_CHECK_WAITING && ++ p->mark_nominated_on_response_arrival)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { + p->stun_message.buffer = NULL; +-- +2.13.6 + + +From afd8d41bb34afb3864e838ef79026ae4ef15c0d4 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:32:49 +0200 +Subject: [PATCH 35/70] conncheck: new pairs never have the nominated flag + preset + +This patch disables the possibility to set the nominated flag of a +candidate pair at creation time. This possibility was used when a new +pair is created from a new peer reflexive remote candidate, when the +agent is in controlled mode, and an stun request with USE-CANDIDATE is +received. In this case, since previous commit "conncheck: fix a +nomination corner case", we set the nominated flag when the stun +response of this new pair will arrive, and not before. Consequently, +this flag is no longer required when the pair is created. + +Differential Revision:
https://phabricator.freedesktop.org/D881
+--- + agent/conncheck.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 4f4af40..3cd0424 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -65,7 +65,7 @@ + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); + static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); + static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); +-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); ++static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); + static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); + static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, + guint component_id, NiceCandidate *remote, NiceCandidate *local, +@@ -1573,7 +1573,8 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); + if (icheck->use_candidate) + priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, pair->remote); + } + } + } +@@ -1716,7 +1717,8 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + + if (icheck->use_candidate) + priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, candidate); + } + } + } +@@ -2043,7 +2045,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority) + */ + static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + guint stream_id, NiceComponent *component, NiceCandidate *local, +- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) ++ NiceCandidate *remote, NiceCheckState initial_state) + { + NiceStream *stream; + CandidateCheckPair *pair; +@@ -2081,7 +2083,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr)); + } +- pair->nominated = use_candidate; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); + +@@ -2127,7 +2128,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + agent, local->foundation, remote->foundation, + stream_id, component->id); + pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, +- initial_state, FALSE); ++ initial_state); + if (component->state == NICE_COMPONENT_STATE_CONNECTED || + component->state == NICE_COMPONENT_STATE_READY) { + agent_signal_component_state_change (agent, +@@ -2774,9 +2775,8 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + * @param component the check is related to + * @param local_socket socket from which the inbound check was received + * @param remote_cand remote candidate from which the inbound check was sent +- * @param use_candidate whether the original check had USE-CANDIDATE attribute set + */ +-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) ++static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand) + { + GSList *i; + NiceCandidate *local = NULL; +@@ -2872,7 +2872,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + + if (i) { + nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); +- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate); ++ priv_add_new_check_pair (agent, stream->id, component, ++ local, remote_cand, NICE_CHECK_WAITING); + return TRUE; + } + else { +@@ -2926,7 +2927,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + + if (rcand) { + /* note: upon successful check, make the reserve check immediately */ +- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); ++ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); + + if (use_candidate) + priv_mark_pair_nominated (agent, stream, component, lcand, rcand); +-- +2.13.6 + + +From 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 12:56:28 +0200 +Subject: [PATCH 36/70] conncheck: update the pair state in triggered check + list + +With this patch, we update the state of the pair to waiting when +it is put in the triggered check queue. We also take care to call +priv_schedule_triggered_check() before priv_mark_pair_nominated() +so a pair to be rechecked and put on the triggered check queue +will have a unique state to be tested in the following call to +priv_mark_pair_nominated() when evaluating its nomination attributes. + +Differential Revision:
https://phabricator.freedesktop.org/D883
+--- + agent/conncheck.c | 33 +++++++++------------------------ + 1 file changed, 9 insertions(+), 24 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 3cd0424..9950970 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -183,6 +183,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa + { + g_assert (pair); + ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); + if (agent->triggered_check_queue == NULL || + g_slist_find (agent->triggered_check_queue, pair) == NULL) + agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); +@@ -580,8 +582,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + nice_debug ("Agent %p : pair %p was cancelled, " + "triggering a new connection check", agent, p); + p->recheck_on_timeout = FALSE; +- p->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } +@@ -1571,10 +1571,10 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + if (nice_address_equal (&icheck->from, &pair->remote->addr) && + icheck->local_socket == pair->sockptr) { + nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); + priv_schedule_triggered_check (agent, stream, component, + icheck->local_socket, pair->remote); ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); + } + } + } +@@ -1715,10 +1715,10 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + else + conn_check_add_for_candidate (agent, stream->id, component, candidate); + +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); + priv_schedule_triggered_check (agent, stream, component, + icheck->local_socket, candidate); ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); + } + } + } +@@ -1967,7 +1967,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + } + /* note: this case is not covered by the ICE spec, 7.2.1.5, + * "Updating the Nominated Flag", but a pair in waiting state +- * deserves the same treatment than a pair in-progress. ++ * deserves the same treatment than a pair in-progress. A pair ++ * can be in waiting state as the result of being enqueued in ++ * the triggered check list for example. + */ + if (pair->state == NICE_CHECK_WAITING) { + pair->mark_nominated_on_response_arrival = TRUE; +@@ -1975,19 +1977,6 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + "will be nominated on response receipt.", + agent, pair, pair->foundation); + } +- /* note: this case is not covered by the ICE spec, 7.2.1.5, +- * "Updating the Nominated Flag" either, but a pair in frozen +- * state, and in the triggered check list should also be +- * considered like a pair in-progress. This case happens with +- * the new-dribble test, when an agent replays incoming early +- * connchecks. +- */ +- if (pair->state == NICE_CHECK_FROZEN) { +- pair->mark_nominated_on_response_arrival = TRUE; +- nice_debug ("Agent %p : pair %p (%s) is frozen, " +- "will be nominated on response receipt.", +- agent, pair, pair->foundation); +- } + } else if (pair->local == localcand && pair->remote == remotecand) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); + pair->nominated = TRUE; +@@ -2926,9 +2915,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + } + + if (rcand) { +- /* note: upon successful check, make the reserve check immediately */ + priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); +- + if (use_candidate) + priv_mark_pair_nominated (agent, stream, component, lcand, rcand); + } +@@ -3345,9 +3332,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + + p->stun_message.buffer = NULL; + p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_WAITING; + priv_add_pair_to_triggered_check_queue (agent, p); +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); + } + trans_found = TRUE; + } else { +-- +2.13.6 + + +From 11d4e37a030eb144a355dc26c705ef5aa5a975a7 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Fri, 1 Apr 2016 17:31:44 +0200 +Subject: [PATCH 37/70] conncheck: remove a useless pair recheck + +This exception to the ICE spec is no longer needed: when a pair is in +the succeeded state, there is no needed to recheck it again upon +reception of an incoming stun request on it. + +Differential Revision:
https://phabricator.freedesktop.org/D884
+--- + agent/conncheck.c | 17 ----------------- + 1 file changed, 17 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 9950970..95e2556 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2820,23 +2820,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * that causes the ready -> connected transition. + */ + priv_update_check_list_state_for_ready (agent, stream, component); +- +- /* note: this new check is required by the new-dribble test, +- * when early icheck on the peer controlled agent causes an +- * incoming stun request to an already succeeded (and +- * nominated) pair on the controlling agent. If the +- * controlling agent doesn't retrigger a check with +- * USE-CANDIDATE=1, the peer agent has no way to nominate it. +- * +- * This behavior differs from ICE spec 7.2.1.4 +- */ +- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || +- agent->compatibility == NICE_COMPATIBILITY_WLM2009 || +- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && +- agent->controlling_mode) { +- priv_add_pair_to_triggered_check_queue (agent, p); +- conn_check_schedule_next(agent); +- } + } else if (p->state == NICE_CHECK_FAILED) { + /* 7.2.1.4 Triggered Checks + * If the state of the pair is Failed, it is changed to Waiting +-- +2.13.6 + + +From 8fa648a15a6700d08165fe97a09f5c068abae1e6 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 11 Apr 2016 13:13:51 +0200 +Subject: [PATCH 38/70] conncheck: dont cancel a pair for triggered check + +This patch adds another supplementary "corner" case, not covered by the +ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in +the triggered check list should be considered like an in-progress pair, +and cancelled only if its priority is lower than the priority of the +nominated pair. This is required in some aggressive nomination +situations for both peers to select the same pair, having the highest +priority. + +Differential Revision:
https://phabricator.freedesktop.org/D933
+--- + agent/conncheck.c | 48 ++++++++++++++++++++++++++++++++---------------- + 1 file changed, 32 insertions(+), 16 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 95e2556..79f678a 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -64,7 +64,7 @@ + + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); + static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); +-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); ++static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id); + static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); + static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); + static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, +@@ -176,6 +176,16 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + } + } + ++/* Verify if the pair is in the triggered checks list ++ */ ++ ++static gboolean ++priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) ++{ ++ g_assert (pair); ++ return (g_slist_find (agent->triggered_check_queue, pair) != NULL); ++} ++ + /* Add the pair to the triggered checks list, if not already present + */ + static void +@@ -1897,7 +1907,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream + /* Only go to READY if no checks are left in progress. If there are + * any that are kept, then this function will be called again when the + * conncheck tick timer finishes them all */ +- if (priv_prune_pending_checks (stream, component->id) == 0) { ++ if (priv_prune_pending_checks (agent, stream, component->id) == 0) { + /* Continue through the states to give client code a nice + * logical progression. See
http://phabricator.freedesktop.org/D218
for + * discussion. */ +@@ -2693,14 +2703,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + * + * @see priv_update_check_list_state_failed_components() + */ +-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) ++static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id) + { + GSList *i; + guint64 highest_nominated_priority = 0; + guint in_progress = 0; + +- nice_debug ("Agent XXX: Finding highest priority for component %d", +- component_id); ++ nice_debug ("Agent %p: Finding highest priority for component %d", ++ agent, component_id); + + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; +@@ -2712,41 +2722,47 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + } + } + +- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority " +- "is %" G_GUINT64_FORMAT, highest_nominated_priority); ++ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority " ++ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ + /* note: this case is not covered by the ICE spec, 8.1.2 + * "Updating States", but a pair in waiting state which will be + * nominated on response receipt should be treated the same way +- * that an in-progress pair. ++ * that an in-progress pair. A pair in waiting state and in ++ * the triggered check list should also be treated like an in-progress ++ * pair. + */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; ++ + if (p->component_id == component_id) { ++ gboolean like_in_progress = ++ p->mark_nominated_on_response_arrival || ++ priv_is_pair_in_triggered_check_queue (agent, p); ++ + if (p->state == NICE_CHECK_FROZEN || +- (p->state == NICE_CHECK_WAITING && +- !p->mark_nominated_on_response_arrival)) { ++ (p->state == NICE_CHECK_WAITING && !like_in_progress)) { + p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ + if (p->state == NICE_CHECK_IN_PROGRESS || +- (p->state == NICE_CHECK_WAITING && +- p->mark_nominated_on_response_arrival)) { ++ (p->state == NICE_CHECK_WAITING && like_in_progress)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { + p->stun_message.buffer = NULL; + p->stun_message.buffer_len = 0; + p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); + } else { + /* We must keep the higher priority pairs running because if a udp + * packet was lost, we might end up using a bad candidate */ +- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %" ++ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %" + G_GUINT64_FORMAT " is higher than currently nominated pair %" +- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority); ++ G_GUINT64_FORMAT, agent, ++ p, p->priority, highest_nominated_priority); + in_progress++; + } + } +-- +2.13.6 + + +From 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:04:49 +0200 +Subject: [PATCH 39/70] conncheck: try to change earlier to state ready + +We check if we can move from state connected to ready just +after a pair expired its retransmission count. This pair +will be marked failed, and will no longer be in-progress. +The number of in-progress dropping down to zero is one +of the conditions needed to make the transition to ready, +per component (and not globally as it's the case in other +locations where this check function is called). + +Differential Revision:
https://phabricator.freedesktop.org/D1117
+--- + agent/conncheck.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 79f678a..d31b77f 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -626,6 +626,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + { + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ NiceComponent *component; + + /* case: conncheck cancelled due to in-progress incoming + * check, requeing the pair, ICE spec, sect 7.2.1.4 +@@ -646,6 +647,16 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + priv_print_conn_check_lists (agent, G_STRFUNC, + ", retransmission failed"); + ++ /* perform a check if a transition state from connected to ++ * ready can be performed. This may happen here, when the last ++ * in-progress pair has expired its retransmission count ++ * in priv_conn_check_tick_stream(), which is a condition to ++ * make the transition connected to ready. ++ */ ++ if (agent_find_component (agent, p->stream_id, p->component_id, ++ NULL, &component)) ++ priv_update_check_list_state_for_ready (agent, stream, ++ component); + break; + } + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +-- +2.13.6 + + +From 59fe48517c0b7db77b99183d31fdd84b55adb5d4 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:12:16 +0200 +Subject: [PATCH 40/70] conncheck: fix a state transition case + +When a new stun request hits a valid pair, of a failed component, we may +have a transition from state failed to connected. In this situation, we +do a logical progression failed -> connecting -> connected, like we do +in function priv_update_check_list_state_for_ready() + +Similarily, when a new stun request hits a failed pair, of a failed +component, triggering a new conncheck for this pair may also cause the +component state to move back from failed to connecting state. + +Differential Revision:
https://phabricator.freedesktop.org/D1118
+--- + agent/conncheck.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index d31b77f..e1a5cf1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1973,12 +1973,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + pair->nominated = TRUE; + priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { ++ if (component->state == NICE_COMPONENT_STATE_FAILED) ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } + priv_update_check_list_state_for_ready (agent, stream, component); + } else if (pair->state == NICE_CHECK_IN_PROGRESS) { + pair->mark_nominated_on_response_arrival = TRUE; +@@ -2004,13 +2006,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + if (pair->valid) { + priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { ++ if (component->state == NICE_COMPONENT_STATE_FAILED) ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } +- + } + priv_update_check_list_state_for_ready (agent, stream, component); + } +@@ -2854,6 +2857,14 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + pair (representing a new STUN Binding request transaction), by + enqueueing the pair in the triggered check queue. */ + priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } + } + + /* note: the spec says the we SHOULD retransmit in-progress +-- +2.13.6 + + +From f19d209decac432a1597d84c3d5809d2208f7457 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:20:49 +0200 +Subject: [PATCH 41/70] conncheck: do not recheck a just succeeded pair + +We cancel the potential in-progress transaction cancellation, caused by +sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before +transmission timeout, or just after timeout, when the pair is +temporarily put on the triggered check list on the way to be +rechecked. This situation is not covered by the RFC 5245. + +Differential Revision:
https://phabricator.freedesktop.org/D1119
+--- + agent/conncheck.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index e1a5cf1..4b785b5 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3117,6 +3117,16 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (new_pair == p) + p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; ++ /* note: we cancel the potential in-progress transaction ++ * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if ++ * we receive a valid reply before transmission timeout... ++ */ ++ p->recheck_on_timeout = FALSE; ++ /* ... or just after the transmission timeout, while the pair is ++ * temporarily put on the triggered check list on the way to be ++ * be rechecked: we remove it from the list too. ++ */ ++ priv_remove_pair_from_triggered_check_queue (agent, p); + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + nice_component_add_valid_candidate (component, remote_candidate); + } +@@ -3151,6 +3161,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ + p->state = NICE_CHECK_SUCCEEDED; ++ p->recheck_on_timeout = FALSE; ++ priv_remove_pair_from_triggered_check_queue (agent, p); + nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", + agent, p, new_pair); + } +-- +2.13.6 + + +From d516fca1b0e0a6606afec797bdc0690104e779a9 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:32:26 +0200 +Subject: [PATCH 42/70] conncheck: adjust recheck on timeout strategy + +The pair recheck on timeout can easily cause repetitive rechecks in +a ping-pong effect, if both peers with the same behaviour try to +check the same pair almost simultaneously, and if the network rtt +is greater than the initial timer rto. The reply to the initial +stun request may arrive after the in-progress conncheck +cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation +creates a new stun request, and forgets the initial one. +The conncheck timer is restarted with the same initial value, +so the same situation happens again later. + +We choose to avoid resetting the timer in such situation. After enough +retransmissions, the timeout delay, that doubles after each timeout, +becomes longer than the rtt, and the stun reply can be handled. + +Differential Revision:
https://phabricator.freedesktop.org/D1115
+--- + agent/conncheck.c | 30 ++++++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 4b785b5..88d2534 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -591,7 +591,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + */ + nice_debug ("Agent %p : pair %p was cancelled, " + "triggering a new connection check", agent, p); +- p->recheck_on_timeout = FALSE; + priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } +@@ -2650,9 +2649,32 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + if (nice_socket_is_reliable(pair->sockptr)) { + stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); + } else { +- stun_timer_start (&pair->timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); ++ StunTimer *timer = &pair->timer; ++ ++ if (pair->recheck_on_timeout) ++ /* The pair recheck on timeout can easily cause repetitive rechecks in ++ * a ping-pong effect, if both peers with the same behaviour try to ++ * check the same pair almost simultaneously, and if the network rtt ++ * is greater than the initial timer rto. The reply to the initial ++ * stun request may arrive after the in-progress conncheck ++ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation ++ * creates a new stun request, and forgets the initial one. ++ * The conncheck timer is restarted with the same initial value, ++ * so the same situation happens again later. ++ * ++ * We choose to avoid resetting the timer in such situation. ++ * After enough retransmissions, the timeout delay becomes ++ * longer than the rtt, and the stun reply can be handled. ++ */ ++ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", ++ agent, pair, ++ timer->retransmissions, timer->max_retransmissions, ++ timer->delay - stun_timer_remainder (timer), timer->delay); ++ else ++ stun_timer_start (timer, ++ priv_compute_conncheck_timer (agent, stream), ++ agent->stun_max_retransmissions); ++ pair->recheck_on_timeout = FALSE; + } + + /* TCP-ACTIVE candidate must create a new socket before sending +-- +2.13.6 + + +From 95f8805eb7b77755337e28daf1f134587d42b35f Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 16 Jun 2016 17:32:39 +0200 +Subject: [PATCH 43/70] conncheck: remove cancelled pair state + +Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for +removal after the nomination of a pair with an higher priority, +described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They +include also pairs that overflow the conncheck list size, but this is a +somewhat more marginal situation. So we are mainly interested in the +first use case of this state. + +This state mixes two different situations, that deserve a distinct +handling : on one side, there are waiting or frozen pairs that must be +removed, this is an immediate action that doesn't need a dedicated state +for that. And on the other side, there are in-progress pairs that +should no longer be retransmitted, because another pair with a higher +priority has already been nominated. + +This patch removes the cancelled state, and adds a flag +retransmit_on_timeout to deal with this last situation. Note that this +case should not generate a triggered check, as per described in section +7.2.1.4, when the state of the pair is In-Progress or Failed, since this +pair of lower priority has no hope to replace the nominated one. + +Differential Revision:
https://phabricator.freedesktop.org/D1114
+--- + agent/conncheck.c | 142 +++++++++++++++++++++++++++++------------------------- + agent/conncheck.h | 3 +- + 2 files changed, 77 insertions(+), 68 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 88d2534..b0e2222 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -100,8 +100,6 @@ priv_state_to_gchar (NiceCheckState state) + return 'F'; + case NICE_CHECK_FROZEN: + return 'Z'; +- case NICE_CHECK_CANCELLED: +- return 'C'; + case NICE_CHECK_DISCOVERED: + return 'D'; + default: +@@ -627,6 +625,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; + NiceComponent *component; + ++timer_timeout: + /* case: conncheck cancelled due to in-progress incoming + * check, requeing the pair, ICE spec, sect 7.2.1.4 + * "Triggered Checks", "If the state of that pair is +@@ -662,6 +661,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + { + unsigned int timeout = stun_timer_remainder (&p->timer); + ++ /* case: retransmission stopped, due to the nomination of ++ * a pair with a higher priority than this in-progress pair, ++ * ICE spec, sect 8.1.2 "Updating States", item 2.2 ++ */ ++ if (!p->retransmit_on_timeout) ++ goto timer_timeout; ++ + /* case: conncheck cancelled due to in-progress incoming + * check, requeing the pair, ICE spec, sect 7.2.1.4 + * "Triggered Checks", "If the state of that pair is +@@ -1600,26 +1606,6 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + } + + +-static GSList *prune_cancelled_conn_check (GSList *conncheck_list) +-{ +- GSList *item = conncheck_list; +- +- while (item) { +- CandidateCheckPair *pair = item->data; +- GSList *next = item->next; +- +- if (pair->state == NICE_CHECK_CANCELLED) { +- conn_check_free_item (pair); +- conncheck_list = g_slist_delete_link (conncheck_list, item); +- } +- +- item = next; +- } +- +- return conncheck_list; +-} +- +- + /* + * Handle any processing steps for connectivity checks after + * remote credentials have been set. This function handles +@@ -1754,9 +1740,6 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + (GDestroyNotify) incoming_check_free); + component->incoming_checks = NULL; + } +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); + } + + /* +@@ -1764,7 +1747,7 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + * in ICE spec section 5.7.3 (ID-19). See also + * conn_check_add_for_candidate(). + */ +-static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) ++static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) + { + guint valid = 0; + guint cancelled = 0; +@@ -1772,22 +1755,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper + + while (item) { + CandidateCheckPair *pair = item->data; ++ GSList *next = item->next; + +- if (pair->state != NICE_CHECK_CANCELLED) { +- valid++; +- if (valid > upper_limit) { +- pair->state = NICE_CHECK_CANCELLED; ++ valid++; ++ if (valid > upper_limit) { ++ conn_check_free_item (pair); ++ conncheck_list = g_slist_delete_link (conncheck_list, item); + cancelled++; +- } + } +- +- item = item->next; ++ item = next; + } + + if (cancelled > 0) + nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements" + " left. Maximum connchecks allowed : %d", cancelled, valid, + upper_limit); ++ return conncheck_list; + } + + /* +@@ -2097,6 +2080,7 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + } + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); ++ pair->retransmit_on_timeout = TRUE; + + stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, + (GCompareFunc)conn_check_compare); +@@ -2106,7 +2090,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + /* implement the hard upper limit for number of + checks (see sect 5.7.3 ICE ID-19): */ + if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { +- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); ++ stream->conncheck_list = priv_limit_conn_check_list_size ( ++ stream->conncheck_list, agent->max_conn_checks); + } + + return pair; +@@ -2769,8 +2754,10 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + * the triggered check list should also be treated like an in-progress + * pair. + */ +- for (i = stream->conncheck_list; i; i = i->next) { ++ i = stream->conncheck_list; ++ while (i) { + CandidateCheckPair *p = i->data; ++ GSList *next = i->next; + + if (p->component_id == component_id) { + gboolean like_in_progress = +@@ -2779,19 +2766,20 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + + if (p->state == NICE_CHECK_FROZEN || + (p->state == NICE_CHECK_WAITING && !like_in_progress)) { +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); ++ nice_debug ("Agent %p : pair %p removed.", agent, p); ++ conn_check_free_item (p); ++ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- if (p->state == NICE_CHECK_IN_PROGRESS || ++ else if (p->state == NICE_CHECK_IN_PROGRESS || + (p->state == NICE_CHECK_WAITING && like_in_progress)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); ++ p->retransmit_on_timeout = FALSE; ++ p->recheck_on_timeout = FALSE; ++ nice_debug ("Agent %p : pair %p will not be retransmitted.", ++ agent, p); + } else { + /* We must keep the higher priority pairs running because if a udp + * packet was lost, we might end up using a bad candidate */ +@@ -2803,6 +2791,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + } + } + } ++ i = next; + } + + return in_progress; +@@ -2841,29 +2830,42 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p = p->succeeded_pair; + } + +- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); ++ nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", ++ agent, p, p->foundation, priv_state_to_gchar (p->state)); + + if (p->state == NICE_CHECK_WAITING || +- p->state == NICE_CHECK_FROZEN) ++ p->state == NICE_CHECK_FROZEN) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); ++ } + else if (p->state == NICE_CHECK_IN_PROGRESS) { + /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" + * we cancel the in-progress transaction, and after the + * retransmission timeout, we create a new connectivity check + * for that pair. The controlling role of this new check may + * be different from the role of this cancelled check. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * so there's no reason to recheck this pair, since it can in ++ * no way replace the nominated one. + */ + if (!nice_socket_is_reliable (p->sockptr)) { +- nice_debug ("Agent %p : check already in progress, " +- "cancelling this check..", agent); +- /* this flag will determine the action at the retransmission +- * timeout of the stun timer +- */ +- p->recheck_on_timeout = TRUE; ++ if (p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p will be rechecked " ++ "on stun timer timeout.", agent, p); ++ /* this flag will determine the action at the retransmission ++ * timeout of the stun timer ++ */ ++ p->recheck_on_timeout = TRUE; ++ } else ++ nice_debug ("Agent %p : pair %p won't be retransmitted.", ++ agent, p); + } + } + else if (p->state == NICE_CHECK_SUCCEEDED) { +- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); ++ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); + /* note: this is a bit unsure corner-case -- let's do the + same state update as for processing responses to our own checks */ + /* note: this update is required by the dribble test, to +@@ -2875,18 +2877,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + } else if (p->state == NICE_CHECK_FAILED) { + /* 7.2.1.4 Triggered Checks + * If the state of the pair is Failed, it is changed to Waiting +- and the agent MUST create a new connectivity check for that +- pair (representing a new STUN Binding request transaction), by +- enqueueing the pair in the triggered check queue. */ +- priv_add_pair_to_triggered_check_queue (agent, p); +- /* If the component for this pair is in failed state, move it +- * back to connecting, and reinitiate the timers ++ * and the agent MUST create a new connectivity check for that ++ * pair (representing a new STUN Binding request transaction), by ++ * enqueueing the pair in the triggered check queue. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * we apply the same strategy than with an in-progress pair ++ * above. + */ +- if (component->state == NICE_COMPONENT_STATE_FAILED) { +- agent_signal_component_state_change (agent, stream->id, +- component->id, NICE_COMPONENT_STATE_CONNECTING); +- conn_check_schedule_next (agent); +- } ++ if (p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } ++ } else ++ nice_debug ("Agent %p : pair %p won't be retransmitted.", ++ agent, p); + } + + /* note: the spec says the we SHOULD retransmit in-progress +@@ -3401,10 +3415,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + } + } + +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); +- + return trans_found; + } + +diff --git a/agent/conncheck.h b/agent/conncheck.h +index c07fb22..909d469 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -56,7 +56,6 @@ + * @NICE_CHECK_SUCCEEDED: Connection successfully checked. + * @NICE_CHECK_FAILED: No connectivity; retransmissions ceased. + * @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING. +- * @NICE_CHECK_CANCELLED: Check cancelled. + * @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list. + * + * States for checking a candidate pair. +@@ -68,7 +67,6 @@ typedef enum + NICE_CHECK_SUCCEEDED, + NICE_CHECK_FAILED, + NICE_CHECK_FROZEN, +- NICE_CHECK_CANCELLED, + NICE_CHECK_DISCOVERED, + } NiceCheckState; + +@@ -89,6 +87,7 @@ struct _CandidateCheckPair + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; + gboolean recheck_on_timeout; ++ gboolean retransmit_on_timeout; + struct _CandidateCheckPair *discovered_pair; + struct _CandidateCheckPair *succeeded_pair; + guint64 priority; +-- +2.13.6 + + +From 07366a5bca7e4818b8df29d9c7c220da8f752547 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 21 Jun 2016 21:47:42 +0200 +Subject: [PATCH 44/70] conncheck: fix the component failed transition + +This patch fixes the transition of a component from connecting to +failed, that previously occured due to the propagation of the +keep_timer_going variable, and to the final call to function +priv_update_check_list_failed_components(), after the global agent +timer was stopped. + +Previously, the code almost never entered to failed state, because the +timer was going one, as long as the number of nominated pair was not +enough, and as long as there were valid pairs not yet nominated. Even +if all pair timers were over. + +The definition of the Failed state of a conncheck list is somewhat +contradictory in the spec, depending on weather you read : + + * sect 5.7.4. "Computing States", + "Failed: In this state, the ICE checks have not completed successfully + for this media stream." + + or + + * sect 7.1.3.3. "Check List and Timer State Updates", + "If all of the pairs in the check list are now either in the Failed or + Succeeded state: If there is not a pair in the valid list for each + component of the media stream, the state of the check list is set to + Failed." + +Our understanding of the ICE spec is that, the proper way to enter failed +state instead in when all connchecks have no longer in-progress pairs. +All pairs are either in state succeeded, discovered, or failed. No timer +is still running, and we have no hope that the conncheck list changes +again, except if an unexpected STUN packet arrives later. All pairs in +frozen state is a special case, that is handled separately (sect +7.1.3.3). + +A special grace delay is added before declaring a component in state +Failed. This delay is not part of the RFC, and it is aimed to limit the +cases when a conncheck list is reactivated just after it's been declared +failed, causing a user visible transition from connecting to failed, and +back from failed to connecting again. This is also required by the test +suite, that counts exactly the number of time each state is entered, and +doesn't expect these transcient failed states to happen (frequent due to +the nature of the testsuite, less frequent in real life). + +Differential Revision:
https://phabricator.freedesktop.org/D1111
+--- + agent/agent-priv.h | 14 +++++++++++ + agent/conncheck.c | 71 +++++++++++++++++++++++++++++++++++++++++++----------- + 2 files changed, 71 insertions(+), 14 deletions(-) + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 3384180..714ecff 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -122,6 +122,18 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ + (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) + ++/* A grace period before declaring a component as failed, in msecs. This ++ * delay is added to reduce the chance to see the agent receiving new ++ * stun activity just after the conncheck list has been declared failed, ++ * reactiviting conncheck activity, and causing a (valid) state ++ * transitions like that: connecting -> failed -> connecting -> ++ * connected -> ready. ++ * Such transitions are not buggy per-se, but may break the ++ * test-suite, that counts precisely the number of time each state ++ * has been set, and doesnt expect these transcient failed states. ++ */ ++#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000 ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +@@ -176,6 +188,8 @@ struct _NiceAgent + guint16 rfc4571_expecting_length; + gboolean use_ice_udp; + gboolean use_ice_tcp; ++ ++ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ + /* XXX: add pointer to internal data struct for ABI-safe extensions */ + }; + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index b0e2222..63db471 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -709,7 +709,7 @@ timer_timeout: + } + } + } +- } ++ } + + /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" + * note: This code is executed when the triggered checks list is +@@ -795,11 +795,8 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + if (s_inprogress) + keep_timer_going = TRUE; + +- /* note: if some components have established connectivity, +- * but yet no nominated pair, keep timer going */ + if (s_nominated < stream->n_components && + s_waiting_for_nomination) { +- keep_timer_going = TRUE; + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { + if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && + agent->controlling_mode) { +@@ -888,6 +885,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->recheck_on_timeout = FALSE; + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); ++ keep_timer_going = TRUE; + break; /* move to the next component */ + } + } +@@ -911,6 +909,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->recheck_on_timeout = FALSE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); ++ keep_timer_going = TRUE; + break; /* move to the next component */ + } + } +@@ -937,6 +936,7 @@ conn_check_stop (NiceAgent *agent) + g_source_destroy (agent->conncheck_timer_source); + g_source_unref (agent->conncheck_timer_source); + agent->conncheck_timer_source = NULL; ++ agent->conncheck_timer_grace_period = 0; + } + + +@@ -1005,9 +1005,39 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + keep_timer_going = TRUE; + } + ++ /* step: if no work left and a conncheck list of a stream is still ++ * frozen, set the pairs to waiting, according to ICE SPEC, sect ++ * 7.1.3.3. "Check List and Timer State Updates" ++ */ ++ if (!keep_timer_going) { ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (priv_is_checklist_frozen (stream)) { ++ nice_debug ("Agent %p : stream %d conncheck list is still " ++ "frozen, while other lists are completed. Unfreeze it.", ++ agent, stream->id); ++ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream); ++ } ++ } ++ } ++ ++ /* note: we provide a grace period before declaring a component as ++ * failed. Components marked connected, and then ready follow another ++ * code path, and are not concerned by this grace period. ++ */ ++ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0) ++ nice_debug ("Agent %p : waiting %d msecs before checking " ++ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD); ++ ++ if (keep_timer_going) ++ agent->conncheck_timer_grace_period = 0; ++ else ++ agent->conncheck_timer_grace_period += agent->timer_ta; ++ + /* step: stop timer if no work left */ +- if (keep_timer_going != TRUE) { +- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); ++ if (!keep_timer_going && ++ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) { ++ nice_debug ("Agent %p : checking for failed components now.", agent); + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; + priv_update_check_list_failed_components (agent, stream); +@@ -1017,6 +1047,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + } + } + ++ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", conncheck timer stopped"); + +@@ -1027,9 +1058,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + + /* XXX: what to signal, is all processing now really done? */ + nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); ++ return FALSE; + } + +- return keep_timer_going; ++ return TRUE; + } + + static gboolean priv_conn_check_tick (gpointer pointer) +@@ -1810,15 +1842,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp + * Updates the check list state. + * + * Implements parts of the algorithm described in +- * ICE sect 8.1.2. "Updating States" (ID-19): if for any ++ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any + * component, all checks have been completed and have +- * failed, mark that component's state to NICE_CHECK_FAILED. ++ * failed to produce a nominated pair, mark that component's ++ * state to NICE_CHECK_FAILED. + * + * Sends a component state changesignal via 'agent'. + */ + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) + { + GSList *i; ++ gboolean completed; ++ guint nominated; + /* note: emitting a signal might cause the client + * to remove the stream, thus the component count + * must be fetched before entering the loop*/ +@@ -1842,6 +1877,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) + continue; + ++ nominated = 0; ++ completed = TRUE; + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +@@ -1849,16 +1886,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + g_assert (p->stream_id == stream->id); + + if (p->component_id == (c + 1)) { +- if (p->state != NICE_CHECK_FAILED) +- break; ++ if (p->nominated) ++ ++nominated; ++ if (p->state != NICE_CHECK_FAILED && ++ p->state != NICE_CHECK_SUCCEEDED && ++ p->state != NICE_CHECK_DISCOVERED) ++ completed = FALSE; + } + } + +- /* note: all checks have failed ++ /* note: all pairs are either failed or succeeded, and the component ++ * has not produced a nominated pair. + * Set the component to FAILED only if it actually had remote candidates + * that failed.. */ +- if (i == NULL && comp != NULL && comp->remote_candidates != NULL) +- agent_signal_component_state_change (agent, ++ if (completed && nominated == 0 && ++ comp != NULL && comp->remote_candidates != NULL) ++ agent_signal_component_state_change (agent, + stream->id, + (c + 1), /* component-id */ + NICE_COMPONENT_STATE_FAILED); +-- +2.13.6 + + +From 195db6b344fc4f9fadc39419dfeec2fc14b23fac Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Fri, 15 Jul 2016 23:31:42 +0200 +Subject: [PATCH 45/70] agent: add new pairs only for gathering streams + +At the end of the local candidate gathering process, we only create new +pairs for streams that are in gathering state. + +Other stream that may be in ready state for example, due to a +previously succeeded conncheck process, may have accumulated some +couples (local,remote) candidates that have not resulted in the creation +a new pair during this previous conncheck process, and we don't want +these new pairs to be added now, because it would generate unneeded +transition changes for a stream unconcerned by this gathering. + +Differential Revision:
https://phabricator.freedesktop.org/D1755
+--- + agent/agent.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/agent/agent.c b/agent/agent.c +index 577a7e0..e3705ed 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -2032,6 +2032,17 @@ void agent_gathering_done (NiceAgent *agent) + + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; ++ ++ /* We ignore streams not in gathering state, typically already in ++ * ready state. Such streams may have couples (local,remote) ++ * candidates that have not resulted in the creation a new pair ++ * during a previous conncheck session, and we don't want these new ++ * pairs to be added now, because it would generate unneeded ++ * transition changes for a stream unconcerned by this gathering. ++ */ ++ if (!stream->gathering) ++ continue; ++ + for (j = stream->components; j; j = j->next) { + NiceComponent *component = j->data; + +-- +2.13.6 + + +From b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 28 May 2017 22:20:36 +0200 +Subject: [PATCH 46/70] stun: fix gcc7 implicit fallthrough warning + +Differential Revision:
https://phabricator.freedesktop.org/D1754
+--- + stun/stunmessage.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/stun/stunmessage.c b/stun/stunmessage.c +index e8184c4..4cc3392 100644 +--- a/stun/stunmessage.c ++++ b/stun/stunmessage.c +@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type, + /* Only fingerprint may come after M-I */ + if (type == STUN_ATTRIBUTE_FINGERPRINT) + break; ++ return NULL; + + case STUN_ATTRIBUTE_FINGERPRINT: + /* Nothing may come after FPR */ +-- +2.13.6 + + +From c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 18 Jun 2017 10:12:58 +0200 +Subject: [PATCH 47/70] agent: remove spurious newlines + +Differential Revision:
https://phabricator.freedesktop.org/D1756
+--- + agent/agent.c | 2 +- + agent/component.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index e3705ed..27e6193 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3905,7 +3905,7 @@ agent_recv_message_unlocked ( + + nice_address_to_string (message->from, str); + nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" +- " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, ++ " %s:%d sock-type: %d", agent, stream->id, component->id, str, + nice_address_get_port (message->from), nicesock->type); + } + +diff --git a/agent/component.c b/agent/component.c +index ab665b6..6e207d3 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -1461,7 +1461,7 @@ nice_component_add_valid_candidate (NiceComponent *component, + char str[INET6_ADDRSTRLEN]; + nice_address_to_string (&candidate->addr, str); + nice_debug ("Agent %p : %d:%d Adding valid source" +- " candidate: %s:%d trans: %d\n", component->agent, ++ " candidate: %s:%d trans: %d", component->agent, + candidate->stream_id, candidate->component_id, str, + nice_address_get_port (&candidate->addr), candidate->transport); + } +-- +2.13.6 + + +From e3ddaa285e389baf3f26cfb6964919718a8f6a00 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 21 Jun 2017 16:55:32 -0400 +Subject: [PATCH 48/70] agent: Adjust the nice_agent_new_full() to use flags + +This makes it easier to read and more extensible. +--- + agent/agent.c | 9 +++++---- + agent/agent.h | 27 ++++++++++++++++++++++----- + tests/test-nomination.c | 8 ++++---- + 3 files changed, 31 insertions(+), 13 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 27e6193..8fd8ead 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -1168,14 +1168,15 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) + NICEAPI_EXPORT NiceAgent * + nice_agent_new_full (GMainContext *ctx, + NiceCompatibility compat, +- gboolean reliable, +- NiceNominationMode nomination) ++ NiceAgentOption flags) + { + NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, + "compatibility", compat, + "main-context", ctx, +- "reliable", reliable, +- "nomination-mode", nomination, ++ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE, ++ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ? ++ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE, ++ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, + NULL); + + return agent; +diff --git a/agent/agent.h b/agent/agent.h +index 6e233c6..ed6f6e4 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -399,6 +399,25 @@ typedef enum + } NiceNominationMode; + + /** ++ * NiceAgentOption: ++ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default ++ * is aggrssive mode (see #NiceNominationMode). ++ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable(). ++ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode ++ * ++ * These are options that can be passed to nice_agent_new_full(). They set ++ * various properties on the agent. Not including them sets the property to ++ * the other value. ++ * ++ * Since: UNRELEASED ++ */ ++typedef enum { ++ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, ++ NICE_AGENT_OPTION_RELIABLE = 1 << 1, ++ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, ++} NiceAgentOption; ++ ++/** + * NiceAgentRecvFunc: + * @agent: The #NiceAgent Object + * @stream_id: The id of the stream +@@ -452,13 +471,12 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + * nice_agent_new_full: + * @ctx: The Glib Mainloop Context to use for timers + * @compat: The compatibility mode of the agent +- * @reliable: The reliability mode of the agent +- * @nomination: The nomination mode of the agent ++ * @flags: Flags to set the properties + * + * Create a new #NiceAgent with parameters that must be be defined at + * construction time. + * The returned object must be freed with g_object_unref() +- * <para> See also: #NiceNominationMode </para> ++ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * + * Since: UNRELEASED + * +@@ -467,8 +485,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + NiceAgent * + nice_agent_new_full (GMainContext *ctx, + NiceCompatibility compat, +- gboolean reliable, +- NiceNominationMode nomination); ++ NiceAgentOption flags); + + /** + * nice_agent_add_local_address: +diff --git a/tests/test-nomination.c b/tests/test-nomination.c +index b5a5e5f..bf21557 100644 +--- a/tests/test-nomination.c ++++ b/tests/test-nomination.c +@@ -140,13 +140,13 @@ run_test(NiceNominationMode l_nomination_mode, + + lagent = nice_agent_new_full (NULL, + NICE_COMPATIBILITY_RFC5245, +- FALSE, /* reliable */ +- l_nomination_mode); ++ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); + + ragent = nice_agent_new_full (NULL, + NICE_COMPATIBILITY_RFC5245, +- FALSE, /* reliable */ +- r_nomination_mode); ++ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); + + g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); + g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); +-- +2.13.6 + + +From dcb0d647174416a292492f8deca86f83a2ef124c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 21 Jun 2017 17:07:17 -0400 +Subject: [PATCH 49/70] Repleace UNRELEASED with 0.1.15 + +--- + agent/agent.c | 8 ++++---- + agent/agent.h | 6 +++--- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 8fd8ead..15af9ed 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -448,7 +448,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * the selection of valid pairs to be used upstream. + * <para> See also: #NiceNominationMode </para> + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, + g_param_spec_enum ( +@@ -744,7 +744,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * to the READY state, and on the time needed to complete the GATHERING + * state. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + + g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, +@@ -769,7 +769,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * divided by two instead (RFC 5389 indicates that a customisable + * multiplier 'Rm' to 'RTO' should be used). + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + + g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, +@@ -788,7 +788,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * The initial timeout of the STUN binding requests used + * for a reliable timer. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + + g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, +diff --git a/agent/agent.h b/agent/agent.h +index ed6f6e4..520c4c5 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -390,7 +390,7 @@ typedef enum + * faster, than the regular mode, potentially causing the nominated + * pair to change until the connection check completes. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + typedef enum + { +@@ -409,7 +409,7 @@ typedef enum + * various properties on the agent. Not including them sets the property to + * the other value. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + typedef enum { + NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, +@@ -478,7 +478,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + * The returned object must be freed with g_object_unref() + * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + * + * Returns: The new agent GObject + */ +-- +2.13.6 + + +From 2c50d73b82f2ec2422a8e0ea393194486c193c64 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 10 Feb 2016 23:20:39 -0500 +Subject: [PATCH 50/70] agent: Don't crash if recv cancelled without a GError + +--- + agent/agent.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 15af9ed..e48d7f3 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -4279,7 +4279,10 @@ static gboolean + nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) + { + GError **error = user_data; +- return !g_cancellable_set_error_if_cancelled (cancellable, error); ++ ++ if (error && *error) ++ g_cancellable_set_error_if_cancelled (cancellable, error); ++ return G_SOURCE_REMOVE; + } + + static gint +-- +2.13.6 + + +From 63d273cea42def3567701ad9feab91f63cf9345f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Thu, 11 Feb 2016 22:16:48 -0500 +Subject: [PATCH 51/70] component: Use non-GClosure dummy callbacks + +GClosures are not that cheap to setup +--- + agent/component.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/agent/component.c b/agent/component.c +index 6e207d3..6eee90e 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -1005,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass) + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + } + ++static gboolean ++dummy_callback (gpointer data) ++{ ++ return G_SOURCE_CONTINUE; ++} ++ ++static void ++source_set_dummy_callback (GSource *source) ++{ ++ g_source_set_callback (source, dummy_callback, NULL, NULL); ++} ++ + static void + nice_component_init (NiceComponent *component) + { +@@ -1027,7 +1039,7 @@ nice_component_init (NiceComponent *component) + component->stop_cancellable = g_cancellable_new (); + component->stop_cancellable_source = + g_cancellable_source_new (component->stop_cancellable); +- g_source_set_dummy_callback (component->stop_cancellable_source); ++ source_set_dummy_callback (component->stop_cancellable_source); + g_source_attach (component->stop_cancellable_source, component->own_ctx); + component->ctx = g_main_context_ref (component->own_ctx); + +@@ -1242,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_) + child_socket_source->source = + g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, + NULL); +- g_source_set_dummy_callback (child_socket_source->source); ++ source_set_dummy_callback (child_socket_source->source); + g_source_add_child_source (source, child_socket_source->source); + g_source_unref (child_socket_source->source); + component_source->socket_sources = +@@ -1387,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id, + GSource *cancellable_source; + + cancellable_source = g_cancellable_source_new (cancellable); +- g_source_set_dummy_callback (cancellable_source); ++ source_set_dummy_callback (cancellable_source); + g_source_add_child_source ((GSource *) component_source, + cancellable_source); + g_source_unref (cancellable_source); +-- +2.13.6 + + +From 9f800d3597767855accccc592c34bc4e945f5bd5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 21 Jun 2017 20:42:57 -0400 +Subject: [PATCH 52/70] configure: Remove -Wswitch-enum + +Creates useless warnings when other libraries change. + +https://phabricator.freedesktop.org/T7770 +--- + configure.ac | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 6c106ff..16988ad 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -154,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \ + ]) + AS_IF([test "$enable_compile_warnings" = "maximum" -o \ + "$enable_compile_warnings" = "error"],[ +- NICE_ADD_FLAG([-Wswitch-enum]) + NICE_ADD_FLAG([-Wswitch-default]) + NICE_ADD_FLAG([-Waggregate-return]) + ]) +-- +2.13.6 + + +From dbaf8f5ccd76089e340883887c7e08e6c04de80a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:22:21 +0200 +Subject: [PATCH 53/70] conncheck: improve role conflict debug + +This patch displays explicitely the controlling or controlled +role of the agent. + +Differential Revision:
https://phabricator.freedesktop.org/D874
+--- + agent/conncheck.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 63db471..8945e0f 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3134,14 +3134,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) + { + /* role conflict, change mode; wait for a new conn. check */ + if (control != agent->controlling_mode) { +- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control); ++ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".", ++ agent, control ? "controlling" : "controlled"); + agent->controlling_mode = control; + /* the pair priorities depend on the roles, so recalculation + * is needed */ + priv_recalculate_pair_priorities (agent); + } + else +- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control); ++ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", ++ agent, control ? "controlling" : "controlled"); + } + + /* +@@ -3429,13 +3431,25 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + /* case: role conflict error, need to restart with new role */ + nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); + ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ ++ + if (p->stun_message.buffer != NULL) { + guint64 tie; + gboolean controlled_mode; + +- /* note: our role might already have changed due to an +- * incoming request, but if not, change role now; +- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ + controlled_mode = (stun_message_find64 (&p->stun_message, + STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == + STUN_MESSAGE_RETURN_SUCCESS); +-- +2.13.6 + + +From 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 5 Sep 2017 14:50:29 -0400 +Subject: [PATCH 54/70] agent: Set error if it isn't set + +--- + agent/agent.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index e48d7f3..a4dcc0c 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -4280,7 +4280,7 @@ nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) + { + GError **error = user_data; + +- if (error && *error) ++ if (error && !*error) + g_cancellable_set_error_if_cancelled (cancellable, error); + return G_SOURCE_REMOVE; + } +-- +2.13.6 + + +From 25be00271a4c8c684a2d435d29ae0811dbf5e21c Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 20:36:35 +0200 +Subject: [PATCH 55/70] conncheck: reorder some chunks of code + +With this patch we simplify the levels of code indentation. + +Differential Revision:
https://phabricator.freedesktop.org/D1758
+--- + agent/conncheck.c | 858 +++++++++++++++++++++++++++--------------------------- + 1 file changed, 422 insertions(+), 436 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 8945e0f..874f7b1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -608,106 +608,106 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + gboolean keep_timer_going = FALSE; + GSList *i; + CandidateCheckPair *pair; ++ unsigned int timeout; + + /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; ++ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ NiceComponent *component; ++ ++ if (!agent_find_component (agent, p->stream_id, p->component_id, ++ NULL, &component)) ++ continue; ++ ++ if (p->state != NICE_CHECK_IN_PROGRESS) ++ continue; + +- if (p->state == NICE_CHECK_IN_PROGRESS) { +- if (p->stun_message.buffer == NULL) { +- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); +- } else if (priv_timer_expired (&p->next_tick, now)) { +- switch (stun_timer_refresh (&p->timer)) { +- case STUN_USAGE_TIMER_RETURN_TIMEOUT: +- { +- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; +- NiceComponent *component; ++ if (p->stun_message.buffer == NULL) { ++ nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); ++ p->state = NICE_CHECK_FAILED; ++ nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ continue; ++ } + ++ if (!priv_timer_expired (&p->next_tick, now)) ++ continue; ++ ++ switch (stun_timer_refresh (&p->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: + timer_timeout: +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; + +- /* case: error, abort processing */ +- nice_address_to_string (&p->local->addr, tmpbuf1); +- nice_address_to_string (&p->remote->addr, tmpbuf2); +- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); +- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, +- tmpbuf1, nice_address_get_port (&p->local->addr), +- tmpbuf2, nice_address_get_port (&p->remote->addr)); +- candidate_check_pair_fail (stream, agent, p); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", retransmission failed"); +- +- /* perform a check if a transition state from connected to +- * ready can be performed. This may happen here, when the last +- * in-progress pair has expired its retransmission count +- * in priv_conn_check_tick_stream(), which is a condition to +- * make the transition connected to ready. +- */ +- if (agent_find_component (agent, p->stream_id, p->component_id, +- NULL, &component)) +- priv_update_check_list_state_for_ready (agent, stream, +- component); +- break; +- } +- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +- { +- unsigned int timeout = stun_timer_remainder (&p->timer); ++ /* case: error, abort processing */ ++ nice_address_to_string (&p->local->addr, tmpbuf1); ++ nice_address_to_string (&p->remote->addr, tmpbuf2); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, ++ tmpbuf1, nice_address_get_port (&p->local->addr), ++ tmpbuf2, nice_address_get_port (&p->remote->addr)); ++ candidate_check_pair_fail (stream, agent, p); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", retransmission failed"); ++ ++ /* perform a check if a transition state from connected to ++ * ready can be performed. This may happen here, when the last ++ * in-progress pair has expired its retransmission count ++ * in priv_conn_check_tick_stream(), which is a condition to ++ * make the transition connected to ready. ++ */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ break; ++ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: ++ timeout = stun_timer_remainder (&p->timer); + +- /* case: retransmission stopped, due to the nomination of +- * a pair with a higher priority than this in-progress pair, +- * ICE spec, sect 8.1.2 "Updating States", item 2.2 +- */ +- if (!p->retransmit_on_timeout) +- goto timer_timeout; ++ /* case: retransmission stopped, due to the nomination of ++ * a pair with a higher priority than this in-progress pair, ++ * ICE spec, sect 8.1.2 "Updating States", item 2.2 ++ */ ++ if (!p->retransmit_on_timeout) ++ goto timer_timeout; + +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; + +- /* case: not ready, so schedule a new timeout */ +- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " +- "(timeout %dms, delay=%dms, retrans=%d).", +- agent, p, timeout, p->timer.delay, p->timer.retransmissions); ++ /* case: not ready, so schedule a new timeout */ ++ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " ++ "(timeout %dms, delay=%dms, retrans=%d).", ++ agent, p, timeout, p->timer.delay, p->timer.retransmissions); + +- agent_socket_send (p->sockptr, &p->remote->addr, +- stun_message_length (&p->stun_message), +- (gchar *)p->stun_buffer); ++ agent_socket_send (p->sockptr, &p->remote->addr, ++ stun_message_length (&p->stun_message), ++ (gchar *)p->stun_buffer); + + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ p->next_tick = *now; ++ g_time_val_add (&p->next_tick, timeout * 1000); + +- return TRUE; +- } +- case STUN_USAGE_TIMER_RETURN_SUCCESS: +- { +- unsigned int timeout = stun_timer_remainder (&p->timer); ++ return TRUE; ++ case STUN_USAGE_TIMER_RETURN_SUCCESS: ++ timeout = stun_timer_remainder (&p->timer); + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ p->next_tick = *now; ++ g_time_val_add (&p->next_tick, timeout * 1000); + +- keep_timer_going = TRUE; +- break; +- } +- default: +- /* Nothing to do. */ +- break; +- } +- } ++ keep_timer_going = TRUE; ++ break; ++ default: ++ /* Nothing to do. */ ++ break; + } + } + +@@ -2628,27 +2628,23 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { + switch (agent->nomination_mode) { + case NICE_NOMINATION_MODE_REGULAR: +- { +- /* We are doing regular nomination, so we set the use-candidate +- * attrib, when the controlling agent decided which valid pair to +- * resend with this flag in priv_conn_check_tick_stream() +- */ +- cand_use = pair->use_candidate_on_next_check; +- nice_debug ("Agent %p : %s: set cand_use=%d " +- "(regular nomination).", agent, G_STRFUNC, cand_use); +- break; +- } ++ /* We are doing regular nomination, so we set the use-candidate ++ * attrib, when the controlling agent decided which valid pair to ++ * resend with this flag in priv_conn_check_tick_stream() ++ */ ++ cand_use = pair->use_candidate_on_next_check; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(regular nomination).", agent, G_STRFUNC, cand_use); ++ break; + case NICE_NOMINATION_MODE_AGGRESSIVE: +- { +- /* We are doing aggressive nomination, we set the use-candidate +- * attrib in every check we send, when we are the controlling +- * agent, RFC 5245, 8.1.1.2 +- */ +- cand_use = controlling; +- nice_debug ("Agent %p : %s: set cand_use=%d " +- "(aggressive nomination).", agent, G_STRFUNC, cand_use); +- break; +- } ++ /* We are doing aggressive nomination, we set the use-candidate ++ * attrib in every check we send, when we are the controlling ++ * agent, RFC 5245, 8.1.1.2 ++ */ ++ cand_use = controlling; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(aggressive nomination).", agent, G_STRFUNC, cand_use); ++ break; + default: + /* Nothing to do. */ + break; +@@ -2656,107 +2652,105 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + } else if (cand_use) + pair->nominated = controlling; + +- if (uname_len > 0) { +- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, +- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), +- uname, uname_len, password, password_len, +- cand_use, controlling, pair->prflx_priority, +- agent->tie_breaker, +- pair->local->foundation, +- agent_to_ice_compatibility (agent)); ++ if (uname_len == 0) { ++ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); ++ pair->stun_message.buffer = NULL; ++ pair->stun_message.buffer_len = 0; ++ return -1; ++ } + +- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, +- pair->stun_message.buffer); ++ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, ++ &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), ++ uname, uname_len, password, password_len, ++ cand_use, controlling, pair->prflx_priority, ++ agent->tie_breaker, ++ pair->local->foundation, ++ agent_to_ice_compatibility (agent)); + +- if (agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- g_free (password); +- } ++ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, ++ pair->stun_message.buffer); + +- if (buffer_len > 0) { +- if (nice_socket_is_reliable(pair->sockptr)) { +- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); +- } else { +- StunTimer *timer = &pair->timer; +- +- if (pair->recheck_on_timeout) +- /* The pair recheck on timeout can easily cause repetitive rechecks in +- * a ping-pong effect, if both peers with the same behaviour try to +- * check the same pair almost simultaneously, and if the network rtt +- * is greater than the initial timer rto. The reply to the initial +- * stun request may arrive after the in-progress conncheck +- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation +- * creates a new stun request, and forgets the initial one. +- * The conncheck timer is restarted with the same initial value, +- * so the same situation happens again later. +- * +- * We choose to avoid resetting the timer in such situation. +- * After enough retransmissions, the timeout delay becomes +- * longer than the rtt, and the stun reply can be handled. +- */ +- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", +- agent, pair, +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay); +- else +- stun_timer_start (timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); +- pair->recheck_on_timeout = FALSE; +- } ++ if (agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ g_free (password); ++ } + +- /* TCP-ACTIVE candidate must create a new socket before sending +- * by connecting to the peer. The new socket is stored in the candidate +- * check pair, until we discover a new local peer reflexive */ +- if (pair->sockptr->fileno == NULL && +- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && +- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { +- NiceStream *stream2 = NULL; +- NiceComponent *component2 = NULL; +- NiceSocket *new_socket; +- +- if (agent_find_component (agent, pair->stream_id, pair->component_id, +- &stream2, &component2)) { +- new_socket = nice_tcp_active_socket_connect (pair->sockptr, +- &pair->remote->addr); +- if (new_socket) { +- pair->sockptr = new_socket; +- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); +- +- if (agent->reliable) { +- nice_socket_set_writable_callback (pair->sockptr, +- _tcp_sock_is_writable, component2); +- } ++ if (buffer_len == 0) { ++ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); ++ pair->stun_message.buffer = NULL; ++ pair->stun_message.buffer_len = 0; ++ return -1; ++ } + +- nice_component_attach_socket (component2, new_socket); +- } +- } +- } +- /* send the conncheck */ +- agent_socket_send (pair->sockptr, &pair->remote->addr, +- buffer_len, (gchar *)pair->stun_buffer); ++ if (nice_socket_is_reliable(pair->sockptr)) ++ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); ++ else { ++ StunTimer *timer = &pair->timer; ++ ++ if (pair->recheck_on_timeout) ++ /* The pair recheck on timeout can easily cause repetitive rechecks in ++ * a ping-pong effect, if both peers with the same behaviour try to ++ * check the same pair almost simultaneously, and if the network rtt ++ * is greater than the initial timer rto. The reply to the initial ++ * stun request may arrive after the in-progress conncheck ++ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation ++ * creates a new stun request, and forgets the initial one. ++ * The conncheck timer is restarted with the same initial value, ++ * so the same situation happens again later. ++ * ++ * We choose to avoid resetting the timer in such situation. ++ * After enough retransmissions, the timeout delay becomes ++ * longer than the rtt, and the stun reply can be handled. ++ */ ++ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", ++ agent, pair, ++ timer->retransmissions, timer->max_retransmissions, ++ timer->delay - stun_timer_remainder (timer), timer->delay); ++ else ++ stun_timer_start (timer, ++ priv_compute_conncheck_timer (agent, stream), ++ agent->stun_max_retransmissions); ++ pair->recheck_on_timeout = FALSE; ++ } + +- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { +- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, +- &pair->remote->addr); ++ /* TCP-ACTIVE candidate must create a new socket before sending ++ * by connecting to the peer. The new socket is stored in the candidate ++ * check pair, until we discover a new local peer reflexive */ ++ if (pair->sockptr->fileno == NULL && ++ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && ++ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { ++ NiceStream *stream2 = NULL; ++ NiceComponent *component2 = NULL; ++ NiceSocket *new_socket; ++ ++ if (agent_find_component (agent, pair->stream_id, pair->component_id, ++ &stream2, &component2)) { ++ new_socket = nice_tcp_active_socket_connect (pair->sockptr, ++ &pair->remote->addr); ++ if (new_socket) { ++ pair->sockptr = new_socket; ++ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); ++ ++ if (agent->reliable) ++ nice_socket_set_writable_callback (pair->sockptr, ++ _tcp_sock_is_writable, component2); ++ ++ nice_component_attach_socket (component2, new_socket); + } +- +- timeout = stun_timer_remainder (&pair->timer); +- /* note: convert from milli to microseconds for g_time_val_add() */ +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); +- } else { +- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; +- return -1; + } +- } else { +- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; +- return -1; + } ++ /* send the conncheck */ ++ agent_socket_send (pair->sockptr, &pair->remote->addr, ++ buffer_len, (gchar *)pair->stun_buffer); ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, ++ &pair->remote->addr); ++ ++ timeout = stun_timer_remainder (&pair->timer); ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ g_get_current_time (&pair->next_tick); ++ g_time_val_add (&pair->next_tick, timeout * 1000); + + return 0; + } +@@ -2876,74 +2870,74 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", + agent, p, p->foundation, priv_state_to_gchar (p->state)); + +- if (p->state == NICE_CHECK_WAITING || +- p->state == NICE_CHECK_FROZEN) { +- nice_debug ("Agent %p : pair %p added for a triggered check.", +- agent, p); +- priv_add_pair_to_triggered_check_queue (agent, p); +- } +- else if (p->state == NICE_CHECK_IN_PROGRESS) { +- /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" +- * we cancel the in-progress transaction, and after the +- * retransmission timeout, we create a new connectivity check +- * for that pair. The controlling role of this new check may +- * be different from the role of this cancelled check. +- * +- * note: the flag retransmit_on_timeout unset means that +- * another pair, with a higher priority is already nominated, +- * so there's no reason to recheck this pair, since it can in +- * no way replace the nominated one. +- */ +- if (!nice_socket_is_reliable (p->sockptr)) { +- if (p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p will be rechecked " +- "on stun timer timeout.", agent, p); +- /* this flag will determine the action at the retransmission +- * timeout of the stun timer +- */ +- p->recheck_on_timeout = TRUE; +- } else +- nice_debug ("Agent %p : pair %p won't be retransmitted.", +- agent, p); +- } +- } +- else if (p->state == NICE_CHECK_SUCCEEDED) { +- nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); +- /* note: this is a bit unsure corner-case -- let's do the +- same state update as for processing responses to our own checks */ +- /* note: this update is required by the dribble test, to +- * ensure the transition ready -> connected -> ready, because +- * an incoming stun request generates a discovered peer reflexive, +- * that causes the ready -> connected transition. +- */ +- priv_update_check_list_state_for_ready (agent, stream, component); +- } else if (p->state == NICE_CHECK_FAILED) { +- /* 7.2.1.4 Triggered Checks +- * If the state of the pair is Failed, it is changed to Waiting +- * and the agent MUST create a new connectivity check for that +- * pair (representing a new STUN Binding request transaction), by +- * enqueueing the pair in the triggered check queue. +- * +- * note: the flag retransmit_on_timeout unset means that +- * another pair, with a higher priority is already nominated, +- * we apply the same strategy than with an in-progress pair +- * above. +- */ +- if (p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p added for a triggered check.", ++ switch (p->state) { ++ case NICE_CHECK_WAITING: ++ case NICE_CHECK_FROZEN: ++ nice_debug ("Agent %p : pair %p added for a triggered check.", + agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); +- /* If the component for this pair is in failed state, move it +- * back to connecting, and reinitiate the timers ++ break; ++ case NICE_CHECK_IN_PROGRESS: ++ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" ++ * we cancel the in-progress transaction, and after the ++ * retransmission timeout, we create a new connectivity check ++ * for that pair. The controlling role of this new check may ++ * be different from the role of this cancelled check. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * so there's no reason to recheck this pair, since it can in ++ * no way replace the nominated one. + */ +- if (component->state == NICE_COMPONENT_STATE_FAILED) { +- agent_signal_component_state_change (agent, stream->id, +- component->id, NICE_COMPONENT_STATE_CONNECTING); +- conn_check_schedule_next (agent); ++ if (!nice_socket_is_reliable (p->sockptr) && ++ p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p will be rechecked " ++ "on stun timer timeout.", agent, p); ++ /* this flag will determine the action at the retransmission ++ * timeout of the stun timer ++ */ ++ p->recheck_on_timeout = TRUE; + } +- } else +- nice_debug ("Agent %p : pair %p won't be retransmitted.", +- agent, p); ++ break; ++ case NICE_CHECK_SUCCEEDED: ++ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); ++ /* note: this is a bit unsure corner-case -- let's do the ++ same state update as for processing responses to our own checks */ ++ /* note: this update is required by the dribble test, to ++ * ensure the transition ready -> connected -> ready, because ++ * an incoming stun request generates a discovered peer reflexive, ++ * that causes the ready -> connected transition. ++ */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ break; ++ case NICE_CHECK_FAILED: ++ /* 7.2.1.4 Triggered Checks ++ * If the state of the pair is Failed, it is changed to Waiting ++ * and the agent MUST create a new connectivity check for that ++ * pair (representing a new STUN Binding request transaction), by ++ * enqueueing the pair in the triggered check queue. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * we apply the same strategy than with an in-progress pair ++ * above. ++ */ ++ if (p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } ++ } ++ break; ++ default: ++ break; + } + + /* note: the spec says the we SHOULD retransmit in-progress +@@ -3271,208 +3265,200 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + socklen_t socklen = sizeof (sockaddr); + GSList *i; + StunUsageIceReturn res; +- gboolean trans_found = FALSE; + StunTransactionId discovery_id; + StunTransactionId response_id; + stun_message_id (resp, response_id); + +- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) { ++ for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +- if (p->stun_message.buffer) { +- stun_message_id (&p->stun_message, discovery_id); ++ if (p->stun_message.buffer == NULL) ++ continue; + +- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) { +- res = stun_usage_ice_conncheck_process (resp, +- &sockaddr.storage, &socklen, +- agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " +- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); +- +- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || +- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* case: found a matching connectivity check request */ +- +- CandidateCheckPair *ok_pair = NULL; +- +- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- +- /* step: verify that response came from the same IP address we +- * sent the original request to (see 7.1.2.1. "Failure +- * Cases") */ +- if (nice_address_equal (from, &p->remote->addr) != TRUE) { +- +- p->state = NICE_CHECK_FAILED; +- if (nice_debug_is_enabled ()) { +- gchar tmpbuf[INET6_ADDRSTRLEN]; +- gchar tmpbuf2[INET6_ADDRSTRLEN]; +- nice_debug ("Agent %p : conncheck %p FAILED" +- " (mismatch of source address).", agent, p); +- nice_address_to_string (&p->remote->addr, tmpbuf); +- nice_address_to_string (from, tmpbuf2); +- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, +- tmpbuf, nice_address_get_port (&p->remote->addr), +- tmpbuf2, nice_address_get_port (from)); +- } +- trans_found = TRUE; +- break; +- } ++ stun_message_id (&p->stun_message, discovery_id); + +- /* note: CONNECTED but not yet READY, see docs */ +- +- /* step: handle the possible case of a peer-reflexive +- * candidate where the mapped-address in response does +- * not match any local candidate, see 7.1.2.2.1 +- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ +- +- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* note: this is same as "adding to VALID LIST" in the spec +- text */ +- p->state = NICE_CHECK_SUCCEEDED; +- p->valid = TRUE; +- g_assert_not_reached (); +- nice_debug ("Agent %p : Mapped address not found." +- " conncheck %p SUCCEEDED.", agent, p); +- nice_component_add_valid_candidate (component, p->remote); +- } else { +- ok_pair = priv_process_response_check_for_reflexive (agent, +- stream, component, p, sockptr, &sockaddr.addr, +- local_candidate, remote_candidate); +- } ++ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) ++ continue; + +- /* note: The success of this check might also +- * cause the state of other checks to change as well, ICE +- * spec 7.1.3.2.3 +- */ +- priv_conn_check_unfreeze_related (agent, stream, p); +- +- /* Note: this assignment helps to reduce the numbers of cases +- * to be tested. If ok_pair and p refer to distinct pairs, it +- * means that ok_pair is a discovered peer reflexive one, +- * caused by the check made on pair p. In that case, the +- * flags to be tested are on p, but the nominated flag will be +- * set on ok_pair. When there's no discovered pair, p and +- * ok_pair refer to the same pair. +- * To summarize : p is a SUCCEEDED pair, ok_pair is a +- * DISCOVERED, VALID, and eventually NOMINATED pair. +- */ +- if (!ok_pair) +- ok_pair = p; +- +- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the +- Nominated Flag" (ID-19) */ +- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { +- nice_debug ("Agent %p : Updating nominated flag (%s): " +- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", +- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? +- "UDP" : "TCP", +- ok_pair, ok_pair->use_candidate_on_next_check, +- ok_pair->mark_nominated_on_response_arrival, +- p, p->use_candidate_on_next_check, +- p->mark_nominated_on_response_arrival); +- +- if (agent->controlling_mode) { +- switch (agent->nomination_mode) { +- case NICE_NOMINATION_MODE_REGULAR: +- if (p->use_candidate_on_next_check) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(regular nomination, control=1, " +- "use_cand_on_next_check=1).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- case NICE_NOMINATION_MODE_AGGRESSIVE: +- if (!p->nominated) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(aggressive nomination, control=1).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- default: +- /* Nothing to do */ +- break; ++ res = stun_usage_ice_conncheck_process (resp, ++ &sockaddr.storage, &socklen, ++ agent_to_ice_compatibility (agent)); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " ++ "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); ++ ++ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || ++ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ /* case: found a matching connectivity check request */ ++ ++ CandidateCheckPair *ok_pair = NULL; ++ ++ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; ++ ++ /* step: verify that response came from the same IP address we ++ * sent the original request to (see 7.1.2.1. "Failure ++ * Cases") */ ++ if (nice_address_equal (from, &p->remote->addr) == FALSE) { ++ ++ p->state = NICE_CHECK_FAILED; ++ if (nice_debug_is_enabled ()) { ++ gchar tmpbuf[INET6_ADDRSTRLEN]; ++ gchar tmpbuf2[INET6_ADDRSTRLEN]; ++ nice_debug ("Agent %p : conncheck %p FAILED" ++ " (mismatch of source address).", agent, p); ++ nice_address_to_string (&p->remote->addr, tmpbuf); ++ nice_address_to_string (from, tmpbuf2); ++ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, ++ tmpbuf, nice_address_get_port (&p->remote->addr), ++ tmpbuf2, nice_address_get_port (from)); ++ } ++ return TRUE; ++ } ++ ++ /* note: CONNECTED but not yet READY, see docs */ ++ ++ /* step: handle the possible case of a peer-reflexive ++ * candidate where the mapped-address in response does ++ * not match any local candidate, see 7.1.2.2.1 ++ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ ++ ++ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ p->state = NICE_CHECK_SUCCEEDED; ++ p->valid = TRUE; ++ nice_debug ("Agent %p : Mapped address not found." ++ " conncheck %p SUCCEEDED.", agent, p); ++ nice_component_add_valid_candidate (component, p->remote); ++ } else ++ ok_pair = priv_process_response_check_for_reflexive (agent, ++ stream, component, p, sockptr, &sockaddr.addr, ++ local_candidate, remote_candidate); ++ ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ ++ if (!ok_pair) ++ ok_pair = p; ++ ++ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the ++ Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, control=1, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; + } +- } else { +- if (p->mark_nominated_on_response_arrival) { ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(%s nomination, control=0, mark_on_response=1).", +- agent, ok_pair, ok_pair->foundation, +- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? +- "aggressive" : "regular"); ++ "(aggressive nomination, control=1).", ++ agent, ok_pair, ok_pair->foundation); + ok_pair->nominated = TRUE; + } +- } ++ break; ++ default: ++ /* Nothing to do */ ++ break; + } +- +- if (ok_pair->nominated == TRUE) { +- priv_update_selected_pair (agent, component, ok_pair); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a nominated pair"); +- +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, control=0, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; + } ++ } ++ } + +- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair +- states" and 8.1.2 "Updating States", ID-19) */ +- priv_update_check_list_state_for_ready (agent, stream, component); ++ if (ok_pair->nominated == TRUE) { ++ priv_update_selected_pair (agent, component, ok_pair); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a nominated pair"); + +- trans_found = TRUE; +- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { +- /* case: role conflict error, need to restart with new role */ +- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- +- /* note: this res value indicates that the role of the peer +- * agent has not changed after the tie-breaker comparison, so +- * this is our role that must change. see ICE sect. 7.1.3.1 +- * "Failure Cases". Our role might already have changed due to +- * an earlier incoming request, but if not, change role now. +- * +- * Sect. 7.1.3.1 is not clear on this point, but we choose to +- * put the candidate pair in the triggered check list even +- * when the agent did not switch its role. The reason for this +- * interpretation is that the reception of the stun reply, even +- * an error reply, is a good sign that this pair will be +- * valid, if we retry the check after the role of both peers +- * has been fixed. +- */ ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } + +- if (p->stun_message.buffer != NULL) { +- guint64 tie; +- gboolean controlled_mode; ++ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair ++ states" and 8.1.2 "Updating States", ID-19) */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { ++ /* case: role conflict error, need to restart with new role */ ++ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); ++ ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ + +- controlled_mode = (stun_message_find64 (&p->stun_message, +- STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == +- STUN_MESSAGE_RETURN_SUCCESS); ++ if (p->stun_message.buffer != NULL) { ++ guint64 tie; ++ gboolean controlled_mode; + +- priv_check_for_role_conflict (agent, controlled_mode); ++ controlled_mode = (stun_message_find64 (&p->stun_message, ++ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == ++ STUN_MESSAGE_RETURN_SUCCESS); + +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- priv_add_pair_to_triggered_check_queue (agent, p); +- } +- trans_found = TRUE; +- } else { +- /* case: STUN error, the check STUN context was freed */ +- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- trans_found = TRUE; +- } ++ priv_check_for_role_conflict (agent, controlled_mode); ++ ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; ++ priv_add_pair_to_triggered_check_queue (agent, p); + } ++ } else { ++ /* case: STUN error, the check STUN context was freed */ ++ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; + } ++ return TRUE; + } + +- return trans_found; ++ return FALSE; + } + + /* +-- +2.13.6 + + +From ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 20:41:49 +0200 +Subject: [PATCH 56/70] conncheck: make debug sentences more accurate + +We add a helper function to print the pair state in-extenso. + +Differential Revision:
https://phabricator.freedesktop.org/D1759
+--- + agent/conncheck.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 61 insertions(+), 9 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 874f7b1..9517ee1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -108,6 +108,54 @@ priv_state_to_gchar (NiceCheckState state) + } + + static const gchar * ++priv_state_to_string (NiceCheckState state) ++{ ++ switch (state) { ++ case NICE_CHECK_WAITING: ++ return "waiting"; ++ case NICE_CHECK_IN_PROGRESS: ++ return "in progress"; ++ case NICE_CHECK_SUCCEEDED: ++ return "succeeded"; ++ case NICE_CHECK_FAILED: ++ return "failed"; ++ case NICE_CHECK_FROZEN: ++ return "frozen"; ++ case NICE_CHECK_DISCOVERED: ++ return "discovered"; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ ++static const gchar * ++priv_ice_return_to_string (StunUsageIceReturn ice_return) ++{ ++ switch (ice_return) { ++ case STUN_USAGE_ICE_RETURN_SUCCESS: ++ return "success"; ++ case STUN_USAGE_ICE_RETURN_ERROR: ++ return "error"; ++ case STUN_USAGE_ICE_RETURN_INVALID: ++ return "invalid"; ++ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT: ++ return "role conflict"; ++ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST: ++ return "invalid request"; ++ case STUN_USAGE_ICE_RETURN_INVALID_METHOD: ++ return "invalid method"; ++ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR: ++ return "memory error"; ++ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS: ++ return "invalid address"; ++ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS: ++ return "no mapped address"; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ ++static const gchar * + priv_candidate_type_to_string (NiceCandidateType type) + { + switch (type) { +@@ -2614,7 +2662,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + nice_address_to_string (&pair->remote->addr, tmpbuf2); + nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " + "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " +- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, ++ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr), + pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, +@@ -2622,7 +2670,8 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + (unsigned long long)agent->tie_breaker, + (int) uname_len, uname, uname_len, + (int) password_len, password, password_len, +- pair->prflx_priority, controlling); ++ pair->prflx_priority, ++ controlling ? "controlling" : "controlled"); + } + + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { +@@ -2867,8 +2916,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p = p->succeeded_pair; + } + +- nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", +- agent, p, p->foundation, priv_state_to_gchar (p->state)); ++ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", ++ agent, p, p->foundation, priv_state_to_string (p->state)); + + switch (p->state) { + case NICE_CHECK_WAITING: +@@ -3283,8 +3332,11 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + res = stun_usage_ice_conncheck_process (resp, + &sockaddr.storage, &socklen, + agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " +- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " ++ "%s,res=%s.", ++ agent, p, ++ agent->controlling_mode ? "controlling" : "controlled", ++ priv_ice_return_to_string (res)); + + if (res == STUN_USAGE_ICE_RETURN_SUCCESS || + res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +@@ -3370,7 +3422,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + case NICE_NOMINATION_MODE_REGULAR: + if (p->use_candidate_on_next_check) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(regular nomination, control=1, " ++ "(regular nomination, controlling, " + "use_cand_on_next_check=1).", + agent, ok_pair, ok_pair->foundation); + ok_pair->nominated = TRUE; +@@ -3379,7 +3431,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + case NICE_NOMINATION_MODE_AGGRESSIVE: + if (!p->nominated) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(aggressive nomination, control=1).", ++ "(aggressive nomination, controlling).", + agent, ok_pair, ok_pair->foundation); + ok_pair->nominated = TRUE; + } +@@ -3391,7 +3443,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + } else { + if (p->mark_nominated_on_response_arrival) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(%s nomination, control=0, mark_on_response=1).", ++ "(%s nomination, controlled, mark_on_response=1).", + agent, ok_pair, ok_pair->foundation, + agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? + "aggressive" : "regular"); +-- +2.13.6 + + +From e860948b5fe3a791119957f26045b8f5159baeff Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 21:06:36 +0200 +Subject: [PATCH 57/70] conncheck: use stun_timer_remainder less frequently + +We try to use stun_timer_remainder() less frequently, particularily +in the debug messages, and favour of the next_tick value associated +to the pair. + +Differential Revision:
https://phabricator.freedesktop.org/D1760
+--- + agent/conncheck.c | 77 ++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 48 insertions(+), 29 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 9517ee1..8075d4f 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -86,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) + now->tv_sec >= timer->tv_sec; + } + ++static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now) ++{ ++ unsigned int delay; ++ if (now->tv_sec > timer->tv_sec || ++ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec)) ++ return 0; ++ delay = (timer->tv_sec - now->tv_sec) * 1000; ++ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000; ++ return delay; ++} ++ + static gchar + priv_state_to_gchar (NiceCheckState state) + { +@@ -180,10 +191,13 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + { + GSList *i, *k; + guint j; ++ GTimeVal now; + + if (!nice_debug_is_verbose ()) + return; + ++ g_get_current_time (&now); ++ + #define PRIORITY_LEN 32 + + nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", +@@ -209,7 +223,8 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + priv_candidate_type_to_string (pair->local->type), + priv_candidate_type_to_string (pair->remote->type), + timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay, ++ timer->delay - priv_timer_remainder (&pair->next_tick, &now), ++ timer->delay, + local_addr, nice_address_get_port (&pair->local->addr), + remote_addr, nice_address_get_port (&pair->remote->addr), + priv_state_to_gchar (pair->state), +@@ -445,8 +460,6 @@ priv_find_first_frozen_check_list (NiceAgent *agent) + */ + static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) + { +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); + pair->state = NICE_CHECK_IN_PROGRESS; + nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); + conn_check_send (agent, pair); +@@ -651,12 +664,15 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + * + * @return will return FALSE when no more pending timers. + */ +-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) ++static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) + { + gboolean keep_timer_going = FALSE; + GSList *i; + CandidateCheckPair *pair; + unsigned int timeout; ++ GTimeVal now; ++ ++ g_get_current_time (&now); + + /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { +@@ -678,7 +694,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + continue; + } + +- if (!priv_timer_expired (&p->next_tick, now)) ++ if (!priv_timer_expired (&p->next_tick, &now)) + continue; + + switch (stun_timer_refresh (&p->timer)) { +@@ -712,8 +728,6 @@ timer_timeout: + priv_update_check_list_state_for_ready (agent, stream, component); + break; + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +- timeout = stun_timer_remainder (&p->timer); +- + /* case: retransmission stopped, due to the nomination of + * a pair with a higher priority than this in-progress pair, + * ICE spec, sect 8.1.2 "Updating States", item 2.2 +@@ -730,9 +744,13 @@ timer_timeout: + break; + + /* case: not ready, so schedule a new timeout */ ++ timeout = stun_timer_remainder (&p->timer); ++ + nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " +- "(timeout %dms, delay=%dms, retrans=%d).", +- agent, p, timeout, p->timer.delay, p->timer.retransmissions); ++ "(timer=%d/%d %d/%dms).", ++ agent, p, ++ p->timer.retransmissions, p->timer.max_retransmissions, ++ p->timer.delay - timeout, p->timer.delay); + + agent_socket_send (p->sockptr, &p->remote->addr, + stun_message_length (&p->stun_message), +@@ -740,7 +758,7 @@ timer_timeout: + + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; ++ p->next_tick = now; + g_time_val_add (&p->next_tick, timeout * 1000); + + return TRUE; +@@ -748,7 +766,7 @@ timer_timeout: + timeout = stun_timer_remainder (&p->timer); + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; ++ p->next_tick = now; + g_time_val_add (&p->next_tick, timeout * 1000); + + keep_timer_going = TRUE; +@@ -1001,9 +1019,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + CandidateCheckPair *pair = NULL; + gboolean keep_timer_going = FALSE; + GSList *i, *j; +- GTimeVal now; +- +- g_get_current_time (&now); + + /* the conncheck really starts when we have built + * a connection check list for each stream +@@ -1047,7 +1062,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + */ + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +- if (priv_conn_check_tick_stream (stream, agent, &now)) ++ if (priv_conn_check_tick_stream (stream, agent)) + keep_timer_going = TRUE; + if (priv_conn_check_tick_stream_nominate (stream, agent)) + keep_timer_going = TRUE; +@@ -2731,12 +2746,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + return -1; + } + +- if (nice_socket_is_reliable(pair->sockptr)) +- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); +- else { ++ if (nice_socket_is_reliable(pair->sockptr)) { ++ timeout = agent->stun_reliable_timeout; ++ stun_timer_start_reliable(&pair->timer, timeout); ++ } else { + StunTimer *timer = &pair->timer; + +- if (pair->recheck_on_timeout) ++ if (pair->recheck_on_timeout) { ++ GTimeVal now; + /* The pair recheck on timeout can easily cause repetitive rechecks in + * a ping-pong effect, if both peers with the same behaviour try to + * check the same pair almost simultaneously, and if the network rtt +@@ -2751,17 +2768,24 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + * After enough retransmissions, the timeout delay becomes + * longer than the rtt, and the stun reply can be handled. + */ ++ ++ g_get_current_time (&now); ++ timeout = priv_timer_remainder (&pair->next_tick, &now); + nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", + agent, pair, + timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay); +- else +- stun_timer_start (timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); ++ timer->delay - timeout, ++ timer->delay); ++ } else { ++ timeout = priv_compute_conncheck_timer (agent, stream); ++ stun_timer_start (timer, timeout, agent->stun_max_retransmissions); ++ } + pair->recheck_on_timeout = FALSE; + } + ++ g_get_current_time (&pair->next_tick); ++ g_time_val_add (&pair->next_tick, timeout * 1000); ++ + /* TCP-ACTIVE candidate must create a new socket before sending + * by connecting to the peer. The new socket is stored in the candidate + * check pair, until we discover a new local peer reflexive */ +@@ -2796,11 +2820,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, + &pair->remote->addr); + +- timeout = stun_timer_remainder (&pair->timer); +- /* note: convert from milli to microseconds for g_time_val_add() */ +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); +- + return 0; + } + +-- +2.13.6 + + +From 36f306f4a95f1c2b3e9c584b5a645a78e231c020 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 21:41:44 +0200 +Subject: [PATCH 58/70] conncheck: support several stun requests per pair + +This patch should improve the reliabily of the connection check by +keeping the record of several simultaneous ongoing stun requests per +pair. A new stun request on an in-progress pair typically is caused by +in inbound stun request from the peer on this same pair. This is named +"Triggered Checks" in the spec. When this situation arises, it is fair +to handle these two stun requests simultaneously, the triggered check, +and the initial ordinary check, since both can potentially succeed. + +Differential Revision:
https://phabricator.freedesktop.org/D1761
+--- + agent/conncheck.c | 701 +++++++++++++++++++++++++++--------------------------- + agent/conncheck.h | 21 +- + 2 files changed, 361 insertions(+), 361 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 8075d4f..2a85738 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -189,8 +189,8 @@ priv_candidate_type_to_string (NiceCandidateType type) + static void + priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) + { +- GSList *i, *k; +- guint j; ++ GSList *i, *k, *l; ++ guint j, m; + GTimeVal now; + + if (!nice_debug_is_verbose ()) +@@ -210,27 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + if (pair->component_id == j) { + gchar local_addr[INET6_ADDRSTRLEN]; + gchar remote_addr[INET6_ADDRSTRLEN]; +- StunTimer *timer = &pair->timer; + + nice_address_to_string (&pair->local->addr, local_addr); + nice_address_to_string (&pair->remote->addr, remote_addr); + + nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " +- "f=%s t=%s:%s timer=%d/%d %d/%dms " +- "[%s]:%u > [%s]:%u state=%c%s%s%s", ++ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s", + agent, pair->stream_id, pair->component_id, pair, + pair->foundation, + priv_candidate_type_to_string (pair->local->type), + priv_candidate_type_to_string (pair->remote->type), +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - priv_timer_remainder (&pair->next_tick, &now), +- timer->delay, + local_addr, nice_address_get_port (&pair->local->addr), + remote_addr, nice_address_get_port (&pair->remote->addr), + priv_state_to_gchar (pair->state), + pair->valid ? "V" : "", + pair->nominated ? "N" : "", + g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); ++ ++ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) { ++ StunTransaction *stun = l->data; ++ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " ++ "stun#=%d timer=%d/%d %d/%dms buf=%p %s", ++ agent, pair->stream_id, pair->component_id, pair, m, ++ stun->timer.retransmissions, stun->timer.max_retransmissions, ++ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now), ++ stun->timer.delay, ++ stun->message.buffer, ++ (m == 0 && pair->retransmit) ? "(R)" : ""); ++ } + } + } + } +@@ -608,52 +615,92 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + } + } + ++/* ++ * Create a new STUN transaction and add it to the list ++ * of ongoing stun transactions of a pair. ++ * ++ * @pair the pair the new stun transaction should be added to. ++ * @return the created stun transaction. ++ */ ++static StunTransaction * ++priv_add_stun_transaction (CandidateCheckPair *pair) ++{ ++ StunTransaction *stun = g_slice_new0 (StunTransaction); ++ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun); ++ pair->retransmit = TRUE; ++ return stun; ++} ++ ++/* ++ * Forget a STUN transaction. ++ * ++ * @data the stun transaction to be forgotten. ++ * @user_data the component contained the concerned stun agent. ++ */ + static void +-candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) ++priv_forget_stun_transaction (gpointer data, gpointer user_data) + { ++ StunTransaction *stun = data; ++ NiceComponent *component = user_data; + StunTransactionId id; +- NiceComponent *component; +- +- component = nice_stream_find_component_by_id (stream, p->component_id); +- +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); + +- if (p->stun_message.buffer != NULL) { +- stun_message_id (&p->stun_message, id); ++ if (stun->message.buffer != NULL) { ++ stun_message_id (&stun->message, id); + stun_agent_forget_transaction (&component->stun_agent, id); + } ++} + +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++static void ++priv_free_stun_transaction (gpointer data) ++{ ++ g_slice_free (StunTransaction, data); + } + + /* +- * Function that resubmits a new connection check, after a previous +- * check in in-progress state got cancelled due to an incoming stun +- * request matching this same pair ++ * Remove a STUN transaction from a pair, and forget it ++ * from the related component stun agent. + * +- * @return will return TRUE if the pair is scheduled for recheck ++ * @pair the pair the stun transaction should be removed from. ++ * @stun the stun transaction to be removed. ++ * @component the component containing the stun agent used to ++ * forget the stun transaction. + */ +-static gboolean +-priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) ++static void ++priv_remove_stun_transaction (CandidateCheckPair *pair, ++ StunTransaction *stun, NiceComponent *component) + { +- if (p->recheck_on_timeout) { +- g_assert (p->state == NICE_CHECK_IN_PROGRESS); +- /* this cancelled pair may have the flag 'mark nominated +- * on response arrival' set, we want to keep it, because +- * this is needed to nominate this pair in aggressive +- * nomination, when the agent is in controlled mode. +- * +- * this cancelled pair may also have the flag 'use candidate +- * on next check' set, that we want to preserve too. +- */ +- nice_debug ("Agent %p : pair %p was cancelled, " +- "triggering a new connection check", agent, p); +- priv_add_pair_to_triggered_check_queue (agent, p); +- return TRUE; +- } +- return FALSE; ++ priv_forget_stun_transaction (stun, component); ++ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun); ++ priv_free_stun_transaction (stun); ++} ++ ++/* ++ * Remove all STUN transactions from a pair, and forget them ++ * from the related component stun agent. ++ * ++ * @pair the pair the stun list should be cleared. ++ * @component the component containing the stun agent used to ++ * forget the stun transactions. ++ */ ++static void ++priv_free_all_stun_transactions (CandidateCheckPair *pair, ++ NiceComponent *component) ++{ ++ if (component) ++ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component); ++ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction); ++ pair->stun_transactions = NULL; ++} ++ ++static void ++candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) ++{ ++ NiceComponent *component; ++ ++ component = nice_stream_find_component_by_id (stream, p->component_id); ++ p->state = NICE_CHECK_FAILED; ++ nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ priv_free_all_stun_transactions (p, component); + } + + /* +@@ -667,7 +714,7 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) + { + gboolean keep_timer_going = FALSE; +- GSList *i; ++ GSList *i, *j; + CandidateCheckPair *pair; + unsigned int timeout; + GTimeVal now; +@@ -679,39 +726,59 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + CandidateCheckPair *p = i->data; + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; + NiceComponent *component; ++ StunTransaction *stun; ++ ++ if (p->stun_transactions == NULL) ++ continue; + + if (!agent_find_component (agent, p->stream_id, p->component_id, + NULL, &component)) + continue; + +- if (p->state != NICE_CHECK_IN_PROGRESS) +- continue; ++ /* The first stun transaction of the list may eventually be ++ * retransmitted, other stun transactions just have their ++ * timer updated. ++ */ + +- if (p->stun_message.buffer == NULL) { +- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); +- continue; ++ j = p->stun_transactions->next; ++ ++ /* process all stun transactions except the first one */ ++ while (j) { ++ StunTransaction *s = j->data; ++ GSList *next = j->next; ++ ++ if (priv_timer_expired (&s->next_tick, &now)) ++ switch (stun_timer_refresh (&s->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: ++ priv_remove_stun_transaction (p, s, component); ++ break; ++ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: ++ timeout = stun_timer_remainder (&s->timer); ++ s->next_tick = now; ++ g_time_val_add (&s->next_tick, timeout * 1000); ++ break; ++ default: ++ break; ++ } ++ j = next; + } + +- if (!priv_timer_expired (&p->next_tick, &now)) ++ if (p->state != NICE_CHECK_IN_PROGRESS) + continue; + +- switch (stun_timer_refresh (&p->timer)) { +- case STUN_USAGE_TIMER_RETURN_TIMEOUT: +-timer_timeout: +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ /* process the first stun transaction of the list */ ++ stun = p->stun_transactions->data; ++ if (!priv_timer_expired (&stun->next_tick, &now)) ++ continue; + ++ switch (stun_timer_refresh (&stun->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: ++timer_return_timeout: + /* case: error, abort processing */ + nice_address_to_string (&p->local->addr, tmpbuf1); + nice_address_to_string (&p->remote->addr, tmpbuf2); +- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on " ++ "connectivity check %p", agent, p); + nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, + tmpbuf1, nice_address_get_port (&p->local->addr), + tmpbuf2, nice_address_get_port (&p->remote->addr)); +@@ -732,42 +799,33 @@ timer_timeout: + * a pair with a higher priority than this in-progress pair, + * ICE spec, sect 8.1.2 "Updating States", item 2.2 + */ +- if (!p->retransmit_on_timeout) +- goto timer_timeout; +- +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ if (!p->retransmit) ++ goto timer_return_timeout; + + /* case: not ready, so schedule a new timeout */ +- timeout = stun_timer_remainder (&p->timer); ++ timeout = stun_timer_remainder (&stun->timer); + + nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " + "(timer=%d/%d %d/%dms).", + agent, p, +- p->timer.retransmissions, p->timer.max_retransmissions, +- p->timer.delay - timeout, p->timer.delay); ++ stun->timer.retransmissions, stun->timer.max_retransmissions, ++ stun->timer.delay - timeout, stun->timer.delay); + + agent_socket_send (p->sockptr, &p->remote->addr, +- stun_message_length (&p->stun_message), +- (gchar *)p->stun_buffer); +- ++ stun_message_length (&stun->message), ++ (gchar *)stun->buffer); + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); + + return TRUE; + case STUN_USAGE_TIMER_RETURN_SUCCESS: +- timeout = stun_timer_remainder (&p->timer); ++ timeout = stun_timer_remainder (&stun->timer); + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); + + keep_timer_going = TRUE; + break; +@@ -948,7 +1006,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + g_assert (p->state == NICE_CHECK_SUCCEEDED); + nice_debug ("Agent %p : restarting check %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); +- p->recheck_on_timeout = FALSE; + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); + keep_timer_going = TRUE; +@@ -972,7 +1029,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; +- p->recheck_on_timeout = FALSE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); + keep_timer_going = TRUE; +@@ -2186,7 +2242,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + } + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); +- pair->retransmit_on_timeout = TRUE; + + stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, + (GCompareFunc)conn_check_compare); +@@ -2381,8 +2436,7 @@ static void conn_check_free_item (gpointer data) + + if (pair->agent) + priv_remove_pair_from_triggered_check_queue (pair->agent, pair); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; ++ priv_free_all_stun_transactions (pair, NULL); + g_slice_free (CandidateCheckPair, pair); + } + +@@ -2655,6 +2709,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + bool cand_use = controlling; + size_t buffer_len; + unsigned int timeout; ++ StunTransaction *stun; + + if (!agent_find_component (agent, pair->stream_id, pair->component_id, + &stream, &component)) +@@ -2718,13 +2773,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + + if (uname_len == 0) { + nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; + return -1; + } + ++ stun = priv_add_stun_transaction (pair); ++ + buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, +- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), ++ &stun->message, stun->buffer, sizeof(stun->buffer), + uname, uname_len, password, password_len, + cand_use, controlling, pair->prflx_priority, + agent->tie_breaker, +@@ -2732,7 +2787,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + agent_to_ice_compatibility (agent)); + + nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, +- pair->stun_message.buffer); ++ stun->message.buffer); + + if (agent->compatibility == NICE_COMPATIBILITY_MSN || + agent->compatibility == NICE_COMPATIBILITY_OC2007) { +@@ -2741,50 +2796,20 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + + if (buffer_len == 0) { + nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; ++ priv_remove_stun_transaction (pair, stun, component); + return -1; + } + + if (nice_socket_is_reliable(pair->sockptr)) { + timeout = agent->stun_reliable_timeout; +- stun_timer_start_reliable(&pair->timer, timeout); ++ stun_timer_start_reliable(&stun->timer, timeout); + } else { +- StunTimer *timer = &pair->timer; +- +- if (pair->recheck_on_timeout) { +- GTimeVal now; +- /* The pair recheck on timeout can easily cause repetitive rechecks in +- * a ping-pong effect, if both peers with the same behaviour try to +- * check the same pair almost simultaneously, and if the network rtt +- * is greater than the initial timer rto. The reply to the initial +- * stun request may arrive after the in-progress conncheck +- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation +- * creates a new stun request, and forgets the initial one. +- * The conncheck timer is restarted with the same initial value, +- * so the same situation happens again later. +- * +- * We choose to avoid resetting the timer in such situation. +- * After enough retransmissions, the timeout delay becomes +- * longer than the rtt, and the stun reply can be handled. +- */ +- +- g_get_current_time (&now); +- timeout = priv_timer_remainder (&pair->next_tick, &now); +- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", +- agent, pair, +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - timeout, +- timer->delay); +- } else { +- timeout = priv_compute_conncheck_timer (agent, stream); +- stun_timer_start (timer, timeout, agent->stun_max_retransmissions); +- } +- pair->recheck_on_timeout = FALSE; ++ timeout = priv_compute_conncheck_timer (agent, stream); ++ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); + } + +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); ++ g_get_current_time (&stun->next_tick); ++ g_time_val_add (&stun->next_tick, timeout * 1000); + + /* TCP-ACTIVE candidate must create a new socket before sending + * by connecting to the peer. The new socket is stored in the candidate +@@ -2814,10 +2839,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + } + /* send the conncheck */ + agent_socket_send (pair->sockptr, &pair->remote->addr, +- buffer_len, (gchar *)pair->stun_buffer); ++ buffer_len, (gchar *)stun->buffer); + + if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) +- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, ++ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, + &pair->remote->addr); + + return 0; +@@ -2881,8 +2906,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + (p->state == NICE_CHECK_WAITING && like_in_progress)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { +- p->retransmit_on_timeout = FALSE; +- p->recheck_on_timeout = FALSE; ++ p->retransmit = FALSE; + nice_debug ("Agent %p : pair %p will not be retransmitted.", + agent, p); + } else { +@@ -2938,9 +2962,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", + agent, p, p->foundation, priv_state_to_string (p->state)); + +- switch (p->state) { +- case NICE_CHECK_WAITING: +- case NICE_CHECK_FROZEN: ++ switch (p->state) { ++ case NICE_CHECK_WAITING: ++ case NICE_CHECK_FROZEN: + nice_debug ("Agent %p : pair %p added for a triggered check.", + agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); +@@ -2952,19 +2976,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * for that pair. The controlling role of this new check may + * be different from the role of this cancelled check. + * +- * note: the flag retransmit_on_timeout unset means that ++ * note: the flag retransmit unset means that + * another pair, with a higher priority is already nominated, + * so there's no reason to recheck this pair, since it can in + * no way replace the nominated one. + */ +- if (!nice_socket_is_reliable (p->sockptr) && +- p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p will be rechecked " +- "on stun timer timeout.", agent, p); +- /* this flag will determine the action at the retransmission +- * timeout of the stun timer ++ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ } ++ break; ++ case NICE_CHECK_FAILED: ++ if (p->retransmit) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers + */ +- p->recheck_on_timeout = TRUE; ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } + } + break; + case NICE_CHECK_SUCCEEDED: +@@ -2978,32 +3013,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + */ + priv_update_check_list_state_for_ready (agent, stream, component); + break; +- case NICE_CHECK_FAILED: +- /* 7.2.1.4 Triggered Checks +- * If the state of the pair is Failed, it is changed to Waiting +- * and the agent MUST create a new connectivity check for that +- * pair (representing a new STUN Binding request transaction), by +- * enqueueing the pair in the triggered check queue. +- * +- * note: the flag retransmit_on_timeout unset means that +- * another pair, with a higher priority is already nominated, +- * we apply the same strategy than with an in-progress pair +- * above. +- */ +- if (p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p added for a triggered check.", +- agent, p); +- priv_add_pair_to_triggered_check_queue (agent, p); +- /* If the component for this pair is in failed state, move it +- * back to connecting, and reinitiate the timers +- */ +- if (component->state == NICE_COMPONENT_STATE_FAILED) { +- agent_signal_component_state_change (agent, stream->id, +- component->id, NICE_COMPONENT_STATE_CONNECTING); +- conn_check_schedule_next (agent); +- } +- } +- break; + default: + break; + } +@@ -3260,16 +3269,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (new_pair == p) + p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; +- /* note: we cancel the potential in-progress transaction +- * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if +- * we receive a valid reply before transmission timeout... +- */ +- p->recheck_on_timeout = FALSE; +- /* ... or just after the transmission timeout, while the pair is +- * temporarily put on the triggered check list on the way to be +- * be rechecked: we remove it from the list too. +- */ + priv_remove_pair_from_triggered_check_queue (agent, p); ++ priv_free_all_stun_transactions (p, component); + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + nice_component_add_valid_candidate (component, remote_candidate); + } +@@ -3304,8 +3305,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ + p->state = NICE_CHECK_SUCCEEDED; +- p->recheck_on_timeout = FALSE; + priv_remove_pair_from_triggered_check_queue (agent, p); ++ priv_free_all_stun_transactions (p, component); + nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", + agent, p, new_pair); + } +@@ -3331,7 +3332,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + struct sockaddr addr; + } sockaddr; + socklen_t socklen = sizeof (sockaddr); +- GSList *i; ++ GSList *i, *j; ++ guint k; + StunUsageIceReturn res; + StunTransactionId discovery_id; + StunTransactionId response_id; +@@ -3340,193 +3342,186 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +- if (p->stun_message.buffer == NULL) +- continue; +- +- stun_message_id (&p->stun_message, discovery_id); +- +- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) +- continue; +- +- res = stun_usage_ice_conncheck_process (resp, +- &sockaddr.storage, &socklen, +- agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " +- "%s,res=%s.", +- agent, p, +- agent->controlling_mode ? "controlling" : "controlled", +- priv_ice_return_to_string (res)); +- +- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || +- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* case: found a matching connectivity check request */ +- +- CandidateCheckPair *ok_pair = NULL; +- +- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- +- /* step: verify that response came from the same IP address we +- * sent the original request to (see 7.1.2.1. "Failure +- * Cases") */ +- if (nice_address_equal (from, &p->remote->addr) == FALSE) { +- +- p->state = NICE_CHECK_FAILED; +- if (nice_debug_is_enabled ()) { +- gchar tmpbuf[INET6_ADDRSTRLEN]; +- gchar tmpbuf2[INET6_ADDRSTRLEN]; +- nice_debug ("Agent %p : conncheck %p FAILED" +- " (mismatch of source address).", agent, p); +- nice_address_to_string (&p->remote->addr, tmpbuf); +- nice_address_to_string (from, tmpbuf2); +- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, +- tmpbuf, nice_address_get_port (&p->remote->addr), +- tmpbuf2, nice_address_get_port (from)); +- } +- return TRUE; +- } +- +- /* note: CONNECTED but not yet READY, see docs */ +- +- /* step: handle the possible case of a peer-reflexive +- * candidate where the mapped-address in response does +- * not match any local candidate, see 7.1.2.2.1 +- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ +- +- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- p->state = NICE_CHECK_SUCCEEDED; +- p->valid = TRUE; +- nice_debug ("Agent %p : Mapped address not found." +- " conncheck %p SUCCEEDED.", agent, p); +- nice_component_add_valid_candidate (component, p->remote); +- } else +- ok_pair = priv_process_response_check_for_reflexive (agent, +- stream, component, p, sockptr, &sockaddr.addr, +- local_candidate, remote_candidate); +- +- /* note: The success of this check might also +- * cause the state of other checks to change as well, ICE +- * spec 7.1.3.2.3 +- */ +- priv_conn_check_unfreeze_related (agent, stream, p); +- +- /* Note: this assignment helps to reduce the numbers of cases +- * to be tested. If ok_pair and p refer to distinct pairs, it +- * means that ok_pair is a discovered peer reflexive one, +- * caused by the check made on pair p. In that case, the +- * flags to be tested are on p, but the nominated flag will be +- * set on ok_pair. When there's no discovered pair, p and +- * ok_pair refer to the same pair. +- * To summarize : p is a SUCCEEDED pair, ok_pair is a +- * DISCOVERED, VALID, and eventually NOMINATED pair. +- */ +- if (!ok_pair) +- ok_pair = p; +- +- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the +- Nominated Flag" (ID-19) */ +- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { +- nice_debug ("Agent %p : Updating nominated flag (%s): " +- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", +- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? +- "UDP" : "TCP", +- ok_pair, ok_pair->use_candidate_on_next_check, +- ok_pair->mark_nominated_on_response_arrival, +- p, p->use_candidate_on_next_check, +- p->mark_nominated_on_response_arrival); +- +- if (agent->controlling_mode) { +- switch (agent->nomination_mode) { +- case NICE_NOMINATION_MODE_REGULAR: +- if (p->use_candidate_on_next_check) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(regular nomination, controlling, " +- "use_cand_on_next_check=1).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- case NICE_NOMINATION_MODE_AGGRESSIVE: +- if (!p->nominated) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(aggressive nomination, controlling).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- default: +- /* Nothing to do */ +- break; +- } +- } else { +- if (p->mark_nominated_on_response_arrival) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(%s nomination, controlled, mark_on_response=1).", +- agent, ok_pair, ok_pair->foundation, +- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? +- "aggressive" : "regular"); +- ok_pair->nominated = TRUE; +- } +- } +- } +- +- if (ok_pair->nominated == TRUE) { +- priv_update_selected_pair (agent, component, ok_pair); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a nominated pair"); ++ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) { ++ StunTransaction *stun = j->data; ++ ++ stun_message_id (&stun->message, discovery_id); ++ ++ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) ++ continue; ++ ++ res = stun_usage_ice_conncheck_process (resp, ++ &sockaddr.storage, &socklen, ++ agent_to_ice_compatibility (agent)); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " ++ "%s,res=%s,stun#=%d.", ++ agent, p, ++ agent->controlling_mode ? "controlling" : "controlled", ++ priv_ice_return_to_string (res), k); ++ ++ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || ++ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ /* case: found a matching connectivity check request */ ++ ++ CandidateCheckPair *ok_pair = NULL; ++ ++ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); ++ priv_remove_stun_transaction (p, stun, component); ++ ++ /* step: verify that response came from the same IP address we ++ * sent the original request to (see 7.1.2.1. "Failure ++ * Cases") */ ++ if (nice_address_equal (from, &p->remote->addr) == FALSE) { ++ candidate_check_pair_fail (stream, agent, p); ++ if (nice_debug_is_enabled ()) { ++ gchar tmpbuf[INET6_ADDRSTRLEN]; ++ gchar tmpbuf2[INET6_ADDRSTRLEN]; ++ nice_debug ("Agent %p : conncheck %p FAILED" ++ " (mismatch of source address).", agent, p); ++ nice_address_to_string (&p->remote->addr, tmpbuf); ++ nice_address_to_string (from, tmpbuf2); ++ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, ++ tmpbuf, nice_address_get_port (&p->remote->addr), ++ tmpbuf2, nice_address_get_port (from)); ++ } ++ return TRUE; ++ } + +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } ++ /* note: CONNECTED but not yet READY, see docs */ ++ ++ /* step: handle the possible case of a peer-reflexive ++ * candidate where the mapped-address in response does ++ * not match any local candidate, see 7.1.2.2.1 ++ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ ++ ++ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ p->state = NICE_CHECK_SUCCEEDED; ++ p->valid = TRUE; ++ nice_debug ("Agent %p : Mapped address not found." ++ " conncheck %p SUCCEEDED.", agent, p); ++ nice_component_add_valid_candidate (component, p->remote); ++ } else ++ ok_pair = priv_process_response_check_for_reflexive (agent, ++ stream, component, p, sockptr, &sockaddr.addr, ++ local_candidate, remote_candidate); ++ ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ ++ if (!ok_pair) ++ ok_pair = p; ++ ++ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the ++ Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, controlling, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(aggressive nomination, controlling).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ default: ++ /* Nothing to do */ ++ break; ++ } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, controlled, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; ++ } ++ } ++ } + +- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair +- states" and 8.1.2 "Updating States", ID-19) */ +- priv_update_check_list_state_for_ready (agent, stream, component); +- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { +- /* case: role conflict error, need to restart with new role */ +- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- +- /* note: this res value indicates that the role of the peer +- * agent has not changed after the tie-breaker comparison, so +- * this is our role that must change. see ICE sect. 7.1.3.1 +- * "Failure Cases". Our role might already have changed due to +- * an earlier incoming request, but if not, change role now. +- * +- * Sect. 7.1.3.1 is not clear on this point, but we choose to +- * put the candidate pair in the triggered check list even +- * when the agent did not switch its role. The reason for this +- * interpretation is that the reception of the stun reply, even +- * an error reply, is a good sign that this pair will be +- * valid, if we retry the check after the role of both peers +- * has been fixed. +- */ ++ if (ok_pair->nominated == TRUE) { ++ priv_update_selected_pair (agent, component, ok_pair); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a nominated pair"); ++ ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } + +- if (p->stun_message.buffer != NULL) { ++ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair ++ states" and 8.1.2 "Updating States", ID-19) */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { + guint64 tie; + gboolean controlled_mode; + +- controlled_mode = (stun_message_find64 (&p->stun_message, ++ /* case: role conflict error, need to restart with new role */ ++ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); ++ ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ ++ controlled_mode = (stun_message_find64 (&stun->message, + STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == + STUN_MESSAGE_RETURN_SUCCESS); + + priv_check_for_role_conflict (agent, controlled_mode); +- +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++ priv_remove_stun_transaction (p, stun, component); + priv_add_pair_to_triggered_check_queue (agent, p); ++ } else { ++ /* case: STUN error, the check STUN context was freed */ ++ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); ++ candidate_check_pair_fail (stream, agent, p); + } +- } else { +- /* case: STUN error, the check STUN context was freed */ +- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++ return TRUE; + } +- return TRUE; + } + + return FALSE; +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 909d469..e16dc67 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -71,6 +71,15 @@ typedef enum + } NiceCheckState; + + typedef struct _CandidateCheckPair CandidateCheckPair; ++typedef struct _StunTransaction StunTransaction; ++ ++struct _StunTransaction ++{ ++ GTimeVal next_tick; /* next tick timestamp */ ++ StunTimer timer; ++ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; ++ StunMessage message; ++}; + + struct _CandidateCheckPair + { +@@ -86,16 +95,12 @@ struct _CandidateCheckPair + gboolean valid; + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; +- gboolean recheck_on_timeout; +- gboolean retransmit_on_timeout; +- struct _CandidateCheckPair *discovered_pair; +- struct _CandidateCheckPair *succeeded_pair; ++ gboolean retransmit; /* if the first stun request must be retransmitted */ ++ CandidateCheckPair *discovered_pair; ++ CandidateCheckPair *succeeded_pair; + guint64 priority; + guint32 prflx_priority; +- GTimeVal next_tick; /* next tick timestamp */ +- StunTimer timer; +- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; +- StunMessage stun_message; ++ GSList *stun_transactions; /* a list of ongoing stun requests */ + }; + + int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); +-- +2.13.6 + + +From 6fe64fdbc53ab87dffd79972f492665cff14c0a0 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 27 Jun 2017 11:01:14 +0200 +Subject: [PATCH 59/70] conncheck: update the state of triggered checks pairs + +With this patch, we fix an ambiguity of some parts of the spec, when +the document refers to in-progress pairs, that also concern pairs in +the triggered checks list. + +The first cast is in section 7.1.2.5, "Updating the Nominated Flag", +when the in-progress pair will be nominated on response arrival. This is +handled in function priv_mark_pair_nominated(), when a pair is put to +the triggered check list in reaction to a matching inbound stun request. +Such a pair in priv_mark_pair_nominated() will _always_ be in the +triggered check list, from the previously called function +priv_schedule_triggered_check(). + +The second case is in section 8.1.2, "Updating State" when an in-progress +pair stops its retransmission when another pair of higher priority is +already nominated. This is handled by function priv_prune_pending_checks(). + +Until now, pairs enqueued in the triggered check list move transiently +to state waiting, according to 7.2.1.4. But this state causes wrong +decisions in the two previous cases, because such pairs should in fact +rather be considered "like in-progress", to avoid discarding them +inadvertantly. + +This patch update the state of the triggered check list +pairs to in-progress. It allows to remove exception handling cited +above: the code is a bit more simple, and allows some refactoring +in priv_mark_pair_nominated() between RFC and compatibility modes. + +Differential Revision:
https://phabricator.freedesktop.org/D1762
+--- + agent/conncheck.c | 96 +++++++++++++++---------------------------------------- + 1 file changed, 25 insertions(+), 71 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 2a85738..628c708 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -244,16 +244,6 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + } + } + +-/* Verify if the pair is in the triggered checks list +- */ +- +-static gboolean +-priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) +-{ +- g_assert (pair); +- return (g_slist_find (agent->triggered_check_queue, pair) != NULL); +-} +- + /* Add the pair to the triggered checks list, if not already present + */ + static void +@@ -261,8 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa + { + g_assert (pair); + +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ pair->state = NICE_CHECK_IN_PROGRESS; ++ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); + if (agent->triggered_check_queue == NULL || + g_slist_find (agent->triggered_check_queue, pair) == NULL) + agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); +@@ -841,12 +831,6 @@ timer_return_timeout: + */ + pair = priv_conn_check_find_next_waiting (stream->conncheck_list); + if (pair) { +- /* remove the pair from the triggered check list if needed. This +- * may happen with the cancelled pair, that's just been added +- * in state waiting to the triggered check list above in the +- * same function. +- */ +- priv_remove_pair_from_triggered_check_queue (agent, pair); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair in Waiting state"); + priv_conn_check_initiate (agent, pair); +@@ -1109,7 +1093,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + if (pair) { + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair from triggered check list"); +- priv_conn_check_initiate (agent, pair); ++ conn_check_send (agent, pair); + return TRUE; + } + +@@ -2098,8 +2082,7 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + /* step: search for at least one nominated pair */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *pair = i->data; +- if (pair->local == localcand && pair->remote == remotecand && +- NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ if (pair->local == localcand && pair->remote == remotecand) { + /* ICE, 7.2.1.5. Updating the Nominated Flag */ + /* note: TCP candidates typically produce peer reflexive + * candidate, generating a "discovered" pair that can be +@@ -2111,44 +2094,27 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + g_assert (pair->state == NICE_CHECK_DISCOVERED); + } + +- if (pair->valid) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated", +- agent, pair, pair->foundation); +- pair->nominated = TRUE; +- priv_update_selected_pair (agent, component, pair); +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state == NICE_COMPONENT_STATE_FAILED) +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); +- if (component->state == NICE_COMPONENT_STATE_CONNECTING) +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- priv_update_check_list_state_for_ready (agent, stream, component); +- } else if (pair->state == NICE_CHECK_IN_PROGRESS) { ++ /* If the state of this pair is In-Progress, [...] the resulting ++ * valid pair has its nominated flag set when the response ++ * arrives. ++ */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && ++ pair->state == NICE_CHECK_IN_PROGRESS) { + pair->mark_nominated_on_response_arrival = TRUE; + nice_debug ("Agent %p : pair %p (%s) is in-progress, " + "will be nominated on response receipt.", + agent, pair, pair->foundation); + } +- /* note: this case is not covered by the ICE spec, 7.2.1.5, +- * "Updating the Nominated Flag", but a pair in waiting state +- * deserves the same treatment than a pair in-progress. A pair +- * can be in waiting state as the result of being enqueued in +- * the triggered check list for example. +- */ +- if (pair->state == NICE_CHECK_WAITING) { +- pair->mark_nominated_on_response_arrival = TRUE; +- nice_debug ("Agent %p : pair %p (%s) is waiting, " +- "will be nominated on response receipt.", ++ ++ if (pair->valid || ++ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated", + agent, pair, pair->foundation); ++ pair->nominated = TRUE; + } +- } else if (pair->local == localcand && pair->remote == remotecand) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); +- pair->nominated = TRUE; ++ + if (pair->valid) { +- priv_update_selected_pair (agent, component, pair); ++ priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ + if (component->state == NICE_COMPONENT_STATE_FAILED) + agent_signal_component_state_change (agent, +@@ -2159,7 +2125,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); + } +- priv_update_check_list_state_for_ready (agent, stream, component); ++ ++ if (pair->nominated) ++ priv_update_check_list_state_for_ready (agent, stream, component); + } + } + } +@@ -2731,12 +2699,12 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + nice_address_to_string (&pair->local->addr, tmpbuf1); + nice_address_to_string (&pair->remote->addr, tmpbuf2); + nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " +- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " ++ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " + "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr), + pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, +- pair->foundation, pair->component_id, ++ pair, pair->component_id, + (unsigned long long)agent->tie_breaker, + (int) uname_len, uname, uname_len, + (int) password_len, password, password_len, +@@ -2877,35 +2845,21 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ +- /* note: this case is not covered by the ICE spec, 8.1.2 +- * "Updating States", but a pair in waiting state which will be +- * nominated on response receipt should be treated the same way +- * that an in-progress pair. A pair in waiting state and in +- * the triggered check list should also be treated like an in-progress +- * pair. +- */ + i = stream->conncheck_list; + while (i) { + CandidateCheckPair *p = i->data; + GSList *next = i->next; + + if (p->component_id == component_id) { +- gboolean like_in_progress = +- p->mark_nominated_on_response_arrival || +- priv_is_pair_in_triggered_check_queue (agent, p); +- +- if (p->state == NICE_CHECK_FROZEN || +- (p->state == NICE_CHECK_WAITING && !like_in_progress)) { ++ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) { + nice_debug ("Agent %p : pair %p removed.", agent, p); + conn_check_free_item (p); + stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- else if (p->state == NICE_CHECK_IN_PROGRESS || +- (p->state == NICE_CHECK_WAITING && like_in_progress)) { +- if (highest_nominated_priority != 0 && +- p->priority < highest_nominated_priority) { ++ else if (p->state == NICE_CHECK_IN_PROGRESS) { ++ if (p->priority < highest_nominated_priority) { + p->retransmit = FALSE; + nice_debug ("Agent %p : pair %p will not be retransmitted.", + agent, p); +-- +2.13.6 + + +From 72dd26a3368d3506fe8faca7067a02784fb5f0fd Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Wed, 28 Jun 2017 12:06:48 +0200 +Subject: [PATCH 60/70] conncheck: forgot to put a pair in triggered check list + +When a new pair is created from an unknown remote candidate, it +should be enqueue for a triggered check, to allow it to be marked +as nominated on response arrival in priv_mark_pair_nominated(). +Creating it in waiting state is not sufficient since the update +in priv_mark_pair_nominated() from previous commits. + +Differential Revision:
https://phabricator.freedesktop.org/D1763
+--- + agent/conncheck.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 628c708..0e3ce88 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2893,11 +2893,12 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + { + GSList *i; + NiceCandidate *local = NULL; ++ CandidateCheckPair *p; + + g_assert (remote_cand != NULL); + + for (i = stream->conncheck_list; i ; i = i->next) { +- CandidateCheckPair *p = i->data; ++ p = i->data; + if (p->component_id == component->id && + p->remote == remote_cand && + ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && +@@ -2986,8 +2987,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + + if (i) { + nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); +- priv_add_new_check_pair (agent, stream->id, component, ++ p = priv_add_new_check_pair (agent, stream->id, component, + local, remote_cand, NICE_CHECK_WAITING); ++ priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } + else { +-- +2.13.6 + + +From 14102d44449d2eb4148588ce54fa897fa13b87ad Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 2 Jul 2017 16:02:09 +0200 +Subject: [PATCH 61/70] conncheck: change state before updating nominated pairs + +When a pair is nominated while in state failed, we first move +back to state connecting, then we update the selected pair, and +finally we move to state connected. +--- + agent/conncheck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 0e3ce88..e584c0e 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2114,11 +2114,11 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + } + + if (pair->valid) { +- priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ + if (component->state == NICE_COMPONENT_STATE_FAILED) + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ priv_update_selected_pair (agent, component, pair); + if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ +-- +2.13.6 + + +From 1a1803a45778000720c93d91060cedeb19124a27 Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Tue, 12 Sep 2017 13:23:31 +0100 +Subject: [PATCH 62/70] tests: Fix copyright dates in test-gstreamer.c + +This code is not 1000 years old. + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +--- + tests/test-gstreamer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c +index 74d7133..a0be68e 100644 +--- a/tests/test-gstreamer.c ++++ b/tests/test-gstreamer.c +@@ -1,7 +1,7 @@ + /* + * This file is part of the Nice GLib ICE library. + * +- * (C) 1015 Kurento. ++ * (C) 2015 Kurento. + * Contact: Jose Antonio Santos Cadenas + * + * The contents of this file are subject to the Mozilla Public License Version +-- +2.13.6 + + +From 4c4834ab634f735145c8f758a22cbdd9cab79bac Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Tue, 12 Sep 2017 13:23:53 +0100 +Subject: [PATCH 63/70] tests: Fix agent.h header inclusion in test-gstreamer.c + +Spotted by Lukas Gradl on the mailing list. + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +--- + tests/test-gstreamer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c +index a0be68e..f060efc 100644 +--- a/tests/test-gstreamer.c ++++ b/tests/test-gstreamer.c +@@ -34,7 +34,7 @@ + */ + + #include <gst/check/gstcheck.h> +-#include <nice/agent.h> ++#include "agent.h" + + #define RTP_HEADER_SIZE 12 + #define RTP_PAYLOAD_SIZE 1024 +-- +2.13.6 + + +From fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Thu, 3 Aug 2017 12:20:32 +0100 +Subject: [PATCH 64/70] stun: Fix FD leak in test/utility code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +https://phabricator.freedesktop.org/T7798 + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1819
+--- + stun/usages/bind.c | 29 ++++++++++++++++++++++------- + 1 file changed, 22 insertions(+), 7 deletions(-) + +diff --git a/stun/usages/bind.c b/stun/usages/bind.c +index d56790f..ee600a0 100644 +--- a/stun/usages/bind.c ++++ b/stun/usages/bind.c +@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen); + if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { + stun_debug ("STUN transaction failed: couldn't create transport."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) { + stun_debug ("STUN transaction failed: couldn't send request."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, +@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + switch (stun_timer_refresh (&timer)) { + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + stun_debug ("STUN transaction failed: time out."); +- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! ++ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! ++ goto done; + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: + stun_debug ("STUN transaction retransmitted (timeout %dms).", + stun_timer_remainder (&timer)); + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) { + stun_debug ("STUN transaction failed: couldn't resend request."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + continue; + case STUN_USAGE_TIMER_RETURN_SUCCESS: +@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + + valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL); + if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE) +- return STUN_USAGE_BIND_RETURN_ERROR; ++ { ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; ++ } + + if (valid != STUN_VALIDATION_SUCCESS) { + ret = STUN_USAGE_TRANS_RETURN_RETRY; +@@ -554,12 +561,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + (struct sockaddr *) &alternate_server, alternate_server_len); + + if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) +- return STUN_USAGE_BIND_RETURN_ERROR; ++ { ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; ++ } + + stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, + STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +@@ -573,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + } + while (ret == STUN_USAGE_TRANS_RETURN_RETRY); + ++done: ++ if (trans.fd != -1) ++ stun_trans_deinit (&trans); ++ + return bind_ret; + } +-- +2.13.6 + + +From 02216a6766caccb652387d5ee19686149eedbc93 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 21 Nov 2017 15:12:45 +0100 +Subject: [PATCH 65/70] agent: prevent external role change while conncheck is + running + +With this patch, we stash the controlling mode property change, and +apply it safely, when it won't interfere with an ongoing conncheck +running. According to RFC5245, sect 5.2. "Determining Role", the role +is determined for a session, and persists unless an ICE is restarted. + +Differential Revision:
https://phabricator.freedesktop.org/D1887
+--- + agent/agent-priv.h | 4 +++- + agent/agent.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- + tests/test-restart.c | 15 ++++++++++++++ + 3 files changed, 74 insertions(+), 3 deletions(-) + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 714ecff..7269be0 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -146,7 +146,7 @@ struct _NiceAgent + NiceProxyType proxy_type; /* property: Proxy type */ + gchar *proxy_username; /* property: Proxy username */ + gchar *proxy_password; /* property: Proxy password */ +- gboolean controlling_mode; /* property: controlling-mode */ ++ gboolean saved_controlling_mode;/* property: controlling-mode */ + guint timer_ta; /* property: timer Ta */ + guint max_conn_checks; /* property: max connectivity checks */ + gboolean force_relay; /* property: force relay */ +@@ -190,6 +190,8 @@ struct _NiceAgent + gboolean use_ice_tcp; + + guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ ++ gboolean controlling_mode; /* controlling mode used by the ++ conncheck */ + /* XXX: add pointer to internal data struct for ABI-safe extensions */ + }; + +diff --git a/agent/agent.c b/agent/agent.c +index a4dcc0c..0773c53 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -405,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass) + 1, /* not a construct property, ignored */ + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:controlling-mode: ++ * ++ * Whether the agent has the controlling role. This property should ++ * be modified before gathering candidates, any modification occuring ++ * later will be hold until ICE is restarted. ++ */ + g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE, + g_param_spec_boolean ( + "controlling-mode", +@@ -1107,6 +1114,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) + } + + static void ++priv_update_controlling_mode (NiceAgent *agent, gboolean value) ++{ ++ gboolean update_controlling_mode; ++ GSList *i, *j; ++ ++ agent->saved_controlling_mode = value; ++ /* It is safe to update the agent controlling mode when all ++ * components are still in state disconnected. When we leave ++ * this state, the role must stay under the control of the ++ * conncheck algorithm exclusively, until the conncheck is ++ * eventually restarted. See RFC5245, sect 5.2. Determining Role ++ */ ++ if (agent->controlling_mode != agent->saved_controlling_mode) { ++ update_controlling_mode = TRUE; ++ for (i = agent->streams; ++ i && update_controlling_mode; i = i->next) { ++ NiceStream *stream = i->data; ++ for (j = stream->components; ++ j && update_controlling_mode; j = j->next) { ++ NiceComponent *component = j->data; ++ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED) ++ update_controlling_mode = FALSE; ++ } ++ } ++ if (update_controlling_mode) { ++ agent->controlling_mode = agent->saved_controlling_mode; ++ nice_debug ("Agent %p : Property set, changing role to \"%s\".", ++ agent, agent->controlling_mode ? "controlling" : "controlled"); ++ } else { ++ nice_debug ("Agent %p : Property set, role switch requested " ++ "but conncheck already started.", agent); ++ nice_debug ("Agent %p : Property set, staying with role \"%s\" " ++ "until restart.", agent, ++ agent->controlling_mode ? "controlling" : "controlled"); ++ } ++ } else ++ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent, ++ agent->controlling_mode ? "controlling" : "controlled"); ++} ++ ++static void + nice_agent_init (NiceAgent *agent) + { + agent->next_candidate_id = 1; +@@ -1115,6 +1163,7 @@ nice_agent_init (NiceAgent *agent) + /* set defaults; not construct params, so set here */ + agent->stun_server_port = DEFAULT_STUN_PORT; + agent->controlling_mode = TRUE; ++ agent->saved_controlling_mode = TRUE; + agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; + agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; + +@@ -1213,7 +1262,7 @@ nice_agent_get_property ( + break; + + case PROP_CONTROLLING_MODE: +- g_value_set_boolean (value, agent->controlling_mode); ++ g_value_set_boolean (value, agent->saved_controlling_mode); + break; + + case PROP_FULL_MODE: +@@ -1422,7 +1471,7 @@ nice_agent_set_property ( + break; + + case PROP_CONTROLLING_MODE: +- agent->controlling_mode = g_value_get_boolean (value); ++ priv_update_controlling_mode (agent, g_value_get_boolean (value)); + break; + + case PROP_FULL_MODE: +@@ -4930,6 +4979,11 @@ nice_agent_restart ( + /* step: regenerate tie-breaker value */ + priv_generate_tie_breaker (agent); + ++ /* step: reset controlling mode from the property value */ ++ agent->controlling_mode = agent->saved_controlling_mode; ++ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".", ++ agent, agent->controlling_mode ? "controlling" : "controlled"); ++ + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; + +diff --git a/tests/test-restart.c b/tests/test-restart.c +index c2cbe9a..afc51b6 100644 +--- a/tests/test-restart.c ++++ b/tests/test-restart.c +@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands); + cdes.addr = laddr_rtcp; + nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands); ++ /* This role switch request will be effective after restart. We test ++ * here that the role cannot be externally modified after conncheck ++ * has started. */ ++ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL); ++ g_assert (ragent->controlling_mode == FALSE); + + g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed..."); + +@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + global_ragent_read = 0; + g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16); + ++ /* Both agent have a distinct role at the end of the conncheck */ ++ g_assert (lagent->controlling_mode == TRUE); ++ g_assert (ragent->controlling_mode == FALSE); + /* step: restart agents, exchange updated credentials */ + tie_breaker = ragent->tie_breaker; + nice_agent_restart (ragent); + g_assert (tie_breaker != ragent->tie_breaker); ++ /* This role switch of ragent should be done now, and both agents ++ * have now the same role, which should generate a role conflict ++ * resolution situation */ ++ g_assert (lagent->controlling_mode == TRUE); ++ g_assert (ragent->controlling_mode == TRUE); + nice_agent_restart (lagent); + { + gchar *ufrag = NULL, *password = NULL; +@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + /* note: verify binding requests were resent after restart */ + g_assert (global_lagent_ibr_received == TRUE); + g_assert (global_ragent_ibr_received == TRUE); ++ /* note: verify that a role switch occured for one of the agents */ ++ g_assert (ragent->controlling_mode != lagent->controlling_mode); + + g_debug ("test-restart: Ran mainloop, removing streams..."); + +-- +2.13.6 + + +From c63349894b3fe974494453a883dfb5ad05df5a46 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 23 Nov 2017 18:31:31 +0100 +Subject: [PATCH 66/70] Makefile: really enable debug for tests + +Differential Revision:
https://phabricator.freedesktop.org/D1888
+--- + tests/Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index b623764..e94822d 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" + + AM_TESTS_ENVIRONMENT = \ + G_MESSAGES_DEBUG=all \ +- NICE_DEBUG=all; ++ NICE_DEBUG=all + + COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) + +-- +2.13.6 + + +From 17f30e4465efe9533799b02d6f95feeaf0f2748c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miguel=20Par=C3=ADs?= <mparisdiaz(a)gmail.com> +Date: Wed, 8 Nov 2017 16:26:47 +0000 +Subject: [PATCH 67/70] conncheck: do not require that all streams have a + connection check list + +One or more streams might not have any connection check list if the +number of streams differs from the peer agent. +Differential Revision:
https://phabricator.freedesktop.org/D1880
+--- + agent/conncheck.c | 23 ---- + tests/Makefile.am | 3 + + tests/test-different-number-streams.c | 208 ++++++++++++++++++++++++++++++++++ + 3 files changed, 211 insertions(+), 23 deletions(-) + create mode 100644 tests/test-different-number-streams.c + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index e584c0e..beb43c3 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -402,23 +402,6 @@ priv_conn_check_find_next_frozen (GSList *conn_check_list) + } + + /* +- * Returns the number of check lists of the agent +- */ +-static guint +-priv_number_of_check_lists (NiceAgent *agent) +-{ +- guint n = 0; +- GSList *i; +- +- for (i = agent->streams; i ; i = i->next) { +- NiceStream *stream = i->data; +- if (stream->conncheck_list != NULL) +- n++; +- } +- return n; +-} +- +-/* + * Returns the number of active check lists of the agent + */ + static guint +@@ -1060,12 +1043,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + gboolean keep_timer_going = FALSE; + GSList *i, *j; + +- /* the conncheck really starts when we have built +- * a connection check list for each stream +- */ +- if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) +- return TRUE; +- + /* configure the initial state of the check lists of the agent + * as described in ICE spec, 5.7.4 + * +diff --git a/tests/Makefile.am b/tests/Makefile.am +index e94822d..30d6f8e 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -46,6 +46,7 @@ check_PROGRAMS = \ + test-socket-is-based-on \ + test-priority \ + test-fullmode \ ++ test-different-number-streams \ + test-restart \ + test-fallback \ + test-thread \ +@@ -114,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD) + + test_fullmode_LDADD = $(COMMON_LDADD) + ++test_different_number_streams_LDADD = $(COMMON_LDADD) ++ + test_restart_LDADD = $(COMMON_LDADD) + + test_fallback_LDADD = $(COMMON_LDADD) +diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c +new file mode 100644 +index 0000000..7fd4763 +--- /dev/null ++++ b/tests/test-different-number-streams.c +@@ -0,0 +1,208 @@ ++#ifdef HAVE_CONFIG_H ++# include <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#define ADD_2_STREAMS TRUE ++#define USE_SECOND_STREAM TRUE ++ ++static GMainLoop *global_mainloop = NULL; ++ ++static guint global_components_ready = 0; ++static guint global_components_ready_exit = 0; ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id); ++} ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)", ++ agent, stream_id, component_id, nice_component_state_to_string (state)); ++ ++ if (state == NICE_COMPONENT_STATE_READY) { ++ global_components_ready++; ++ } ++ ++ /* signal status via a global variable */ ++ if (global_components_ready == global_components_ready_exit) { ++ g_debug ("Components ready/failed achieved. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ } ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL, *i; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id); ++} ++ ++int main (void) ++{ ++ NiceAgent *lagent, *ragent; ++ guint timer_id; ++ guint ls_id, rs_id_1, rs_id_2; ++ gchar *lufrag = NULL, *lpassword = NULL; ++ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL; ++ ++#ifdef G_OS_WIN32 ++ WSADATA w; ++ ++ WSAStartup(0x0202, &w); ++#endif ++ ++ global_mainloop = g_main_loop_new (NULL, FALSE); ++ ++ /* step: create the agents L and R */ ++ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_GOOGLE); ++ g_debug ("lagent: %p", lagent); ++ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent"); ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), NULL); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), NULL); ++ ++ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_GOOGLE); ++ g_debug ("ragent: %p", ragent); ++ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent"); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), NULL); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), NULL); ++ ++ /* step: add a timer to catch state changes triggered by signals */ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ ls_id = nice_agent_add_stream (lagent, 2); ++ g_assert (ls_id > 0); ++ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword); ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ ++ global_components_ready_exit = 4; ++ ++ if (ADD_2_STREAMS) { ++ rs_id_1 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_1 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); ++ ++ rs_id_2 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_2 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2); ++ ++ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword); ++ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2); ++ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); ++ ++ if (USE_SECOND_STREAM) { ++ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP); ++ } else { ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); ++ } ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ } else { ++ rs_id_1 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_1 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); ++ ++ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword); ++ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1); ++ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); ++ } ++ ++ /* step: run the mainloop until connectivity checks succeed ++ * (see timer_cb() above) */ ++ g_main_loop_run (global_mainloop); ++ ++ g_free (lufrag); ++ g_free (lpassword); ++ g_free (rufrag1); ++ g_free (rpassword1); ++ g_free (rufrag2); ++ g_free (rpassword2); ++ g_object_unref (lagent); ++ g_object_unref (ragent); ++ ++ g_main_loop_unref (global_mainloop); ++ global_mainloop = NULL; ++ ++ g_source_remove (timer_id); ++ ++#ifdef G_OS_WIN32 ++ WSACleanup(); ++#endif ++ ++ return 0; ++} +-- +2.13.6 + + +From 59fcf95d505c3995f858b826d10cd48321ed383e Mon Sep 17 00:00:00 2001 +From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Mon, 27 Nov 2017 17:07:02 -0500 +Subject: [PATCH 68/70] turn: Add support for ALTERNATE_SERVER in OC2007 + Compatibility + +The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in +allocation responses, and if they are not handled, we end up using the +main turn server to send allocation requests that then get sent to the +alternate server which will return the XOR_MAPPED_ADDRESS containing +the IP address of the turn server that proxied the message instead of +our own actual external IP. +--- + agent/conncheck.c | 14 ++++++++++++++ + stun/usages/turn.c | 11 +++++++++++ + stun/usages/turn.h | 4 ++++ + 3 files changed, 29 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index beb43c3..229c8b1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3764,6 +3764,20 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + recv_realm = (uint8_t *) stun_message_find (resp, + STUN_ATTRIBUTE_REALM, &recv_realm_len); + ++ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && ++ alternatelen != sizeof(alternate)) { ++ NiceAddress alternate_addr; ++ ++ nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); ++ ++ if (!nice_address_equal (&alternate_addr, &d->server)) { ++ nice_address_set_from_sockaddr (&d->server, &alternate.addr); ++ nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ ++ d->pending = FALSE; ++ } ++ } + /* check for unauthorized error response */ + if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || + agent->compatibility == NICE_COMPATIBILITY_OC2007 || +diff --git a/stun/usages/turn.c b/stun/usages/turn.c +index 3b94959..ec12642 100644 +--- a/stun/usages/turn.c ++++ b/stun/usages/turn.c +@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, + stun_debug (" STUN error message received (code: %d)", code); + + /* ALTERNATE-SERVER mechanism */ ++ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 && ++ alternate_server && alternate_server_len && ++ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER, ++ alternate_server, ++ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) { ++ stun_debug ("Found alternate server"); ++ /* The ALTERNATE_SERVER will always be returned by the MS turn server. ++ * We need to check if the ALTERNATE_SERVER is the same as the current ++ * server to decide whether we need to switch servers or not. ++ */ ++ } + if ((code / 100) == 3) { + if (alternate_server && alternate_server_len) { + if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, +diff --git a/stun/usages/turn.h b/stun/usages/turn.h +index 7a2d4e6..83fa00a 100644 +--- a/stun/usages/turn.h ++++ b/stun/usages/turn.h +@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, + * Allocate request, in case the currently used TURN server is requesting the use + * of an alternate server. This argument will only be filled if the return value + * of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER ++ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the ++ * @alternate_server could be filled at any time, and should only be considered ++ * if the request was sent to a different server than the address returned ++ * in the @alternate_server field + * @alternate_server_len: The length of @alternate_server + * @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us + * @lifetime: A pointer to fill with the lifetime of the allocation +-- +2.13.6 + + +From 4172d48852ecd1c86cc7bd4665b23697603d1eed Mon Sep 17 00:00:00 2001 +From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Tue, 28 Nov 2017 15:14:11 -0500 +Subject: [PATCH 69/70] discovery: Increase discovery_unsched_items whenever we + restart a check + +The discovery_unsched_items is decremented every time a DiscoveryCandidate +goes from non-pending to pending. So if we restart a check by setting +pending to FALSE, we should re-increase the discovery_unsched_items. +--- + agent/conncheck.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 229c8b1..5b08311 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3507,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa + d->server = niceaddr; + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { + /* case: successful binding discovery, create a new local candidate */ + +@@ -3648,6 +3649,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || + res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { + /* case: successful allocate, create a new local candidate */ +@@ -3776,6 +3778,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } + } + /* check for unauthorized error response */ +@@ -3798,6 +3801,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + d->stun_resp_msg.buffer = d->stun_resp_buffer; + d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer); + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else { + /* case: a real unauthorized error */ + d->stun_message.buffer = NULL; +-- +2.13.6 + + +From fb2f1f77a31baa91968fc81c205f980b6913f403 Mon Sep 17 00:00:00 2001 +From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Tue, 28 Nov 2017 16:05:18 -0500 +Subject: [PATCH 70/70] conncheck: handle alternate-server for turn relays + differently + +If a relay gives us an alternate-server, we need to cancel and reset +every candidate discovery attempt that uses the same server, to avoid +ending up with one component on one server and the other component on +another server (causing relay candidates with mismatched foundations). +--- + agent/conncheck.c | 56 ++++++++++++++++++++++++++++++++++++++++++------------- + 1 file changed, 43 insertions(+), 13 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 5b08311..c8a4edf 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3592,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand + return cand; + } + ++static void priv_handle_turn_alternate_server (NiceAgent *agent, ++ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate) ++{ ++ /* We need to cancel and reset all candidate discovery turn for the same ++ stream and type if there is an alternate server. Otherwise, we might end up ++ with two relay components on different servers, creating candidates with ++ unique foundations that only contain one component. ++ */ ++ GSList *i; ++ ++ for (i = agent->discovery_list; i; i = i->next) { ++ CandidateDiscovery *d = i->data; ++ ++ if (!d->done && ++ d->type == disco->type && ++ d->stream == disco->stream && ++ d->turn->type == disco->turn->type && ++ nice_address_equal (&d->server, &server)) { ++ gchar ip[INET6_ADDRSTRLEN]; ++ // Cancel the pending request to avoid a race condition with another ++ // component responding with another altenrate-server ++ d->stun_message.buffer = NULL; ++ d->stun_message.buffer_len = 0; ++ ++ nice_address_to_string (&server, ip); ++ nice_debug ("Agent %p : Cancelling and setting alternate server %s for " ++ "CandidateDiscovery %p", agent, ip, d); ++ d->server = alternate; ++ d->turn->server = alternate; ++ d->pending = FALSE; ++ agent->discovery_unsched_items++; ++ } ++ } ++} ++ + /* + * Tries to match STUN reply in 'buf' to an existing STUN discovery + * transaction. If found, a reply is sent. +@@ -3644,12 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + agent, d, (int)res); + + if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) { +- /* handle alternate server */ +- nice_address_set_from_sockaddr (&d->server, &alternate.addr); +- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ NiceAddress addr; + +- d->pending = FALSE; +- agent->discovery_unsched_items++; ++ /* handle alternate server */ ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); + } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || + res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { + /* case: successful allocate, create a new local candidate */ +@@ -3769,16 +3803,12 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || + agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && + alternatelen != sizeof(alternate)) { +- NiceAddress alternate_addr; +- +- nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); ++ NiceAddress addr; + +- if (!nice_address_equal (&alternate_addr, &d->server)) { +- nice_address_set_from_sockaddr (&d->server, &alternate.addr); +- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); + +- d->pending = FALSE; +- agent->discovery_unsched_items++; ++ if (!nice_address_equal (&addr, &d->server)) { ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); + } + } + /* check for unauthorized error response */ +-- +2.13.6 + +From db6166ee247a8f9fa4ebe2a08d223de73cbd2e96 Mon Sep 17 00:00:00 2001 +From: Jozsef Vass <jozsef(a)discordapp.com> +Date: Mon, 8 Jan 2018 10:53:23 -0800 +Subject: [PATCH 01/15] agent: Fixes incompatible pointer type warning on OSX. + +The variable tie is actually never read. +--- + agent/conncheck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index c8a4edf..38a90cd 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3421,7 +3421,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + states" and 8.1.2 "Updating States", ID-19) */ + priv_update_check_list_state_for_ready (agent, stream, component); + } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { +- guint64 tie; ++ uint64_t tie; + gboolean controlled_mode; + + /* case: role conflict error, need to restart with new role */ +-- +2.14.3 + + +From ae3e5acc775ee6c1701ff9a2404b14e4d5dd6c20 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 26 Nov 2017 17:13:19 +0100 +Subject: [PATCH 02/15] conncheck: rework early stun requests handling +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this patch we simplify the code used to handle the incoming stun +request when remote candidates or remote credentials have not been +received yet. + +When the remote credentials is unknown, the stun request is stored +in a list of incoming_checks for later processing, and no further +processing is done, except responding to the request. + +When the remote credentials are received, the triggered checks for these +incoming checks can now be queued, and the related pairs are created. + +If the remote candidates have not been received when the stun request +on a valid local port arrives, a peer-reflexive remote candidate will be +created. This candidate may need to be updated later when remote +candidates are finally received, including candidate priority and +foundation, and also related pairs. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1889
+--- + agent/agent.c | 42 ++++++++++++-- + agent/conncheck.c | 168 ++++++++++-------------------------------------------- + agent/conncheck.h | 1 + + 3 files changed, 66 insertions(+), 145 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 0773c53..49fc371 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3300,6 +3300,28 @@ nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr) + return TRUE; + } + ++/* Recompute foundations of all candidate pairs from a given stream ++ * having a specific remote candidate ++ */ ++static void priv_update_pair_foundations (NiceAgent *agent, ++ guint stream_id, NiceCandidate *remote) ++{ ++ NiceStream *stream = agent_find_stream (agent, stream_id); ++ if (stream) { ++ GSList *i; ++ for (i = stream->conncheck_list; i; i = i->next) { ++ CandidateCheckPair *pair = i->data; ++ if (pair->remote == remote) { ++ g_snprintf (pair->foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", ++ pair->local->foundation, pair->remote->foundation); ++ nice_debug ("Agent %p : Updating pair %p foundation to '%s'", ++ agent, pair, pair->foundation); ++ } ++ } ++ } ++} ++ + static gboolean priv_add_remote_candidate ( + NiceAgent *agent, + guint stream_id, +@@ -3331,8 +3353,7 @@ static gboolean priv_add_remote_candidate ( + + /* If it was a discovered remote peer reflexive candidate, then it should + * be updated according to RFC 5245 section 7.2.1.3 */ +- if (candidate && candidate->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE && +- candidate->priority == priority) { ++ if (candidate && candidate->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) { + nice_debug ("Agent %p : Updating existing peer-rfx remote candidate to %s", + agent, _cand_type_to_sdp (type)); + candidate->type = type; +@@ -3375,6 +3396,13 @@ static gboolean priv_add_remote_candidate ( + g_free (candidate->password); + candidate->password = g_strdup (password); + } ++ ++ /* since the type of the existing candidate may have changed, ++ * the pairs priority and foundation related to this candidate need ++ * to be recomputed. ++ */ ++ recalculate_pair_priorities (agent); ++ priv_update_pair_foundations (agent, stream_id, candidate); + } + else { + /* case 2: add a new candidate */ +@@ -3429,12 +3457,14 @@ static gboolean priv_add_remote_candidate ( + if (foundation) + g_strlcpy (candidate->foundation, foundation, + NICE_CANDIDATE_MAX_FOUNDATION); +- } + +- if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0) { +- goto errors; ++ /* We only create a pair when a candidate is new, and not when ++ * updating an existing one. ++ */ ++ if (conn_check_add_for_candidate (agent, stream_id, ++ component, candidate) < 0) ++ goto errors; + } +- + return TRUE; + + errors: +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 38a90cd..11ef9c9 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1690,34 +1690,6 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair * + return 0; + } + +-/* +- * Preprocesses a new connectivity check by going through list +- * of a any stored early incoming connectivity checks from +- * the remote peer. If a matching incoming check has been already +- * received, update the state of the new outgoing check 'pair'. +- * +- * @param agent context pointer +- * @param stream which stream (of the agent) +- * @param component pointer to component object to which 'pair'has been added +- * @param pair newly added connectivity check +- */ +-static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *pair) +-{ +- GSList *i; +- for (i = component->incoming_checks; i; i = i->next) { +- IncomingCheck *icheck = i->data; +- if (nice_address_equal (&icheck->from, &pair->remote->addr) && +- icheck->local_socket == pair->sockptr) { +- nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); +- priv_schedule_triggered_check (agent, stream, component, +- icheck->local_socket, pair->remote); +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); +- } +- } +-} +- +- + /* + * Handle any processing steps for connectivity checks after + * remote credentials have been set. This function handles +@@ -1728,126 +1700,39 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + */ + void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + { +- GSList *j, *k, *l, *m, *n, *o; ++ GSList *j, *k, *l, *m; + +- for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *pair = j->data; +- NiceComponent *component = +- nice_stream_find_component_by_id (stream, pair->component_id); +- gboolean match = FALSE; +- +- /* performn delayed processing of spec steps section 7.2.1.4, +- and section 7.2.1.5 */ +- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); ++ for (j = stream->components; j ; j = j->next) { ++ NiceComponent *component = j->data; + + for (k = component->incoming_checks; k; k = k->next) { + IncomingCheck *icheck = k->data; + /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to + * be handled separately */ + for (l = component->remote_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&icheck->from, &cand->addr)) { +- match = TRUE; +- break; +- } +- } +- if (match != TRUE) { +- /* note: we have gotten an incoming connectivity check from +- * an address that is not a known remote candidate */ +- +- NiceCandidate *local_candidate = NULL; +- NiceCandidate *remote_candidate = NULL; +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || +- agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- /* We need to find which local candidate was used */ +- uint8_t uname[NICE_STREAM_MAX_UNAME]; +- guint uname_len; +- +- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " +- "stored pending check", agent); +- +- for (m = component->remote_candidates; +- m != NULL && remote_candidate == NULL; m = m->next) { +- for (n = component->local_candidates; n; n = n->next) { +- NiceCandidate *rcand = m->data; +- NiceCandidate *lcand = n->data; +- +- uname_len = priv_create_username (agent, stream, +- component->id, rcand, lcand, +- uname, sizeof (uname), TRUE); +- +- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", +- icheck->username_len, uname_len, +- icheck->username && uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0); +- stun_debug_bytes (" first username: ", +- icheck->username, +- icheck->username? icheck->username_len : 0); +- stun_debug_bytes (" second username: ", uname, uname_len); +- +- if (icheck->username && +- uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0) { +- local_candidate = lcand; +- remote_candidate = rcand; +- break; +- } +- } +- } +- } else { +- for (l = component->local_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; ++ NiceCandidate *rcand = l->data; ++ NiceCandidate *lcand = NULL; ++ if (nice_address_equal (&rcand->addr, &icheck->from)) { ++ for (m = component->local_candidates; m; m = m->next) { ++ NiceCandidate *cand = m->data; + if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { +- local_candidate = cand; ++ lcand = cand; + break; + } + } +- } +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && +- local_candidate == NULL) { +- /* if we couldn't match the username, then the matching remote +- * candidate hasn't been received yet.. we must wait */ +- nice_debug ("Agent %p : Username check failed. pending check has " +- "to wait to be processed", agent); +- } else { +- NiceCandidate *candidate; +- +- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", +- agent); +- candidate = +- discovery_learn_remote_peer_reflexive_candidate (agent, +- stream, +- component, +- icheck->priority, +- &icheck->from, +- icheck->local_socket, +- local_candidate, remote_candidate); +- if (candidate) { +- if (local_candidate && +- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) +- priv_conn_check_add_for_candidate_pair_matched (agent, +- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); +- else +- conn_check_add_for_candidate (agent, stream->id, component, candidate); +- +- priv_schedule_triggered_check (agent, stream, component, +- icheck->local_socket, candidate); +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); +- } ++ g_assert (lcand != NULL); ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, rcand); ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, ++ lcand, rcand); ++ break; + } + } + } +- } +- +- /* Once we process the pending checks, we should free them to avoid +- * reprocessing them again if a dribble-mode set_remote_candidates +- * is called */ +- for (o = stream->components; o; o = o->next) { +- NiceComponent *component = o->data; ++ /* Once we process the pending checks, we should free them to avoid ++ * reprocessing them again if a dribble-mode set_remote_candidates ++ * is called */ + g_slist_free_full (component->incoming_checks, + (GDestroyNotify) incoming_check_free); + component->incoming_checks = NULL; +@@ -2964,8 +2849,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + + if (i) { + nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); +- p = priv_add_new_check_pair (agent, stream->id, component, +- local, remote_cand, NICE_CHECK_WAITING); ++ p = priv_conn_check_add_for_candidate_pair_matched (agent, stream->id, ++ component, local, remote_cand, NICE_CHECK_WAITING); + priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } +@@ -3018,7 +2903,12 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr); + } + +- if (rcand) { ++ /* We react to this stun request when we have the remote credentials. ++ * When credentials are not yet known, this request is stored ++ * in incoming_checks for later processing when returning from this ++ * function. ++ */ ++ if (rcand && stream->remote_ufrag[0]) { + priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); + if (use_candidate) + priv_mark_pair_nominated (agent, stream, component, lcand, rcand); +@@ -3114,7 +3004,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + * Recalculates priorities of all candidate pairs. This + * is required after a conflict in ICE roles. + */ +-static void priv_recalculate_pair_priorities (NiceAgent *agent) ++void recalculate_pair_priorities (NiceAgent *agent) + { + GSList *i, *j; + +@@ -3143,7 +3033,7 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) + agent->controlling_mode = control; + /* the pair priorities depend on the roles, so recalculation + * is needed */ +- priv_recalculate_pair_priorities (agent); ++ recalculate_pair_priorities (agent); + } + else + nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", +diff --git a/agent/conncheck.h b/agent/conncheck.h +index e16dc67..8cfe2d6 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -119,5 +119,6 @@ conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *co + NiceSocket *sock); + + guint32 ensure_unique_priority (NiceComponent *component, guint32 priority); ++void recalculate_pair_priorities (NiceAgent *agent); + + #endif /*_NICE_CONNCHECK_H */ +-- +2.14.3 + + +From 025d84b53bd4ffc0626dd25aa8351319f4d77944 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 26 Nov 2017 17:36:27 +0100 +Subject: [PATCH 03/15] test-new-dribble: make credentials swap asymmetric +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +the first case of test-new-dribble (standard-test) is updated, by making +the credentials swap between the left and right agent asymmetric. +Previously, ragent started to receive stun requests without initially +knowing lagent candidates. Now, ragent also ignores lagent credentials. +This modification allows to test changes introduced by the previous +commit. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1890
+--- + tests/test-new-dribble.c | 55 ++++++++++++++++++++---------------------------- + 1 file changed, 23 insertions(+), 32 deletions(-) + +diff --git a/tests/test-new-dribble.c b/tests/test-new-dribble.c +index 3e60ae3..947f55d 100644 +--- a/tests/test-new-dribble.c ++++ b/tests/test-new-dribble.c +@@ -269,7 +269,7 @@ static gpointer stun_thread_func (const gpointer user_data) + return NULL; + } + +-static void set_credentials (NiceAgent *lagent, guint lstream, ++static void swap_credentials (NiceAgent *lagent, guint lstream, + NiceAgent *ragent, guint rstream) + { + gchar *ufrag = NULL, *password = NULL; +@@ -279,12 +279,6 @@ static void set_credentials (NiceAgent *lagent, guint lstream, + + g_free (ufrag); + g_free (password); +- +- nice_agent_get_local_credentials (ragent, rstream, &ufrag, &password); +- nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); +- +- g_free (ufrag); +- g_free (password); + } + + static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) +@@ -500,12 +494,10 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent) + g_cancellable_reset (global_cancellable); + g_assert (ragent_candidate_gathering_done); + +- set_credentials (lagent, global_ls_id, ragent, global_rs_id); + + g_debug ("Setting local candidates of ragent as remote candidates of lagent"); +- swap_candidates (ragent, global_rs_id, +- lagent, global_ls_id, +- TRUE); ++ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, TRUE); ++ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); + + while (!data_received) + g_main_context_iteration (NULL, TRUE); +@@ -514,9 +506,9 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent) + data_received); + + g_debug ("Setting local candidates of lagent as remote candidates of ragent"); +- swap_candidates (lagent, global_ls_id, +- ragent, global_rs_id, +- FALSE); ++ swap_candidates (lagent, global_ls_id, ragent, global_rs_id, FALSE); ++ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); ++ + while (!lagent_candidate_gathering_done) + g_main_context_iteration (NULL, TRUE); + g_cancellable_reset (global_cancellable); +@@ -557,22 +549,21 @@ static void bad_credentials_test(NiceAgent *lagent, NiceAgent *ragent) + g_cancellable_reset (global_cancellable); + g_assert (ragent_candidate_gathering_done); + +- swap_candidates (ragent, global_rs_id, +- lagent, global_ls_id, +- FALSE); ++ g_debug ("Setting local candidates of ragent as remote candidates of lagent"); ++ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, FALSE); ++ + while (global_lagent_state != NICE_COMPONENT_STATE_FAILED) + g_main_context_iteration (NULL, TRUE); + g_cancellable_reset (global_cancellable); + + // Set the correct credentials and swap candidates +- set_credentials (lagent, global_ls_id, ragent, global_rs_id); +- swap_candidates (ragent, global_rs_id, +- lagent, global_ls_id, +- FALSE); ++ g_debug ("Setting local candidates of ragent as remote candidates of lagent"); ++ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, FALSE); ++ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); + +- swap_candidates (lagent, global_ls_id, +- ragent, global_rs_id, +- FALSE); ++ g_debug ("Setting local candidates of lagent as remote candidates of ragent"); ++ swap_candidates (lagent, global_ls_id, ragent, global_rs_id, FALSE); ++ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); + + while (!data_received) + g_main_context_iteration (NULL, TRUE); +@@ -628,15 +619,14 @@ static void bad_candidate_test(NiceAgent *lagent,NiceAgent *ragent) + + g_assert (global_lagent_state == NICE_COMPONENT_STATE_FAILED && + !data_received); +- set_credentials (lagent, global_ls_id, ragent, global_rs_id); + +- swap_candidates (ragent, global_rs_id, +- lagent, global_ls_id, +- FALSE); ++ g_debug ("Setting local candidates of ragent as remote candidates of lagent"); ++ swap_candidates (ragent, global_rs_id, lagent, global_ls_id, FALSE); ++ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); + +- swap_candidates (lagent, global_ls_id, +- ragent, global_rs_id, +- FALSE); ++ g_debug ("Setting local candidates of lagent as remote candidates of ragent"); ++ swap_candidates (lagent, global_ls_id, ragent, global_rs_id, FALSE); ++ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); + + while (!data_received) + g_main_context_iteration (NULL, TRUE); +@@ -655,7 +645,8 @@ static void new_candidate_test(NiceAgent *lagent, NiceAgent *ragent) + g_debug ("test-dribblemode:%s", G_STRFUNC); + + init_test (lagent, ragent, TRUE); +- set_credentials (lagent, global_ls_id, ragent, global_rs_id); ++ swap_credentials (lagent, global_ls_id, ragent, global_rs_id); ++ swap_credentials (ragent, global_rs_id, lagent, global_ls_id); + + nice_agent_gather_candidates (lagent, global_ls_id); + while (!got_stun_packet) +-- +2.14.3 + + +From b5dd5e2aa72a68ac9f027bbcc22700db84a35677 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 26 Nov 2017 17:49:25 +0100 +Subject: [PATCH 04/15] conncheck: the conncheck send function may fail +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this patch, we put the pair in state failed if we cannot send +the connection check, for example due to missing local credentials. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1891
+--- + agent/conncheck.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 11ef9c9..9618c3a 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -442,7 +442,11 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * + { + pair->state = NICE_CHECK_IN_PROGRESS; + nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); +- conn_check_send (agent, pair); ++ if (conn_check_send (agent, pair)) { ++ pair->state = NICE_CHECK_FAILED; ++ nice_debug ("Agent %p : pair %p state FAILED", agent, pair); ++ return FALSE; ++ } + return TRUE; + } + +@@ -1070,7 +1074,11 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + if (pair) { + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair from triggered check list"); +- conn_check_send (agent, pair); ++ if (conn_check_send (agent, pair)) { ++ pair->state = NICE_CHECK_FAILED; ++ nice_debug ("Agent %p : pair %p state FAILED", agent, pair); ++ return FALSE; ++ } + return TRUE; + } + +-- +2.14.3 + + +From 47aa02885cda9ddf6e938f966a926be000611c5a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 26 Nov 2017 18:10:12 +0100 +Subject: [PATCH 05/15] conncheck: factorize pair state debug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1892
+--- + agent/conncheck.c | 69 +++++++++++++++++++++++++------------------------------ + 1 file changed, 31 insertions(+), 38 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 9618c3a..00d02c5 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -123,22 +123,29 @@ priv_state_to_string (NiceCheckState state) + { + switch (state) { + case NICE_CHECK_WAITING: +- return "waiting"; ++ return "WAITING"; + case NICE_CHECK_IN_PROGRESS: +- return "in progress"; ++ return "IN_PROGRESS"; + case NICE_CHECK_SUCCEEDED: +- return "succeeded"; ++ return "SUCCEEDED"; + case NICE_CHECK_FAILED: +- return "failed"; ++ return "FAILED"; + case NICE_CHECK_FROZEN: +- return "frozen"; ++ return "FROZEN"; + case NICE_CHECK_DISCOVERED: +- return "discovered"; ++ return "DISCOVERED"; + default: + g_assert_not_reached (); + } + } + ++#define SET_PAIR_STATE( a, p, s ) G_STMT_START{\ ++ g_assert (p); \ ++ p->state = s; \ ++ nice_debug ("Agent %p : pair %p state %s (%s)", \ ++ a, p, priv_state_to_string (s), G_STRFUNC); \ ++}G_STMT_END ++ + static const gchar * + priv_ice_return_to_string (StunUsageIceReturn ice_return) + { +@@ -251,8 +258,7 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa + { + g_assert (pair); + +- pair->state = NICE_CHECK_IN_PROGRESS; +- nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS); + if (agent->triggered_check_queue == NULL || + g_slist_find (agent->triggered_check_queue, pair) == NULL) + agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); +@@ -440,11 +446,9 @@ priv_find_first_frozen_check_list (NiceAgent *agent) + */ + static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) + { +- pair->state = NICE_CHECK_IN_PROGRESS; +- nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_IN_PROGRESS); + if (conn_check_send (agent, pair)) { +- pair->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, pair); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED); + return FALSE; + } + return TRUE; +@@ -495,8 +499,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *str + if (pair) { + nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", + agent, pair, pair->stream_id, pair->component_id, pair->foundation); +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING); + result = TRUE; + } + } +@@ -535,8 +538,7 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + strncmp (p->foundation, ok_check->foundation, + NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { + nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); +- p->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING); + } + } + } +@@ -559,8 +561,7 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + if (p->state == NICE_CHECK_FROZEN && + priv_foundation_matches_a_valid_pair (p->foundation, stream)) { + nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); +- p->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING); + } + } + } else if (priv_is_checklist_frozen (s)) { +@@ -576,8 +577,7 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { + match_found = TRUE; + nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); +- p->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ SET_PAIR_STATE (agent, p, NICE_CHECK_WAITING); + } + } + +@@ -675,8 +675,7 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP + NiceComponent *component; + + component = nice_stream_find_component_by_id (stream, p->component_id); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ SET_PAIR_STATE (agent, p, NICE_CHECK_FAILED); + priv_free_all_stun_transactions (p, component); + } + +@@ -841,8 +840,7 @@ timer_return_timeout: + if (pair) { + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair in Frozen state"); +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_WAITING); + priv_conn_check_initiate (agent, pair); + return TRUE; + } +@@ -1075,8 +1073,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair from triggered check list"); + if (conn_check_send (agent, pair)) { +- pair->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, pair); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_FAILED); + return FALSE; + } + return TRUE; +@@ -2067,8 +2064,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation); + + pair->priority = agent_candidate_pair_priority (agent, local, remote); +- pair->state = initial_state; +- nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state); ++ nice_debug ("Agent %p : creating a new pair", agent); ++ SET_PAIR_STATE (agent, pair, initial_state); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; + gchar tmpbuf2[INET6_ADDRSTRLEN]; +@@ -2976,10 +2973,10 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->local = local_cand; + pair->remote = parent_pair->remote; + pair->sockptr = local_cand->sockptr; +- pair->state = NICE_CHECK_DISCOVERED; + parent_pair->discovered_pair = pair; + pair->succeeded_pair = parent_pair; +- nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); ++ nice_debug ("Agent %p : creating a new pair", agent); ++ SET_PAIR_STATE (agent, pair, NICE_CHECK_DISCOVERED); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; + gchar tmpbuf2[INET6_ADDRSTRLEN]; +@@ -3099,10 +3096,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + */ + if (new_pair == p) + p->valid = TRUE; +- p->state = NICE_CHECK_SUCCEEDED; ++ SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED); + priv_remove_pair_from_triggered_check_queue (agent, p); + priv_free_all_stun_transactions (p, component); +- nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + nice_component_add_valid_candidate (component, remote_candidate); + } + else { +@@ -3135,11 +3131,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + /* step: The agent sets the state of the pair that *generated* the check to + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ +- p->state = NICE_CHECK_SUCCEEDED; ++ SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED); + priv_remove_pair_from_triggered_check_queue (agent, p); + priv_free_all_stun_transactions (p, component); +- nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", +- agent, p, new_pair); + } + + if (new_pair && new_pair->valid) +@@ -3226,10 +3220,9 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + * "Discovering Peer Reflexive Candidates" ICE ID-19) */ + + if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- p->state = NICE_CHECK_SUCCEEDED; ++ nice_debug ("Agent %p : Mapped address not found", agent); ++ SET_PAIR_STATE (agent, p, NICE_CHECK_SUCCEEDED); + p->valid = TRUE; +- nice_debug ("Agent %p : Mapped address not found." +- " conncheck %p SUCCEEDED.", agent, p); + nice_component_add_valid_candidate (component, p->remote); + } else + ok_pair = priv_process_response_check_for_reflexive (agent, +-- +2.14.3 + + +From 05f1e30239a448385709df0edd43ec3ac5173218 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 26 Nov 2017 19:31:39 +0100 +Subject: [PATCH 06/15] conncheck: make debug more homonegeous +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1893
+--- + agent/conncheck.c | 35 ++++++++++++++++++----------------- + 1 file changed, 18 insertions(+), 17 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 00d02c5..25bfd80 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -753,8 +753,8 @@ timer_return_timeout: + /* case: error, abort processing */ + nice_address_to_string (&p->local->addr, tmpbuf1); + nice_address_to_string (&p->remote->addr, tmpbuf2); +- nice_debug ("Agent %p : Retransmissions failed, giving up on " +- "connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p", ++ agent, p); + nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, + tmpbuf1, nice_address_get_port (&p->local->addr), + tmpbuf2, nice_address_get_port (&p->remote->addr)); +@@ -973,7 +973,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p = p->succeeded_pair; + } + g_assert (p->state == NICE_CHECK_SUCCEEDED); +- nice_debug ("Agent %p : restarting check %p with " ++ nice_debug ("Agent %p : restarting check of pair %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); +@@ -996,7 +996,8 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + if (p->component_id == component->id && + (p->state == NICE_CHECK_SUCCEEDED || + p->state == NICE_CHECK_DISCOVERED)) { +- nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); ++ nice_debug ("Agent %p : restarting check of pair %p as the " ++ "nominated pair.", agent, p); + p->nominated = TRUE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); +@@ -2081,7 +2082,9 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, + (GCompareFunc)conn_check_compare); + +- nice_debug ("Agent %p : added a new conncheck %p with foundation of '%s' to list %u.", agent, pair, pair->foundation, stream_id); ++ nice_debug ("Agent %p : added a new pair %p with foundation '%s' to " ++ "stream %u component %u.", agent, pair, pair->foundation, stream_id, ++ component->id); + + /* implement the hard upper limit for number of + checks (see sect 5.7.3 ICE ID-19): */ +@@ -2117,9 +2120,6 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + { + CandidateCheckPair *pair; + +- nice_debug ("Agent %p : Adding check pair between %s and %s for s%d/c%d", +- agent, local->foundation, remote->foundation, +- stream_id, component->id); + pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, + initial_state); + if (component->state == NICE_COMPONENT_STATE_CONNECTED || +@@ -2997,7 +2997,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->nominated = FALSE; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local_cand)); +- nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); ++ nice_debug ("Agent %p : added a new peer-discovered pair with " ++ "foundation '%s'.", agent, pair->foundation); + + stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, + (GCompareFunc)conn_check_compare); +@@ -3190,7 +3191,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + + CandidateCheckPair *ok_pair = NULL; + +- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); ++ nice_debug ("Agent %p : pair %p MATCHED.", agent, p); + priv_remove_stun_transaction (p, stun, component); + + /* step: verify that response came from the same IP address we +@@ -3201,7 +3202,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + if (nice_debug_is_enabled ()) { + gchar tmpbuf[INET6_ADDRSTRLEN]; + gchar tmpbuf2[INET6_ADDRSTRLEN]; +- nice_debug ("Agent %p : conncheck %p FAILED" ++ nice_debug ("Agent %p : pair %p FAILED" + " (mismatch of source address).", agent, p); + nice_address_to_string (&p->remote->addr, tmpbuf); + nice_address_to_string (from, tmpbuf2); +@@ -3316,7 +3317,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + gboolean controlled_mode; + + /* case: role conflict error, need to restart with new role */ +- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); ++ nice_debug ("Agent %p : Role conflict with pair %p, restarting", ++ agent, p); + + /* note: this res value indicates that the role of the peer + * agent has not changed after the tie-breaker comparison, so +@@ -3341,7 +3343,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + priv_add_pair_to_triggered_check_queue (agent, p); + } else { + /* case: STUN error, the check STUN context was freed */ +- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); + candidate_check_pair_fail (stream, agent, p); + } + return TRUE; +@@ -4228,8 +4229,8 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + agent_signal_initial_binding_request_received (agent, stream); + + if (remote_candidate == NULL) { +- nice_debug ("Agent %p : No matching remote candidate for incoming check ->" +- "peer-reflexive candidate.", agent); ++ nice_debug ("Agent %p : No matching remote candidate for incoming " ++ "check -> peer-reflexive candidate.", agent); + remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( + agent, stream, component, priority, from, nicesock, + local_candidate, +@@ -4332,8 +4333,8 @@ conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *co + if ((p->local != NULL && p->local->sockptr == sock) || + (p->remote != NULL && p->remote->sockptr == sock) || + (p->sockptr == sock)) { +- nice_debug ("Agent %p : Retransmissions failed, giving up on " +- "connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on pair %p", ++ agent, p); + candidate_check_pair_fail (stream, agent, p); + conn_check_free_item (p); + stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l); +-- +2.14.3 + + +From 00dfcc6a625e6c6ed758a4b3b4d0858113508a2f Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 27 Nov 2017 23:56:17 +0100 +Subject: [PATCH 07/15] socket: ping the stun server address on the right + socket +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Verify the compatibility of the socket domain with the stun server +IP address, before sending a request. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1894
+--- + agent/agent.c | 12 +++++++----- + agent/conncheck.c | 4 +++- + 2 files changed, 10 insertions(+), 6 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 49fc371..3306378 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3062,11 +3062,13 @@ nice_agent_gather_candidates ( + if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) { + nice_address_set_port (&stun_server, agent->stun_server_port); + +- priv_add_new_candidate_discovery_stun (agent, +- host_candidate->sockptr, +- stun_server, +- stream, +- cid); ++ if (nice_address_ip_version (&host_candidate->addr) == ++ nice_address_ip_version (&stun_server)) ++ priv_add_new_candidate_discovery_stun (agent, ++ host_candidate->sockptr, ++ stun_server, ++ stream, ++ cid); + } + } + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 25bfd80..4d91f41 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1459,7 +1459,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) + for (k = component->local_candidates; k; k = k->next) { + NiceCandidate *candidate = (NiceCandidate *) k->data; + if (candidate->type == NICE_CANDIDATE_TYPE_HOST && +- candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP) { ++ candidate->transport == NICE_CANDIDATE_TRANSPORT_UDP && ++ nice_address_ip_version (&candidate->addr) == ++ nice_address_ip_version (&stun_server)) { + /* send the conncheck */ + nice_debug ("Agent %p : resending STUN on %s to keep the " + "candidate alive.", agent, candidate->foundation); +-- +2.14.3 + + +From ea05a3d51990d17bbe25984eb5730849f46bfae0 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Wed, 29 Nov 2017 11:04:04 +0100 +Subject: [PATCH 08/15] conncheck: dont fail a stream with a empty conncheck + list +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Since commit 17f30e4, we may have a stream with an empty conncheck list, +and such a stream obviously should not be tested for failed components. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1895
+--- + agent/conncheck.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 4d91f41..0ebe7e9 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1832,6 +1832,9 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + * must be fetched before entering the loop*/ + guint c, components = stream->n_components; + ++ if (stream->conncheck_list == NULL) ++ return; ++ + for (i = agent->discovery_list; i; i = i->next) { + CandidateDiscovery *d = i->data; + +@@ -1846,8 +1849,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + + /* note: iterate the conncheck list for each component separately */ + for (c = 0; c < components; c++) { +- NiceComponent *comp = NULL; +- if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) ++ NiceComponent *component = NULL; ++ if (!agent_find_component (agent, stream->id, c+1, NULL, &component)) + continue; + + nominated = 0; +@@ -1873,7 +1876,7 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + * Set the component to FAILED only if it actually had remote candidates + * that failed.. */ + if (completed && nominated == 0 && +- comp != NULL && comp->remote_candidates != NULL) ++ component != NULL && component->remote_candidates != NULL) + agent_signal_component_state_change (agent, + stream->id, + (c + 1), /* component-id */ +-- +2.14.3 + + +From a9ac0487b0d1708d780d7c0b7a2206c71a8c7163 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 30 Nov 2017 20:11:22 +0100 +Subject: [PATCH 09/15] discovery: ignore all non-relay local candidates when + relay is forced +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The tcp server reflexive discovered local candidates must be ignored +when force_relay is set. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1899
+--- + agent/discovery.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/agent/discovery.c b/agent/discovery.c +index 4cc99c2..e2142a2 100644 +--- a/agent/discovery.c ++++ b/agent/discovery.c +@@ -688,7 +688,8 @@ discovery_discover_tcp_server_reflexive_candidates ( + + caddr = c->addr; + nice_address_set_port (&caddr, 0); +- if (c->transport != NICE_CANDIDATE_TRANSPORT_UDP && ++ if (agent->force_relay == FALSE && ++ c->transport != NICE_CANDIDATE_TRANSPORT_UDP && + c->type == NICE_CANDIDATE_TYPE_HOST && + nice_address_equal (&base_addr, &caddr)) { + nice_address_set_port (address, nice_address_get_port (&c->addr)); +-- +2.14.3 + + +From 5a644f459dc75c80dfb19c7772f74e37a0258771 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 11 Dec 2017 08:50:33 +0100 +Subject: [PATCH 10/15] agent: make candidate username and password immutable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this patch we prevent the username and the password of a candidate +to be modified during a session, as required by the RFC, sect 9.1.2. +This is also needed from a memory management point of view, because the +password string pointer may be recorded in the components stun agent +sent_ids[] struct key member, and freeing these values there may cause +an use-after-free condition, when an inbound stun is received from this +candidate. This behavior has been observed with pidgin, xmpp, and +farstream when a same remote candidates are "updated" several times, +even if the credentials don't change in this case. + +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1917
+--- + agent/agent.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 3306378..dbece3b 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3388,15 +3388,22 @@ static gboolean priv_add_remote_candidate ( + * this is essential to overcome a race condition where we might receive + * a valid binding request from a valid candidate that wasn't yet added to + * our list of candidates.. this 'update' will make the peer-rflx a +- * server-rflx/host candidate again and restore that user/pass it needed +- * to have in the first place */ ++ * server-rflx/host candidate again */ + if (username) { +- g_free (candidate->username); +- candidate->username = g_strdup (username); ++ if (candidate->username == NULL) ++ candidate->username = g_strdup (username); ++ else if (g_strcmp0 (username, candidate->username)) ++ nice_debug ("Agent %p : Candidate username '%s' is not allowed " ++ "to change to '%s' now (ICE restart only).", agent, ++ candidate->username, username); + } + if (password) { +- g_free (candidate->password); +- candidate->password = g_strdup (password); ++ if (candidate->password == NULL) ++ candidate->password = g_strdup (password); ++ else if (g_strcmp0 (password, candidate->password)) ++ nice_debug ("Agent %p : candidate password '%s' is not allowed " ++ "to change to '%s' now (ICE restart only).", agent, ++ candidate->password, password); + } + + /* since the type of the existing candidate may have changed, +-- +2.14.3 + + +From 54fb03427ebc13413cd1ddd5d9e91c1751eac0cb Mon Sep 17 00:00:00 2001 +From: Jakub Adam <jakub.adam(a)ktknet.cz> +Date: Sat, 3 Feb 2018 23:59:20 +0100 +Subject: [PATCH 11/15] discovery: ignore bogus Skype for Business srflx + addresses + +If main SfB TURN server sends our allocation request to an alternate +server, the response will have XOR_MAPPED_ADDRESS containing the IP +address of the turn server that proxied the message instead of our own +actual external IP. + +Before we create server reflexive candidates upon receiving an allocate +response, check that the TURN port got assigned on the same server we +sent out allocate request to. Otherwise, the request was proxied and +XOR_MAPPED_ADDRESS contains a bogus value we should ignore. + +Issue introduced by 59fcf95d505c3995f858b826d10cd48321ed383e. +Differential Revision:
https://phabricator.freedesktop.org/D1949
+--- + agent/conncheck.c | 31 +++++++++++++++++++++---------- + 1 file changed, 21 insertions(+), 10 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 0ebe7e9..19729c2 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3587,9 +3587,13 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + NiceAddress niceaddr; + NiceCandidate *relay_cand; + ++ nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr); ++ + if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { ++ NiceAddress mappedniceaddr; ++ + /* We also received our mapped address */ +- nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr); ++ nice_address_set_from_sockaddr (&mappedniceaddr, &sockaddr.addr); + + /* TCP or TLS TURNS means the server-reflexive address was + * on a TCP connection, which cannot be used for server-reflexive +@@ -3601,21 +3605,28 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + d->agent, + d->stream->id, + d->component->id, +- &niceaddr, ++ &mappedniceaddr, + NICE_CANDIDATE_TRANSPORT_UDP, + d->nicesock, + FALSE); + } +- if (d->agent->use_ice_tcp) +- discovery_discover_tcp_server_reflexive_candidates ( +- d->agent, +- d->stream->id, +- d->component->id, +- &niceaddr, +- d->nicesock); ++ if (d->agent->use_ice_tcp) { ++ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && ++ !nice_address_equal_no_port (&niceaddr, &d->turn->server)) { ++ nice_debug("TURN port got allocated on an alternate server, " ++ "ignoring bogus srflx address"); ++ } else { ++ discovery_discover_tcp_server_reflexive_candidates ( ++ d->agent, ++ d->stream->id, ++ d->component->id, ++ &mappedniceaddr, ++ d->nicesock); ++ } ++ } + } + +- nice_address_set_from_sockaddr (&niceaddr, &relayaddr.addr); + if (nice_socket_is_reliable (d->nicesock)) { + relay_cand = discovery_add_relay_candidate ( + d->agent, +-- +2.14.3 + + +From 922ee4e61b4d9c6b403933f4a3261e67589d5099 Mon Sep 17 00:00:00 2001 +From: Jakub Adam <jakub.adam(a)ktknet.cz> +Date: Wed, 19 Apr 2017 14:17:04 +0200 +Subject: [PATCH 12/15] agent: don't require "reliable" be TRUE in order to use + "ice-tcp" + +Setting writable socket callbacks doesn't have to be limited to reliable +agents. TCP sockets need the callback in any case for correct operation +and calling nice_socket_set_writable_callback() on a NiceSocket that has +UDP as its base has no effect. + +Differential Revision:
https://phabricator.freedesktop.org/D1726
+--- + agent/agent.c | 11 ++++------- + agent/conncheck.c | 5 ++--- + 2 files changed, 6 insertions(+), 10 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index dbece3b..89e3514 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -2547,9 +2547,8 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, + if (nicesock == NULL) + return; + +- if (agent->reliable) +- nice_socket_set_writable_callback (nicesock, _tcp_sock_is_writable, +- component); ++ nice_socket_set_writable_callback (nicesock, _tcp_sock_is_writable, component); ++ + if (turn->type == NICE_RELAY_TYPE_TURN_TLS && + agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { + nicesock = nice_pseudossl_socket_new (nicesock, +@@ -3033,10 +3032,8 @@ nice_agent_gather_candidates ( + found_local_address = TRUE; + nice_address_set_port (addr, 0); + +- +- if (agent->reliable) +- nice_socket_set_writable_callback (host_candidate->sockptr, +- _tcp_sock_is_writable, component); ++ nice_socket_set_writable_callback (host_candidate->sockptr, ++ _tcp_sock_is_writable, component); + + #ifdef HAVE_GUPNP + if (agent->upnp_enabled && agent->upnp && +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 19729c2..64a3cb8 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2669,9 +2669,8 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + pair->sockptr = new_socket; + _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); + +- if (agent->reliable) +- nice_socket_set_writable_callback (pair->sockptr, +- _tcp_sock_is_writable, component2); ++ nice_socket_set_writable_callback (pair->sockptr, _tcp_sock_is_writable, ++ component2); + + nice_component_attach_socket (component2, new_socket); + } +-- +2.14.3 + + +From e6217f8eba6ea17d90eac67ef5fa5412fbf10dad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Fri, 4 May 2018 15:44:05 +0200 +Subject: [PATCH 13/15] Ignore function case warnings + +This makes GLib usage annoying as it makes GSourceFunc casts invalid. +--- + configure.ac | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/configure.ac b/configure.ac +index 16988ad..36bd622 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -136,6 +136,7 @@ AS_IF([test "x$enable_compile_warnings" != "xno" -a \ + NICE_ADD_FLAG([-Wcast-align]) + NICE_ADD_FLAG([-Wformat-nonliteral]) + NICE_ADD_FLAG([-Wformat-security]) ++ NICE_ADD_FLAG([-Wno-cast-function-type]) + ]) + AS_IF([test "$enable_compile_warnings" = "yes" -o \ + "$enable_compile_warnings" = "maximum" -o \ +-- +2.14.3 + + +From 3a9d92818b4c2f083e26fe164a1be82212bd4061 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Fri, 4 May 2018 16:44:45 +0200 +Subject: [PATCH 14/15] stund: Pass the right length for ipv6 + +--- + stun/tools/stund.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/stun/tools/stund.c b/stun/tools/stund.c +index addc4fa..c148e51 100644 +--- a/stun/tools/stund.c ++++ b/stun/tools/stund.c +@@ -100,6 +100,8 @@ int listen_socket (int fam, int type, int proto, unsigned int port) + struct sockaddr_in6 in6; + struct sockaddr_storage storage; + } addr; ++ int len; ++ + if (fd == -1) + { + perror ("Error opening IP port"); +@@ -118,6 +120,7 @@ int listen_socket (int fam, int type, int proto, unsigned int port) + { + case AF_INET: + addr.in.sin_port = htons (port); ++ len = sizeof (struct sockaddr_in); + break; + + case AF_INET6: +@@ -125,13 +128,14 @@ int listen_socket (int fam, int type, int proto, unsigned int port) + setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes)); + #endif + addr.in6.sin6_port = htons (port); ++ len = sizeof (struct sockaddr_in6); + break; + + default: + assert (0); /* should never be reached */ + } + +- if (bind (fd, &addr.addr, sizeof (struct sockaddr))) ++ if (bind (fd, &addr.addr, len)) + { + perror ("Error opening IP port"); + goto error; +-- +2.14.3 + + +From 34d60446ddfcdb98f2543611151ef8fbc5be4805 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Fri, 4 May 2018 16:50:45 +0200 +Subject: [PATCH 15/15] stund: Pass sockaddr_storage size for both families + +--- + stun/tools/stund.c | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +diff --git a/stun/tools/stund.c b/stun/tools/stund.c +index c148e51..00a0881 100644 +--- a/stun/tools/stund.c ++++ b/stun/tools/stund.c +@@ -100,15 +100,12 @@ int listen_socket (int fam, int type, int proto, unsigned int port) + struct sockaddr_in6 in6; + struct sockaddr_storage storage; + } addr; +- int len; + + if (fd == -1) + { + perror ("Error opening IP port"); + return -1; + } +- if (fd < 3) +- goto error; + + memset (&addr, 0, sizeof (addr)); + addr.storage.ss_family = fam; +@@ -120,7 +117,6 @@ int listen_socket (int fam, int type, int proto, unsigned int port) + { + case AF_INET: + addr.in.sin_port = htons (port); +- len = sizeof (struct sockaddr_in); + break; + + case AF_INET6: +@@ -128,14 +124,13 @@ int listen_socket (int fam, int type, int proto, unsigned int port) + setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes)); + #endif + addr.in6.sin6_port = htons (port); +- len = sizeof (struct sockaddr_in6); + break; + + default: + assert (0); /* should never be reached */ + } + +- if (bind (fd, &addr.addr, len)) ++ if (bind (fd, &addr.addr, sizeof (struct sockaddr_storage))) + { + perror ("Error opening IP port"); + goto error; +@@ -192,7 +187,7 @@ static int dgram_process (int sock, StunAgent *oldagent, StunAgent *newagent) + StunValidationStatus validation; + StunAgent *agent = NULL; + +- addr_len = sizeof (struct sockaddr_in); ++ addr_len = sizeof (struct sockaddr_storage); + len = recvfrom (sock, buf, sizeof(buf), 0, &addr.addr, &addr_len); + if (len == (size_t)-1) + return -1; +-- +2.14.3 + diff --git a/libnice-0.1.14-tests-koji.patch b/libnice-0.1.14-tests-koji.patch index f9feb34..997e712 100644 --- a/libnice-0.1.14-tests-koji.patch +++ b/libnice-0.1.14-tests-koji.patch @@ -22,7 +22,7 @@ index 16988ad..b7b74fa 100644 AC_CONFIG_FILES([ Makefile -@@ -262,7 +262,7 @@ AC_SUBST(gstplugindir) +@@ -263,7 +263,7 @@ AC_SUBST(gstplugindir) AC_SUBST(gstplugin010dir) AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes) diff --git a/libnice.spec b/libnice.spec index c0960ef..26f66dc 100644 --- a/libnice.spec +++ b/libnice.spec @@ -1,16 +1,20 @@ # disable building of plugin for gstreamer 0.10 %bcond_with gst010 +%global upstream_date 20180504 +%global upstream_rnum 85 +%global upstream_hash 34d6044 + Name: libnice Version: 0.1.14 -Release: 6.20171128gitfb2f1f7%{?dist} +Release: 7.%{upstream_date}git%{upstream_hash}%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL:
https://nice.freedesktop.org/wiki/
Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
-Patch1: libnice-0.1.14-70-gfb2f1f7.patch +Patch1: libnice-0.1.14-%{upstream_rnum}-g%{upstream_hash}.patch # make tests compile on i686 Patch2: libnice-0.1.14-tests-i686.patch @@ -150,6 +154,9 @@ make check %changelog +* Mon May 07 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-7.20180504git34d6044 +- update to 0.1.14-85-g34d6044 (#1541646) + * Mon Apr 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7 - temporarily make the upstream test-suite run on Intel arches only - disable test-send-recv, which fails in Koji commit 368c412d96e96c3adf1163a1bdeadf1ee2dec294 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Mon Apr 16 16:09:02 2018 +0200 temporarily make the upstream test-suite run on Intel arches only diff --git a/libnice.spec b/libnice.spec index 87ef224..c0960ef 100644 --- a/libnice.spec +++ b/libnice.spec @@ -107,8 +107,13 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %check +# Temporarily make the upstream test-suite run on Intel arches only because we +# are getting random crashes in Koji on secondary arches but I have not been +# able to reproduce them locally so far. +%ifarch x86_64 %{ix86} export LD_LIBRARY_PATH="$PWD/nice/.libs" make check +%endif %post -p /sbin/ldconfig @@ -146,6 +151,7 @@ make check %changelog * Mon Apr 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7 +- temporarily make the upstream test-suite run on Intel arches only - disable test-send-recv, which fails in Koji * Fri Mar 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7 commit d274df7d77937ce5004f9c1c7d3e86fe565e0f28 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Mon Apr 16 15:18:17 2018 +0200 disable test-send-recv, which fails in Koji ... due to insufficiently configured network diff --git a/libnice.spec b/libnice.spec index 4b4202d..87ef224 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@ Name: libnice Version: 0.1.14 -Release: 5.20171128gitfb2f1f7%{?dist} +Release: 6.20171128gitfb2f1f7%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries @@ -80,8 +80,11 @@ developing applications that use %{name}. %patch3 -p1 chmod 0755 scripts/valgrind-test-driver -# disable test-new-dribble that sometimes hangs indefinitely -sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am +# disable test-new-dribble, which sometimes hangs indefinitely, and +# test-send-recv, which fails in Koji due to insufficiently configured network +sed -e 's/test-new-dribble/#&/' \ + -e 's/test-send-recv/#&/' \ + -i tests/Makefile.am autoreconf -fiv @@ -142,6 +145,9 @@ make check %changelog +* Mon Apr 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-6.20171128gitfb2f1f7 +- disable test-send-recv, which fails in Koji + * Fri Mar 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7 - do not build with -Werror by default - make the build more verbose commit cb05dbf2151a8ed7c8de960914286bd4e08b5ccf Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Mar 16 17:20:56 2018 +0100 do not build with -Werror by default diff --git a/libnice.spec b/libnice.spec index 108c26f..4b4202d 100644 --- a/libnice.spec +++ b/libnice.spec @@ -87,18 +87,7 @@ autoreconf -fiv %build -export CFLAGS="$RPM_OPT_FLAGS" -%if 0%{?fedora} == 28 -# FIXME: This makes the code compile on Fedora 28 but the resulting binaries -# cannot work properly!!! -CFLAGS="$CFLAGS -Wno-error=cast-function-type" -%endif -%ifarch armv7hl -# FIXME: basically the same problem on armv7hl with both Fedora 27 and 28 -CFLAGS="$CFLAGS -Wno-error=cast-align" -%endif - -%configure --disable-static \ +%configure --enable-compile-warnings=yes --disable-static \ %if %{with gst010} --with-gstreamer-0.10 %else @@ -154,6 +143,7 @@ make check %changelog * Fri Mar 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7 +- do not build with -Werror by default - make the build more verbose * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 commit 288dc3b59279d38e0d188f838efaa9fe3c466aad Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Mar 16 17:18:56 2018 +0100 make the build more verbose ... mainly to see compiler flags diff --git a/libnice.spec b/libnice.spec index bb22cea..108c26f 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@ Name: libnice Version: 0.1.14 -Release: 4.20171128gitfb2f1f7%{?dist} +Release: 5.20171128gitfb2f1f7%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries @@ -106,7 +106,7 @@ CFLAGS="$CFLAGS -Wno-error=cast-align" %endif sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool -make %{?_smp_mflags} +make %{?_smp_mflags} V=1 %install @@ -153,6 +153,9 @@ make check %changelog +* Fri Mar 16 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-5.20171128gitfb2f1f7 +- make the build more verbose + * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 - enable make check again - make tests pass in Koji commit 63a783842195268faa634e86d09505769c01db10 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 16:46:01 2018 +0100 enable make check again diff --git a/libnice.spec b/libnice.spec index f341e45..bb22cea 100644 --- a/libnice.spec +++ b/libnice.spec @@ -78,15 +78,13 @@ developing applications that use %{name}. %patch1 -p1 %patch2 -p1 %patch3 -p1 +chmod 0755 scripts/valgrind-test-driver # disable test-new-dribble that sometimes hangs indefinitely sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am autoreconf -fiv -%check -#make check - %build export CFLAGS="$RPM_OPT_FLAGS" @@ -116,6 +114,11 @@ make install DESTDIR=$RPM_BUILD_ROOT find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' +%check +export LD_LIBRARY_PATH="$PWD/nice/.libs" +make check + + %post -p /sbin/ldconfig @@ -151,6 +154,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- enable make check again - make tests pass in Koji - disable test-new-dribble that sometimes hangs indefinitely - make tests compile on i686 commit 8535255eda778d7739e2358b2dac12e94991afe3 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 19:16:26 2018 +0100 make tests pass in Koji diff --git a/libnice-0.1.14-tests-koji.patch b/libnice-0.1.14-tests-koji.patch new file mode 100644 index 0000000..f9feb34 --- /dev/null +++ b/libnice-0.1.14-tests-koji.patch @@ -0,0 +1,36 @@ +From 527c30ba453753e75d3d31be29a277ea6adc17c0 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka <kdudka(a)redhat.com> +Date: Fri, 9 Feb 2018 19:10:20 +0100 +Subject: [PATCH] tests: make the test-suite more verbose + +... and skip test-gstreamer if user's home is not /builddir (a heuristic +to detect mock) because multicast traffic is blocked on Koji buildhosts. +--- + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 16988ad..b7b74fa 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -12,7 +12,7 @@ AC_CANONICAL_TARGET + + AC_CONFIG_SRCDIR([agent/agent.c]) + AC_CONFIG_HEADERS([config.h]) +-AM_INIT_AUTOMAKE([1.12 -Wall -Wno-portability subdir-objects]) ++AM_INIT_AUTOMAKE([1.12 -Wall -Wno-portability subdir-objects serial-tests]) + + AC_CONFIG_FILES([ + Makefile +@@ -262,7 +262,7 @@ AC_SUBST(gstplugindir) + AC_SUBST(gstplugin010dir) + + AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes) +-AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes) ++AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes -a "$HOME" != /builddir) + AM_CONDITIONAL(WITH_GSTREAMER010, test "$with_gstreamer010" = yes) + + GUPNP_IGD_REQUIRED=0.2.4 +-- +2.13.6 + diff --git a/libnice.spec b/libnice.spec index e937514..f341e45 100644 --- a/libnice.spec +++ b/libnice.spec @@ -15,6 +15,9 @@ Patch1: libnice-0.1.14-70-gfb2f1f7.patch # make tests compile on i686 Patch2: libnice-0.1.14-tests-i686.patch +# make tests pass in Koji +Patch3: libnice-0.1.14-tests-koji.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: glib2-devel @@ -74,6 +77,7 @@ developing applications that use %{name}. %setup -q %patch1 -p1 %patch2 -p1 +%patch3 -p1 # disable test-new-dribble that sometimes hangs indefinitely sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am @@ -147,6 +151,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- make tests pass in Koji - disable test-new-dribble that sometimes hangs indefinitely - make tests compile on i686 - make the package build on armv7hl commit d99a0c0fb4cc3024b35b03a3da993be3eed43630 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 23:03:38 2018 +0100 disable test-new-dribble that sometimes hangs indefinitely diff --git a/libnice.spec b/libnice.spec index b8c13cf..e937514 100644 --- a/libnice.spec +++ b/libnice.spec @@ -74,6 +74,10 @@ developing applications that use %{name}. %setup -q %patch1 -p1 %patch2 -p1 + +# disable test-new-dribble that sometimes hangs indefinitely +sed -e 's/test-new-dribble/#&/' -i tests/Makefile.am + autoreconf -fiv %check @@ -143,6 +147,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- disable test-new-dribble that sometimes hangs indefinitely - make tests compile on i686 - make the package build on armv7hl - make the package build on Fedora 28 commit 554edd4e12901c7dc91d9601d7d9a673515b26c6 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 18:05:03 2018 +0100 make tests/test-pseudotcp.c compile on i686 diff --git a/libnice-0.1.14-tests-i686.patch b/libnice-0.1.14-tests-i686.patch new file mode 100644 index 0000000..6558549 --- /dev/null +++ b/libnice-0.1.14-tests-i686.patch @@ -0,0 +1,48 @@ +From 3f8364b41207d8c26d3d3be518a7d9ebf4243b92 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka <kdudka(a)redhat.com> +Date: Fri, 9 Feb 2018 18:01:57 +0100 +Subject: [PATCH] tests: make them compile on i686 + +--- + tests/test-pseudotcp-fuzzy.c | 4 ++-- + tests/test-pseudotcp.c | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tests/test-pseudotcp-fuzzy.c b/tests/test-pseudotcp-fuzzy.c +index 4a714e6..030c03f 100644 +--- a/tests/test-pseudotcp-fuzzy.c ++++ b/tests/test-pseudotcp-fuzzy.c +@@ -129,7 +129,7 @@ write_to_sock (PseudoTcpSocket *sock) + total += wlen; + total_read += wlen; + if (wlen < (gint) len) { +- g_debug ("seeking %ld from %lu", wlen - len, ftell (in)); ++ g_debug ("seeking %ld from %lu", (long) wlen - len, ftell (in)); + fseek (in, wlen - len, SEEK_CUR); + g_assert (!feof (in)); + g_debug ("Socket queue full after %d bytes written", total); +@@ -355,7 +355,7 @@ static void adjust_clock (PseudoTcpSocket *sock) + + if (pseudo_tcp_socket_get_next_clock (sock, &timeout)) { + timeout -= g_get_monotonic_time () / 1000; +- g_debug ("Socket %p: Adjusting clock to %ld ms", sock, timeout); ++ g_debug ("Socket %p: Adjusting clock to %ld ms", sock, (long) timeout); + if (sock == left) { + if (left_clock != 0) + g_source_remove (left_clock); +diff --git a/tests/test-pseudotcp.c b/tests/test-pseudotcp.c +index 1a8391a..584a0d0 100644 +--- a/tests/test-pseudotcp.c ++++ b/tests/test-pseudotcp.c +@@ -81,7 +81,7 @@ static void write_to_sock (PseudoTcpSocket *sock) + total += wlen; + total_read += wlen; + if (wlen < (gint) len) { +- g_debug ("seeking %ld from %lu", wlen - len, ftell (in)); ++ g_debug ("seeking %ld from %lu", (long) wlen - len, ftell (in)); + fseek (in, wlen - len, SEEK_CUR); + g_assert (!feof (in)); + g_debug ("Socket queue full after %d bytes written", total); +-- +2.13.6 + diff --git a/libnice.spec b/libnice.spec index 17156df..b8c13cf 100644 --- a/libnice.spec +++ b/libnice.spec @@ -12,6 +12,9 @@ URL:
https://nice.freedesktop.org/wiki/
Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
Patch1: libnice-0.1.14-70-gfb2f1f7.patch +# make tests compile on i686 +Patch2: libnice-0.1.14-tests-i686.patch + BuildRequires: autoconf BuildRequires: automake BuildRequires: glib2-devel @@ -70,6 +73,7 @@ developing applications that use %{name}. %prep %setup -q %patch1 -p1 +%patch2 -p1 autoreconf -fiv %check @@ -139,6 +143,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- make tests compile on i686 - make the package build on armv7hl - make the package build on Fedora 28 - avoid build failure if gstreamer-plugins-base-devel is installed commit 729f8ecea839d9dcc79fbc0d2f92ced1c9925396 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 19:13:28 2018 +0100 make the package build on armv7hl FIXME: The resulting binaries cannot work properly!!! diff --git a/libnice.spec b/libnice.spec index b51908b..17156df 100644 --- a/libnice.spec +++ b/libnice.spec @@ -83,6 +83,10 @@ export CFLAGS="$RPM_OPT_FLAGS" # cannot work properly!!! CFLAGS="$CFLAGS -Wno-error=cast-function-type" %endif +%ifarch armv7hl +# FIXME: basically the same problem on armv7hl with both Fedora 27 and 28 +CFLAGS="$CFLAGS -Wno-error=cast-align" +%endif %configure --disable-static \ %if %{with gst010} @@ -135,6 +139,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- make the package build on armv7hl - make the package build on Fedora 28 - avoid build failure if gstreamer-plugins-base-devel is installed - move autoreconf invocation to %%prep commit 6eb057143c035b930de9e99ba8376580f7d6762b Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 17:48:10 2018 +0100 make the package build on Fedora 28 FIXME: The resulting binaries cannot work properly!!! diff --git a/libnice.spec b/libnice.spec index 1575942..b51908b 100644 --- a/libnice.spec +++ b/libnice.spec @@ -77,6 +77,13 @@ autoreconf -fiv %build +export CFLAGS="$RPM_OPT_FLAGS" +%if 0%{?fedora} == 28 +# FIXME: This makes the code compile on Fedora 28 but the resulting binaries +# cannot work properly!!! +CFLAGS="$CFLAGS -Wno-error=cast-function-type" +%endif + %configure --disable-static \ %if %{with gst010} --with-gstreamer-0.10 @@ -128,6 +135,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- make the package build on Fedora 28 - avoid build failure if gstreamer-plugins-base-devel is installed - move autoreconf invocation to %%prep - use Name Version Release that explicitly identifies an SCM snapshot (#1541646) commit 15e5f2042d406591e98abff299951b3c3a562e33 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 16:50:16 2018 +0100 avoid build failure if gstreamer-plugins-base-devel is installed diff --git a/libnice.spec b/libnice.spec index d6bb439..1575942 100644 --- a/libnice.spec +++ b/libnice.spec @@ -77,7 +77,12 @@ autoreconf -fiv %build -%configure --disable-static +%configure --disable-static \ +%if %{with gst010} + --with-gstreamer-0.10 +%else + --without-gstreamer-0.10 +%endif sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool make %{?_smp_mflags} @@ -123,6 +128,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- avoid build failure if gstreamer-plugins-base-devel is installed - move autoreconf invocation to %%prep - use Name Version Release that explicitly identifies an SCM snapshot (#1541646) commit 0acfa758ecc36ea8b0049fb6f34507a43e769ab0 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 16:01:32 2018 +0100 move autoreconf invocation to %prep diff --git a/libnice.spec b/libnice.spec index a1850c8..d6bb439 100644 --- a/libnice.spec +++ b/libnice.spec @@ -70,13 +70,13 @@ developing applications that use %{name}. %prep %setup -q %patch1 -p1 +autoreconf -fiv %check #make check %build -autoreconf -f -i %configure --disable-static sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool @@ -123,6 +123,7 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- move autoreconf invocation to %%prep - use Name Version Release that explicitly identifies an SCM snapshot (#1541646) * Fri Feb 09 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-3 commit 794e7ad7d94e48b6a20f923c99c68daf642f6095 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 15:59:27 2018 +0100 Related: #1541646 - use Name Version Release that explicitly identifies an SCM snapshot diff --git a/libnice.spec b/libnice.spec index 63e60a2..a1850c8 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@ Name: libnice Version: 0.1.14 -Release: 3%{?dist} +Release: 4.20171128gitfb2f1f7%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries @@ -122,6 +122,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog +* Fri Feb 09 2018 Kamil Dudka <kdudka(a)redhat.com> - 0.1.14-4.20171128gitfb2f1f7 +- use Name Version Release that explicitly identifies an SCM snapshot (#1541646) + * Fri Feb 09 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-3 - update to 0.1.14-70-gfb2f1f7 with alternate server fixes for SIPE - add autoreconf build step commit f1ca1fa20f059e7c39e252302c3edb61acb7bf6d Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 15:48:29 2018 +0100 libnice-0.1.14-70-gfb2f1f7.patch: now produced by git format-patch ... so that we are able to easily bisect over the upstream commits diff --git a/libnice-0.1.14-20171128.patch b/libnice-0.1.14-20171128.patch deleted file mode 100644 index ff2d2ae..0000000 --- a/libnice-0.1.14-20171128.patch +++ /dev/null @@ -1,6711 +0,0 @@ -commit fb2f1f77a31baa91968fc81c205f980b6913f403 -Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue Nov 28 16:05:18 2017 -0500 - - conncheck: handle alternate-server for turn relays differently - - If a relay gives us an alternate-server, we need to cancel and reset - every candidate discovery attempt that uses the same server, to avoid - ending up with one component on one server and the other component on - another server (causing relay candidates with mismatched foundations). - -commit 4172d48852ecd1c86cc7bd4665b23697603d1eed -Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue Nov 28 15:14:11 2017 -0500 - - discovery: Increase discovery_unsched_items whenever we restart a check - - The discovery_unsched_items is decremented every time a DiscoveryCandidate - goes from non-pending to pending. So if we restart a check by setting - pending to FALSE, we should re-increase the discovery_unsched_items. - -commit 59fcf95d505c3995f858b826d10cd48321ed383e -Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Mon Nov 27 17:07:02 2017 -0500 - - turn: Add support for ALTERNATE_SERVER in OC2007 Compatibility - - The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in - allocation responses, and if they are not handled, we end up using the - main turn server to send allocation requests that then get sent to the - alternate server which will return the XOR_MAPPED_ADDRESS containing - the IP address of the turn server that proxied the message instead of - our own actual external IP. - -commit 17f30e4465efe9533799b02d6f95feeaf0f2748c -Author: Miguel París <mparisdiaz(a)gmail.com> -Date: Wed Nov 8 16:26:47 2017 +0000 - - conncheck: do not require that all streams have a connection check list - - One or more streams might not have any connection check list if the - number of streams differs from the peer agent. - Differential Revision:
https://phabricator.freedesktop.org/D1880
- -commit c63349894b3fe974494453a883dfb5ad05df5a46 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu Nov 23 18:31:31 2017 +0100 - - Makefile: really enable debug for tests - - Differential Revision:
https://phabricator.freedesktop.org/D1888
- -commit 02216a6766caccb652387d5ee19686149eedbc93 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Nov 21 15:12:45 2017 +0100 - - agent: prevent external role change while conncheck is running - - With this patch, we stash the controlling mode property change, and - apply it safely, when it won't interfere with an ongoing conncheck - running. According to RFC5245, sect 5.2. "Determining Role", the role - is determined for a session, and persists unless an ICE is restarted. - - Differential Revision:
https://phabricator.freedesktop.org/D1887
- -commit fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 -Author: Philip Withnall <withnall(a)endlessm.com> -Date: Thu Aug 3 12:20:32 2017 +0100 - - stun: Fix FD leak in test/utility code - -
https://phabricator.freedesktop.org/T7798
- - Signed-off-by: Philip Withnall <withnall(a)endlessm.com> - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D1819
- -commit 4c4834ab634f735145c8f758a22cbdd9cab79bac -Author: Philip Withnall <withnall(a)endlessm.com> -Date: Tue Sep 12 13:23:53 2017 +0100 - - tests: Fix agent.h header inclusion in test-gstreamer.c - - Spotted by Lukas Gradl on the mailing list. - - Signed-off-by: Philip Withnall <withnall(a)endlessm.com> - -commit 1a1803a45778000720c93d91060cedeb19124a27 -Author: Philip Withnall <withnall(a)endlessm.com> -Date: Tue Sep 12 13:23:31 2017 +0100 - - tests: Fix copyright dates in test-gstreamer.c - - This code is not 1000 years old. - - Signed-off-by: Philip Withnall <withnall(a)endlessm.com> - -commit 14102d44449d2eb4148588ce54fa897fa13b87ad -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sun Jul 2 16:02:09 2017 +0200 - - conncheck: change state before updating nominated pairs - - When a pair is nominated while in state failed, we first move - back to state connecting, then we update the selected pair, and - finally we move to state connected. - -commit 72dd26a3368d3506fe8faca7067a02784fb5f0fd -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Wed Jun 28 12:06:48 2017 +0200 - - conncheck: forgot to put a pair in triggered check list - - When a new pair is created from an unknown remote candidate, it - should be enqueue for a triggered check, to allow it to be marked - as nominated on response arrival in priv_mark_pair_nominated(). - Creating it in waiting state is not sufficient since the update - in priv_mark_pair_nominated() from previous commits. - - Differential Revision:
https://phabricator.freedesktop.org/D1763
- -commit 6fe64fdbc53ab87dffd79972f492665cff14c0a0 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Jun 27 11:01:14 2017 +0200 - - conncheck: update the state of triggered checks pairs - - With this patch, we fix an ambiguity of some parts of the spec, when - the document refers to in-progress pairs, that also concern pairs in - the triggered checks list. - - The first cast is in section 7.1.2.5, "Updating the Nominated Flag", - when the in-progress pair will be nominated on response arrival. This is - handled in function priv_mark_pair_nominated(), when a pair is put to - the triggered check list in reaction to a matching inbound stun request. - Such a pair in priv_mark_pair_nominated() will _always_ be in the - triggered check list, from the previously called function - priv_schedule_triggered_check(). - - The second case is in section 8.1.2, "Updating State" when an in-progress - pair stops its retransmission when another pair of higher priority is - already nominated. This is handled by function priv_prune_pending_checks(). - - Until now, pairs enqueued in the triggered check list move transiently - to state waiting, according to 7.2.1.4. But this state causes wrong - decisions in the two previous cases, because such pairs should in fact - rather be considered "like in-progress", to avoid discarding them - inadvertantly. - - This patch update the state of the triggered check list - pairs to in-progress. It allows to remove exception handling cited - above: the code is a bit more simple, and allows some refactoring - in priv_mark_pair_nominated() between RFC and compatibility modes. - - Differential Revision:
https://phabricator.freedesktop.org/D1762
- -commit 36f306f4a95f1c2b3e9c584b5a645a78e231c020 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Jun 26 21:41:44 2017 +0200 - - conncheck: support several stun requests per pair - - This patch should improve the reliabily of the connection check by - keeping the record of several simultaneous ongoing stun requests per - pair. A new stun request on an in-progress pair typically is caused by - in inbound stun request from the peer on this same pair. This is named - "Triggered Checks" in the spec. When this situation arises, it is fair - to handle these two stun requests simultaneously, the triggered check, - and the initial ordinary check, since both can potentially succeed. - - Differential Revision:
https://phabricator.freedesktop.org/D1761
- -commit e860948b5fe3a791119957f26045b8f5159baeff -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Jun 26 21:06:36 2017 +0200 - - conncheck: use stun_timer_remainder less frequently - - We try to use stun_timer_remainder() less frequently, particularily - in the debug messages, and favour of the next_tick value associated - to the pair. - - Differential Revision:
https://phabricator.freedesktop.org/D1760
- -commit ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Jun 26 20:41:49 2017 +0200 - - conncheck: make debug sentences more accurate - - We add a helper function to print the pair state in-extenso. - - Differential Revision:
https://phabricator.freedesktop.org/D1759
- -commit 25be00271a4c8c684a2d435d29ae0811dbf5e21c -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Jun 26 20:36:35 2017 +0200 - - conncheck: reorder some chunks of code - - With this patch we simplify the levels of code indentation. - - Differential Revision:
https://phabricator.freedesktop.org/D1758
- -commit 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Sep 5 14:50:29 2017 -0400 - - agent: Set error if it isn't set - -commit dbaf8f5ccd76089e340883887c7e08e6c04de80a -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 13:22:21 2016 +0200 - - conncheck: improve role conflict debug - - This patch displays explicitely the controlling or controlled - role of the agent. - - Differential Revision:
https://phabricator.freedesktop.org/D874
- -commit 9f800d3597767855accccc592c34bc4e945f5bd5 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Jun 21 20:42:57 2017 -0400 - - configure: Remove -Wswitch-enum - - Creates useless warnings when other libraries change. - -
https://phabricator.freedesktop.org/T7770
- -commit 63d273cea42def3567701ad9feab91f63cf9345f -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Feb 11 22:16:48 2016 -0500 - - component: Use non-GClosure dummy callbacks - - GClosures are not that cheap to setup - -commit 2c50d73b82f2ec2422a8e0ea393194486c193c64 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Feb 10 23:20:39 2016 -0500 - - agent: Don't crash if recv cancelled without a GError - -commit dcb0d647174416a292492f8deca86f83a2ef124c -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Jun 21 17:07:17 2017 -0400 - - Repleace UNRELEASED with 0.1.15 - -commit e3ddaa285e389baf3f26cfb6964919718a8f6a00 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Jun 21 16:55:32 2017 -0400 - - agent: Adjust the nice_agent_new_full() to use flags - - This makes it easier to read and more extensible. - -commit c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sun Jun 18 10:12:58 2017 +0200 - - agent: remove spurious newlines - - Differential Revision:
https://phabricator.freedesktop.org/D1756
- -commit b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sun May 28 22:20:36 2017 +0200 - - stun: fix gcc7 implicit fallthrough warning - - Differential Revision:
https://phabricator.freedesktop.org/D1754
- -commit 195db6b344fc4f9fadc39419dfeec2fc14b23fac -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Fri Jul 15 23:31:42 2016 +0200 - - agent: add new pairs only for gathering streams - - At the end of the local candidate gathering process, we only create new - pairs for streams that are in gathering state. - - Other stream that may be in ready state for example, due to a - previously succeeded conncheck process, may have accumulated some - couples (local,remote) candidates that have not resulted in the creation - a new pair during this previous conncheck process, and we don't want - these new pairs to be added now, because it would generate unneeded - transition changes for a stream unconcerned by this gathering. - - Differential Revision:
https://phabricator.freedesktop.org/D1755
- -commit 07366a5bca7e4818b8df29d9c7c220da8f752547 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Jun 21 21:47:42 2016 +0200 - - conncheck: fix the component failed transition - - This patch fixes the transition of a component from connecting to - failed, that previously occured due to the propagation of the - keep_timer_going variable, and to the final call to function - priv_update_check_list_failed_components(), after the global agent - timer was stopped. - - Previously, the code almost never entered to failed state, because the - timer was going one, as long as the number of nominated pair was not - enough, and as long as there were valid pairs not yet nominated. Even - if all pair timers were over. - - The definition of the Failed state of a conncheck list is somewhat - contradictory in the spec, depending on weather you read : - - * sect 5.7.4. "Computing States", - "Failed: In this state, the ICE checks have not completed successfully - for this media stream." - - or - - * sect 7.1.3.3. "Check List and Timer State Updates", - "If all of the pairs in the check list are now either in the Failed or - Succeeded state: If there is not a pair in the valid list for each - component of the media stream, the state of the check list is set to - Failed." - - Our understanding of the ICE spec is that, the proper way to enter failed - state instead in when all connchecks have no longer in-progress pairs. - All pairs are either in state succeeded, discovered, or failed. No timer - is still running, and we have no hope that the conncheck list changes - again, except if an unexpected STUN packet arrives later. All pairs in - frozen state is a special case, that is handled separately (sect - 7.1.3.3). - - A special grace delay is added before declaring a component in state - Failed. This delay is not part of the RFC, and it is aimed to limit the - cases when a conncheck list is reactivated just after it's been declared - failed, causing a user visible transition from connecting to failed, and - back from failed to connecting again. This is also required by the test - suite, that counts exactly the number of time each state is entered, and - doesn't expect these transcient failed states to happen (frequent due to - the nature of the testsuite, less frequent in real life). - - Differential Revision:
https://phabricator.freedesktop.org/D1111
- -commit 95f8805eb7b77755337e28daf1f134587d42b35f -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu Jun 16 17:32:39 2016 +0200 - - conncheck: remove cancelled pair state - - Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for - removal after the nomination of a pair with an higher priority, - described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They - include also pairs that overflow the conncheck list size, but this is a - somewhat more marginal situation. So we are mainly interested in the - first use case of this state. - - This state mixes two different situations, that deserve a distinct - handling : on one side, there are waiting or frozen pairs that must be - removed, this is an immediate action that doesn't need a dedicated state - for that. And on the other side, there are in-progress pairs that - should no longer be retransmitted, because another pair with a higher - priority has already been nominated. - - This patch removes the cancelled state, and adds a flag - retransmit_on_timeout to deal with this last situation. Note that this - case should not generate a triggered check, as per described in section - 7.2.1.4, when the state of the pair is In-Progress or Failed, since this - pair of lower priority has no hope to replace the nominated one. - - Differential Revision:
https://phabricator.freedesktop.org/D1114
- -commit d516fca1b0e0a6606afec797bdc0690104e779a9 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Jun 14 21:32:26 2016 +0200 - - conncheck: adjust recheck on timeout strategy - - The pair recheck on timeout can easily cause repetitive rechecks in - a ping-pong effect, if both peers with the same behaviour try to - check the same pair almost simultaneously, and if the network rtt - is greater than the initial timer rto. The reply to the initial - stun request may arrive after the in-progress conncheck - cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation - creates a new stun request, and forgets the initial one. - The conncheck timer is restarted with the same initial value, - so the same situation happens again later. - - We choose to avoid resetting the timer in such situation. After enough - retransmissions, the timeout delay, that doubles after each timeout, - becomes longer than the rtt, and the stun reply can be handled. - - Differential Revision:
https://phabricator.freedesktop.org/D1115
- -commit f19d209decac432a1597d84c3d5809d2208f7457 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Jun 14 21:20:49 2016 +0200 - - conncheck: do not recheck a just succeeded pair - - We cancel the potential in-progress transaction cancellation, caused by - sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before - transmission timeout, or just after timeout, when the pair is - temporarily put on the triggered check list on the way to be - rechecked. This situation is not covered by the RFC 5245. - - Differential Revision:
https://phabricator.freedesktop.org/D1119
- -commit 59fe48517c0b7db77b99183d31fdd84b55adb5d4 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Jun 14 21:12:16 2016 +0200 - - conncheck: fix a state transition case - - When a new stun request hits a valid pair, of a failed component, we may - have a transition from state failed to connected. In this situation, we - do a logical progression failed -> connecting -> connected, like we do - in function priv_update_check_list_state_for_ready() - - Similarily, when a new stun request hits a failed pair, of a failed - component, triggering a new conncheck for this pair may also cause the - component state to move back from failed to connecting state. - - Differential Revision:
https://phabricator.freedesktop.org/D1118
- -commit 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Jun 14 21:04:49 2016 +0200 - - conncheck: try to change earlier to state ready - - We check if we can move from state connected to ready just - after a pair expired its retransmission count. This pair - will be marked failed, and will no longer be in-progress. - The number of in-progress dropping down to zero is one - of the conditions needed to make the transition to ready, - per component (and not globally as it's the case in other - locations where this check function is called). - - Differential Revision:
https://phabricator.freedesktop.org/D1117
- -commit 8fa648a15a6700d08165fe97a09f5c068abae1e6 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Apr 11 13:13:51 2016 +0200 - - conncheck: dont cancel a pair for triggered check - - This patch adds another supplementary "corner" case, not covered by the - ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in - the triggered check list should be considered like an in-progress pair, - and cancelled only if its priority is lower than the priority of the - nominated pair. This is required in some aggressive nomination - situations for both peers to select the same pair, having the highest - priority. - - Differential Revision:
https://phabricator.freedesktop.org/D933
- -commit 11d4e37a030eb144a355dc26c705ef5aa5a975a7 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Fri Apr 1 17:31:44 2016 +0200 - - conncheck: remove a useless pair recheck - - This exception to the ICE spec is no longer needed: when a pair is in - the succeeded state, there is no needed to recheck it again upon - reception of an incoming stun request on it. - - Differential Revision:
https://phabricator.freedesktop.org/D884
- -commit 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 12:56:28 2016 +0200 - - conncheck: update the pair state in triggered check list - - With this patch, we update the state of the pair to waiting when - it is put in the triggered check queue. We also take care to call - priv_schedule_triggered_check() before priv_mark_pair_nominated() - so a pair to be rechecked and put on the triggered check queue - will have a unique state to be tested in the following call to - priv_mark_pair_nominated() when evaluating its nomination attributes. - - Differential Revision:
https://phabricator.freedesktop.org/D883
- -commit afd8d41bb34afb3864e838ef79026ae4ef15c0d4 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 13:32:49 2016 +0200 - - conncheck: new pairs never have the nominated flag preset - - This patch disables the possibility to set the nominated flag of a - candidate pair at creation time. This possibility was used when a new - pair is created from a new peer reflexive remote candidate, when the - agent is in controlled mode, and an stun request with USE-CANDIDATE is - received. In this case, since previous commit "conncheck: fix a - nomination corner case", we set the nominated flag when the stun - response of this new pair will arrive, and not before. Consequently, - this flag is no longer required when the pair is created. - - Differential Revision:
https://phabricator.freedesktop.org/D881
- -commit 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 13:30:04 2016 +0200 - - conncheck: fix a nomination corner case - - This patch add two supplementary cases, not covered by the ICE spec, - sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent - receives a STUN request with the USE-CANDIDATE flag, for a pair that is - in the waiting state. We consider that this case is similar to the - in-progress state, and should be handled in the same way. We also accept - when the pair is in frozen state. This latter case happens in the - new-dribble test, when an agent replays incoming early connchecks. - - Differential Revision:
https://phabricator.freedesktop.org/D880
- -commit 9103a5f2e184211fc160d1d3070ce4d043c71ff0 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 19 18:16:26 2016 +0200 - - conncheck: use the right pair when retriggering a check - - This patch completes the previous patch by adding a link back from the - discovered pair, to the parent pair that generated this check. This link - is needed by the ICE spec, to comply with section 8.1.1.1, "Regular - nomination", where the check to be retriggered is the initial check that - caused the discovery of the valid pair. When the valid pair is a - peer-reflexive pair, the retriggered check must target the succeeded - pair, and not the valid discovered pair. - - Differential Revision:
https://phabricator.freedesktop.org/D879
- -commit 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 13:25:16 2016 +0200 - - conncheck: link succeeded and discovered pairs - - When the agent has the role of the stun server, is in controlled mode, - and receives a pair with the "use-candidate" attribute set, it must find - a matching succeded or discovered pair in its conncheck list. This is - described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1. - When a matching pair is in succeeded state, the agent must nominate the - valid pair (a discovered pair) constructed from section 7.1.3.2.2, - that's been created from this succeeded one. To make this lookup, we - introduce a new "discovered_pair" member of the CandidateCheckPair - struct, that links the succeeded pair, and its discovered pair - if any. - - Differential Revision:
https://phabricator.freedesktop.org/D878
- -commit 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 19 17:59:27 2016 +0200 - - conncheck: improve triggered check of in-progress pairs - - This patch update the way triggered checks of in-progress pairs are - handled, according to ICE spec, section 7.2.1.4. Previously the same - connection check was retransmitted with an updated timeout. This causes - problems when a controlling role switch occurs in this time frame. - This is the reason why a new connection check must be generated - reflecting the updated role. We introduce a new flag "recheck_on_timeout" - in the pair indicating that the pair must be rechecked at the next timer - expiration. - - Differential Revision:
https://phabricator.freedesktop.org/D875
- -commit ead3453d04fc70865d176ab073636f8b9078cbbc -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 13:20:38 2016 +0200 - - conncheck: invoke the debug dump in more places - - Differential Revision:
https://phabricator.freedesktop.org/D1123
- -commit 15c0546f624113b8c0546a1f883a48bff7020f1b -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 19 17:06:32 2016 +0200 - - conncheck: improve the selection of the pairs to be checked - - This patch aims to implement more closely the algorithm described - in RFC 5245 indicating how pairs are transitionned from state Frozen - to Waiting. This is described in 7.1.3.2 when a check succeeded, and - correspond to modifications in function priv_conn_check_unfreeze_related(). - This is also described in 5.7.4 when defining the initial state of the - pairs in a conncheck, and correspond to modifications in function - priv_conn_check_unfreeze_next(). - - This patch introduces the notion of active and frozen check list. It - allows us to define the timer restranmission delay as described in 16.1. - - Another modification in priv_conn_check_tick_unlocked() is that every - stream in handled consecutively, and in an independant way. The pacing - was previously of a single STUN request emitted per callback, it is now - of a triggered check per callback OR a single STUN per callback AND per - stream per callback. - - The description of ordinary checks per stream in 5.8 is detailled in - function priv_conn_check_tick_stream(), and a remaining of the code - used to nominate a pair by the controlling agent is put in a dedicated - function priv_conn_check_tick_stream_nominate() - - Differential Revision:
https://phabricator.freedesktop.org/D813
- -commit 58d061df8f5425dc1add9c6030a2f891ebda4616 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Mar 7 16:35:09 2016 +0100 - - conncheck: update pair valid property selectively - - With this patch, we fix a corner case when the succeeded pair is a - peer-reflexive candidate pair, that already has been discovered - previously, In this case, the current pair -p- should not be marked - valid, because the valid flag is already set on the discovered pair. - - Differential Revision:
https://phabricator.freedesktop.org/D1124
- -commit 0636f9addc041cf93c4ff4eaa351b1768d48a32e -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 19 13:12:48 2016 +0200 - - conncheck: implement ice regular nomination method - - This patch implements Regular Nomation as described in RFC5245 - 8.1.1.1. The controlling agent lets valid pairs accumulate, and - decides which pair to recheck with the use-candidate attribute set. - priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated - pair when acting as a STUN server, and - priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to - update the nominated pair when acting as a STUN client. A new - property is also added to the agent to control the nomination - mode, which can be regular of aggressive, with default value - set to aggressive. - - Two new flags are introduced in the CandidateCheckPair structure: - - - use_candidate_on_next_check indicates the STUN client to add the - use-candidate attribute when the pair will be checked. At this - time, the nominated flag has not been set on this pair yet. - - - mark_nominated_on_response_arrival indicates the STUN server - to nominate the pair when its succesfull response to a - previous triggered check will arrive (7.2.1.5, item #2) - - Differential Revision:
https://phabricator.freedesktop.org/D811
- -commit a602ff57aae6a6afdeab843954c48e6fb5d82d31 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 12 13:02:45 2016 +0200 - - conncheck: fix pair state transition when successful response is received - - According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a - successful check should go to state succeeded, not only the valid - pair built in section 7.1.3.2.2. - - Differential Revision:
https://phabricator.freedesktop.org/D810
- -commit 3a58ba6120b188d78c5709e0349c0346bfa21c1a -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Feb 1 11:10:21 2016 +0100 - - conncheck: peer reflexive candidates are not paired - - This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning - Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive - Candidates", where discovered candidates do not cause the creation - of new pairs to be checked. - - Differential Revision:
https://phabricator.freedesktop.org/D805
- -commit 7a2c1edf502849a868b6f1026e8e2c343dee4ded -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Jun 6 22:24:50 2016 +0200 - - conncheck: update selected pair when nominated flag is set - - This modifies commit 8f1f615. It is better focused to update the - selected pair just after its nominated flag has been set. We also keep - the code homogeneous with other places, where the call to - priv_update_selected_pair() immediately follows the setting of - pair->nominated. Moreover in priv_update_check_list_state_for_ready(), - we would call priv_update_selected_pair() more times that necessary when - iterating on all nominated pairs. - - Differential Revision:
https://phabricator.freedesktop.org/D1125
- -commit 8bb210c5af4bcaf342d7fa4fef6034269e976532 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu Jun 9 23:28:43 2016 +0200 - - stun timer: make properties for stun timer tunables - - Three STUN binding request properties should be customisable. RFC 5245 - describes the retransmission timer of the STUN transaction 'RTO', and - RFC 5389 describes the number of retransmissions to send until a - response is received 'Rc'. The third property is the 'RTO' when - a reliable connection is used. - - RFC 5389 introduces a supplementary property 'Rm' as a multiplier used - to compute the final timeout RTO * Rm. However, this property is not - added in libnice, because this would require breaking the public API for - STUN. Currently, our STUN implementation hardcodes a division by two for - this final timeout. - - Differential Revision:
https://phabricator.freedesktop.org/D1109
- -commit 80c613699786567fd93db74377138600794a86e0 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Jun 8 16:34:21 2017 -0400 - - agent: Use base_addr to generate rport in SDP - - Reported by Capricornus (zhushengliang) - -
https://phabricator.freedesktop.org/T7763
- -commit b4abda09c79e4ce372a3771300abf568c85c7ff5 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu Apr 21 18:18:59 2016 +0200 - - interfaces: ignore predefined network interfaces - - Some interfaces, like the one managed by libvirtd to provide a network - bridge to locally hosted virtual machines, can be completely ignored - when gathering ICE candidates. The motivation for adding this - possibility is that, ignoring them doesn't remove capabilities, and - improves the overall speed of the connection check method, by reducing - the number of pairs to be tested. This patch adds the possibility to - define such interfaces in the configuration script. - - Differential Revision:
https://phabricator.freedesktop.org/D948
- -commit d5446a72233eab8501be0b3fb9060c8be3ba034b -Author: Philip Withnall <withnall(a)endlessm.com> -Date: Mon May 1 08:51:40 2017 +0100 - - examples: Stop installing the examples - - There’s no point in installing them; their benefit is in providing - example code to developers. - - Debian doesn’t package them; Fedora packages them in a separate - subpackage which will have to disappear. - - Signed-off-by: Philip Withnall <withnall(a)endlessm.com> - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D1737
- -commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Tue Apr 5 21:32:39 2016 +0200 - - agent: do not create a GSource for UDP TURN socket - - With this patch, we don't create a new GSource for udp-turn socket, - because it would duplicate the packets already received on the base UDP - socket, as the underlying GSocket is the same. This is a race condition, - because an UDP packet arriving on the base socket, may randomly be - handled by the GSource callback created for the base socket (udp-bsd) of - the callback created for the udp-turn socket. Moreover this callback - already knows how to parse UDP datagrams received from a known turn - server. - - This patch also prevents a subtle bug, when a STUN request is received - directly from a peer, is handled by the udp turn socket. If the agent - already has a valid permission for this remote candidate, established - for another pair, it will happily send the STUN reply through the turn - relay. This generates a source address mismatch on the peer agent, when - it'll receive the STUN response from the turn relay instead of the - initial address the request has been sent to. - - Differential Revision:
https://phabricator.freedesktop.org/D932
- -commit f6f704c5e8d2193bc67ba2b697c77694e1698c43 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Thu Jun 9 22:22:33 2016 +0200 - - stun timer: fix timeout of the last retransmission - - According to RFC 5389, section 7.2.1, a special timeout is applied to - the last retransmission (Rm * RTO), with Rm default value of 16, instead - of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7. - - As spotted by Olivier Crete, stun_timer_* is a public API, that cannot - be changed, and the initial delay (RTO) is not preserved in the - stun_timer_s struct. So we use a hack that implicitely guess Rm from the - number of transmissions Rc, by generalizing the default value of the - spec for Rm and Rc to other values of Rc passed in stun_timer_start( - - According to the spec, with the default value of Rc=7, the last delay - should be (64 * RTO), and it is instead (16 * RTO). So the last delay - can be computed by dividing the penultimate delay by two, instead of - multiplying it by two. - - Differential Revision:
https://phabricator.freedesktop.org/D1108
- -commit b0538d8c51f65019867b56a45cf90a70bef38f01 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 11 18:31:21 2017 -0400 - - agent: Ignore remote candidate of non-accepted types - - If we disable ice-tcp or ice-udp, ignore the remote - candidates for those types. - -commit f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 11 16:42:55 2017 -0400 - - conncheck: Check the controlling state when the req was sent - - It was checking when the pair was created, but the role may have - already changed when the request is sent. - -commit 8fc22b0034d04cbc222e0637152b1cee2879eef3 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Apr 5 17:43:26 2017 -0400 - - tests_: Add test to verify that only packets from validated addresses pass - -
https://phabricator.freedesktop.org/T104
- - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D1717
- -commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 21:27:39 2017 -0400 - - agent: Drop packets not from validated addresses - - This is required by the WebRTC spec. - - Remove test-mainloop as it doesnt even try to do - a negotiation. - -
https://phabricator.freedesktop.org/T104
- - Differential Revision:
https://phabricator.freedesktop.org/D1716
- -commit 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 14:41:51 2017 -0400 - - candidate: Add equality check function - - Add a function that can check if two candidates point to the same place. - -
https://phabricator.freedesktop.org/T104
- - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D1715
- -commit 10c557f23f8337f1304fff27bd85d2eb713cb249 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Apr 5 17:01:35 2017 -0400 - - test-credentials: Fix leak - -commit ae6d939e48366b80570d713b83334191b0982e71 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 20:34:05 2017 -0400 - - debug: Use libnice-verbose, not libnice-nice-verbose - -commit efc6a9be8cb34c899f0454c32e8a1e62b38df474 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 18:42:57 2017 -0400 - - tests: Use automake test-driver for valgrind - - This fixes the valgrind integration with the new test drivers. - -commit 4e605885c9dcaeb3ee443ec902c9c9189b19043f -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 16:16:46 2017 -0400 - - agent: Remove impossible case - -commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 16:16:05 2017 -0400 - - agent: Separate return from NiceSocket and internal enum - - The same variable was used for return values from NiceSocket and - for the internal enum, but 0 and -1 have different meanings in both. - -commit cd255bddc7fa0ddae056b5358a22b380c4eefc42 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 15:24:43 2017 -0400 - - udp-turn: Add some const to internal APIs - -commit db05e8b0fdc713df93cd6a4c3914e5aee38b2391 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 12:30:27 2017 -0400 - - Make clang-analyzer happy - - Various little things, none of which should make a functional difference. - -commit 0672758b9621801c8f0d9e3c920370983b267a68 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 12:29:29 2017 -0400 - - agent: Don't set variable that won't be used - - It exits the loop immediately, so no point to set the variable. - And it makes the clang static analyzer happy. - -commit 0de1657e4d15d4f1911ab1fad84ea23e7013070f -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Apr 4 12:25:50 2017 -0400 - - conncheck: Use the right test for empty remote_frag - - It's now an array, not a pointer, so needs to test to emptyness. - - It's a bugfix on the previous commit, 59ce41df - -commit 59ce41dfb837adf4222b25490cde2e394384ad15 -Author: Miguel París Díaz <mparisdiaz(a)gmail.com> -Date: Fri Mar 31 20:20:38 2017 -0400 - - conncheck: consider answer received when remote credentials are set - - Consider that the answer is received when remote credentials - are set instead of when a remote candidate is set, - which could not happen or could cause more delay for the - connection establishment. - - Ported to git master by Olivier Crête - - Differential Revision:
https://phabricator.freedesktop.org/D1704
-diff --git a/agent/Makefile.am b/agent/Makefile.am -index b585393..915f312 100644 ---- a/agent/Makefile.am -+++ b/agent/Makefile.am -@@ -22,6 +22,12 @@ if WINDOWS - AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP - endif - -+BUILT_SOURCES = \ -+ agent-enum-types.h \ -+ agent-enum-types.c -+ -+CLEANFILES += $(BUILT_SOURCES) -+ - noinst_LTLIBRARIES = libagent.la - - libagent_la_SOURCES = \ -@@ -54,6 +60,23 @@ libagent_la_SOURCES = \ - outputstream.c \ - $(BUILT_SOURCES) - -+agent-enum-types.h: agent.h Makefile -+ $(AM_V_GEN)$(GLIB_MKENUMS) \ -+ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ -+ --fprod "/* enumerations from \"@filename@\" */\n" \ -+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ -+ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \ -+ $(addprefix $(srcdir)/,agent.h) > $@ -+ -+agent-enum-types.c: agent.h Makefile agent-enum-types.h -+ $(AM_V_GEN)$(GLIB_MKENUMS) \ -+ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \ -+ --fprod "\n/* enumerations from \"@filename@\" */" \ -+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \ -+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ -+ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \ -+ $(addprefix $(srcdir)/,agent.h) > $@ -+ - libagent_la_LIBADD = \ - $(top_builddir)/random/libnice-random.la \ - $(top_builddir)/socket/libsocket.la \ -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index 4d8c9b8..7269be0 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -106,7 +106,6 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - - #define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */ - #define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */ --#define NICE_AGENT_TIMER_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */ - #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */ - - -@@ -114,6 +113,27 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - * MTU and estimated typical sizes of ICE STUN packet */ - #define MAX_STUN_DATAGRAM_PAYLOAD 1300 - -+#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ -+ -+/* A convenient macro to test if the agent is compatible with RFC5245 -+ * or OC2007R2. Specifically these two modes share the support -+ * of the regular or aggressive nomination mode */ -+#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \ -+ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ -+ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ -+/* A grace period before declaring a component as failed, in msecs. This -+ * delay is added to reduce the chance to see the agent receiving new -+ * stun activity just after the conncheck list has been declared failed, -+ * reactiviting conncheck activity, and causing a (valid) state -+ * transitions like that: connecting -> failed -> connecting -> -+ * connected -> ready. -+ * Such transitions are not buggy per-se, but may break the -+ * test-suite, that counts precisely the number of time each state -+ * has been set, and doesnt expect these transcient failed states. -+ */ -+#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000 -+ - struct _NiceAgent - { - GObject parent; /* gobject pointer */ -@@ -126,10 +146,14 @@ struct _NiceAgent - NiceProxyType proxy_type; /* property: Proxy type */ - gchar *proxy_username; /* property: Proxy username */ - gchar *proxy_password; /* property: Proxy password */ -- gboolean controlling_mode; /* property: controlling-mode */ -+ gboolean saved_controlling_mode;/* property: controlling-mode */ - guint timer_ta; /* property: timer Ta */ - guint max_conn_checks; /* property: max connectivity checks */ - gboolean force_relay; /* property: force relay */ -+ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ -+ guint stun_initial_timeout; /* property: stun initial timeout, RTO */ -+ guint stun_reliable_timeout; /* property: stun reliable timeout */ -+ NiceNominationMode nomination_mode; /* property: Nomination mode */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -@@ -164,6 +188,10 @@ struct _NiceAgent - guint16 rfc4571_expecting_length; - gboolean use_ice_udp; - gboolean use_ice_tcp; -+ -+ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ -+ gboolean controlling_mode; /* controlling mode used by the -+ conncheck */ - /* XXX: add pointer to internal data struct for ABI-safe extensions */ - }; - -diff --git a/agent/agent.c b/agent/agent.c -index 555fd16..0773c53 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -73,6 +73,7 @@ - #include "interfaces.h" - - #include "pseudotcp.h" -+#include "agent-enum-types.h" - - /* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b - * wide. */ -@@ -113,6 +114,10 @@ enum - PROP_BYTESTREAM_TCP, - PROP_KEEPALIVE_CONNCHECK, - PROP_FORCE_RELAY, -+ PROP_STUN_MAX_RETRANSMISSIONS, -+ PROP_STUN_INITIAL_TIMEOUT, -+ PROP_STUN_RELIABLE_TIMEOUT, -+ PROP_NOMINATION_MODE, - }; - - -@@ -400,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass) - 1, /* not a construct property, ignored */ - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:controlling-mode: -+ * -+ * Whether the agent has the controlling role. This property should -+ * be modified before gathering candidates, any modification occuring -+ * later will be hold until ICE is restarted. -+ */ - g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE, - g_param_spec_boolean ( - "controlling-mode", -@@ -436,6 +448,24 @@ nice_agent_class_init (NiceAgentClass *klass) - 0, /* default set in init */ - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:nomination-mode: -+ * -+ * The nomination mode used in the ICE specification for describing -+ * the selection of valid pairs to be used upstream. -+ * <para> See also: #NiceNominationMode </para> -+ * -+ * Since: 0.1.15 -+ */ -+ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, -+ g_param_spec_enum ( -+ "nomination-mode", -+ "ICE nomination mode", -+ "Nomination mode used in the ICE specification for describing " -+ "the selection of valid pairs to be used upstream", -+ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ - /** - * NiceAgent:proxy-ip: - * -@@ -708,6 +738,76 @@ nice_agent_class_init (NiceAgentClass *klass) - FALSE, - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:stun-max-retransmissions -+ * -+ * The maximum number of retransmissions of the STUN binding requests -+ * used in the gathering stage, to find our local candidates, and used -+ * in the connection check stage, to test the validity of each -+ * constructed pair. This property is described as 'Rc' in the RFC -+ * 5389, with a default value of 7. The timeout of each STUN request -+ * is doubled for each retransmission, so the choice of this value has -+ * a direct impact on the time needed to move from the CONNECTED state -+ * to the READY state, and on the time needed to complete the GATHERING -+ * state. -+ * -+ * Since: 0.1.15 -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, -+ g_param_spec_uint ( -+ "stun-max-retransmissions", -+ "STUN Max Retransmissions", -+ "Maximum number of STUN binding requests retransmissions " -+ "described as 'Rc' in the STUN specification.", -+ 1, 99, -+ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * NiceAgent:stun-initial-timeout -+ * -+ * The initial timeout (msecs) of the STUN binding requests -+ * used in the gathering stage, to find our local candidates. -+ * This property is described as 'RTO' in the RFC 5389 and RFC 5245. -+ * This timeout is doubled for each retransmission, until -+ * #NiceAgent:stun-max-retransmissions have been done, -+ * with an exception for the last restransmission, where the timeout is -+ * divided by two instead (RFC 5389 indicates that a customisable -+ * multiplier 'Rm' to 'RTO' should be used). -+ * -+ * Since: 0.1.15 -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, -+ g_param_spec_uint ( -+ "stun-initial-timeout", -+ "STUN Initial Timeout", -+ "STUN timeout in msecs of the initial binding requests used in the " -+ "gathering state, described as 'RTO' in the ICE specification.", -+ 20, 9999, -+ STUN_TIMER_DEFAULT_TIMEOUT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * NiceAgent:stun-reliable-timeout -+ * -+ * The initial timeout of the STUN binding requests used -+ * for a reliable timer. -+ * -+ * Since: 0.1.15 -+ */ -+ -+ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, -+ g_param_spec_uint ( -+ "stun-reliable-timeout", -+ "STUN Reliable Timeout", -+ "STUN timeout in msecs of the initial binding requests used for " -+ "a reliable timer.", -+ 20, 99999, -+ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ - /* install signals */ - - /** -@@ -1013,6 +1113,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) - nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker); - } - -+static void -+priv_update_controlling_mode (NiceAgent *agent, gboolean value) -+{ -+ gboolean update_controlling_mode; -+ GSList *i, *j; -+ -+ agent->saved_controlling_mode = value; -+ /* It is safe to update the agent controlling mode when all -+ * components are still in state disconnected. When we leave -+ * this state, the role must stay under the control of the -+ * conncheck algorithm exclusively, until the conncheck is -+ * eventually restarted. See RFC5245, sect 5.2. Determining Role -+ */ -+ if (agent->controlling_mode != agent->saved_controlling_mode) { -+ update_controlling_mode = TRUE; -+ for (i = agent->streams; -+ i && update_controlling_mode; i = i->next) { -+ NiceStream *stream = i->data; -+ for (j = stream->components; -+ j && update_controlling_mode; j = j->next) { -+ NiceComponent *component = j->data; -+ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED) -+ update_controlling_mode = FALSE; -+ } -+ } -+ if (update_controlling_mode) { -+ agent->controlling_mode = agent->saved_controlling_mode; -+ nice_debug ("Agent %p : Property set, changing role to \"%s\".", -+ agent, agent->controlling_mode ? "controlling" : "controlled"); -+ } else { -+ nice_debug ("Agent %p : Property set, role switch requested " -+ "but conncheck already started.", agent); -+ nice_debug ("Agent %p : Property set, staying with role \"%s\" " -+ "until restart.", agent, -+ agent->controlling_mode ? "controlling" : "controlled"); -+ } -+ } else -+ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent, -+ agent->controlling_mode ? "controlling" : "controlled"); -+} -+ - static void - nice_agent_init (NiceAgent *agent) - { -@@ -1022,7 +1163,9 @@ nice_agent_init (NiceAgent *agent) - /* set defaults; not construct params, so set here */ - agent->stun_server_port = DEFAULT_STUN_PORT; - agent->controlling_mode = TRUE; -+ agent->saved_controlling_mode = TRUE; - agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; -+ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; - - agent->discovery_list = NULL; - agent->discovery_unsched_items = 0; -@@ -1071,6 +1214,24 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) - } - - -+NICEAPI_EXPORT NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ NiceAgentOption flags) -+{ -+ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, -+ "compatibility", compat, -+ "main-context", ctx, -+ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE, -+ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ? -+ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE, -+ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, -+ NULL); -+ -+ return agent; -+} -+ -+ - static void - nice_agent_get_property ( - GObject *object, -@@ -1101,7 +1262,7 @@ nice_agent_get_property ( - break; - - case PROP_CONTROLLING_MODE: -- g_value_set_boolean (value, agent->controlling_mode); -+ g_value_set_boolean (value, agent->saved_controlling_mode); - break; - - case PROP_FULL_MODE: -@@ -1117,6 +1278,10 @@ nice_agent_get_property ( - /* XXX: should we prune the list of already existing checks? */ - break; - -+ case PROP_NOMINATION_MODE: -+ g_value_set_enum (value, agent->nomination_mode); -+ break; -+ - case PROP_PROXY_IP: - g_value_set_string (value, agent->proxy_ip); - break; -@@ -1187,6 +1352,18 @@ nice_agent_get_property ( - g_value_set_boolean (value, agent->force_relay); - break; - -+ case PROP_STUN_MAX_RETRANSMISSIONS: -+ g_value_set_uint (value, agent->stun_max_retransmissions); -+ break; -+ -+ case PROP_STUN_INITIAL_TIMEOUT: -+ g_value_set_uint (value, agent->stun_initial_timeout); -+ break; -+ -+ case PROP_STUN_RELIABLE_TIMEOUT: -+ g_value_set_uint (value, agent->stun_reliable_timeout); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1294,7 +1471,7 @@ nice_agent_set_property ( - break; - - case PROP_CONTROLLING_MODE: -- agent->controlling_mode = g_value_get_boolean (value); -+ priv_update_controlling_mode (agent, g_value_get_boolean (value)); - break; - - case PROP_FULL_MODE: -@@ -1309,6 +1486,10 @@ nice_agent_set_property ( - agent->max_conn_checks = g_value_get_uint (value); - break; - -+ case PROP_NOMINATION_MODE: -+ agent->nomination_mode = g_value_get_enum (value); -+ break; -+ - case PROP_PROXY_IP: - g_free (agent->proxy_ip); - agent->proxy_ip = g_value_dup_string (value); -@@ -1374,6 +1555,18 @@ nice_agent_set_property ( - agent->force_relay = g_value_get_boolean (value); - break; - -+ case PROP_STUN_MAX_RETRANSMISSIONS: -+ agent->stun_max_retransmissions = g_value_get_uint (value); -+ break; -+ -+ case PROP_STUN_INITIAL_TIMEOUT: -+ agent->stun_initial_timeout = g_value_get_uint (value); -+ break; -+ -+ case PROP_STUN_RELIABLE_TIMEOUT: -+ agent->stun_reliable_timeout = g_value_get_uint (value); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1552,7 +1745,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, - - if (len == 0) { - /* Reached EOS. */ -- len = 0; - goto done; - } else if (len < 0 && - pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) { -@@ -1890,6 +2082,17 @@ void agent_gathering_done (NiceAgent *agent) - - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; -+ -+ /* We ignore streams not in gathering state, typically already in -+ * ready state. Such streams may have couples (local,remote) -+ * candidates that have not resulted in the creation a new pair -+ * during a previous conncheck session, and we don't want these new -+ * pairs to be added now, because it would generate unneeded -+ * transition changes for a stream unconcerned by this gathering. -+ */ -+ if (!stream->gathering) -+ continue; -+ - for (j = stream->components; j; j = j->next) { - NiceComponent *component = j->data; - -@@ -3113,6 +3316,13 @@ static gboolean priv_add_remote_candidate ( - NiceComponent *component; - NiceCandidate *candidate; - -+ if (transport == NICE_CANDIDATE_TRANSPORT_UDP && -+ !agent->use_ice_udp) -+ return FALSE; -+ if (transport != NICE_CANDIDATE_TRANSPORT_UDP && -+ !agent->use_ice_tcp) -+ return FALSE; -+ - if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) - return FALSE; - -@@ -3195,6 +3405,19 @@ static gboolean priv_add_remote_candidate ( - username, password, priority); - } - -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ /* note: If there are TCP candidates for a media stream, -+ * a controlling agent MUST use the regular selection algorithm, -+ * RFC 6544, sect 8, "Concluding ICE Processing" -+ */ -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE && -+ transport != NICE_CANDIDATE_TRANSPORT_UDP) { -+ nice_debug ("Agent %p : we have TCP candidates, switching back " -+ "to regular nomination mode", agent); -+ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR; -+ } -+ } -+ - if (base_addr) - candidate->base_addr = *base_addr; - -@@ -3240,6 +3463,8 @@ nice_agent_set_remote_credentials ( - g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG); - g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD); - -+ conn_check_remote_credentials_set(agent, stream); -+ - ret = TRUE; - goto done; - } -@@ -3342,8 +3567,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, - } - } - -- conn_check_remote_candidates_set(agent, stream, component); -- - if (added > 0) { - conn_check_schedule_next (agent); - } -@@ -3423,7 +3646,8 @@ agent_recv_message_unlocked ( - { - NiceAddress from; - GList *item; -- gint retval; -+ RecvStatus retval; -+ gint sockret; - gboolean is_turn = FALSE; - - /* We need an address for packet parsing, below. */ -@@ -3484,8 +3708,8 @@ agent_recv_message_unlocked ( - local_bufs[i + 1].buffer = message->buffers[i].buffer; - local_bufs[i + 1].size = message->buffers[i].size; - } -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - message->length = ntohs (rfc4571_frame); - } - } else { -@@ -3500,7 +3724,7 @@ agent_recv_message_unlocked ( - _priv_set_socket_tos (agent, new_socket, stream->tos); - nice_component_attach_socket (component, new_socket); - } -- retval = 0; -+ sockret = 0; - } else { - /* In the case of a real ICE-TCP connection, we can use the socket as a - * bytestream and do the read here with caching of data being read -@@ -3509,9 +3733,9 @@ agent_recv_message_unlocked ( - - /* TODO: Support bytestream reads */ - message->length = 0; -- retval = 0; -+ sockret = 0; - if (available <= 0) { -- retval = available; -+ sockret = available; - - /* If we don't call check_connect_result on an outbound connection, - * then is_connected will always return FALSE. That's why we check -@@ -3524,7 +3748,7 @@ agent_recv_message_unlocked ( - * not connected, it means that it failed to connect, so we must - * return an error to make the socket fail/closed - */ -- retval = -1; -+ sockret = -1; - } else { - gint flags = G_SOCKET_MSG_PEEK; - -@@ -3537,7 +3761,7 @@ agent_recv_message_unlocked ( - */ - if (g_socket_receive_message (nicesock->fileno, NULL, - NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0) -- retval = -1; -+ sockret = -1; - } - } else if (agent->rfc4571_expecting_length == 0) { - if ((gsize) available >= sizeof(guint16)) { -@@ -3545,8 +3769,8 @@ agent_recv_message_unlocked ( - GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)}; - NiceInputMessage local_message = { &local_buf, 1, message->from, 0}; - -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - agent->rfc4571_expecting_length = ntohs (rfc4571_frame); - available = g_socket_get_available_bytes (nicesock->fileno); - } -@@ -3590,8 +3814,8 @@ agent_recv_message_unlocked ( - off += local_bufs[i].size; - } - } -- retval = nice_socket_recv_messages (nicesock, &local_message, 1); -- if (retval == 1) { -+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); -+ if (sockret == 1) { - message->length = local_message.length; - agent->rfc4571_expecting_length -= local_message.length; - } -@@ -3599,23 +3823,26 @@ agent_recv_message_unlocked ( - } - } - } else { -- retval = nice_socket_recv_messages (nicesock, message, 1); -+ sockret = nice_socket_recv_messages (nicesock, message, 1); - } - -- if (retval == 0) { -+ if (sockret == 0) { - retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ - nice_debug_verbose ("%s: Agent %p: no message available on read attempt", - G_STRFUNC, agent); - goto done; -- } else if (retval < 0) { -+ } else if (sockret < 0) { - nice_debug ("Agent %p: %s returned %d, errno (%d) : %s", -- agent, G_STRFUNC, retval, errno, g_strerror (errno)); -+ agent, G_STRFUNC, sockret, errno, g_strerror (errno)); - - retval = RECV_ERROR; - goto done; -+ } else { -+ retval = sockret; - } - -- if (retval == RECV_OOB || message->length == 0) { -+ g_assert (retval != RECV_OOB); -+ if (message->length == 0) { - retval = RECV_OOB; - nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC, - agent); -@@ -3679,8 +3906,6 @@ agent_recv_message_unlocked ( - if (retval == RECV_OOB) - goto done; - -- agent->media_after_tick = TRUE; -- - /* If the message’s stated length is equal to its actual length, it’s probably - * a STUN message; otherwise it’s probably data. */ - if (stun_message_validate_buffer_length_fast ( -@@ -3712,6 +3937,7 @@ agent_recv_message_unlocked ( - nice_debug ("%s: Valid STUN packet received.", G_STRFUNC); - retval = RECV_OOB; - g_free (big_buf); -+ agent->media_after_tick = TRUE; - goto done; - } - } -@@ -3722,6 +3948,23 @@ agent_recv_message_unlocked ( - g_free (big_buf); - } - -+ if (!nice_component_verify_remote_candidate (component, -+ message->from, nicesock)) { -+ if (nice_debug_is_verbose ()) { -+ gchar str[INET6_ADDRSTRLEN]; -+ -+ nice_address_to_string (message->from, str); -+ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" -+ " %s:%d sock-type: %d", agent, stream->id, component->id, str, -+ nice_address_get_port (message->from), nicesock->type); -+ } -+ -+ retval = RECV_OOB; -+ goto done; -+ } -+ -+ agent->media_after_tick = TRUE; -+ - /* Unhandled STUN; try handling TCP data, then pass to the client. */ - if (message->length > 0 && agent->reliable) { - if (!nice_socket_is_reliable (nicesock) && -@@ -4085,7 +4328,10 @@ static gboolean - nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) - { - GError **error = user_data; -- return !g_cancellable_set_error_if_cancelled (cancellable, error); -+ -+ if (error && !*error) -+ g_cancellable_set_error_if_cancelled (cancellable, error); -+ return G_SOURCE_REMOVE; - } - - static gint -@@ -4245,7 +4491,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - "Component removed during call."); - - component = NULL; -- error_reported = TRUE; - - goto recv_error; - } -@@ -4734,6 +4979,11 @@ nice_agent_restart ( - /* step: regenerate tie-breaker value */ - priv_generate_tie_breaker (agent); - -+ /* step: reset controlling mode from the property value */ -+ agent->controlling_mode = agent->saved_controlling_mode; -+ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".", -+ agent, agent->controlling_mode ? "controlling" : "controlled"); -+ - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; - -@@ -5665,7 +5915,7 @@ _generate_candidate_sdp (NiceAgent *agent, - g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type)); - if (nice_address_is_valid (&candidate->base_addr) && - !nice_address_equal (&candidate->addr, &candidate->base_addr)) { -- port = nice_address_get_port (&candidate->addr); -+ port = nice_address_get_port (&candidate->base_addr); - nice_address_to_string (&candidate->base_addr, ip4); - g_string_append_printf (sdp, " raddr %s rport %d", ip4, - port == 0 ? 9 : port); -diff --git a/agent/agent.h b/agent/agent.h -index 47c4d5a..520c4c5 100644 ---- a/agent/agent.h -+++ b/agent/agent.h -@@ -377,6 +377,45 @@ typedef enum - NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP, - } NiceProxyType; - -+/** -+ * NiceNominationMode: -+ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode -+ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode -+ * -+ * An enum to specity the kind of nomination mode to use by -+ * the agent, as described in RFC 5245. Two modes exists, -+ * regular and aggressive. They differ by the way the controlling -+ * agent chooses to put the USE-CANDIDATE attribute in its STUN -+ * messages. The aggressive mode is supposed to nominate a pair -+ * faster, than the regular mode, potentially causing the nominated -+ * pair to change until the connection check completes. -+ * -+ * Since: 0.1.15 -+ */ -+typedef enum -+{ -+ NICE_NOMINATION_MODE_REGULAR = 0, -+ NICE_NOMINATION_MODE_AGGRESSIVE, -+} NiceNominationMode; -+ -+/** -+ * NiceAgentOption: -+ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default -+ * is aggrssive mode (see #NiceNominationMode). -+ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable(). -+ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode -+ * -+ * These are options that can be passed to nice_agent_new_full(). They set -+ * various properties on the agent. Not including them sets the property to -+ * the other value. -+ * -+ * Since: 0.1.15 -+ */ -+typedef enum { -+ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, -+ NICE_AGENT_OPTION_RELIABLE = 1 << 1, -+ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, -+} NiceAgentOption; - - /** - * NiceAgentRecvFunc: -@@ -428,6 +467,26 @@ nice_agent_new (GMainContext *ctx, NiceCompatibility compat); - NiceAgent * - nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - -+/** -+ * nice_agent_new_full: -+ * @ctx: The Glib Mainloop Context to use for timers -+ * @compat: The compatibility mode of the agent -+ * @flags: Flags to set the properties -+ * -+ * Create a new #NiceAgent with parameters that must be be defined at -+ * construction time. -+ * The returned object must be freed with g_object_unref() -+ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> -+ * -+ * Since: 0.1.15 -+ * -+ * Returns: The new agent GObject -+ */ -+NiceAgent * -+nice_agent_new_full (GMainContext *ctx, -+ NiceCompatibility compat, -+ NiceAgentOption flags); -+ - /** - * nice_agent_add_local_address: - * @agent: The #NiceAgent Object -@@ -447,7 +506,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); - gboolean - nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr); - -- - /** - * nice_agent_add_stream: - * @agent: The #NiceAgent Object -diff --git a/agent/candidate.c b/agent/candidate.c -index 27966ef..85f8f65 100644 ---- a/agent/candidate.c -+++ b/agent/candidate.c -@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate) - - return copy; - } -+ -+NICEAPI_EXPORT gboolean -+nice_candidate_equal_target (const NiceCandidate *candidate1, -+ const NiceCandidate *candidate2) -+{ -+ g_return_val_if_fail (candidate1 != NULL, FALSE); -+ g_return_val_if_fail (candidate2 != NULL, FALSE); -+ -+ return (candidate1->transport == candidate2->transport && -+ nice_address_equal (&candidate1->addr, &candidate2->addr)); -+} -diff --git a/agent/candidate.h b/agent/candidate.h -index fadfce3..e556c16 100644 ---- a/agent/candidate.h -+++ b/agent/candidate.h -@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate); - NiceCandidate * - nice_candidate_copy (const NiceCandidate *candidate); - --GType nice_candidate_get_type (void); -+/** -+ * nice_candidate_equal_target: -+ * @candidate1: A candidate -+ * @candidate2: A candidate -+ * -+ * Verifies that the candidates point to the same place, meaning they have -+ * the same transport and the same address. It ignores all other aspects. -+ * -+ * Returns: %TRUE if the candidates point to the same place -+ * -+ * Since: 0.1.15 -+ */ -+gboolean -+nice_candidate_equal_target (const NiceCandidate *candidate1, -+ const NiceCandidate *candidate2); -+ -+ GType nice_candidate_get_type (void); - - /** - * NICE_TYPE_CANDIDATE: -diff --git a/agent/component.c b/agent/component.c -index 32f7463..6eee90e 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context) - if (socket_source->socket->fileno == NULL) - return; - -+ /* Do not create a GSource for UDP turn socket, because it -+ * would duplicate the packets already received on the base -+ * UDP socket. -+ */ -+ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN) -+ return; -+ - /* Create a source. */ - source = g_socket_create_source (socket_source->socket->fileno, - G_IO_IN, NULL); -@@ -380,7 +387,7 @@ nice_component_restart (NiceComponent *cmp) - for (i = cmp->remote_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; - -- /* note: do not remove the local candidate that is -+ /* note: do not remove the remote candidate that is - * currently part of the 'selected pair', see ICE - * 9.1.1.1. "ICE Restarts" (ID-19) */ - if (candidate == cmp->selected_pair.remote) { -@@ -435,6 +442,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa - component->selected_pair.remote = pair->remote; - component->selected_pair.priority = pair->priority; - component->selected_pair.prflx_priority = pair->prflx_priority; -+ -+ nice_component_add_valid_candidate (component, pair->remote); - } - - /* -@@ -514,6 +523,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, - component->selected_pair.remote = remote; - component->selected_pair.priority = priority; - -+ /* Get into fallback mode where packets from any source is accepted once -+ * this has been called. This is the expected behavior of pre-ICE SIP. -+ */ -+ component->fallback_mode = TRUE; -+ - return local; - } - -@@ -991,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass) - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - } - -+static gboolean -+dummy_callback (gpointer data) -+{ -+ return G_SOURCE_CONTINUE; -+} -+ -+static void -+source_set_dummy_callback (GSource *source) -+{ -+ g_source_set_callback (source, dummy_callback, NULL, NULL); -+} -+ - static void - nice_component_init (NiceComponent *component) - { -@@ -1013,7 +1039,7 @@ nice_component_init (NiceComponent *component) - component->stop_cancellable = g_cancellable_new (); - component->stop_cancellable_source = - g_cancellable_source_new (component->stop_cancellable); -- g_source_set_dummy_callback (component->stop_cancellable_source); -+ source_set_dummy_callback (component->stop_cancellable_source); - g_source_attach (component->stop_cancellable_source, component->own_ctx); - component->ctx = g_main_context_ref (component->own_ctx); - -@@ -1107,6 +1133,9 @@ nice_component_finalize (GObject *obj) - g_warn_if_fail (cmp->remote_candidates == NULL); - g_warn_if_fail (cmp->incoming_checks == NULL); - -+ g_list_free_full (cmp->valid_candidates, -+ (GDestroyNotify) nice_candidate_free); -+ - g_clear_object (&cmp->tcp); - g_clear_object (&cmp->stop_cancellable); - g_clear_object (&cmp->iostream); -@@ -1225,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_) - child_socket_source->source = - g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, - NULL); -- g_source_set_dummy_callback (child_socket_source->source); -+ source_set_dummy_callback (child_socket_source->source); - g_source_add_child_source (source, child_socket_source->source); - g_source_unref (child_socket_source->source); - component_source->socket_sources = -@@ -1370,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id, - GSource *cancellable_source; - - cancellable_source = g_cancellable_source_new (cancellable); -- g_source_set_dummy_callback (cancellable_source); -+ source_set_dummy_callback (cancellable_source); - g_source_add_child_source ((GSource *) component_source, - cancellable_source); - g_source_unref (cancellable_source); -@@ -1421,3 +1450,83 @@ turn_server_unref (TurnServer *turn) - g_slice_free (TurnServer, turn); - } - } -+ -+void -+nice_component_add_valid_candidate (NiceComponent *component, -+ const NiceCandidate *candidate) -+{ -+ guint count = 0; -+ GList *item, *last = NULL; -+ -+ for (item = component->valid_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ last = item; -+ count++; -+ if (nice_candidate_equal_target (cand, candidate)) -+ return; -+ } -+ -+ /* New candidate */ -+ -+ if (nice_debug_is_enabled ()) { -+ char str[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&candidate->addr, str); -+ nice_debug ("Agent %p : %d:%d Adding valid source" -+ " candidate: %s:%d trans: %d", component->agent, -+ candidate->stream_id, candidate->component_id, str, -+ nice_address_get_port (&candidate->addr), candidate->transport); -+ } -+ -+ component->valid_candidates = g_list_prepend ( -+ component->valid_candidates, nice_candidate_copy (candidate)); -+ -+ /* Delete the last one to make sure we don't have a list that is too long, -+ * the candidates are not freed on ICE restart as this would be more complex, -+ * we just keep the list not too long. -+ */ -+ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) { -+ NiceCandidate *cand = last->data; -+ -+ component->valid_candidates = g_list_delete_link ( -+ component->valid_candidates, last); -+ nice_candidate_free (cand); -+ } -+} -+ -+gboolean -+nice_component_verify_remote_candidate (NiceComponent *component, -+ const NiceAddress *address, NiceSocket *nicesock) -+{ -+ GList *item; -+ -+ if (component->fallback_mode) -+ return TRUE; -+ -+ for (item = component->valid_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD && -+ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || -+ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && -+ nice_address_equal (address, &cand->addr)) { -+ /* fast return if it's already the first */ -+ if (item == component->valid_candidates) -+ return TRUE; -+ -+ /* Put the current candidate at the top so that in the normal use-case, -+ * this function becomes O(1). -+ */ -+ component->valid_candidates = g_list_remove_link ( -+ component->valid_candidates, item); -+ component->valid_candidates = g_list_concat (item, -+ component->valid_candidates); -+ -+ return TRUE; -+ } -+ } -+ -+ return FALSE; -+} -diff --git a/agent/component.h b/agent/component.h -index 6712794..a8a1222 100644 ---- a/agent/component.h -+++ b/agent/component.h -@@ -159,12 +159,14 @@ struct _NiceComponent { - NiceComponentState state; - GSList *local_candidates; /* list of NiceCandidate objs */ - GSList *remote_candidates; /* list of NiceCandidate objs */ -+ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */ - GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */ - guint socket_sources_age; /* incremented when socket_sources changes */ - GSList *incoming_checks; /* list of IncomingCheck objs */ - GList *turn_servers; /* List of TurnServer objs */ - CandidatePair selected_pair; /* independent from checklists, - see ICE 11.1. "Sending Media" (ID-19) */ -+ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */ - NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */ - NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */ - /* I/O handling. The main context must always be non-NULL, and is used for all -@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn); - void - turn_server_unref (TurnServer *turn); - -+void -+nice_component_add_valid_candidate (NiceComponent *component, -+ const NiceCandidate *candidate); -+ -+gboolean -+nice_component_verify_remote_candidate (NiceComponent *component, -+ const NiceAddress *address, NiceSocket *nicesock); -+ - - G_END_DECLS - -diff --git a/agent/conncheck.c b/agent/conncheck.c -index dda2f2f..c8a4edf 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -64,8 +64,8 @@ - - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); - static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); --static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); --static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); -+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id); -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); - static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); - static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, - guint component_id, NiceCandidate *remote, NiceCandidate *local, -@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data); - static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - NiceAgent *agent, guint stream_id, NiceComponent *component, - NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); -+static gboolean priv_update_selected_pair (NiceAgent *agent, -+ NiceComponent *component, CandidateCheckPair *pair); - - static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - { -@@ -84,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - now->tv_sec >= timer->tv_sec; - } - -+static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now) -+{ -+ unsigned int delay; -+ if (now->tv_sec > timer->tv_sec || -+ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec)) -+ return 0; -+ delay = (timer->tv_sec - now->tv_sec) * 1000; -+ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000; -+ return delay; -+} -+ - static gchar - priv_state_to_gchar (NiceCheckState state) - { -@@ -98,8 +111,6 @@ priv_state_to_gchar (NiceCheckState state) - return 'F'; - case NICE_CHECK_FROZEN: - return 'Z'; -- case NICE_CHECK_CANCELLED: -- return 'C'; - case NICE_CHECK_DISCOVERED: - return 'D'; - default: -@@ -107,6 +118,54 @@ priv_state_to_gchar (NiceCheckState state) - } - } - -+static const gchar * -+priv_state_to_string (NiceCheckState state) -+{ -+ switch (state) { -+ case NICE_CHECK_WAITING: -+ return "waiting"; -+ case NICE_CHECK_IN_PROGRESS: -+ return "in progress"; -+ case NICE_CHECK_SUCCEEDED: -+ return "succeeded"; -+ case NICE_CHECK_FAILED: -+ return "failed"; -+ case NICE_CHECK_FROZEN: -+ return "frozen"; -+ case NICE_CHECK_DISCOVERED: -+ return "discovered"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * -+priv_ice_return_to_string (StunUsageIceReturn ice_return) -+{ -+ switch (ice_return) { -+ case STUN_USAGE_ICE_RETURN_SUCCESS: -+ return "success"; -+ case STUN_USAGE_ICE_RETURN_ERROR: -+ return "error"; -+ case STUN_USAGE_ICE_RETURN_INVALID: -+ return "invalid"; -+ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT: -+ return "role conflict"; -+ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST: -+ return "invalid request"; -+ case STUN_USAGE_ICE_RETURN_INVALID_METHOD: -+ return "invalid method"; -+ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR: -+ return "memory error"; -+ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS: -+ return "invalid address"; -+ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS: -+ return "no mapped address"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ - static const gchar * - priv_candidate_type_to_string (NiceCandidateType type) - { -@@ -130,12 +189,15 @@ priv_candidate_type_to_string (NiceCandidateType type) - static void - priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) - { -- GSList *i, *k; -- guint j; -+ GSList *i, *k, *l; -+ guint j, m; -+ GTimeVal now; - - if (!nice_debug_is_verbose ()) - return; - -+ g_get_current_time (&now); -+ - #define PRIORITY_LEN 32 - - nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", -@@ -148,26 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * - if (pair->component_id == j) { - gchar local_addr[INET6_ADDRSTRLEN]; - gchar remote_addr[INET6_ADDRSTRLEN]; -- StunTimer *timer = &pair->timer; - - nice_address_to_string (&pair->local->addr, local_addr); - nice_address_to_string (&pair->remote->addr, remote_addr); - - nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -- "f=%s t=%s:%s timer=%d/%d %d/%dms " -- "[%s]:%u > [%s]:%u state=%c%s%s%s", -+ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s", - agent, pair->stream_id, pair->component_id, pair, - pair->foundation, - priv_candidate_type_to_string (pair->local->type), - priv_candidate_type_to_string (pair->remote->type), -- timer->retransmissions, timer->max_retransmissions, -- timer->delay - stun_timer_remainder (timer), timer->delay, - local_addr, nice_address_get_port (&pair->local->addr), - remote_addr, nice_address_get_port (&pair->remote->addr), - priv_state_to_gchar (pair->state), - pair->valid ? "V" : "", - pair->nominated ? "N" : "", - g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); -+ -+ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) { -+ StunTransaction *stun = l->data; -+ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -+ "stun#=%d timer=%d/%d %d/%dms buf=%p %s", -+ agent, pair->stream_id, pair->component_id, pair, m, -+ stun->timer.retransmissions, stun->timer.max_retransmissions, -+ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now), -+ stun->timer.delay, -+ stun->message.buffer, -+ (m == 0 && pair->retransmit) ? "(R)" : ""); -+ } - } - } - } -@@ -181,6 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa - { - g_assert (pair); - -+ pair->state = NICE_CHECK_IN_PROGRESS; -+ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); - if (agent->triggered_check_queue == NULL || - g_slist_find (agent->triggered_check_queue, pair) == NULL) - agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -@@ -209,6 +281,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) - return pair; - } - -+/* -+ * Check if the conncheck list if Active according to -+ * ICE spec, 5.7.4 (Computing States) -+ * -+ * note: the ICE spec in unclear about that, but the check list should -+ * be considered active when there is at least a pair in Waiting state -+ * OR a pair in In-Progress state. -+ */ -+static gboolean -+priv_is_checklist_active (NiceStream *stream) -+{ -+ GSList *i; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS) -+ return TRUE; -+ } -+ return FALSE; -+} -+ -+/* -+ * Check if the conncheck list if Frozen according to -+ * ICE spec, 5.7.4 (Computing States) -+ */ -+static gboolean -+priv_is_checklist_frozen (NiceStream *stream) -+{ -+ GSList *i; -+ -+ if (stream->conncheck_list == NULL) -+ return FALSE; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state != NICE_CHECK_FROZEN) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* -+ * Check if all components of the stream have -+ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.) -+ */ -+static gboolean -+priv_all_components_have_valid_pair (NiceStream *stream) -+{ -+ guint i; -+ GSList *j; -+ -+ for (i = 1; i <= stream->n_components; i++) { -+ for (j = stream->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (p->component_id == i && p->valid) -+ break; -+ } -+ if (j == NULL) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* -+ * Check if the foundation in parameter matches the foundation -+ * of a valid pair in the conncheck list [of stream] (used for ICE spec, -+ * 7.1.3.2.3, point 2.) -+ */ -+static gboolean -+priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream) -+{ -+ GSList *i; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->valid && -+ strncmp (p->foundation, foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) -+ return TRUE; -+ } -+ return FALSE; -+} -+ - /* - * Finds the next connectivity check in WAITING state. - */ -@@ -218,7 +373,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - - /* note: list is sorted in priority order to first waiting check has - * the highest priority */ -- - for (i = conn_check_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->state == NICE_CHECK_WAITING) -@@ -228,6 +382,57 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - return NULL; - } - -+/* -+ * Finds the next connectivity check in FROZEN state. -+ */ -+static CandidateCheckPair * -+priv_conn_check_find_next_frozen (GSList *conn_check_list) -+{ -+ GSList *i; -+ -+ /* note: list is sorted in priority order to first frozen check has -+ * the highest priority */ -+ for (i = conn_check_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_FROZEN) -+ return p; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Returns the number of active check lists of the agent -+ */ -+static guint -+priv_number_of_active_check_lists (NiceAgent *agent) -+{ -+ guint n = 0; -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) -+ if (priv_is_checklist_active (i->data)) -+ n++; -+ return n; -+} -+ -+/* -+ * Returns the first stream of the agent having a Frozen -+ * connection check list -+ */ -+static NiceStream * -+priv_find_first_frozen_check_list (NiceAgent *agent) -+{ -+ GSList *i; -+ -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (priv_is_checklist_frozen (stream)) -+ return stream; -+ } -+ return NULL; -+} -+ - /* - * Initiates a new connectivity check for a ICE candidate pair. - * -@@ -235,8 +440,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - */ - static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) - { -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); - pair->state = NICE_CHECK_IN_PROGRESS; - nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); - conn_check_send (agent, pair); -@@ -246,58 +449,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * - /* - * Unfreezes the next connectivity check in the list. Follows the - * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec -- * (ID-19), with some exceptions (see comments in code). -+ * (RFC5245) - * - * See also sect 7.1.2.2.3 (Updating Pair States), and - * priv_conn_check_unfreeze_related(). - * - * @return TRUE on success, and FALSE if no frozen candidates were found. - */ --static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) -+static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream) - { -- CandidateCheckPair *pair = NULL; - GSList *i, *j; -- -- /* XXX: the unfreezing is implemented a bit differently than in the -- * current ICE spec, but should still be interoperate: -- * - checks are not grouped by foundation -- * - one frozen check is unfrozen (lowest component-id, highest -- * priority) -- */ -+ GSList *found_list = NULL; -+ gboolean result = FALSE; - - priv_print_conn_check_lists (agent, G_STRFUNC, NULL); - -- for (i = agent->streams; i; i = i->next) { -- NiceStream *stream = i->data; -- guint64 max_frozen_priority = 0; -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p1 = i->data; -+ CandidateCheckPair *pair = NULL; -+ guint lowest_component_id = stream->n_components + 1; -+ guint64 highest_priority = 0; - -+ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp)) -+ continue; -+ found_list = g_slist_prepend (found_list, p1->foundation); - - for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *p = j->data; -- -- /* XXX: the prio check could be removed as the pairs are sorted -- * already */ -- -- if (p->state == NICE_CHECK_FROZEN) { -- if (p->priority > max_frozen_priority) { -- max_frozen_priority = p->priority; -- pair = p; -- } -+ CandidateCheckPair *p2 = i->data; -+ if (strncmp (p2->foundation, p1->foundation, -+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { -+ if (p2->component_id < lowest_component_id || -+ (p2->component_id == lowest_component_id && -+ p2->priority > highest_priority)) { -+ pair = p2; -+ lowest_component_id = p2->component_id; -+ highest_priority = p2->priority; -+ } - } - } - -- if (pair) -- break; -- } -- -- if (pair) { -- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); -- pair->state = NICE_CHECK_WAITING; -- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -- return TRUE; -+ if (pair) { -+ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", -+ agent, pair, pair->stream_id, pair->component_id, pair->foundation); -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ result = TRUE; -+ } - } -- -- return FALSE; -+ g_slist_free (found_list); -+ return result; - } - - /* -@@ -314,7 +514,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) - { - GSList *i, *j; -- guint unfrozen = 0; - - g_assert (ok_check); - g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); -@@ -334,60 +533,147 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre - nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); - p->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- ++unfrozen; - } - } - } - - /* step: perform the step (2) of 'Updating Pair States' */ - stream = agent_find_stream (agent, ok_check->stream_id); -- if (nice_stream_all_components_ready (stream)) { -- /* step: unfreeze checks from other streams */ -+ if (priv_all_components_have_valid_pair (stream)) { - for (i = agent->streams; i ; i = i->next) { -+ /* the agent examines the check list for each other -+ * media stream in turn -+ */ - NiceStream *s = i->data; -- for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *p = j->data; -- -- if (p->stream_id == s->id && -- p->stream_id != ok_check->stream_id) { -- if (p->state == NICE_CHECK_FROZEN && -- strcmp (p->foundation, ok_check->foundation) == 0) { -+ if (s->id == ok_check->stream_id) -+ continue; -+ if (priv_is_checklist_active (s)) { -+ /* checklist is Active -+ */ -+ for (j = s->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (p->state == NICE_CHECK_FROZEN && -+ priv_foundation_matches_a_valid_pair (p->foundation, stream)) { - nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); - p->state = NICE_CHECK_WAITING; - nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- ++unfrozen; -- -- } -- } -+ } -+ } -+ } else if (priv_is_checklist_frozen (s)) { -+ /* checklist is Frozen -+ */ -+ gboolean match_found = FALSE; -+ /* check if there is one pair in the check list whose -+ * foundation matches a pair in the valid list under -+ * consideration -+ */ -+ for (j = s->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *p = j->data; -+ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { -+ match_found = TRUE; -+ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); -+ p->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, p); -+ } -+ } -+ -+ if (!match_found) { -+ /* set the pair with the lowest component ID -+ * and highest priority to Waiting -+ */ -+ priv_conn_check_unfreeze_next (agent, s); -+ } - } -- /* note: only unfreeze check from one stream at a time */ -- if (unfrozen) -- break; - } - } -+} - -- if (unfrozen == 0) -- priv_conn_check_unfreeze_next (agent); -+/* -+ * Create a new STUN transaction and add it to the list -+ * of ongoing stun transactions of a pair. -+ * -+ * @pair the pair the new stun transaction should be added to. -+ * @return the created stun transaction. -+ */ -+static StunTransaction * -+priv_add_stun_transaction (CandidateCheckPair *pair) -+{ -+ StunTransaction *stun = g_slice_new0 (StunTransaction); -+ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun); -+ pair->retransmit = TRUE; -+ return stun; - } - -+/* -+ * Forget a STUN transaction. -+ * -+ * @data the stun transaction to be forgotten. -+ * @user_data the component contained the concerned stun agent. -+ */ - static void --candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) -+priv_forget_stun_transaction (gpointer data, gpointer user_data) - { -+ StunTransaction *stun = data; -+ NiceComponent *component = user_data; - StunTransactionId id; -+ -+ if (stun->message.buffer != NULL) { -+ stun_message_id (&stun->message, id); -+ stun_agent_forget_transaction (&component->stun_agent, id); -+ } -+} -+ -+static void -+priv_free_stun_transaction (gpointer data) -+{ -+ g_slice_free (StunTransaction, data); -+} -+ -+/* -+ * Remove a STUN transaction from a pair, and forget it -+ * from the related component stun agent. -+ * -+ * @pair the pair the stun transaction should be removed from. -+ * @stun the stun transaction to be removed. -+ * @component the component containing the stun agent used to -+ * forget the stun transaction. -+ */ -+static void -+priv_remove_stun_transaction (CandidateCheckPair *pair, -+ StunTransaction *stun, NiceComponent *component) -+{ -+ priv_forget_stun_transaction (stun, component); -+ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun); -+ priv_free_stun_transaction (stun); -+} -+ -+/* -+ * Remove all STUN transactions from a pair, and forget them -+ * from the related component stun agent. -+ * -+ * @pair the pair the stun list should be cleared. -+ * @component the component containing the stun agent used to -+ * forget the stun transactions. -+ */ -+static void -+priv_free_all_stun_transactions (CandidateCheckPair *pair, -+ NiceComponent *component) -+{ -+ if (component) -+ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component); -+ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction); -+ pair->stun_transactions = NULL; -+} -+ -+static void -+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) -+{ - NiceComponent *component; - - component = nice_stream_find_component_by_id (stream, p->component_id); -- - p->state = NICE_CHECK_FAILED; - nice_debug ("Agent %p : pair %p state FAILED", agent, p); -- -- if (p->stun_message.buffer != NULL) { -- stun_message_id (&p->stun_message, id); -- stun_agent_forget_transaction (&component->stun_agent, id); -- } -- -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -+ priv_free_all_stun_transactions (p, component); - } - - /* -@@ -398,78 +684,183 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) - { - gboolean keep_timer_going = FALSE; -- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, -- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; -- guint frozen = 0, waiting = 0; -- GSList *i, *k; -+ GSList *i, *j; -+ CandidateCheckPair *pair; -+ unsigned int timeout; -+ GTimeVal now; - -+ g_get_current_time (&now); -+ -+ /* step: process ongoing STUN transactions */ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; -+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ NiceComponent *component; -+ StunTransaction *stun; - -- if (p->state == NICE_CHECK_IN_PROGRESS) { -- if (p->stun_message.buffer == NULL) { -- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -- } else if (priv_timer_expired (&p->next_tick, now)) { -- switch (stun_timer_refresh (&p->timer)) { -- case STUN_USAGE_TIMER_RETURN_TIMEOUT: -- { -- /* case: error, abort processing */ -- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -- nice_address_to_string (&p->local->addr, tmpbuf1); -- nice_address_to_string (&p->remote->addr, tmpbuf2); -- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -- tmpbuf1, nice_address_get_port (&p->local->addr), -- tmpbuf2, nice_address_get_port (&p->remote->addr)); -- candidate_check_pair_fail (stream, agent, p); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", retransmission failed"); -- -- break; -- } -- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -- { -- /* case: not ready, so schedule a new timeout */ -- unsigned int timeout = stun_timer_remainder (&p->timer); -- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -- "(timeout %dms, delay=%dms, retrans=%d).", -- agent, p, timeout, p->timer.delay, p->timer.retransmissions); -+ if (p->stun_transactions == NULL) -+ continue; - -- agent_socket_send (p->sockptr, &p->remote->addr, -- stun_message_length (&p->stun_message), -- (gchar *)p->stun_buffer); -+ if (!agent_find_component (agent, p->stream_id, p->component_id, -+ NULL, &component)) -+ continue; - -+ /* The first stun transaction of the list may eventually be -+ * retransmitted, other stun transactions just have their -+ * timer updated. -+ */ - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -+ j = p->stun_transactions->next; - -- *stun_transmitted = TRUE; -- return TRUE; -- } -- case STUN_USAGE_TIMER_RETURN_SUCCESS: -- { -- unsigned int timeout = stun_timer_remainder (&p->timer); -+ /* process all stun transactions except the first one */ -+ while (j) { -+ StunTransaction *s = j->data; -+ GSList *next = j->next; - -- /* note: convert from milli to microseconds for g_time_val_add() */ -- p->next_tick = *now; -- g_time_val_add (&p->next_tick, timeout * 1000); -- -- keep_timer_going = TRUE; -- break; -- } -+ if (priv_timer_expired (&s->next_tick, &now)) -+ switch (stun_timer_refresh (&s->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: -+ priv_remove_stun_transaction (p, s, component); -+ break; -+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -+ timeout = stun_timer_remainder (&s->timer); -+ s->next_tick = now; -+ g_time_val_add (&s->next_tick, timeout * 1000); -+ break; - default: -- /* Nothing to do. */ - break; -- } - } -+ j = next; - } - -+ if (p->state != NICE_CHECK_IN_PROGRESS) -+ continue; -+ -+ /* process the first stun transaction of the list */ -+ stun = p->stun_transactions->data; -+ if (!priv_timer_expired (&stun->next_tick, &now)) -+ continue; -+ -+ switch (stun_timer_refresh (&stun->timer)) { -+ case STUN_USAGE_TIMER_RETURN_TIMEOUT: -+timer_return_timeout: -+ /* case: error, abort processing */ -+ nice_address_to_string (&p->local->addr, tmpbuf1); -+ nice_address_to_string (&p->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : Retransmissions failed, giving up on " -+ "connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -+ tmpbuf1, nice_address_get_port (&p->local->addr), -+ tmpbuf2, nice_address_get_port (&p->remote->addr)); -+ candidate_check_pair_fail (stream, agent, p); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", retransmission failed"); -+ -+ /* perform a check if a transition state from connected to -+ * ready can be performed. This may happen here, when the last -+ * in-progress pair has expired its retransmission count -+ * in priv_conn_check_tick_stream(), which is a condition to -+ * make the transition connected to ready. -+ */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ break; -+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: -+ /* case: retransmission stopped, due to the nomination of -+ * a pair with a higher priority than this in-progress pair, -+ * ICE spec, sect 8.1.2 "Updating States", item 2.2 -+ */ -+ if (!p->retransmit) -+ goto timer_return_timeout; -+ -+ /* case: not ready, so schedule a new timeout */ -+ timeout = stun_timer_remainder (&stun->timer); -+ -+ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -+ "(timer=%d/%d %d/%dms).", -+ agent, p, -+ stun->timer.retransmissions, stun->timer.max_retransmissions, -+ stun->timer.delay - timeout, stun->timer.delay); -+ -+ agent_socket_send (p->sockptr, &p->remote->addr, -+ stun_message_length (&stun->message), -+ (gchar *)stun->buffer); -+ -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); -+ -+ return TRUE; -+ case STUN_USAGE_TIMER_RETURN_SUCCESS: -+ timeout = stun_timer_remainder (&stun->timer); -+ -+ /* note: convert from milli to microseconds for g_time_val_add() */ -+ stun->next_tick = now; -+ g_time_val_add (&stun->next_tick, timeout * 1000); -+ -+ keep_timer_going = TRUE; -+ break; -+ default: -+ /* Nothing to do. */ -+ break; -+ } -+ } -+ -+ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" -+ * note: This code is executed when the triggered checks list is -+ * empty, and when no STUN message has been sent (pacing constraint) -+ */ -+ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -+ if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Waiting state"); -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ -+ /* note: this is unclear in the ICE spec, but a check list in Frozen -+ * state (where all pairs are in Frozen state) is not supposed to -+ * change its state by an ordinary check, but only by the success of -+ * checks in other check lists, in priv_conn_check_unfreeze_related(). -+ * The underlying idea is to concentrate the checks on a single check -+ * list initially. -+ */ -+ if (priv_is_checklist_frozen (stream)) -+ return keep_timer_going; -+ -+ /* step: ordinary check continued, if there's no pair in the waiting -+ * state, pick a pair in the frozen state -+ */ -+ pair = priv_conn_check_find_next_frozen (stream->conncheck_list); -+ if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Frozen state"); -+ pair->state = NICE_CHECK_WAITING; -+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ return keep_timer_going; -+} -+ -+static gboolean -+priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) -+{ -+ gboolean keep_timer_going = FALSE; -+ guint s_inprogress = 0; -+ guint s_succeeded = 0; -+ guint s_discovered = 0; -+ guint s_nominated = 0; -+ guint s_waiting_for_nomination = 0; -+ guint s_valid = 0; -+ guint frozen = 0; -+ guint waiting = 0; -+ GSList *i, *k; -+ -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; - if (p->state == NICE_CHECK_FROZEN) - ++frozen; - else if (p->state == NICE_CHECK_IN_PROGRESS) -@@ -495,12 +886,102 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - if (s_inprogress) - keep_timer_going = TRUE; - -- /* note: if some components have established connectivity, -- * but yet no nominated pair, keep timer going */ - if (s_nominated < stream->n_components && - s_waiting_for_nomination) { -- keep_timer_going = TRUE; -- if (agent->controlling_mode) { -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && -+ agent->controlling_mode) { -+#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2 -+ /* ICE 8.1.1.1 Regular nomination -+ * we choose to nominate the valid pair of a component if -+ * - there is no pair left frozen, waiting or in-progress, or -+ * - if there are at least two valid pairs, or -+ * - if there is at least one valid pair of type HOST-HOST -+ * -+ * This is the "stopping criterion" described in 8.1.1.1, and is -+ * a "local optimization" between accumulating more valid pairs, -+ * and limiting the time spent waiting for in-progress connections -+ * checks until they finally fail. -+ */ -+ GSList *component_item; -+ -+ for (component_item = stream->components; component_item; -+ component_item = component_item->next) { -+ NiceComponent *component = component_item->data; -+ gboolean already_done = FALSE; -+ gboolean stopping_criterion = FALSE; -+ guint p_valid = 0; -+ guint p_frozen = 0; -+ guint p_waiting = 0; -+ guint p_inprogress = 0; -+ guint p_host_host_valid = 0; -+ -+ /* verify that the choice of the pair to be nominated -+ * has not already been done -+ */ -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ if (p->component_id == component->id) { -+ if (p->use_candidate_on_next_check) -+ already_done = TRUE; -+ if (p->state == NICE_CHECK_FROZEN) -+ p_frozen++; -+ else if (p->state == NICE_CHECK_WAITING) -+ p_waiting++; -+ else if (p->state == NICE_CHECK_IN_PROGRESS) -+ p_inprogress++; -+ if (p->valid) -+ p_valid++; -+ if (p->valid && -+ p->local->type == NICE_CANDIDATE_TYPE_HOST && -+ p->remote->type == NICE_CANDIDATE_TYPE_HOST) -+ p_host_host_valid++; -+ } -+ } -+ -+ if (already_done) -+ continue; -+ -+ stopping_criterion = -+ (p_host_host_valid > 0 || -+ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS || -+ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0)); -+ -+ if (!stopping_criterion) -+ continue; -+ -+ /* when the stopping criterion is satisfied, we choose -+ * a pair to be nominated in the list of valid pairs, -+ * and add it to the triggered checks list -+ */ -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *p = k->data; -+ /* note: highest priority item selected (list always sorted) */ -+ if (p->component_id == component->id && -+ !p->nominated && -+ !p->use_candidate_on_next_check && -+ p->valid) { -+ /* According a ICE spec, sect 8.1.1.1. "Regular -+ * Nomination", we enqueue the check that produced this -+ * valid pair. When this pair has been discovered, we want -+ * to test its parent pair instead. -+ */ -+ if (p->succeeded_pair != NULL) { -+ g_assert (p->state == NICE_CHECK_DISCOVERED); -+ p = p->succeeded_pair; -+ } -+ g_assert (p->state == NICE_CHECK_SUCCEEDED); -+ nice_debug ("Agent %p : restarting check %p with " -+ "USE-CANDIDATE attrib (regular nomination)", agent, p); -+ p->use_candidate_on_next_check = TRUE; -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ keep_timer_going = TRUE; -+ break; /* move to the next component */ -+ } -+ } -+ } -+ } -+ } else if (agent->controlling_mode) { - GSList *component_item; - - for (component_item = stream->components; component_item; -@@ -515,7 +996,9 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -+ priv_update_selected_pair (agent, component, p); - priv_add_pair_to_triggered_check_queue (agent, p); -+ keep_timer_going = TRUE; - break; /* move to the next component */ - } - } -@@ -542,6 +1025,7 @@ conn_check_stop (NiceAgent *agent) - g_source_destroy (agent->conncheck_timer_source); - g_source_unref (agent->conncheck_timer_source); - agent->conncheck_timer_source = NULL; -+ agent->conncheck_timer_grace_period = 0; - } - - -@@ -557,75 +1041,83 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - { - CandidateCheckPair *pair = NULL; - gboolean keep_timer_going = FALSE; -- gboolean res; -- /* note: we try to only generate a single stun transaction per timer -- * callback, to respect some pacing of STUN transaction, as per -- * appendix B.1 of ICE spec. -- */ -- gboolean stun_transmitted = FALSE; - GSList *i, *j; -- GTimeVal now; - -- /* step: process ongoing STUN transactions */ -- g_get_current_time (&now); -- -- for (j = agent->streams; j; j = j->next) { -- NiceStream *stream = j->data; -- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); -- if (res) -- keep_timer_going = res; -- if (stun_transmitted) -- return TRUE; -+ /* configure the initial state of the check lists of the agent -+ * as described in ICE spec, 5.7.4 -+ * -+ * if all pairs in all check lists are in frozen state, then -+ * we are in the initial state (5.7.4, point 1.) -+ */ -+ if (priv_number_of_active_check_lists (agent) == 0) { -+ /* set some pairs of the first stream in the waiting state -+ * ICE spec, 5.7.4, point 2. -+ * -+ * note: we adapt the ICE spec here, by selecting the first -+ * frozen check list, which is not necessarily the check -+ * list of the first stream (the first stream may be completed) -+ */ -+ NiceStream *stream = priv_find_first_frozen_check_list (agent); -+ if (stream) -+ priv_conn_check_unfreeze_next (agent, stream); - } - -- /* step: first initiate a conncheck with a pair from the triggered list */ -+ /* step: perform a test from the triggered checks list, -+ * ICE spec, 5.8 "Scheduling Checks" -+ */ - pair = priv_get_pair_from_triggered_check_queue (agent); - - if (pair) { - priv_print_conn_check_lists (agent, G_STRFUNC, - ", got a pair from triggered check list"); -- priv_conn_check_initiate (agent, pair); -+ conn_check_send (agent, pair); - return TRUE; - } - -- /* step: when the triggered list is empty, -- * find the highest priority waiting check and send it */ -+ /* step: process ongoing STUN transactions and -+ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" -+ */ - for (i = agent->streams; i ; i = i->next) { - NiceStream *stream = i->data; -- -- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -- if (pair) -- break; -+ if (priv_conn_check_tick_stream (stream, agent)) -+ keep_timer_going = TRUE; -+ if (priv_conn_check_tick_stream_nominate (stream, agent)) -+ keep_timer_going = TRUE; - } - -- if (pair) { -- priv_conn_check_initiate (agent, pair); -- return TRUE; -+ /* step: if no work left and a conncheck list of a stream is still -+ * frozen, set the pairs to waiting, according to ICE SPEC, sect -+ * 7.1.3.3. "Check List and Timer State Updates" -+ */ -+ if (!keep_timer_going) { -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ if (priv_is_checklist_frozen (stream)) { -+ nice_debug ("Agent %p : stream %d conncheck list is still " -+ "frozen, while other lists are completed. Unfreeze it.", -+ agent, stream->id); -+ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream); -+ } -+ } - } - -- /* step: when there's no pair in the Waiting state, -- * unfreeze a new pair and check it -+ /* note: we provide a grace period before declaring a component as -+ * failed. Components marked connected, and then ready follow another -+ * code path, and are not concerned by this grace period. - */ -- res = priv_conn_check_unfreeze_next (agent); -- -- for (i = agent->streams; i ; i = i->next) { -- NiceStream *stream = i->data; -+ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0) -+ nice_debug ("Agent %p : waiting %d msecs before checking " -+ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD); - -- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -- if (pair) -- break; -- } -- -- if (pair) { -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a pair in Waiting state"); -- priv_conn_check_initiate (agent, pair); -- return TRUE; -- } -+ if (keep_timer_going) -+ agent->conncheck_timer_grace_period = 0; -+ else -+ agent->conncheck_timer_grace_period += agent->timer_ta; - - /* step: stop timer if no work left */ -- if (keep_timer_going != TRUE) { -- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); -+ if (!keep_timer_going && -+ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) { -+ nice_debug ("Agent %p : checking for failed components now.", agent); - for (i = agent->streams; i; i = i->next) { - NiceStream *stream = i->data; - priv_update_check_list_failed_components (agent, stream); -@@ -635,6 +1127,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - } - } - -+ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); - priv_print_conn_check_lists (agent, G_STRFUNC, - ", conncheck timer stopped"); - -@@ -645,9 +1138,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - - /* XXX: what to signal, is all processing now really done? */ - nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); -+ return FALSE; - } - -- return keep_timer_going; -+ return TRUE; - } - - static gboolean priv_conn_check_tick (gpointer pointer) -@@ -888,8 +1382,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - agent, buf_len, p->keepalive.stun_message.buffer); - - if (buf_len > 0) { -- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&p->keepalive.timer, -+ agent->stun_initial_timeout, -+ agent->stun_max_retransmissions); - - agent->media_after_tick = FALSE; - -@@ -1116,8 +1611,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand) - } - - if (buffer_len > 0) { -- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&cand->timer, -+ cand->agent->stun_initial_timeout, -+ cand->agent->stun_max_retransmissions); - - /* send the refresh */ - agent_socket_send (cand->nicesock, &cand->server, -@@ -1213,154 +1709,136 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea - if (nice_address_equal (&icheck->from, &pair->remote->addr) && - icheck->local_socket == pair->sockptr) { - nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, pair->remote); - if (icheck->use_candidate) - priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); - } - } - } - - --static GSList *prune_cancelled_conn_check (GSList *conncheck_list) --{ -- GSList *item = conncheck_list; -- -- while (item) { -- CandidateCheckPair *pair = item->data; -- GSList *next = item->next; -- -- if (pair->state == NICE_CHECK_CANCELLED) { -- conn_check_free_item (pair); -- conncheck_list = g_slist_delete_link (conncheck_list, item); -- } -- -- item = next; -- } -- -- return conncheck_list; --} -- -- - /* - * Handle any processing steps for connectivity checks after -- * remote candidates have been set. This function handles -+ * remote credentials have been set. This function handles - * the special case where answerer has sent us connectivity -- * checks before the answer (containing candidate information), -+ * checks before the answer (containing credentials information), - * reaches us. The special case is documented in sect 7.2 - * if ICE spec (ID-19). - */ --void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) -+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) - { -- GSList *j, *k, *l, *m, *n; -+ GSList *j, *k, *l, *m, *n, *o; - - for (j = stream->conncheck_list; j ; j = j->next) { - CandidateCheckPair *pair = j->data; -- if (pair->component_id == component->id) { -- gboolean match = FALSE; -- -- /* performn delayed processing of spec steps section 7.2.1.4, -- and section 7.2.1.5 */ -- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -- -- for (k = component->incoming_checks; k; k = k->next) { -- IncomingCheck *icheck = k->data; -- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to -- * be handled separately */ -- for (l = component->remote_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&icheck->from, &cand->addr)) { -- match = TRUE; -- break; -- } -- } -- if (match != TRUE) { -- /* note: we have gotten an incoming connectivity check from -- * an address that is not a known remote candidate */ -- -- NiceCandidate *local_candidate = NULL; -- NiceCandidate *remote_candidate = NULL; -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -- agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- /* We need to find which local candidate was used */ -- uint8_t uname[NICE_STREAM_MAX_UNAME]; -- guint uname_len; -- -- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -- "stored pending check", agent); -- -- for (m = component->remote_candidates; -- m != NULL && remote_candidate == NULL; m = m->next) { -- for (n = component->local_candidates; n; n = n->next) { -- NiceCandidate *rcand = m->data; -- NiceCandidate *lcand = n->data; -- -- uname_len = priv_create_username (agent, stream, -- component->id, rcand, lcand, -- uname, sizeof (uname), TRUE); -- -- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -- icheck->username_len, uname_len, -- icheck->username && uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0); -- stun_debug_bytes (" first username: ", -- icheck->username, -- icheck->username? icheck->username_len : 0); -- stun_debug_bytes (" second username: ", uname, uname_len); -- -- if (icheck->username && -- uname_len == icheck->username_len && -- memcmp (uname, icheck->username, icheck->username_len) == 0) { -- local_candidate = lcand; -- remote_candidate = rcand; -- break; -- } -- } -- } -- } else { -- for (l = component->local_candidates; l; l = l->next) { -- NiceCandidate *cand = l->data; -- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -- local_candidate = cand; -+ NiceComponent *component = -+ nice_stream_find_component_by_id (stream, pair->component_id); -+ gboolean match = FALSE; -+ -+ /* performn delayed processing of spec steps section 7.2.1.4, -+ and section 7.2.1.5 */ -+ priv_preprocess_conn_check_pending_data (agent, stream, component, pair); -+ -+ for (k = component->incoming_checks; k; k = k->next) { -+ IncomingCheck *icheck = k->data; -+ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to -+ * be handled separately */ -+ for (l = component->remote_candidates; l; l = l->next) { -+ NiceCandidate *cand = l->data; -+ if (nice_address_equal (&icheck->from, &cand->addr)) { -+ match = TRUE; -+ break; -+ } -+ } -+ if (match != TRUE) { -+ /* note: we have gotten an incoming connectivity check from -+ * an address that is not a known remote candidate */ -+ -+ NiceCandidate *local_candidate = NULL; -+ NiceCandidate *remote_candidate = NULL; -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -+ agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ /* We need to find which local candidate was used */ -+ uint8_t uname[NICE_STREAM_MAX_UNAME]; -+ guint uname_len; -+ -+ nice_debug ("Agent %p: We have a peer-reflexive candidate in a " -+ "stored pending check", agent); -+ -+ for (m = component->remote_candidates; -+ m != NULL && remote_candidate == NULL; m = m->next) { -+ for (n = component->local_candidates; n; n = n->next) { -+ NiceCandidate *rcand = m->data; -+ NiceCandidate *lcand = n->data; -+ -+ uname_len = priv_create_username (agent, stream, -+ component->id, rcand, lcand, -+ uname, sizeof (uname), TRUE); -+ -+ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", -+ icheck->username_len, uname_len, -+ icheck->username && uname_len == icheck->username_len && -+ memcmp (uname, icheck->username, icheck->username_len) == 0); -+ stun_debug_bytes (" first username: ", -+ icheck->username, -+ icheck->username? icheck->username_len : 0); -+ stun_debug_bytes (" second username: ", uname, uname_len); -+ -+ if (icheck->username && -+ uname_len == icheck->username_len && -+ memcmp (uname, icheck->username, icheck->username_len) == 0) { -+ local_candidate = lcand; -+ remote_candidate = rcand; - break; - } - } - } -- -- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -- local_candidate == NULL) { -- /* if we couldn't match the username, then the matching remote -- * candidate hasn't been received yet.. we must wait */ -- nice_debug ("Agent %p : Username check failed. pending check has " -- "to wait to be processed", agent); -- } else { -- NiceCandidate *candidate; -- -- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -- agent); -- candidate = -- discovery_learn_remote_peer_reflexive_candidate (agent, -- stream, -- component, -- icheck->priority, -- &icheck->from, -- icheck->local_socket, -- local_candidate, remote_candidate); -- if (candidate) { -- if (local_candidate && -- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -- priv_conn_check_add_for_candidate_pair_matched (agent, -- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -- else -- conn_check_add_for_candidate (agent, stream->id, component, candidate); -- -- if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); -+ } else { -+ for (l = component->local_candidates; l; l = l->next) { -+ NiceCandidate *cand = l->data; -+ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { -+ local_candidate = cand; -+ break; - } - } - } -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && -+ local_candidate == NULL) { -+ /* if we couldn't match the username, then the matching remote -+ * candidate hasn't been received yet.. we must wait */ -+ nice_debug ("Agent %p : Username check failed. pending check has " -+ "to wait to be processed", agent); -+ } else { -+ NiceCandidate *candidate; -+ -+ nice_debug ("Agent %p : Discovered peer reflexive from early i-check", -+ agent); -+ candidate = -+ discovery_learn_remote_peer_reflexive_candidate (agent, -+ stream, -+ component, -+ icheck->priority, -+ &icheck->from, -+ icheck->local_socket, -+ local_candidate, remote_candidate); -+ if (candidate) { -+ if (local_candidate && -+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -+ priv_conn_check_add_for_candidate_pair_matched (agent, -+ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); -+ else -+ conn_check_add_for_candidate (agent, stream->id, component, candidate); -+ -+ priv_schedule_triggered_check (agent, stream, component, -+ icheck->local_socket, candidate); -+ if (icheck->use_candidate) -+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); -+ } -+ } - } - } - } -@@ -1368,12 +1846,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice - /* Once we process the pending checks, we should free them to avoid - * reprocessing them again if a dribble-mode set_remote_candidates - * is called */ -- g_slist_free_full (component->incoming_checks, -- (GDestroyNotify) incoming_check_free); -- component->incoming_checks = NULL; -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); -+ for (o = stream->components; o; o = o->next) { -+ NiceComponent *component = o->data; -+ g_slist_free_full (component->incoming_checks, -+ (GDestroyNotify) incoming_check_free); -+ component->incoming_checks = NULL; -+ } - } - - /* -@@ -1381,7 +1859,7 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice - * in ICE spec section 5.7.3 (ID-19). See also - * conn_check_add_for_candidate(). - */ --static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) -+static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) - { - guint valid = 0; - guint cancelled = 0; -@@ -1389,22 +1867,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper - - while (item) { - CandidateCheckPair *pair = item->data; -+ GSList *next = item->next; - -- if (pair->state != NICE_CHECK_CANCELLED) { -- valid++; -- if (valid > upper_limit) { -- pair->state = NICE_CHECK_CANCELLED; -+ valid++; -+ if (valid > upper_limit) { -+ conn_check_free_item (pair); -+ conncheck_list = g_slist_delete_link (conncheck_list, item); - cancelled++; -- } - } -- -- item = item->next; -+ item = next; - } - - if (cancelled > 0) - nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements" - " left. Maximum connchecks allowed : %d", cancelled, valid, - upper_limit); -+ return conncheck_list; - } - - /* -@@ -1444,15 +1922,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp - * Updates the check list state. - * - * Implements parts of the algorithm described in -- * ICE sect 8.1.2. "Updating States" (ID-19): if for any -+ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any - * component, all checks have been completed and have -- * failed, mark that component's state to NICE_CHECK_FAILED. -+ * failed to produce a nominated pair, mark that component's -+ * state to NICE_CHECK_FAILED. - * - * Sends a component state changesignal via 'agent'. - */ - static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) - { - GSList *i; -+ gboolean completed; -+ guint nominated; - /* note: emitting a signal might cause the client - * to remove the stream, thus the component count - * must be fetched before entering the loop*/ -@@ -1476,6 +1957,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) - continue; - -+ nominated = 0; -+ completed = TRUE; - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -1483,16 +1966,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre - g_assert (p->stream_id == stream->id); - - if (p->component_id == (c + 1)) { -- if (p->state != NICE_CHECK_FAILED) -- break; -+ if (p->nominated) -+ ++nominated; -+ if (p->state != NICE_CHECK_FAILED && -+ p->state != NICE_CHECK_SUCCEEDED && -+ p->state != NICE_CHECK_DISCOVERED) -+ completed = FALSE; - } - } - -- /* note: all checks have failed -+ /* note: all pairs are either failed or succeeded, and the component -+ * has not produced a nominated pair. - * Set the component to FAILED only if it actually had remote candidates - * that failed.. */ -- if (i == NULL && comp != NULL && comp->remote_candidates != NULL) -- agent_signal_component_state_change (agent, -+ if (completed && nominated == 0 && -+ comp != NULL && comp->remote_candidates != NULL) -+ agent_signal_component_state_change (agent, - stream->id, - (c + 1), /* component-id */ - NICE_COMPONENT_STATE_FAILED); -@@ -1525,7 +2014,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream - ++valid; - if (p->nominated == TRUE) { - ++nominated; -- priv_update_selected_pair (agent, component, p); - } - } - } -@@ -1535,7 +2023,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream - /* Only go to READY if no checks are left in progress. If there are - * any that are kept, then this function will be called again when the - * conncheck tick timer finishes them all */ -- if (priv_prune_pending_checks (stream, component->id) == 0) { -+ if (priv_prune_pending_checks (agent, stream, component->id) == 0) { - /* Continue through the states to give client code a nice - * logical progression. See
http://phabricator.freedesktop.org/D218
for - * discussion. */ -@@ -1564,24 +2052,59 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice - - g_assert (component); - -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && -+ agent->controlling_mode) -+ return; -+ - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; - if (pair->local == localcand && pair->remote == remotecand) { -- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); -- pair->nominated = TRUE; -+ /* ICE, 7.2.1.5. Updating the Nominated Flag */ -+ /* note: TCP candidates typically produce peer reflexive -+ * candidate, generating a "discovered" pair that can be -+ * nominated. -+ */ -+ if (pair->state == NICE_CHECK_SUCCEEDED && -+ pair->discovered_pair != NULL) { -+ pair = pair->discovered_pair; -+ g_assert (pair->state == NICE_CHECK_DISCOVERED); -+ } -+ -+ /* If the state of this pair is In-Progress, [...] the resulting -+ * valid pair has its nominated flag set when the response -+ * arrives. -+ */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && -+ pair->state == NICE_CHECK_IN_PROGRESS) { -+ pair->mark_nominated_on_response_arrival = TRUE; -+ nice_debug ("Agent %p : pair %p (%s) is in-progress, " -+ "will be nominated on response receipt.", -+ agent, pair, pair->foundation); -+ } -+ -+ if (pair->valid || -+ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated", -+ agent, pair, pair->foundation); -+ pair->nominated = TRUE; -+ } -+ - if (pair->valid) { -- priv_update_selected_pair (agent, component, pair); - /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -+ if (component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); -+ priv_update_selected_pair (agent, component, pair); -+ if (component->state == NICE_COMPONENT_STATE_CONNECTING) - /* step: notify the client of a new component state (must be done - * before the possible check list state update step */ - agent_signal_component_state_change (agent, - stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -- - } -- priv_update_check_list_state_for_ready (agent, stream, component); -+ -+ if (pair->nominated) -+ priv_update_check_list_state_for_ready (agent, stream, component); - } - } - } -@@ -1624,7 +2147,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority) - */ - static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - guint stream_id, NiceComponent *component, NiceCandidate *local, -- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) -+ NiceCandidate *remote, NiceCheckState initial_state) - { - NiceStream *stream; - CandidateCheckPair *pair; -@@ -1662,8 +2185,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr)); - } -- pair->nominated = use_candidate; -- pair->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local)); - -@@ -1675,7 +2196,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, - /* implement the hard upper limit for number of - checks (see sect 5.7.3 ICE ID-19): */ - if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { -- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); -+ stream->conncheck_list = priv_limit_conn_check_list_size ( -+ stream->conncheck_list, agent->max_conn_checks); - } - - return pair; -@@ -1709,7 +2231,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( - agent, local->foundation, remote->foundation, - stream_id, component->id); - pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, -- initial_state, FALSE); -+ initial_state); - if (component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY) { - agent_signal_component_state_change (agent, -@@ -1781,6 +2303,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone - - g_assert (remote != NULL); - -+ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates", -+ * the agent does not pair this candidate with any local candidates. -+ */ -+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && -+ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) -+ { -+ return added; -+ } -+ - for (i = component->local_candidates; i ; i = i->next) { - NiceCandidate *local = i->data; - -@@ -1815,6 +2346,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC - - g_assert (local != NULL); - -+ /* -+ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive -+ * Candidates", the peer reflexive candidate is not paired -+ * with other remote candidates -+ */ -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && -+ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) -+ { -+ return added; -+ } -+ - for (i = component->remote_candidates; i ; i = i->next) { - - NiceCandidate *remote = i->data; -@@ -1838,8 +2381,7 @@ static void conn_check_free_item (gpointer data) - - if (pair->agent) - priv_remove_pair_from_triggered_check_queue (pair->agent, pair); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -+ priv_free_all_stun_transactions (pair, NULL); - g_slice_free (CandidateCheckPair, pair); - } - -@@ -2057,30 +2599,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - - /* Implement the computation specific in RFC 5245 section 16 */ - --static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) -+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream) - { -- GSList *item1, *item2; -+ GSList *i; - guint waiting_and_in_progress = 0; -+ guint n = 0; - unsigned int rto = 0; - -- -- for (item1 = agent->streams; item1; item1 = item1->next) { -- NiceStream *stream = item1->data;; -- for (item2 = stream->conncheck_list; item2; item2 = item2->next) { -- CandidateCheckPair *pair = item2->data; -- -- if (pair->state == NICE_CHECK_IN_PROGRESS || -- pair->state == NICE_CHECK_WAITING) -- waiting_and_in_progress++; -- } -+ for (i = stream->conncheck_list; i ; i = i->next) { -+ CandidateCheckPair *p = i->data; -+ if (p->state == NICE_CHECK_IN_PROGRESS || -+ p->state == NICE_CHECK_WAITING) -+ waiting_and_in_progress++; - } - -- rto = agent->timer_ta * waiting_and_in_progress; -+ n = priv_number_of_active_check_lists (agent); -+ rto = agent->timer_ta * n * waiting_and_in_progress; - - /* We assume non-reliable streams are RTP, so we use 100 as the max */ -- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", -+ nice_debug ("Agent %p : timer set to %dms, " -+ "waiting+in_progress=%d, nb_active=%d", - agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), -- waiting_and_in_progress); -+ waiting_and_in_progress, n); - if (agent->reliable) - return MAX (rto, 500); - else -@@ -2114,6 +2654,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - bool cand_use = controlling; - size_t buffer_len; - unsigned int timeout; -+ StunTransaction *stun; - - if (!agent_find_component (agent, pair->stream_id, pair->component_id, - &stream, &component)) -@@ -2135,99 +2676,119 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - nice_address_to_string (&pair->local->addr, tmpbuf1); - nice_address_to_string (&pair->remote->addr, tmpbuf2); - nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " -- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, -+ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -+ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, - tmpbuf1, nice_address_get_port (&pair->local->addr), - tmpbuf2, nice_address_get_port (&pair->remote->addr), - pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, -- pair->foundation, pair->component_id, -+ pair, pair->component_id, - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -- pair->prflx_priority, controlling); -+ pair->prflx_priority, -+ controlling ? "controlling" : "controlled"); - } - -- if (cand_use) -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ /* We are doing regular nomination, so we set the use-candidate -+ * attrib, when the controlling agent decided which valid pair to -+ * resend with this flag in priv_conn_check_tick_stream() -+ */ -+ cand_use = pair->use_candidate_on_next_check; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(regular nomination).", agent, G_STRFUNC, cand_use); -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ /* We are doing aggressive nomination, we set the use-candidate -+ * attrib in every check we send, when we are the controlling -+ * agent, RFC 5245, 8.1.1.2 -+ */ -+ cand_use = controlling; -+ nice_debug ("Agent %p : %s: set cand_use=%d " -+ "(aggressive nomination).", agent, G_STRFUNC, cand_use); -+ break; -+ default: -+ /* Nothing to do. */ -+ break; -+ } -+ } else if (cand_use) - pair->nominated = controlling; - -- if (uname_len > 0) { -- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), -- uname, uname_len, password, password_len, -- cand_use, controlling, pair->prflx_priority, -- agent->tie_breaker, -- pair->local->foundation, -- agent_to_ice_compatibility (agent)); -+ if (uname_len == 0) { -+ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -+ return -1; -+ } - -- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -- pair->stun_message.buffer); -+ stun = priv_add_stun_transaction (pair); - -- if (agent->compatibility == NICE_COMPATIBILITY_MSN || -- agent->compatibility == NICE_COMPATIBILITY_OC2007) { -- g_free (password); -- } -+ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, -+ &stun->message, stun->buffer, sizeof(stun->buffer), -+ uname, uname_len, password, password_len, -+ cand_use, controlling, pair->prflx_priority, -+ agent->tie_breaker, -+ pair->local->foundation, -+ agent_to_ice_compatibility (agent)); - -- if (buffer_len > 0) { -- if (nice_socket_is_reliable(pair->sockptr)) { -- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -- } else { -- stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -- } -+ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, -+ stun->message.buffer); - -- /* TCP-ACTIVE candidate must create a new socket before sending -- * by connecting to the peer. The new socket is stored in the candidate -- * check pair, until we discover a new local peer reflexive */ -- if (pair->sockptr->fileno == NULL && -- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && -- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -- NiceStream *stream2 = NULL; -- NiceComponent *component2 = NULL; -- NiceSocket *new_socket; -- -- if (agent_find_component (agent, pair->stream_id, pair->component_id, -- &stream2, &component2)) { -- new_socket = nice_tcp_active_socket_connect (pair->sockptr, -- &pair->remote->addr); -- if (new_socket) { -- pair->sockptr = new_socket; -- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -- -- if (agent->reliable) { -- nice_socket_set_writable_callback (pair->sockptr, -- _tcp_sock_is_writable, component2); -- } -+ if (agent->compatibility == NICE_COMPATIBILITY_MSN || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007) { -+ g_free (password); -+ } - -- nice_component_attach_socket (component2, new_socket); -- } -- } -- } -- /* send the conncheck */ -- agent_socket_send (pair->sockptr, &pair->remote->addr, -- buffer_len, (gchar *)pair->stun_buffer); -+ if (buffer_len == 0) { -+ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -+ priv_remove_stun_transaction (pair, stun, component); -+ return -1; -+ } - -- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { -- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, -- &pair->remote->addr); -- } -+ if (nice_socket_is_reliable(pair->sockptr)) { -+ timeout = agent->stun_reliable_timeout; -+ stun_timer_start_reliable(&stun->timer, timeout); -+ } else { -+ timeout = priv_compute_conncheck_timer (agent, stream); -+ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); -+ } - -- timeout = stun_timer_remainder (&pair->timer); -- /* note: convert from milli to microseconds for g_time_val_add() */ -- g_get_current_time (&pair->next_tick); -- g_time_val_add (&pair->next_tick, timeout * 1000); -- } else { -- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -- return -1; -+ g_get_current_time (&stun->next_tick); -+ g_time_val_add (&stun->next_tick, timeout * 1000); -+ -+ /* TCP-ACTIVE candidate must create a new socket before sending -+ * by connecting to the peer. The new socket is stored in the candidate -+ * check pair, until we discover a new local peer reflexive */ -+ if (pair->sockptr->fileno == NULL && -+ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && -+ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -+ NiceStream *stream2 = NULL; -+ NiceComponent *component2 = NULL; -+ NiceSocket *new_socket; -+ -+ if (agent_find_component (agent, pair->stream_id, pair->component_id, -+ &stream2, &component2)) { -+ new_socket = nice_tcp_active_socket_connect (pair->sockptr, -+ &pair->remote->addr); -+ if (new_socket) { -+ pair->sockptr = new_socket; -+ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -+ -+ if (agent->reliable) -+ nice_socket_set_writable_callback (pair->sockptr, -+ _tcp_sock_is_writable, component2); -+ -+ nice_component_attach_socket (component2, new_socket); -+ } - } -- } else { -- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); -- pair->stun_message.buffer = NULL; -- pair->stun_message.buffer_len = 0; -- return -1; - } -+ /* send the conncheck */ -+ agent_socket_send (pair->sockptr, &pair->remote->addr, -+ buffer_len, (gchar *)stun->buffer); -+ -+ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) -+ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, -+ &pair->remote->addr); - - return 0; - } -@@ -2238,14 +2799,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - * @see priv_update_check_list_state_failed_components() - */ --static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) -+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id) - { - GSList *i; - guint64 highest_nominated_priority = 0; - guint in_progress = 0; - -- nice_debug ("Agent XXX: Finding highest priority for component %d", -- component_id); -+ nice_debug ("Agent %p: Finding highest priority for component %d", -+ agent, component_id); - - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -@@ -2257,37 +2818,40 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - } - } - -- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority " -- "is %" G_GUINT64_FORMAT, highest_nominated_priority); -+ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority " -+ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); - - /* step: cancel all FROZEN and WAITING pairs for the component */ -- for (i = stream->conncheck_list; i; i = i->next) { -+ i = stream->conncheck_list; -+ while (i) { - CandidateCheckPair *p = i->data; -+ GSList *next = i->next; -+ - if (p->component_id == component_id) { -- if (p->state == NICE_CHECK_FROZEN || -- p->state == NICE_CHECK_WAITING) { -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) { -+ nice_debug ("Agent %p : pair %p removed.", agent, p); -+ conn_check_free_item (p); -+ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); - } - - /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ -- if (p->state == NICE_CHECK_IN_PROGRESS) { -- if (highest_nominated_priority != 0 && -- p->priority < highest_nominated_priority) { -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_CANCELLED; -- nice_debug ("Agent XXX : pair %p state CANCELED", p); -+ else if (p->state == NICE_CHECK_IN_PROGRESS) { -+ if (p->priority < highest_nominated_priority) { -+ p->retransmit = FALSE; -+ nice_debug ("Agent %p : pair %p will not be retransmitted.", -+ agent, p); - } else { - /* We must keep the higher priority pairs running because if a udp - * packet was lost, we might end up using a bad candidate */ -- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %" -+ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %" - G_GUINT64_FORMAT " is higher than currently nominated pair %" -- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority); -+ G_GUINT64_FORMAT, agent, -+ p, p->priority, highest_nominated_priority); - in_progress++; - } - } - } -+ i = next; - } - - return in_progress; -@@ -2301,17 +2865,17 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - * @param component the check is related to - * @param local_socket socket from which the inbound check was received - * @param remote_cand remote candidate from which the inbound check was sent -- * @param use_candidate whether the original check had USE-CANDIDATE attribute set - */ --static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand) - { - GSList *i; - NiceCandidate *local = NULL; -+ CandidateCheckPair *p; - - g_assert (remote_cand != NULL); - - for (i = stream->conncheck_list; i ; i = i->next) { -- CandidateCheckPair *p = i->data; -+ p = i->data; - if (p->component_id == component->id && - p->remote == remote_cand && - ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && -@@ -2322,61 +2886,67 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - * tcp-active we don't want to retrigger a check on a pair that - * was FAILED when a peer-reflexive pair was created */ - -- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); -+ if (p->succeeded_pair != NULL) { -+ g_assert (p->state == NICE_CHECK_DISCOVERED); -+ p = p->succeeded_pair; -+ } -+ -+ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", -+ agent, p, p->foundation, priv_state_to_string (p->state)); - -- if (p->state == NICE_CHECK_WAITING || -- p->state == NICE_CHECK_FROZEN) -- priv_add_pair_to_triggered_check_queue (agent, p); -- else if (p->state == NICE_CHECK_IN_PROGRESS) { -- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), -- * we should cancel the existing one, instead we reset our timer, so -- * we'll resend the exiting transactions faster if needed...? :P -- */ -- nice_debug ("Agent %p : check already in progress, " -- "restarting the timer again?: %s ..", agent, -- p->timer_restarted ? "no" : "yes"); -- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { -- stun_timer_start (&p->timer, -- priv_compute_conncheck_timer (agent), -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -- p->timer_restarted = TRUE; -- } -- } -- else if (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) { -- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); -- /* note: this is a bit unsure corner-case -- let's do the -- same state update as for processing responses to our own checks */ -- /* note: this update is required by the dribble test, to -- * ensure the transition ready -> connected -> ready, because -- * an incoming stun request generates a discovered peer reflexive, -- * that causes the ready -> connected transition. -- */ -- priv_update_check_list_state_for_ready (agent, stream, component); -- -- /* note: this new check is required by the new-dribble test, -- * when early icheck on the peer controlled agent causes an -- * incoming stun request to an already succeeded (and -- * nominated) pair on the controlling agent. If the -- * controlling agent doesn't retrigger a check with -- * USE-CANDIDATE=1, the peer agent has no way to nominate it. -- * -- * This behavior differs from ICE spec 7.2.1.4 -- */ -- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || -- agent->compatibility == NICE_COMPATIBILITY_WLM2009 || -- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -- agent->controlling_mode) { -+ switch (p->state) { -+ case NICE_CHECK_WAITING: -+ case NICE_CHECK_FROZEN: -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); - priv_add_pair_to_triggered_check_queue (agent, p); -- conn_check_schedule_next(agent); -- } -- } else if (p->state == NICE_CHECK_FAILED) { -- /* 7.2.1.4 Triggered Checks -- * If the state of the pair is Failed, it is changed to Waiting -- and the agent MUST create a new connectivity check for that -- pair (representing a new STUN Binding request transaction), by -- enqueueing the pair in the triggered check queue. */ -- priv_add_pair_to_triggered_check_queue (agent, p); -+ break; -+ case NICE_CHECK_IN_PROGRESS: -+ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" -+ * we cancel the in-progress transaction, and after the -+ * retransmission timeout, we create a new connectivity check -+ * for that pair. The controlling role of this new check may -+ * be different from the role of this cancelled check. -+ * -+ * note: the flag retransmit unset means that -+ * another pair, with a higher priority is already nominated, -+ * so there's no reason to recheck this pair, since it can in -+ * no way replace the nominated one. -+ */ -+ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ } -+ break; -+ case NICE_CHECK_FAILED: -+ if (p->retransmit) { -+ nice_debug ("Agent %p : pair %p added for a triggered check.", -+ agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ /* If the component for this pair is in failed state, move it -+ * back to connecting, and reinitiate the timers -+ */ -+ if (component->state == NICE_COMPONENT_STATE_FAILED) { -+ agent_signal_component_state_change (agent, stream->id, -+ component->id, NICE_COMPONENT_STATE_CONNECTING); -+ conn_check_schedule_next (agent); -+ } -+ } -+ break; -+ case NICE_CHECK_SUCCEEDED: -+ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); -+ /* note: this is a bit unsure corner-case -- let's do the -+ same state update as for processing responses to our own checks */ -+ /* note: this update is required by the dribble test, to -+ * ensure the transition ready -> connected -> ready, because -+ * an incoming stun request generates a discovered peer reflexive, -+ * that causes the ready -> connected transition. -+ */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ break; -+ default: -+ break; - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -2394,7 +2964,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str - - if (i) { - nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); -- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate); -+ p = priv_add_new_check_pair (agent, stream->id, component, -+ local, remote_cand, NICE_CHECK_WAITING); -+ priv_add_pair_to_triggered_check_queue (agent, p); - return TRUE; - } - else { -@@ -2447,9 +3019,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, - } - - if (rcand) { -- /* note: upon successful check, make the reserve check immediately */ -- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); -- -+ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); - if (use_candidate) - priv_mark_pair_nominated (agent, stream, component, lcand, rcand); - } -@@ -2509,6 +3079,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->remote = parent_pair->remote; - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; -+ parent_pair->discovered_pair = pair; -+ pair->succeeded_pair = parent_pair; - nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); - { - gchar tmpbuf1[INET6_ADDRSTRLEN]; -@@ -2528,7 +3100,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->priority = nice_candidate_pair_priority (pair->remote->priority, - pair->local->priority); - pair->nominated = FALSE; -- pair->controlling = agent->controlling_mode; - pair->prflx_priority = ensure_unique_priority (component, - peer_reflexive_candidate_priority (agent, local_cand)); - nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); -@@ -2567,14 +3138,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) - { - /* role conflict, change mode; wait for a new conn. check */ - if (control != agent->controlling_mode) { -- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control); -+ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".", -+ agent, control ? "controlling" : "controlled"); - agent->controlling_mode = control; - /* the pair priorities depend on the roles, so recalculation - * is needed */ - priv_recalculate_pair_priorities (agent); - } - else -- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control); -+ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", -+ agent, control ? "controlling" : "controlled"); - } - - /* -@@ -2621,13 +3194,25 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - } - - if (new_pair) { -+ /* note: when new_pair is distinct from p, it means new_pair is a -+ * previously discovered peer-reflexive candidate pair, so we don't -+ * set the valid flag on p in this case, because the valid flag is -+ * already set on the discovered pair. -+ */ -+ if (new_pair == p) -+ p->valid = TRUE; - p->state = NICE_CHECK_SUCCEEDED; -+ priv_remove_pair_from_triggered_check_queue (agent, p); -+ priv_free_all_stun_transactions (p, component); - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); -- priv_conn_check_unfreeze_related (agent, stream, p); -+ nice_component_add_valid_candidate (component, remote_candidate); - } - else { - if (!local_cand) { -- if (!agent->force_relay) -+ if (!agent->force_relay) { -+ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1. -+ * "Discovering Peer Reflexive Candidates" -+ */ - local_cand = discovery_add_peer_reflexive_candidate (agent, - stream->id, - component->id, -@@ -2635,8 +3220,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - sockptr, - local_candidate, - remote_candidate); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p", -+ agent, local_cand); -+ } - } - - /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 -@@ -2644,13 +3230,23 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * - if (local_cand) - new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, - local_cand, p); -- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); -+ /* note: this is same as "adding to VALID LIST" in the spec -+ text */ -+ if (new_pair) -+ new_pair->valid = TRUE; -+ /* step: The agent sets the state of the pair that *generated* the check to -+ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" -+ */ -+ p->state = NICE_CHECK_SUCCEEDED; -+ priv_remove_pair_from_triggered_check_queue (agent, p); -+ priv_free_all_stun_transactions (p, component); -+ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", -+ agent, p, new_pair); - } - -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -- if (new_pair) -- new_pair->valid = TRUE; -+ if (new_pair && new_pair->valid) -+ nice_component_add_valid_candidate (component, remote_candidate); -+ - - return new_pair; - } -@@ -2669,134 +3265,199 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre - struct sockaddr addr; - } sockaddr; - socklen_t socklen = sizeof (sockaddr); -- GSList *i; -+ GSList *i, *j; -+ guint k; - StunUsageIceReturn res; -- gboolean trans_found = FALSE; - StunTransactionId discovery_id; - StunTransactionId response_id; - stun_message_id (resp, response_id); - -- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) { -+ for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - -- if (p->stun_message.buffer) { -- stun_message_id (&p->stun_message, discovery_id); -- -- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) { -- res = stun_usage_ice_conncheck_process (resp, -- &sockaddr.storage, &socklen, -- agent_to_ice_compatibility (agent)); -- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " -- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); -- -- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* case: found a matching connectivity check request */ -- -- CandidateCheckPair *ok_pair = NULL; -- -- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- -- /* step: verify that response came from the same IP address we -- * sent the original request to (see 7.1.2.1. "Failure -- * Cases") */ -- if (nice_address_equal (from, &p->remote->addr) != TRUE) { -- -- p->state = NICE_CHECK_FAILED; -- if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- gchar tmpbuf2[INET6_ADDRSTRLEN]; -- nice_debug ("Agent %p : conncheck %p FAILED" -- " (mismatch of source address).", agent, p); -- nice_address_to_string (&p->remote->addr, tmpbuf); -- nice_address_to_string (from, tmpbuf2); -- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -- tmpbuf, nice_address_get_port (&p->remote->addr), -- tmpbuf2, nice_address_get_port (from)); -- } -- trans_found = TRUE; -- break; -- } -- -- /* note: CONNECTED but not yet READY, see docs */ -- -- /* step: handle the possible case of a peer-reflexive -- * candidate where the mapped-address in response does -- * not match any local candidate, see 7.1.2.2.1 -- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -- -- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -- p->state = NICE_CHECK_SUCCEEDED; -- p->valid = TRUE; -- g_assert_not_reached (); -- nice_debug ("Agent %p : Mapped address not found." -- " conncheck %p SUCCEEDED.", agent, p); -- priv_conn_check_unfreeze_related (agent, stream, p); -- } else { -- ok_pair = priv_process_response_check_for_reflexive (agent, -- stream, component, p, sockptr, &sockaddr.addr, -- local_candidate, remote_candidate); -- } -- -- -- if (!ok_pair) -- ok_pair = p; -- -- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -- Nominated Flag" (ID-19) */ -- if (ok_pair->nominated == TRUE) { -- priv_update_selected_pair (agent, component, ok_pair); -- priv_print_conn_check_lists (agent, G_STRFUNC, -- ", got a nominated pair"); -+ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) { -+ StunTransaction *stun = j->data; -+ -+ stun_message_id (&stun->message, discovery_id); -+ -+ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) -+ continue; -+ -+ res = stun_usage_ice_conncheck_process (resp, -+ &sockaddr.storage, &socklen, -+ agent_to_ice_compatibility (agent)); -+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " -+ "%s,res=%s,stun#=%d.", -+ agent, p, -+ agent->controlling_mode ? "controlling" : "controlled", -+ priv_ice_return_to_string (res), k); -+ -+ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || -+ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ /* case: found a matching connectivity check request */ -+ -+ CandidateCheckPair *ok_pair = NULL; -+ -+ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); -+ priv_remove_stun_transaction (p, stun, component); -+ -+ /* step: verify that response came from the same IP address we -+ * sent the original request to (see 7.1.2.1. "Failure -+ * Cases") */ -+ if (nice_address_equal (from, &p->remote->addr) == FALSE) { -+ candidate_check_pair_fail (stream, agent, p); -+ if (nice_debug_is_enabled ()) { -+ gchar tmpbuf[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_debug ("Agent %p : conncheck %p FAILED" -+ " (mismatch of source address).", agent, p); -+ nice_address_to_string (&p->remote->addr, tmpbuf); -+ nice_address_to_string (from, tmpbuf2); -+ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, -+ tmpbuf, nice_address_get_port (&p->remote->addr), -+ tmpbuf2, nice_address_get_port (from)); -+ } -+ return TRUE; -+ } - -- /* Do not step down to CONNECTED if we're already at state READY*/ -- if (component->state != NICE_COMPONENT_STATE_READY) { -- /* step: notify the client of a new component state (must be done -- * before the possible check list state update step */ -- agent_signal_component_state_change (agent, -- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -- } -- } -+ /* note: CONNECTED but not yet READY, see docs */ -+ -+ /* step: handle the possible case of a peer-reflexive -+ * candidate where the mapped-address in response does -+ * not match any local candidate, see 7.1.2.2.1 -+ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ -+ -+ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { -+ p->state = NICE_CHECK_SUCCEEDED; -+ p->valid = TRUE; -+ nice_debug ("Agent %p : Mapped address not found." -+ " conncheck %p SUCCEEDED.", agent, p); -+ nice_component_add_valid_candidate (component, p->remote); -+ } else -+ ok_pair = priv_process_response_check_for_reflexive (agent, -+ stream, component, p, sockptr, &sockaddr.addr, -+ local_candidate, remote_candidate); -+ -+ /* note: The success of this check might also -+ * cause the state of other checks to change as well, ICE -+ * spec 7.1.3.2.3 -+ */ -+ priv_conn_check_unfreeze_related (agent, stream, p); -+ -+ /* Note: this assignment helps to reduce the numbers of cases -+ * to be tested. If ok_pair and p refer to distinct pairs, it -+ * means that ok_pair is a discovered peer reflexive one, -+ * caused by the check made on pair p. In that case, the -+ * flags to be tested are on p, but the nominated flag will be -+ * set on ok_pair. When there's no discovered pair, p and -+ * ok_pair refer to the same pair. -+ * To summarize : p is a SUCCEEDED pair, ok_pair is a -+ * DISCOVERED, VALID, and eventually NOMINATED pair. -+ */ -+ if (!ok_pair) -+ ok_pair = p; -+ -+ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the -+ Nominated Flag" (ID-19) */ -+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { -+ nice_debug ("Agent %p : Updating nominated flag (%s): " -+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", -+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? -+ "UDP" : "TCP", -+ ok_pair, ok_pair->use_candidate_on_next_check, -+ ok_pair->mark_nominated_on_response_arrival, -+ p, p->use_candidate_on_next_check, -+ p->mark_nominated_on_response_arrival); -+ -+ if (agent->controlling_mode) { -+ switch (agent->nomination_mode) { -+ case NICE_NOMINATION_MODE_REGULAR: -+ if (p->use_candidate_on_next_check) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(regular nomination, controlling, " -+ "use_cand_on_next_check=1).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ case NICE_NOMINATION_MODE_AGGRESSIVE: -+ if (!p->nominated) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(aggressive nomination, controlling).", -+ agent, ok_pair, ok_pair->foundation); -+ ok_pair->nominated = TRUE; -+ } -+ break; -+ default: -+ /* Nothing to do */ -+ break; -+ } -+ } else { -+ if (p->mark_nominated_on_response_arrival) { -+ nice_debug ("Agent %p : marking pair %p (%s) as nominated " -+ "(%s nomination, controlled, mark_on_response=1).", -+ agent, ok_pair, ok_pair->foundation, -+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? -+ "aggressive" : "regular"); -+ ok_pair->nominated = TRUE; -+ } -+ } -+ } - -- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -- states" and 8.1.2 "Updating States", ID-19) */ -- priv_update_check_list_state_for_ready (agent, stream, component); -+ if (ok_pair->nominated == TRUE) { -+ priv_update_selected_pair (agent, component, ok_pair); -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a nominated pair"); -+ -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } - -- trans_found = TRUE; -- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -- /* case: role conflict error, need to restart with new role */ -- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -- /* note: our role might already have changed due to an -- * incoming request, but if not, change role now; -- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ -- priv_check_for_role_conflict (agent, !p->controlling); -- -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- p->state = NICE_CHECK_WAITING; -- priv_add_pair_to_triggered_check_queue (agent, p); -- nice_debug ("Agent %p : pair %p state WAITING", agent, p); -- trans_found = TRUE; -- } else { -- /* case: STUN error, the check STUN context was freed */ -- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -- p->stun_message.buffer = NULL; -- p->stun_message.buffer_len = 0; -- trans_found = TRUE; -- } -+ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair -+ states" and 8.1.2 "Updating States", ID-19) */ -+ priv_update_check_list_state_for_ready (agent, stream, component); -+ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { -+ guint64 tie; -+ gboolean controlled_mode; -+ -+ /* case: role conflict error, need to restart with new role */ -+ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); -+ -+ /* note: this res value indicates that the role of the peer -+ * agent has not changed after the tie-breaker comparison, so -+ * this is our role that must change. see ICE sect. 7.1.3.1 -+ * "Failure Cases". Our role might already have changed due to -+ * an earlier incoming request, but if not, change role now. -+ * -+ * Sect. 7.1.3.1 is not clear on this point, but we choose to -+ * put the candidate pair in the triggered check list even -+ * when the agent did not switch its role. The reason for this -+ * interpretation is that the reception of the stun reply, even -+ * an error reply, is a good sign that this pair will be -+ * valid, if we retry the check after the role of both peers -+ * has been fixed. -+ */ -+ controlled_mode = (stun_message_find64 (&stun->message, -+ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == -+ STUN_MESSAGE_RETURN_SUCCESS); -+ -+ priv_check_for_role_conflict (agent, controlled_mode); -+ priv_remove_stun_transaction (p, stun, component); -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ } else { -+ /* case: STUN error, the check STUN context was freed */ -+ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); -+ candidate_check_pair_fail (stream, agent, p); - } -+ return TRUE; - } - } - -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); -- -- return trans_found; -+ return FALSE; - } - - /* -@@ -2846,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa - d->server = niceaddr; - - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { - /* case: successful binding discovery, create a new local candidate */ - -@@ -2930,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand - return cand; - } - -+static void priv_handle_turn_alternate_server (NiceAgent *agent, -+ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate) -+{ -+ /* We need to cancel and reset all candidate discovery turn for the same -+ stream and type if there is an alternate server. Otherwise, we might end up -+ with two relay components on different servers, creating candidates with -+ unique foundations that only contain one component. -+ */ -+ GSList *i; -+ -+ for (i = agent->discovery_list; i; i = i->next) { -+ CandidateDiscovery *d = i->data; -+ -+ if (!d->done && -+ d->type == disco->type && -+ d->stream == disco->stream && -+ d->turn->type == disco->turn->type && -+ nice_address_equal (&d->server, &server)) { -+ gchar ip[INET6_ADDRSTRLEN]; -+ // Cancel the pending request to avoid a race condition with another -+ // component responding with another altenrate-server -+ d->stun_message.buffer = NULL; -+ d->stun_message.buffer_len = 0; -+ -+ nice_address_to_string (&server, ip); -+ nice_debug ("Agent %p : Cancelling and setting alternate server %s for " -+ "CandidateDiscovery %p", agent, ip, d); -+ d->server = alternate; -+ d->turn->server = alternate; -+ d->pending = FALSE; -+ agent->discovery_unsched_items++; -+ } -+ } -+} -+ - /* - * Tries to match STUN reply in 'buf' to an existing STUN discovery - * transaction. If found, a reply is sent. -@@ -2982,11 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - agent, d, (int)res); - - if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) { -- /* handle alternate server */ -- nice_address_set_from_sockaddr (&d->server, &alternate.addr); -- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); -+ NiceAddress addr; - -- d->pending = FALSE; -+ /* handle alternate server */ -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); - } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || - res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { - /* case: successful allocate, create a new local candidate */ -@@ -3103,6 +3800,17 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - recv_realm = (uint8_t *) stun_message_find (resp, - STUN_ATTRIBUTE_REALM, &recv_realm_len); - -+ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || -+ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -+ alternatelen != sizeof(alternate)) { -+ NiceAddress addr; -+ -+ nice_address_set_from_sockaddr (&addr, &alternate.addr); -+ -+ if (!nice_address_equal (&addr, &d->server)) { -+ priv_handle_turn_alternate_server (agent, d, d->server, addr); -+ } -+ } - /* check for unauthorized error response */ - if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || - agent->compatibility == NICE_COMPATIBILITY_OC2007 || -@@ -3123,6 +3831,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - d->stun_resp_msg.buffer = d->stun_resp_buffer; - d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer); - d->pending = FALSE; -+ agent->discovery_unsched_items++; - } else { - /* case: a real unauthorized error */ - d->stun_message.buffer = NULL; -@@ -3619,8 +4328,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - stun_usage_ice_conncheck_use_candidate (&req); - uint32_t priority = stun_usage_ice_conncheck_priority (&req); - -- if (agent->controlling_mode || -- agent->compatibility == NICE_COMPATIBILITY_GOOGLE || -+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || - agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007) - use_candidate = TRUE; -@@ -3628,21 +4336,21 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - if (stream->initial_binding_request_received != TRUE) - agent_signal_initial_binding_request_received (agent, stream); - -- if (component->remote_candidates && remote_candidate == NULL) { -+ if (remote_candidate == NULL) { - nice_debug ("Agent %p : No matching remote candidate for incoming check ->" - "peer-reflexive candidate.", agent); - remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( - agent, stream, component, priority, from, nicesock, - local_candidate, - remote_candidate2 ? remote_candidate2 : remote_candidate); -- if(remote_candidate) { -+ if(remote_candidate && stream->remote_ufrag[0]) { - if (local_candidate && - local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { - CandidateCheckPair *pair; - - pair = priv_conn_check_add_for_candidate_pair_matched (agent, - stream->id, component, local_candidate, remote_candidate, -- NICE_CHECK_DISCOVERED); -+ NICE_CHECK_SUCCEEDED); - if (pair) { - pair->valid = TRUE; - } -@@ -3651,13 +4359,15 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, - } - } - -+ nice_component_add_valid_candidate (component, remote_candidate); -+ - priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); - -- if (component->remote_candidates == NULL) { -+ if (stream->remote_ufrag[0] == 0) { - /* case: We've got a valid binding request to a local candidate -- * but we do not yet know remote credentials nor -- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply -+ * but we do not yet know remote credentials. -+ * As per sect 7.2 of ICE (ID-19), we send a reply - * immediately but postpone all other processing until - * we get information about the remote candidates */ - -diff --git a/agent/conncheck.h b/agent/conncheck.h -index 431c606..e16dc67 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -56,7 +56,6 @@ - * @NICE_CHECK_SUCCEEDED: Connection successfully checked. - * @NICE_CHECK_FAILED: No connectivity; retransmissions ceased. - * @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING. -- * @NICE_CHECK_CANCELLED: Check cancelled. - * @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list. - * - * States for checking a candidate pair. -@@ -68,11 +67,19 @@ typedef enum - NICE_CHECK_SUCCEEDED, - NICE_CHECK_FAILED, - NICE_CHECK_FROZEN, -- NICE_CHECK_CANCELLED, - NICE_CHECK_DISCOVERED, - } NiceCheckState; - - typedef struct _CandidateCheckPair CandidateCheckPair; -+typedef struct _StunTransaction StunTransaction; -+ -+struct _StunTransaction -+{ -+ GTimeVal next_tick; /* next tick timestamp */ -+ StunTimer timer; -+ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; -+ StunMessage message; -+}; - - struct _CandidateCheckPair - { -@@ -85,15 +92,15 @@ struct _CandidateCheckPair - gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; - NiceCheckState state; - gboolean nominated; -- gboolean controlling; -- gboolean timer_restarted; - gboolean valid; -+ gboolean use_candidate_on_next_check; -+ gboolean mark_nominated_on_response_arrival; -+ gboolean retransmit; /* if the first stun request must be retransmitted */ -+ CandidateCheckPair *discovered_pair; -+ CandidateCheckPair *succeeded_pair; - guint64 priority; - guint32 prflx_priority; -- GTimeVal next_tick; /* next tick timestamp */ -- StunTimer timer; -- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; -- StunMessage stun_message; -+ GSList *stun_transactions; /* a list of ongoing stun requests */ - }; - - int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); -@@ -105,7 +112,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); - void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); - gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); - gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); --void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); -+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream); - NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); - void - conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, -diff --git a/agent/debug.c b/agent/debug.c -index e1a298c..84fee94 100644 ---- a/agent/debug.c -+++ b/agent/debug.c -@@ -102,7 +102,7 @@ void nice_debug_init (void) - flags |= g_parse_debug_string (gflags_string, gkeys, 4); - if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) - flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; -- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { -+ if (gflags_string && strstr (gflags_string, "libnice-verbose")) { - flags |= NICE_DEBUG_NICE_VERBOSE; - } - -diff --git a/agent/discovery.c b/agent/discovery.c -index 7a890a0..4cc99c2 100644 ---- a/agent/discovery.c -+++ b/agent/discovery.c -@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) - - if (buffer_len > 0) { - if (nice_socket_is_reliable (cand->nicesock)) { -- stun_timer_start_reliable (&cand->timer, -- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); -+ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); - } else { -- stun_timer_start (&cand->timer, 200, -- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -+ stun_timer_start (&cand->timer, -+ agent->stun_initial_timeout, -+ agent->stun_max_retransmissions); - } - - /* send the conncheck */ -diff --git a/agent/interfaces.c b/agent/interfaces.c -index 0fa2fd7..a81888e 100644 ---- a/agent/interfaces.c -+++ b/agent/interfaces.c -@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback) - nice_debug ("Ignoring loopback interface"); - g_free (addr_string); - } -+#ifdef IGNORED_IFACE_PREFIX -+ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) { -+ nice_debug ("Ignoring interface %s as it matches prefix %s", -+ ifa->ifa_name, IGNORED_IFACE_PREFIX); -+ g_free (addr_string); -+#endif - } else { - if (nice_interfaces_is_private_ip (ifa->ifa_addr)) - ips = add_ip_to_list (ips, addr_string, TRUE); -diff --git a/agent/stream.c b/agent/stream.c -index 8121e12..533ff15 100644 ---- a/agent/stream.c -+++ b/agent/stream.c -@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) - return NULL; - } - --/* -- * Returns true if all components of the stream are either -- * 'CONNECTED' or 'READY' (connected plus nominated). -- */ --gboolean --nice_stream_all_components_ready (NiceStream *stream) --{ -- GSList *i; -- -- for (i = stream->components; i; i = i->next) { -- NiceComponent *component = i->data; -- if (component && -- !(component->state == NICE_COMPONENT_STATE_CONNECTED || -- component->state == NICE_COMPONENT_STATE_READY)) -- return FALSE; -- } -- -- return TRUE; --} -- -- - /* - * Initialized the local crendentials for the stream. - */ -diff --git a/agent/stream.h b/agent/stream.h -index f9188cb..954ba66 100644 ---- a/agent/stream.h -+++ b/agent/stream.h -@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); - void - nice_stream_close (NiceStream *stream); - --gboolean --nice_stream_all_components_ready (NiceStream *stream); -- - NiceComponent * - nice_stream_find_component_by_id (NiceStream *stream, guint id); - -diff --git a/common.mk b/common.mk -index e2ca3f4..b16380d 100644 ---- a/common.mk -+++ b/common.mk -@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice - - - check-valgrind: -- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check -+ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check -+ -+LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver - - .PHONY: check-valgrind -diff --git a/configure.ac b/configure.ac -index b39bfe3..16988ad 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -57,6 +57,7 @@ AC_PROG_CC - AM_PROG_AR - LT_PREREQ([2.2.6]) - LT_INIT([dlopen win32-dll disable-static]) -+AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) - - # Check Operating System - AC_MSG_CHECKING([operating system]) -@@ -153,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \ - ]) - AS_IF([test "$enable_compile_warnings" = "maximum" -o \ - "$enable_compile_warnings" = "error"],[ -- NICE_ADD_FLAG([-Wswitch-enum]) - NICE_ADD_FLAG([-Wswitch-default]) - NICE_ADD_FLAG([-Waggregate-return]) - ]) -@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false) - # GObject introspection - GOBJECT_INTROSPECTION_CHECK([1.30.0]) - -+dnl Ignore a specific network interface name prefix from the connection check -+AC_MSG_CHECKING([whether to ignore a specific network interface name prefix]) -+AC_ARG_WITH([ignored-network-interface-prefix], -+ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string], -+ [Ignore network interfaces whose name starts with "string" from the ICE connection -+ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge -+ handled by libvirtd, do not help in finding connectivity.])], -+ [interface_prefix="$withval"]) -+AS_IF([test -n "$interface_prefix"], -+ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"], -+ [Ignore this network interface prefix from the connection check]) -+ AC_MSG_RESULT([yes, $interface_prefix])], -+ [AC_MSG_RESULT([no])]) -+ - AC_CONFIG_MACRO_DIR(m4) - - AC_OUTPUT -diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml -index 53487bc..391be1a 100644 ---- a/docs/reference/libnice/libnice-docs.xml -+++ b/docs/reference/libnice/libnice-docs.xml -@@ -105,6 +105,10 @@ - <title>Index of new symbols in 0.1.14</title> - <xi:include href="xml/api-index-0.1.14.xml"><xi:fallback/></xi:include> - </index> -+ <index role="0.1.15"> -+ <title>Index of new symbols in 0.1.15</title> -+ <xi:include href="xml/api-index-0.1.15.xml"><xi:fallback/></xi:include> -+ </index> - <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> - </part> - </book> -diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt -index d377257..a481106 100644 ---- a/docs/reference/libnice/libnice-sections.txt -+++ b/docs/reference/libnice/libnice-sections.txt -@@ -5,6 +5,7 @@ NiceAgent - NiceComponentState - NiceComponentType - NiceProxyType -+NiceNominationMode - NiceCompatibility - NiceAgentRecvFunc - NiceInputMessage -@@ -12,6 +13,7 @@ NiceOutputMessage - NICE_AGENT_MAX_REMOTE_CANDIDATES - nice_agent_new - nice_agent_new_reliable -+nice_agent_new_full - nice_agent_add_local_address - nice_agent_set_port_range - nice_agent_add_stream -@@ -76,6 +78,7 @@ NICE_CANDIDATE_MAX_FOUNDATION - nice_candidate_new - nice_candidate_free - nice_candidate_copy -+nice_candidate_equal_target - <SUBSECTION Standard> - NICE_TYPE_CANDIDATE - nice_candidate_get_type -diff --git a/examples/Makefile.am b/examples/Makefile.am -index 1e7decf..9c80854 100644 ---- a/examples/Makefile.am -+++ b/examples/Makefile.am -@@ -18,7 +18,7 @@ AM_CFLAGS = \ - $(GLIB_CFLAGS) \ - $(GUPNP_CFLAGS) - --bin_PROGRAMS = simple-example threaded-example sdp-example -+noinst_PROGRAMS = simple-example threaded-example sdp-example - - simple_example_SOURCES = simple-example.c - simple_example_LDADD = $(top_builddir)/agent/libagent.la \ -diff --git a/nice/libnice.sym b/nice/libnice.sym -index b04bb95..1e522ad 100644 ---- a/nice/libnice.sym -+++ b/nice/libnice.sym -@@ -58,6 +58,7 @@ nice_agent_set_software - nice_agent_set_stream_name - nice_agent_set_stream_tos - nice_candidate_copy -+nice_candidate_equal_target - nice_candidate_free - nice_candidate_new - nice_component_state_to_string -diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver -new file mode 100755 -index 0000000..5b660ee ---- /dev/null -+++ b/scripts/valgrind-test-driver -@@ -0,0 +1,162 @@ -+#! /bin/sh -+# test-driver - basic testsuite driver script. -+ -+scriptversion=2017-04-04.22; # UTC -+ -+# Copyright (C) 2011-2014 Free Software Foundation, Inc. -+# -+# This program is free software; you can redistribute it and/or modify -+# it under the terms of the GNU General Public License as published by -+# the Free Software Foundation; either version 2, or (at your option) -+# any later version. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see <
http://www.gnu.org/licenses/
>. -+ -+# As a special exception to the GNU General Public License, if you -+# distribute this file as part of a program that contains a -+# configuration script generated by Autoconf, you may include it under -+# the same distribution terms that you use for the rest of that program. -+ -+# This file is maintained in Automake, please report -+# bugs to <bug-automake(a)gnu.org> or send patches to -+# <automake-patches(a)gnu.org>. -+ -+# Make unconditional expansion of undefined variables an error. This -+# helps a lot in preventing typo-related bugs. -+set -u -+ -+usage_error () -+{ -+ echo "$0: $*" >&2 -+ print_usage >&2 -+ exit 2 -+} -+ -+print_usage () -+{ -+ cat <<END -+Usage: -+ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH -+ [--expect-failure={yes|no}] [--color-tests={yes|no}] -+ [--enable-hard-errors={yes|no}] [--] -+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] -+The '--test-name', '--log-file' and '--trs-file' options are mandatory. -+END -+} -+ -+test_name= # Used for reporting. -+log_file= # Where to save the output of the test script. -+trs_file= # Where to save the metadata of the test run. -+expect_failure=no -+color_tests=no -+enable_hard_errors=yes -+while test $# -gt 0; do -+ case $1 in -+ --help) print_usage; exit $?;; -+ --version) echo "test-driver $scriptversion"; exit $?;; -+ --test-name) test_name=$2; shift;; -+ --log-file) log_file=$2; shift;; -+ --trs-file) trs_file=$2; shift;; -+ --color-tests) color_tests=$2; shift;; -+ --expect-failure) expect_failure=$2; shift;; -+ --enable-hard-errors) enable_hard_errors=$2; shift;; -+ --) shift; break;; -+ -*) usage_error "invalid option: '$1'";; -+ *) break;; -+ esac -+ shift -+done -+ -+missing_opts= -+test x"$test_name" = x && missing_opts="$missing_opts --test-name" -+test x"$log_file" = x && missing_opts="$missing_opts --log-file" -+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" -+if test x"$missing_opts" != x; then -+ usage_error "the following mandatory options are missing:$missing_opts" -+fi -+ -+if test $# -eq 0; then -+ usage_error "missing argument" -+fi -+ -+if test $color_tests = yes; then -+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. -+ red='[0;31m' # Red. -+ grn='[0;32m' # Green. -+ lgn='[1;32m' # Light green. -+ blu='[1;34m' # Blue. -+ mgn='[0;35m' # Magenta. -+ std='[m' # No color. -+else -+ red= grn= lgn= blu= mgn= std= -+fi -+ -+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' -+trap "st=129; $do_exit" 1 -+trap "st=130; $do_exit" 2 -+trap "st=141; $do_exit" 13 -+trap "st=143; $do_exit" 15 -+ -+# Test script is run here. -+top_srcdir="`dirname $0`/.." -+tests_dir="${top_srcdir}/tests" -+ -+USE_VALGRIND="`printenv USE_VALGRIND`" -+ -+if test "x${USE_VALGRIND}" = "x1"; then -+ ${top_srcdir}/libtool --mode=execute valgrind \ -+ --leak-check=full \ -+ --show-reachable=no \ -+ --error-exitcode=1 \ -+ --suppressions=$tests_dir/libnice.supp \ -+ --num-callers=30 "$@" >$log_file 2>&1 -+else -+ "$@" >$log_file 2>&1 -+fi -+estatus=$? -+ -+if test $enable_hard_errors = no && test $estatus -eq 99; then -+ tweaked_estatus=1 -+else -+ tweaked_estatus=$estatus -+fi -+ -+case $tweaked_estatus:$expect_failure in -+ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; -+ 0:*) col=$grn res=PASS recheck=no gcopy=no;; -+ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; -+ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; -+ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; -+ *:*) col=$red res=FAIL recheck=yes gcopy=yes;; -+esac -+ -+# Report the test outcome and exit status in the logs, so that one can -+# know whether the test passed or failed simply by looking at the '.log' -+# file, without the need of also peaking into the corresponding '.trs' -+# file (automake bug#11814). -+echo "$res $test_name (exit status: $estatus)" >>$log_file -+ -+# Report outcome to console. -+echo "${col}${res}${std}: $test_name" -+ -+# Register the test result, and other relevant metadata. -+echo ":test-result: $res" > $trs_file -+echo ":global-test-result: $res" >> $trs_file -+echo ":recheck: $recheck" >> $trs_file -+echo ":copy-in-global-log: $gcopy" >> $trs_file -+ -+# Local Variables: -+# mode: shell-script -+# sh-indentation: 2 -+# eval: (add-hook 'write-file-hooks 'time-stamp) -+# time-stamp-start: "scriptversion=" -+# time-stamp-format: "%:y-%02m-%02d.%02H" -+# time-stamp-time-zone: "UTC" -+# time-stamp-end: "; # UTC" -+# End: -diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh -deleted file mode 100755 -index 2864b6f..0000000 ---- a/scripts/valgrind.sh -+++ /dev/null -@@ -1,28 +0,0 @@ --#!/bin/sh -- --export G_SLICE=always-malloc --export G_DEBUG=gc-friendly -- --tests_dir="`dirname $0`/../tests" -- --report=`libtool --mode=execute valgrind \ -- --leak-check=full \ -- --show-reachable=no \ -- --error-exitcode=1 \ -- --suppressions=$tests_dir/libnice.supp \ -- --num-callers=30 \ -- $1 2>&1` -- --#if echo "$report" | grep -q ==; then --if test $? != 0; then -- echo "$report" -- exit 1 --fi -- --if echo "$report" | grep -q "definitely lost"; then -- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then -- echo "$report" -- exit 1 -- fi --fi -- -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index cc3409b..190a9ea 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data) - - NiceSocket * - nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, -- NiceSocket *base_socket, NiceAddress *server_addr, -- gchar *username, gchar *password, -+ NiceSocket *base_socket, const NiceAddress *server_addr, -+ const gchar *username, const gchar *password, - NiceTurnSocketCompatibility compatibility) - { - UdpTurnPriv *priv; -@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock, - - /* Split up the monolithic buffer again into the caller-provided buffers. */ - if (parsed_buffer_length > 0 && allocated_buffer) { -- parsed_buffer_length = -- memcpy_buffer_to_input_message (message, buffer, -- parsed_buffer_length); -+ memcpy_buffer_to_input_message (message, buffer, -+ parsed_buffer_length); - } - - if (allocated_buffer) -@@ -1185,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - gsize - nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - NiceAddress *from, gsize len, guint8 *buf, -- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len) -+ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len) - { - - UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv; -@@ -1195,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - ChannelBinding *binding = NULL; - - union { -- guint8 *u8; -- guint16 *u16; -+ const guint8 *u8; -+ const guint16 *u16; - } recv_buf; - - /* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN) -diff --git a/socket/udp-turn.h b/socket/udp-turn.h -index b1eeeb4..df10a1c 100644 ---- a/socket/udp-turn.h -+++ b/socket/udp-turn.h -@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - gsize - nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - NiceAddress *from, gsize len, guint8 *buf, -- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len); -+ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len); - - gboolean - nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer); - - NiceSocket * - nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, -- NiceSocket *base_socket, NiceAddress *server_addr, -- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility); -+ NiceSocket *base_socket, const NiceAddress *server_addr, -+ const gchar *username, const gchar *password, -+ NiceTurnSocketCompatibility compatibility); - - void - nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); -diff --git a/stun/stunmessage.c b/stun/stunmessage.c -index e8184c4..4cc3392 100644 ---- a/stun/stunmessage.c -+++ b/stun/stunmessage.c -@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type, - /* Only fingerprint may come after M-I */ - if (type == STUN_ATTRIBUTE_FINGERPRINT) - break; -+ return NULL; - - case STUN_ATTRIBUTE_FINGERPRINT: - /* Nothing may come after FPR */ -diff --git a/stun/usages/bind.c b/stun/usages/bind.c -index 8dd7afc..ee600a0 100644 ---- a/stun/usages/bind.c -+++ b/stun/usages/bind.c -@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - size_t len; - StunUsageTransReturn ret; - int val; -- struct sockaddr_storage alternate_server; -+ struct sockaddr_storage alternate_server = { AF_UNSPEC } ; - socklen_t alternate_server_len = sizeof (alternate_server); - StunUsageBindReturn bind_ret; - -@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen); - if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { - stun_debug ("STUN transaction failed: couldn't create transport."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) { - stun_debug ("STUN transaction failed: couldn't send request."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, -@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - switch (stun_timer_refresh (&timer)) { - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - stun_debug ("STUN transaction failed: time out."); -- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! -+ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! -+ goto done; - case STUN_USAGE_TIMER_RETURN_RETRANSMIT: - stun_debug ("STUN transaction retransmitted (timeout %dms).", - stun_timer_remainder (&timer)); - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) { - stun_debug ("STUN transaction failed: couldn't resend request."); -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - continue; - case STUN_USAGE_TIMER_RETURN_SUCCESS: -@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - - valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL); - if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE) -- return STUN_USAGE_BIND_RETURN_ERROR; -+ { -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; -+ } - - if (valid != STUN_VALIDATION_SUCCESS) { - ret = STUN_USAGE_TRANS_RETURN_RETRY; -@@ -548,16 +555,22 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) { - stun_trans_deinit (&trans); - -+ assert (alternate_server.ss_family != AF_UNSPEC); -+ - ret = stun_trans_create (&trans, SOCK_DGRAM, 0, - (struct sockaddr *) &alternate_server, alternate_server_len); - - if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { -- return STUN_USAGE_BIND_RETURN_ERROR; -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; - } - - val = stun_trans_send (&trans, req_buf, len); - if (val < -1) -- return STUN_USAGE_BIND_RETURN_ERROR; -+ { -+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; -+ goto done; -+ } - - stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); -@@ -571,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, - } - while (ret == STUN_USAGE_TRANS_RETURN_RETRY); - -+done: -+ if (trans.fd != -1) -+ stun_trans_deinit (&trans); -+ - return bind_ret; - } -diff --git a/stun/usages/timer.c b/stun/usages/timer.c -index 2862ab8..5370cba 100644 ---- a/stun/usages/timer.c -+++ b/stun/usages/timer.c -@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer) - if (timer->retransmissions >= timer->max_retransmissions) - return STUN_USAGE_TIMER_RETURN_TIMEOUT; - -- add_delay (&timer->deadline, timer->delay *= 2); -+ if (timer->retransmissions == timer->max_retransmissions - 1) -+ timer->delay = timer->delay / 2; -+ else -+ timer->delay = timer->delay * 2; -+ add_delay (&timer->deadline, timer->delay); - timer->retransmissions++; - return STUN_USAGE_TIMER_RETURN_RETRANSMIT; - } -diff --git a/stun/usages/timer.h b/stun/usages/timer.h -index e74353b..097e75b 100644 ---- a/stun/usages/timer.h -+++ b/stun/usages/timer.h -@@ -132,7 +132,11 @@ struct stun_timer_s { - * The default intial timeout to use for the timer - * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most - * cases as it is also what is used by SIP style VoIP when sending A-Law and -- * mu-Law audio, so 200ms should be hyper safe. -+ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout -+ * of 200ms, a default of 7 transmissions, the last timeout will be -+ * 16 * 200ms, and we expect to receive a response from the stun server -+ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial -+ * stun request has been sent. - */ - #define STUN_TIMER_DEFAULT_TIMEOUT 200 - -diff --git a/stun/usages/turn.c b/stun/usages/turn.c -index 3b94959..ec12642 100644 ---- a/stun/usages/turn.c -+++ b/stun/usages/turn.c -@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, - stun_debug (" STUN error message received (code: %d)", code); - - /* ALTERNATE-SERVER mechanism */ -+ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 && -+ alternate_server && alternate_server_len && -+ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER, -+ alternate_server, -+ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) { -+ stun_debug ("Found alternate server"); -+ /* The ALTERNATE_SERVER will always be returned by the MS turn server. -+ * We need to check if the ALTERNATE_SERVER is the same as the current -+ * server to decide whether we need to switch servers or not. -+ */ -+ } - if ((code / 100) == 3) { - if (alternate_server && alternate_server_len) { - if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, -diff --git a/stun/usages/turn.h b/stun/usages/turn.h -index 7a2d4e6..83fa00a 100644 ---- a/stun/usages/turn.h -+++ b/stun/usages/turn.h -@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, - * Allocate request, in case the currently used TURN server is requesting the use - * of an alternate server. This argument will only be filled if the return value - * of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER -+ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the -+ * @alternate_server could be filled at any time, and should only be considered -+ * if the request was sent to a different server than the address returned -+ * in the @alternate_server field - * @alternate_server_len: The length of @alternate_server - * @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us - * @lifetime: A pointer to fill with the lifetime of the allocation -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 7bfe075..30d6f8e 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" - - AM_TESTS_ENVIRONMENT = \ - G_MESSAGES_DEBUG=all \ -- NICE_DEBUG=all; -+ NICE_DEBUG=all - - COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) - -@@ -45,8 +45,8 @@ check_PROGRAMS = \ - test-send-recv \ - test-socket-is-based-on \ - test-priority \ -- test-mainloop \ - test-fullmode \ -+ test-different-number-streams \ - test-restart \ - test-fallback \ - test-thread \ -@@ -55,7 +55,9 @@ check_PROGRAMS = \ - test-tcp \ - test-icetcp \ - test-credentials \ -- test-turn -+ test-turn \ -+ test-drop-invalid \ -+ test-nomination - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -113,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD) - - test_fullmode_LDADD = $(COMMON_LDADD) - -+test_different_number_streams_LDADD = $(COMMON_LDADD) -+ - test_restart_LDADD = $(COMMON_LDADD) - - test_fallback_LDADD = $(COMMON_LDADD) -@@ -129,6 +133,10 @@ test_credentials_LDADD = $(COMMON_LDADD) - - test_turn_LDADD = $(COMMON_LDADD) - -+test_drop_invalid_LDADD = $(COMMON_LDADD) -+ -+test_nomination_LDADD = $(COMMON_LDADD) -+ - test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) - test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) - -diff --git a/tests/test-credentials.c b/tests/test-credentials.c -index 1de4e49..f1ea89d 100644 ---- a/tests/test-credentials.c -+++ b/tests/test-credentials.c -@@ -184,6 +184,8 @@ int main (void) - nice_agent_get_local_credentials (lagent, 1, &ufrag, &password); - g_assert (g_strcmp0("unicorns", ufrag) == 0); - g_assert (g_strcmp0("awesome", password) == 0); -+ g_free (ufrag); -+ g_free (password); - - nice_agent_gather_candidates (lagent, 1); - nice_agent_gather_candidates (ragent, 1); -diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c -new file mode 100644 -index 0000000..7fd4763 ---- /dev/null -+++ b/tests/test-different-number-streams.c -@@ -0,0 +1,208 @@ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+#define ADD_2_STREAMS TRUE -+#define USE_SECOND_STREAM TRUE -+ -+static GMainLoop *global_mainloop = NULL; -+ -+static guint global_components_ready = 0; -+static guint global_components_ready_exit = 0; -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id); -+} -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)", -+ agent, stream_id, component_id, nice_component_state_to_string (state)); -+ -+ if (state == NICE_COMPONENT_STATE_READY) { -+ global_components_ready++; -+ } -+ -+ /* signal status via a global variable */ -+ if (global_components_ready == global_components_ready_exit) { -+ g_debug ("Components ready/failed achieved. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id); -+} -+ -+int main (void) -+{ -+ NiceAgent *lagent, *ragent; -+ guint timer_id; -+ guint ls_id, rs_id_1, rs_id_2; -+ gchar *lufrag = NULL, *lpassword = NULL; -+ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL; -+ -+#ifdef G_OS_WIN32 -+ WSADATA w; -+ -+ WSAStartup(0x0202, &w); -+#endif -+ -+ global_mainloop = g_main_loop_new (NULL, FALSE); -+ -+ /* step: create the agents L and R */ -+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_GOOGLE); -+ g_debug ("lagent: %p", lagent); -+ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent"); -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), NULL); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), NULL); -+ -+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_GOOGLE); -+ g_debug ("ragent: %p", ragent); -+ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent"); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), NULL); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), NULL); -+ -+ /* step: add a timer to catch state changes triggered by signals */ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 2); -+ g_assert (ls_id > 0); -+ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword); -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ -+ global_components_ready_exit = 4; -+ -+ if (ADD_2_STREAMS) { -+ rs_id_1 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_1 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); -+ -+ rs_id_2 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_2 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2); -+ -+ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword); -+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2); -+ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); -+ -+ if (USE_SECOND_STREAM) { -+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP); -+ } else { -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); -+ } -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ } else { -+ rs_id_1 = nice_agent_add_stream (ragent, 2); -+ g_assert (rs_id_1 > 0); -+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); -+ -+ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword); -+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1); -+ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); -+ -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); -+ } -+ -+ /* step: run the mainloop until connectivity checks succeed -+ * (see timer_cb() above) */ -+ g_main_loop_run (global_mainloop); -+ -+ g_free (lufrag); -+ g_free (lpassword); -+ g_free (rufrag1); -+ g_free (rpassword1); -+ g_free (rufrag2); -+ g_free (rpassword2); -+ g_object_unref (lagent); -+ g_object_unref (ragent); -+ -+ g_main_loop_unref (global_mainloop); -+ global_mainloop = NULL; -+ -+ g_source_remove (timer_id); -+ -+#ifdef G_OS_WIN32 -+ WSACleanup(); -+#endif -+ -+ return 0; -+} -diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c -new file mode 100644 -index 0000000..97e3586 ---- /dev/null -+++ b/tests/test-drop-invalid.c -@@ -0,0 +1,517 @@ -+/* -+ * This file is part of the Nice GLib ICE library. -+ * -+ * Unit test for ICE full-mode related features. -+ * -+ * (C) 2007 Nokia Corporation. All rights reserved. -+ * Contact: Kai Vehmanen -+ * (C) 2017 Collabora Ltd -+ * Contact: Olivier Crete <olivier.crete(a)collabora.com> -+ * -+ * The contents of this file are subject to the Mozilla Public License Version -+ * 1.1 (the "License"); you may not use this file except in compliance with -+ * the License. You may obtain a copy of the License at -+ *
http://www.mozilla.org/MPL/
-+ * -+ * Software distributed under the License is distributed on an "AS IS" basis, -+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -+ * for the specific language governing rights and limitations under the -+ * License. -+ * -+ * The Original Code is the Nice GLib ICE library. -+ * -+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia -+ * Corporation. All Rights Reserved. -+ * -+ * Contributors: -+ * Kai Vehmanen, Nokia -+ * -+ * Alternatively, the contents of this file may be used under the terms of the -+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -+ * case the provisions of LGPL are applicable instead of those above. If you -+ * wish to allow use of your version of this file only under the terms of the -+ * LGPL and not to allow others to use your version of this file under the -+ * MPL, indicate your decision by deleting the provisions above and replace -+ * them with the notice and other provisions required by the LGPL. If you do -+ * not delete the provisions above, a recipient may use your version of this -+ * file under either the MPL or the LGPL. -+ */ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "agent.h" -+ -+#include "socket/socket.h" -+ -+#include <stdlib.h> -+#include <string.h> -+ -+ -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static guint global_components_ready_exit = 0; -+static guint global_components_failed = 0; -+static guint global_components_failed_exit = 0; -+static GMainLoop *global_mainloop = NULL; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static gboolean global_lagent_ibr_received = FALSE; -+static gboolean global_ragent_ibr_received = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+static gint global_ragent_read = 0; -+static guint global_exit_when_ibr_received = 0; -+ -+static void priv_print_global_status (void) -+{ -+ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done); -+ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]); -+ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]); -+ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands); -+} -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* Core of the test -+ * Assert on any unreleated packet received. This would include anything -+ * send before the negotiation is over. -+ */ -+ g_assert (len == 16); -+ g_assert (strncmp ("1234567812345678", buf, 16) == 0); -+ -+ if (component_id == 2) -+ return; -+ -+ if (GPOINTER_TO_UINT (user_data) == 2) { -+ g_debug ("right agent received %d bytes, stopping mainloop", len); -+ global_ragent_read = len; -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+ -+ if (global_lagent_gathering_done && -+ global_ragent_gathering_done) -+ g_main_loop_quit (global_mainloop); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; -+} -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ if (state == NICE_COMPONENT_STATE_FAILED) -+ global_components_failed++; -+ -+ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit); -+ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit); -+ -+ /* signal status via a global variable */ -+ if (global_components_ready == global_components_ready_exit && -+ global_components_failed == global_components_failed_exit) { -+ g_debug ("Components ready/failed achieved. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ return; -+ } -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; (void)component_id; -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, -+ gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; -+} -+ -+static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, -+ gchar *foundation, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation; -+} -+ -+static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_ibr_received = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_ibr_received = TRUE; -+ -+ if (global_exit_when_ibr_received) { -+ g_debug ("Received initial binding request. Stopping mailoop"); -+ g_main_loop_quit (global_mainloop); -+ } -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)data; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL; -+ GSList *peer_cands = NULL; -+ GSList *item1, *item2; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ peer_cands = nice_agent_get_local_candidates (to, to_stream, component); -+ -+ /* -+ * Core of the test: -+ * -+ * Send packets that shoudl be dropped. -+ */ -+ -+ for (item1 = cands; item1; item1 = item1->next) { -+ NiceCandidate *cand = item1->data; -+ NiceSocket *nicesock = cand->sockptr; -+ -+ g_assert (nicesock); -+ -+ for (item2 = peer_cands; item2; item2 = item2->next) { -+ NiceCandidate *target_cand = item2->data; -+ -+ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB"); -+ } -+ -+ } -+ -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free); -+ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed) -+{ -+ guint ls_id, rs_id; -+ gint ret; -+ -+ /* XXX: dear compiler, this is for you */ -+ (void)baseaddr; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_components_ready_exit = ready; -+ global_components_failed = 0; -+ global_components_failed_exit = failed; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_ibr_received = -+ global_ragent_ibr_received = FALSE; -+ global_lagent_cands = -+ global_ragent_cands = 0; -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ /* step: add one stream, with RTP+RTCP components, to each agent */ -+ ls_id = nice_agent_add_stream (lagent, 2); -+ -+ rs_id = nice_agent_add_stream (ragent, 2); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000); -+ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001); -+ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345); -+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001); -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE); -+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL); -+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL); -+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ { -+ GSList *cands = NULL, *i; -+ NiceCandidate *cand = NULL; -+ -+ cands = nice_agent_get_local_candidates (lagent, ls_id, 1); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10000); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (lagent, ls_id, 2); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10001); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (ragent, rs_id, 1); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 12345); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ cands = nice_agent_get_local_candidates (ragent, rs_id, 2); -+ g_assert (g_slist_length (cands) == 1); -+ cand = cands->data; -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); -+ g_assert (nice_address_get_port (&cand->addr) == 10002); -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+ -+ } -+ -+ /* step: attach to mainloop (needed to register the fds) */ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (2)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, -+ g_main_loop_get_context (global_mainloop), cb_nice_recv, -+ GUINT_TO_POINTER (2)); -+ -+ /* step: run mainloop until local candidates are ready -+ * (see timer_cb() above) */ -+ if (global_lagent_gathering_done != TRUE || -+ global_ragent_gathering_done != TRUE) { -+ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'..."); -+ g_main_loop_run (global_mainloop); -+ g_assert (global_lagent_gathering_done == TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ } -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ /* step: pass the remote candidates to agents */ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP); -+ -+ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed..."); -+ -+ /* step: run the mainloop until connectivity checks succeed -+ * (see timer_cb() above) */ -+ g_main_loop_run (global_mainloop); -+ -+ /* note: verify that STUN binding requests were sent */ -+ g_assert (global_lagent_ibr_received == TRUE); -+ g_assert (global_ragent_ibr_received == TRUE); -+ -+ /* note: Send a packet from another address */ -+ /* These should also be ignored */ -+ { -+ NiceCandidate *local_cand = NULL; -+ NiceCandidate *remote_cand = NULL; -+ NiceSocket *tmpsock; -+ -+ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand, -+ &remote_cand)); -+ g_assert (local_cand); -+ g_assert (remote_cand); -+ -+ tmpsock = nice_udp_bsd_socket_new (NULL); -+ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD"); -+ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE"); -+ nice_socket_free (tmpsock); -+ } -+ -+ /* note: test payload send and receive */ -+ global_ragent_read = 0; -+ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"); -+ g_assert (ret != -1); -+ g_debug ("Sent %d bytes", ret); -+ g_assert (ret == 16); -+ while (global_ragent_read != 16) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_ragent_read == 16); -+ -+ g_debug ("test-drop-invalid: Ran mainloop, removing streams..."); -+ -+ /* step: clean up resources and exit */ -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ return 0; -+} -+ -+int main (void) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ NiceAddress baseaddr; -+ int result; -+ guint timer_id; -+ -+#ifdef G_OS_WIN32 -+ WSADATA w; -+ -+ WSAStartup(0x0202, &w); -+#endif -+ -+ global_mainloop = g_main_loop_new (NULL, FALSE); -+ -+ /* step: create the agents L and R */ -+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_RFC5245); -+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), -+ NICE_COMPATIBILITY_RFC5245); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ -+ -+ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent"); -+ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent"); -+ -+ /* step: add a timer to catch state changes triggered by signals */ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ /* step: specify which local interface to use */ -+ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1")) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &baseaddr); -+ nice_agent_add_local_address (ragent, &baseaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-candidate", -+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "new-candidate", -+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received", -+ G_CALLBACK (cb_initial_binding_request_received), -+ GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received", -+ G_CALLBACK (cb_initial_binding_request_received), -+ GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ -+ -+ /* step: run test the first time */ -+ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time"); -+ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0); -+ priv_print_global_status (); -+ g_assert (result == 0); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY); -+ /* When using TURN, we get peer reflexive candidates for the host cands -+ that we removed so we can get another new_selected_pair signal later -+ depending on timing/racing, we could double (or not) the amount we expected -+ */ -+ -+ /* note: verify that correct number of local candidates were reported */ -+ g_assert (global_lagent_cands == 2); -+ g_assert (global_ragent_cands == 2); -+ -+ g_object_unref (lagent); -+ g_object_unref (ragent); -+ -+ g_main_loop_unref (global_mainloop); -+ global_mainloop = NULL; -+ -+ g_source_remove (timer_id); -+#ifdef G_OS_WIN32 -+ WSACleanup(); -+#endif -+ return result; -+} -diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c -index 74d7133..f060efc 100644 ---- a/tests/test-gstreamer.c -+++ b/tests/test-gstreamer.c -@@ -1,7 +1,7 @@ - /* - * This file is part of the Nice GLib ICE library. - * -- * (C) 1015 Kurento. -+ * (C) 2015 Kurento. - * Contact: Jose Antonio Santos Cadenas - * - * The contents of this file are subject to the Mozilla Public License Version -@@ -34,7 +34,7 @@ - */ - - #include <gst/check/gstcheck.h> --#include <nice/agent.h> -+#include "agent.h" - - #define RTP_HEADER_SIZE 12 - #define RTP_PAYLOAD_SIZE 1024 -diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c -deleted file mode 100644 -index 7c52daa..0000000 ---- a/tests/test-mainloop.c -+++ /dev/null -@@ -1,108 +0,0 @@ --/* -- * This file is part of the Nice GLib ICE library. -- * -- * (C) 2006, 2007 Collabora Ltd. -- * Contact: Dafydd Harries -- * (C) 2006, 2007 Nokia Corporation. All rights reserved. -- * Contact: Kai Vehmanen -- * -- * The contents of this file are subject to the Mozilla Public License Version -- * 1.1 (the "License"); you may not use this file except in compliance with -- * the License. You may obtain a copy of the License at -- *
http://www.mozilla.org/MPL/
-- * -- * Software distributed under the License is distributed on an "AS IS" basis, -- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -- * for the specific language governing rights and limitations under the -- * License. -- * -- * The Original Code is the Nice GLib ICE library. -- * -- * The Initial Developers of the Original Code are Collabora Ltd and Nokia -- * Corporation. All Rights Reserved. -- * -- * Contributors: -- * Dafydd Harries, Collabora Ltd. -- * Kai Vehmanen, Nokia -- * -- * Alternatively, the contents of this file may be used under the terms of the -- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -- * case the provisions of LGPL are applicable instead of those above. If you -- * wish to allow use of your version of this file only under the terms of the -- * LGPL and not to allow others to use your version of this file under the -- * MPL, indicate your decision by deleting the provisions above and replace -- * them with the notice and other provisions required by the LGPL. If you do -- * not delete the provisions above, a recipient may use your version of this -- * file under either the MPL or the LGPL. -- */ --#ifdef HAVE_CONFIG_H --# include <config.h> --#endif -- --#include <string.h> -- --#include <nice/nice.h> --#include "socket/socket.h" -- --static GMainLoop *loop = NULL; -- --static void --recv_cb ( -- NiceAgent *agent, -- guint stream_id, -- guint component_id, -- guint len, -- gchar *buf, -- gpointer data) --{ -- g_assert (agent != NULL); -- g_assert (stream_id == 1); -- g_assert (component_id == 1); -- g_assert (len == 6); -- g_assert (0 == strncmp (buf, "\x80hello", len)); -- g_assert (42 == GPOINTER_TO_UINT (data)); -- g_main_loop_quit (loop); --} -- --int --main (void) --{ -- NiceAgent *agent; -- NiceAddress addr; -- guint stream; -- -- nice_address_init (&addr); -- -- loop = g_main_loop_new (NULL, FALSE); -- -- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245); -- nice_address_set_ipv4 (&addr, 0x7f000001); -- nice_agent_add_local_address (agent, &addr); -- stream = nice_agent_add_stream (agent, 1); -- nice_agent_gather_candidates (agent, stream); -- -- // attach to default main context -- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP, -- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42)); -- -- { -- NiceCandidate *candidate; -- GSList *candidates, *i; -- -- candidates = nice_agent_get_local_candidates (agent, 1, 1); -- candidate = candidates->data; -- -- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello"); -- for (i = candidates; i; i = i->next) -- nice_candidate_free ((NiceCandidate *) i->data); -- g_slist_free (candidates); -- } -- -- g_main_loop_run (loop); -- -- nice_agent_remove_stream (agent, stream); -- g_object_unref (agent); -- -- return 0; --} -- -diff --git a/tests/test-nomination.c b/tests/test-nomination.c -new file mode 100644 -index 0000000..bf21557 ---- /dev/null -+++ b/tests/test-nomination.c -@@ -0,0 +1,263 @@ -+#include <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <gio/gio.h> -+#include <agent.h> -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* -+ * Lets ignore stun packets that got through -+ */ -+ if (len < 8) -+ return; -+ if (strncmp ("12345678", buf, 8)) -+ return; -+ -+ if (component_id != 1) -+ return; -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+} -+ -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ g_assert (state != NICE_COMPONENT_STATE_FAILED); -+ -+ g_debug ("test-nomination: checks READY %u.", global_components_ready); -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, -+ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static void -+run_test(NiceNominationMode l_nomination_mode, -+ NiceNominationMode r_nomination_mode) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ const gchar *localhost; -+ NiceAddress localaddr; -+ guint ls_id, rs_id; -+ gulong timer_id; -+ -+ localhost = "127.0.0.1"; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_cands = global_ragent_cands = 0; -+ -+ lagent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); -+ -+ ragent = nice_agent_new_full (NULL, -+ NICE_COMPATIBILITY_RFC5245, -+ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? -+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ nice_agent_set_software (lagent, "Test-nomination, Left Agent"); -+ nice_agent_set_software (ragent, "Test-nomination, Right Agent"); -+ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ if (!nice_address_set_from_string (&localaddr, localhost)) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &localaddr); -+ nice_agent_add_local_address (ragent, &localaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 1); -+ rs_id = nice_agent_add_stream (ragent, 1); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); -+ -+ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'..."); -+ while (!global_lagent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_gathering_done == TRUE); -+ while (!global_ragent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); -+ -+ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || -+ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ g_source_remove (timer_id); -+ -+ g_clear_object(&lagent); -+ g_clear_object(&ragent); -+} -+ -+static void -+regular (void) -+{ -+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR); -+} -+ -+static void -+aggressive (void) -+{ -+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE); -+} -+ -+static void -+mixed_ra (void) -+{ -+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE); -+} -+ -+static void -+mixed_ar (void) -+{ -+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR); -+} -+ -+int -+main (int argc, char **argv) -+{ -+ int ret; -+ -+ g_test_init (&argc, &argv, NULL); -+ -+ g_test_add_func ("/nice/nomination/regular", regular); -+ g_test_add_func ("/nice/nomination/aggressive", aggressive); -+ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra); -+ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar); -+ -+ ret = g_test_run (); -+ -+ return ret; -+} -diff --git a/tests/test-restart.c b/tests/test-restart.c -index c2cbe9a..afc51b6 100644 ---- a/tests/test-restart.c -+++ b/tests/test-restart.c -@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands); - cdes.addr = laddr_rtcp; - nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands); -+ /* This role switch request will be effective after restart. We test -+ * here that the role cannot be externally modified after conncheck -+ * has started. */ -+ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL); -+ g_assert (ragent->controlling_mode == FALSE); - - g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed..."); - -@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - global_ragent_read = 0; - g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16); - -+ /* Both agent have a distinct role at the end of the conncheck */ -+ g_assert (lagent->controlling_mode == TRUE); -+ g_assert (ragent->controlling_mode == FALSE); - /* step: restart agents, exchange updated credentials */ - tie_breaker = ragent->tie_breaker; - nice_agent_restart (ragent); - g_assert (tie_breaker != ragent->tie_breaker); -+ /* This role switch of ragent should be done now, and both agents -+ * have now the same role, which should generate a role conflict -+ * resolution situation */ -+ g_assert (lagent->controlling_mode == TRUE); -+ g_assert (ragent->controlling_mode == TRUE); - nice_agent_restart (lagent); - { - gchar *ufrag = NULL, *password = NULL; -@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * - /* note: verify binding requests were resent after restart */ - g_assert (global_lagent_ibr_received == TRUE); - g_assert (global_ragent_ibr_received == TRUE); -+ /* note: verify that a role switch occured for one of the agents */ -+ g_assert (ragent->controlling_mode != lagent->controlling_mode); - - g_debug ("test-restart: Ran mainloop, removing streams..."); - diff --git a/libnice-0.1.14-70-gfb2f1f7.patch b/libnice-0.1.14-70-gfb2f1f7.patch new file mode 100644 index 0000000..db148cb --- /dev/null +++ b/libnice-0.1.14-70-gfb2f1f7.patch @@ -0,0 +1,10531 @@ +From a4bacb2fe2ff06ccb1a2d7f7c0b62bd41674565b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Mon, 3 Apr 2017 14:30:10 -0400 +Subject: [PATCH 01/70] Version 0.1.14.1 + +--- + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 5fabdb4..b39bfe3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -5,8 +5,8 @@ dnl Always compile with -Wall; if --enable-compile-warnings=error is passed, + dnl also use -Werror. git and pre-releases default to -Werror + + dnl use a three digit version number for releases, and four for cvs/prerelease +-AC_INIT([libnice],[0.1.14]) +-LIBNICE_RELEASE="yes" ++AC_INIT([libnice],[0.1.14.1]) ++LIBNICE_RELEASE="no" + + AC_CANONICAL_TARGET + +-- +2.13.6 + + +From 59ce41dfb837adf4222b25490cde2e394384ad15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= <mparisdiaz(a)gmail.com> +Date: Fri, 31 Mar 2017 20:20:38 -0400 +Subject: [PATCH 02/70] conncheck: consider answer received when remote + credentials are set +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Consider that the answer is received when remote credentials +are set instead of when a remote candidate is set, +which could not happen or could cause more delay for the +connection establishment. + +Ported to git master by Olivier Crête + +Differential Revision:
https://phabricator.freedesktop.org/D1704
+--- + agent/agent.c | 4 +- + agent/conncheck.c | 225 +++++++++++++++++++++++++++--------------------------- + agent/conncheck.h | 2 +- + 3 files changed, 117 insertions(+), 114 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 555fd16..4d9381c 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3240,6 +3240,8 @@ nice_agent_set_remote_credentials ( + g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG); + g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD); + ++ conn_check_remote_credentials_set(agent, stream); ++ + ret = TRUE; + goto done; + } +@@ -3342,8 +3344,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, + } + } + +- conn_check_remote_candidates_set(agent, stream, component); +- + if (added > 0) { + conn_check_schedule_next (agent); + } +diff --git a/agent/conncheck.c b/agent/conncheck.c +index dda2f2f..2abbc5e 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1243,124 +1243,124 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list) + + /* + * Handle any processing steps for connectivity checks after +- * remote candidates have been set. This function handles ++ * remote credentials have been set. This function handles + * the special case where answerer has sent us connectivity +- * checks before the answer (containing candidate information), ++ * checks before the answer (containing credentials information), + * reaches us. The special case is documented in sect 7.2 + * if ICE spec (ID-19). + */ +-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) ++void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + { +- GSList *j, *k, *l, *m, *n; ++ GSList *j, *k, *l, *m, *n, *o; + + for (j = stream->conncheck_list; j ; j = j->next) { + CandidateCheckPair *pair = j->data; +- if (pair->component_id == component->id) { +- gboolean match = FALSE; +- +- /* performn delayed processing of spec steps section 7.2.1.4, +- and section 7.2.1.5 */ +- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); +- +- for (k = component->incoming_checks; k; k = k->next) { +- IncomingCheck *icheck = k->data; +- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to +- * be handled separately */ +- for (l = component->remote_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&icheck->from, &cand->addr)) { +- match = TRUE; +- break; +- } +- } +- if (match != TRUE) { +- /* note: we have gotten an incoming connectivity check from +- * an address that is not a known remote candidate */ +- +- NiceCandidate *local_candidate = NULL; +- NiceCandidate *remote_candidate = NULL; +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || +- agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- /* We need to find which local candidate was used */ +- uint8_t uname[NICE_STREAM_MAX_UNAME]; +- guint uname_len; +- +- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " +- "stored pending check", agent); +- +- for (m = component->remote_candidates; +- m != NULL && remote_candidate == NULL; m = m->next) { +- for (n = component->local_candidates; n; n = n->next) { +- NiceCandidate *rcand = m->data; +- NiceCandidate *lcand = n->data; +- +- uname_len = priv_create_username (agent, stream, +- component->id, rcand, lcand, +- uname, sizeof (uname), TRUE); +- +- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", +- icheck->username_len, uname_len, +- icheck->username && uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0); +- stun_debug_bytes (" first username: ", +- icheck->username, +- icheck->username? icheck->username_len : 0); +- stun_debug_bytes (" second username: ", uname, uname_len); +- +- if (icheck->username && +- uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0) { +- local_candidate = lcand; +- remote_candidate = rcand; +- break; +- } +- } +- } +- } else { +- for (l = component->local_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { +- local_candidate = cand; ++ NiceComponent *component = ++ nice_stream_find_component_by_id (stream, pair->component_id); ++ gboolean match = FALSE; ++ ++ /* performn delayed processing of spec steps section 7.2.1.4, ++ and section 7.2.1.5 */ ++ priv_preprocess_conn_check_pending_data (agent, stream, component, pair); ++ ++ for (k = component->incoming_checks; k; k = k->next) { ++ IncomingCheck *icheck = k->data; ++ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to ++ * be handled separately */ ++ for (l = component->remote_candidates; l; l = l->next) { ++ NiceCandidate *cand = l->data; ++ if (nice_address_equal (&icheck->from, &cand->addr)) { ++ match = TRUE; ++ break; ++ } ++ } ++ if (match != TRUE) { ++ /* note: we have gotten an incoming connectivity check from ++ * an address that is not a known remote candidate */ ++ ++ NiceCandidate *local_candidate = NULL; ++ NiceCandidate *remote_candidate = NULL; ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || ++ agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ /* We need to find which local candidate was used */ ++ uint8_t uname[NICE_STREAM_MAX_UNAME]; ++ guint uname_len; ++ ++ nice_debug ("Agent %p: We have a peer-reflexive candidate in a " ++ "stored pending check", agent); ++ ++ for (m = component->remote_candidates; ++ m != NULL && remote_candidate == NULL; m = m->next) { ++ for (n = component->local_candidates; n; n = n->next) { ++ NiceCandidate *rcand = m->data; ++ NiceCandidate *lcand = n->data; ++ ++ uname_len = priv_create_username (agent, stream, ++ component->id, rcand, lcand, ++ uname, sizeof (uname), TRUE); ++ ++ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", ++ icheck->username_len, uname_len, ++ icheck->username && uname_len == icheck->username_len && ++ memcmp (uname, icheck->username, icheck->username_len) == 0); ++ stun_debug_bytes (" first username: ", ++ icheck->username, ++ icheck->username? icheck->username_len : 0); ++ stun_debug_bytes (" second username: ", uname, uname_len); ++ ++ if (icheck->username && ++ uname_len == icheck->username_len && ++ memcmp (uname, icheck->username, icheck->username_len) == 0) { ++ local_candidate = lcand; ++ remote_candidate = rcand; + break; + } + } + } +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && +- local_candidate == NULL) { +- /* if we couldn't match the username, then the matching remote +- * candidate hasn't been received yet.. we must wait */ +- nice_debug ("Agent %p : Username check failed. pending check has " +- "to wait to be processed", agent); +- } else { +- NiceCandidate *candidate; +- +- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", +- agent); +- candidate = +- discovery_learn_remote_peer_reflexive_candidate (agent, +- stream, +- component, +- icheck->priority, +- &icheck->from, +- icheck->local_socket, +- local_candidate, remote_candidate); +- if (candidate) { +- if (local_candidate && +- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) +- priv_conn_check_add_for_candidate_pair_matched (agent, +- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); +- else +- conn_check_add_for_candidate (agent, stream->id, component, candidate); +- +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ } else { ++ for (l = component->local_candidates; l; l = l->next) { ++ NiceCandidate *cand = l->data; ++ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { ++ local_candidate = cand; ++ break; + } + } + } ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && ++ local_candidate == NULL) { ++ /* if we couldn't match the username, then the matching remote ++ * candidate hasn't been received yet.. we must wait */ ++ nice_debug ("Agent %p : Username check failed. pending check has " ++ "to wait to be processed", agent); ++ } else { ++ NiceCandidate *candidate; ++ ++ nice_debug ("Agent %p : Discovered peer reflexive from early i-check", ++ agent); ++ candidate = ++ discovery_learn_remote_peer_reflexive_candidate (agent, ++ stream, ++ component, ++ icheck->priority, ++ &icheck->from, ++ icheck->local_socket, ++ local_candidate, remote_candidate); ++ if (candidate) { ++ if (local_candidate && ++ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) ++ priv_conn_check_add_for_candidate_pair_matched (agent, ++ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); ++ else ++ conn_check_add_for_candidate (agent, stream->id, component, candidate); ++ ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); ++ priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ } ++ } + } + } + } +@@ -1368,9 +1368,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice + /* Once we process the pending checks, we should free them to avoid + * reprocessing them again if a dribble-mode set_remote_candidates + * is called */ +- g_slist_free_full (component->incoming_checks, +- (GDestroyNotify) incoming_check_free); +- component->incoming_checks = NULL; ++ for (o = stream->components; o; o = o->next) { ++ NiceComponent *component = o->data; ++ g_slist_free_full (component->incoming_checks, ++ (GDestroyNotify) incoming_check_free); ++ component->incoming_checks = NULL; ++ } + + stream->conncheck_list = + prune_cancelled_conn_check (stream->conncheck_list); +@@ -3628,14 +3631,14 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + if (stream->initial_binding_request_received != TRUE) + agent_signal_initial_binding_request_received (agent, stream); + +- if (component->remote_candidates && remote_candidate == NULL) { ++ if (remote_candidate == NULL) { + nice_debug ("Agent %p : No matching remote candidate for incoming check ->" + "peer-reflexive candidate.", agent); + remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( + agent, stream, component, priority, from, nicesock, + local_candidate, + remote_candidate2 ? remote_candidate2 : remote_candidate); +- if(remote_candidate) { ++ if(remote_candidate && stream->remote_ufrag != NULL) { + if (local_candidate && + local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { + CandidateCheckPair *pair; +@@ -3654,10 +3657,10 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +- if (component->remote_candidates == NULL) { ++ if (stream->remote_ufrag == NULL) { + /* case: We've got a valid binding request to a local candidate +- * but we do not yet know remote credentials nor +- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply ++ * but we do not yet know remote credentials. ++ * As per sect 7.2 of ICE (ID-19), we send a reply + * immediately but postpone all other processing until + * we get information about the remote candidates */ + +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 431c606..10319cc 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -105,7 +105,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); + void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); + gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); + gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); +-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); ++void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream); + NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); + void + conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, +-- +2.13.6 + + +From 0de1657e4d15d4f1911ab1fad84ea23e7013070f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 12:25:50 -0400 +Subject: [PATCH 03/70] conncheck: Use the right test for empty remote_frag + +It's now an array, not a pointer, so needs to test to emptyness. + +It's a bugfix on the previous commit, 59ce41df +--- + agent/conncheck.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 2abbc5e..7096b42 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3638,7 +3638,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + agent, stream, component, priority, from, nicesock, + local_candidate, + remote_candidate2 ? remote_candidate2 : remote_candidate); +- if(remote_candidate && stream->remote_ufrag != NULL) { ++ if(remote_candidate && stream->remote_ufrag[0]) { + if (local_candidate && + local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { + CandidateCheckPair *pair; +@@ -3657,7 +3657,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +- if (stream->remote_ufrag == NULL) { ++ if (stream->remote_ufrag[0] == 0) { + /* case: We've got a valid binding request to a local candidate + * but we do not yet know remote credentials. + * As per sect 7.2 of ICE (ID-19), we send a reply +-- +2.13.6 + + +From 0672758b9621801c8f0d9e3c920370983b267a68 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 12:29:29 -0400 +Subject: [PATCH 04/70] agent: Don't set variable that won't be used + +It exits the loop immediately, so no point to set the variable. +And it makes the clang static analyzer happy. +--- + agent/agent.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 4d9381c..8ba99bc 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -4245,7 +4245,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, + "Component removed during call."); + + component = NULL; +- error_reported = TRUE; + + goto recv_error; + } +-- +2.13.6 + + +From db05e8b0fdc713df93cd6a4c3914e5aee38b2391 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 12:30:27 -0400 +Subject: [PATCH 05/70] Make clang-analyzer happy + +Various little things, none of which should make a functional difference. +--- + agent/agent.c | 1 - + agent/conncheck.c | 2 +- + socket/udp-turn.c | 5 ++--- + stun/usages/bind.c | 4 +++- + 4 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 8ba99bc..28d7ccf 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -1552,7 +1552,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, + + if (len == 0) { + /* Reached EOS. */ +- len = 0; + goto done; + } else if (len < 0 && + pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) { +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 7096b42..1dc13dd 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -606,7 +606,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + /* step: when there's no pair in the Waiting state, + * unfreeze a new pair and check it + */ +- res = priv_conn_check_unfreeze_next (agent); ++ priv_conn_check_unfreeze_next (agent); + + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +diff --git a/socket/udp-turn.c b/socket/udp-turn.c +index cc3409b..a9c57e5 100644 +--- a/socket/udp-turn.c ++++ b/socket/udp-turn.c +@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock, + + /* Split up the monolithic buffer again into the caller-provided buffers. */ + if (parsed_buffer_length > 0 && allocated_buffer) { +- parsed_buffer_length = +- memcpy_buffer_to_input_message (message, buffer, +- parsed_buffer_length); ++ memcpy_buffer_to_input_message (message, buffer, ++ parsed_buffer_length); + } + + if (allocated_buffer) +diff --git a/stun/usages/bind.c b/stun/usages/bind.c +index 8dd7afc..d56790f 100644 +--- a/stun/usages/bind.c ++++ b/stun/usages/bind.c +@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + size_t len; + StunUsageTransReturn ret; + int val; +- struct sockaddr_storage alternate_server; ++ struct sockaddr_storage alternate_server = { AF_UNSPEC } ; + socklen_t alternate_server_len = sizeof (alternate_server); + StunUsageBindReturn bind_ret; + +@@ -548,6 +548,8 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) { + stun_trans_deinit (&trans); + ++ assert (alternate_server.ss_family != AF_UNSPEC); ++ + ret = stun_trans_create (&trans, SOCK_DGRAM, 0, + (struct sockaddr *) &alternate_server, alternate_server_len); + +-- +2.13.6 + + +From cd255bddc7fa0ddae056b5358a22b380c4eefc42 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 15:24:43 -0400 +Subject: [PATCH 06/70] udp-turn: Add some const to internal APIs + +--- + agent/component.c | 2 +- + socket/udp-turn.c | 10 +++++----- + socket/udp-turn.h | 7 ++++--- + 3 files changed, 10 insertions(+), 9 deletions(-) + +diff --git a/agent/component.c b/agent/component.c +index 32f7463..a679b30 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -380,7 +380,7 @@ nice_component_restart (NiceComponent *cmp) + for (i = cmp->remote_candidates; i; i = i->next) { + NiceCandidate *candidate = i->data; + +- /* note: do not remove the local candidate that is ++ /* note: do not remove the remote candidate that is + * currently part of the 'selected pair', see ICE + * 9.1.1.1. "ICE Restarts" (ID-19) */ + if (candidate == cmp->selected_pair.remote) { +diff --git a/socket/udp-turn.c b/socket/udp-turn.c +index a9c57e5..190a9ea 100644 +--- a/socket/udp-turn.c ++++ b/socket/udp-turn.c +@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data) + + NiceSocket * + nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, +- NiceSocket *base_socket, NiceAddress *server_addr, +- gchar *username, gchar *password, ++ NiceSocket *base_socket, const NiceAddress *server_addr, ++ const gchar *username, const gchar *password, + NiceTurnSocketCompatibility compatibility) + { + UdpTurnPriv *priv; +@@ -1184,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc + gsize + nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + NiceAddress *from, gsize len, guint8 *buf, +- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len) ++ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len) + { + + UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv; +@@ -1194,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + ChannelBinding *binding = NULL; + + union { +- guint8 *u8; +- guint16 *u16; ++ const guint8 *u8; ++ const guint16 *u16; + } recv_buf; + + /* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN) +diff --git a/socket/udp-turn.h b/socket/udp-turn.h +index b1eeeb4..df10a1c 100644 +--- a/socket/udp-turn.h ++++ b/socket/udp-turn.h +@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc + gsize + nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + NiceAddress *from, gsize len, guint8 *buf, +- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len); ++ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len); + + gboolean + nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer); + + NiceSocket * + nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, +- NiceSocket *base_socket, NiceAddress *server_addr, +- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility); ++ NiceSocket *base_socket, const NiceAddress *server_addr, ++ const gchar *username, const gchar *password, ++ NiceTurnSocketCompatibility compatibility); + + void + nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); +-- +2.13.6 + + +From e56b910d2d8b70f5677bbd4be579d5b95aff33ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 16:16:05 -0400 +Subject: [PATCH 07/70] agent: Separate return from NiceSocket and internal + enum + +The same variable was used for return values from NiceSocket and +for the internal enum, but 0 and -1 have different meanings in both. +--- + agent/agent.c | 35 +++++++++++++++++++---------------- + 1 file changed, 19 insertions(+), 16 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 28d7ccf..7b8a9fc 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3422,7 +3422,8 @@ agent_recv_message_unlocked ( + { + NiceAddress from; + GList *item; +- gint retval; ++ RecvStatus retval; ++ gint sockret; + gboolean is_turn = FALSE; + + /* We need an address for packet parsing, below. */ +@@ -3483,8 +3484,8 @@ agent_recv_message_unlocked ( + local_bufs[i + 1].buffer = message->buffers[i].buffer; + local_bufs[i + 1].size = message->buffers[i].size; + } +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + message->length = ntohs (rfc4571_frame); + } + } else { +@@ -3499,7 +3500,7 @@ agent_recv_message_unlocked ( + _priv_set_socket_tos (agent, new_socket, stream->tos); + nice_component_attach_socket (component, new_socket); + } +- retval = 0; ++ sockret = 0; + } else { + /* In the case of a real ICE-TCP connection, we can use the socket as a + * bytestream and do the read here with caching of data being read +@@ -3508,9 +3509,9 @@ agent_recv_message_unlocked ( + + /* TODO: Support bytestream reads */ + message->length = 0; +- retval = 0; ++ sockret = 0; + if (available <= 0) { +- retval = available; ++ sockret = available; + + /* If we don't call check_connect_result on an outbound connection, + * then is_connected will always return FALSE. That's why we check +@@ -3523,7 +3524,7 @@ agent_recv_message_unlocked ( + * not connected, it means that it failed to connect, so we must + * return an error to make the socket fail/closed + */ +- retval = -1; ++ sockret = -1; + } else { + gint flags = G_SOCKET_MSG_PEEK; + +@@ -3536,7 +3537,7 @@ agent_recv_message_unlocked ( + */ + if (g_socket_receive_message (nicesock->fileno, NULL, + NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0) +- retval = -1; ++ sockret = -1; + } + } else if (agent->rfc4571_expecting_length == 0) { + if ((gsize) available >= sizeof(guint16)) { +@@ -3544,8 +3545,8 @@ agent_recv_message_unlocked ( + GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)}; + NiceInputMessage local_message = { &local_buf, 1, message->from, 0}; + +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + agent->rfc4571_expecting_length = ntohs (rfc4571_frame); + available = g_socket_get_available_bytes (nicesock->fileno); + } +@@ -3589,8 +3590,8 @@ agent_recv_message_unlocked ( + off += local_bufs[i].size; + } + } +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + message->length = local_message.length; + agent->rfc4571_expecting_length -= local_message.length; + } +@@ -3598,20 +3599,22 @@ agent_recv_message_unlocked ( + } + } + } else { +- retval = nice_socket_recv_messages (nicesock, message, 1); ++ sockret = nice_socket_recv_messages (nicesock, message, 1); + } + +- if (retval == 0) { ++ if (sockret == 0) { + retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ + nice_debug_verbose ("%s: Agent %p: no message available on read attempt", + G_STRFUNC, agent); + goto done; +- } else if (retval < 0) { ++ } else if (sockret < 0) { + nice_debug ("Agent %p: %s returned %d, errno (%d) : %s", +- agent, G_STRFUNC, retval, errno, g_strerror (errno)); ++ agent, G_STRFUNC, sockret, errno, g_strerror (errno)); + + retval = RECV_ERROR; + goto done; ++ } else { ++ retval = sockret; + } + + if (retval == RECV_OOB || message->length == 0) { +-- +2.13.6 + + +From 4e605885c9dcaeb3ee443ec902c9c9189b19043f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 16:16:46 -0400 +Subject: [PATCH 08/70] agent: Remove impossible case + +--- + agent/agent.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 7b8a9fc..77f27e3 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3617,7 +3617,8 @@ agent_recv_message_unlocked ( + retval = sockret; + } + +- if (retval == RECV_OOB || message->length == 0) { ++ g_assert (retval != RECV_OOB); ++ if (message->length == 0) { + retval = RECV_OOB; + nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC, + agent); +-- +2.13.6 + + +From efc6a9be8cb34c899f0454c32e8a1e62b38df474 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 18:42:57 -0400 +Subject: [PATCH 09/70] tests: Use automake test-driver for valgrind + +This fixes the valgrind integration with the new test drivers. +--- + common.mk | 4 +- + scripts/valgrind-test-driver | 162 +++++++++++++++++++++++++++++++++++++++++++ + scripts/valgrind.sh | 28 -------- + 3 files changed, 165 insertions(+), 29 deletions(-) + create mode 100755 scripts/valgrind-test-driver + delete mode 100755 scripts/valgrind.sh + +diff --git a/common.mk b/common.mk +index e2ca3f4..b16380d 100644 +--- a/common.mk ++++ b/common.mk +@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice + + + check-valgrind: +- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check ++ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check ++ ++LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver + + .PHONY: check-valgrind +diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver +new file mode 100755 +index 0000000..5b660ee +--- /dev/null ++++ b/scripts/valgrind-test-driver +@@ -0,0 +1,162 @@ ++#! /bin/sh ++# test-driver - basic testsuite driver script. ++ ++scriptversion=2017-04-04.22; # UTC ++ ++# Copyright (C) 2011-2014 Free Software Foundation, Inc. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2, or (at your option) ++# any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <
http://www.gnu.org/licenses/
>. ++ ++# As a special exception to the GNU General Public License, if you ++# distribute this file as part of a program that contains a ++# configuration script generated by Autoconf, you may include it under ++# the same distribution terms that you use for the rest of that program. ++ ++# This file is maintained in Automake, please report ++# bugs to <bug-automake(a)gnu.org> or send patches to ++# <automake-patches(a)gnu.org>. ++ ++# Make unconditional expansion of undefined variables an error. This ++# helps a lot in preventing typo-related bugs. ++set -u ++ ++usage_error () ++{ ++ echo "$0: $*" >&2 ++ print_usage >&2 ++ exit 2 ++} ++ ++print_usage () ++{ ++ cat <<END ++Usage: ++ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH ++ [--expect-failure={yes|no}] [--color-tests={yes|no}] ++ [--enable-hard-errors={yes|no}] [--] ++ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] ++The '--test-name', '--log-file' and '--trs-file' options are mandatory. ++END ++} ++ ++test_name= # Used for reporting. ++log_file= # Where to save the output of the test script. ++trs_file= # Where to save the metadata of the test run. ++expect_failure=no ++color_tests=no ++enable_hard_errors=yes ++while test $# -gt 0; do ++ case $1 in ++ --help) print_usage; exit $?;; ++ --version) echo "test-driver $scriptversion"; exit $?;; ++ --test-name) test_name=$2; shift;; ++ --log-file) log_file=$2; shift;; ++ --trs-file) trs_file=$2; shift;; ++ --color-tests) color_tests=$2; shift;; ++ --expect-failure) expect_failure=$2; shift;; ++ --enable-hard-errors) enable_hard_errors=$2; shift;; ++ --) shift; break;; ++ -*) usage_error "invalid option: '$1'";; ++ *) break;; ++ esac ++ shift ++done ++ ++missing_opts= ++test x"$test_name" = x && missing_opts="$missing_opts --test-name" ++test x"$log_file" = x && missing_opts="$missing_opts --log-file" ++test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" ++if test x"$missing_opts" != x; then ++ usage_error "the following mandatory options are missing:$missing_opts" ++fi ++ ++if test $# -eq 0; then ++ usage_error "missing argument" ++fi ++ ++if test $color_tests = yes; then ++ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. ++ red='[0;31m' # Red. ++ grn='[0;32m' # Green. ++ lgn='[1;32m' # Light green. ++ blu='[1;34m' # Blue. ++ mgn='[0;35m' # Magenta. ++ std='[m' # No color. ++else ++ red= grn= lgn= blu= mgn= std= ++fi ++ ++do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' ++trap "st=129; $do_exit" 1 ++trap "st=130; $do_exit" 2 ++trap "st=141; $do_exit" 13 ++trap "st=143; $do_exit" 15 ++ ++# Test script is run here. ++top_srcdir="`dirname $0`/.." ++tests_dir="${top_srcdir}/tests" ++ ++USE_VALGRIND="`printenv USE_VALGRIND`" ++ ++if test "x${USE_VALGRIND}" = "x1"; then ++ ${top_srcdir}/libtool --mode=execute valgrind \ ++ --leak-check=full \ ++ --show-reachable=no \ ++ --error-exitcode=1 \ ++ --suppressions=$tests_dir/libnice.supp \ ++ --num-callers=30 "$@" >$log_file 2>&1 ++else ++ "$@" >$log_file 2>&1 ++fi ++estatus=$? ++ ++if test $enable_hard_errors = no && test $estatus -eq 99; then ++ tweaked_estatus=1 ++else ++ tweaked_estatus=$estatus ++fi ++ ++case $tweaked_estatus:$expect_failure in ++ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; ++ 0:*) col=$grn res=PASS recheck=no gcopy=no;; ++ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; ++ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; ++ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; ++ *:*) col=$red res=FAIL recheck=yes gcopy=yes;; ++esac ++ ++# Report the test outcome and exit status in the logs, so that one can ++# know whether the test passed or failed simply by looking at the '.log' ++# file, without the need of also peaking into the corresponding '.trs' ++# file (automake bug#11814). ++echo "$res $test_name (exit status: $estatus)" >>$log_file ++ ++# Report outcome to console. ++echo "${col}${res}${std}: $test_name" ++ ++# Register the test result, and other relevant metadata. ++echo ":test-result: $res" > $trs_file ++echo ":global-test-result: $res" >> $trs_file ++echo ":recheck: $recheck" >> $trs_file ++echo ":copy-in-global-log: $gcopy" >> $trs_file ++ ++# Local Variables: ++# mode: shell-script ++# sh-indentation: 2 ++# eval: (add-hook 'write-file-hooks 'time-stamp) ++# time-stamp-start: "scriptversion=" ++# time-stamp-format: "%:y-%02m-%02d.%02H" ++# time-stamp-time-zone: "UTC" ++# time-stamp-end: "; # UTC" ++# End: +diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh +deleted file mode 100755 +index 2864b6f..0000000 +--- a/scripts/valgrind.sh ++++ /dev/null +@@ -1,28 +0,0 @@ +-#!/bin/sh +- +-export G_SLICE=always-malloc +-export G_DEBUG=gc-friendly +- +-tests_dir="`dirname $0`/../tests" +- +-report=`libtool --mode=execute valgrind \ +- --leak-check=full \ +- --show-reachable=no \ +- --error-exitcode=1 \ +- --suppressions=$tests_dir/libnice.supp \ +- --num-callers=30 \ +- $1 2>&1` +- +-#if echo "$report" | grep -q ==; then +-if test $? != 0; then +- echo "$report" +- exit 1 +-fi +- +-if echo "$report" | grep -q "definitely lost"; then +- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then +- echo "$report" +- exit 1 +- fi +-fi +- +-- +2.13.6 + + +From ae6d939e48366b80570d713b83334191b0982e71 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 20:34:05 -0400 +Subject: [PATCH 10/70] debug: Use libnice-verbose, not libnice-nice-verbose + +--- + agent/debug.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/debug.c b/agent/debug.c +index e1a298c..84fee94 100644 +--- a/agent/debug.c ++++ b/agent/debug.c +@@ -102,7 +102,7 @@ void nice_debug_init (void) + flags |= g_parse_debug_string (gflags_string, gkeys, 4); + if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) + flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; +- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { ++ if (gflags_string && strstr (gflags_string, "libnice-verbose")) { + flags |= NICE_DEBUG_NICE_VERBOSE; + } + +-- +2.13.6 + + +From 10c557f23f8337f1304fff27bd85d2eb713cb249 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 5 Apr 2017 17:01:35 -0400 +Subject: [PATCH 11/70] test-credentials: Fix leak + +--- + tests/test-credentials.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/test-credentials.c b/tests/test-credentials.c +index 1de4e49..f1ea89d 100644 +--- a/tests/test-credentials.c ++++ b/tests/test-credentials.c +@@ -184,6 +184,8 @@ int main (void) + nice_agent_get_local_credentials (lagent, 1, &ufrag, &password); + g_assert (g_strcmp0("unicorns", ufrag) == 0); + g_assert (g_strcmp0("awesome", password) == 0); ++ g_free (ufrag); ++ g_free (password); + + nice_agent_gather_candidates (lagent, 1); + nice_agent_gather_candidates (ragent, 1); +-- +2.13.6 + + +From 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 14:41:51 -0400 +Subject: [PATCH 12/70] candidate: Add equality check function + +Add a function that can check if two candidates point to the same place. + +https://phabricator.freedesktop.org/T104 + +Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> +Differential Revision:
https://phabricator.freedesktop.org/D1715
+--- + agent/candidate.c | 11 +++++++++++ + agent/candidate.h | 18 +++++++++++++++++- + docs/reference/libnice/libnice-docs.xml | 4 ++++ + docs/reference/libnice/libnice-sections.txt | 1 + + nice/libnice.sym | 1 + + 5 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/agent/candidate.c b/agent/candidate.c +index 27966ef..85f8f65 100644 +--- a/agent/candidate.c ++++ b/agent/candidate.c +@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate) + + return copy; + } ++ ++NICEAPI_EXPORT gboolean ++nice_candidate_equal_target (const NiceCandidate *candidate1, ++ const NiceCandidate *candidate2) ++{ ++ g_return_val_if_fail (candidate1 != NULL, FALSE); ++ g_return_val_if_fail (candidate2 != NULL, FALSE); ++ ++ return (candidate1->transport == candidate2->transport && ++ nice_address_equal (&candidate1->addr, &candidate2->addr)); ++} +diff --git a/agent/candidate.h b/agent/candidate.h +index fadfce3..e556c16 100644 +--- a/agent/candidate.h ++++ b/agent/candidate.h +@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate); + NiceCandidate * + nice_candidate_copy (const NiceCandidate *candidate); + +-GType nice_candidate_get_type (void); ++/** ++ * nice_candidate_equal_target: ++ * @candidate1: A candidate ++ * @candidate2: A candidate ++ * ++ * Verifies that the candidates point to the same place, meaning they have ++ * the same transport and the same address. It ignores all other aspects. ++ * ++ * Returns: %TRUE if the candidates point to the same place ++ * ++ * Since: 0.1.15 ++ */ ++gboolean ++nice_candidate_equal_target (const NiceCandidate *candidate1, ++ const NiceCandidate *candidate2); ++ ++ GType nice_candidate_get_type (void); + + /** + * NICE_TYPE_CANDIDATE: +diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml +index 53487bc..391be1a 100644 +--- a/docs/reference/libnice/libnice-docs.xml ++++ b/docs/reference/libnice/libnice-docs.xml +@@ -105,6 +105,10 @@ + <title>Index of new symbols in 0.1.14</title> + <xi:include href="xml/api-index-0.1.14.xml"><xi:fallback/></xi:include> + </index> ++ <index role="0.1.15"> ++ <title>Index of new symbols in 0.1.15</title> ++ <xi:include href="xml/api-index-0.1.15.xml"><xi:fallback/></xi:include> ++ </index> + <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + </part> + </book> +diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt +index d377257..88a6cd2 100644 +--- a/docs/reference/libnice/libnice-sections.txt ++++ b/docs/reference/libnice/libnice-sections.txt +@@ -76,6 +76,7 @@ NICE_CANDIDATE_MAX_FOUNDATION + nice_candidate_new + nice_candidate_free + nice_candidate_copy ++nice_candidate_equal_target + <SUBSECTION Standard> + NICE_TYPE_CANDIDATE + nice_candidate_get_type +diff --git a/nice/libnice.sym b/nice/libnice.sym +index b04bb95..1e522ad 100644 +--- a/nice/libnice.sym ++++ b/nice/libnice.sym +@@ -58,6 +58,7 @@ nice_agent_set_software + nice_agent_set_stream_name + nice_agent_set_stream_tos + nice_candidate_copy ++nice_candidate_equal_target + nice_candidate_free + nice_candidate_new + nice_component_state_to_string +-- +2.13.6 + + +From ffc7fddac42728bac6e4753a17bc52e5e610ae8b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 4 Apr 2017 21:27:39 -0400 +Subject: [PATCH 13/70] agent: Drop packets not from validated addresses + +This is required by the WebRTC spec. + +Remove test-mainloop as it doesnt even try to do +a negotiation. + +https://phabricator.freedesktop.org/T104 + +Differential Revision:
https://phabricator.freedesktop.org/D1716
+--- + agent/agent-priv.h | 2 + + agent/agent.c | 20 +++++++++- + agent/component.c | 90 +++++++++++++++++++++++++++++++++++++++++ + agent/component.h | 10 +++++ + agent/conncheck.c | 8 +++- + tests/Makefile.am | 1 - + tests/test-mainloop.c | 108 -------------------------------------------------- + 7 files changed, 127 insertions(+), 112 deletions(-) + delete mode 100644 tests/test-mainloop.c + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 4d8c9b8..ada3630 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -114,6 +114,8 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + * MTU and estimated typical sizes of ICE STUN packet */ + #define MAX_STUN_DATAGRAM_PAYLOAD 1300 + ++#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +diff --git a/agent/agent.c b/agent/agent.c +index 77f27e3..eff62f0 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3682,8 +3682,6 @@ agent_recv_message_unlocked ( + if (retval == RECV_OOB) + goto done; + +- agent->media_after_tick = TRUE; +- + /* If the message’s stated length is equal to its actual length, it’s probably + * a STUN message; otherwise it’s probably data. */ + if (stun_message_validate_buffer_length_fast ( +@@ -3715,6 +3713,7 @@ agent_recv_message_unlocked ( + nice_debug ("%s: Valid STUN packet received.", G_STRFUNC); + retval = RECV_OOB; + g_free (big_buf); ++ agent->media_after_tick = TRUE; + goto done; + } + } +@@ -3725,6 +3724,23 @@ agent_recv_message_unlocked ( + g_free (big_buf); + } + ++ if (!nice_component_verify_remote_candidate (component, ++ message->from, nicesock)) { ++ if (nice_debug_is_verbose ()) { ++ gchar str[INET6_ADDRSTRLEN]; ++ ++ nice_address_to_string (message->from, str); ++ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" ++ " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, ++ nice_address_get_port (message->from), nicesock->type); ++ } ++ ++ retval = RECV_OOB; ++ goto done; ++ } ++ ++ agent->media_after_tick = TRUE; ++ + /* Unhandled STUN; try handling TCP data, then pass to the client. */ + if (message->length > 0 && agent->reliable) { + if (!nice_socket_is_reliable (nicesock) && +diff --git a/agent/component.c b/agent/component.c +index a679b30..ba28ffa 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -435,6 +435,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa + component->selected_pair.remote = pair->remote; + component->selected_pair.priority = pair->priority; + component->selected_pair.prflx_priority = pair->prflx_priority; ++ ++ nice_component_add_valid_candidate (component, pair->remote); + } + + /* +@@ -514,6 +516,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, + component->selected_pair.remote = remote; + component->selected_pair.priority = priority; + ++ /* Get into fallback mode where packets from any source is accepted once ++ * this has been called. This is the expected behavior of pre-ICE SIP. ++ */ ++ component->fallback_mode = TRUE; ++ + return local; + } + +@@ -1107,6 +1114,9 @@ nice_component_finalize (GObject *obj) + g_warn_if_fail (cmp->remote_candidates == NULL); + g_warn_if_fail (cmp->incoming_checks == NULL); + ++ g_list_free_full (cmp->valid_candidates, ++ (GDestroyNotify) nice_candidate_free); ++ + g_clear_object (&cmp->tcp); + g_clear_object (&cmp->stop_cancellable); + g_clear_object (&cmp->iostream); +@@ -1421,3 +1431,83 @@ turn_server_unref (TurnServer *turn) + g_slice_free (TurnServer, turn); + } + } ++ ++void ++nice_component_add_valid_candidate (NiceComponent *component, ++ const NiceCandidate *candidate) ++{ ++ guint count = 0; ++ GList *item, *last = NULL; ++ ++ for (item = component->valid_candidates; item; item = item->next) { ++ NiceCandidate *cand = item->data; ++ ++ last = item; ++ count++; ++ if (nice_candidate_equal_target (cand, candidate)) ++ return; ++ } ++ ++ /* New candidate */ ++ ++ if (nice_debug_is_enabled ()) { ++ char str[INET6_ADDRSTRLEN]; ++ nice_address_to_string (&candidate->addr, str); ++ nice_debug ("Agent %p : %d:%d Adding valid source" ++ " candidate: %s:%d trans: %d\n", component->agent, ++ candidate->stream_id, candidate->component_id, str, ++ nice_address_get_port (&candidate->addr), candidate->transport); ++ } ++ ++ component->valid_candidates = g_list_prepend ( ++ component->valid_candidates, nice_candidate_copy (candidate)); ++ ++ /* Delete the last one to make sure we don't have a list that is too long, ++ * the candidates are not freed on ICE restart as this would be more complex, ++ * we just keep the list not too long. ++ */ ++ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) { ++ NiceCandidate *cand = last->data; ++ ++ component->valid_candidates = g_list_delete_link ( ++ component->valid_candidates, last); ++ nice_candidate_free (cand); ++ } ++} ++ ++gboolean ++nice_component_verify_remote_candidate (NiceComponent *component, ++ const NiceAddress *address, NiceSocket *nicesock) ++{ ++ GList *item; ++ ++ if (component->fallback_mode) ++ return TRUE; ++ ++ for (item = component->valid_candidates; item; item = item->next) { ++ NiceCandidate *cand = item->data; ++ ++ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD && ++ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && ++ nice_address_equal (address, &cand->addr)) { ++ /* fast return if it's already the first */ ++ if (item == component->valid_candidates) ++ return TRUE; ++ ++ /* Put the current candidate at the top so that in the normal use-case, ++ * this function becomes O(1). ++ */ ++ component->valid_candidates = g_list_remove_link ( ++ component->valid_candidates, item); ++ component->valid_candidates = g_list_concat (item, ++ component->valid_candidates); ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} +diff --git a/agent/component.h b/agent/component.h +index 6712794..a8a1222 100644 +--- a/agent/component.h ++++ b/agent/component.h +@@ -159,12 +159,14 @@ struct _NiceComponent { + NiceComponentState state; + GSList *local_candidates; /* list of NiceCandidate objs */ + GSList *remote_candidates; /* list of NiceCandidate objs */ ++ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */ + GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */ + guint socket_sources_age; /* incremented when socket_sources changes */ + GSList *incoming_checks; /* list of IncomingCheck objs */ + GList *turn_servers; /* List of TurnServer objs */ + CandidatePair selected_pair; /* independent from checklists, + see ICE 11.1. "Sending Media" (ID-19) */ ++ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */ + NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */ + NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */ + /* I/O handling. The main context must always be non-NULL, and is used for all +@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn); + void + turn_server_unref (TurnServer *turn); + ++void ++nice_component_add_valid_candidate (NiceComponent *component, ++ const NiceCandidate *candidate); ++ ++gboolean ++nice_component_verify_remote_candidate (NiceComponent *component, ++ const NiceAddress *address, NiceSocket *nicesock); ++ + + G_END_DECLS + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 1dc13dd..7ffa3db 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2627,6 +2627,7 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + p->state = NICE_CHECK_SUCCEEDED; + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + priv_conn_check_unfreeze_related (agent, stream, p); ++ nice_component_add_valid_candidate (component, remote_candidate); + } + else { + if (!local_cand) { +@@ -2652,8 +2653,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + + /* note: this is same as "adding to VALID LIST" in the spec + text */ +- if (new_pair) ++ if (new_pair) { + new_pair->valid = TRUE; ++ nice_component_add_valid_candidate (component, remote_candidate); ++ } + + return new_pair; + } +@@ -2739,6 +2742,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + nice_debug ("Agent %p : Mapped address not found." + " conncheck %p SUCCEEDED.", agent, p); + priv_conn_check_unfreeze_related (agent, stream, p); ++ nice_component_add_valid_candidate (component, p->remote); + } else { + ok_pair = priv_process_response_check_for_reflexive (agent, + stream, component, p, sockptr, &sockaddr.addr, +@@ -3654,6 +3658,8 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + } + } + ++ nice_component_add_valid_candidate (component, remote_candidate); ++ + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 7bfe075..d24a2aa 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -45,7 +45,6 @@ check_PROGRAMS = \ + test-send-recv \ + test-socket-is-based-on \ + test-priority \ +- test-mainloop \ + test-fullmode \ + test-restart \ + test-fallback \ +diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c +deleted file mode 100644 +index 7c52daa..0000000 +--- a/tests/test-mainloop.c ++++ /dev/null +@@ -1,108 +0,0 @@ +-/* +- * This file is part of the Nice GLib ICE library. +- * +- * (C) 2006, 2007 Collabora Ltd. +- * Contact: Dafydd Harries +- * (C) 2006, 2007 Nokia Corporation. All rights reserved. +- * Contact: Kai Vehmanen +- * +- * The contents of this file are subject to the Mozilla Public License Version +- * 1.1 (the "License"); you may not use this file except in compliance with +- * the License. You may obtain a copy of the License at +- *
http://www.mozilla.org/MPL/
+- * +- * Software distributed under the License is distributed on an "AS IS" basis, +- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +- * for the specific language governing rights and limitations under the +- * License. +- * +- * The Original Code is the Nice GLib ICE library. +- * +- * The Initial Developers of the Original Code are Collabora Ltd and Nokia +- * Corporation. All Rights Reserved. +- * +- * Contributors: +- * Dafydd Harries, Collabora Ltd. +- * Kai Vehmanen, Nokia +- * +- * Alternatively, the contents of this file may be used under the terms of the +- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which +- * case the provisions of LGPL are applicable instead of those above. If you +- * wish to allow use of your version of this file only under the terms of the +- * LGPL and not to allow others to use your version of this file under the +- * MPL, indicate your decision by deleting the provisions above and replace +- * them with the notice and other provisions required by the LGPL. If you do +- * not delete the provisions above, a recipient may use your version of this +- * file under either the MPL or the LGPL. +- */ +-#ifdef HAVE_CONFIG_H +-# include <config.h> +-#endif +- +-#include <string.h> +- +-#include <nice/nice.h> +-#include "socket/socket.h" +- +-static GMainLoop *loop = NULL; +- +-static void +-recv_cb ( +- NiceAgent *agent, +- guint stream_id, +- guint component_id, +- guint len, +- gchar *buf, +- gpointer data) +-{ +- g_assert (agent != NULL); +- g_assert (stream_id == 1); +- g_assert (component_id == 1); +- g_assert (len == 6); +- g_assert (0 == strncmp (buf, "\x80hello", len)); +- g_assert (42 == GPOINTER_TO_UINT (data)); +- g_main_loop_quit (loop); +-} +- +-int +-main (void) +-{ +- NiceAgent *agent; +- NiceAddress addr; +- guint stream; +- +- nice_address_init (&addr); +- +- loop = g_main_loop_new (NULL, FALSE); +- +- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245); +- nice_address_set_ipv4 (&addr, 0x7f000001); +- nice_agent_add_local_address (agent, &addr); +- stream = nice_agent_add_stream (agent, 1); +- nice_agent_gather_candidates (agent, stream); +- +- // attach to default main context +- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP, +- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42)); +- +- { +- NiceCandidate *candidate; +- GSList *candidates, *i; +- +- candidates = nice_agent_get_local_candidates (agent, 1, 1); +- candidate = candidates->data; +- +- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello"); +- for (i = candidates; i; i = i->next) +- nice_candidate_free ((NiceCandidate *) i->data); +- g_slist_free (candidates); +- } +- +- g_main_loop_run (loop); +- +- nice_agent_remove_stream (agent, stream); +- g_object_unref (agent); +- +- return 0; +-} +- +-- +2.13.6 + + +From 8fc22b0034d04cbc222e0637152b1cee2879eef3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 5 Apr 2017 17:43:26 -0400 +Subject: [PATCH 14/70] tests_: Add test to verify that only packets from + validated addresses pass + +https://phabricator.freedesktop.org/T104 + +Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> +Differential Revision:
https://phabricator.freedesktop.org/D1717
+--- + tests/Makefile.am | 5 +- + tests/test-drop-invalid.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 521 insertions(+), 1 deletion(-) + create mode 100644 tests/test-drop-invalid.c + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index d24a2aa..62d5d64 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -54,7 +54,8 @@ check_PROGRAMS = \ + test-tcp \ + test-icetcp \ + test-credentials \ +- test-turn ++ test-turn \ ++ test-drop-invalid + + dist_check_SCRIPTS = \ + check-test-fullmode-with-stun.sh \ +@@ -128,6 +129,8 @@ test_credentials_LDADD = $(COMMON_LDADD) + + test_turn_LDADD = $(COMMON_LDADD) + ++test_drop_invalid_LDADD = $(COMMON_LDADD) ++ + test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) + test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) + +diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c +new file mode 100644 +index 0000000..97e3586 +--- /dev/null ++++ b/tests/test-drop-invalid.c +@@ -0,0 +1,517 @@ ++/* ++ * This file is part of the Nice GLib ICE library. ++ * ++ * Unit test for ICE full-mode related features. ++ * ++ * (C) 2007 Nokia Corporation. All rights reserved. ++ * Contact: Kai Vehmanen ++ * (C) 2017 Collabora Ltd ++ * Contact: Olivier Crete <olivier.crete(a)collabora.com> ++ * ++ * The contents of this file are subject to the Mozilla Public License Version ++ * 1.1 (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ *
http://www.mozilla.org/MPL/
++ * ++ * Software distributed under the License is distributed on an "AS IS" basis, ++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ++ * for the specific language governing rights and limitations under the ++ * License. ++ * ++ * The Original Code is the Nice GLib ICE library. ++ * ++ * The Initial Developers of the Original Code are Collabora Ltd and Nokia ++ * Corporation. All Rights Reserved. ++ * ++ * Contributors: ++ * Kai Vehmanen, Nokia ++ * ++ * Alternatively, the contents of this file may be used under the terms of the ++ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which ++ * case the provisions of LGPL are applicable instead of those above. If you ++ * wish to allow use of your version of this file only under the terms of the ++ * LGPL and not to allow others to use your version of this file under the ++ * MPL, indicate your decision by deleting the provisions above and replace ++ * them with the notice and other provisions required by the LGPL. If you do ++ * not delete the provisions above, a recipient may use your version of this ++ * file under either the MPL or the LGPL. ++ */ ++#ifdef HAVE_CONFIG_H ++# include <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include "socket/socket.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++ ++ ++static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static guint global_components_ready = 0; ++static guint global_components_ready_exit = 0; ++static guint global_components_failed = 0; ++static guint global_components_failed_exit = 0; ++static GMainLoop *global_mainloop = NULL; ++static gboolean global_lagent_gathering_done = FALSE; ++static gboolean global_ragent_gathering_done = FALSE; ++static gboolean global_lagent_ibr_received = FALSE; ++static gboolean global_ragent_ibr_received = FALSE; ++static int global_lagent_cands = 0; ++static int global_ragent_cands = 0; ++static gint global_ragent_read = 0; ++static guint global_exit_when_ibr_received = 0; ++ ++static void priv_print_global_status (void) ++{ ++ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done); ++ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]); ++ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]); ++ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands); ++} ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)buf; ++ ++ /* Core of the test ++ * Assert on any unreleated packet received. This would include anything ++ * send before the negotiation is over. ++ */ ++ g_assert (len == 16); ++ g_assert (strncmp ("1234567812345678", buf, 16) == 0); ++ ++ if (component_id == 2) ++ return; ++ ++ if (GPOINTER_TO_UINT (user_data) == 2) { ++ g_debug ("right agent received %d bytes, stopping mainloop", len); ++ global_ragent_read = len; ++ g_main_loop_quit (global_mainloop); ++ } ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_gathering_done = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_gathering_done = TRUE; ++ ++ if (global_lagent_gathering_done && ++ global_ragent_gathering_done) ++ g_main_loop_quit (global_mainloop); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; ++} ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ gboolean ready_to_connected = FALSE; ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) { ++ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_lagent_state[component_id - 1] = state; ++ } else if (GPOINTER_TO_UINT (data) == 2) { ++ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_ragent_state[component_id - 1] = state; ++ } ++ ++ if (state == NICE_COMPONENT_STATE_READY) ++ global_components_ready++; ++ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) ++ global_components_ready--; ++ if (state == NICE_COMPONENT_STATE_FAILED) ++ global_components_failed++; ++ ++ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit); ++ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit); ++ ++ /* signal status via a global variable */ ++ if (global_components_ready == global_components_ready_exit && ++ global_components_failed == global_components_failed_exit) { ++ g_debug ("Components ready/failed achieved. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ return; ++ } ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; (void)component_id; ++} ++ ++static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, ++ gchar *lfoundation, gchar* rfoundation, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ ++global_lagent_cands; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ ++global_ragent_cands; ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; ++} ++ ++static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, ++ gchar *foundation, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation; ++} ++ ++static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_ibr_received = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_ibr_received = TRUE; ++ ++ if (global_exit_when_ibr_received) { ++ g_debug ("Received initial binding request. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ } ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL; ++ GSList *peer_cands = NULL; ++ GSList *item1, *item2; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ peer_cands = nice_agent_get_local_candidates (to, to_stream, component); ++ ++ /* ++ * Core of the test: ++ * ++ * Send packets that shoudl be dropped. ++ */ ++ ++ for (item1 = cands; item1; item1 = item1->next) { ++ NiceCandidate *cand = item1->data; ++ NiceSocket *nicesock = cand->sockptr; ++ ++ g_assert (nicesock); ++ ++ for (item2 = peer_cands; item2; item2 = item2->next) { ++ NiceCandidate *target_cand = item2->data; ++ ++ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB"); ++ } ++ ++ } ++ ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free); ++ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free); ++} ++ ++static void set_credentials (NiceAgent *lagent, guint lstream, ++ NiceAgent *ragent, guint rstream) ++{ ++ gchar *ufrag = NULL, *password = NULL; ++ ++ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++} ++ ++static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed) ++{ ++ guint ls_id, rs_id; ++ gint ret; ++ ++ /* XXX: dear compiler, this is for you */ ++ (void)baseaddr; ++ ++ /* step: initialize variables modified by the callbacks */ ++ global_components_ready = 0; ++ global_components_ready_exit = ready; ++ global_components_failed = 0; ++ global_components_failed_exit = failed; ++ global_lagent_gathering_done = FALSE; ++ global_ragent_gathering_done = FALSE; ++ global_lagent_ibr_received = ++ global_ragent_ibr_received = FALSE; ++ global_lagent_cands = ++ global_ragent_cands = 0; ++ ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ ++ /* step: add one stream, with RTP+RTCP components, to each agent */ ++ ls_id = nice_agent_add_stream (lagent, 2); ++ ++ rs_id = nice_agent_add_stream (ragent, 2); ++ g_assert (ls_id > 0); ++ g_assert (rs_id > 0); ++ ++ /* Gather candidates and test nice_agent_set_port_range */ ++ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000); ++ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001); ++ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345); ++ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001); ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE); ++ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL); ++ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL); ++ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); ++ ++ { ++ GSList *cands = NULL, *i; ++ NiceCandidate *cand = NULL; ++ ++ cands = nice_agent_get_local_candidates (lagent, ls_id, 1); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10000); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (lagent, ls_id, 2); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10001); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (ragent, rs_id, 1); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 12345); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (ragent, rs_id, 2); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10002); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ } ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (2)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (2)); ++ ++ /* step: run mainloop until local candidates are ready ++ * (see timer_cb() above) */ ++ if (global_lagent_gathering_done != TRUE || ++ global_ragent_gathering_done != TRUE) { ++ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'..."); ++ g_main_loop_run (global_mainloop); ++ g_assert (global_lagent_gathering_done == TRUE); ++ g_assert (global_ragent_gathering_done == TRUE); ++ } ++ ++ set_credentials (lagent, ls_id, ragent, rs_id); ++ ++ /* step: pass the remote candidates to agents */ ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP); ++ ++ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed..."); ++ ++ /* step: run the mainloop until connectivity checks succeed ++ * (see timer_cb() above) */ ++ g_main_loop_run (global_mainloop); ++ ++ /* note: verify that STUN binding requests were sent */ ++ g_assert (global_lagent_ibr_received == TRUE); ++ g_assert (global_ragent_ibr_received == TRUE); ++ ++ /* note: Send a packet from another address */ ++ /* These should also be ignored */ ++ { ++ NiceCandidate *local_cand = NULL; ++ NiceCandidate *remote_cand = NULL; ++ NiceSocket *tmpsock; ++ ++ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand, ++ &remote_cand)); ++ g_assert (local_cand); ++ g_assert (remote_cand); ++ ++ tmpsock = nice_udp_bsd_socket_new (NULL); ++ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD"); ++ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE"); ++ nice_socket_free (tmpsock); ++ } ++ ++ /* note: test payload send and receive */ ++ global_ragent_read = 0; ++ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"); ++ g_assert (ret != -1); ++ g_debug ("Sent %d bytes", ret); ++ g_assert (ret == 16); ++ while (global_ragent_read != 16) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_ragent_read == 16); ++ ++ g_debug ("test-drop-invalid: Ran mainloop, removing streams..."); ++ ++ /* step: clean up resources and exit */ ++ ++ nice_agent_remove_stream (lagent, ls_id); ++ nice_agent_remove_stream (ragent, rs_id); ++ ++ return 0; ++} ++ ++int main (void) ++{ ++ NiceAgent *lagent, *ragent; /* agent's L and R */ ++ NiceAddress baseaddr; ++ int result; ++ guint timer_id; ++ ++#ifdef G_OS_WIN32 ++ WSADATA w; ++ ++ WSAStartup(0x0202, &w); ++#endif ++ ++ global_mainloop = g_main_loop_new (NULL, FALSE); ++ ++ /* step: create the agents L and R */ ++ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_RFC5245); ++ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_RFC5245); ++ ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ ++ ++ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent"); ++ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent"); ++ ++ /* step: add a timer to catch state changes triggered by signals */ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ /* step: specify which local interface to use */ ++ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1")) ++ g_assert_not_reached (); ++ nice_agent_add_local_address (lagent, &baseaddr); ++ nice_agent_add_local_address (ragent, &baseaddr); ++ ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-candidate", ++ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "new-candidate", ++ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received", ++ G_CALLBACK (cb_initial_binding_request_received), ++ GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received", ++ G_CALLBACK (cb_initial_binding_request_received), ++ GUINT_TO_POINTER (2)); ++ ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ ++ ++ /* step: run test the first time */ ++ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time"); ++ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0); ++ priv_print_global_status (); ++ g_assert (result == 0); ++ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY); ++ /* When using TURN, we get peer reflexive candidates for the host cands ++ that we removed so we can get another new_selected_pair signal later ++ depending on timing/racing, we could double (or not) the amount we expected ++ */ ++ ++ /* note: verify that correct number of local candidates were reported */ ++ g_assert (global_lagent_cands == 2); ++ g_assert (global_ragent_cands == 2); ++ ++ g_object_unref (lagent); ++ g_object_unref (ragent); ++ ++ g_main_loop_unref (global_mainloop); ++ global_mainloop = NULL; ++ ++ g_source_remove (timer_id); ++#ifdef G_OS_WIN32 ++ WSACleanup(); ++#endif ++ return result; ++} +-- +2.13.6 + + +From f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 11 Apr 2017 16:42:55 -0400 +Subject: [PATCH 15/70] conncheck: Check the controlling state when the req was + sent + +It was checking when the pair was created, but the role may have +already changed when the request is sent. +--- + agent/conncheck.c | 30 +++++++++++++++++++----------- + agent/conncheck.h | 1 - + 2 files changed, 19 insertions(+), 12 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 7ffa3db..5501c2b 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1666,7 +1666,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + tmpbuf2, nice_address_get_port (&pair->remote->addr)); + } + pair->nominated = use_candidate; +- pair->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); + +@@ -2531,7 +2530,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->priority = nice_candidate_pair_priority (pair->remote->priority, + pair->local->priority); + pair->nominated = FALSE; +- pair->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local_cand)); + nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); +@@ -2777,16 +2775,26 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { + /* case: role conflict error, need to restart with new role */ + nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- /* note: our role might already have changed due to an +- * incoming request, but if not, change role now; +- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ +- priv_check_for_role_conflict (agent, !p->controlling); + +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_WAITING; +- priv_add_pair_to_triggered_check_queue (agent, p); +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ if (p->stun_message.buffer != NULL) { ++ guint64 tie; ++ gboolean controlled_mode; ++ ++ /* note: our role might already have changed due to an ++ * incoming request, but if not, change role now; ++ * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ ++ controlled_mode = (stun_message_find64 (&p->stun_message, ++ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == ++ STUN_MESSAGE_RETURN_SUCCESS); ++ ++ priv_check_for_role_conflict (agent, controlled_mode); ++ ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; ++ p->state = NICE_CHECK_WAITING; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ } + trans_found = TRUE; + } else { + /* case: STUN error, the check STUN context was freed */ +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 10319cc..c204475 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -85,7 +85,6 @@ struct _CandidateCheckPair + gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; + NiceCheckState state; + gboolean nominated; +- gboolean controlling; + gboolean timer_restarted; + gboolean valid; + guint64 priority; +-- +2.13.6 + + +From b0538d8c51f65019867b56a45cf90a70bef38f01 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 11 Apr 2017 18:31:21 -0400 +Subject: [PATCH 16/70] agent: Ignore remote candidate of non-accepted types + +If we disable ice-tcp or ice-udp, ignore the remote +candidates for those types. +--- + agent/agent.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/agent/agent.c b/agent/agent.c +index eff62f0..77fb1eb 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3112,6 +3112,13 @@ static gboolean priv_add_remote_candidate ( + NiceComponent *component; + NiceCandidate *candidate; + ++ if (transport == NICE_CANDIDATE_TRANSPORT_UDP && ++ !agent->use_ice_udp) ++ return FALSE; ++ if (transport != NICE_CANDIDATE_TRANSPORT_UDP && ++ !agent->use_ice_tcp) ++ return FALSE; ++ + if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) + return FALSE; + +-- +2.13.6 + + +From f6f704c5e8d2193bc67ba2b697c77694e1698c43 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 9 Jun 2016 22:22:33 +0200 +Subject: [PATCH 17/70] stun timer: fix timeout of the last retransmission + +According to RFC 5389, section 7.2.1, a special timeout is applied to +the last retransmission (Rm * RTO), with Rm default value of 16, instead +of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7. + +As spotted by Olivier Crete, stun_timer_* is a public API, that cannot +be changed, and the initial delay (RTO) is not preserved in the +stun_timer_s struct. So we use a hack that implicitely guess Rm from the +number of transmissions Rc, by generalizing the default value of the +spec for Rm and Rc to other values of Rc passed in stun_timer_start( + +According to the spec, with the default value of Rc=7, the last delay +should be (64 * RTO), and it is instead (16 * RTO). So the last delay +can be computed by dividing the penultimate delay by two, instead of +multiplying it by two. + +Differential Revision:
https://phabricator.freedesktop.org/D1108
+--- + stun/usages/timer.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/stun/usages/timer.c b/stun/usages/timer.c +index 2862ab8..5370cba 100644 +--- a/stun/usages/timer.c ++++ b/stun/usages/timer.c +@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer) + if (timer->retransmissions >= timer->max_retransmissions) + return STUN_USAGE_TIMER_RETURN_TIMEOUT; + +- add_delay (&timer->deadline, timer->delay *= 2); ++ if (timer->retransmissions == timer->max_retransmissions - 1) ++ timer->delay = timer->delay / 2; ++ else ++ timer->delay = timer->delay * 2; ++ add_delay (&timer->deadline, timer->delay); + timer->retransmissions++; + return STUN_USAGE_TIMER_RETURN_RETRANSMIT; + } +-- +2.13.6 + + +From 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 5 Apr 2016 21:32:39 +0200 +Subject: [PATCH 18/70] agent: do not create a GSource for UDP TURN socket + +With this patch, we don't create a new GSource for udp-turn socket, +because it would duplicate the packets already received on the base UDP +socket, as the underlying GSocket is the same. This is a race condition, +because an UDP packet arriving on the base socket, may randomly be +handled by the GSource callback created for the base socket (udp-bsd) of +the callback created for the udp-turn socket. Moreover this callback +already knows how to parse UDP datagrams received from a known turn +server. + +This patch also prevents a subtle bug, when a STUN request is received +directly from a peer, is handled by the udp turn socket. If the agent +already has a valid permission for this remote candidate, established +for another pair, it will happily send the STUN reply through the turn +relay. This generates a source address mismatch on the peer agent, when +it'll receive the STUN response from the turn relay instead of the +initial address the request has been sent to. + +Differential Revision:
https://phabricator.freedesktop.org/D932
+--- + agent/component.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/agent/component.c b/agent/component.c +index ba28ffa..ab665b6 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context) + if (socket_source->socket->fileno == NULL) + return; + ++ /* Do not create a GSource for UDP turn socket, because it ++ * would duplicate the packets already received on the base ++ * UDP socket. ++ */ ++ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN) ++ return; ++ + /* Create a source. */ + source = g_socket_create_source (socket_source->socket->fileno, + G_IO_IN, NULL); +-- +2.13.6 + + +From d5446a72233eab8501be0b3fb9060c8be3ba034b Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Mon, 1 May 2017 08:51:40 +0100 +Subject: [PATCH 19/70] examples: Stop installing the examples +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There’s no point in installing them; their benefit is in providing +example code to developers. + +Debian doesn’t package them; Fedora packages them in a separate +subpackage which will have to disappear. + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1737
+--- + examples/Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/examples/Makefile.am b/examples/Makefile.am +index 1e7decf..9c80854 100644 +--- a/examples/Makefile.am ++++ b/examples/Makefile.am +@@ -18,7 +18,7 @@ AM_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(GUPNP_CFLAGS) + +-bin_PROGRAMS = simple-example threaded-example sdp-example ++noinst_PROGRAMS = simple-example threaded-example sdp-example + + simple_example_SOURCES = simple-example.c + simple_example_LDADD = $(top_builddir)/agent/libagent.la \ +-- +2.13.6 + + +From b4abda09c79e4ce372a3771300abf568c85c7ff5 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 21 Apr 2016 18:18:59 +0200 +Subject: [PATCH 20/70] interfaces: ignore predefined network interfaces + +Some interfaces, like the one managed by libvirtd to provide a network +bridge to locally hosted virtual machines, can be completely ignored +when gathering ICE candidates. The motivation for adding this +possibility is that, ignoring them doesn't remove capabilities, and +improves the overall speed of the connection check method, by reducing +the number of pairs to be tested. This patch adds the possibility to +define such interfaces in the configuration script. + +Differential Revision:
https://phabricator.freedesktop.org/D948
+--- + agent/interfaces.c | 6 ++++++ + configure.ac | 14 ++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/agent/interfaces.c b/agent/interfaces.c +index 0fa2fd7..a81888e 100644 +--- a/agent/interfaces.c ++++ b/agent/interfaces.c +@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback) + nice_debug ("Ignoring loopback interface"); + g_free (addr_string); + } ++#ifdef IGNORED_IFACE_PREFIX ++ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) { ++ nice_debug ("Ignoring interface %s as it matches prefix %s", ++ ifa->ifa_name, IGNORED_IFACE_PREFIX); ++ g_free (addr_string); ++#endif + } else { + if (nice_interfaces_is_private_ip (ifa->ifa_addr)) + ips = add_ip_to_list (ips, addr_string, TRUE); +diff --git a/configure.ac b/configure.ac +index b39bfe3..98bbc08 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false) + # GObject introspection + GOBJECT_INTROSPECTION_CHECK([1.30.0]) + ++dnl Ignore a specific network interface name prefix from the connection check ++AC_MSG_CHECKING([whether to ignore a specific network interface name prefix]) ++AC_ARG_WITH([ignored-network-interface-prefix], ++ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string], ++ [Ignore network interfaces whose name starts with "string" from the ICE connection ++ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge ++ handled by libvirtd, do not help in finding connectivity.])], ++ [interface_prefix="$withval"]) ++AS_IF([test -n "$interface_prefix"], ++ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"], ++ [Ignore this network interface prefix from the connection check]) ++ AC_MSG_RESULT([yes, $interface_prefix])], ++ [AC_MSG_RESULT([no])]) ++ + AC_CONFIG_MACRO_DIR(m4) + + AC_OUTPUT +-- +2.13.6 + + +From 80c613699786567fd93db74377138600794a86e0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Thu, 8 Jun 2017 16:34:21 -0400 +Subject: [PATCH 21/70] agent: Use base_addr to generate rport in SDP + +Reported by Capricornus (zhushengliang) + +https://phabricator.freedesktop.org/T7763 +--- + agent/agent.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 77fb1eb..1ff09af 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -5690,7 +5690,7 @@ _generate_candidate_sdp (NiceAgent *agent, + g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type)); + if (nice_address_is_valid (&candidate->base_addr) && + !nice_address_equal (&candidate->addr, &candidate->base_addr)) { +- port = nice_address_get_port (&candidate->addr); ++ port = nice_address_get_port (&candidate->base_addr); + nice_address_to_string (&candidate->base_addr, ip4); + g_string_append_printf (sdp, " raddr %s rport %d", ip4, + port == 0 ? 9 : port); +-- +2.13.6 + + +From 8bb210c5af4bcaf342d7fa4fef6034269e976532 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 9 Jun 2016 23:28:43 +0200 +Subject: [PATCH 22/70] stun timer: make properties for stun timer tunables + +Three STUN binding request properties should be customisable. RFC 5245 +describes the retransmission timer of the STUN transaction 'RTO', and +RFC 5389 describes the number of retransmissions to send until a +response is received 'Rc'. The third property is the 'RTO' when +a reliable connection is used. + +RFC 5389 introduces a supplementary property 'Rm' as a multiplier used +to compute the final timeout RTO * Rm. However, this property is not +added in libnice, because this would require breaking the public API for +STUN. Currently, our STUN implementation hardcodes a division by two for +this final timeout. + +Differential Revision:
https://phabricator.freedesktop.org/D1109
+--- + agent/agent-priv.h | 4 ++- + agent/agent.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + agent/conncheck.c | 16 +++++---- + agent/discovery.c | 8 ++--- + stun/usages/timer.h | 6 +++- + 5 files changed, 118 insertions(+), 13 deletions(-) + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index ada3630..162ea63 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -106,7 +106,6 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + + #define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */ + #define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */ +-#define NICE_AGENT_TIMER_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */ + #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */ + + +@@ -132,6 +131,9 @@ struct _NiceAgent + guint timer_ta; /* property: timer Ta */ + guint max_conn_checks; /* property: max connectivity checks */ + gboolean force_relay; /* property: force relay */ ++ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ ++ guint stun_initial_timeout; /* property: stun initial timeout, RTO */ ++ guint stun_reliable_timeout; /* property: stun reliable timeout */ + + GSList *local_addresses; /* list of NiceAddresses for local + interfaces */ +diff --git a/agent/agent.c b/agent/agent.c +index 1ff09af..25d7886 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -113,6 +113,9 @@ enum + PROP_BYTESTREAM_TCP, + PROP_KEEPALIVE_CONNCHECK, + PROP_FORCE_RELAY, ++ PROP_STUN_MAX_RETRANSMISSIONS, ++ PROP_STUN_INITIAL_TIMEOUT, ++ PROP_STUN_RELIABLE_TIMEOUT, + }; + + +@@ -708,6 +711,76 @@ nice_agent_class_init (NiceAgentClass *klass) + FALSE, + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:stun-max-retransmissions ++ * ++ * The maximum number of retransmissions of the STUN binding requests ++ * used in the gathering stage, to find our local candidates, and used ++ * in the connection check stage, to test the validity of each ++ * constructed pair. This property is described as 'Rc' in the RFC ++ * 5389, with a default value of 7. The timeout of each STUN request ++ * is doubled for each retransmission, so the choice of this value has ++ * a direct impact on the time needed to move from the CONNECTED state ++ * to the READY state, and on the time needed to complete the GATHERING ++ * state. ++ * ++ * Since: UNRELEASED ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, ++ g_param_spec_uint ( ++ "stun-max-retransmissions", ++ "STUN Max Retransmissions", ++ "Maximum number of STUN binding requests retransmissions " ++ "described as 'Rc' in the STUN specification.", ++ 1, 99, ++ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * NiceAgent:stun-initial-timeout ++ * ++ * The initial timeout (msecs) of the STUN binding requests ++ * used in the gathering stage, to find our local candidates. ++ * This property is described as 'RTO' in the RFC 5389 and RFC 5245. ++ * This timeout is doubled for each retransmission, until ++ * #NiceAgent:stun-max-retransmissions have been done, ++ * with an exception for the last restransmission, where the timeout is ++ * divided by two instead (RFC 5389 indicates that a customisable ++ * multiplier 'Rm' to 'RTO' should be used). ++ * ++ * Since: UNRELEASED ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, ++ g_param_spec_uint ( ++ "stun-initial-timeout", ++ "STUN Initial Timeout", ++ "STUN timeout in msecs of the initial binding requests used in the " ++ "gathering state, described as 'RTO' in the ICE specification.", ++ 20, 9999, ++ STUN_TIMER_DEFAULT_TIMEOUT, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * NiceAgent:stun-reliable-timeout ++ * ++ * The initial timeout of the STUN binding requests used ++ * for a reliable timer. ++ * ++ * Since: UNRELEASED ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, ++ g_param_spec_uint ( ++ "stun-reliable-timeout", ++ "STUN Reliable Timeout", ++ "STUN timeout in msecs of the initial binding requests used for " ++ "a reliable timer.", ++ 20, 99999, ++ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ + /* install signals */ + + /** +@@ -1187,6 +1260,18 @@ nice_agent_get_property ( + g_value_set_boolean (value, agent->force_relay); + break; + ++ case PROP_STUN_MAX_RETRANSMISSIONS: ++ g_value_set_uint (value, agent->stun_max_retransmissions); ++ break; ++ ++ case PROP_STUN_INITIAL_TIMEOUT: ++ g_value_set_uint (value, agent->stun_initial_timeout); ++ break; ++ ++ case PROP_STUN_RELIABLE_TIMEOUT: ++ g_value_set_uint (value, agent->stun_reliable_timeout); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +@@ -1374,6 +1459,18 @@ nice_agent_set_property ( + agent->force_relay = g_value_get_boolean (value); + break; + ++ case PROP_STUN_MAX_RETRANSMISSIONS: ++ agent->stun_max_retransmissions = g_value_get_uint (value); ++ break; ++ ++ case PROP_STUN_INITIAL_TIMEOUT: ++ agent->stun_initial_timeout = g_value_get_uint (value); ++ break; ++ ++ case PROP_STUN_RELIABLE_TIMEOUT: ++ agent->stun_reliable_timeout = g_value_get_uint (value); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 5501c2b..14fdcd9 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -888,8 +888,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) + agent, buf_len, p->keepalive.stun_message.buffer); + + if (buf_len > 0) { +- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&p->keepalive.timer, ++ agent->stun_initial_timeout, ++ agent->stun_max_retransmissions); + + agent->media_after_tick = FALSE; + +@@ -1116,8 +1117,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand) + } + + if (buffer_len > 0) { +- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&cand->timer, ++ cand->agent->stun_initial_timeout, ++ cand->agent->stun_max_retransmissions); + + /* send the refresh */ + agent_socket_send (cand->nicesock, &cand->server, +@@ -2171,11 +2173,11 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + + if (buffer_len > 0) { + if (nice_socket_is_reliable(pair->sockptr)) { +- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); ++ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); + } else { + stun_timer_start (&pair->timer, + priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ agent->stun_max_retransmissions); + } + + /* TCP-ACTIVE candidate must create a new socket before sending +@@ -2340,7 +2342,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { + stun_timer_start (&p->timer, + priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ agent->stun_max_retransmissions); + p->timer_restarted = TRUE; + } + } +diff --git a/agent/discovery.c b/agent/discovery.c +index 7a890a0..4cc99c2 100644 +--- a/agent/discovery.c ++++ b/agent/discovery.c +@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) + + if (buffer_len > 0) { + if (nice_socket_is_reliable (cand->nicesock)) { +- stun_timer_start_reliable (&cand->timer, +- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); ++ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); + } else { +- stun_timer_start (&cand->timer, 200, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&cand->timer, ++ agent->stun_initial_timeout, ++ agent->stun_max_retransmissions); + } + + /* send the conncheck */ +diff --git a/stun/usages/timer.h b/stun/usages/timer.h +index e74353b..097e75b 100644 +--- a/stun/usages/timer.h ++++ b/stun/usages/timer.h +@@ -132,7 +132,11 @@ struct stun_timer_s { + * The default intial timeout to use for the timer + * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most + * cases as it is also what is used by SIP style VoIP when sending A-Law and +- * mu-Law audio, so 200ms should be hyper safe. ++ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout ++ * of 200ms, a default of 7 transmissions, the last timeout will be ++ * 16 * 200ms, and we expect to receive a response from the stun server ++ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial ++ * stun request has been sent. + */ + #define STUN_TIMER_DEFAULT_TIMEOUT 200 + +-- +2.13.6 + + +From 7a2c1edf502849a868b6f1026e8e2c343dee4ded Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 6 Jun 2016 22:24:50 +0200 +Subject: [PATCH 23/70] conncheck: update selected pair when nominated flag is + set + +This modifies commit 8f1f615. It is better focused to update the +selected pair just after its nominated flag has been set. We also keep +the code homogeneous with other places, where the call to +priv_update_selected_pair() immediately follows the setting of +pair->nominated. Moreover in priv_update_check_list_state_for_ready(), +we would call priv_update_selected_pair() more times that necessary when +iterating on all nominated pairs. + +Differential Revision:
https://phabricator.freedesktop.org/D1125
+--- + agent/conncheck.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 14fdcd9..8c55269 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data); + static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + NiceAgent *agent, guint stream_id, NiceComponent *component, + NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); ++static gboolean priv_update_selected_pair (NiceAgent *agent, ++ NiceComponent *component, CandidateCheckPair *pair); + + static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) + { +@@ -515,6 +517,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; ++ priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); + break; /* move to the next component */ + } +@@ -1530,7 +1533,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream + ++valid; + if (p->nominated == TRUE) { + ++nominated; +- priv_update_selected_pair (agent, component, p); + } + } + } +-- +2.13.6 + + +From 3a58ba6120b188d78c5709e0349c0346bfa21c1a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 1 Feb 2016 11:10:21 +0100 +Subject: [PATCH 24/70] conncheck: peer reflexive candidates are not paired + +This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning +Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive +Candidates", where discovered candidates do not cause the creation +of new pairs to be checked. + +Differential Revision:
https://phabricator.freedesktop.org/D805
+--- + agent/conncheck.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 8c55269..cdf1025 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1787,6 +1787,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone + + g_assert (remote != NULL); + ++ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates", ++ * the agent does not pair this candidate with any local candidates. ++ */ ++ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && ++ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) ++ { ++ return added; ++ } ++ + for (i = component->local_candidates; i ; i = i->next) { + NiceCandidate *local = i->data; + +@@ -1821,6 +1830,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC + + g_assert (local != NULL); + ++ /* ++ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive ++ * Candidates", the peer reflexive candidate is not paired ++ * with other remote candidates ++ */ ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && ++ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) ++ { ++ return added; ++ } ++ + for (i = component->remote_candidates; i ; i = i->next) { + + NiceCandidate *remote = i->data; +-- +2.13.6 + + +From a602ff57aae6a6afdeab843954c48e6fb5d82d31 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:02:45 +0200 +Subject: [PATCH 25/70] conncheck: fix pair state transition when successful + response is received + +According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a +successful check should go to state succeeded, not only the valid +pair built in section 7.1.3.2.2. + +Differential Revision:
https://phabricator.freedesktop.org/D810
+--- + agent/conncheck.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index cdf1025..7fc2a1d 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2654,7 +2654,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + } + else { + if (!local_cand) { +- if (!agent->force_relay) ++ if (!agent->force_relay) { ++ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1. ++ * "Discovering Peer Reflexive Candidates" ++ */ + local_cand = discovery_add_peer_reflexive_candidate (agent, + stream->id, + component->id, +@@ -2662,8 +2665,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + sockptr, + local_candidate, + remote_candidate); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p", ++ agent, local_cand); ++ } + } + + /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 +@@ -2671,7 +2675,12 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (local_cand) + new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, + local_cand, p); +- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); ++ /* step: The agent sets the state of the pair that *generated* the check to ++ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" ++ */ ++ p->state = NICE_CHECK_SUCCEEDED; ++ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", ++ agent, p, new_pair); + } + + /* note: this is same as "adding to VALID LIST" in the spec +-- +2.13.6 + + +From 0636f9addc041cf93c4ff4eaa351b1768d48a32e Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 13:12:48 +0200 +Subject: [PATCH 26/70] conncheck: implement ice regular nomination method + +This patch implements Regular Nomation as described in RFC5245 +8.1.1.1. The controlling agent lets valid pairs accumulate, and +decides which pair to recheck with the use-candidate attribute set. +priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated +pair when acting as a STUN server, and +priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to +update the nominated pair when acting as a STUN client. A new +property is also added to the agent to control the nomination +mode, which can be regular of aggressive, with default value +set to aggressive. + +Two new flags are introduced in the CandidateCheckPair structure: + +- use_candidate_on_next_check indicates the STUN client to add the + use-candidate attribute when the pair will be checked. At this + time, the nominated flag has not been set on this pair yet. + +- mark_nominated_on_response_arrival indicates the STUN server + to nominate the pair when its succesfull response to a + previous triggered check will arrive (7.2.1.5, item #2) + +Differential Revision:
https://phabricator.freedesktop.org/D811
+--- + agent/Makefile.am | 23 ++++ + agent/agent-priv.h | 8 ++ + agent/agent.c | 59 +++++++++ + agent/agent.h | 43 ++++++- + agent/conncheck.c | 178 +++++++++++++++++++++++++++- + agent/conncheck.h | 2 + + configure.ac | 1 + + docs/reference/libnice/libnice-sections.txt | 2 + + 8 files changed, 309 insertions(+), 7 deletions(-) + +diff --git a/agent/Makefile.am b/agent/Makefile.am +index b585393..915f312 100644 +--- a/agent/Makefile.am ++++ b/agent/Makefile.am +@@ -22,6 +22,12 @@ if WINDOWS + AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP + endif + ++BUILT_SOURCES = \ ++ agent-enum-types.h \ ++ agent-enum-types.c ++ ++CLEANFILES += $(BUILT_SOURCES) ++ + noinst_LTLIBRARIES = libagent.la + + libagent_la_SOURCES = \ +@@ -54,6 +60,23 @@ libagent_la_SOURCES = \ + outputstream.c \ + $(BUILT_SOURCES) + ++agent-enum-types.h: agent.h Makefile ++ $(AM_V_GEN)$(GLIB_MKENUMS) \ ++ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ ++ --fprod "/* enumerations from \"@filename@\" */\n" \ ++ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ ++ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \ ++ $(addprefix $(srcdir)/,agent.h) > $@ ++ ++agent-enum-types.c: agent.h Makefile agent-enum-types.h ++ $(AM_V_GEN)$(GLIB_MKENUMS) \ ++ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \ ++ --fprod "\n/* enumerations from \"@filename@\" */" \ ++ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \ ++ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ ++ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \ ++ $(addprefix $(srcdir)/,agent.h) > $@ ++ + libagent_la_LIBADD = \ + $(top_builddir)/random/libnice-random.la \ + $(top_builddir)/socket/libsocket.la \ +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 162ea63..3384180 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -115,6 +115,13 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + + #define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ + ++/* A convenient macro to test if the agent is compatible with RFC5245 ++ * or OC2007R2. Specifically these two modes share the support ++ * of the regular or aggressive nomination mode */ ++#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \ ++ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ ++ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +@@ -134,6 +141,7 @@ struct _NiceAgent + guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ + guint stun_initial_timeout; /* property: stun initial timeout, RTO */ + guint stun_reliable_timeout; /* property: stun reliable timeout */ ++ NiceNominationMode nomination_mode; /* property: Nomination mode */ + + GSList *local_addresses; /* list of NiceAddresses for local + interfaces */ +diff --git a/agent/agent.c b/agent/agent.c +index 25d7886..577a7e0 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -73,6 +73,7 @@ + #include "interfaces.h" + + #include "pseudotcp.h" ++#include "agent-enum-types.h" + + /* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b + * wide. */ +@@ -116,6 +117,7 @@ enum + PROP_STUN_MAX_RETRANSMISSIONS, + PROP_STUN_INITIAL_TIMEOUT, + PROP_STUN_RELIABLE_TIMEOUT, ++ PROP_NOMINATION_MODE, + }; + + +@@ -440,6 +442,24 @@ nice_agent_class_init (NiceAgentClass *klass) + G_PARAM_READWRITE)); + + /** ++ * NiceAgent:nomination-mode: ++ * ++ * The nomination mode used in the ICE specification for describing ++ * the selection of valid pairs to be used upstream. ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * Since: UNRELEASED ++ */ ++ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, ++ g_param_spec_enum ( ++ "nomination-mode", ++ "ICE nomination mode", ++ "Nomination mode used in the ICE specification for describing " ++ "the selection of valid pairs to be used upstream", ++ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** + * NiceAgent:proxy-ip: + * + * The proxy server IP used to bypass a proxy firewall +@@ -1096,6 +1116,7 @@ nice_agent_init (NiceAgent *agent) + agent->stun_server_port = DEFAULT_STUN_PORT; + agent->controlling_mode = TRUE; + agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; ++ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; + + agent->discovery_list = NULL; + agent->discovery_unsched_items = 0; +@@ -1144,6 +1165,23 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) + } + + ++NICEAPI_EXPORT NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ gboolean reliable, ++ NiceNominationMode nomination) ++{ ++ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, ++ "compatibility", compat, ++ "main-context", ctx, ++ "reliable", reliable, ++ "nomination-mode", nomination, ++ NULL); ++ ++ return agent; ++} ++ ++ + static void + nice_agent_get_property ( + GObject *object, +@@ -1190,6 +1228,10 @@ nice_agent_get_property ( + /* XXX: should we prune the list of already existing checks? */ + break; + ++ case PROP_NOMINATION_MODE: ++ g_value_set_enum (value, agent->nomination_mode); ++ break; ++ + case PROP_PROXY_IP: + g_value_set_string (value, agent->proxy_ip); + break; +@@ -1394,6 +1436,10 @@ nice_agent_set_property ( + agent->max_conn_checks = g_value_get_uint (value); + break; + ++ case PROP_NOMINATION_MODE: ++ agent->nomination_mode = g_value_get_enum (value); ++ break; ++ + case PROP_PROXY_IP: + g_free (agent->proxy_ip); + agent->proxy_ip = g_value_dup_string (value); +@@ -3298,6 +3344,19 @@ static gboolean priv_add_remote_candidate ( + username, password, priority); + } + ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ /* note: If there are TCP candidates for a media stream, ++ * a controlling agent MUST use the regular selection algorithm, ++ * RFC 6544, sect 8, "Concluding ICE Processing" ++ */ ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE && ++ transport != NICE_CANDIDATE_TRANSPORT_UDP) { ++ nice_debug ("Agent %p : we have TCP candidates, switching back " ++ "to regular nomination mode", agent); ++ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR; ++ } ++ } ++ + if (base_addr) + candidate->base_addr = *base_addr; + +diff --git a/agent/agent.h b/agent/agent.h +index 47c4d5a..6e233c6 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -377,6 +377,26 @@ typedef enum + NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP, + } NiceProxyType; + ++/** ++ * NiceNominationMode: ++ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode ++ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode ++ * ++ * An enum to specity the kind of nomination mode to use by ++ * the agent, as described in RFC 5245. Two modes exists, ++ * regular and aggressive. They differ by the way the controlling ++ * agent chooses to put the USE-CANDIDATE attribute in its STUN ++ * messages. The aggressive mode is supposed to nominate a pair ++ * faster, than the regular mode, potentially causing the nominated ++ * pair to change until the connection check completes. ++ * ++ * Since: UNRELEASED ++ */ ++typedef enum ++{ ++ NICE_NOMINATION_MODE_REGULAR = 0, ++ NICE_NOMINATION_MODE_AGGRESSIVE, ++} NiceNominationMode; + + /** + * NiceAgentRecvFunc: +@@ -429,6 +449,28 @@ NiceAgent * + nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + + /** ++ * nice_agent_new_full: ++ * @ctx: The Glib Mainloop Context to use for timers ++ * @compat: The compatibility mode of the agent ++ * @reliable: The reliability mode of the agent ++ * @nomination: The nomination mode of the agent ++ * ++ * Create a new #NiceAgent with parameters that must be be defined at ++ * construction time. ++ * The returned object must be freed with g_object_unref() ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * Since: UNRELEASED ++ * ++ * Returns: The new agent GObject ++ */ ++NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ gboolean reliable, ++ NiceNominationMode nomination); ++ ++/** + * nice_agent_add_local_address: + * @agent: The #NiceAgent Object + * @addr: The address to listen to +@@ -447,7 +489,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + gboolean + nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr); + +- + /** + * nice_agent_add_stream: + * @agent: The #NiceAgent Object +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 7fc2a1d..6827e6e 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -502,7 +502,62 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + if (s_nominated < stream->n_components && + s_waiting_for_nomination) { + keep_timer_going = TRUE; +- if (agent->controlling_mode) { ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && ++ agent->controlling_mode && ++ ((waiting == 0 && s_inprogress == 0) || ++ (s_succeeded + s_discovered) >= 5 * stream->n_components)){ ++ /* ICE 8.1.1.1 Regular nomination ++ * we choose to nominate the valid pair if ++ * there is no pair left waiting or in-progress or ++ * if there are at least 5 valid pairs per stream on average. ++ * ++ * This is the "stopping criterion" described in 8.1.1.1, and is ++ * a "local optimization" between accumulating more valid pairs, ++ * and limiting the time spent waiting for in-progress connections ++ * checks until they finally fail. ++ */ ++ GSList *component_item; ++ ++ for (component_item = stream->components; component_item; ++ component_item = component_item->next) { ++ NiceComponent *component = component_item->data; ++ gboolean already_done = FALSE; ++ ++ /* verify that the choice of the pair to be nominated ++ * has not already been done ++ */ ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ if (p->component_id == component->id && ++ p->use_candidate_on_next_check) { ++ already_done = TRUE; ++ break; ++ } ++ } ++ ++ /* choose a pair to be nominated in the list of valid ++ * pairs, and add it to the triggered checks list ++ */ ++ if (!already_done) { ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ /* note: highest priority item selected (list always sorted) */ ++ if (p->component_id == component->id && ++ !p->nominated && ++ !p->use_candidate_on_next_check && ++ p->valid) { ++ nice_debug ("Agent %p : restarting check %p with " ++ "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->use_candidate_on_next_check = TRUE; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ break; /* move to the next component */ ++ } ++ } ++ } ++ } ++ } ++ } else if (agent->controlling_mode) { + GSList *component_item; + + for (component_item = stream->components; component_item; +@@ -1571,10 +1626,40 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + + g_assert (component); + ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && ++ agent->controlling_mode) ++ return; ++ + /* step: search for at least one nominated pair */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *pair = i->data; +- if (pair->local == localcand && pair->remote == remotecand) { ++ if (pair->local == localcand && pair->remote == remotecand && ++ NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ /* ICE, 7.2.1.5. Updating the Nominated Flag */ ++ /* note: TCP candidates typically produce peer reflexive ++ * candidate, generating a "discovered" pair that can be ++ * nominated. ++ */ ++ if (pair->valid) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated", ++ agent, pair, pair->foundation); ++ pair->nominated = TRUE; ++ priv_update_selected_pair (agent, component, pair); ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) { ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (pair->state == NICE_CHECK_IN_PROGRESS) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is in-progress, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } ++ } else if (pair->local == localcand && pair->remote == remotecand) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); + pair->nominated = TRUE; + if (pair->valid) { +@@ -2174,7 +2259,35 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + pair->prflx_priority, controlling); + } + +- if (cand_use) ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ { ++ /* We are doing regular nomination, so we set the use-candidate ++ * attrib, when the controlling agent decided which valid pair to ++ * resend with this flag in priv_conn_check_tick_stream() ++ */ ++ cand_use = pair->use_candidate_on_next_check; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(regular nomination).", agent, G_STRFUNC, cand_use); ++ break; ++ } ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ { ++ /* We are doing aggressive nomination, we set the use-candidate ++ * attrib in every check we send, when we are the controlling ++ * agent, RFC 5245, 8.1.1.2 ++ */ ++ cand_use = controlling; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(aggressive nomination).", agent, G_STRFUNC, cand_use); ++ break; ++ } ++ default: ++ /* Nothing to do. */ ++ break; ++ } ++ } else if (cand_use) + pair->nominated = controlling; + + if (uname_len > 0) { +@@ -2781,12 +2894,66 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + local_candidate, remote_candidate); + } + +- ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ + if (!ok_pair) + ok_pair = p; + + /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the + Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, control=1, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(aggressive nomination, control=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ default: ++ /* Nothing to do */ ++ break; ++ } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, control=0, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; ++ } ++ } ++ } ++ + if (ok_pair->nominated == TRUE) { + priv_update_selected_pair (agent, component, ok_pair); + priv_print_conn_check_lists (agent, G_STRFUNC, +@@ -3668,8 +3835,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + stun_usage_ice_conncheck_use_candidate (&req); + uint32_t priority = stun_usage_ice_conncheck_priority (&req); + +- if (agent->controlling_mode || +- agent->compatibility == NICE_COMPATIBILITY_GOOGLE || ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || + agent->compatibility == NICE_COMPATIBILITY_MSN || + agent->compatibility == NICE_COMPATIBILITY_OC2007) + use_candidate = TRUE; +diff --git a/agent/conncheck.h b/agent/conncheck.h +index c204475..0f988de 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -87,6 +87,8 @@ struct _CandidateCheckPair + gboolean nominated; + gboolean timer_restarted; + gboolean valid; ++ gboolean use_candidate_on_next_check; ++ gboolean mark_nominated_on_response_arrival; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +diff --git a/configure.ac b/configure.ac +index 98bbc08..6c106ff 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -57,6 +57,7 @@ AC_PROG_CC + AM_PROG_AR + LT_PREREQ([2.2.6]) + LT_INIT([dlopen win32-dll disable-static]) ++AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) + + # Check Operating System + AC_MSG_CHECKING([operating system]) +diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt +index 88a6cd2..a481106 100644 +--- a/docs/reference/libnice/libnice-sections.txt ++++ b/docs/reference/libnice/libnice-sections.txt +@@ -5,6 +5,7 @@ NiceAgent + NiceComponentState + NiceComponentType + NiceProxyType ++NiceNominationMode + NiceCompatibility + NiceAgentRecvFunc + NiceInputMessage +@@ -12,6 +13,7 @@ NiceOutputMessage + NICE_AGENT_MAX_REMOTE_CANDIDATES + nice_agent_new + nice_agent_new_reliable ++nice_agent_new_full + nice_agent_add_local_address + nice_agent_set_port_range + nice_agent_add_stream +-- +2.13.6 + + +From 4497d9b7afaaea7124db4a2cd13546d9366b5986 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Wed, 22 Jun 2016 15:44:39 +0200 +Subject: [PATCH 27/70] test-nomination: added a new test for the nomination + mode + +Differential Revision:
https://phabricator.freedesktop.org/D1107
+--- + tests/Makefile.am | 5 +- + tests/test-nomination.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 267 insertions(+), 1 deletion(-) + create mode 100644 tests/test-nomination.c + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 62d5d64..b623764 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -55,7 +55,8 @@ check_PROGRAMS = \ + test-icetcp \ + test-credentials \ + test-turn \ +- test-drop-invalid ++ test-drop-invalid \ ++ test-nomination + + dist_check_SCRIPTS = \ + check-test-fullmode-with-stun.sh \ +@@ -131,6 +132,8 @@ test_turn_LDADD = $(COMMON_LDADD) + + test_drop_invalid_LDADD = $(COMMON_LDADD) + ++test_nomination_LDADD = $(COMMON_LDADD) ++ + test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) + test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) + +diff --git a/tests/test-nomination.c b/tests/test-nomination.c +new file mode 100644 +index 0000000..b5a5e5f +--- /dev/null ++++ b/tests/test-nomination.c +@@ -0,0 +1,263 @@ ++#include <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#include <gio/gio.h> ++#include <agent.h> ++ ++static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static guint global_components_ready = 0; ++static gboolean global_lagent_gathering_done = FALSE; ++static gboolean global_ragent_gathering_done = FALSE; ++static int global_lagent_cands = 0; ++static int global_ragent_cands = 0; ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)buf; ++ ++ /* ++ * Lets ignore stun packets that got through ++ */ ++ if (len < 8) ++ return; ++ if (strncmp ("12345678", buf, 8)) ++ return; ++ ++ if (component_id != 1) ++ return; ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_gathering_done = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_gathering_done = TRUE; ++} ++ ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ gboolean ready_to_connected = FALSE; ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) { ++ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_lagent_state[component_id - 1] = state; ++ } else if (GPOINTER_TO_UINT (data) == 2) { ++ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_ragent_state[component_id - 1] = state; ++ } ++ ++ if (state == NICE_COMPONENT_STATE_READY) ++ global_components_ready++; ++ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) ++ global_components_ready--; ++ g_assert (state != NICE_COMPONENT_STATE_FAILED); ++ ++ g_debug ("test-nomination: checks READY %u.", global_components_ready); ++} ++ ++static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, ++ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ ++global_lagent_cands; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ ++global_ragent_cands; ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL, *i; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++} ++ ++static void set_credentials (NiceAgent *lagent, guint lstream, ++ NiceAgent *ragent, guint rstream) ++{ ++ gchar *ufrag = NULL, *password = NULL; ++ ++ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++} ++ ++static void ++run_test(NiceNominationMode l_nomination_mode, ++ NiceNominationMode r_nomination_mode) ++{ ++ NiceAgent *lagent, *ragent; /* agent's L and R */ ++ const gchar *localhost; ++ NiceAddress localaddr; ++ guint ls_id, rs_id; ++ gulong timer_id; ++ ++ localhost = "127.0.0.1"; ++ ++ /* step: initialize variables modified by the callbacks */ ++ global_components_ready = 0; ++ global_lagent_gathering_done = FALSE; ++ global_ragent_gathering_done = FALSE; ++ global_lagent_cands = global_ragent_cands = 0; ++ ++ lagent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ FALSE, /* reliable */ ++ l_nomination_mode); ++ ++ ragent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ FALSE, /* reliable */ ++ r_nomination_mode); ++ ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ nice_agent_set_software (lagent, "Test-nomination, Left Agent"); ++ nice_agent_set_software (ragent, "Test-nomination, Right Agent"); ++ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ if (!nice_address_set_from_string (&localaddr, localhost)) ++ g_assert_not_reached (); ++ nice_agent_add_local_address (lagent, &localaddr); ++ nice_agent_add_local_address (ragent, &localaddr); ++ ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); ++ ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ ++ ls_id = nice_agent_add_stream (lagent, 1); ++ rs_id = nice_agent_add_stream (ragent, 1); ++ g_assert (ls_id > 0); ++ g_assert (rs_id > 0); ++ ++ /* Gather candidates and test nice_agent_set_port_range */ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); ++ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); ++ ++ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'..."); ++ while (!global_lagent_gathering_done) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_lagent_gathering_done == TRUE); ++ while (!global_ragent_gathering_done) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_ragent_gathering_done == TRUE); ++ ++ set_credentials (lagent, ls_id, ragent, rs_id); ++ ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); ++ ++ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || ++ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); ++ ++ nice_agent_remove_stream (lagent, ls_id); ++ nice_agent_remove_stream (ragent, rs_id); ++ ++ g_source_remove (timer_id); ++ ++ g_clear_object(&lagent); ++ g_clear_object(&ragent); ++} ++ ++static void ++regular (void) ++{ ++ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR); ++} ++ ++static void ++aggressive (void) ++{ ++ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE); ++} ++ ++static void ++mixed_ra (void) ++{ ++ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE); ++} ++ ++static void ++mixed_ar (void) ++{ ++ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int ret; ++ ++ g_test_init (&argc, &argv, NULL); ++ ++ g_test_add_func ("/nice/nomination/regular", regular); ++ g_test_add_func ("/nice/nomination/aggressive", aggressive); ++ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra); ++ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar); ++ ++ ret = g_test_run (); ++ ++ return ret; ++} +-- +2.13.6 + + +From 58d061df8f5425dc1add9c6030a2f891ebda4616 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 7 Mar 2016 16:35:09 +0100 +Subject: [PATCH 28/70] conncheck: update pair valid property selectively + +With this patch, we fix a corner case when the succeeded pair is a +peer-reflexive candidate pair, that already has been discovered +previously, In this case, the current pair -p- should not be marked +valid, because the valid flag is already set on the discovered pair. + +Differential Revision:
https://phabricator.freedesktop.org/D1124
+--- + agent/conncheck.c | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 6827e6e..ef8df68 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2760,6 +2760,13 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + } + + if (new_pair) { ++ /* note: when new_pair is distinct from p, it means new_pair is a ++ * previously discovered peer-reflexive candidate pair, so we don't ++ * set the valid flag on p in this case, because the valid flag is ++ * already set on the discovered pair. ++ */ ++ if (new_pair == p) ++ p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + priv_conn_check_unfreeze_related (agent, stream, p); +@@ -2788,6 +2795,10 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (local_cand) + new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, + local_cand, p); ++ /* note: this is same as "adding to VALID LIST" in the spec ++ text */ ++ if (new_pair) ++ new_pair->valid = TRUE; + /* step: The agent sets the state of the pair that *generated* the check to + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ +@@ -2796,12 +2807,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + agent, p, new_pair); + } + +- /* note: this is same as "adding to VALID LIST" in the spec +- text */ +- if (new_pair) { +- new_pair->valid = TRUE; ++ if (new_pair && new_pair->valid) + nice_component_add_valid_candidate (component, remote_candidate); +- } ++ + + return new_pair; + } +-- +2.13.6 + + +From 15c0546f624113b8c0546a1f883a48bff7020f1b Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 17:06:32 +0200 +Subject: [PATCH 29/70] conncheck: improve the selection of the pairs to be + checked + +This patch aims to implement more closely the algorithm described +in RFC 5245 indicating how pairs are transitionned from state Frozen +to Waiting. This is described in 7.1.3.2 when a check succeeded, and +correspond to modifications in function priv_conn_check_unfreeze_related(). +This is also described in 5.7.4 when defining the initial state of the +pairs in a conncheck, and correspond to modifications in function +priv_conn_check_unfreeze_next(). + +This patch introduces the notion of active and frozen check list. It +allows us to define the timer restranmission delay as described in 16.1. + +Another modification in priv_conn_check_tick_unlocked() is that every +stream in handled consecutively, and in an independant way. The pacing +was previously of a single STUN request emitted per callback, it is now +of a triggered check per callback OR a single STUN per callback AND per +stream per callback. + +The description of ordinary checks per stream in 5.8 is detailled in +function priv_conn_check_tick_stream(), and a remaining of the code +used to nominate a pair by the controlling agent is put in a dedicated +function priv_conn_check_tick_stream_nominate() + +Differential Revision:
https://phabricator.freedesktop.org/D813
+--- + agent/conncheck.c | 535 ++++++++++++++++++++++++++++++++++++++---------------- + agent/stream.c | 21 --- + agent/stream.h | 3 - + 3 files changed, 381 insertions(+), 178 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index ef8df68..6b1b7e3 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -212,6 +212,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) + } + + /* ++ * Check if the conncheck list if Active according to ++ * ICE spec, 5.7.4 (Computing States) ++ * ++ * note: the ICE spec in unclear about that, but the check list should ++ * be considered active when there is at least a pair in Waiting state ++ * OR a pair in In-Progress state. ++ */ ++static gboolean ++priv_is_checklist_active (NiceStream *stream) ++{ ++ GSList *i; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS) ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* ++ * Check if the conncheck list if Frozen according to ++ * ICE spec, 5.7.4 (Computing States) ++ */ ++static gboolean ++priv_is_checklist_frozen (NiceStream *stream) ++{ ++ GSList *i; ++ ++ if (stream->conncheck_list == NULL) ++ return FALSE; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state != NICE_CHECK_FROZEN) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* ++ * Check if all components of the stream have ++ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.) ++ */ ++static gboolean ++priv_all_components_have_valid_pair (NiceStream *stream) ++{ ++ guint i; ++ GSList *j; ++ ++ for (i = 1; i <= stream->n_components; i++) { ++ for (j = stream->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (p->component_id == i && p->valid) ++ break; ++ } ++ if (j == NULL) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* ++ * Check if the foundation in parameter matches the foundation ++ * of a valid pair in the conncheck list [of stream] (used for ICE spec, ++ * 7.1.3.2.3, point 2.) ++ */ ++static gboolean ++priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream) ++{ ++ GSList *i; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->valid && ++ strncmp (p->foundation, foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* + * Finds the next connectivity check in WAITING state. + */ + static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list) +@@ -220,7 +303,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + + /* note: list is sorted in priority order to first waiting check has + * the highest priority */ +- + for (i = conn_check_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; + if (p->state == NICE_CHECK_WAITING) +@@ -231,6 +313,74 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + } + + /* ++ * Finds the next connectivity check in FROZEN state. ++ */ ++static CandidateCheckPair * ++priv_conn_check_find_next_frozen (GSList *conn_check_list) ++{ ++ GSList *i; ++ ++ /* note: list is sorted in priority order to first frozen check has ++ * the highest priority */ ++ for (i = conn_check_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_FROZEN) ++ return p; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Returns the number of check lists of the agent ++ */ ++static guint ++priv_number_of_check_lists (NiceAgent *agent) ++{ ++ guint n = 0; ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (stream->conncheck_list != NULL) ++ n++; ++ } ++ return n; ++} ++ ++/* ++ * Returns the number of active check lists of the agent ++ */ ++static guint ++priv_number_of_active_check_lists (NiceAgent *agent) ++{ ++ guint n = 0; ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) ++ if (priv_is_checklist_active (i->data)) ++ n++; ++ return n; ++} ++ ++/* ++ * Returns the first stream of the agent having a Frozen ++ * connection check list ++ */ ++static NiceStream * ++priv_find_first_frozen_check_list (NiceAgent *agent) ++{ ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (priv_is_checklist_frozen (stream)) ++ return stream; ++ } ++ return NULL; ++} ++ ++/* + * Initiates a new connectivity check for a ICE candidate pair. + * + * @return TRUE on success, FALSE on error +@@ -248,58 +398,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * + /* + * Unfreezes the next connectivity check in the list. Follows the + * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec +- * (ID-19), with some exceptions (see comments in code). ++ * (RFC5245) + * + * See also sect 7.1.2.2.3 (Updating Pair States), and + * priv_conn_check_unfreeze_related(). + * + * @return TRUE on success, and FALSE if no frozen candidates were found. + */ +-static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) ++static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream) + { +- CandidateCheckPair *pair = NULL; + GSList *i, *j; +- +- /* XXX: the unfreezing is implemented a bit differently than in the +- * current ICE spec, but should still be interoperate: +- * - checks are not grouped by foundation +- * - one frozen check is unfrozen (lowest component-id, highest +- * priority) +- */ ++ GSList *found_list = NULL; ++ gboolean result = FALSE; + + priv_print_conn_check_lists (agent, G_STRFUNC, NULL); + +- for (i = agent->streams; i; i = i->next) { +- NiceStream *stream = i->data; +- guint64 max_frozen_priority = 0; ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p1 = i->data; ++ CandidateCheckPair *pair = NULL; ++ guint lowest_component_id = stream->n_components + 1; ++ guint64 highest_priority = 0; + ++ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp)) ++ continue; ++ found_list = g_slist_prepend (found_list, p1->foundation); + + for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *p = j->data; +- +- /* XXX: the prio check could be removed as the pairs are sorted +- * already */ +- +- if (p->state == NICE_CHECK_FROZEN) { +- if (p->priority > max_frozen_priority) { +- max_frozen_priority = p->priority; +- pair = p; +- } ++ CandidateCheckPair *p2 = i->data; ++ if (strncmp (p2->foundation, p1->foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { ++ if (p2->component_id < lowest_component_id || ++ (p2->component_id == lowest_component_id && ++ p2->priority > highest_priority)) { ++ pair = p2; ++ lowest_component_id = p2->component_id; ++ highest_priority = p2->priority; ++ } + } + } + +- if (pair) +- break; +- } +- +- if (pair) { +- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); +- return TRUE; ++ if (pair) { ++ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", ++ agent, pair, pair->stream_id, pair->component_id, pair->foundation); ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ result = TRUE; ++ } + } +- +- return FALSE; ++ g_slist_free (found_list); ++ return result; + } + + /* +@@ -316,7 +463,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) + static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) + { + GSList *i, *j; +- guint unfrozen = 0; + + g_assert (ok_check); + g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); +@@ -336,40 +482,59 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); + p->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- ++unfrozen; + } + } + } + + /* step: perform the step (2) of 'Updating Pair States' */ + stream = agent_find_stream (agent, ok_check->stream_id); +- if (nice_stream_all_components_ready (stream)) { +- /* step: unfreeze checks from other streams */ ++ if (priv_all_components_have_valid_pair (stream)) { + for (i = agent->streams; i ; i = i->next) { ++ /* the agent examines the check list for each other ++ * media stream in turn ++ */ + NiceStream *s = i->data; +- for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *p = j->data; +- +- if (p->stream_id == s->id && +- p->stream_id != ok_check->stream_id) { +- if (p->state == NICE_CHECK_FROZEN && +- strcmp (p->foundation, ok_check->foundation) == 0) { ++ if (s->id == ok_check->stream_id) ++ continue; ++ if (priv_is_checklist_active (s)) { ++ /* checklist is Active ++ */ ++ for (j = s->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (p->state == NICE_CHECK_FROZEN && ++ priv_foundation_matches_a_valid_pair (p->foundation, stream)) { + nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); + p->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- ++unfrozen; +- +- } +- } ++ } ++ } ++ } else if (priv_is_checklist_frozen (s)) { ++ /* checklist is Frozen ++ */ ++ gboolean match_found = FALSE; ++ /* check if there is one pair in the check list whose ++ * foundation matches a pair in the valid list under ++ * consideration ++ */ ++ for (j = s->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { ++ match_found = TRUE; ++ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); ++ p->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ } ++ } ++ ++ if (!match_found) { ++ /* set the pair with the lowest component ID ++ * and highest priority to Waiting ++ */ ++ priv_conn_check_unfreeze_next (agent, s); ++ } + } +- /* note: only unfreeze check from one stream at a time */ +- if (unfrozen) +- break; + } + } +- +- if (unfrozen == 0) +- priv_conn_check_unfreeze_next (agent); + } + + static void +@@ -400,14 +565,13 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP + * + * @return will return FALSE when no more pending timers. + */ +-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) ++static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) + { + gboolean keep_timer_going = FALSE; +- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, +- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; +- guint frozen = 0, waiting = 0; +- GSList *i, *k; ++ GSList *i; ++ CandidateCheckPair *pair; + ++ /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; + +@@ -451,7 +615,6 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + p->next_tick = *now; + g_time_val_add (&p->next_tick, timeout * 1000); + +- *stun_transmitted = TRUE; + return TRUE; + } + case STUN_USAGE_TIMER_RETURN_SUCCESS: +@@ -471,7 +634,57 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + } + } + } ++ } + ++ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" ++ * note: This code is executed when the triggered checks list is ++ * empty, and when no STUN message has been sent (pacing constraint) ++ */ ++ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); ++ if (pair) { ++ priv_conn_check_initiate (agent, pair); ++ return TRUE; ++ } ++ ++ /* note: this is unclear in the ICE spec, but a check list in Frozen ++ * state (where all pairs are in Frozen state) is not supposed to ++ * change its state by an ordinary check, but only by the success of ++ * checks in other check lists, in priv_conn_check_unfreeze_related(). ++ * The underlying idea is to concentrate the checks on a single check ++ * list initially. ++ */ ++ if (priv_is_checklist_frozen (stream)) ++ return keep_timer_going; ++ ++ /* step: ordinary check continued, if there's no pair in the waiting ++ * state, pick a pair in the frozen state ++ */ ++ pair = priv_conn_check_find_next_frozen (stream->conncheck_list); ++ if (pair) { ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ priv_conn_check_initiate (agent, pair); ++ return TRUE; ++ } ++ return keep_timer_going; ++} ++ ++static gboolean ++priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) ++{ ++ gboolean keep_timer_going = FALSE; ++ guint s_inprogress = 0; ++ guint s_succeeded = 0; ++ guint s_discovered = 0; ++ guint s_nominated = 0; ++ guint s_waiting_for_nomination = 0; ++ guint s_valid = 0; ++ guint frozen = 0; ++ guint waiting = 0; ++ GSList *i, *k; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; + if (p->state == NICE_CHECK_FROZEN) + ++frozen; + else if (p->state == NICE_CHECK_IN_PROGRESS) +@@ -504,13 +717,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + keep_timer_going = TRUE; + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { + if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && +- agent->controlling_mode && +- ((waiting == 0 && s_inprogress == 0) || +- (s_succeeded + s_discovered) >= 5 * stream->n_components)){ ++ agent->controlling_mode) { ++#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2 + /* ICE 8.1.1.1 Regular nomination +- * we choose to nominate the valid pair if +- * there is no pair left waiting or in-progress or +- * if there are at least 5 valid pairs per stream on average. ++ * we choose to nominate the valid pair of a component if ++ * - there is no pair left frozen, waiting or in-progress, or ++ * - if there are at least two valid pairs, or ++ * - if there is at least one valid pair of type HOST-HOST + * + * This is the "stopping criterion" described in 8.1.1.1, and is + * a "local optimization" between accumulating more valid pairs, +@@ -523,36 +736,63 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + component_item = component_item->next) { + NiceComponent *component = component_item->data; + gboolean already_done = FALSE; ++ gboolean stopping_criterion = FALSE; ++ guint p_valid = 0; ++ guint p_frozen = 0; ++ guint p_waiting = 0; ++ guint p_inprogress = 0; ++ guint p_host_host_valid = 0; + + /* verify that the choice of the pair to be nominated + * has not already been done + */ + for (k = stream->conncheck_list; k ; k = k->next) { + CandidateCheckPair *p = k->data; +- if (p->component_id == component->id && +- p->use_candidate_on_next_check) { +- already_done = TRUE; +- break; ++ if (p->component_id == component->id) { ++ if (p->use_candidate_on_next_check) ++ already_done = TRUE; ++ if (p->state == NICE_CHECK_FROZEN) ++ p_frozen++; ++ else if (p->state == NICE_CHECK_WAITING) ++ p_waiting++; ++ else if (p->state == NICE_CHECK_IN_PROGRESS) ++ p_inprogress++; ++ if (p->valid) ++ p_valid++; ++ if (p->valid && ++ p->local->type == NICE_CANDIDATE_TYPE_HOST && ++ p->remote->type == NICE_CANDIDATE_TYPE_HOST) ++ p_host_host_valid++; + } + } + +- /* choose a pair to be nominated in the list of valid +- * pairs, and add it to the triggered checks list ++ if (already_done) ++ continue; ++ ++ stopping_criterion = ++ (p_host_host_valid > 0 || ++ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS || ++ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0)); ++ ++ if (!stopping_criterion) ++ continue; ++ ++ /* when the stopping criterion is satisfied, we choose ++ * a pair to be nominated in the list of valid pairs, ++ * and add it to the triggered checks list + */ +- if (!already_done) { +- for (k = stream->conncheck_list; k ; k = k->next) { +- CandidateCheckPair *p = k->data; +- /* note: highest priority item selected (list always sorted) */ +- if (p->component_id == component->id && +- !p->nominated && +- !p->use_candidate_on_next_check && +- p->valid) { +- nice_debug ("Agent %p : restarting check %p with " +- "USE-CANDIDATE attrib (regular nomination)", agent, p); +- p->use_candidate_on_next_check = TRUE; +- priv_add_pair_to_triggered_check_queue (agent, p); +- break; /* move to the next component */ +- } ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ /* note: highest priority item selected (list always sorted) */ ++ if (p->component_id == component->id && ++ !p->nominated && ++ !p->use_candidate_on_next_check && ++ p->valid) { ++ nice_debug ("Agent %p : restarting check %p with " ++ "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->use_candidate_on_next_check = TRUE; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ break; /* move to the next component */ + } + } + } +@@ -615,70 +855,55 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + { + CandidateCheckPair *pair = NULL; + gboolean keep_timer_going = FALSE; +- gboolean res; +- /* note: we try to only generate a single stun transaction per timer +- * callback, to respect some pacing of STUN transaction, as per +- * appendix B.1 of ICE spec. +- */ +- gboolean stun_transmitted = FALSE; + GSList *i, *j; + GTimeVal now; + +- /* step: process ongoing STUN transactions */ + g_get_current_time (&now); + +- for (j = agent->streams; j; j = j->next) { +- NiceStream *stream = j->data; +- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); +- if (res) +- keep_timer_going = res; +- if (stun_transmitted) +- return TRUE; +- } +- +- /* step: first initiate a conncheck with a pair from the triggered list */ +- pair = priv_get_pair_from_triggered_check_queue (agent); +- +- if (pair) { +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a pair from triggered check list"); +- priv_conn_check_initiate (agent, pair); ++ /* the conncheck really starts when we have built ++ * a connection check list for each stream ++ */ ++ if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) + return TRUE; +- } + +- /* step: when the triggered list is empty, +- * find the highest priority waiting check and send it */ +- for (i = agent->streams; i ; i = i->next) { +- NiceStream *stream = i->data; +- +- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); +- if (pair) +- break; ++ /* configure the initial state of the check lists of the agent ++ * as described in ICE spec, 5.7.4 ++ * ++ * if all pairs in all check lists are in frozen state, then ++ * we are in the initial state (5.7.4, point 1.) ++ */ ++ if (priv_number_of_active_check_lists (agent) == 0) { ++ /* set some pairs of the first stream in the waiting state ++ * ICE spec, 5.7.4, point 2. ++ * ++ * note: we adapt the ICE spec here, by selecting the first ++ * frozen check list, which is not necessarily the check ++ * list of the first stream (the first stream may be completed) ++ */ ++ NiceStream *stream = priv_find_first_frozen_check_list (agent); ++ if (stream) ++ priv_conn_check_unfreeze_next (agent, stream); + } + ++ /* step: perform a test from the triggered checks list, ++ * ICE spec, 5.8 "Scheduling Checks" ++ */ ++ pair = priv_get_pair_from_triggered_check_queue (agent); ++ + if (pair) { + priv_conn_check_initiate (agent, pair); + return TRUE; + } + +- /* step: when there's no pair in the Waiting state, +- * unfreeze a new pair and check it ++ /* step: process ongoing STUN transactions and ++ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" + */ +- priv_conn_check_unfreeze_next (agent); +- + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +- +- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); +- if (pair) +- break; +- } +- +- if (pair) { +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a pair in Waiting state"); +- priv_conn_check_initiate (agent, pair); +- return TRUE; ++ if (priv_conn_check_tick_stream (stream, agent, &now)) ++ keep_timer_going = TRUE; ++ if (priv_conn_check_tick_stream_nominate (stream, agent)) ++ keep_timer_going = TRUE; + } + + /* step: stop timer if no work left */ +@@ -2169,30 +2394,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, + + /* Implement the computation specific in RFC 5245 section 16 */ + +-static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) ++static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream) + { +- GSList *item1, *item2; ++ GSList *i; + guint waiting_and_in_progress = 0; ++ guint n = 0; + unsigned int rto = 0; + +- +- for (item1 = agent->streams; item1; item1 = item1->next) { +- NiceStream *stream = item1->data;; +- for (item2 = stream->conncheck_list; item2; item2 = item2->next) { +- CandidateCheckPair *pair = item2->data; +- +- if (pair->state == NICE_CHECK_IN_PROGRESS || +- pair->state == NICE_CHECK_WAITING) +- waiting_and_in_progress++; +- } ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_IN_PROGRESS || ++ p->state == NICE_CHECK_WAITING) ++ waiting_and_in_progress++; + } + +- rto = agent->timer_ta * waiting_and_in_progress; ++ n = priv_number_of_active_check_lists (agent); ++ rto = agent->timer_ta * n * waiting_and_in_progress; + + /* We assume non-reliable streams are RTP, so we use 100 as the max */ +- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", ++ nice_debug ("Agent %p : timer set to %dms, " ++ "waiting+in_progress=%d, nb_active=%d", + agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), +- waiting_and_in_progress); ++ waiting_and_in_progress, n); + if (agent->reliable) + return MAX (rto, 500); + else +@@ -2312,7 +2535,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); + } else { + stun_timer_start (&pair->timer, +- priv_compute_conncheck_timer (agent), ++ priv_compute_conncheck_timer (agent, stream), + agent->stun_max_retransmissions); + } + +@@ -2477,7 +2700,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p->timer_restarted ? "no" : "yes"); + if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { + stun_timer_start (&p->timer, +- priv_compute_conncheck_timer (agent), ++ priv_compute_conncheck_timer (agent, stream), + agent->stun_max_retransmissions); + p->timer_restarted = TRUE; + } +@@ -2769,7 +2992,6 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); +- priv_conn_check_unfreeze_related (agent, stream, p); + nice_component_add_valid_candidate (component, remote_candidate); + } + else { +@@ -2894,7 +3116,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + g_assert_not_reached (); + nice_debug ("Agent %p : Mapped address not found." + " conncheck %p SUCCEEDED.", agent, p); +- priv_conn_check_unfreeze_related (agent, stream, p); + nice_component_add_valid_candidate (component, p->remote); + } else { + ok_pair = priv_process_response_check_for_reflexive (agent, +@@ -2902,6 +3123,12 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + local_candidate, remote_candidate); + } + ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ + /* Note: this assignment helps to reduce the numbers of cases + * to be tested. If ok_pair and p refer to distinct pairs, it + * means that ok_pair is a discovered peer reflexive one, +diff --git a/agent/stream.c b/agent/stream.c +index 8121e12..533ff15 100644 +--- a/agent/stream.c ++++ b/agent/stream.c +@@ -104,27 +104,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) + } + + /* +- * Returns true if all components of the stream are either +- * 'CONNECTED' or 'READY' (connected plus nominated). +- */ +-gboolean +-nice_stream_all_components_ready (NiceStream *stream) +-{ +- GSList *i; +- +- for (i = stream->components; i; i = i->next) { +- NiceComponent *component = i->data; +- if (component && +- !(component->state == NICE_COMPONENT_STATE_CONNECTED || +- component->state == NICE_COMPONENT_STATE_READY)) +- return FALSE; +- } +- +- return TRUE; +-} +- +- +-/* + * Initialized the local crendentials for the stream. + */ + void +diff --git a/agent/stream.h b/agent/stream.h +index f9188cb..954ba66 100644 +--- a/agent/stream.h ++++ b/agent/stream.h +@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); + void + nice_stream_close (NiceStream *stream); + +-gboolean +-nice_stream_all_components_ready (NiceStream *stream); +- + NiceComponent * + nice_stream_find_component_by_id (NiceStream *stream, guint id); + +-- +2.13.6 + + +From ead3453d04fc70865d176ab073636f8b9078cbbc Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:20:38 +0200 +Subject: [PATCH 30/70] conncheck: invoke the debug dump in more places + +Differential Revision:
https://phabricator.freedesktop.org/D1123
+--- + agent/conncheck.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 6b1b7e3..2d2224d 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -642,6 +642,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + */ + pair = priv_conn_check_find_next_waiting (stream->conncheck_list); + if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Waiting state"); + priv_conn_check_initiate (agent, pair); + return TRUE; + } +@@ -661,6 +663,8 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + */ + pair = priv_conn_check_find_next_frozen (stream->conncheck_list); + if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Frozen state"); + pair->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, pair); + priv_conn_check_initiate (agent, pair); +@@ -891,6 +895,8 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + pair = priv_get_pair_from_triggered_check_queue (agent); + + if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair from triggered check list"); + priv_conn_check_initiate (agent, pair); + return TRUE; + } +-- +2.13.6 + + +From 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 17:59:27 +0200 +Subject: [PATCH 31/70] conncheck: improve triggered check of in-progress pairs + +This patch update the way triggered checks of in-progress pairs are +handled, according to ICE spec, section 7.2.1.4. Previously the same +connection check was retransmitted with an updated timeout. This causes +problems when a controlling role switch occurs in this time frame. +This is the reason why a new connection check must be generated +reflecting the updated role. We introduce a new flag "recheck_on_timeout" +in the pair indicating that the pair must be rechecked at the next timer +expiration. + +Differential Revision:
https://phabricator.freedesktop.org/D875
+--- + agent/conncheck.c | 88 +++++++++++++++++++++++++++++++++++++++++++++---------- + agent/conncheck.h | 2 +- + 2 files changed, 74 insertions(+), 16 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 2d2224d..3a489fe 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -558,6 +558,37 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP + } + + /* ++ * Function that resubmits a new connection check, after a previous ++ * check in in-progress state got cancelled due to an incoming stun ++ * request matching this same pair ++ * ++ * @return will return TRUE if the pair is scheduled for recheck ++ */ ++static gboolean ++priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) ++{ ++ if (p->recheck_on_timeout) { ++ g_assert (p->state == NICE_CHECK_IN_PROGRESS); ++ /* this cancelled pair may have the flag 'mark nominated ++ * on response arrival' set, we want to keep it, because ++ * this is needed to nominate this pair in aggressive ++ * nomination, when the agent is in controlled mode. ++ * ++ * this cancelled pair may also have the flag 'use candidate ++ * on next check' set, that we want to preserve too. ++ */ ++ nice_debug ("Agent %p : pair %p was cancelled, " ++ "triggering a new connection check", agent, p); ++ p->recheck_on_timeout = FALSE; ++ p->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* + * Helper function for connectivity check timer callback that + * runs through the stream specific part of the state machine. + * +@@ -584,8 +615,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + switch (stun_timer_refresh (&p->timer)) { + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + { +- /* case: error, abort processing */ + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; ++ ++ /* case: error, abort processing */ + nice_address_to_string (&p->local->addr, tmpbuf1); + nice_address_to_string (&p->remote->addr, tmpbuf2); + nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); +@@ -600,8 +640,17 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + } + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: + { +- /* case: not ready, so schedule a new timeout */ + unsigned int timeout = stun_timer_remainder (&p->timer); ++ ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; ++ ++ /* case: not ready, so schedule a new timeout */ + nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " + "(timeout %dms, delay=%dms, retrans=%d).", + agent, p, timeout, p->timer.delay, p->timer.retransmissions); +@@ -642,6 +691,12 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + */ + pair = priv_conn_check_find_next_waiting (stream->conncheck_list); + if (pair) { ++ /* remove the pair from the triggered check list if needed. This ++ * may happen with the cancelled pair, that's just been added ++ * in state waiting to the triggered check list above in the ++ * same function. ++ */ ++ priv_remove_pair_from_triggered_check_queue (agent, pair); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair in Waiting state"); + priv_conn_check_initiate (agent, pair); +@@ -794,6 +849,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->valid) { + nice_debug ("Agent %p : restarting check %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->recheck_on_timeout = FALSE; + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); + break; /* move to the next component */ +@@ -816,6 +872,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; ++ p->recheck_on_timeout = FALSE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); + break; /* move to the next component */ +@@ -2697,19 +2754,20 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p->state == NICE_CHECK_FROZEN) + priv_add_pair_to_triggered_check_queue (agent, p); + else if (p->state == NICE_CHECK_IN_PROGRESS) { +- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), +- * we should cancel the existing one, instead we reset our timer, so +- * we'll resend the exiting transactions faster if needed...? :P +- */ +- nice_debug ("Agent %p : check already in progress, " +- "restarting the timer again?: %s ..", agent, +- p->timer_restarted ? "no" : "yes"); +- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { +- stun_timer_start (&p->timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); +- p->timer_restarted = TRUE; +- } ++ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" ++ * we cancel the in-progress transaction, and after the ++ * retransmission timeout, we create a new connectivity check ++ * for that pair. The controlling role of this new check may ++ * be different from the role of this cancelled check. ++ */ ++ if (!nice_socket_is_reliable (p->sockptr)) { ++ nice_debug ("Agent %p : check already in progress, " ++ "cancelling this check..", agent); ++ /* this flag will determine the action at the retransmission ++ * timeout of the stun timer ++ */ ++ p->recheck_on_timeout = TRUE; ++ } + } + else if (p->state == NICE_CHECK_SUCCEEDED || + p->state == NICE_CHECK_DISCOVERED) { +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 0f988de..785a6cd 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -85,10 +85,10 @@ struct _CandidateCheckPair + gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; + NiceCheckState state; + gboolean nominated; +- gboolean timer_restarted; + gboolean valid; + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; ++ gboolean recheck_on_timeout; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +-- +2.13.6 + + +From 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:25:16 +0200 +Subject: [PATCH 32/70] conncheck: link succeeded and discovered pairs + +When the agent has the role of the stun server, is in controlled mode, +and receives a pair with the "use-candidate" attribute set, it must find +a matching succeded or discovered pair in its conncheck list. This is +described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1. +When a matching pair is in succeeded state, the agent must nominate the +valid pair (a discovered pair) constructed from section 7.1.3.2.2, +that's been created from this succeeded one. To make this lookup, we +introduce a new "discovered_pair" member of the CandidateCheckPair +struct, that links the succeeded pair, and its discovered pair +if any. + +Differential Revision:
https://phabricator.freedesktop.org/D878
+--- + agent/conncheck.c | 7 +++++++ + agent/conncheck.h | 1 + + 2 files changed, 8 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 3a489fe..99cb6d2 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1928,6 +1928,12 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + * candidate, generating a "discovered" pair that can be + * nominated. + */ ++ if (pair->state == NICE_CHECK_SUCCEEDED && ++ pair->discovered_pair != NULL) { ++ pair = pair->discovered_pair; ++ g_assert (pair->state == NICE_CHECK_DISCOVERED); ++ } ++ + if (pair->valid) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", + agent, pair, pair->foundation); +@@ -2936,6 +2942,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->remote = parent_pair->remote; + pair->sockptr = local_cand->sockptr; + pair->state = NICE_CHECK_DISCOVERED; ++ parent_pair->discovered_pair = pair; + nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 785a6cd..dd47ebe 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -89,6 +89,7 @@ struct _CandidateCheckPair + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; + gboolean recheck_on_timeout; ++ struct _CandidateCheckPair *discovered_pair; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +-- +2.13.6 + + +From 9103a5f2e184211fc160d1d3070ce4d043c71ff0 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 19 Apr 2016 18:16:26 +0200 +Subject: [PATCH 33/70] conncheck: use the right pair when retriggering a check + +This patch completes the previous patch by adding a link back from the +discovered pair, to the parent pair that generated this check. This link +is needed by the ICE spec, to comply with section 8.1.1.1, "Regular +nomination", where the check to be retriggered is the initial check that +caused the discovery of the valid pair. When the valid pair is a +peer-reflexive pair, the retriggered check must target the succeeded +pair, and not the valid discovered pair. + +Differential Revision:
https://phabricator.freedesktop.org/D879
+--- + agent/conncheck.c | 21 ++++++++++++++++++--- + agent/conncheck.h | 1 + + 2 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 99cb6d2..79685df 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -847,6 +847,16 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + !p->nominated && + !p->use_candidate_on_next_check && + p->valid) { ++ /* According a ICE spec, sect 8.1.1.1. "Regular ++ * Nomination", we enqueue the check that produced this ++ * valid pair. When this pair has been discovered, we want ++ * to test its parent pair instead. ++ */ ++ if (p->succeeded_pair != NULL) { ++ g_assert (p->state == NICE_CHECK_DISCOVERED); ++ p = p->succeeded_pair; ++ } ++ g_assert (p->state == NICE_CHECK_SUCCEEDED); + nice_debug ("Agent %p : restarting check %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); + p->recheck_on_timeout = FALSE; +@@ -2754,6 +2764,11 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * tcp-active we don't want to retrigger a check on a pair that + * was FAILED when a peer-reflexive pair was created */ + ++ if (p->succeeded_pair != NULL) { ++ g_assert (p->state == NICE_CHECK_DISCOVERED); ++ p = p->succeeded_pair; ++ } ++ + nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); + + if (p->state == NICE_CHECK_WAITING || +@@ -2775,8 +2790,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p->recheck_on_timeout = TRUE; + } + } +- else if (p->state == NICE_CHECK_SUCCEEDED || +- p->state == NICE_CHECK_DISCOVERED) { ++ else if (p->state == NICE_CHECK_SUCCEEDED) { + nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); + /* note: this is a bit unsure corner-case -- let's do the + same state update as for processing responses to our own checks */ +@@ -2943,6 +2957,7 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->sockptr = local_cand->sockptr; + pair->state = NICE_CHECK_DISCOVERED; + parent_pair->discovered_pair = pair; ++ pair->succeeded_pair = parent_pair; + nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; +@@ -4163,7 +4178,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + + pair = priv_conn_check_add_for_candidate_pair_matched (agent, + stream->id, component, local_candidate, remote_candidate, +- NICE_CHECK_DISCOVERED); ++ NICE_CHECK_SUCCEEDED); + if (pair) { + pair->valid = TRUE; + } +diff --git a/agent/conncheck.h b/agent/conncheck.h +index dd47ebe..c07fb22 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -90,6 +90,7 @@ struct _CandidateCheckPair + gboolean mark_nominated_on_response_arrival; + gboolean recheck_on_timeout; + struct _CandidateCheckPair *discovered_pair; ++ struct _CandidateCheckPair *succeeded_pair; + guint64 priority; + guint32 prflx_priority; + GTimeVal next_tick; /* next tick timestamp */ +-- +2.13.6 + + +From 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:30:04 +0200 +Subject: [PATCH 34/70] conncheck: fix a nomination corner case + +This patch add two supplementary cases, not covered by the ICE spec, +sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent +receives a STUN request with the USE-CANDIDATE flag, for a pair that is +in the waiting state. We consider that this case is similar to the +in-progress state, and should be handled in the same way. We also accept +when the pair is in frozen state. This latter case happens in the +new-dribble test, when an agent replays incoming early connchecks. + +Differential Revision:
https://phabricator.freedesktop.org/D880
+--- + agent/conncheck.c | 35 +++++++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 79685df..4f4af40 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1963,6 +1963,29 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + "will be nominated on response receipt.", + agent, pair, pair->foundation); + } ++ /* note: this case is not covered by the ICE spec, 7.2.1.5, ++ * "Updating the Nominated Flag", but a pair in waiting state ++ * deserves the same treatment than a pair in-progress. ++ */ ++ if (pair->state == NICE_CHECK_WAITING) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is waiting, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } ++ /* note: this case is not covered by the ICE spec, 7.2.1.5, ++ * "Updating the Nominated Flag" either, but a pair in frozen ++ * state, and in the triggered check list should also be ++ * considered like a pair in-progress. This case happens with ++ * the new-dribble test, when an agent replays incoming early ++ * connchecks. ++ */ ++ if (pair->state == NICE_CHECK_FROZEN) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is frozen, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } + } else if (pair->local == localcand && pair->remote == remotecand) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); + pair->nominated = TRUE; +@@ -2703,17 +2726,25 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + "is %" G_GUINT64_FORMAT, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ ++ /* note: this case is not covered by the ICE spec, 8.1.2 ++ * "Updating States", but a pair in waiting state which will be ++ * nominated on response receipt should be treated the same way ++ * that an in-progress pair. ++ */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + if (p->component_id == component_id) { + if (p->state == NICE_CHECK_FROZEN || +- p->state == NICE_CHECK_WAITING) { ++ (p->state == NICE_CHECK_WAITING && ++ !p->mark_nominated_on_response_arrival)) { + p->state = NICE_CHECK_CANCELLED; + nice_debug ("Agent XXX : pair %p state CANCELED", p); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- if (p->state == NICE_CHECK_IN_PROGRESS) { ++ if (p->state == NICE_CHECK_IN_PROGRESS || ++ (p->state == NICE_CHECK_WAITING && ++ p->mark_nominated_on_response_arrival)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { + p->stun_message.buffer = NULL; +-- +2.13.6 + + +From afd8d41bb34afb3864e838ef79026ae4ef15c0d4 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:32:49 +0200 +Subject: [PATCH 35/70] conncheck: new pairs never have the nominated flag + preset + +This patch disables the possibility to set the nominated flag of a +candidate pair at creation time. This possibility was used when a new +pair is created from a new peer reflexive remote candidate, when the +agent is in controlled mode, and an stun request with USE-CANDIDATE is +received. In this case, since previous commit "conncheck: fix a +nomination corner case", we set the nominated flag when the stun +response of this new pair will arrive, and not before. Consequently, +this flag is no longer required when the pair is created. + +Differential Revision:
https://phabricator.freedesktop.org/D881
+--- + agent/conncheck.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 4f4af40..3cd0424 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -65,7 +65,7 @@ + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); + static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); + static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); +-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); ++static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); + static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); + static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, + guint component_id, NiceCandidate *remote, NiceCandidate *local, +@@ -1573,7 +1573,8 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); + if (icheck->use_candidate) + priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, pair->remote); + } + } + } +@@ -1716,7 +1717,8 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + + if (icheck->use_candidate) + priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, candidate); + } + } + } +@@ -2043,7 +2045,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority) + */ + static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + guint stream_id, NiceComponent *component, NiceCandidate *local, +- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) ++ NiceCandidate *remote, NiceCheckState initial_state) + { + NiceStream *stream; + CandidateCheckPair *pair; +@@ -2081,7 +2083,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr)); + } +- pair->nominated = use_candidate; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); + +@@ -2127,7 +2128,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + agent, local->foundation, remote->foundation, + stream_id, component->id); + pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, +- initial_state, FALSE); ++ initial_state); + if (component->state == NICE_COMPONENT_STATE_CONNECTED || + component->state == NICE_COMPONENT_STATE_READY) { + agent_signal_component_state_change (agent, +@@ -2774,9 +2775,8 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + * @param component the check is related to + * @param local_socket socket from which the inbound check was received + * @param remote_cand remote candidate from which the inbound check was sent +- * @param use_candidate whether the original check had USE-CANDIDATE attribute set + */ +-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) ++static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand) + { + GSList *i; + NiceCandidate *local = NULL; +@@ -2872,7 +2872,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + + if (i) { + nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); +- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate); ++ priv_add_new_check_pair (agent, stream->id, component, ++ local, remote_cand, NICE_CHECK_WAITING); + return TRUE; + } + else { +@@ -2926,7 +2927,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + + if (rcand) { + /* note: upon successful check, make the reserve check immediately */ +- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); ++ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); + + if (use_candidate) + priv_mark_pair_nominated (agent, stream, component, lcand, rcand); +-- +2.13.6 + + +From 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 12:56:28 +0200 +Subject: [PATCH 36/70] conncheck: update the pair state in triggered check + list + +With this patch, we update the state of the pair to waiting when +it is put in the triggered check queue. We also take care to call +priv_schedule_triggered_check() before priv_mark_pair_nominated() +so a pair to be rechecked and put on the triggered check queue +will have a unique state to be tested in the following call to +priv_mark_pair_nominated() when evaluating its nomination attributes. + +Differential Revision:
https://phabricator.freedesktop.org/D883
+--- + agent/conncheck.c | 33 +++++++++------------------------ + 1 file changed, 9 insertions(+), 24 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 3cd0424..9950970 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -183,6 +183,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa + { + g_assert (pair); + ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); + if (agent->triggered_check_queue == NULL || + g_slist_find (agent->triggered_check_queue, pair) == NULL) + agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); +@@ -580,8 +582,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + nice_debug ("Agent %p : pair %p was cancelled, " + "triggering a new connection check", agent, p); + p->recheck_on_timeout = FALSE; +- p->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } +@@ -1571,10 +1571,10 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + if (nice_address_equal (&icheck->from, &pair->remote->addr) && + icheck->local_socket == pair->sockptr) { + nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); + priv_schedule_triggered_check (agent, stream, component, + icheck->local_socket, pair->remote); ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); + } + } + } +@@ -1715,10 +1715,10 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + else + conn_check_add_for_candidate (agent, stream->id, component, candidate); + +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); + priv_schedule_triggered_check (agent, stream, component, + icheck->local_socket, candidate); ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); + } + } + } +@@ -1967,7 +1967,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + } + /* note: this case is not covered by the ICE spec, 7.2.1.5, + * "Updating the Nominated Flag", but a pair in waiting state +- * deserves the same treatment than a pair in-progress. ++ * deserves the same treatment than a pair in-progress. A pair ++ * can be in waiting state as the result of being enqueued in ++ * the triggered check list for example. + */ + if (pair->state == NICE_CHECK_WAITING) { + pair->mark_nominated_on_response_arrival = TRUE; +@@ -1975,19 +1977,6 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + "will be nominated on response receipt.", + agent, pair, pair->foundation); + } +- /* note: this case is not covered by the ICE spec, 7.2.1.5, +- * "Updating the Nominated Flag" either, but a pair in frozen +- * state, and in the triggered check list should also be +- * considered like a pair in-progress. This case happens with +- * the new-dribble test, when an agent replays incoming early +- * connchecks. +- */ +- if (pair->state == NICE_CHECK_FROZEN) { +- pair->mark_nominated_on_response_arrival = TRUE; +- nice_debug ("Agent %p : pair %p (%s) is frozen, " +- "will be nominated on response receipt.", +- agent, pair, pair->foundation); +- } + } else if (pair->local == localcand && pair->remote == remotecand) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); + pair->nominated = TRUE; +@@ -2926,9 +2915,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + } + + if (rcand) { +- /* note: upon successful check, make the reserve check immediately */ + priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); +- + if (use_candidate) + priv_mark_pair_nominated (agent, stream, component, lcand, rcand); + } +@@ -3345,9 +3332,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + + p->stun_message.buffer = NULL; + p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_WAITING; + priv_add_pair_to_triggered_check_queue (agent, p); +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); + } + trans_found = TRUE; + } else { +-- +2.13.6 + + +From 11d4e37a030eb144a355dc26c705ef5aa5a975a7 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Fri, 1 Apr 2016 17:31:44 +0200 +Subject: [PATCH 37/70] conncheck: remove a useless pair recheck + +This exception to the ICE spec is no longer needed: when a pair is in +the succeeded state, there is no needed to recheck it again upon +reception of an incoming stun request on it. + +Differential Revision:
https://phabricator.freedesktop.org/D884
+--- + agent/conncheck.c | 17 ----------------- + 1 file changed, 17 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 9950970..95e2556 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2820,23 +2820,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * that causes the ready -> connected transition. + */ + priv_update_check_list_state_for_ready (agent, stream, component); +- +- /* note: this new check is required by the new-dribble test, +- * when early icheck on the peer controlled agent causes an +- * incoming stun request to an already succeeded (and +- * nominated) pair on the controlling agent. If the +- * controlling agent doesn't retrigger a check with +- * USE-CANDIDATE=1, the peer agent has no way to nominate it. +- * +- * This behavior differs from ICE spec 7.2.1.4 +- */ +- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || +- agent->compatibility == NICE_COMPATIBILITY_WLM2009 || +- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && +- agent->controlling_mode) { +- priv_add_pair_to_triggered_check_queue (agent, p); +- conn_check_schedule_next(agent); +- } + } else if (p->state == NICE_CHECK_FAILED) { + /* 7.2.1.4 Triggered Checks + * If the state of the pair is Failed, it is changed to Waiting +-- +2.13.6 + + +From 8fa648a15a6700d08165fe97a09f5c068abae1e6 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 11 Apr 2016 13:13:51 +0200 +Subject: [PATCH 38/70] conncheck: dont cancel a pair for triggered check + +This patch adds another supplementary "corner" case, not covered by the +ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in +the triggered check list should be considered like an in-progress pair, +and cancelled only if its priority is lower than the priority of the +nominated pair. This is required in some aggressive nomination +situations for both peers to select the same pair, having the highest +priority. + +Differential Revision:
https://phabricator.freedesktop.org/D933
+--- + agent/conncheck.c | 48 ++++++++++++++++++++++++++++++++---------------- + 1 file changed, 32 insertions(+), 16 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 95e2556..79f678a 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -64,7 +64,7 @@ + + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); + static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); +-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); ++static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id); + static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); + static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); + static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, +@@ -176,6 +176,16 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + } + } + ++/* Verify if the pair is in the triggered checks list ++ */ ++ ++static gboolean ++priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) ++{ ++ g_assert (pair); ++ return (g_slist_find (agent->triggered_check_queue, pair) != NULL); ++} ++ + /* Add the pair to the triggered checks list, if not already present + */ + static void +@@ -1897,7 +1907,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream + /* Only go to READY if no checks are left in progress. If there are + * any that are kept, then this function will be called again when the + * conncheck tick timer finishes them all */ +- if (priv_prune_pending_checks (stream, component->id) == 0) { ++ if (priv_prune_pending_checks (agent, stream, component->id) == 0) { + /* Continue through the states to give client code a nice + * logical progression. See
http://phabricator.freedesktop.org/D218
for + * discussion. */ +@@ -2693,14 +2703,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + * + * @see priv_update_check_list_state_failed_components() + */ +-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) ++static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id) + { + GSList *i; + guint64 highest_nominated_priority = 0; + guint in_progress = 0; + +- nice_debug ("Agent XXX: Finding highest priority for component %d", +- component_id); ++ nice_debug ("Agent %p: Finding highest priority for component %d", ++ agent, component_id); + + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; +@@ -2712,41 +2722,47 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + } + } + +- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority " +- "is %" G_GUINT64_FORMAT, highest_nominated_priority); ++ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority " ++ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ + /* note: this case is not covered by the ICE spec, 8.1.2 + * "Updating States", but a pair in waiting state which will be + * nominated on response receipt should be treated the same way +- * that an in-progress pair. ++ * that an in-progress pair. A pair in waiting state and in ++ * the triggered check list should also be treated like an in-progress ++ * pair. + */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; ++ + if (p->component_id == component_id) { ++ gboolean like_in_progress = ++ p->mark_nominated_on_response_arrival || ++ priv_is_pair_in_triggered_check_queue (agent, p); ++ + if (p->state == NICE_CHECK_FROZEN || +- (p->state == NICE_CHECK_WAITING && +- !p->mark_nominated_on_response_arrival)) { ++ (p->state == NICE_CHECK_WAITING && !like_in_progress)) { + p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ + if (p->state == NICE_CHECK_IN_PROGRESS || +- (p->state == NICE_CHECK_WAITING && +- p->mark_nominated_on_response_arrival)) { ++ (p->state == NICE_CHECK_WAITING && like_in_progress)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { + p->stun_message.buffer = NULL; + p->stun_message.buffer_len = 0; + p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ nice_debug ("Agent %p : pair %p state CANCELED", agent, p); + } else { + /* We must keep the higher priority pairs running because if a udp + * packet was lost, we might end up using a bad candidate */ +- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %" ++ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %" + G_GUINT64_FORMAT " is higher than currently nominated pair %" +- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority); ++ G_GUINT64_FORMAT, agent, ++ p, p->priority, highest_nominated_priority); + in_progress++; + } + } +-- +2.13.6 + + +From 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:04:49 +0200 +Subject: [PATCH 39/70] conncheck: try to change earlier to state ready + +We check if we can move from state connected to ready just +after a pair expired its retransmission count. This pair +will be marked failed, and will no longer be in-progress. +The number of in-progress dropping down to zero is one +of the conditions needed to make the transition to ready, +per component (and not globally as it's the case in other +locations where this check function is called). + +Differential Revision:
https://phabricator.freedesktop.org/D1117
+--- + agent/conncheck.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 79f678a..d31b77f 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -626,6 +626,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + { + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ NiceComponent *component; + + /* case: conncheck cancelled due to in-progress incoming + * check, requeing the pair, ICE spec, sect 7.2.1.4 +@@ -646,6 +647,16 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + priv_print_conn_check_lists (agent, G_STRFUNC, + ", retransmission failed"); + ++ /* perform a check if a transition state from connected to ++ * ready can be performed. This may happen here, when the last ++ * in-progress pair has expired its retransmission count ++ * in priv_conn_check_tick_stream(), which is a condition to ++ * make the transition connected to ready. ++ */ ++ if (agent_find_component (agent, p->stream_id, p->component_id, ++ NULL, &component)) ++ priv_update_check_list_state_for_ready (agent, stream, ++ component); + break; + } + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +-- +2.13.6 + + +From 59fe48517c0b7db77b99183d31fdd84b55adb5d4 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:12:16 +0200 +Subject: [PATCH 40/70] conncheck: fix a state transition case + +When a new stun request hits a valid pair, of a failed component, we may +have a transition from state failed to connected. In this situation, we +do a logical progression failed -> connecting -> connected, like we do +in function priv_update_check_list_state_for_ready() + +Similarily, when a new stun request hits a failed pair, of a failed +component, triggering a new conncheck for this pair may also cause the +component state to move back from failed to connecting state. + +Differential Revision:
https://phabricator.freedesktop.org/D1118
+--- + agent/conncheck.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index d31b77f..e1a5cf1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -1973,12 +1973,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + pair->nominated = TRUE; + priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { ++ if (component->state == NICE_COMPONENT_STATE_FAILED) ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } + priv_update_check_list_state_for_ready (agent, stream, component); + } else if (pair->state == NICE_CHECK_IN_PROGRESS) { + pair->mark_nominated_on_response_arrival = TRUE; +@@ -2004,13 +2006,14 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + if (pair->valid) { + priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { ++ if (component->state == NICE_COMPONENT_STATE_FAILED) ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } +- + } + priv_update_check_list_state_for_ready (agent, stream, component); + } +@@ -2854,6 +2857,14 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + pair (representing a new STUN Binding request transaction), by + enqueueing the pair in the triggered check queue. */ + priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } + } + + /* note: the spec says the we SHOULD retransmit in-progress +-- +2.13.6 + + +From f19d209decac432a1597d84c3d5809d2208f7457 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:20:49 +0200 +Subject: [PATCH 41/70] conncheck: do not recheck a just succeeded pair + +We cancel the potential in-progress transaction cancellation, caused by +sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before +transmission timeout, or just after timeout, when the pair is +temporarily put on the triggered check list on the way to be +rechecked. This situation is not covered by the RFC 5245. + +Differential Revision:
https://phabricator.freedesktop.org/D1119
+--- + agent/conncheck.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index e1a5cf1..4b785b5 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3117,6 +3117,16 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (new_pair == p) + p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; ++ /* note: we cancel the potential in-progress transaction ++ * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if ++ * we receive a valid reply before transmission timeout... ++ */ ++ p->recheck_on_timeout = FALSE; ++ /* ... or just after the transmission timeout, while the pair is ++ * temporarily put on the triggered check list on the way to be ++ * be rechecked: we remove it from the list too. ++ */ ++ priv_remove_pair_from_triggered_check_queue (agent, p); + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + nice_component_add_valid_candidate (component, remote_candidate); + } +@@ -3151,6 +3161,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ + p->state = NICE_CHECK_SUCCEEDED; ++ p->recheck_on_timeout = FALSE; ++ priv_remove_pair_from_triggered_check_queue (agent, p); + nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", + agent, p, new_pair); + } +-- +2.13.6 + + +From d516fca1b0e0a6606afec797bdc0690104e779a9 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 14 Jun 2016 21:32:26 +0200 +Subject: [PATCH 42/70] conncheck: adjust recheck on timeout strategy + +The pair recheck on timeout can easily cause repetitive rechecks in +a ping-pong effect, if both peers with the same behaviour try to +check the same pair almost simultaneously, and if the network rtt +is greater than the initial timer rto. The reply to the initial +stun request may arrive after the in-progress conncheck +cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation +creates a new stun request, and forgets the initial one. +The conncheck timer is restarted with the same initial value, +so the same situation happens again later. + +We choose to avoid resetting the timer in such situation. After enough +retransmissions, the timeout delay, that doubles after each timeout, +becomes longer than the rtt, and the stun reply can be handled. + +Differential Revision:
https://phabricator.freedesktop.org/D1115
+--- + agent/conncheck.c | 30 ++++++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 4b785b5..88d2534 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -591,7 +591,6 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + */ + nice_debug ("Agent %p : pair %p was cancelled, " + "triggering a new connection check", agent, p); +- p->recheck_on_timeout = FALSE; + priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } +@@ -2650,9 +2649,32 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + if (nice_socket_is_reliable(pair->sockptr)) { + stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); + } else { +- stun_timer_start (&pair->timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); ++ StunTimer *timer = &pair->timer; ++ ++ if (pair->recheck_on_timeout) ++ /* The pair recheck on timeout can easily cause repetitive rechecks in ++ * a ping-pong effect, if both peers with the same behaviour try to ++ * check the same pair almost simultaneously, and if the network rtt ++ * is greater than the initial timer rto. The reply to the initial ++ * stun request may arrive after the in-progress conncheck ++ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation ++ * creates a new stun request, and forgets the initial one. ++ * The conncheck timer is restarted with the same initial value, ++ * so the same situation happens again later. ++ * ++ * We choose to avoid resetting the timer in such situation. ++ * After enough retransmissions, the timeout delay becomes ++ * longer than the rtt, and the stun reply can be handled. ++ */ ++ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", ++ agent, pair, ++ timer->retransmissions, timer->max_retransmissions, ++ timer->delay - stun_timer_remainder (timer), timer->delay); ++ else ++ stun_timer_start (timer, ++ priv_compute_conncheck_timer (agent, stream), ++ agent->stun_max_retransmissions); ++ pair->recheck_on_timeout = FALSE; + } + + /* TCP-ACTIVE candidate must create a new socket before sending +-- +2.13.6 + + +From 95f8805eb7b77755337e28daf1f134587d42b35f Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 16 Jun 2016 17:32:39 +0200 +Subject: [PATCH 43/70] conncheck: remove cancelled pair state + +Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for +removal after the nomination of a pair with an higher priority, +described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They +include also pairs that overflow the conncheck list size, but this is a +somewhat more marginal situation. So we are mainly interested in the +first use case of this state. + +This state mixes two different situations, that deserve a distinct +handling : on one side, there are waiting or frozen pairs that must be +removed, this is an immediate action that doesn't need a dedicated state +for that. And on the other side, there are in-progress pairs that +should no longer be retransmitted, because another pair with a higher +priority has already been nominated. + +This patch removes the cancelled state, and adds a flag +retransmit_on_timeout to deal with this last situation. Note that this +case should not generate a triggered check, as per described in section +7.2.1.4, when the state of the pair is In-Progress or Failed, since this +pair of lower priority has no hope to replace the nominated one. + +Differential Revision:
https://phabricator.freedesktop.org/D1114
+--- + agent/conncheck.c | 142 +++++++++++++++++++++++++++++------------------------- + agent/conncheck.h | 3 +- + 2 files changed, 77 insertions(+), 68 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 88d2534..b0e2222 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -100,8 +100,6 @@ priv_state_to_gchar (NiceCheckState state) + return 'F'; + case NICE_CHECK_FROZEN: + return 'Z'; +- case NICE_CHECK_CANCELLED: +- return 'C'; + case NICE_CHECK_DISCOVERED: + return 'D'; + default: +@@ -627,6 +625,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; + NiceComponent *component; + ++timer_timeout: + /* case: conncheck cancelled due to in-progress incoming + * check, requeing the pair, ICE spec, sect 7.2.1.4 + * "Triggered Checks", "If the state of that pair is +@@ -662,6 +661,13 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + { + unsigned int timeout = stun_timer_remainder (&p->timer); + ++ /* case: retransmission stopped, due to the nomination of ++ * a pair with a higher priority than this in-progress pair, ++ * ICE spec, sect 8.1.2 "Updating States", item 2.2 ++ */ ++ if (!p->retransmit_on_timeout) ++ goto timer_timeout; ++ + /* case: conncheck cancelled due to in-progress incoming + * check, requeing the pair, ICE spec, sect 7.2.1.4 + * "Triggered Checks", "If the state of that pair is +@@ -1600,26 +1606,6 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + } + + +-static GSList *prune_cancelled_conn_check (GSList *conncheck_list) +-{ +- GSList *item = conncheck_list; +- +- while (item) { +- CandidateCheckPair *pair = item->data; +- GSList *next = item->next; +- +- if (pair->state == NICE_CHECK_CANCELLED) { +- conn_check_free_item (pair); +- conncheck_list = g_slist_delete_link (conncheck_list, item); +- } +- +- item = next; +- } +- +- return conncheck_list; +-} +- +- + /* + * Handle any processing steps for connectivity checks after + * remote credentials have been set. This function handles +@@ -1754,9 +1740,6 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + (GDestroyNotify) incoming_check_free); + component->incoming_checks = NULL; + } +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); + } + + /* +@@ -1764,7 +1747,7 @@ void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + * in ICE spec section 5.7.3 (ID-19). See also + * conn_check_add_for_candidate(). + */ +-static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) ++static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) + { + guint valid = 0; + guint cancelled = 0; +@@ -1772,22 +1755,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper + + while (item) { + CandidateCheckPair *pair = item->data; ++ GSList *next = item->next; + +- if (pair->state != NICE_CHECK_CANCELLED) { +- valid++; +- if (valid > upper_limit) { +- pair->state = NICE_CHECK_CANCELLED; ++ valid++; ++ if (valid > upper_limit) { ++ conn_check_free_item (pair); ++ conncheck_list = g_slist_delete_link (conncheck_list, item); + cancelled++; +- } + } +- +- item = item->next; ++ item = next; + } + + if (cancelled > 0) + nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements" + " left. Maximum connchecks allowed : %d", cancelled, valid, + upper_limit); ++ return conncheck_list; + } + + /* +@@ -2097,6 +2080,7 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + } + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); ++ pair->retransmit_on_timeout = TRUE; + + stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, + (GCompareFunc)conn_check_compare); +@@ -2106,7 +2090,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + /* implement the hard upper limit for number of + checks (see sect 5.7.3 ICE ID-19): */ + if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { +- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); ++ stream->conncheck_list = priv_limit_conn_check_list_size ( ++ stream->conncheck_list, agent->max_conn_checks); + } + + return pair; +@@ -2769,8 +2754,10 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + * the triggered check list should also be treated like an in-progress + * pair. + */ +- for (i = stream->conncheck_list; i; i = i->next) { ++ i = stream->conncheck_list; ++ while (i) { + CandidateCheckPair *p = i->data; ++ GSList *next = i->next; + + if (p->component_id == component_id) { + gboolean like_in_progress = +@@ -2779,19 +2766,20 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + + if (p->state == NICE_CHECK_FROZEN || + (p->state == NICE_CHECK_WAITING && !like_in_progress)) { +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); ++ nice_debug ("Agent %p : pair %p removed.", agent, p); ++ conn_check_free_item (p); ++ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- if (p->state == NICE_CHECK_IN_PROGRESS || ++ else if (p->state == NICE_CHECK_IN_PROGRESS || + (p->state == NICE_CHECK_WAITING && like_in_progress)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent %p : pair %p state CANCELED", agent, p); ++ p->retransmit_on_timeout = FALSE; ++ p->recheck_on_timeout = FALSE; ++ nice_debug ("Agent %p : pair %p will not be retransmitted.", ++ agent, p); + } else { + /* We must keep the higher priority pairs running because if a udp + * packet was lost, we might end up using a bad candidate */ +@@ -2803,6 +2791,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + } + } + } ++ i = next; + } + + return in_progress; +@@ -2841,29 +2830,42 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p = p->succeeded_pair; + } + +- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); ++ nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", ++ agent, p, p->foundation, priv_state_to_gchar (p->state)); + + if (p->state == NICE_CHECK_WAITING || +- p->state == NICE_CHECK_FROZEN) ++ p->state == NICE_CHECK_FROZEN) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); ++ } + else if (p->state == NICE_CHECK_IN_PROGRESS) { + /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" + * we cancel the in-progress transaction, and after the + * retransmission timeout, we create a new connectivity check + * for that pair. The controlling role of this new check may + * be different from the role of this cancelled check. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * so there's no reason to recheck this pair, since it can in ++ * no way replace the nominated one. + */ + if (!nice_socket_is_reliable (p->sockptr)) { +- nice_debug ("Agent %p : check already in progress, " +- "cancelling this check..", agent); +- /* this flag will determine the action at the retransmission +- * timeout of the stun timer +- */ +- p->recheck_on_timeout = TRUE; ++ if (p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p will be rechecked " ++ "on stun timer timeout.", agent, p); ++ /* this flag will determine the action at the retransmission ++ * timeout of the stun timer ++ */ ++ p->recheck_on_timeout = TRUE; ++ } else ++ nice_debug ("Agent %p : pair %p won't be retransmitted.", ++ agent, p); + } + } + else if (p->state == NICE_CHECK_SUCCEEDED) { +- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); ++ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); + /* note: this is a bit unsure corner-case -- let's do the + same state update as for processing responses to our own checks */ + /* note: this update is required by the dribble test, to +@@ -2875,18 +2877,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + } else if (p->state == NICE_CHECK_FAILED) { + /* 7.2.1.4 Triggered Checks + * If the state of the pair is Failed, it is changed to Waiting +- and the agent MUST create a new connectivity check for that +- pair (representing a new STUN Binding request transaction), by +- enqueueing the pair in the triggered check queue. */ +- priv_add_pair_to_triggered_check_queue (agent, p); +- /* If the component for this pair is in failed state, move it +- * back to connecting, and reinitiate the timers ++ * and the agent MUST create a new connectivity check for that ++ * pair (representing a new STUN Binding request transaction), by ++ * enqueueing the pair in the triggered check queue. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * we apply the same strategy than with an in-progress pair ++ * above. + */ +- if (component->state == NICE_COMPONENT_STATE_FAILED) { +- agent_signal_component_state_change (agent, stream->id, +- component->id, NICE_COMPONENT_STATE_CONNECTING); +- conn_check_schedule_next (agent); +- } ++ if (p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } ++ } else ++ nice_debug ("Agent %p : pair %p won't be retransmitted.", ++ agent, p); + } + + /* note: the spec says the we SHOULD retransmit in-progress +@@ -3401,10 +3415,6 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + } + } + +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); +- + return trans_found; + } + +diff --git a/agent/conncheck.h b/agent/conncheck.h +index c07fb22..909d469 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -56,7 +56,6 @@ + * @NICE_CHECK_SUCCEEDED: Connection successfully checked. + * @NICE_CHECK_FAILED: No connectivity; retransmissions ceased. + * @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING. +- * @NICE_CHECK_CANCELLED: Check cancelled. + * @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list. + * + * States for checking a candidate pair. +@@ -68,7 +67,6 @@ typedef enum + NICE_CHECK_SUCCEEDED, + NICE_CHECK_FAILED, + NICE_CHECK_FROZEN, +- NICE_CHECK_CANCELLED, + NICE_CHECK_DISCOVERED, + } NiceCheckState; + +@@ -89,6 +87,7 @@ struct _CandidateCheckPair + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; + gboolean recheck_on_timeout; ++ gboolean retransmit_on_timeout; + struct _CandidateCheckPair *discovered_pair; + struct _CandidateCheckPair *succeeded_pair; + guint64 priority; +-- +2.13.6 + + +From 07366a5bca7e4818b8df29d9c7c220da8f752547 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 21 Jun 2016 21:47:42 +0200 +Subject: [PATCH 44/70] conncheck: fix the component failed transition + +This patch fixes the transition of a component from connecting to +failed, that previously occured due to the propagation of the +keep_timer_going variable, and to the final call to function +priv_update_check_list_failed_components(), after the global agent +timer was stopped. + +Previously, the code almost never entered to failed state, because the +timer was going one, as long as the number of nominated pair was not +enough, and as long as there were valid pairs not yet nominated. Even +if all pair timers were over. + +The definition of the Failed state of a conncheck list is somewhat +contradictory in the spec, depending on weather you read : + + * sect 5.7.4. "Computing States", + "Failed: In this state, the ICE checks have not completed successfully + for this media stream." + + or + + * sect 7.1.3.3. "Check List and Timer State Updates", + "If all of the pairs in the check list are now either in the Failed or + Succeeded state: If there is not a pair in the valid list for each + component of the media stream, the state of the check list is set to + Failed." + +Our understanding of the ICE spec is that, the proper way to enter failed +state instead in when all connchecks have no longer in-progress pairs. +All pairs are either in state succeeded, discovered, or failed. No timer +is still running, and we have no hope that the conncheck list changes +again, except if an unexpected STUN packet arrives later. All pairs in +frozen state is a special case, that is handled separately (sect +7.1.3.3). + +A special grace delay is added before declaring a component in state +Failed. This delay is not part of the RFC, and it is aimed to limit the +cases when a conncheck list is reactivated just after it's been declared +failed, causing a user visible transition from connecting to failed, and +back from failed to connecting again. This is also required by the test +suite, that counts exactly the number of time each state is entered, and +doesn't expect these transcient failed states to happen (frequent due to +the nature of the testsuite, less frequent in real life). + +Differential Revision:
https://phabricator.freedesktop.org/D1111
+--- + agent/agent-priv.h | 14 +++++++++++ + agent/conncheck.c | 71 +++++++++++++++++++++++++++++++++++++++++++----------- + 2 files changed, 71 insertions(+), 14 deletions(-) + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 3384180..714ecff 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -122,6 +122,18 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ + (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) + ++/* A grace period before declaring a component as failed, in msecs. This ++ * delay is added to reduce the chance to see the agent receiving new ++ * stun activity just after the conncheck list has been declared failed, ++ * reactiviting conncheck activity, and causing a (valid) state ++ * transitions like that: connecting -> failed -> connecting -> ++ * connected -> ready. ++ * Such transitions are not buggy per-se, but may break the ++ * test-suite, that counts precisely the number of time each state ++ * has been set, and doesnt expect these transcient failed states. ++ */ ++#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000 ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +@@ -176,6 +188,8 @@ struct _NiceAgent + guint16 rfc4571_expecting_length; + gboolean use_ice_udp; + gboolean use_ice_tcp; ++ ++ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ + /* XXX: add pointer to internal data struct for ABI-safe extensions */ + }; + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index b0e2222..63db471 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -709,7 +709,7 @@ timer_timeout: + } + } + } +- } ++ } + + /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" + * note: This code is executed when the triggered checks list is +@@ -795,11 +795,8 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + if (s_inprogress) + keep_timer_going = TRUE; + +- /* note: if some components have established connectivity, +- * but yet no nominated pair, keep timer going */ + if (s_nominated < stream->n_components && + s_waiting_for_nomination) { +- keep_timer_going = TRUE; + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { + if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && + agent->controlling_mode) { +@@ -888,6 +885,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->recheck_on_timeout = FALSE; + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); ++ keep_timer_going = TRUE; + break; /* move to the next component */ + } + } +@@ -911,6 +909,7 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->recheck_on_timeout = FALSE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); ++ keep_timer_going = TRUE; + break; /* move to the next component */ + } + } +@@ -937,6 +936,7 @@ conn_check_stop (NiceAgent *agent) + g_source_destroy (agent->conncheck_timer_source); + g_source_unref (agent->conncheck_timer_source); + agent->conncheck_timer_source = NULL; ++ agent->conncheck_timer_grace_period = 0; + } + + +@@ -1005,9 +1005,39 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + keep_timer_going = TRUE; + } + ++ /* step: if no work left and a conncheck list of a stream is still ++ * frozen, set the pairs to waiting, according to ICE SPEC, sect ++ * 7.1.3.3. "Check List and Timer State Updates" ++ */ ++ if (!keep_timer_going) { ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (priv_is_checklist_frozen (stream)) { ++ nice_debug ("Agent %p : stream %d conncheck list is still " ++ "frozen, while other lists are completed. Unfreeze it.", ++ agent, stream->id); ++ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream); ++ } ++ } ++ } ++ ++ /* note: we provide a grace period before declaring a component as ++ * failed. Components marked connected, and then ready follow another ++ * code path, and are not concerned by this grace period. ++ */ ++ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0) ++ nice_debug ("Agent %p : waiting %d msecs before checking " ++ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD); ++ ++ if (keep_timer_going) ++ agent->conncheck_timer_grace_period = 0; ++ else ++ agent->conncheck_timer_grace_period += agent->timer_ta; ++ + /* step: stop timer if no work left */ +- if (keep_timer_going != TRUE) { +- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); ++ if (!keep_timer_going && ++ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) { ++ nice_debug ("Agent %p : checking for failed components now.", agent); + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; + priv_update_check_list_failed_components (agent, stream); +@@ -1017,6 +1047,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + } + } + ++ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", conncheck timer stopped"); + +@@ -1027,9 +1058,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + + /* XXX: what to signal, is all processing now really done? */ + nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); ++ return FALSE; + } + +- return keep_timer_going; ++ return TRUE; + } + + static gboolean priv_conn_check_tick (gpointer pointer) +@@ -1810,15 +1842,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp + * Updates the check list state. + * + * Implements parts of the algorithm described in +- * ICE sect 8.1.2. "Updating States" (ID-19): if for any ++ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any + * component, all checks have been completed and have +- * failed, mark that component's state to NICE_CHECK_FAILED. ++ * failed to produce a nominated pair, mark that component's ++ * state to NICE_CHECK_FAILED. + * + * Sends a component state changesignal via 'agent'. + */ + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) + { + GSList *i; ++ gboolean completed; ++ guint nominated; + /* note: emitting a signal might cause the client + * to remove the stream, thus the component count + * must be fetched before entering the loop*/ +@@ -1842,6 +1877,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) + continue; + ++ nominated = 0; ++ completed = TRUE; + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +@@ -1849,16 +1886,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + g_assert (p->stream_id == stream->id); + + if (p->component_id == (c + 1)) { +- if (p->state != NICE_CHECK_FAILED) +- break; ++ if (p->nominated) ++ ++nominated; ++ if (p->state != NICE_CHECK_FAILED && ++ p->state != NICE_CHECK_SUCCEEDED && ++ p->state != NICE_CHECK_DISCOVERED) ++ completed = FALSE; + } + } + +- /* note: all checks have failed ++ /* note: all pairs are either failed or succeeded, and the component ++ * has not produced a nominated pair. + * Set the component to FAILED only if it actually had remote candidates + * that failed.. */ +- if (i == NULL && comp != NULL && comp->remote_candidates != NULL) +- agent_signal_component_state_change (agent, ++ if (completed && nominated == 0 && ++ comp != NULL && comp->remote_candidates != NULL) ++ agent_signal_component_state_change (agent, + stream->id, + (c + 1), /* component-id */ + NICE_COMPONENT_STATE_FAILED); +-- +2.13.6 + + +From 195db6b344fc4f9fadc39419dfeec2fc14b23fac Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Fri, 15 Jul 2016 23:31:42 +0200 +Subject: [PATCH 45/70] agent: add new pairs only for gathering streams + +At the end of the local candidate gathering process, we only create new +pairs for streams that are in gathering state. + +Other stream that may be in ready state for example, due to a +previously succeeded conncheck process, may have accumulated some +couples (local,remote) candidates that have not resulted in the creation +a new pair during this previous conncheck process, and we don't want +these new pairs to be added now, because it would generate unneeded +transition changes for a stream unconcerned by this gathering. + +Differential Revision:
https://phabricator.freedesktop.org/D1755
+--- + agent/agent.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/agent/agent.c b/agent/agent.c +index 577a7e0..e3705ed 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -2032,6 +2032,17 @@ void agent_gathering_done (NiceAgent *agent) + + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; ++ ++ /* We ignore streams not in gathering state, typically already in ++ * ready state. Such streams may have couples (local,remote) ++ * candidates that have not resulted in the creation a new pair ++ * during a previous conncheck session, and we don't want these new ++ * pairs to be added now, because it would generate unneeded ++ * transition changes for a stream unconcerned by this gathering. ++ */ ++ if (!stream->gathering) ++ continue; ++ + for (j = stream->components; j; j = j->next) { + NiceComponent *component = j->data; + +-- +2.13.6 + + +From b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 28 May 2017 22:20:36 +0200 +Subject: [PATCH 46/70] stun: fix gcc7 implicit fallthrough warning + +Differential Revision:
https://phabricator.freedesktop.org/D1754
+--- + stun/stunmessage.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/stun/stunmessage.c b/stun/stunmessage.c +index e8184c4..4cc3392 100644 +--- a/stun/stunmessage.c ++++ b/stun/stunmessage.c +@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type, + /* Only fingerprint may come after M-I */ + if (type == STUN_ATTRIBUTE_FINGERPRINT) + break; ++ return NULL; + + case STUN_ATTRIBUTE_FINGERPRINT: + /* Nothing may come after FPR */ +-- +2.13.6 + + +From c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 18 Jun 2017 10:12:58 +0200 +Subject: [PATCH 47/70] agent: remove spurious newlines + +Differential Revision:
https://phabricator.freedesktop.org/D1756
+--- + agent/agent.c | 2 +- + agent/component.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index e3705ed..27e6193 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -3905,7 +3905,7 @@ agent_recv_message_unlocked ( + + nice_address_to_string (message->from, str); + nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" +- " %s:%d sock-type: %d\n", agent, stream->id, component->id, str, ++ " %s:%d sock-type: %d", agent, stream->id, component->id, str, + nice_address_get_port (message->from), nicesock->type); + } + +diff --git a/agent/component.c b/agent/component.c +index ab665b6..6e207d3 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -1461,7 +1461,7 @@ nice_component_add_valid_candidate (NiceComponent *component, + char str[INET6_ADDRSTRLEN]; + nice_address_to_string (&candidate->addr, str); + nice_debug ("Agent %p : %d:%d Adding valid source" +- " candidate: %s:%d trans: %d\n", component->agent, ++ " candidate: %s:%d trans: %d", component->agent, + candidate->stream_id, candidate->component_id, str, + nice_address_get_port (&candidate->addr), candidate->transport); + } +-- +2.13.6 + + +From e3ddaa285e389baf3f26cfb6964919718a8f6a00 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 21 Jun 2017 16:55:32 -0400 +Subject: [PATCH 48/70] agent: Adjust the nice_agent_new_full() to use flags + +This makes it easier to read and more extensible. +--- + agent/agent.c | 9 +++++---- + agent/agent.h | 27 ++++++++++++++++++++++----- + tests/test-nomination.c | 8 ++++---- + 3 files changed, 31 insertions(+), 13 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 27e6193..8fd8ead 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -1168,14 +1168,15 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) + NICEAPI_EXPORT NiceAgent * + nice_agent_new_full (GMainContext *ctx, + NiceCompatibility compat, +- gboolean reliable, +- NiceNominationMode nomination) ++ NiceAgentOption flags) + { + NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, + "compatibility", compat, + "main-context", ctx, +- "reliable", reliable, +- "nomination-mode", nomination, ++ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE, ++ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ? ++ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE, ++ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, + NULL); + + return agent; +diff --git a/agent/agent.h b/agent/agent.h +index 6e233c6..ed6f6e4 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -399,6 +399,25 @@ typedef enum + } NiceNominationMode; + + /** ++ * NiceAgentOption: ++ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default ++ * is aggrssive mode (see #NiceNominationMode). ++ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable(). ++ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode ++ * ++ * These are options that can be passed to nice_agent_new_full(). They set ++ * various properties on the agent. Not including them sets the property to ++ * the other value. ++ * ++ * Since: UNRELEASED ++ */ ++typedef enum { ++ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, ++ NICE_AGENT_OPTION_RELIABLE = 1 << 1, ++ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, ++} NiceAgentOption; ++ ++/** + * NiceAgentRecvFunc: + * @agent: The #NiceAgent Object + * @stream_id: The id of the stream +@@ -452,13 +471,12 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + * nice_agent_new_full: + * @ctx: The Glib Mainloop Context to use for timers + * @compat: The compatibility mode of the agent +- * @reliable: The reliability mode of the agent +- * @nomination: The nomination mode of the agent ++ * @flags: Flags to set the properties + * + * Create a new #NiceAgent with parameters that must be be defined at + * construction time. + * The returned object must be freed with g_object_unref() +- * <para> See also: #NiceNominationMode </para> ++ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * + * Since: UNRELEASED + * +@@ -467,8 +485,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + NiceAgent * + nice_agent_new_full (GMainContext *ctx, + NiceCompatibility compat, +- gboolean reliable, +- NiceNominationMode nomination); ++ NiceAgentOption flags); + + /** + * nice_agent_add_local_address: +diff --git a/tests/test-nomination.c b/tests/test-nomination.c +index b5a5e5f..bf21557 100644 +--- a/tests/test-nomination.c ++++ b/tests/test-nomination.c +@@ -140,13 +140,13 @@ run_test(NiceNominationMode l_nomination_mode, + + lagent = nice_agent_new_full (NULL, + NICE_COMPATIBILITY_RFC5245, +- FALSE, /* reliable */ +- l_nomination_mode); ++ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); + + ragent = nice_agent_new_full (NULL, + NICE_COMPATIBILITY_RFC5245, +- FALSE, /* reliable */ +- r_nomination_mode); ++ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); + + g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); + g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); +-- +2.13.6 + + +From dcb0d647174416a292492f8deca86f83a2ef124c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 21 Jun 2017 17:07:17 -0400 +Subject: [PATCH 49/70] Repleace UNRELEASED with 0.1.15 + +--- + agent/agent.c | 8 ++++---- + agent/agent.h | 6 +++--- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 8fd8ead..15af9ed 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -448,7 +448,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * the selection of valid pairs to be used upstream. + * <para> See also: #NiceNominationMode </para> + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, + g_param_spec_enum ( +@@ -744,7 +744,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * to the READY state, and on the time needed to complete the GATHERING + * state. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + + g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, +@@ -769,7 +769,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * divided by two instead (RFC 5389 indicates that a customisable + * multiplier 'Rm' to 'RTO' should be used). + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + + g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, +@@ -788,7 +788,7 @@ nice_agent_class_init (NiceAgentClass *klass) + * The initial timeout of the STUN binding requests used + * for a reliable timer. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + + g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, +diff --git a/agent/agent.h b/agent/agent.h +index ed6f6e4..520c4c5 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -390,7 +390,7 @@ typedef enum + * faster, than the regular mode, potentially causing the nominated + * pair to change until the connection check completes. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + typedef enum + { +@@ -409,7 +409,7 @@ typedef enum + * various properties on the agent. Not including them sets the property to + * the other value. + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + */ + typedef enum { + NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, +@@ -478,7 +478,7 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + * The returned object must be freed with g_object_unref() + * <para> See also: #NiceNominationMode and #NiceAgentOption</para> + * +- * Since: UNRELEASED ++ * Since: 0.1.15 + * + * Returns: The new agent GObject + */ +-- +2.13.6 + + +From 2c50d73b82f2ec2422a8e0ea393194486c193c64 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 10 Feb 2016 23:20:39 -0500 +Subject: [PATCH 50/70] agent: Don't crash if recv cancelled without a GError + +--- + agent/agent.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index 15af9ed..e48d7f3 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -4279,7 +4279,10 @@ static gboolean + nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) + { + GError **error = user_data; +- return !g_cancellable_set_error_if_cancelled (cancellable, error); ++ ++ if (error && *error) ++ g_cancellable_set_error_if_cancelled (cancellable, error); ++ return G_SOURCE_REMOVE; + } + + static gint +-- +2.13.6 + + +From 63d273cea42def3567701ad9feab91f63cf9345f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Thu, 11 Feb 2016 22:16:48 -0500 +Subject: [PATCH 51/70] component: Use non-GClosure dummy callbacks + +GClosures are not that cheap to setup +--- + agent/component.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/agent/component.c b/agent/component.c +index 6e207d3..6eee90e 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -1005,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass) + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + } + ++static gboolean ++dummy_callback (gpointer data) ++{ ++ return G_SOURCE_CONTINUE; ++} ++ ++static void ++source_set_dummy_callback (GSource *source) ++{ ++ g_source_set_callback (source, dummy_callback, NULL, NULL); ++} ++ + static void + nice_component_init (NiceComponent *component) + { +@@ -1027,7 +1039,7 @@ nice_component_init (NiceComponent *component) + component->stop_cancellable = g_cancellable_new (); + component->stop_cancellable_source = + g_cancellable_source_new (component->stop_cancellable); +- g_source_set_dummy_callback (component->stop_cancellable_source); ++ source_set_dummy_callback (component->stop_cancellable_source); + g_source_attach (component->stop_cancellable_source, component->own_ctx); + component->ctx = g_main_context_ref (component->own_ctx); + +@@ -1242,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_) + child_socket_source->source = + g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, + NULL); +- g_source_set_dummy_callback (child_socket_source->source); ++ source_set_dummy_callback (child_socket_source->source); + g_source_add_child_source (source, child_socket_source->source); + g_source_unref (child_socket_source->source); + component_source->socket_sources = +@@ -1387,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id, + GSource *cancellable_source; + + cancellable_source = g_cancellable_source_new (cancellable); +- g_source_set_dummy_callback (cancellable_source); ++ source_set_dummy_callback (cancellable_source); + g_source_add_child_source ((GSource *) component_source, + cancellable_source); + g_source_unref (cancellable_source); +-- +2.13.6 + + +From 9f800d3597767855accccc592c34bc4e945f5bd5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Wed, 21 Jun 2017 20:42:57 -0400 +Subject: [PATCH 52/70] configure: Remove -Wswitch-enum + +Creates useless warnings when other libraries change. + +https://phabricator.freedesktop.org/T7770 +--- + configure.ac | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 6c106ff..16988ad 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -154,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \ + ]) + AS_IF([test "$enable_compile_warnings" = "maximum" -o \ + "$enable_compile_warnings" = "error"],[ +- NICE_ADD_FLAG([-Wswitch-enum]) + NICE_ADD_FLAG([-Wswitch-default]) + NICE_ADD_FLAG([-Waggregate-return]) + ]) +-- +2.13.6 + + +From dbaf8f5ccd76089e340883887c7e08e6c04de80a Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 12 Apr 2016 13:22:21 +0200 +Subject: [PATCH 53/70] conncheck: improve role conflict debug + +This patch displays explicitely the controlling or controlled +role of the agent. + +Differential Revision:
https://phabricator.freedesktop.org/D874
+--- + agent/conncheck.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 63db471..8945e0f 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3134,14 +3134,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) + { + /* role conflict, change mode; wait for a new conn. check */ + if (control != agent->controlling_mode) { +- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control); ++ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".", ++ agent, control ? "controlling" : "controlled"); + agent->controlling_mode = control; + /* the pair priorities depend on the roles, so recalculation + * is needed */ + priv_recalculate_pair_priorities (agent); + } + else +- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control); ++ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", ++ agent, control ? "controlling" : "controlled"); + } + + /* +@@ -3429,13 +3431,25 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + /* case: role conflict error, need to restart with new role */ + nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); + ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ ++ + if (p->stun_message.buffer != NULL) { + guint64 tie; + gboolean controlled_mode; + +- /* note: our role might already have changed due to an +- * incoming request, but if not, change role now; +- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ + controlled_mode = (stun_message_find64 (&p->stun_message, + STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == + STUN_MESSAGE_RETURN_SUCCESS); +-- +2.13.6 + + +From 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= <olivier.crete(a)collabora.com> +Date: Tue, 5 Sep 2017 14:50:29 -0400 +Subject: [PATCH 54/70] agent: Set error if it isn't set + +--- + agent/agent.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/agent.c b/agent/agent.c +index e48d7f3..a4dcc0c 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -4280,7 +4280,7 @@ nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) + { + GError **error = user_data; + +- if (error && *error) ++ if (error && !*error) + g_cancellable_set_error_if_cancelled (cancellable, error); + return G_SOURCE_REMOVE; + } +-- +2.13.6 + + +From 25be00271a4c8c684a2d435d29ae0811dbf5e21c Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 20:36:35 +0200 +Subject: [PATCH 55/70] conncheck: reorder some chunks of code + +With this patch we simplify the levels of code indentation. + +Differential Revision:
https://phabricator.freedesktop.org/D1758
+--- + agent/conncheck.c | 858 +++++++++++++++++++++++++++--------------------------- + 1 file changed, 422 insertions(+), 436 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 8945e0f..874f7b1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -608,106 +608,106 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + gboolean keep_timer_going = FALSE; + GSList *i; + CandidateCheckPair *pair; ++ unsigned int timeout; + + /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; ++ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ NiceComponent *component; ++ ++ if (!agent_find_component (agent, p->stream_id, p->component_id, ++ NULL, &component)) ++ continue; ++ ++ if (p->state != NICE_CHECK_IN_PROGRESS) ++ continue; + +- if (p->state == NICE_CHECK_IN_PROGRESS) { +- if (p->stun_message.buffer == NULL) { +- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); +- } else if (priv_timer_expired (&p->next_tick, now)) { +- switch (stun_timer_refresh (&p->timer)) { +- case STUN_USAGE_TIMER_RETURN_TIMEOUT: +- { +- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; +- NiceComponent *component; ++ if (p->stun_message.buffer == NULL) { ++ nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); ++ p->state = NICE_CHECK_FAILED; ++ nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ continue; ++ } + ++ if (!priv_timer_expired (&p->next_tick, now)) ++ continue; ++ ++ switch (stun_timer_refresh (&p->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: + timer_timeout: +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; + +- /* case: error, abort processing */ +- nice_address_to_string (&p->local->addr, tmpbuf1); +- nice_address_to_string (&p->remote->addr, tmpbuf2); +- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); +- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, +- tmpbuf1, nice_address_get_port (&p->local->addr), +- tmpbuf2, nice_address_get_port (&p->remote->addr)); +- candidate_check_pair_fail (stream, agent, p); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", retransmission failed"); +- +- /* perform a check if a transition state from connected to +- * ready can be performed. This may happen here, when the last +- * in-progress pair has expired its retransmission count +- * in priv_conn_check_tick_stream(), which is a condition to +- * make the transition connected to ready. +- */ +- if (agent_find_component (agent, p->stream_id, p->component_id, +- NULL, &component)) +- priv_update_check_list_state_for_ready (agent, stream, +- component); +- break; +- } +- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +- { +- unsigned int timeout = stun_timer_remainder (&p->timer); ++ /* case: error, abort processing */ ++ nice_address_to_string (&p->local->addr, tmpbuf1); ++ nice_address_to_string (&p->remote->addr, tmpbuf2); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, ++ tmpbuf1, nice_address_get_port (&p->local->addr), ++ tmpbuf2, nice_address_get_port (&p->remote->addr)); ++ candidate_check_pair_fail (stream, agent, p); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", retransmission failed"); ++ ++ /* perform a check if a transition state from connected to ++ * ready can be performed. This may happen here, when the last ++ * in-progress pair has expired its retransmission count ++ * in priv_conn_check_tick_stream(), which is a condition to ++ * make the transition connected to ready. ++ */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ break; ++ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: ++ timeout = stun_timer_remainder (&p->timer); + +- /* case: retransmission stopped, due to the nomination of +- * a pair with a higher priority than this in-progress pair, +- * ICE spec, sect 8.1.2 "Updating States", item 2.2 +- */ +- if (!p->retransmit_on_timeout) +- goto timer_timeout; ++ /* case: retransmission stopped, due to the nomination of ++ * a pair with a higher priority than this in-progress pair, ++ * ICE spec, sect 8.1.2 "Updating States", item 2.2 ++ */ ++ if (!p->retransmit_on_timeout) ++ goto timer_timeout; + +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ /* case: conncheck cancelled due to in-progress incoming ++ * check, requeing the pair, ICE spec, sect 7.2.1.4 ++ * "Triggered Checks", "If the state of that pair is ++ * In-Progress..." ++ */ ++ if (priv_conn_recheck_on_timeout (agent, p)) ++ break; + +- /* case: not ready, so schedule a new timeout */ +- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " +- "(timeout %dms, delay=%dms, retrans=%d).", +- agent, p, timeout, p->timer.delay, p->timer.retransmissions); ++ /* case: not ready, so schedule a new timeout */ ++ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " ++ "(timeout %dms, delay=%dms, retrans=%d).", ++ agent, p, timeout, p->timer.delay, p->timer.retransmissions); + +- agent_socket_send (p->sockptr, &p->remote->addr, +- stun_message_length (&p->stun_message), +- (gchar *)p->stun_buffer); ++ agent_socket_send (p->sockptr, &p->remote->addr, ++ stun_message_length (&p->stun_message), ++ (gchar *)p->stun_buffer); + + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ p->next_tick = *now; ++ g_time_val_add (&p->next_tick, timeout * 1000); + +- return TRUE; +- } +- case STUN_USAGE_TIMER_RETURN_SUCCESS: +- { +- unsigned int timeout = stun_timer_remainder (&p->timer); ++ return TRUE; ++ case STUN_USAGE_TIMER_RETURN_SUCCESS: ++ timeout = stun_timer_remainder (&p->timer); + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ p->next_tick = *now; ++ g_time_val_add (&p->next_tick, timeout * 1000); + +- keep_timer_going = TRUE; +- break; +- } +- default: +- /* Nothing to do. */ +- break; +- } +- } ++ keep_timer_going = TRUE; ++ break; ++ default: ++ /* Nothing to do. */ ++ break; + } + } + +@@ -2628,27 +2628,23 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { + switch (agent->nomination_mode) { + case NICE_NOMINATION_MODE_REGULAR: +- { +- /* We are doing regular nomination, so we set the use-candidate +- * attrib, when the controlling agent decided which valid pair to +- * resend with this flag in priv_conn_check_tick_stream() +- */ +- cand_use = pair->use_candidate_on_next_check; +- nice_debug ("Agent %p : %s: set cand_use=%d " +- "(regular nomination).", agent, G_STRFUNC, cand_use); +- break; +- } ++ /* We are doing regular nomination, so we set the use-candidate ++ * attrib, when the controlling agent decided which valid pair to ++ * resend with this flag in priv_conn_check_tick_stream() ++ */ ++ cand_use = pair->use_candidate_on_next_check; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(regular nomination).", agent, G_STRFUNC, cand_use); ++ break; + case NICE_NOMINATION_MODE_AGGRESSIVE: +- { +- /* We are doing aggressive nomination, we set the use-candidate +- * attrib in every check we send, when we are the controlling +- * agent, RFC 5245, 8.1.1.2 +- */ +- cand_use = controlling; +- nice_debug ("Agent %p : %s: set cand_use=%d " +- "(aggressive nomination).", agent, G_STRFUNC, cand_use); +- break; +- } ++ /* We are doing aggressive nomination, we set the use-candidate ++ * attrib in every check we send, when we are the controlling ++ * agent, RFC 5245, 8.1.1.2 ++ */ ++ cand_use = controlling; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(aggressive nomination).", agent, G_STRFUNC, cand_use); ++ break; + default: + /* Nothing to do. */ + break; +@@ -2656,107 +2652,105 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + } else if (cand_use) + pair->nominated = controlling; + +- if (uname_len > 0) { +- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, +- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), +- uname, uname_len, password, password_len, +- cand_use, controlling, pair->prflx_priority, +- agent->tie_breaker, +- pair->local->foundation, +- agent_to_ice_compatibility (agent)); ++ if (uname_len == 0) { ++ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); ++ pair->stun_message.buffer = NULL; ++ pair->stun_message.buffer_len = 0; ++ return -1; ++ } + +- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, +- pair->stun_message.buffer); ++ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, ++ &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), ++ uname, uname_len, password, password_len, ++ cand_use, controlling, pair->prflx_priority, ++ agent->tie_breaker, ++ pair->local->foundation, ++ agent_to_ice_compatibility (agent)); + +- if (agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- g_free (password); +- } ++ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, ++ pair->stun_message.buffer); + +- if (buffer_len > 0) { +- if (nice_socket_is_reliable(pair->sockptr)) { +- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); +- } else { +- StunTimer *timer = &pair->timer; +- +- if (pair->recheck_on_timeout) +- /* The pair recheck on timeout can easily cause repetitive rechecks in +- * a ping-pong effect, if both peers with the same behaviour try to +- * check the same pair almost simultaneously, and if the network rtt +- * is greater than the initial timer rto. The reply to the initial +- * stun request may arrive after the in-progress conncheck +- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation +- * creates a new stun request, and forgets the initial one. +- * The conncheck timer is restarted with the same initial value, +- * so the same situation happens again later. +- * +- * We choose to avoid resetting the timer in such situation. +- * After enough retransmissions, the timeout delay becomes +- * longer than the rtt, and the stun reply can be handled. +- */ +- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", +- agent, pair, +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay); +- else +- stun_timer_start (timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); +- pair->recheck_on_timeout = FALSE; +- } ++ if (agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ g_free (password); ++ } + +- /* TCP-ACTIVE candidate must create a new socket before sending +- * by connecting to the peer. The new socket is stored in the candidate +- * check pair, until we discover a new local peer reflexive */ +- if (pair->sockptr->fileno == NULL && +- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && +- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { +- NiceStream *stream2 = NULL; +- NiceComponent *component2 = NULL; +- NiceSocket *new_socket; +- +- if (agent_find_component (agent, pair->stream_id, pair->component_id, +- &stream2, &component2)) { +- new_socket = nice_tcp_active_socket_connect (pair->sockptr, +- &pair->remote->addr); +- if (new_socket) { +- pair->sockptr = new_socket; +- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); +- +- if (agent->reliable) { +- nice_socket_set_writable_callback (pair->sockptr, +- _tcp_sock_is_writable, component2); +- } ++ if (buffer_len == 0) { ++ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); ++ pair->stun_message.buffer = NULL; ++ pair->stun_message.buffer_len = 0; ++ return -1; ++ } + +- nice_component_attach_socket (component2, new_socket); +- } +- } +- } +- /* send the conncheck */ +- agent_socket_send (pair->sockptr, &pair->remote->addr, +- buffer_len, (gchar *)pair->stun_buffer); ++ if (nice_socket_is_reliable(pair->sockptr)) ++ stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); ++ else { ++ StunTimer *timer = &pair->timer; ++ ++ if (pair->recheck_on_timeout) ++ /* The pair recheck on timeout can easily cause repetitive rechecks in ++ * a ping-pong effect, if both peers with the same behaviour try to ++ * check the same pair almost simultaneously, and if the network rtt ++ * is greater than the initial timer rto. The reply to the initial ++ * stun request may arrive after the in-progress conncheck ++ * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation ++ * creates a new stun request, and forgets the initial one. ++ * The conncheck timer is restarted with the same initial value, ++ * so the same situation happens again later. ++ * ++ * We choose to avoid resetting the timer in such situation. ++ * After enough retransmissions, the timeout delay becomes ++ * longer than the rtt, and the stun reply can be handled. ++ */ ++ nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", ++ agent, pair, ++ timer->retransmissions, timer->max_retransmissions, ++ timer->delay - stun_timer_remainder (timer), timer->delay); ++ else ++ stun_timer_start (timer, ++ priv_compute_conncheck_timer (agent, stream), ++ agent->stun_max_retransmissions); ++ pair->recheck_on_timeout = FALSE; ++ } + +- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { +- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, +- &pair->remote->addr); ++ /* TCP-ACTIVE candidate must create a new socket before sending ++ * by connecting to the peer. The new socket is stored in the candidate ++ * check pair, until we discover a new local peer reflexive */ ++ if (pair->sockptr->fileno == NULL && ++ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && ++ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { ++ NiceStream *stream2 = NULL; ++ NiceComponent *component2 = NULL; ++ NiceSocket *new_socket; ++ ++ if (agent_find_component (agent, pair->stream_id, pair->component_id, ++ &stream2, &component2)) { ++ new_socket = nice_tcp_active_socket_connect (pair->sockptr, ++ &pair->remote->addr); ++ if (new_socket) { ++ pair->sockptr = new_socket; ++ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); ++ ++ if (agent->reliable) ++ nice_socket_set_writable_callback (pair->sockptr, ++ _tcp_sock_is_writable, component2); ++ ++ nice_component_attach_socket (component2, new_socket); + } +- +- timeout = stun_timer_remainder (&pair->timer); +- /* note: convert from milli to microseconds for g_time_val_add() */ +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); +- } else { +- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; +- return -1; + } +- } else { +- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; +- return -1; + } ++ /* send the conncheck */ ++ agent_socket_send (pair->sockptr, &pair->remote->addr, ++ buffer_len, (gchar *)pair->stun_buffer); ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, ++ &pair->remote->addr); ++ ++ timeout = stun_timer_remainder (&pair->timer); ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ g_get_current_time (&pair->next_tick); ++ g_time_val_add (&pair->next_tick, timeout * 1000); + + return 0; + } +@@ -2876,74 +2870,74 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", + agent, p, p->foundation, priv_state_to_gchar (p->state)); + +- if (p->state == NICE_CHECK_WAITING || +- p->state == NICE_CHECK_FROZEN) { +- nice_debug ("Agent %p : pair %p added for a triggered check.", +- agent, p); +- priv_add_pair_to_triggered_check_queue (agent, p); +- } +- else if (p->state == NICE_CHECK_IN_PROGRESS) { +- /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" +- * we cancel the in-progress transaction, and after the +- * retransmission timeout, we create a new connectivity check +- * for that pair. The controlling role of this new check may +- * be different from the role of this cancelled check. +- * +- * note: the flag retransmit_on_timeout unset means that +- * another pair, with a higher priority is already nominated, +- * so there's no reason to recheck this pair, since it can in +- * no way replace the nominated one. +- */ +- if (!nice_socket_is_reliable (p->sockptr)) { +- if (p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p will be rechecked " +- "on stun timer timeout.", agent, p); +- /* this flag will determine the action at the retransmission +- * timeout of the stun timer +- */ +- p->recheck_on_timeout = TRUE; +- } else +- nice_debug ("Agent %p : pair %p won't be retransmitted.", +- agent, p); +- } +- } +- else if (p->state == NICE_CHECK_SUCCEEDED) { +- nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); +- /* note: this is a bit unsure corner-case -- let's do the +- same state update as for processing responses to our own checks */ +- /* note: this update is required by the dribble test, to +- * ensure the transition ready -> connected -> ready, because +- * an incoming stun request generates a discovered peer reflexive, +- * that causes the ready -> connected transition. +- */ +- priv_update_check_list_state_for_ready (agent, stream, component); +- } else if (p->state == NICE_CHECK_FAILED) { +- /* 7.2.1.4 Triggered Checks +- * If the state of the pair is Failed, it is changed to Waiting +- * and the agent MUST create a new connectivity check for that +- * pair (representing a new STUN Binding request transaction), by +- * enqueueing the pair in the triggered check queue. +- * +- * note: the flag retransmit_on_timeout unset means that +- * another pair, with a higher priority is already nominated, +- * we apply the same strategy than with an in-progress pair +- * above. +- */ +- if (p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p added for a triggered check.", ++ switch (p->state) { ++ case NICE_CHECK_WAITING: ++ case NICE_CHECK_FROZEN: ++ nice_debug ("Agent %p : pair %p added for a triggered check.", + agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); +- /* If the component for this pair is in failed state, move it +- * back to connecting, and reinitiate the timers ++ break; ++ case NICE_CHECK_IN_PROGRESS: ++ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" ++ * we cancel the in-progress transaction, and after the ++ * retransmission timeout, we create a new connectivity check ++ * for that pair. The controlling role of this new check may ++ * be different from the role of this cancelled check. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * so there's no reason to recheck this pair, since it can in ++ * no way replace the nominated one. + */ +- if (component->state == NICE_COMPONENT_STATE_FAILED) { +- agent_signal_component_state_change (agent, stream->id, +- component->id, NICE_COMPONENT_STATE_CONNECTING); +- conn_check_schedule_next (agent); ++ if (!nice_socket_is_reliable (p->sockptr) && ++ p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p will be rechecked " ++ "on stun timer timeout.", agent, p); ++ /* this flag will determine the action at the retransmission ++ * timeout of the stun timer ++ */ ++ p->recheck_on_timeout = TRUE; + } +- } else +- nice_debug ("Agent %p : pair %p won't be retransmitted.", +- agent, p); ++ break; ++ case NICE_CHECK_SUCCEEDED: ++ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); ++ /* note: this is a bit unsure corner-case -- let's do the ++ same state update as for processing responses to our own checks */ ++ /* note: this update is required by the dribble test, to ++ * ensure the transition ready -> connected -> ready, because ++ * an incoming stun request generates a discovered peer reflexive, ++ * that causes the ready -> connected transition. ++ */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ break; ++ case NICE_CHECK_FAILED: ++ /* 7.2.1.4 Triggered Checks ++ * If the state of the pair is Failed, it is changed to Waiting ++ * and the agent MUST create a new connectivity check for that ++ * pair (representing a new STUN Binding request transaction), by ++ * enqueueing the pair in the triggered check queue. ++ * ++ * note: the flag retransmit_on_timeout unset means that ++ * another pair, with a higher priority is already nominated, ++ * we apply the same strategy than with an in-progress pair ++ * above. ++ */ ++ if (p->retransmit_on_timeout) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } ++ } ++ break; ++ default: ++ break; + } + + /* note: the spec says the we SHOULD retransmit in-progress +@@ -3271,208 +3265,200 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + socklen_t socklen = sizeof (sockaddr); + GSList *i; + StunUsageIceReturn res; +- gboolean trans_found = FALSE; + StunTransactionId discovery_id; + StunTransactionId response_id; + stun_message_id (resp, response_id); + +- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) { ++ for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +- if (p->stun_message.buffer) { +- stun_message_id (&p->stun_message, discovery_id); ++ if (p->stun_message.buffer == NULL) ++ continue; + +- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) { +- res = stun_usage_ice_conncheck_process (resp, +- &sockaddr.storage, &socklen, +- agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " +- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); +- +- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || +- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* case: found a matching connectivity check request */ +- +- CandidateCheckPair *ok_pair = NULL; +- +- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- +- /* step: verify that response came from the same IP address we +- * sent the original request to (see 7.1.2.1. "Failure +- * Cases") */ +- if (nice_address_equal (from, &p->remote->addr) != TRUE) { +- +- p->state = NICE_CHECK_FAILED; +- if (nice_debug_is_enabled ()) { +- gchar tmpbuf[INET6_ADDRSTRLEN]; +- gchar tmpbuf2[INET6_ADDRSTRLEN]; +- nice_debug ("Agent %p : conncheck %p FAILED" +- " (mismatch of source address).", agent, p); +- nice_address_to_string (&p->remote->addr, tmpbuf); +- nice_address_to_string (from, tmpbuf2); +- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, +- tmpbuf, nice_address_get_port (&p->remote->addr), +- tmpbuf2, nice_address_get_port (from)); +- } +- trans_found = TRUE; +- break; +- } ++ stun_message_id (&p->stun_message, discovery_id); + +- /* note: CONNECTED but not yet READY, see docs */ +- +- /* step: handle the possible case of a peer-reflexive +- * candidate where the mapped-address in response does +- * not match any local candidate, see 7.1.2.2.1 +- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ +- +- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* note: this is same as "adding to VALID LIST" in the spec +- text */ +- p->state = NICE_CHECK_SUCCEEDED; +- p->valid = TRUE; +- g_assert_not_reached (); +- nice_debug ("Agent %p : Mapped address not found." +- " conncheck %p SUCCEEDED.", agent, p); +- nice_component_add_valid_candidate (component, p->remote); +- } else { +- ok_pair = priv_process_response_check_for_reflexive (agent, +- stream, component, p, sockptr, &sockaddr.addr, +- local_candidate, remote_candidate); +- } ++ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) ++ continue; + +- /* note: The success of this check might also +- * cause the state of other checks to change as well, ICE +- * spec 7.1.3.2.3 +- */ +- priv_conn_check_unfreeze_related (agent, stream, p); +- +- /* Note: this assignment helps to reduce the numbers of cases +- * to be tested. If ok_pair and p refer to distinct pairs, it +- * means that ok_pair is a discovered peer reflexive one, +- * caused by the check made on pair p. In that case, the +- * flags to be tested are on p, but the nominated flag will be +- * set on ok_pair. When there's no discovered pair, p and +- * ok_pair refer to the same pair. +- * To summarize : p is a SUCCEEDED pair, ok_pair is a +- * DISCOVERED, VALID, and eventually NOMINATED pair. +- */ +- if (!ok_pair) +- ok_pair = p; +- +- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the +- Nominated Flag" (ID-19) */ +- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { +- nice_debug ("Agent %p : Updating nominated flag (%s): " +- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", +- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? +- "UDP" : "TCP", +- ok_pair, ok_pair->use_candidate_on_next_check, +- ok_pair->mark_nominated_on_response_arrival, +- p, p->use_candidate_on_next_check, +- p->mark_nominated_on_response_arrival); +- +- if (agent->controlling_mode) { +- switch (agent->nomination_mode) { +- case NICE_NOMINATION_MODE_REGULAR: +- if (p->use_candidate_on_next_check) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(regular nomination, control=1, " +- "use_cand_on_next_check=1).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- case NICE_NOMINATION_MODE_AGGRESSIVE: +- if (!p->nominated) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(aggressive nomination, control=1).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- default: +- /* Nothing to do */ +- break; ++ res = stun_usage_ice_conncheck_process (resp, ++ &sockaddr.storage, &socklen, ++ agent_to_ice_compatibility (agent)); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " ++ "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); ++ ++ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || ++ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ /* case: found a matching connectivity check request */ ++ ++ CandidateCheckPair *ok_pair = NULL; ++ ++ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; ++ ++ /* step: verify that response came from the same IP address we ++ * sent the original request to (see 7.1.2.1. "Failure ++ * Cases") */ ++ if (nice_address_equal (from, &p->remote->addr) == FALSE) { ++ ++ p->state = NICE_CHECK_FAILED; ++ if (nice_debug_is_enabled ()) { ++ gchar tmpbuf[INET6_ADDRSTRLEN]; ++ gchar tmpbuf2[INET6_ADDRSTRLEN]; ++ nice_debug ("Agent %p : conncheck %p FAILED" ++ " (mismatch of source address).", agent, p); ++ nice_address_to_string (&p->remote->addr, tmpbuf); ++ nice_address_to_string (from, tmpbuf2); ++ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, ++ tmpbuf, nice_address_get_port (&p->remote->addr), ++ tmpbuf2, nice_address_get_port (from)); ++ } ++ return TRUE; ++ } ++ ++ /* note: CONNECTED but not yet READY, see docs */ ++ ++ /* step: handle the possible case of a peer-reflexive ++ * candidate where the mapped-address in response does ++ * not match any local candidate, see 7.1.2.2.1 ++ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ ++ ++ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ p->state = NICE_CHECK_SUCCEEDED; ++ p->valid = TRUE; ++ nice_debug ("Agent %p : Mapped address not found." ++ " conncheck %p SUCCEEDED.", agent, p); ++ nice_component_add_valid_candidate (component, p->remote); ++ } else ++ ok_pair = priv_process_response_check_for_reflexive (agent, ++ stream, component, p, sockptr, &sockaddr.addr, ++ local_candidate, remote_candidate); ++ ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ ++ if (!ok_pair) ++ ok_pair = p; ++ ++ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the ++ Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, control=1, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; + } +- } else { +- if (p->mark_nominated_on_response_arrival) { ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(%s nomination, control=0, mark_on_response=1).", +- agent, ok_pair, ok_pair->foundation, +- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? +- "aggressive" : "regular"); ++ "(aggressive nomination, control=1).", ++ agent, ok_pair, ok_pair->foundation); + ok_pair->nominated = TRUE; + } +- } ++ break; ++ default: ++ /* Nothing to do */ ++ break; + } +- +- if (ok_pair->nominated == TRUE) { +- priv_update_selected_pair (agent, component, ok_pair); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a nominated pair"); +- +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, control=0, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; + } ++ } ++ } + +- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair +- states" and 8.1.2 "Updating States", ID-19) */ +- priv_update_check_list_state_for_ready (agent, stream, component); ++ if (ok_pair->nominated == TRUE) { ++ priv_update_selected_pair (agent, component, ok_pair); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a nominated pair"); + +- trans_found = TRUE; +- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { +- /* case: role conflict error, need to restart with new role */ +- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- +- /* note: this res value indicates that the role of the peer +- * agent has not changed after the tie-breaker comparison, so +- * this is our role that must change. see ICE sect. 7.1.3.1 +- * "Failure Cases". Our role might already have changed due to +- * an earlier incoming request, but if not, change role now. +- * +- * Sect. 7.1.3.1 is not clear on this point, but we choose to +- * put the candidate pair in the triggered check list even +- * when the agent did not switch its role. The reason for this +- * interpretation is that the reception of the stun reply, even +- * an error reply, is a good sign that this pair will be +- * valid, if we retry the check after the role of both peers +- * has been fixed. +- */ ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } + +- if (p->stun_message.buffer != NULL) { +- guint64 tie; +- gboolean controlled_mode; ++ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair ++ states" and 8.1.2 "Updating States", ID-19) */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { ++ /* case: role conflict error, need to restart with new role */ ++ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); ++ ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ + +- controlled_mode = (stun_message_find64 (&p->stun_message, +- STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == +- STUN_MESSAGE_RETURN_SUCCESS); ++ if (p->stun_message.buffer != NULL) { ++ guint64 tie; ++ gboolean controlled_mode; + +- priv_check_for_role_conflict (agent, controlled_mode); ++ controlled_mode = (stun_message_find64 (&p->stun_message, ++ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == ++ STUN_MESSAGE_RETURN_SUCCESS); + +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- priv_add_pair_to_triggered_check_queue (agent, p); +- } +- trans_found = TRUE; +- } else { +- /* case: STUN error, the check STUN context was freed */ +- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- trans_found = TRUE; +- } ++ priv_check_for_role_conflict (agent, controlled_mode); ++ ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; ++ priv_add_pair_to_triggered_check_queue (agent, p); + } ++ } else { ++ /* case: STUN error, the check STUN context was freed */ ++ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); ++ p->stun_message.buffer = NULL; ++ p->stun_message.buffer_len = 0; + } ++ return TRUE; + } + +- return trans_found; ++ return FALSE; + } + + /* +-- +2.13.6 + + +From ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 20:41:49 +0200 +Subject: [PATCH 56/70] conncheck: make debug sentences more accurate + +We add a helper function to print the pair state in-extenso. + +Differential Revision:
https://phabricator.freedesktop.org/D1759
+--- + agent/conncheck.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 61 insertions(+), 9 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 874f7b1..9517ee1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -108,6 +108,54 @@ priv_state_to_gchar (NiceCheckState state) + } + + static const gchar * ++priv_state_to_string (NiceCheckState state) ++{ ++ switch (state) { ++ case NICE_CHECK_WAITING: ++ return "waiting"; ++ case NICE_CHECK_IN_PROGRESS: ++ return "in progress"; ++ case NICE_CHECK_SUCCEEDED: ++ return "succeeded"; ++ case NICE_CHECK_FAILED: ++ return "failed"; ++ case NICE_CHECK_FROZEN: ++ return "frozen"; ++ case NICE_CHECK_DISCOVERED: ++ return "discovered"; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ ++static const gchar * ++priv_ice_return_to_string (StunUsageIceReturn ice_return) ++{ ++ switch (ice_return) { ++ case STUN_USAGE_ICE_RETURN_SUCCESS: ++ return "success"; ++ case STUN_USAGE_ICE_RETURN_ERROR: ++ return "error"; ++ case STUN_USAGE_ICE_RETURN_INVALID: ++ return "invalid"; ++ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT: ++ return "role conflict"; ++ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST: ++ return "invalid request"; ++ case STUN_USAGE_ICE_RETURN_INVALID_METHOD: ++ return "invalid method"; ++ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR: ++ return "memory error"; ++ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS: ++ return "invalid address"; ++ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS: ++ return "no mapped address"; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ ++static const gchar * + priv_candidate_type_to_string (NiceCandidateType type) + { + switch (type) { +@@ -2614,7 +2662,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + nice_address_to_string (&pair->remote->addr, tmpbuf2); + nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " + "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " +- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, ++ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr), + pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, +@@ -2622,7 +2670,8 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + (unsigned long long)agent->tie_breaker, + (int) uname_len, uname, uname_len, + (int) password_len, password, password_len, +- pair->prflx_priority, controlling); ++ pair->prflx_priority, ++ controlling ? "controlling" : "controlled"); + } + + if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { +@@ -2867,8 +2916,8 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + p = p->succeeded_pair; + } + +- nice_debug ("Agent %p : Found a matching pair %p (%s) (state=%c) ...", +- agent, p, p->foundation, priv_state_to_gchar (p->state)); ++ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", ++ agent, p, p->foundation, priv_state_to_string (p->state)); + + switch (p->state) { + case NICE_CHECK_WAITING: +@@ -3283,8 +3332,11 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + res = stun_usage_ice_conncheck_process (resp, + &sockaddr.storage, &socklen, + agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " +- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " ++ "%s,res=%s.", ++ agent, p, ++ agent->controlling_mode ? "controlling" : "controlled", ++ priv_ice_return_to_string (res)); + + if (res == STUN_USAGE_ICE_RETURN_SUCCESS || + res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +@@ -3370,7 +3422,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + case NICE_NOMINATION_MODE_REGULAR: + if (p->use_candidate_on_next_check) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(regular nomination, control=1, " ++ "(regular nomination, controlling, " + "use_cand_on_next_check=1).", + agent, ok_pair, ok_pair->foundation); + ok_pair->nominated = TRUE; +@@ -3379,7 +3431,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + case NICE_NOMINATION_MODE_AGGRESSIVE: + if (!p->nominated) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(aggressive nomination, control=1).", ++ "(aggressive nomination, controlling).", + agent, ok_pair, ok_pair->foundation); + ok_pair->nominated = TRUE; + } +@@ -3391,7 +3443,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + } else { + if (p->mark_nominated_on_response_arrival) { + nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(%s nomination, control=0, mark_on_response=1).", ++ "(%s nomination, controlled, mark_on_response=1).", + agent, ok_pair, ok_pair->foundation, + agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? + "aggressive" : "regular"); +-- +2.13.6 + + +From e860948b5fe3a791119957f26045b8f5159baeff Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 21:06:36 +0200 +Subject: [PATCH 57/70] conncheck: use stun_timer_remainder less frequently + +We try to use stun_timer_remainder() less frequently, particularily +in the debug messages, and favour of the next_tick value associated +to the pair. + +Differential Revision:
https://phabricator.freedesktop.org/D1760
+--- + agent/conncheck.c | 77 ++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 48 insertions(+), 29 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 9517ee1..8075d4f 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -86,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) + now->tv_sec >= timer->tv_sec; + } + ++static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now) ++{ ++ unsigned int delay; ++ if (now->tv_sec > timer->tv_sec || ++ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec)) ++ return 0; ++ delay = (timer->tv_sec - now->tv_sec) * 1000; ++ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000; ++ return delay; ++} ++ + static gchar + priv_state_to_gchar (NiceCheckState state) + { +@@ -180,10 +191,13 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + { + GSList *i, *k; + guint j; ++ GTimeVal now; + + if (!nice_debug_is_verbose ()) + return; + ++ g_get_current_time (&now); ++ + #define PRIORITY_LEN 32 + + nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", +@@ -209,7 +223,8 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + priv_candidate_type_to_string (pair->local->type), + priv_candidate_type_to_string (pair->remote->type), + timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay, ++ timer->delay - priv_timer_remainder (&pair->next_tick, &now), ++ timer->delay, + local_addr, nice_address_get_port (&pair->local->addr), + remote_addr, nice_address_get_port (&pair->remote->addr), + priv_state_to_gchar (pair->state), +@@ -445,8 +460,6 @@ priv_find_first_frozen_check_list (NiceAgent *agent) + */ + static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) + { +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); + pair->state = NICE_CHECK_IN_PROGRESS; + nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); + conn_check_send (agent, pair); +@@ -651,12 +664,15 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + * + * @return will return FALSE when no more pending timers. + */ +-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now) ++static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) + { + gboolean keep_timer_going = FALSE; + GSList *i; + CandidateCheckPair *pair; + unsigned int timeout; ++ GTimeVal now; ++ ++ g_get_current_time (&now); + + /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { +@@ -678,7 +694,7 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + continue; + } + +- if (!priv_timer_expired (&p->next_tick, now)) ++ if (!priv_timer_expired (&p->next_tick, &now)) + continue; + + switch (stun_timer_refresh (&p->timer)) { +@@ -712,8 +728,6 @@ timer_timeout: + priv_update_check_list_state_for_ready (agent, stream, component); + break; + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +- timeout = stun_timer_remainder (&p->timer); +- + /* case: retransmission stopped, due to the nomination of + * a pair with a higher priority than this in-progress pair, + * ICE spec, sect 8.1.2 "Updating States", item 2.2 +@@ -730,9 +744,13 @@ timer_timeout: + break; + + /* case: not ready, so schedule a new timeout */ ++ timeout = stun_timer_remainder (&p->timer); ++ + nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " +- "(timeout %dms, delay=%dms, retrans=%d).", +- agent, p, timeout, p->timer.delay, p->timer.retransmissions); ++ "(timer=%d/%d %d/%dms).", ++ agent, p, ++ p->timer.retransmissions, p->timer.max_retransmissions, ++ p->timer.delay - timeout, p->timer.delay); + + agent_socket_send (p->sockptr, &p->remote->addr, + stun_message_length (&p->stun_message), +@@ -740,7 +758,7 @@ timer_timeout: + + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; ++ p->next_tick = now; + g_time_val_add (&p->next_tick, timeout * 1000); + + return TRUE; +@@ -748,7 +766,7 @@ timer_timeout: + timeout = stun_timer_remainder (&p->timer); + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; ++ p->next_tick = now; + g_time_val_add (&p->next_tick, timeout * 1000); + + keep_timer_going = TRUE; +@@ -1001,9 +1019,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + CandidateCheckPair *pair = NULL; + gboolean keep_timer_going = FALSE; + GSList *i, *j; +- GTimeVal now; +- +- g_get_current_time (&now); + + /* the conncheck really starts when we have built + * a connection check list for each stream +@@ -1047,7 +1062,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + */ + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +- if (priv_conn_check_tick_stream (stream, agent, &now)) ++ if (priv_conn_check_tick_stream (stream, agent)) + keep_timer_going = TRUE; + if (priv_conn_check_tick_stream_nominate (stream, agent)) + keep_timer_going = TRUE; +@@ -2731,12 +2746,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + return -1; + } + +- if (nice_socket_is_reliable(pair->sockptr)) +- stun_timer_start_reliable(&pair->timer, agent->stun_reliable_timeout); +- else { ++ if (nice_socket_is_reliable(pair->sockptr)) { ++ timeout = agent->stun_reliable_timeout; ++ stun_timer_start_reliable(&pair->timer, timeout); ++ } else { + StunTimer *timer = &pair->timer; + +- if (pair->recheck_on_timeout) ++ if (pair->recheck_on_timeout) { ++ GTimeVal now; + /* The pair recheck on timeout can easily cause repetitive rechecks in + * a ping-pong effect, if both peers with the same behaviour try to + * check the same pair almost simultaneously, and if the network rtt +@@ -2751,17 +2768,24 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + * After enough retransmissions, the timeout delay becomes + * longer than the rtt, and the stun reply can be handled. + */ ++ ++ g_get_current_time (&now); ++ timeout = priv_timer_remainder (&pair->next_tick, &now); + nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", + agent, pair, + timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay); +- else +- stun_timer_start (timer, +- priv_compute_conncheck_timer (agent, stream), +- agent->stun_max_retransmissions); ++ timer->delay - timeout, ++ timer->delay); ++ } else { ++ timeout = priv_compute_conncheck_timer (agent, stream); ++ stun_timer_start (timer, timeout, agent->stun_max_retransmissions); ++ } + pair->recheck_on_timeout = FALSE; + } + ++ g_get_current_time (&pair->next_tick); ++ g_time_val_add (&pair->next_tick, timeout * 1000); ++ + /* TCP-ACTIVE candidate must create a new socket before sending + * by connecting to the peer. The new socket is stored in the candidate + * check pair, until we discover a new local peer reflexive */ +@@ -2796,11 +2820,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, + &pair->remote->addr); + +- timeout = stun_timer_remainder (&pair->timer); +- /* note: convert from milli to microseconds for g_time_val_add() */ +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); +- + return 0; + } + +-- +2.13.6 + + +From 36f306f4a95f1c2b3e9c584b5a645a78e231c020 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon, 26 Jun 2017 21:41:44 +0200 +Subject: [PATCH 58/70] conncheck: support several stun requests per pair + +This patch should improve the reliabily of the connection check by +keeping the record of several simultaneous ongoing stun requests per +pair. A new stun request on an in-progress pair typically is caused by +in inbound stun request from the peer on this same pair. This is named +"Triggered Checks" in the spec. When this situation arises, it is fair +to handle these two stun requests simultaneously, the triggered check, +and the initial ordinary check, since both can potentially succeed. + +Differential Revision:
https://phabricator.freedesktop.org/D1761
+--- + agent/conncheck.c | 701 +++++++++++++++++++++++++++--------------------------- + agent/conncheck.h | 21 +- + 2 files changed, 361 insertions(+), 361 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 8075d4f..2a85738 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -189,8 +189,8 @@ priv_candidate_type_to_string (NiceCandidateType type) + static void + priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) + { +- GSList *i, *k; +- guint j; ++ GSList *i, *k, *l; ++ guint j, m; + GTimeVal now; + + if (!nice_debug_is_verbose ()) +@@ -210,27 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + if (pair->component_id == j) { + gchar local_addr[INET6_ADDRSTRLEN]; + gchar remote_addr[INET6_ADDRSTRLEN]; +- StunTimer *timer = &pair->timer; + + nice_address_to_string (&pair->local->addr, local_addr); + nice_address_to_string (&pair->remote->addr, remote_addr); + + nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " +- "f=%s t=%s:%s timer=%d/%d %d/%dms " +- "[%s]:%u > [%s]:%u state=%c%s%s%s", ++ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s", + agent, pair->stream_id, pair->component_id, pair, + pair->foundation, + priv_candidate_type_to_string (pair->local->type), + priv_candidate_type_to_string (pair->remote->type), +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - priv_timer_remainder (&pair->next_tick, &now), +- timer->delay, + local_addr, nice_address_get_port (&pair->local->addr), + remote_addr, nice_address_get_port (&pair->remote->addr), + priv_state_to_gchar (pair->state), + pair->valid ? "V" : "", + pair->nominated ? "N" : "", + g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); ++ ++ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) { ++ StunTransaction *stun = l->data; ++ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " ++ "stun#=%d timer=%d/%d %d/%dms buf=%p %s", ++ agent, pair->stream_id, pair->component_id, pair, m, ++ stun->timer.retransmissions, stun->timer.max_retransmissions, ++ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now), ++ stun->timer.delay, ++ stun->message.buffer, ++ (m == 0 && pair->retransmit) ? "(R)" : ""); ++ } + } + } + } +@@ -608,52 +615,92 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + } + } + ++/* ++ * Create a new STUN transaction and add it to the list ++ * of ongoing stun transactions of a pair. ++ * ++ * @pair the pair the new stun transaction should be added to. ++ * @return the created stun transaction. ++ */ ++static StunTransaction * ++priv_add_stun_transaction (CandidateCheckPair *pair) ++{ ++ StunTransaction *stun = g_slice_new0 (StunTransaction); ++ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun); ++ pair->retransmit = TRUE; ++ return stun; ++} ++ ++/* ++ * Forget a STUN transaction. ++ * ++ * @data the stun transaction to be forgotten. ++ * @user_data the component contained the concerned stun agent. ++ */ + static void +-candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) ++priv_forget_stun_transaction (gpointer data, gpointer user_data) + { ++ StunTransaction *stun = data; ++ NiceComponent *component = user_data; + StunTransactionId id; +- NiceComponent *component; +- +- component = nice_stream_find_component_by_id (stream, p->component_id); +- +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); + +- if (p->stun_message.buffer != NULL) { +- stun_message_id (&p->stun_message, id); ++ if (stun->message.buffer != NULL) { ++ stun_message_id (&stun->message, id); + stun_agent_forget_transaction (&component->stun_agent, id); + } ++} + +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++static void ++priv_free_stun_transaction (gpointer data) ++{ ++ g_slice_free (StunTransaction, data); + } + + /* +- * Function that resubmits a new connection check, after a previous +- * check in in-progress state got cancelled due to an incoming stun +- * request matching this same pair ++ * Remove a STUN transaction from a pair, and forget it ++ * from the related component stun agent. + * +- * @return will return TRUE if the pair is scheduled for recheck ++ * @pair the pair the stun transaction should be removed from. ++ * @stun the stun transaction to be removed. ++ * @component the component containing the stun agent used to ++ * forget the stun transaction. + */ +-static gboolean +-priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) ++static void ++priv_remove_stun_transaction (CandidateCheckPair *pair, ++ StunTransaction *stun, NiceComponent *component) + { +- if (p->recheck_on_timeout) { +- g_assert (p->state == NICE_CHECK_IN_PROGRESS); +- /* this cancelled pair may have the flag 'mark nominated +- * on response arrival' set, we want to keep it, because +- * this is needed to nominate this pair in aggressive +- * nomination, when the agent is in controlled mode. +- * +- * this cancelled pair may also have the flag 'use candidate +- * on next check' set, that we want to preserve too. +- */ +- nice_debug ("Agent %p : pair %p was cancelled, " +- "triggering a new connection check", agent, p); +- priv_add_pair_to_triggered_check_queue (agent, p); +- return TRUE; +- } +- return FALSE; ++ priv_forget_stun_transaction (stun, component); ++ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun); ++ priv_free_stun_transaction (stun); ++} ++ ++/* ++ * Remove all STUN transactions from a pair, and forget them ++ * from the related component stun agent. ++ * ++ * @pair the pair the stun list should be cleared. ++ * @component the component containing the stun agent used to ++ * forget the stun transactions. ++ */ ++static void ++priv_free_all_stun_transactions (CandidateCheckPair *pair, ++ NiceComponent *component) ++{ ++ if (component) ++ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component); ++ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction); ++ pair->stun_transactions = NULL; ++} ++ ++static void ++candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) ++{ ++ NiceComponent *component; ++ ++ component = nice_stream_find_component_by_id (stream, p->component_id); ++ p->state = NICE_CHECK_FAILED; ++ nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ priv_free_all_stun_transactions (p, component); + } + + /* +@@ -667,7 +714,7 @@ priv_conn_recheck_on_timeout (NiceAgent *agent, CandidateCheckPair *p) + static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) + { + gboolean keep_timer_going = FALSE; +- GSList *i; ++ GSList *i, *j; + CandidateCheckPair *pair; + unsigned int timeout; + GTimeVal now; +@@ -679,39 +726,59 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + CandidateCheckPair *p = i->data; + gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; + NiceComponent *component; ++ StunTransaction *stun; ++ ++ if (p->stun_transactions == NULL) ++ continue; + + if (!agent_find_component (agent, p->stream_id, p->component_id, + NULL, &component)) + continue; + +- if (p->state != NICE_CHECK_IN_PROGRESS) +- continue; ++ /* The first stun transaction of the list may eventually be ++ * retransmitted, other stun transactions just have their ++ * timer updated. ++ */ + +- if (p->stun_message.buffer == NULL) { +- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); +- continue; ++ j = p->stun_transactions->next; ++ ++ /* process all stun transactions except the first one */ ++ while (j) { ++ StunTransaction *s = j->data; ++ GSList *next = j->next; ++ ++ if (priv_timer_expired (&s->next_tick, &now)) ++ switch (stun_timer_refresh (&s->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: ++ priv_remove_stun_transaction (p, s, component); ++ break; ++ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: ++ timeout = stun_timer_remainder (&s->timer); ++ s->next_tick = now; ++ g_time_val_add (&s->next_tick, timeout * 1000); ++ break; ++ default: ++ break; ++ } ++ j = next; + } + +- if (!priv_timer_expired (&p->next_tick, &now)) ++ if (p->state != NICE_CHECK_IN_PROGRESS) + continue; + +- switch (stun_timer_refresh (&p->timer)) { +- case STUN_USAGE_TIMER_RETURN_TIMEOUT: +-timer_timeout: +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ /* process the first stun transaction of the list */ ++ stun = p->stun_transactions->data; ++ if (!priv_timer_expired (&stun->next_tick, &now)) ++ continue; + ++ switch (stun_timer_refresh (&stun->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: ++timer_return_timeout: + /* case: error, abort processing */ + nice_address_to_string (&p->local->addr, tmpbuf1); + nice_address_to_string (&p->remote->addr, tmpbuf2); +- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on " ++ "connectivity check %p", agent, p); + nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, + tmpbuf1, nice_address_get_port (&p->local->addr), + tmpbuf2, nice_address_get_port (&p->remote->addr)); +@@ -732,42 +799,33 @@ timer_timeout: + * a pair with a higher priority than this in-progress pair, + * ICE spec, sect 8.1.2 "Updating States", item 2.2 + */ +- if (!p->retransmit_on_timeout) +- goto timer_timeout; +- +- /* case: conncheck cancelled due to in-progress incoming +- * check, requeing the pair, ICE spec, sect 7.2.1.4 +- * "Triggered Checks", "If the state of that pair is +- * In-Progress..." +- */ +- if (priv_conn_recheck_on_timeout (agent, p)) +- break; ++ if (!p->retransmit) ++ goto timer_return_timeout; + + /* case: not ready, so schedule a new timeout */ +- timeout = stun_timer_remainder (&p->timer); ++ timeout = stun_timer_remainder (&stun->timer); + + nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " + "(timer=%d/%d %d/%dms).", + agent, p, +- p->timer.retransmissions, p->timer.max_retransmissions, +- p->timer.delay - timeout, p->timer.delay); ++ stun->timer.retransmissions, stun->timer.max_retransmissions, ++ stun->timer.delay - timeout, stun->timer.delay); + + agent_socket_send (p->sockptr, &p->remote->addr, +- stun_message_length (&p->stun_message), +- (gchar *)p->stun_buffer); +- ++ stun_message_length (&stun->message), ++ (gchar *)stun->buffer); + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); + + return TRUE; + case STUN_USAGE_TIMER_RETURN_SUCCESS: +- timeout = stun_timer_remainder (&p->timer); ++ timeout = stun_timer_remainder (&stun->timer); + + /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); + + keep_timer_going = TRUE; + break; +@@ -948,7 +1006,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + g_assert (p->state == NICE_CHECK_SUCCEEDED); + nice_debug ("Agent %p : restarting check %p with " + "USE-CANDIDATE attrib (regular nomination)", agent, p); +- p->recheck_on_timeout = FALSE; + p->use_candidate_on_next_check = TRUE; + priv_add_pair_to_triggered_check_queue (agent, p); + keep_timer_going = TRUE; +@@ -972,7 +1029,6 @@ priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; +- p->recheck_on_timeout = FALSE; + priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); + keep_timer_going = TRUE; +@@ -2186,7 +2242,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + } + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); +- pair->retransmit_on_timeout = TRUE; + + stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, + (GCompareFunc)conn_check_compare); +@@ -2381,8 +2436,7 @@ static void conn_check_free_item (gpointer data) + + if (pair->agent) + priv_remove_pair_from_triggered_check_queue (pair->agent, pair); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; ++ priv_free_all_stun_transactions (pair, NULL); + g_slice_free (CandidateCheckPair, pair); + } + +@@ -2655,6 +2709,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + bool cand_use = controlling; + size_t buffer_len; + unsigned int timeout; ++ StunTransaction *stun; + + if (!agent_find_component (agent, pair->stream_id, pair->component_id, + &stream, &component)) +@@ -2718,13 +2773,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + + if (uname_len == 0) { + nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; + return -1; + } + ++ stun = priv_add_stun_transaction (pair); ++ + buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, +- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), ++ &stun->message, stun->buffer, sizeof(stun->buffer), + uname, uname_len, password, password_len, + cand_use, controlling, pair->prflx_priority, + agent->tie_breaker, +@@ -2732,7 +2787,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + agent_to_ice_compatibility (agent)); + + nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, +- pair->stun_message.buffer); ++ stun->message.buffer); + + if (agent->compatibility == NICE_COMPATIBILITY_MSN || + agent->compatibility == NICE_COMPATIBILITY_OC2007) { +@@ -2741,50 +2796,20 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + + if (buffer_len == 0) { + nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; ++ priv_remove_stun_transaction (pair, stun, component); + return -1; + } + + if (nice_socket_is_reliable(pair->sockptr)) { + timeout = agent->stun_reliable_timeout; +- stun_timer_start_reliable(&pair->timer, timeout); ++ stun_timer_start_reliable(&stun->timer, timeout); + } else { +- StunTimer *timer = &pair->timer; +- +- if (pair->recheck_on_timeout) { +- GTimeVal now; +- /* The pair recheck on timeout can easily cause repetitive rechecks in +- * a ping-pong effect, if both peers with the same behaviour try to +- * check the same pair almost simultaneously, and if the network rtt +- * is greater than the initial timer rto. The reply to the initial +- * stun request may arrive after the in-progress conncheck +- * cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation +- * creates a new stun request, and forgets the initial one. +- * The conncheck timer is restarted with the same initial value, +- * so the same situation happens again later. +- * +- * We choose to avoid resetting the timer in such situation. +- * After enough retransmissions, the timeout delay becomes +- * longer than the rtt, and the stun reply can be handled. +- */ +- +- g_get_current_time (&now); +- timeout = priv_timer_remainder (&pair->next_tick, &now); +- nice_debug("Agent %p : reusing timer of pair %p: %d/%d %d/%dms", +- agent, pair, +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - timeout, +- timer->delay); +- } else { +- timeout = priv_compute_conncheck_timer (agent, stream); +- stun_timer_start (timer, timeout, agent->stun_max_retransmissions); +- } +- pair->recheck_on_timeout = FALSE; ++ timeout = priv_compute_conncheck_timer (agent, stream); ++ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); + } + +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); ++ g_get_current_time (&stun->next_tick); ++ g_time_val_add (&stun->next_tick, timeout * 1000); + + /* TCP-ACTIVE candidate must create a new socket before sending + * by connecting to the peer. The new socket is stored in the candidate +@@ -2814,10 +2839,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + } + /* send the conncheck */ + agent_socket_send (pair->sockptr, &pair->remote->addr, +- buffer_len, (gchar *)pair->stun_buffer); ++ buffer_len, (gchar *)stun->buffer); + + if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) +- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, ++ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, + &pair->remote->addr); + + return 0; +@@ -2881,8 +2906,7 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + (p->state == NICE_CHECK_WAITING && like_in_progress)) { + if (highest_nominated_priority != 0 && + p->priority < highest_nominated_priority) { +- p->retransmit_on_timeout = FALSE; +- p->recheck_on_timeout = FALSE; ++ p->retransmit = FALSE; + nice_debug ("Agent %p : pair %p will not be retransmitted.", + agent, p); + } else { +@@ -2938,9 +2962,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", + agent, p, p->foundation, priv_state_to_string (p->state)); + +- switch (p->state) { +- case NICE_CHECK_WAITING: +- case NICE_CHECK_FROZEN: ++ switch (p->state) { ++ case NICE_CHECK_WAITING: ++ case NICE_CHECK_FROZEN: + nice_debug ("Agent %p : pair %p added for a triggered check.", + agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); +@@ -2952,19 +2976,30 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * for that pair. The controlling role of this new check may + * be different from the role of this cancelled check. + * +- * note: the flag retransmit_on_timeout unset means that ++ * note: the flag retransmit unset means that + * another pair, with a higher priority is already nominated, + * so there's no reason to recheck this pair, since it can in + * no way replace the nominated one. + */ +- if (!nice_socket_is_reliable (p->sockptr) && +- p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p will be rechecked " +- "on stun timer timeout.", agent, p); +- /* this flag will determine the action at the retransmission +- * timeout of the stun timer ++ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ } ++ break; ++ case NICE_CHECK_FAILED: ++ if (p->retransmit) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers + */ +- p->recheck_on_timeout = TRUE; ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } + } + break; + case NICE_CHECK_SUCCEEDED: +@@ -2978,32 +3013,6 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + */ + priv_update_check_list_state_for_ready (agent, stream, component); + break; +- case NICE_CHECK_FAILED: +- /* 7.2.1.4 Triggered Checks +- * If the state of the pair is Failed, it is changed to Waiting +- * and the agent MUST create a new connectivity check for that +- * pair (representing a new STUN Binding request transaction), by +- * enqueueing the pair in the triggered check queue. +- * +- * note: the flag retransmit_on_timeout unset means that +- * another pair, with a higher priority is already nominated, +- * we apply the same strategy than with an in-progress pair +- * above. +- */ +- if (p->retransmit_on_timeout) { +- nice_debug ("Agent %p : pair %p added for a triggered check.", +- agent, p); +- priv_add_pair_to_triggered_check_queue (agent, p); +- /* If the component for this pair is in failed state, move it +- * back to connecting, and reinitiate the timers +- */ +- if (component->state == NICE_COMPONENT_STATE_FAILED) { +- agent_signal_component_state_change (agent, stream->id, +- component->id, NICE_COMPONENT_STATE_CONNECTING); +- conn_check_schedule_next (agent); +- } +- } +- break; + default: + break; + } +@@ -3260,16 +3269,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (new_pair == p) + p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; +- /* note: we cancel the potential in-progress transaction +- * cancellation, caused by sect 7.2.1.4 "Triggered Checks", if +- * we receive a valid reply before transmission timeout... +- */ +- p->recheck_on_timeout = FALSE; +- /* ... or just after the transmission timeout, while the pair is +- * temporarily put on the triggered check list on the way to be +- * be rechecked: we remove it from the list too. +- */ + priv_remove_pair_from_triggered_check_queue (agent, p); ++ priv_free_all_stun_transactions (p, component); + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); + nice_component_add_valid_candidate (component, remote_candidate); + } +@@ -3304,8 +3305,8 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" + */ + p->state = NICE_CHECK_SUCCEEDED; +- p->recheck_on_timeout = FALSE; + priv_remove_pair_from_triggered_check_queue (agent, p); ++ priv_free_all_stun_transactions (p, component); + nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", + agent, p, new_pair); + } +@@ -3331,7 +3332,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + struct sockaddr addr; + } sockaddr; + socklen_t socklen = sizeof (sockaddr); +- GSList *i; ++ GSList *i, *j; ++ guint k; + StunUsageIceReturn res; + StunTransactionId discovery_id; + StunTransactionId response_id; +@@ -3340,193 +3342,186 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +- if (p->stun_message.buffer == NULL) +- continue; +- +- stun_message_id (&p->stun_message, discovery_id); +- +- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) +- continue; +- +- res = stun_usage_ice_conncheck_process (resp, +- &sockaddr.storage, &socklen, +- agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " +- "%s,res=%s.", +- agent, p, +- agent->controlling_mode ? "controlling" : "controlled", +- priv_ice_return_to_string (res)); +- +- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || +- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* case: found a matching connectivity check request */ +- +- CandidateCheckPair *ok_pair = NULL; +- +- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- +- /* step: verify that response came from the same IP address we +- * sent the original request to (see 7.1.2.1. "Failure +- * Cases") */ +- if (nice_address_equal (from, &p->remote->addr) == FALSE) { +- +- p->state = NICE_CHECK_FAILED; +- if (nice_debug_is_enabled ()) { +- gchar tmpbuf[INET6_ADDRSTRLEN]; +- gchar tmpbuf2[INET6_ADDRSTRLEN]; +- nice_debug ("Agent %p : conncheck %p FAILED" +- " (mismatch of source address).", agent, p); +- nice_address_to_string (&p->remote->addr, tmpbuf); +- nice_address_to_string (from, tmpbuf2); +- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, +- tmpbuf, nice_address_get_port (&p->remote->addr), +- tmpbuf2, nice_address_get_port (from)); +- } +- return TRUE; +- } +- +- /* note: CONNECTED but not yet READY, see docs */ +- +- /* step: handle the possible case of a peer-reflexive +- * candidate where the mapped-address in response does +- * not match any local candidate, see 7.1.2.2.1 +- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ +- +- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- p->state = NICE_CHECK_SUCCEEDED; +- p->valid = TRUE; +- nice_debug ("Agent %p : Mapped address not found." +- " conncheck %p SUCCEEDED.", agent, p); +- nice_component_add_valid_candidate (component, p->remote); +- } else +- ok_pair = priv_process_response_check_for_reflexive (agent, +- stream, component, p, sockptr, &sockaddr.addr, +- local_candidate, remote_candidate); +- +- /* note: The success of this check might also +- * cause the state of other checks to change as well, ICE +- * spec 7.1.3.2.3 +- */ +- priv_conn_check_unfreeze_related (agent, stream, p); +- +- /* Note: this assignment helps to reduce the numbers of cases +- * to be tested. If ok_pair and p refer to distinct pairs, it +- * means that ok_pair is a discovered peer reflexive one, +- * caused by the check made on pair p. In that case, the +- * flags to be tested are on p, but the nominated flag will be +- * set on ok_pair. When there's no discovered pair, p and +- * ok_pair refer to the same pair. +- * To summarize : p is a SUCCEEDED pair, ok_pair is a +- * DISCOVERED, VALID, and eventually NOMINATED pair. +- */ +- if (!ok_pair) +- ok_pair = p; +- +- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the +- Nominated Flag" (ID-19) */ +- if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { +- nice_debug ("Agent %p : Updating nominated flag (%s): " +- "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", +- agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? +- "UDP" : "TCP", +- ok_pair, ok_pair->use_candidate_on_next_check, +- ok_pair->mark_nominated_on_response_arrival, +- p, p->use_candidate_on_next_check, +- p->mark_nominated_on_response_arrival); +- +- if (agent->controlling_mode) { +- switch (agent->nomination_mode) { +- case NICE_NOMINATION_MODE_REGULAR: +- if (p->use_candidate_on_next_check) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(regular nomination, controlling, " +- "use_cand_on_next_check=1).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- case NICE_NOMINATION_MODE_AGGRESSIVE: +- if (!p->nominated) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(aggressive nomination, controlling).", +- agent, ok_pair, ok_pair->foundation); +- ok_pair->nominated = TRUE; +- } +- break; +- default: +- /* Nothing to do */ +- break; +- } +- } else { +- if (p->mark_nominated_on_response_arrival) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated " +- "(%s nomination, controlled, mark_on_response=1).", +- agent, ok_pair, ok_pair->foundation, +- agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? +- "aggressive" : "regular"); +- ok_pair->nominated = TRUE; +- } +- } +- } +- +- if (ok_pair->nominated == TRUE) { +- priv_update_selected_pair (agent, component, ok_pair); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a nominated pair"); ++ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) { ++ StunTransaction *stun = j->data; ++ ++ stun_message_id (&stun->message, discovery_id); ++ ++ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) ++ continue; ++ ++ res = stun_usage_ice_conncheck_process (resp, ++ &sockaddr.storage, &socklen, ++ agent_to_ice_compatibility (agent)); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " ++ "%s,res=%s,stun#=%d.", ++ agent, p, ++ agent->controlling_mode ? "controlling" : "controlled", ++ priv_ice_return_to_string (res), k); ++ ++ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || ++ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ /* case: found a matching connectivity check request */ ++ ++ CandidateCheckPair *ok_pair = NULL; ++ ++ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); ++ priv_remove_stun_transaction (p, stun, component); ++ ++ /* step: verify that response came from the same IP address we ++ * sent the original request to (see 7.1.2.1. "Failure ++ * Cases") */ ++ if (nice_address_equal (from, &p->remote->addr) == FALSE) { ++ candidate_check_pair_fail (stream, agent, p); ++ if (nice_debug_is_enabled ()) { ++ gchar tmpbuf[INET6_ADDRSTRLEN]; ++ gchar tmpbuf2[INET6_ADDRSTRLEN]; ++ nice_debug ("Agent %p : conncheck %p FAILED" ++ " (mismatch of source address).", agent, p); ++ nice_address_to_string (&p->remote->addr, tmpbuf); ++ nice_address_to_string (from, tmpbuf2); ++ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, ++ tmpbuf, nice_address_get_port (&p->remote->addr), ++ tmpbuf2, nice_address_get_port (from)); ++ } ++ return TRUE; ++ } + +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } ++ /* note: CONNECTED but not yet READY, see docs */ ++ ++ /* step: handle the possible case of a peer-reflexive ++ * candidate where the mapped-address in response does ++ * not match any local candidate, see 7.1.2.2.1 ++ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ ++ ++ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ p->state = NICE_CHECK_SUCCEEDED; ++ p->valid = TRUE; ++ nice_debug ("Agent %p : Mapped address not found." ++ " conncheck %p SUCCEEDED.", agent, p); ++ nice_component_add_valid_candidate (component, p->remote); ++ } else ++ ok_pair = priv_process_response_check_for_reflexive (agent, ++ stream, component, p, sockptr, &sockaddr.addr, ++ local_candidate, remote_candidate); ++ ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ ++ if (!ok_pair) ++ ok_pair = p; ++ ++ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the ++ Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, controlling, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(aggressive nomination, controlling).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ default: ++ /* Nothing to do */ ++ break; ++ } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, controlled, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; ++ } ++ } ++ } + +- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair +- states" and 8.1.2 "Updating States", ID-19) */ +- priv_update_check_list_state_for_ready (agent, stream, component); +- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { +- /* case: role conflict error, need to restart with new role */ +- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- +- /* note: this res value indicates that the role of the peer +- * agent has not changed after the tie-breaker comparison, so +- * this is our role that must change. see ICE sect. 7.1.3.1 +- * "Failure Cases". Our role might already have changed due to +- * an earlier incoming request, but if not, change role now. +- * +- * Sect. 7.1.3.1 is not clear on this point, but we choose to +- * put the candidate pair in the triggered check list even +- * when the agent did not switch its role. The reason for this +- * interpretation is that the reception of the stun reply, even +- * an error reply, is a good sign that this pair will be +- * valid, if we retry the check after the role of both peers +- * has been fixed. +- */ ++ if (ok_pair->nominated == TRUE) { ++ priv_update_selected_pair (agent, component, ok_pair); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a nominated pair"); ++ ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } + +- if (p->stun_message.buffer != NULL) { ++ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair ++ states" and 8.1.2 "Updating States", ID-19) */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { + guint64 tie; + gboolean controlled_mode; + +- controlled_mode = (stun_message_find64 (&p->stun_message, ++ /* case: role conflict error, need to restart with new role */ ++ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); ++ ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ ++ controlled_mode = (stun_message_find64 (&stun->message, + STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == + STUN_MESSAGE_RETURN_SUCCESS); + + priv_check_for_role_conflict (agent, controlled_mode); +- +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++ priv_remove_stun_transaction (p, stun, component); + priv_add_pair_to_triggered_check_queue (agent, p); ++ } else { ++ /* case: STUN error, the check STUN context was freed */ ++ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); ++ candidate_check_pair_fail (stream, agent, p); + } +- } else { +- /* case: STUN error, the check STUN context was freed */ +- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++ return TRUE; + } +- return TRUE; + } + + return FALSE; +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 909d469..e16dc67 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -71,6 +71,15 @@ typedef enum + } NiceCheckState; + + typedef struct _CandidateCheckPair CandidateCheckPair; ++typedef struct _StunTransaction StunTransaction; ++ ++struct _StunTransaction ++{ ++ GTimeVal next_tick; /* next tick timestamp */ ++ StunTimer timer; ++ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; ++ StunMessage message; ++}; + + struct _CandidateCheckPair + { +@@ -86,16 +95,12 @@ struct _CandidateCheckPair + gboolean valid; + gboolean use_candidate_on_next_check; + gboolean mark_nominated_on_response_arrival; +- gboolean recheck_on_timeout; +- gboolean retransmit_on_timeout; +- struct _CandidateCheckPair *discovered_pair; +- struct _CandidateCheckPair *succeeded_pair; ++ gboolean retransmit; /* if the first stun request must be retransmitted */ ++ CandidateCheckPair *discovered_pair; ++ CandidateCheckPair *succeeded_pair; + guint64 priority; + guint32 prflx_priority; +- GTimeVal next_tick; /* next tick timestamp */ +- StunTimer timer; +- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; +- StunMessage stun_message; ++ GSList *stun_transactions; /* a list of ongoing stun requests */ + }; + + int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); +-- +2.13.6 + + +From 6fe64fdbc53ab87dffd79972f492665cff14c0a0 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 27 Jun 2017 11:01:14 +0200 +Subject: [PATCH 59/70] conncheck: update the state of triggered checks pairs + +With this patch, we fix an ambiguity of some parts of the spec, when +the document refers to in-progress pairs, that also concern pairs in +the triggered checks list. + +The first cast is in section 7.1.2.5, "Updating the Nominated Flag", +when the in-progress pair will be nominated on response arrival. This is +handled in function priv_mark_pair_nominated(), when a pair is put to +the triggered check list in reaction to a matching inbound stun request. +Such a pair in priv_mark_pair_nominated() will _always_ be in the +triggered check list, from the previously called function +priv_schedule_triggered_check(). + +The second case is in section 8.1.2, "Updating State" when an in-progress +pair stops its retransmission when another pair of higher priority is +already nominated. This is handled by function priv_prune_pending_checks(). + +Until now, pairs enqueued in the triggered check list move transiently +to state waiting, according to 7.2.1.4. But this state causes wrong +decisions in the two previous cases, because such pairs should in fact +rather be considered "like in-progress", to avoid discarding them +inadvertantly. + +This patch update the state of the triggered check list +pairs to in-progress. It allows to remove exception handling cited +above: the code is a bit more simple, and allows some refactoring +in priv_mark_pair_nominated() between RFC and compatibility modes. + +Differential Revision:
https://phabricator.freedesktop.org/D1762
+--- + agent/conncheck.c | 96 +++++++++++++++---------------------------------------- + 1 file changed, 25 insertions(+), 71 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 2a85738..628c708 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -244,16 +244,6 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + } + } + +-/* Verify if the pair is in the triggered checks list +- */ +- +-static gboolean +-priv_is_pair_in_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) +-{ +- g_assert (pair); +- return (g_slist_find (agent->triggered_check_queue, pair) != NULL); +-} +- + /* Add the pair to the triggered checks list, if not already present + */ + static void +@@ -261,8 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa + { + g_assert (pair); + +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ pair->state = NICE_CHECK_IN_PROGRESS; ++ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); + if (agent->triggered_check_queue == NULL || + g_slist_find (agent->triggered_check_queue, pair) == NULL) + agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); +@@ -841,12 +831,6 @@ timer_return_timeout: + */ + pair = priv_conn_check_find_next_waiting (stream->conncheck_list); + if (pair) { +- /* remove the pair from the triggered check list if needed. This +- * may happen with the cancelled pair, that's just been added +- * in state waiting to the triggered check list above in the +- * same function. +- */ +- priv_remove_pair_from_triggered_check_queue (agent, pair); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair in Waiting state"); + priv_conn_check_initiate (agent, pair); +@@ -1109,7 +1093,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + if (pair) { + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair from triggered check list"); +- priv_conn_check_initiate (agent, pair); ++ conn_check_send (agent, pair); + return TRUE; + } + +@@ -2098,8 +2082,7 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + /* step: search for at least one nominated pair */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *pair = i->data; +- if (pair->local == localcand && pair->remote == remotecand && +- NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ if (pair->local == localcand && pair->remote == remotecand) { + /* ICE, 7.2.1.5. Updating the Nominated Flag */ + /* note: TCP candidates typically produce peer reflexive + * candidate, generating a "discovered" pair that can be +@@ -2111,44 +2094,27 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + g_assert (pair->state == NICE_CHECK_DISCOVERED); + } + +- if (pair->valid) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated", +- agent, pair, pair->foundation); +- pair->nominated = TRUE; +- priv_update_selected_pair (agent, component, pair); +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state == NICE_COMPONENT_STATE_FAILED) +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); +- if (component->state == NICE_COMPONENT_STATE_CONNECTING) +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- priv_update_check_list_state_for_ready (agent, stream, component); +- } else if (pair->state == NICE_CHECK_IN_PROGRESS) { ++ /* If the state of this pair is In-Progress, [...] the resulting ++ * valid pair has its nominated flag set when the response ++ * arrives. ++ */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && ++ pair->state == NICE_CHECK_IN_PROGRESS) { + pair->mark_nominated_on_response_arrival = TRUE; + nice_debug ("Agent %p : pair %p (%s) is in-progress, " + "will be nominated on response receipt.", + agent, pair, pair->foundation); + } +- /* note: this case is not covered by the ICE spec, 7.2.1.5, +- * "Updating the Nominated Flag", but a pair in waiting state +- * deserves the same treatment than a pair in-progress. A pair +- * can be in waiting state as the result of being enqueued in +- * the triggered check list for example. +- */ +- if (pair->state == NICE_CHECK_WAITING) { +- pair->mark_nominated_on_response_arrival = TRUE; +- nice_debug ("Agent %p : pair %p (%s) is waiting, " +- "will be nominated on response receipt.", ++ ++ if (pair->valid || ++ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated", + agent, pair, pair->foundation); ++ pair->nominated = TRUE; + } +- } else if (pair->local == localcand && pair->remote == remotecand) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); +- pair->nominated = TRUE; ++ + if (pair->valid) { +- priv_update_selected_pair (agent, component, pair); ++ priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ + if (component->state == NICE_COMPONENT_STATE_FAILED) + agent_signal_component_state_change (agent, +@@ -2159,7 +2125,9 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); + } +- priv_update_check_list_state_for_ready (agent, stream, component); ++ ++ if (pair->nominated) ++ priv_update_check_list_state_for_ready (agent, stream, component); + } + } + } +@@ -2731,12 +2699,12 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + nice_address_to_string (&pair->local->addr, tmpbuf1); + nice_address_to_string (&pair->remote->addr, tmpbuf2); + nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " +- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " ++ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " + "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr), + pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, +- pair->foundation, pair->component_id, ++ pair, pair->component_id, + (unsigned long long)agent->tie_breaker, + (int) uname_len, uname, uname_len, + (int) password_len, password, password_len, +@@ -2877,35 +2845,21 @@ static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, gu + "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ +- /* note: this case is not covered by the ICE spec, 8.1.2 +- * "Updating States", but a pair in waiting state which will be +- * nominated on response receipt should be treated the same way +- * that an in-progress pair. A pair in waiting state and in +- * the triggered check list should also be treated like an in-progress +- * pair. +- */ + i = stream->conncheck_list; + while (i) { + CandidateCheckPair *p = i->data; + GSList *next = i->next; + + if (p->component_id == component_id) { +- gboolean like_in_progress = +- p->mark_nominated_on_response_arrival || +- priv_is_pair_in_triggered_check_queue (agent, p); +- +- if (p->state == NICE_CHECK_FROZEN || +- (p->state == NICE_CHECK_WAITING && !like_in_progress)) { ++ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) { + nice_debug ("Agent %p : pair %p removed.", agent, p); + conn_check_free_item (p); + stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- else if (p->state == NICE_CHECK_IN_PROGRESS || +- (p->state == NICE_CHECK_WAITING && like_in_progress)) { +- if (highest_nominated_priority != 0 && +- p->priority < highest_nominated_priority) { ++ else if (p->state == NICE_CHECK_IN_PROGRESS) { ++ if (p->priority < highest_nominated_priority) { + p->retransmit = FALSE; + nice_debug ("Agent %p : pair %p will not be retransmitted.", + agent, p); +-- +2.13.6 + + +From 72dd26a3368d3506fe8faca7067a02784fb5f0fd Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Wed, 28 Jun 2017 12:06:48 +0200 +Subject: [PATCH 60/70] conncheck: forgot to put a pair in triggered check list + +When a new pair is created from an unknown remote candidate, it +should be enqueue for a triggered check, to allow it to be marked +as nominated on response arrival in priv_mark_pair_nominated(). +Creating it in waiting state is not sufficient since the update +in priv_mark_pair_nominated() from previous commits. + +Differential Revision:
https://phabricator.freedesktop.org/D1763
+--- + agent/conncheck.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 628c708..0e3ce88 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2893,11 +2893,12 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + { + GSList *i; + NiceCandidate *local = NULL; ++ CandidateCheckPair *p; + + g_assert (remote_cand != NULL); + + for (i = stream->conncheck_list; i ; i = i->next) { +- CandidateCheckPair *p = i->data; ++ p = i->data; + if (p->component_id == component->id && + p->remote == remote_cand && + ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && +@@ -2986,8 +2987,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + + if (i) { + nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); +- priv_add_new_check_pair (agent, stream->id, component, ++ p = priv_add_new_check_pair (agent, stream->id, component, + local, remote_cand, NICE_CHECK_WAITING); ++ priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } + else { +-- +2.13.6 + + +From 14102d44449d2eb4148588ce54fa897fa13b87ad Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun, 2 Jul 2017 16:02:09 +0200 +Subject: [PATCH 61/70] conncheck: change state before updating nominated pairs + +When a pair is nominated while in state failed, we first move +back to state connecting, then we update the selected pair, and +finally we move to state connected. +--- + agent/conncheck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 0e3ce88..e584c0e 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -2114,11 +2114,11 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + } + + if (pair->valid) { +- priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ + if (component->state == NICE_COMPONENT_STATE_FAILED) + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ priv_update_selected_pair (agent, component, pair); + if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ +-- +2.13.6 + + +From 1a1803a45778000720c93d91060cedeb19124a27 Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Tue, 12 Sep 2017 13:23:31 +0100 +Subject: [PATCH 62/70] tests: Fix copyright dates in test-gstreamer.c + +This code is not 1000 years old. + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +--- + tests/test-gstreamer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c +index 74d7133..a0be68e 100644 +--- a/tests/test-gstreamer.c ++++ b/tests/test-gstreamer.c +@@ -1,7 +1,7 @@ + /* + * This file is part of the Nice GLib ICE library. + * +- * (C) 1015 Kurento. ++ * (C) 2015 Kurento. + * Contact: Jose Antonio Santos Cadenas + * + * The contents of this file are subject to the Mozilla Public License Version +-- +2.13.6 + + +From 4c4834ab634f735145c8f758a22cbdd9cab79bac Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Tue, 12 Sep 2017 13:23:53 +0100 +Subject: [PATCH 63/70] tests: Fix agent.h header inclusion in test-gstreamer.c + +Spotted by Lukas Gradl on the mailing list. + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +--- + tests/test-gstreamer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c +index a0be68e..f060efc 100644 +--- a/tests/test-gstreamer.c ++++ b/tests/test-gstreamer.c +@@ -34,7 +34,7 @@ + */ + + #include <gst/check/gstcheck.h> +-#include <nice/agent.h> ++#include "agent.h" + + #define RTP_HEADER_SIZE 12 + #define RTP_PAYLOAD_SIZE 1024 +-- +2.13.6 + + +From fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 Mon Sep 17 00:00:00 2001 +From: Philip Withnall <withnall(a)endlessm.com> +Date: Thu, 3 Aug 2017 12:20:32 +0100 +Subject: [PATCH 64/70] stun: Fix FD leak in test/utility code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +https://phabricator.freedesktop.org/T7798 + +Signed-off-by: Philip Withnall <withnall(a)endlessm.com> +Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> +Differential Revision:
https://phabricator.freedesktop.org/D1819
+--- + stun/usages/bind.c | 29 ++++++++++++++++++++++------- + 1 file changed, 22 insertions(+), 7 deletions(-) + +diff --git a/stun/usages/bind.c b/stun/usages/bind.c +index d56790f..ee600a0 100644 +--- a/stun/usages/bind.c ++++ b/stun/usages/bind.c +@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen); + if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { + stun_debug ("STUN transaction failed: couldn't create transport."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) { + stun_debug ("STUN transaction failed: couldn't send request."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, +@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + switch (stun_timer_refresh (&timer)) { + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + stun_debug ("STUN transaction failed: time out."); +- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! ++ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! ++ goto done; + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: + stun_debug ("STUN transaction retransmitted (timeout %dms).", + stun_timer_remainder (&timer)); + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) { + stun_debug ("STUN transaction failed: couldn't resend request."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + continue; + case STUN_USAGE_TIMER_RETURN_SUCCESS: +@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + + valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL); + if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE) +- return STUN_USAGE_BIND_RETURN_ERROR; ++ { ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; ++ } + + if (valid != STUN_VALIDATION_SUCCESS) { + ret = STUN_USAGE_TRANS_RETURN_RETRY; +@@ -554,12 +561,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + (struct sockaddr *) &alternate_server, alternate_server_len); + + if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) +- return STUN_USAGE_BIND_RETURN_ERROR; ++ { ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; ++ } + + stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, + STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +@@ -573,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + } + while (ret == STUN_USAGE_TRANS_RETURN_RETRY); + ++done: ++ if (trans.fd != -1) ++ stun_trans_deinit (&trans); ++ + return bind_ret; + } +-- +2.13.6 + + +From 02216a6766caccb652387d5ee19686149eedbc93 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue, 21 Nov 2017 15:12:45 +0100 +Subject: [PATCH 65/70] agent: prevent external role change while conncheck is + running + +With this patch, we stash the controlling mode property change, and +apply it safely, when it won't interfere with an ongoing conncheck +running. According to RFC5245, sect 5.2. "Determining Role", the role +is determined for a session, and persists unless an ICE is restarted. + +Differential Revision:
https://phabricator.freedesktop.org/D1887
+--- + agent/agent-priv.h | 4 +++- + agent/agent.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- + tests/test-restart.c | 15 ++++++++++++++ + 3 files changed, 74 insertions(+), 3 deletions(-) + +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 714ecff..7269be0 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -146,7 +146,7 @@ struct _NiceAgent + NiceProxyType proxy_type; /* property: Proxy type */ + gchar *proxy_username; /* property: Proxy username */ + gchar *proxy_password; /* property: Proxy password */ +- gboolean controlling_mode; /* property: controlling-mode */ ++ gboolean saved_controlling_mode;/* property: controlling-mode */ + guint timer_ta; /* property: timer Ta */ + guint max_conn_checks; /* property: max connectivity checks */ + gboolean force_relay; /* property: force relay */ +@@ -190,6 +190,8 @@ struct _NiceAgent + gboolean use_ice_tcp; + + guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ ++ gboolean controlling_mode; /* controlling mode used by the ++ conncheck */ + /* XXX: add pointer to internal data struct for ABI-safe extensions */ + }; + +diff --git a/agent/agent.c b/agent/agent.c +index a4dcc0c..0773c53 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -405,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass) + 1, /* not a construct property, ignored */ + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:controlling-mode: ++ * ++ * Whether the agent has the controlling role. This property should ++ * be modified before gathering candidates, any modification occuring ++ * later will be hold until ICE is restarted. ++ */ + g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE, + g_param_spec_boolean ( + "controlling-mode", +@@ -1107,6 +1114,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) + } + + static void ++priv_update_controlling_mode (NiceAgent *agent, gboolean value) ++{ ++ gboolean update_controlling_mode; ++ GSList *i, *j; ++ ++ agent->saved_controlling_mode = value; ++ /* It is safe to update the agent controlling mode when all ++ * components are still in state disconnected. When we leave ++ * this state, the role must stay under the control of the ++ * conncheck algorithm exclusively, until the conncheck is ++ * eventually restarted. See RFC5245, sect 5.2. Determining Role ++ */ ++ if (agent->controlling_mode != agent->saved_controlling_mode) { ++ update_controlling_mode = TRUE; ++ for (i = agent->streams; ++ i && update_controlling_mode; i = i->next) { ++ NiceStream *stream = i->data; ++ for (j = stream->components; ++ j && update_controlling_mode; j = j->next) { ++ NiceComponent *component = j->data; ++ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED) ++ update_controlling_mode = FALSE; ++ } ++ } ++ if (update_controlling_mode) { ++ agent->controlling_mode = agent->saved_controlling_mode; ++ nice_debug ("Agent %p : Property set, changing role to \"%s\".", ++ agent, agent->controlling_mode ? "controlling" : "controlled"); ++ } else { ++ nice_debug ("Agent %p : Property set, role switch requested " ++ "but conncheck already started.", agent); ++ nice_debug ("Agent %p : Property set, staying with role \"%s\" " ++ "until restart.", agent, ++ agent->controlling_mode ? "controlling" : "controlled"); ++ } ++ } else ++ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent, ++ agent->controlling_mode ? "controlling" : "controlled"); ++} ++ ++static void + nice_agent_init (NiceAgent *agent) + { + agent->next_candidate_id = 1; +@@ -1115,6 +1163,7 @@ nice_agent_init (NiceAgent *agent) + /* set defaults; not construct params, so set here */ + agent->stun_server_port = DEFAULT_STUN_PORT; + agent->controlling_mode = TRUE; ++ agent->saved_controlling_mode = TRUE; + agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; + agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; + +@@ -1213,7 +1262,7 @@ nice_agent_get_property ( + break; + + case PROP_CONTROLLING_MODE: +- g_value_set_boolean (value, agent->controlling_mode); ++ g_value_set_boolean (value, agent->saved_controlling_mode); + break; + + case PROP_FULL_MODE: +@@ -1422,7 +1471,7 @@ nice_agent_set_property ( + break; + + case PROP_CONTROLLING_MODE: +- agent->controlling_mode = g_value_get_boolean (value); ++ priv_update_controlling_mode (agent, g_value_get_boolean (value)); + break; + + case PROP_FULL_MODE: +@@ -4930,6 +4979,11 @@ nice_agent_restart ( + /* step: regenerate tie-breaker value */ + priv_generate_tie_breaker (agent); + ++ /* step: reset controlling mode from the property value */ ++ agent->controlling_mode = agent->saved_controlling_mode; ++ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".", ++ agent, agent->controlling_mode ? "controlling" : "controlled"); ++ + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; + +diff --git a/tests/test-restart.c b/tests/test-restart.c +index c2cbe9a..afc51b6 100644 +--- a/tests/test-restart.c ++++ b/tests/test-restart.c +@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands); + cdes.addr = laddr_rtcp; + nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands); ++ /* This role switch request will be effective after restart. We test ++ * here that the role cannot be externally modified after conncheck ++ * has started. */ ++ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL); ++ g_assert (ragent->controlling_mode == FALSE); + + g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed..."); + +@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + global_ragent_read = 0; + g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16); + ++ /* Both agent have a distinct role at the end of the conncheck */ ++ g_assert (lagent->controlling_mode == TRUE); ++ g_assert (ragent->controlling_mode == FALSE); + /* step: restart agents, exchange updated credentials */ + tie_breaker = ragent->tie_breaker; + nice_agent_restart (ragent); + g_assert (tie_breaker != ragent->tie_breaker); ++ /* This role switch of ragent should be done now, and both agents ++ * have now the same role, which should generate a role conflict ++ * resolution situation */ ++ g_assert (lagent->controlling_mode == TRUE); ++ g_assert (ragent->controlling_mode == TRUE); + nice_agent_restart (lagent); + { + gchar *ufrag = NULL, *password = NULL; +@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + /* note: verify binding requests were resent after restart */ + g_assert (global_lagent_ibr_received == TRUE); + g_assert (global_ragent_ibr_received == TRUE); ++ /* note: verify that a role switch occured for one of the agents */ ++ g_assert (ragent->controlling_mode != lagent->controlling_mode); + + g_debug ("test-restart: Ran mainloop, removing streams..."); + +-- +2.13.6 + + +From c63349894b3fe974494453a883dfb5ad05df5a46 Mon Sep 17 00:00:00 2001 +From: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu, 23 Nov 2017 18:31:31 +0100 +Subject: [PATCH 66/70] Makefile: really enable debug for tests + +Differential Revision:
https://phabricator.freedesktop.org/D1888
+--- + tests/Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index b623764..e94822d 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" + + AM_TESTS_ENVIRONMENT = \ + G_MESSAGES_DEBUG=all \ +- NICE_DEBUG=all; ++ NICE_DEBUG=all + + COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) + +-- +2.13.6 + + +From 17f30e4465efe9533799b02d6f95feeaf0f2748c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miguel=20Par=C3=ADs?= <mparisdiaz(a)gmail.com> +Date: Wed, 8 Nov 2017 16:26:47 +0000 +Subject: [PATCH 67/70] conncheck: do not require that all streams have a + connection check list + +One or more streams might not have any connection check list if the +number of streams differs from the peer agent. +Differential Revision:
https://phabricator.freedesktop.org/D1880
+--- + agent/conncheck.c | 23 ---- + tests/Makefile.am | 3 + + tests/test-different-number-streams.c | 208 ++++++++++++++++++++++++++++++++++ + 3 files changed, 211 insertions(+), 23 deletions(-) + create mode 100644 tests/test-different-number-streams.c + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index e584c0e..beb43c3 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -402,23 +402,6 @@ priv_conn_check_find_next_frozen (GSList *conn_check_list) + } + + /* +- * Returns the number of check lists of the agent +- */ +-static guint +-priv_number_of_check_lists (NiceAgent *agent) +-{ +- guint n = 0; +- GSList *i; +- +- for (i = agent->streams; i ; i = i->next) { +- NiceStream *stream = i->data; +- if (stream->conncheck_list != NULL) +- n++; +- } +- return n; +-} +- +-/* + * Returns the number of active check lists of the agent + */ + static guint +@@ -1060,12 +1043,6 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + gboolean keep_timer_going = FALSE; + GSList *i, *j; + +- /* the conncheck really starts when we have built +- * a connection check list for each stream +- */ +- if (priv_number_of_check_lists (agent) < g_slist_length (agent->streams)) +- return TRUE; +- + /* configure the initial state of the check lists of the agent + * as described in ICE spec, 5.7.4 + * +diff --git a/tests/Makefile.am b/tests/Makefile.am +index e94822d..30d6f8e 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -46,6 +46,7 @@ check_PROGRAMS = \ + test-socket-is-based-on \ + test-priority \ + test-fullmode \ ++ test-different-number-streams \ + test-restart \ + test-fallback \ + test-thread \ +@@ -114,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD) + + test_fullmode_LDADD = $(COMMON_LDADD) + ++test_different_number_streams_LDADD = $(COMMON_LDADD) ++ + test_restart_LDADD = $(COMMON_LDADD) + + test_fallback_LDADD = $(COMMON_LDADD) +diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c +new file mode 100644 +index 0000000..7fd4763 +--- /dev/null ++++ b/tests/test-different-number-streams.c +@@ -0,0 +1,208 @@ ++#ifdef HAVE_CONFIG_H ++# include <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#define ADD_2_STREAMS TRUE ++#define USE_SECOND_STREAM TRUE ++ ++static GMainLoop *global_mainloop = NULL; ++ ++static guint global_components_ready = 0; ++static guint global_components_ready_exit = 0; ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id); ++} ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)", ++ agent, stream_id, component_id, nice_component_state_to_string (state)); ++ ++ if (state == NICE_COMPONENT_STATE_READY) { ++ global_components_ready++; ++ } ++ ++ /* signal status via a global variable */ ++ if (global_components_ready == global_components_ready_exit) { ++ g_debug ("Components ready/failed achieved. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ } ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL, *i; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id); ++} ++ ++int main (void) ++{ ++ NiceAgent *lagent, *ragent; ++ guint timer_id; ++ guint ls_id, rs_id_1, rs_id_2; ++ gchar *lufrag = NULL, *lpassword = NULL; ++ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL; ++ ++#ifdef G_OS_WIN32 ++ WSADATA w; ++ ++ WSAStartup(0x0202, &w); ++#endif ++ ++ global_mainloop = g_main_loop_new (NULL, FALSE); ++ ++ /* step: create the agents L and R */ ++ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_GOOGLE); ++ g_debug ("lagent: %p", lagent); ++ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent"); ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), NULL); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), NULL); ++ ++ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_GOOGLE); ++ g_debug ("ragent: %p", ragent); ++ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent"); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), NULL); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), NULL); ++ ++ /* step: add a timer to catch state changes triggered by signals */ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ ls_id = nice_agent_add_stream (lagent, 2); ++ g_assert (ls_id > 0); ++ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword); ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ ++ global_components_ready_exit = 4; ++ ++ if (ADD_2_STREAMS) { ++ rs_id_1 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_1 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); ++ ++ rs_id_2 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_2 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2); ++ ++ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword); ++ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2); ++ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); ++ ++ if (USE_SECOND_STREAM) { ++ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP); ++ } else { ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); ++ } ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ } else { ++ rs_id_1 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_1 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); ++ ++ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword); ++ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1); ++ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); ++ } ++ ++ /* step: run the mainloop until connectivity checks succeed ++ * (see timer_cb() above) */ ++ g_main_loop_run (global_mainloop); ++ ++ g_free (lufrag); ++ g_free (lpassword); ++ g_free (rufrag1); ++ g_free (rpassword1); ++ g_free (rufrag2); ++ g_free (rpassword2); ++ g_object_unref (lagent); ++ g_object_unref (ragent); ++ ++ g_main_loop_unref (global_mainloop); ++ global_mainloop = NULL; ++ ++ g_source_remove (timer_id); ++ ++#ifdef G_OS_WIN32 ++ WSACleanup(); ++#endif ++ ++ return 0; ++} +-- +2.13.6 + + +From 59fcf95d505c3995f858b826d10cd48321ed383e Mon Sep 17 00:00:00 2001 +From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Mon, 27 Nov 2017 17:07:02 -0500 +Subject: [PATCH 68/70] turn: Add support for ALTERNATE_SERVER in OC2007 + Compatibility + +The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in +allocation responses, and if they are not handled, we end up using the +main turn server to send allocation requests that then get sent to the +alternate server which will return the XOR_MAPPED_ADDRESS containing +the IP address of the turn server that proxied the message instead of +our own actual external IP. +--- + agent/conncheck.c | 14 ++++++++++++++ + stun/usages/turn.c | 11 +++++++++++ + stun/usages/turn.h | 4 ++++ + 3 files changed, 29 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index beb43c3..229c8b1 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3764,6 +3764,20 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + recv_realm = (uint8_t *) stun_message_find (resp, + STUN_ATTRIBUTE_REALM, &recv_realm_len); + ++ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && ++ alternatelen != sizeof(alternate)) { ++ NiceAddress alternate_addr; ++ ++ nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); ++ ++ if (!nice_address_equal (&alternate_addr, &d->server)) { ++ nice_address_set_from_sockaddr (&d->server, &alternate.addr); ++ nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ ++ d->pending = FALSE; ++ } ++ } + /* check for unauthorized error response */ + if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || + agent->compatibility == NICE_COMPATIBILITY_OC2007 || +diff --git a/stun/usages/turn.c b/stun/usages/turn.c +index 3b94959..ec12642 100644 +--- a/stun/usages/turn.c ++++ b/stun/usages/turn.c +@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, + stun_debug (" STUN error message received (code: %d)", code); + + /* ALTERNATE-SERVER mechanism */ ++ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 && ++ alternate_server && alternate_server_len && ++ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER, ++ alternate_server, ++ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) { ++ stun_debug ("Found alternate server"); ++ /* The ALTERNATE_SERVER will always be returned by the MS turn server. ++ * We need to check if the ALTERNATE_SERVER is the same as the current ++ * server to decide whether we need to switch servers or not. ++ */ ++ } + if ((code / 100) == 3) { + if (alternate_server && alternate_server_len) { + if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, +diff --git a/stun/usages/turn.h b/stun/usages/turn.h +index 7a2d4e6..83fa00a 100644 +--- a/stun/usages/turn.h ++++ b/stun/usages/turn.h +@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, + * Allocate request, in case the currently used TURN server is requesting the use + * of an alternate server. This argument will only be filled if the return value + * of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER ++ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the ++ * @alternate_server could be filled at any time, and should only be considered ++ * if the request was sent to a different server than the address returned ++ * in the @alternate_server field + * @alternate_server_len: The length of @alternate_server + * @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us + * @lifetime: A pointer to fill with the lifetime of the allocation +-- +2.13.6 + + +From 4172d48852ecd1c86cc7bd4665b23697603d1eed Mon Sep 17 00:00:00 2001 +From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Tue, 28 Nov 2017 15:14:11 -0500 +Subject: [PATCH 69/70] discovery: Increase discovery_unsched_items whenever we + restart a check + +The discovery_unsched_items is decremented every time a DiscoveryCandidate +goes from non-pending to pending. So if we restart a check by setting +pending to FALSE, we should re-increase the discovery_unsched_items. +--- + agent/conncheck.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 229c8b1..5b08311 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3507,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa + d->server = niceaddr; + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { + /* case: successful binding discovery, create a new local candidate */ + +@@ -3648,6 +3649,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || + res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { + /* case: successful allocate, create a new local candidate */ +@@ -3776,6 +3778,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } + } + /* check for unauthorized error response */ +@@ -3798,6 +3801,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + d->stun_resp_msg.buffer = d->stun_resp_buffer; + d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer); + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else { + /* case: a real unauthorized error */ + d->stun_message.buffer = NULL; +-- +2.13.6 + + +From fb2f1f77a31baa91968fc81c205f980b6913f403 Mon Sep 17 00:00:00 2001 +From: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Tue, 28 Nov 2017 16:05:18 -0500 +Subject: [PATCH 70/70] conncheck: handle alternate-server for turn relays + differently + +If a relay gives us an alternate-server, we need to cancel and reset +every candidate discovery attempt that uses the same server, to avoid +ending up with one component on one server and the other component on +another server (causing relay candidates with mismatched foundations). +--- + agent/conncheck.c | 56 ++++++++++++++++++++++++++++++++++++++++++------------- + 1 file changed, 43 insertions(+), 13 deletions(-) + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index 5b08311..c8a4edf 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -3592,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand + return cand; + } + ++static void priv_handle_turn_alternate_server (NiceAgent *agent, ++ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate) ++{ ++ /* We need to cancel and reset all candidate discovery turn for the same ++ stream and type if there is an alternate server. Otherwise, we might end up ++ with two relay components on different servers, creating candidates with ++ unique foundations that only contain one component. ++ */ ++ GSList *i; ++ ++ for (i = agent->discovery_list; i; i = i->next) { ++ CandidateDiscovery *d = i->data; ++ ++ if (!d->done && ++ d->type == disco->type && ++ d->stream == disco->stream && ++ d->turn->type == disco->turn->type && ++ nice_address_equal (&d->server, &server)) { ++ gchar ip[INET6_ADDRSTRLEN]; ++ // Cancel the pending request to avoid a race condition with another ++ // component responding with another altenrate-server ++ d->stun_message.buffer = NULL; ++ d->stun_message.buffer_len = 0; ++ ++ nice_address_to_string (&server, ip); ++ nice_debug ("Agent %p : Cancelling and setting alternate server %s for " ++ "CandidateDiscovery %p", agent, ip, d); ++ d->server = alternate; ++ d->turn->server = alternate; ++ d->pending = FALSE; ++ agent->discovery_unsched_items++; ++ } ++ } ++} ++ + /* + * Tries to match STUN reply in 'buf' to an existing STUN discovery + * transaction. If found, a reply is sent. +@@ -3644,12 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + agent, d, (int)res); + + if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) { +- /* handle alternate server */ +- nice_address_set_from_sockaddr (&d->server, &alternate.addr); +- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ NiceAddress addr; + +- d->pending = FALSE; +- agent->discovery_unsched_items++; ++ /* handle alternate server */ ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); + } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || + res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { + /* case: successful allocate, create a new local candidate */ +@@ -3769,16 +3803,12 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || + agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && + alternatelen != sizeof(alternate)) { +- NiceAddress alternate_addr; +- +- nice_address_set_from_sockaddr (&alternate_addr, &alternate.addr); ++ NiceAddress addr; + +- if (!nice_address_equal (&alternate_addr, &d->server)) { +- nice_address_set_from_sockaddr (&d->server, &alternate.addr); +- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); + +- d->pending = FALSE; +- agent->discovery_unsched_items++; ++ if (!nice_address_equal (&addr, &d->server)) { ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); + } + } + /* check for unauthorized error response */ +-- +2.13.6 + diff --git a/libnice.spec b/libnice.spec index e5944fc..63e60a2 100644 --- a/libnice.spec +++ b/libnice.spec @@ -10,7 +10,7 @@ Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL:
https://nice.freedesktop.org/wiki/
Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
-Patch1: libnice-0.1.14-20171128.patch +Patch1: libnice-0.1.14-70-gfb2f1f7.patch BuildRequires: autoconf BuildRequires: automake commit 5ecb7654163d99321b6206e83dca01578775405d Author: Stefan Becker <chemobejk(a)gmail.com> Date: Mon Jan 29 17:56:32 2018 +0200 update to 0.1.14-70-gfb2f1f7 diff --git a/libnice-0.1.14-20171128.patch b/libnice-0.1.14-20171128.patch new file mode 100644 index 0000000..ff2d2ae --- /dev/null +++ b/libnice-0.1.14-20171128.patch @@ -0,0 +1,6711 @@ +commit fb2f1f77a31baa91968fc81c205f980b6913f403 +Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Tue Nov 28 16:05:18 2017 -0500 + + conncheck: handle alternate-server for turn relays differently + + If a relay gives us an alternate-server, we need to cancel and reset + every candidate discovery attempt that uses the same server, to avoid + ending up with one component on one server and the other component on + another server (causing relay candidates with mismatched foundations). + +commit 4172d48852ecd1c86cc7bd4665b23697603d1eed +Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Tue Nov 28 15:14:11 2017 -0500 + + discovery: Increase discovery_unsched_items whenever we restart a check + + The discovery_unsched_items is decremented every time a DiscoveryCandidate + goes from non-pending to pending. So if we restart a check by setting + pending to FALSE, we should re-increase the discovery_unsched_items. + +commit 59fcf95d505c3995f858b826d10cd48321ed383e +Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> +Date: Mon Nov 27 17:07:02 2017 -0500 + + turn: Add support for ALTERNATE_SERVER in OC2007 Compatibility + + The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in + allocation responses, and if they are not handled, we end up using the + main turn server to send allocation requests that then get sent to the + alternate server which will return the XOR_MAPPED_ADDRESS containing + the IP address of the turn server that proxied the message instead of + our own actual external IP. + +commit 17f30e4465efe9533799b02d6f95feeaf0f2748c +Author: Miguel París <mparisdiaz(a)gmail.com> +Date: Wed Nov 8 16:26:47 2017 +0000 + + conncheck: do not require that all streams have a connection check list + + One or more streams might not have any connection check list if the + number of streams differs from the peer agent. + Differential Revision:
https://phabricator.freedesktop.org/D1880
+ +commit c63349894b3fe974494453a883dfb5ad05df5a46 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu Nov 23 18:31:31 2017 +0100 + + Makefile: really enable debug for tests + + Differential Revision:
https://phabricator.freedesktop.org/D1888
+ +commit 02216a6766caccb652387d5ee19686149eedbc93 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Nov 21 15:12:45 2017 +0100 + + agent: prevent external role change while conncheck is running + + With this patch, we stash the controlling mode property change, and + apply it safely, when it won't interfere with an ongoing conncheck + running. According to RFC5245, sect 5.2. "Determining Role", the role + is determined for a session, and persists unless an ICE is restarted. + + Differential Revision:
https://phabricator.freedesktop.org/D1887
+ +commit fbdccf0c2787ebdc65fe13ac64bd25c829ea7972 +Author: Philip Withnall <withnall(a)endlessm.com> +Date: Thu Aug 3 12:20:32 2017 +0100 + + stun: Fix FD leak in test/utility code + +
https://phabricator.freedesktop.org/T7798
+ + Signed-off-by: Philip Withnall <withnall(a)endlessm.com> + Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> + Differential Revision:
https://phabricator.freedesktop.org/D1819
+ +commit 4c4834ab634f735145c8f758a22cbdd9cab79bac +Author: Philip Withnall <withnall(a)endlessm.com> +Date: Tue Sep 12 13:23:53 2017 +0100 + + tests: Fix agent.h header inclusion in test-gstreamer.c + + Spotted by Lukas Gradl on the mailing list. + + Signed-off-by: Philip Withnall <withnall(a)endlessm.com> + +commit 1a1803a45778000720c93d91060cedeb19124a27 +Author: Philip Withnall <withnall(a)endlessm.com> +Date: Tue Sep 12 13:23:31 2017 +0100 + + tests: Fix copyright dates in test-gstreamer.c + + This code is not 1000 years old. + + Signed-off-by: Philip Withnall <withnall(a)endlessm.com> + +commit 14102d44449d2eb4148588ce54fa897fa13b87ad +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun Jul 2 16:02:09 2017 +0200 + + conncheck: change state before updating nominated pairs + + When a pair is nominated while in state failed, we first move + back to state connecting, then we update the selected pair, and + finally we move to state connected. + +commit 72dd26a3368d3506fe8faca7067a02784fb5f0fd +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Wed Jun 28 12:06:48 2017 +0200 + + conncheck: forgot to put a pair in triggered check list + + When a new pair is created from an unknown remote candidate, it + should be enqueue for a triggered check, to allow it to be marked + as nominated on response arrival in priv_mark_pair_nominated(). + Creating it in waiting state is not sufficient since the update + in priv_mark_pair_nominated() from previous commits. + + Differential Revision:
https://phabricator.freedesktop.org/D1763
+ +commit 6fe64fdbc53ab87dffd79972f492665cff14c0a0 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Jun 27 11:01:14 2017 +0200 + + conncheck: update the state of triggered checks pairs + + With this patch, we fix an ambiguity of some parts of the spec, when + the document refers to in-progress pairs, that also concern pairs in + the triggered checks list. + + The first cast is in section 7.1.2.5, "Updating the Nominated Flag", + when the in-progress pair will be nominated on response arrival. This is + handled in function priv_mark_pair_nominated(), when a pair is put to + the triggered check list in reaction to a matching inbound stun request. + Such a pair in priv_mark_pair_nominated() will _always_ be in the + triggered check list, from the previously called function + priv_schedule_triggered_check(). + + The second case is in section 8.1.2, "Updating State" when an in-progress + pair stops its retransmission when another pair of higher priority is + already nominated. This is handled by function priv_prune_pending_checks(). + + Until now, pairs enqueued in the triggered check list move transiently + to state waiting, according to 7.2.1.4. But this state causes wrong + decisions in the two previous cases, because such pairs should in fact + rather be considered "like in-progress", to avoid discarding them + inadvertantly. + + This patch update the state of the triggered check list + pairs to in-progress. It allows to remove exception handling cited + above: the code is a bit more simple, and allows some refactoring + in priv_mark_pair_nominated() between RFC and compatibility modes. + + Differential Revision:
https://phabricator.freedesktop.org/D1762
+ +commit 36f306f4a95f1c2b3e9c584b5a645a78e231c020 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Jun 26 21:41:44 2017 +0200 + + conncheck: support several stun requests per pair + + This patch should improve the reliabily of the connection check by + keeping the record of several simultaneous ongoing stun requests per + pair. A new stun request on an in-progress pair typically is caused by + in inbound stun request from the peer on this same pair. This is named + "Triggered Checks" in the spec. When this situation arises, it is fair + to handle these two stun requests simultaneously, the triggered check, + and the initial ordinary check, since both can potentially succeed. + + Differential Revision:
https://phabricator.freedesktop.org/D1761
+ +commit e860948b5fe3a791119957f26045b8f5159baeff +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Jun 26 21:06:36 2017 +0200 + + conncheck: use stun_timer_remainder less frequently + + We try to use stun_timer_remainder() less frequently, particularily + in the debug messages, and favour of the next_tick value associated + to the pair. + + Differential Revision:
https://phabricator.freedesktop.org/D1760
+ +commit ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Jun 26 20:41:49 2017 +0200 + + conncheck: make debug sentences more accurate + + We add a helper function to print the pair state in-extenso. + + Differential Revision:
https://phabricator.freedesktop.org/D1759
+ +commit 25be00271a4c8c684a2d435d29ae0811dbf5e21c +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Jun 26 20:36:35 2017 +0200 + + conncheck: reorder some chunks of code + + With this patch we simplify the levels of code indentation. + + Differential Revision:
https://phabricator.freedesktop.org/D1758
+ +commit 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Sep 5 14:50:29 2017 -0400 + + agent: Set error if it isn't set + +commit dbaf8f5ccd76089e340883887c7e08e6c04de80a +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 13:22:21 2016 +0200 + + conncheck: improve role conflict debug + + This patch displays explicitely the controlling or controlled + role of the agent. + + Differential Revision:
https://phabricator.freedesktop.org/D874
+ +commit 9f800d3597767855accccc592c34bc4e945f5bd5 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Wed Jun 21 20:42:57 2017 -0400 + + configure: Remove -Wswitch-enum + + Creates useless warnings when other libraries change. + +
https://phabricator.freedesktop.org/T7770
+ +commit 63d273cea42def3567701ad9feab91f63cf9345f +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Thu Feb 11 22:16:48 2016 -0500 + + component: Use non-GClosure dummy callbacks + + GClosures are not that cheap to setup + +commit 2c50d73b82f2ec2422a8e0ea393194486c193c64 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Wed Feb 10 23:20:39 2016 -0500 + + agent: Don't crash if recv cancelled without a GError + +commit dcb0d647174416a292492f8deca86f83a2ef124c +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Wed Jun 21 17:07:17 2017 -0400 + + Repleace UNRELEASED with 0.1.15 + +commit e3ddaa285e389baf3f26cfb6964919718a8f6a00 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Wed Jun 21 16:55:32 2017 -0400 + + agent: Adjust the nice_agent_new_full() to use flags + + This makes it easier to read and more extensible. + +commit c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun Jun 18 10:12:58 2017 +0200 + + agent: remove spurious newlines + + Differential Revision:
https://phabricator.freedesktop.org/D1756
+ +commit b4b8d6628c8c5d4f10af0101f846db4938a3f6c4 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Sun May 28 22:20:36 2017 +0200 + + stun: fix gcc7 implicit fallthrough warning + + Differential Revision:
https://phabricator.freedesktop.org/D1754
+ +commit 195db6b344fc4f9fadc39419dfeec2fc14b23fac +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Fri Jul 15 23:31:42 2016 +0200 + + agent: add new pairs only for gathering streams + + At the end of the local candidate gathering process, we only create new + pairs for streams that are in gathering state. + + Other stream that may be in ready state for example, due to a + previously succeeded conncheck process, may have accumulated some + couples (local,remote) candidates that have not resulted in the creation + a new pair during this previous conncheck process, and we don't want + these new pairs to be added now, because it would generate unneeded + transition changes for a stream unconcerned by this gathering. + + Differential Revision:
https://phabricator.freedesktop.org/D1755
+ +commit 07366a5bca7e4818b8df29d9c7c220da8f752547 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Jun 21 21:47:42 2016 +0200 + + conncheck: fix the component failed transition + + This patch fixes the transition of a component from connecting to + failed, that previously occured due to the propagation of the + keep_timer_going variable, and to the final call to function + priv_update_check_list_failed_components(), after the global agent + timer was stopped. + + Previously, the code almost never entered to failed state, because the + timer was going one, as long as the number of nominated pair was not + enough, and as long as there were valid pairs not yet nominated. Even + if all pair timers were over. + + The definition of the Failed state of a conncheck list is somewhat + contradictory in the spec, depending on weather you read : + + * sect 5.7.4. "Computing States", + "Failed: In this state, the ICE checks have not completed successfully + for this media stream." + + or + + * sect 7.1.3.3. "Check List and Timer State Updates", + "If all of the pairs in the check list are now either in the Failed or + Succeeded state: If there is not a pair in the valid list for each + component of the media stream, the state of the check list is set to + Failed." + + Our understanding of the ICE spec is that, the proper way to enter failed + state instead in when all connchecks have no longer in-progress pairs. + All pairs are either in state succeeded, discovered, or failed. No timer + is still running, and we have no hope that the conncheck list changes + again, except if an unexpected STUN packet arrives later. All pairs in + frozen state is a special case, that is handled separately (sect + 7.1.3.3). + + A special grace delay is added before declaring a component in state + Failed. This delay is not part of the RFC, and it is aimed to limit the + cases when a conncheck list is reactivated just after it's been declared + failed, causing a user visible transition from connecting to failed, and + back from failed to connecting again. This is also required by the test + suite, that counts exactly the number of time each state is entered, and + doesn't expect these transcient failed states to happen (frequent due to + the nature of the testsuite, less frequent in real life). + + Differential Revision:
https://phabricator.freedesktop.org/D1111
+ +commit 95f8805eb7b77755337e28daf1f134587d42b35f +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu Jun 16 17:32:39 2016 +0200 + + conncheck: remove cancelled pair state + + Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for + removal after the nomination of a pair with an higher priority, + described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They + include also pairs that overflow the conncheck list size, but this is a + somewhat more marginal situation. So we are mainly interested in the + first use case of this state. + + This state mixes two different situations, that deserve a distinct + handling : on one side, there are waiting or frozen pairs that must be + removed, this is an immediate action that doesn't need a dedicated state + for that. And on the other side, there are in-progress pairs that + should no longer be retransmitted, because another pair with a higher + priority has already been nominated. + + This patch removes the cancelled state, and adds a flag + retransmit_on_timeout to deal with this last situation. Note that this + case should not generate a triggered check, as per described in section + 7.2.1.4, when the state of the pair is In-Progress or Failed, since this + pair of lower priority has no hope to replace the nominated one. + + Differential Revision:
https://phabricator.freedesktop.org/D1114
+ +commit d516fca1b0e0a6606afec797bdc0690104e779a9 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Jun 14 21:32:26 2016 +0200 + + conncheck: adjust recheck on timeout strategy + + The pair recheck on timeout can easily cause repetitive rechecks in + a ping-pong effect, if both peers with the same behaviour try to + check the same pair almost simultaneously, and if the network rtt + is greater than the initial timer rto. The reply to the initial + stun request may arrive after the in-progress conncheck + cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation + creates a new stun request, and forgets the initial one. + The conncheck timer is restarted with the same initial value, + so the same situation happens again later. + + We choose to avoid resetting the timer in such situation. After enough + retransmissions, the timeout delay, that doubles after each timeout, + becomes longer than the rtt, and the stun reply can be handled. + + Differential Revision:
https://phabricator.freedesktop.org/D1115
+ +commit f19d209decac432a1597d84c3d5809d2208f7457 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Jun 14 21:20:49 2016 +0200 + + conncheck: do not recheck a just succeeded pair + + We cancel the potential in-progress transaction cancellation, caused by + sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before + transmission timeout, or just after timeout, when the pair is + temporarily put on the triggered check list on the way to be + rechecked. This situation is not covered by the RFC 5245. + + Differential Revision:
https://phabricator.freedesktop.org/D1119
+ +commit 59fe48517c0b7db77b99183d31fdd84b55adb5d4 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Jun 14 21:12:16 2016 +0200 + + conncheck: fix a state transition case + + When a new stun request hits a valid pair, of a failed component, we may + have a transition from state failed to connected. In this situation, we + do a logical progression failed -> connecting -> connected, like we do + in function priv_update_check_list_state_for_ready() + + Similarily, when a new stun request hits a failed pair, of a failed + component, triggering a new conncheck for this pair may also cause the + component state to move back from failed to connecting state. + + Differential Revision:
https://phabricator.freedesktop.org/D1118
+ +commit 6a512b6eca9603ce8bf3ed0814fd314684c66ea7 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Jun 14 21:04:49 2016 +0200 + + conncheck: try to change earlier to state ready + + We check if we can move from state connected to ready just + after a pair expired its retransmission count. This pair + will be marked failed, and will no longer be in-progress. + The number of in-progress dropping down to zero is one + of the conditions needed to make the transition to ready, + per component (and not globally as it's the case in other + locations where this check function is called). + + Differential Revision:
https://phabricator.freedesktop.org/D1117
+ +commit 8fa648a15a6700d08165fe97a09f5c068abae1e6 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Apr 11 13:13:51 2016 +0200 + + conncheck: dont cancel a pair for triggered check + + This patch adds another supplementary "corner" case, not covered by the + ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in + the triggered check list should be considered like an in-progress pair, + and cancelled only if its priority is lower than the priority of the + nominated pair. This is required in some aggressive nomination + situations for both peers to select the same pair, having the highest + priority. + + Differential Revision:
https://phabricator.freedesktop.org/D933
+ +commit 11d4e37a030eb144a355dc26c705ef5aa5a975a7 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Fri Apr 1 17:31:44 2016 +0200 + + conncheck: remove a useless pair recheck + + This exception to the ICE spec is no longer needed: when a pair is in + the succeeded state, there is no needed to recheck it again upon + reception of an incoming stun request on it. + + Differential Revision:
https://phabricator.freedesktop.org/D884
+ +commit 25b3eeec70b4e8e3b2154a18cdc8c5604f572012 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 12:56:28 2016 +0200 + + conncheck: update the pair state in triggered check list + + With this patch, we update the state of the pair to waiting when + it is put in the triggered check queue. We also take care to call + priv_schedule_triggered_check() before priv_mark_pair_nominated() + so a pair to be rechecked and put on the triggered check queue + will have a unique state to be tested in the following call to + priv_mark_pair_nominated() when evaluating its nomination attributes. + + Differential Revision:
https://phabricator.freedesktop.org/D883
+ +commit afd8d41bb34afb3864e838ef79026ae4ef15c0d4 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 13:32:49 2016 +0200 + + conncheck: new pairs never have the nominated flag preset + + This patch disables the possibility to set the nominated flag of a + candidate pair at creation time. This possibility was used when a new + pair is created from a new peer reflexive remote candidate, when the + agent is in controlled mode, and an stun request with USE-CANDIDATE is + received. In this case, since previous commit "conncheck: fix a + nomination corner case", we set the nominated flag when the stun + response of this new pair will arrive, and not before. Consequently, + this flag is no longer required when the pair is created. + + Differential Revision:
https://phabricator.freedesktop.org/D881
+ +commit 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 13:30:04 2016 +0200 + + conncheck: fix a nomination corner case + + This patch add two supplementary cases, not covered by the ICE spec, + sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent + receives a STUN request with the USE-CANDIDATE flag, for a pair that is + in the waiting state. We consider that this case is similar to the + in-progress state, and should be handled in the same way. We also accept + when the pair is in frozen state. This latter case happens in the + new-dribble test, when an agent replays incoming early connchecks. + + Differential Revision:
https://phabricator.freedesktop.org/D880
+ +commit 9103a5f2e184211fc160d1d3070ce4d043c71ff0 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 19 18:16:26 2016 +0200 + + conncheck: use the right pair when retriggering a check + + This patch completes the previous patch by adding a link back from the + discovered pair, to the parent pair that generated this check. This link + is needed by the ICE spec, to comply with section 8.1.1.1, "Regular + nomination", where the check to be retriggered is the initial check that + caused the discovery of the valid pair. When the valid pair is a + peer-reflexive pair, the retriggered check must target the succeeded + pair, and not the valid discovered pair. + + Differential Revision:
https://phabricator.freedesktop.org/D879
+ +commit 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 13:25:16 2016 +0200 + + conncheck: link succeeded and discovered pairs + + When the agent has the role of the stun server, is in controlled mode, + and receives a pair with the "use-candidate" attribute set, it must find + a matching succeded or discovered pair in its conncheck list. This is + described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1. + When a matching pair is in succeeded state, the agent must nominate the + valid pair (a discovered pair) constructed from section 7.1.3.2.2, + that's been created from this succeeded one. To make this lookup, we + introduce a new "discovered_pair" member of the CandidateCheckPair + struct, that links the succeeded pair, and its discovered pair + if any. + + Differential Revision:
https://phabricator.freedesktop.org/D878
+ +commit 2fd7808419f459d5f6e97701ca6a350ddee6b7f2 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 19 17:59:27 2016 +0200 + + conncheck: improve triggered check of in-progress pairs + + This patch update the way triggered checks of in-progress pairs are + handled, according to ICE spec, section 7.2.1.4. Previously the same + connection check was retransmitted with an updated timeout. This causes + problems when a controlling role switch occurs in this time frame. + This is the reason why a new connection check must be generated + reflecting the updated role. We introduce a new flag "recheck_on_timeout" + in the pair indicating that the pair must be rechecked at the next timer + expiration. + + Differential Revision:
https://phabricator.freedesktop.org/D875
+ +commit ead3453d04fc70865d176ab073636f8b9078cbbc +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 13:20:38 2016 +0200 + + conncheck: invoke the debug dump in more places + + Differential Revision:
https://phabricator.freedesktop.org/D1123
+ +commit 15c0546f624113b8c0546a1f883a48bff7020f1b +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 19 17:06:32 2016 +0200 + + conncheck: improve the selection of the pairs to be checked + + This patch aims to implement more closely the algorithm described + in RFC 5245 indicating how pairs are transitionned from state Frozen + to Waiting. This is described in 7.1.3.2 when a check succeeded, and + correspond to modifications in function priv_conn_check_unfreeze_related(). + This is also described in 5.7.4 when defining the initial state of the + pairs in a conncheck, and correspond to modifications in function + priv_conn_check_unfreeze_next(). + + This patch introduces the notion of active and frozen check list. It + allows us to define the timer restranmission delay as described in 16.1. + + Another modification in priv_conn_check_tick_unlocked() is that every + stream in handled consecutively, and in an independant way. The pacing + was previously of a single STUN request emitted per callback, it is now + of a triggered check per callback OR a single STUN per callback AND per + stream per callback. + + The description of ordinary checks per stream in 5.8 is detailled in + function priv_conn_check_tick_stream(), and a remaining of the code + used to nominate a pair by the controlling agent is put in a dedicated + function priv_conn_check_tick_stream_nominate() + + Differential Revision:
https://phabricator.freedesktop.org/D813
+ +commit 58d061df8f5425dc1add9c6030a2f891ebda4616 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Mar 7 16:35:09 2016 +0100 + + conncheck: update pair valid property selectively + + With this patch, we fix a corner case when the succeeded pair is a + peer-reflexive candidate pair, that already has been discovered + previously, In this case, the current pair -p- should not be marked + valid, because the valid flag is already set on the discovered pair. + + Differential Revision:
https://phabricator.freedesktop.org/D1124
+ +commit 0636f9addc041cf93c4ff4eaa351b1768d48a32e +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 19 13:12:48 2016 +0200 + + conncheck: implement ice regular nomination method + + This patch implements Regular Nomation as described in RFC5245 + 8.1.1.1. The controlling agent lets valid pairs accumulate, and + decides which pair to recheck with the use-candidate attribute set. + priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated + pair when acting as a STUN server, and + priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to + update the nominated pair when acting as a STUN client. A new + property is also added to the agent to control the nomination + mode, which can be regular of aggressive, with default value + set to aggressive. + + Two new flags are introduced in the CandidateCheckPair structure: + + - use_candidate_on_next_check indicates the STUN client to add the + use-candidate attribute when the pair will be checked. At this + time, the nominated flag has not been set on this pair yet. + + - mark_nominated_on_response_arrival indicates the STUN server + to nominate the pair when its succesfull response to a + previous triggered check will arrive (7.2.1.5, item #2) + + Differential Revision:
https://phabricator.freedesktop.org/D811
+ +commit a602ff57aae6a6afdeab843954c48e6fb5d82d31 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 12 13:02:45 2016 +0200 + + conncheck: fix pair state transition when successful response is received + + According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a + successful check should go to state succeeded, not only the valid + pair built in section 7.1.3.2.2. + + Differential Revision:
https://phabricator.freedesktop.org/D810
+ +commit 3a58ba6120b188d78c5709e0349c0346bfa21c1a +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Feb 1 11:10:21 2016 +0100 + + conncheck: peer reflexive candidates are not paired + + This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning + Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive + Candidates", where discovered candidates do not cause the creation + of new pairs to be checked. + + Differential Revision:
https://phabricator.freedesktop.org/D805
+ +commit 7a2c1edf502849a868b6f1026e8e2c343dee4ded +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Mon Jun 6 22:24:50 2016 +0200 + + conncheck: update selected pair when nominated flag is set + + This modifies commit 8f1f615. It is better focused to update the + selected pair just after its nominated flag has been set. We also keep + the code homogeneous with other places, where the call to + priv_update_selected_pair() immediately follows the setting of + pair->nominated. Moreover in priv_update_check_list_state_for_ready(), + we would call priv_update_selected_pair() more times that necessary when + iterating on all nominated pairs. + + Differential Revision:
https://phabricator.freedesktop.org/D1125
+ +commit 8bb210c5af4bcaf342d7fa4fef6034269e976532 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu Jun 9 23:28:43 2016 +0200 + + stun timer: make properties for stun timer tunables + + Three STUN binding request properties should be customisable. RFC 5245 + describes the retransmission timer of the STUN transaction 'RTO', and + RFC 5389 describes the number of retransmissions to send until a + response is received 'Rc'. The third property is the 'RTO' when + a reliable connection is used. + + RFC 5389 introduces a supplementary property 'Rm' as a multiplier used + to compute the final timeout RTO * Rm. However, this property is not + added in libnice, because this would require breaking the public API for + STUN. Currently, our STUN implementation hardcodes a division by two for + this final timeout. + + Differential Revision:
https://phabricator.freedesktop.org/D1109
+ +commit 80c613699786567fd93db74377138600794a86e0 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Thu Jun 8 16:34:21 2017 -0400 + + agent: Use base_addr to generate rport in SDP + + Reported by Capricornus (zhushengliang) + +
https://phabricator.freedesktop.org/T7763
+ +commit b4abda09c79e4ce372a3771300abf568c85c7ff5 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu Apr 21 18:18:59 2016 +0200 + + interfaces: ignore predefined network interfaces + + Some interfaces, like the one managed by libvirtd to provide a network + bridge to locally hosted virtual machines, can be completely ignored + when gathering ICE candidates. The motivation for adding this + possibility is that, ignoring them doesn't remove capabilities, and + improves the overall speed of the connection check method, by reducing + the number of pairs to be tested. This patch adds the possibility to + define such interfaces in the configuration script. + + Differential Revision:
https://phabricator.freedesktop.org/D948
+ +commit d5446a72233eab8501be0b3fb9060c8be3ba034b +Author: Philip Withnall <withnall(a)endlessm.com> +Date: Mon May 1 08:51:40 2017 +0100 + + examples: Stop installing the examples + + There’s no point in installing them; their benefit is in providing + example code to developers. + + Debian doesn’t package them; Fedora packages them in a separate + subpackage which will have to disappear. + + Signed-off-by: Philip Withnall <withnall(a)endlessm.com> + Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> + Differential Revision:
https://phabricator.freedesktop.org/D1737
+ +commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Tue Apr 5 21:32:39 2016 +0200 + + agent: do not create a GSource for UDP TURN socket + + With this patch, we don't create a new GSource for udp-turn socket, + because it would duplicate the packets already received on the base UDP + socket, as the underlying GSocket is the same. This is a race condition, + because an UDP packet arriving on the base socket, may randomly be + handled by the GSource callback created for the base socket (udp-bsd) of + the callback created for the udp-turn socket. Moreover this callback + already knows how to parse UDP datagrams received from a known turn + server. + + This patch also prevents a subtle bug, when a STUN request is received + directly from a peer, is handled by the udp turn socket. If the agent + already has a valid permission for this remote candidate, established + for another pair, it will happily send the STUN reply through the turn + relay. This generates a source address mismatch on the peer agent, when + it'll receive the STUN response from the turn relay instead of the + initial address the request has been sent to. + + Differential Revision:
https://phabricator.freedesktop.org/D932
+ +commit f6f704c5e8d2193bc67ba2b697c77694e1698c43 +Author: Fabrice Bellet <fabrice(a)bellet.info> +Date: Thu Jun 9 22:22:33 2016 +0200 + + stun timer: fix timeout of the last retransmission + + According to RFC 5389, section 7.2.1, a special timeout is applied to + the last retransmission (Rm * RTO), with Rm default value of 16, instead + of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7. + + As spotted by Olivier Crete, stun_timer_* is a public API, that cannot + be changed, and the initial delay (RTO) is not preserved in the + stun_timer_s struct. So we use a hack that implicitely guess Rm from the + number of transmissions Rc, by generalizing the default value of the + spec for Rm and Rc to other values of Rc passed in stun_timer_start( + + According to the spec, with the default value of Rc=7, the last delay + should be (64 * RTO), and it is instead (16 * RTO). So the last delay + can be computed by dividing the penultimate delay by two, instead of + multiplying it by two. + + Differential Revision:
https://phabricator.freedesktop.org/D1108
+ +commit b0538d8c51f65019867b56a45cf90a70bef38f01 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 11 18:31:21 2017 -0400 + + agent: Ignore remote candidate of non-accepted types + + If we disable ice-tcp or ice-udp, ignore the remote + candidates for those types. + +commit f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 11 16:42:55 2017 -0400 + + conncheck: Check the controlling state when the req was sent + + It was checking when the pair was created, but the role may have + already changed when the request is sent. + +commit 8fc22b0034d04cbc222e0637152b1cee2879eef3 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Wed Apr 5 17:43:26 2017 -0400 + + tests_: Add test to verify that only packets from validated addresses pass + +
https://phabricator.freedesktop.org/T104
+ + Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> + Differential Revision:
https://phabricator.freedesktop.org/D1717
+ +commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 21:27:39 2017 -0400 + + agent: Drop packets not from validated addresses + + This is required by the WebRTC spec. + + Remove test-mainloop as it doesnt even try to do + a negotiation. + +
https://phabricator.freedesktop.org/T104
+ + Differential Revision:
https://phabricator.freedesktop.org/D1716
+ +commit 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 14:41:51 2017 -0400 + + candidate: Add equality check function + + Add a function that can check if two candidates point to the same place. + +
https://phabricator.freedesktop.org/T104
+ + Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> + Differential Revision:
https://phabricator.freedesktop.org/D1715
+ +commit 10c557f23f8337f1304fff27bd85d2eb713cb249 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Wed Apr 5 17:01:35 2017 -0400 + + test-credentials: Fix leak + +commit ae6d939e48366b80570d713b83334191b0982e71 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 20:34:05 2017 -0400 + + debug: Use libnice-verbose, not libnice-nice-verbose + +commit efc6a9be8cb34c899f0454c32e8a1e62b38df474 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 18:42:57 2017 -0400 + + tests: Use automake test-driver for valgrind + + This fixes the valgrind integration with the new test drivers. + +commit 4e605885c9dcaeb3ee443ec902c9c9189b19043f +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 16:16:46 2017 -0400 + + agent: Remove impossible case + +commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 16:16:05 2017 -0400 + + agent: Separate return from NiceSocket and internal enum + + The same variable was used for return values from NiceSocket and + for the internal enum, but 0 and -1 have different meanings in both. + +commit cd255bddc7fa0ddae056b5358a22b380c4eefc42 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 15:24:43 2017 -0400 + + udp-turn: Add some const to internal APIs + +commit db05e8b0fdc713df93cd6a4c3914e5aee38b2391 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 12:30:27 2017 -0400 + + Make clang-analyzer happy + + Various little things, none of which should make a functional difference. + +commit 0672758b9621801c8f0d9e3c920370983b267a68 +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 12:29:29 2017 -0400 + + agent: Don't set variable that won't be used + + It exits the loop immediately, so no point to set the variable. + And it makes the clang static analyzer happy. + +commit 0de1657e4d15d4f1911ab1fad84ea23e7013070f +Author: Olivier Crête <olivier.crete(a)collabora.com> +Date: Tue Apr 4 12:25:50 2017 -0400 + + conncheck: Use the right test for empty remote_frag + + It's now an array, not a pointer, so needs to test to emptyness. + + It's a bugfix on the previous commit, 59ce41df + +commit 59ce41dfb837adf4222b25490cde2e394384ad15 +Author: Miguel París Díaz <mparisdiaz(a)gmail.com> +Date: Fri Mar 31 20:20:38 2017 -0400 + + conncheck: consider answer received when remote credentials are set + + Consider that the answer is received when remote credentials + are set instead of when a remote candidate is set, + which could not happen or could cause more delay for the + connection establishment. + + Ported to git master by Olivier Crête + + Differential Revision:
https://phabricator.freedesktop.org/D1704
+diff --git a/agent/Makefile.am b/agent/Makefile.am +index b585393..915f312 100644 +--- a/agent/Makefile.am ++++ b/agent/Makefile.am +@@ -22,6 +22,12 @@ if WINDOWS + AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP + endif + ++BUILT_SOURCES = \ ++ agent-enum-types.h \ ++ agent-enum-types.c ++ ++CLEANFILES += $(BUILT_SOURCES) ++ + noinst_LTLIBRARIES = libagent.la + + libagent_la_SOURCES = \ +@@ -54,6 +60,23 @@ libagent_la_SOURCES = \ + outputstream.c \ + $(BUILT_SOURCES) + ++agent-enum-types.h: agent.h Makefile ++ $(AM_V_GEN)$(GLIB_MKENUMS) \ ++ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ ++ --fprod "/* enumerations from \"@filename@\" */\n" \ ++ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ ++ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \ ++ $(addprefix $(srcdir)/,agent.h) > $@ ++ ++agent-enum-types.c: agent.h Makefile agent-enum-types.h ++ $(AM_V_GEN)$(GLIB_MKENUMS) \ ++ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \ ++ --fprod "\n/* enumerations from \"@filename@\" */" \ ++ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \ ++ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ ++ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \ ++ $(addprefix $(srcdir)/,agent.h) > $@ ++ + libagent_la_LIBADD = \ + $(top_builddir)/random/libnice-random.la \ + $(top_builddir)/socket/libsocket.la \ +diff --git a/agent/agent-priv.h b/agent/agent-priv.h +index 4d8c9b8..7269be0 100644 +--- a/agent/agent-priv.h ++++ b/agent/agent-priv.h +@@ -106,7 +106,6 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + + #define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */ + #define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */ +-#define NICE_AGENT_TIMER_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */ + #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */ + + +@@ -114,6 +113,27 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, + * MTU and estimated typical sizes of ICE STUN packet */ + #define MAX_STUN_DATAGRAM_PAYLOAD 1300 + ++#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */ ++ ++/* A convenient macro to test if the agent is compatible with RFC5245 ++ * or OC2007R2. Specifically these two modes share the support ++ * of the regular or aggressive nomination mode */ ++#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \ ++ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \ ++ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ ++/* A grace period before declaring a component as failed, in msecs. This ++ * delay is added to reduce the chance to see the agent receiving new ++ * stun activity just after the conncheck list has been declared failed, ++ * reactiviting conncheck activity, and causing a (valid) state ++ * transitions like that: connecting -> failed -> connecting -> ++ * connected -> ready. ++ * Such transitions are not buggy per-se, but may break the ++ * test-suite, that counts precisely the number of time each state ++ * has been set, and doesnt expect these transcient failed states. ++ */ ++#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000 ++ + struct _NiceAgent + { + GObject parent; /* gobject pointer */ +@@ -126,10 +146,14 @@ struct _NiceAgent + NiceProxyType proxy_type; /* property: Proxy type */ + gchar *proxy_username; /* property: Proxy username */ + gchar *proxy_password; /* property: Proxy password */ +- gboolean controlling_mode; /* property: controlling-mode */ ++ gboolean saved_controlling_mode;/* property: controlling-mode */ + guint timer_ta; /* property: timer Ta */ + guint max_conn_checks; /* property: max connectivity checks */ + gboolean force_relay; /* property: force relay */ ++ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */ ++ guint stun_initial_timeout; /* property: stun initial timeout, RTO */ ++ guint stun_reliable_timeout; /* property: stun reliable timeout */ ++ NiceNominationMode nomination_mode; /* property: Nomination mode */ + + GSList *local_addresses; /* list of NiceAddresses for local + interfaces */ +@@ -164,6 +188,10 @@ struct _NiceAgent + guint16 rfc4571_expecting_length; + gboolean use_ice_udp; + gboolean use_ice_tcp; ++ ++ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */ ++ gboolean controlling_mode; /* controlling mode used by the ++ conncheck */ + /* XXX: add pointer to internal data struct for ABI-safe extensions */ + }; + +diff --git a/agent/agent.c b/agent/agent.c +index 555fd16..0773c53 100644 +--- a/agent/agent.c ++++ b/agent/agent.c +@@ -73,6 +73,7 @@ + #include "interfaces.h" + + #include "pseudotcp.h" ++#include "agent-enum-types.h" + + /* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b + * wide. */ +@@ -113,6 +114,10 @@ enum + PROP_BYTESTREAM_TCP, + PROP_KEEPALIVE_CONNCHECK, + PROP_FORCE_RELAY, ++ PROP_STUN_MAX_RETRANSMISSIONS, ++ PROP_STUN_INITIAL_TIMEOUT, ++ PROP_STUN_RELIABLE_TIMEOUT, ++ PROP_NOMINATION_MODE, + }; + + +@@ -400,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass) + 1, /* not a construct property, ignored */ + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:controlling-mode: ++ * ++ * Whether the agent has the controlling role. This property should ++ * be modified before gathering candidates, any modification occuring ++ * later will be hold until ICE is restarted. ++ */ + g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE, + g_param_spec_boolean ( + "controlling-mode", +@@ -436,6 +448,24 @@ nice_agent_class_init (NiceAgentClass *klass) + 0, /* default set in init */ + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:nomination-mode: ++ * ++ * The nomination mode used in the ICE specification for describing ++ * the selection of valid pairs to be used upstream. ++ * <para> See also: #NiceNominationMode </para> ++ * ++ * Since: 0.1.15 ++ */ ++ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE, ++ g_param_spec_enum ( ++ "nomination-mode", ++ "ICE nomination mode", ++ "Nomination mode used in the ICE specification for describing " ++ "the selection of valid pairs to be used upstream", ++ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); ++ + /** + * NiceAgent:proxy-ip: + * +@@ -708,6 +738,76 @@ nice_agent_class_init (NiceAgentClass *klass) + FALSE, + G_PARAM_READWRITE)); + ++ /** ++ * NiceAgent:stun-max-retransmissions ++ * ++ * The maximum number of retransmissions of the STUN binding requests ++ * used in the gathering stage, to find our local candidates, and used ++ * in the connection check stage, to test the validity of each ++ * constructed pair. This property is described as 'Rc' in the RFC ++ * 5389, with a default value of 7. The timeout of each STUN request ++ * is doubled for each retransmission, so the choice of this value has ++ * a direct impact on the time needed to move from the CONNECTED state ++ * to the READY state, and on the time needed to complete the GATHERING ++ * state. ++ * ++ * Since: 0.1.15 ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS, ++ g_param_spec_uint ( ++ "stun-max-retransmissions", ++ "STUN Max Retransmissions", ++ "Maximum number of STUN binding requests retransmissions " ++ "described as 'Rc' in the STUN specification.", ++ 1, 99, ++ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * NiceAgent:stun-initial-timeout ++ * ++ * The initial timeout (msecs) of the STUN binding requests ++ * used in the gathering stage, to find our local candidates. ++ * This property is described as 'RTO' in the RFC 5389 and RFC 5245. ++ * This timeout is doubled for each retransmission, until ++ * #NiceAgent:stun-max-retransmissions have been done, ++ * with an exception for the last restransmission, where the timeout is ++ * divided by two instead (RFC 5389 indicates that a customisable ++ * multiplier 'Rm' to 'RTO' should be used). ++ * ++ * Since: 0.1.15 ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT, ++ g_param_spec_uint ( ++ "stun-initial-timeout", ++ "STUN Initial Timeout", ++ "STUN timeout in msecs of the initial binding requests used in the " ++ "gathering state, described as 'RTO' in the ICE specification.", ++ 20, 9999, ++ STUN_TIMER_DEFAULT_TIMEOUT, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * NiceAgent:stun-reliable-timeout ++ * ++ * The initial timeout of the STUN binding requests used ++ * for a reliable timer. ++ * ++ * Since: 0.1.15 ++ */ ++ ++ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT, ++ g_param_spec_uint ( ++ "stun-reliable-timeout", ++ "STUN Reliable Timeout", ++ "STUN timeout in msecs of the initial binding requests used for " ++ "a reliable timer.", ++ 20, 99999, ++ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ + /* install signals */ + + /** +@@ -1013,6 +1113,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent) + nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker); + } + ++static void ++priv_update_controlling_mode (NiceAgent *agent, gboolean value) ++{ ++ gboolean update_controlling_mode; ++ GSList *i, *j; ++ ++ agent->saved_controlling_mode = value; ++ /* It is safe to update the agent controlling mode when all ++ * components are still in state disconnected. When we leave ++ * this state, the role must stay under the control of the ++ * conncheck algorithm exclusively, until the conncheck is ++ * eventually restarted. See RFC5245, sect 5.2. Determining Role ++ */ ++ if (agent->controlling_mode != agent->saved_controlling_mode) { ++ update_controlling_mode = TRUE; ++ for (i = agent->streams; ++ i && update_controlling_mode; i = i->next) { ++ NiceStream *stream = i->data; ++ for (j = stream->components; ++ j && update_controlling_mode; j = j->next) { ++ NiceComponent *component = j->data; ++ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED) ++ update_controlling_mode = FALSE; ++ } ++ } ++ if (update_controlling_mode) { ++ agent->controlling_mode = agent->saved_controlling_mode; ++ nice_debug ("Agent %p : Property set, changing role to \"%s\".", ++ agent, agent->controlling_mode ? "controlling" : "controlled"); ++ } else { ++ nice_debug ("Agent %p : Property set, role switch requested " ++ "but conncheck already started.", agent); ++ nice_debug ("Agent %p : Property set, staying with role \"%s\" " ++ "until restart.", agent, ++ agent->controlling_mode ? "controlling" : "controlled"); ++ } ++ } else ++ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent, ++ agent->controlling_mode ? "controlling" : "controlled"); ++} ++ + static void + nice_agent_init (NiceAgent *agent) + { +@@ -1022,7 +1163,9 @@ nice_agent_init (NiceAgent *agent) + /* set defaults; not construct params, so set here */ + agent->stun_server_port = DEFAULT_STUN_PORT; + agent->controlling_mode = TRUE; ++ agent->saved_controlling_mode = TRUE; + agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT; ++ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE; + + agent->discovery_list = NULL; + agent->discovery_unsched_items = 0; +@@ -1071,6 +1214,24 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat) + } + + ++NICEAPI_EXPORT NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ NiceAgentOption flags) ++{ ++ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT, ++ "compatibility", compat, ++ "main-context", ctx, ++ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE, ++ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ? ++ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE, ++ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, ++ NULL); ++ ++ return agent; ++} ++ ++ + static void + nice_agent_get_property ( + GObject *object, +@@ -1101,7 +1262,7 @@ nice_agent_get_property ( + break; + + case PROP_CONTROLLING_MODE: +- g_value_set_boolean (value, agent->controlling_mode); ++ g_value_set_boolean (value, agent->saved_controlling_mode); + break; + + case PROP_FULL_MODE: +@@ -1117,6 +1278,10 @@ nice_agent_get_property ( + /* XXX: should we prune the list of already existing checks? */ + break; + ++ case PROP_NOMINATION_MODE: ++ g_value_set_enum (value, agent->nomination_mode); ++ break; ++ + case PROP_PROXY_IP: + g_value_set_string (value, agent->proxy_ip); + break; +@@ -1187,6 +1352,18 @@ nice_agent_get_property ( + g_value_set_boolean (value, agent->force_relay); + break; + ++ case PROP_STUN_MAX_RETRANSMISSIONS: ++ g_value_set_uint (value, agent->stun_max_retransmissions); ++ break; ++ ++ case PROP_STUN_INITIAL_TIMEOUT: ++ g_value_set_uint (value, agent->stun_initial_timeout); ++ break; ++ ++ case PROP_STUN_RELIABLE_TIMEOUT: ++ g_value_set_uint (value, agent->stun_reliable_timeout); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +@@ -1294,7 +1471,7 @@ nice_agent_set_property ( + break; + + case PROP_CONTROLLING_MODE: +- agent->controlling_mode = g_value_get_boolean (value); ++ priv_update_controlling_mode (agent, g_value_get_boolean (value)); + break; + + case PROP_FULL_MODE: +@@ -1309,6 +1486,10 @@ nice_agent_set_property ( + agent->max_conn_checks = g_value_get_uint (value); + break; + ++ case PROP_NOMINATION_MODE: ++ agent->nomination_mode = g_value_get_enum (value); ++ break; ++ + case PROP_PROXY_IP: + g_free (agent->proxy_ip); + agent->proxy_ip = g_value_dup_string (value); +@@ -1374,6 +1555,18 @@ nice_agent_set_property ( + agent->force_relay = g_value_get_boolean (value); + break; + ++ case PROP_STUN_MAX_RETRANSMISSIONS: ++ agent->stun_max_retransmissions = g_value_get_uint (value); ++ break; ++ ++ case PROP_STUN_INITIAL_TIMEOUT: ++ agent->stun_initial_timeout = g_value_get_uint (value); ++ break; ++ ++ case PROP_STUN_RELIABLE_TIMEOUT: ++ agent->stun_reliable_timeout = g_value_get_uint (value); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +@@ -1552,7 +1745,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, + + if (len == 0) { + /* Reached EOS. */ +- len = 0; + goto done; + } else if (len < 0 && + pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) { +@@ -1890,6 +2082,17 @@ void agent_gathering_done (NiceAgent *agent) + + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; ++ ++ /* We ignore streams not in gathering state, typically already in ++ * ready state. Such streams may have couples (local,remote) ++ * candidates that have not resulted in the creation a new pair ++ * during a previous conncheck session, and we don't want these new ++ * pairs to be added now, because it would generate unneeded ++ * transition changes for a stream unconcerned by this gathering. ++ */ ++ if (!stream->gathering) ++ continue; ++ + for (j = stream->components; j; j = j->next) { + NiceComponent *component = j->data; + +@@ -3113,6 +3316,13 @@ static gboolean priv_add_remote_candidate ( + NiceComponent *component; + NiceCandidate *candidate; + ++ if (transport == NICE_CANDIDATE_TRANSPORT_UDP && ++ !agent->use_ice_udp) ++ return FALSE; ++ if (transport != NICE_CANDIDATE_TRANSPORT_UDP && ++ !agent->use_ice_tcp) ++ return FALSE; ++ + if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) + return FALSE; + +@@ -3195,6 +3405,19 @@ static gboolean priv_add_remote_candidate ( + username, password, priority); + } + ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ /* note: If there are TCP candidates for a media stream, ++ * a controlling agent MUST use the regular selection algorithm, ++ * RFC 6544, sect 8, "Concluding ICE Processing" ++ */ ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE && ++ transport != NICE_CANDIDATE_TRANSPORT_UDP) { ++ nice_debug ("Agent %p : we have TCP candidates, switching back " ++ "to regular nomination mode", agent); ++ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR; ++ } ++ } ++ + if (base_addr) + candidate->base_addr = *base_addr; + +@@ -3240,6 +3463,8 @@ nice_agent_set_remote_credentials ( + g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG); + g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD); + ++ conn_check_remote_credentials_set(agent, stream); ++ + ret = TRUE; + goto done; + } +@@ -3342,8 +3567,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, + } + } + +- conn_check_remote_candidates_set(agent, stream, component); +- + if (added > 0) { + conn_check_schedule_next (agent); + } +@@ -3423,7 +3646,8 @@ agent_recv_message_unlocked ( + { + NiceAddress from; + GList *item; +- gint retval; ++ RecvStatus retval; ++ gint sockret; + gboolean is_turn = FALSE; + + /* We need an address for packet parsing, below. */ +@@ -3484,8 +3708,8 @@ agent_recv_message_unlocked ( + local_bufs[i + 1].buffer = message->buffers[i].buffer; + local_bufs[i + 1].size = message->buffers[i].size; + } +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + message->length = ntohs (rfc4571_frame); + } + } else { +@@ -3500,7 +3724,7 @@ agent_recv_message_unlocked ( + _priv_set_socket_tos (agent, new_socket, stream->tos); + nice_component_attach_socket (component, new_socket); + } +- retval = 0; ++ sockret = 0; + } else { + /* In the case of a real ICE-TCP connection, we can use the socket as a + * bytestream and do the read here with caching of data being read +@@ -3509,9 +3733,9 @@ agent_recv_message_unlocked ( + + /* TODO: Support bytestream reads */ + message->length = 0; +- retval = 0; ++ sockret = 0; + if (available <= 0) { +- retval = available; ++ sockret = available; + + /* If we don't call check_connect_result on an outbound connection, + * then is_connected will always return FALSE. That's why we check +@@ -3524,7 +3748,7 @@ agent_recv_message_unlocked ( + * not connected, it means that it failed to connect, so we must + * return an error to make the socket fail/closed + */ +- retval = -1; ++ sockret = -1; + } else { + gint flags = G_SOCKET_MSG_PEEK; + +@@ -3537,7 +3761,7 @@ agent_recv_message_unlocked ( + */ + if (g_socket_receive_message (nicesock->fileno, NULL, + NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0) +- retval = -1; ++ sockret = -1; + } + } else if (agent->rfc4571_expecting_length == 0) { + if ((gsize) available >= sizeof(guint16)) { +@@ -3545,8 +3769,8 @@ agent_recv_message_unlocked ( + GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)}; + NiceInputMessage local_message = { &local_buf, 1, message->from, 0}; + +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + agent->rfc4571_expecting_length = ntohs (rfc4571_frame); + available = g_socket_get_available_bytes (nicesock->fileno); + } +@@ -3590,8 +3814,8 @@ agent_recv_message_unlocked ( + off += local_bufs[i].size; + } + } +- retval = nice_socket_recv_messages (nicesock, &local_message, 1); +- if (retval == 1) { ++ sockret = nice_socket_recv_messages (nicesock, &local_message, 1); ++ if (sockret == 1) { + message->length = local_message.length; + agent->rfc4571_expecting_length -= local_message.length; + } +@@ -3599,23 +3823,26 @@ agent_recv_message_unlocked ( + } + } + } else { +- retval = nice_socket_recv_messages (nicesock, message, 1); ++ sockret = nice_socket_recv_messages (nicesock, message, 1); + } + +- if (retval == 0) { ++ if (sockret == 0) { + retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ + nice_debug_verbose ("%s: Agent %p: no message available on read attempt", + G_STRFUNC, agent); + goto done; +- } else if (retval < 0) { ++ } else if (sockret < 0) { + nice_debug ("Agent %p: %s returned %d, errno (%d) : %s", +- agent, G_STRFUNC, retval, errno, g_strerror (errno)); ++ agent, G_STRFUNC, sockret, errno, g_strerror (errno)); + + retval = RECV_ERROR; + goto done; ++ } else { ++ retval = sockret; + } + +- if (retval == RECV_OOB || message->length == 0) { ++ g_assert (retval != RECV_OOB); ++ if (message->length == 0) { + retval = RECV_OOB; + nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC, + agent); +@@ -3679,8 +3906,6 @@ agent_recv_message_unlocked ( + if (retval == RECV_OOB) + goto done; + +- agent->media_after_tick = TRUE; +- + /* If the message’s stated length is equal to its actual length, it’s probably + * a STUN message; otherwise it’s probably data. */ + if (stun_message_validate_buffer_length_fast ( +@@ -3712,6 +3937,7 @@ agent_recv_message_unlocked ( + nice_debug ("%s: Valid STUN packet received.", G_STRFUNC); + retval = RECV_OOB; + g_free (big_buf); ++ agent->media_after_tick = TRUE; + goto done; + } + } +@@ -3722,6 +3948,23 @@ agent_recv_message_unlocked ( + g_free (big_buf); + } + ++ if (!nice_component_verify_remote_candidate (component, ++ message->from, nicesock)) { ++ if (nice_debug_is_verbose ()) { ++ gchar str[INET6_ADDRSTRLEN]; ++ ++ nice_address_to_string (message->from, str); ++ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source" ++ " %s:%d sock-type: %d", agent, stream->id, component->id, str, ++ nice_address_get_port (message->from), nicesock->type); ++ } ++ ++ retval = RECV_OOB; ++ goto done; ++ } ++ ++ agent->media_after_tick = TRUE; ++ + /* Unhandled STUN; try handling TCP data, then pass to the client. */ + if (message->length > 0 && agent->reliable) { + if (!nice_socket_is_reliable (nicesock) && +@@ -4085,7 +4328,10 @@ static gboolean + nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data) + { + GError **error = user_data; +- return !g_cancellable_set_error_if_cancelled (cancellable, error); ++ ++ if (error && !*error) ++ g_cancellable_set_error_if_cancelled (cancellable, error); ++ return G_SOURCE_REMOVE; + } + + static gint +@@ -4245,7 +4491,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, + "Component removed during call."); + + component = NULL; +- error_reported = TRUE; + + goto recv_error; + } +@@ -4734,6 +4979,11 @@ nice_agent_restart ( + /* step: regenerate tie-breaker value */ + priv_generate_tie_breaker (agent); + ++ /* step: reset controlling mode from the property value */ ++ agent->controlling_mode = agent->saved_controlling_mode; ++ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".", ++ agent, agent->controlling_mode ? "controlling" : "controlled"); ++ + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; + +@@ -5665,7 +5915,7 @@ _generate_candidate_sdp (NiceAgent *agent, + g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type)); + if (nice_address_is_valid (&candidate->base_addr) && + !nice_address_equal (&candidate->addr, &candidate->base_addr)) { +- port = nice_address_get_port (&candidate->addr); ++ port = nice_address_get_port (&candidate->base_addr); + nice_address_to_string (&candidate->base_addr, ip4); + g_string_append_printf (sdp, " raddr %s rport %d", ip4, + port == 0 ? 9 : port); +diff --git a/agent/agent.h b/agent/agent.h +index 47c4d5a..520c4c5 100644 +--- a/agent/agent.h ++++ b/agent/agent.h +@@ -377,6 +377,45 @@ typedef enum + NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP, + } NiceProxyType; + ++/** ++ * NiceNominationMode: ++ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode ++ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode ++ * ++ * An enum to specity the kind of nomination mode to use by ++ * the agent, as described in RFC 5245. Two modes exists, ++ * regular and aggressive. They differ by the way the controlling ++ * agent chooses to put the USE-CANDIDATE attribute in its STUN ++ * messages. The aggressive mode is supposed to nominate a pair ++ * faster, than the regular mode, potentially causing the nominated ++ * pair to change until the connection check completes. ++ * ++ * Since: 0.1.15 ++ */ ++typedef enum ++{ ++ NICE_NOMINATION_MODE_REGULAR = 0, ++ NICE_NOMINATION_MODE_AGGRESSIVE, ++} NiceNominationMode; ++ ++/** ++ * NiceAgentOption: ++ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default ++ * is aggrssive mode (see #NiceNominationMode). ++ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable(). ++ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode ++ * ++ * These are options that can be passed to nice_agent_new_full(). They set ++ * various properties on the agent. Not including them sets the property to ++ * the other value. ++ * ++ * Since: 0.1.15 ++ */ ++typedef enum { ++ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0, ++ NICE_AGENT_OPTION_RELIABLE = 1 << 1, ++ NICE_AGENT_OPTION_LITE_MODE = 1 << 2, ++} NiceAgentOption; + + /** + * NiceAgentRecvFunc: +@@ -428,6 +467,26 @@ nice_agent_new (GMainContext *ctx, NiceCompatibility compat); + NiceAgent * + nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + ++/** ++ * nice_agent_new_full: ++ * @ctx: The Glib Mainloop Context to use for timers ++ * @compat: The compatibility mode of the agent ++ * @flags: Flags to set the properties ++ * ++ * Create a new #NiceAgent with parameters that must be be defined at ++ * construction time. ++ * The returned object must be freed with g_object_unref() ++ * <para> See also: #NiceNominationMode and #NiceAgentOption</para> ++ * ++ * Since: 0.1.15 ++ * ++ * Returns: The new agent GObject ++ */ ++NiceAgent * ++nice_agent_new_full (GMainContext *ctx, ++ NiceCompatibility compat, ++ NiceAgentOption flags); ++ + /** + * nice_agent_add_local_address: + * @agent: The #NiceAgent Object +@@ -447,7 +506,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat); + gboolean + nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr); + +- + /** + * nice_agent_add_stream: + * @agent: The #NiceAgent Object +diff --git a/agent/candidate.c b/agent/candidate.c +index 27966ef..85f8f65 100644 +--- a/agent/candidate.c ++++ b/agent/candidate.c +@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate) + + return copy; + } ++ ++NICEAPI_EXPORT gboolean ++nice_candidate_equal_target (const NiceCandidate *candidate1, ++ const NiceCandidate *candidate2) ++{ ++ g_return_val_if_fail (candidate1 != NULL, FALSE); ++ g_return_val_if_fail (candidate2 != NULL, FALSE); ++ ++ return (candidate1->transport == candidate2->transport && ++ nice_address_equal (&candidate1->addr, &candidate2->addr)); ++} +diff --git a/agent/candidate.h b/agent/candidate.h +index fadfce3..e556c16 100644 +--- a/agent/candidate.h ++++ b/agent/candidate.h +@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate); + NiceCandidate * + nice_candidate_copy (const NiceCandidate *candidate); + +-GType nice_candidate_get_type (void); ++/** ++ * nice_candidate_equal_target: ++ * @candidate1: A candidate ++ * @candidate2: A candidate ++ * ++ * Verifies that the candidates point to the same place, meaning they have ++ * the same transport and the same address. It ignores all other aspects. ++ * ++ * Returns: %TRUE if the candidates point to the same place ++ * ++ * Since: 0.1.15 ++ */ ++gboolean ++nice_candidate_equal_target (const NiceCandidate *candidate1, ++ const NiceCandidate *candidate2); ++ ++ GType nice_candidate_get_type (void); + + /** + * NICE_TYPE_CANDIDATE: +diff --git a/agent/component.c b/agent/component.c +index 32f7463..6eee90e 100644 +--- a/agent/component.c ++++ b/agent/component.c +@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context) + if (socket_source->socket->fileno == NULL) + return; + ++ /* Do not create a GSource for UDP turn socket, because it ++ * would duplicate the packets already received on the base ++ * UDP socket. ++ */ ++ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN) ++ return; ++ + /* Create a source. */ + source = g_socket_create_source (socket_source->socket->fileno, + G_IO_IN, NULL); +@@ -380,7 +387,7 @@ nice_component_restart (NiceComponent *cmp) + for (i = cmp->remote_candidates; i; i = i->next) { + NiceCandidate *candidate = i->data; + +- /* note: do not remove the local candidate that is ++ /* note: do not remove the remote candidate that is + * currently part of the 'selected pair', see ICE + * 9.1.1.1. "ICE Restarts" (ID-19) */ + if (candidate == cmp->selected_pair.remote) { +@@ -435,6 +442,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa + component->selected_pair.remote = pair->remote; + component->selected_pair.priority = pair->priority; + component->selected_pair.prflx_priority = pair->prflx_priority; ++ ++ nice_component_add_valid_candidate (component, pair->remote); + } + + /* +@@ -514,6 +523,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, + component->selected_pair.remote = remote; + component->selected_pair.priority = priority; + ++ /* Get into fallback mode where packets from any source is accepted once ++ * this has been called. This is the expected behavior of pre-ICE SIP. ++ */ ++ component->fallback_mode = TRUE; ++ + return local; + } + +@@ -991,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass) + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + } + ++static gboolean ++dummy_callback (gpointer data) ++{ ++ return G_SOURCE_CONTINUE; ++} ++ ++static void ++source_set_dummy_callback (GSource *source) ++{ ++ g_source_set_callback (source, dummy_callback, NULL, NULL); ++} ++ + static void + nice_component_init (NiceComponent *component) + { +@@ -1013,7 +1039,7 @@ nice_component_init (NiceComponent *component) + component->stop_cancellable = g_cancellable_new (); + component->stop_cancellable_source = + g_cancellable_source_new (component->stop_cancellable); +- g_source_set_dummy_callback (component->stop_cancellable_source); ++ source_set_dummy_callback (component->stop_cancellable_source); + g_source_attach (component->stop_cancellable_source, component->own_ctx); + component->ctx = g_main_context_ref (component->own_ctx); + +@@ -1107,6 +1133,9 @@ nice_component_finalize (GObject *obj) + g_warn_if_fail (cmp->remote_candidates == NULL); + g_warn_if_fail (cmp->incoming_checks == NULL); + ++ g_list_free_full (cmp->valid_candidates, ++ (GDestroyNotify) nice_candidate_free); ++ + g_clear_object (&cmp->tcp); + g_clear_object (&cmp->stop_cancellable); + g_clear_object (&cmp->iostream); +@@ -1225,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_) + child_socket_source->source = + g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, + NULL); +- g_source_set_dummy_callback (child_socket_source->source); ++ source_set_dummy_callback (child_socket_source->source); + g_source_add_child_source (source, child_socket_source->source); + g_source_unref (child_socket_source->source); + component_source->socket_sources = +@@ -1370,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id, + GSource *cancellable_source; + + cancellable_source = g_cancellable_source_new (cancellable); +- g_source_set_dummy_callback (cancellable_source); ++ source_set_dummy_callback (cancellable_source); + g_source_add_child_source ((GSource *) component_source, + cancellable_source); + g_source_unref (cancellable_source); +@@ -1421,3 +1450,83 @@ turn_server_unref (TurnServer *turn) + g_slice_free (TurnServer, turn); + } + } ++ ++void ++nice_component_add_valid_candidate (NiceComponent *component, ++ const NiceCandidate *candidate) ++{ ++ guint count = 0; ++ GList *item, *last = NULL; ++ ++ for (item = component->valid_candidates; item; item = item->next) { ++ NiceCandidate *cand = item->data; ++ ++ last = item; ++ count++; ++ if (nice_candidate_equal_target (cand, candidate)) ++ return; ++ } ++ ++ /* New candidate */ ++ ++ if (nice_debug_is_enabled ()) { ++ char str[INET6_ADDRSTRLEN]; ++ nice_address_to_string (&candidate->addr, str); ++ nice_debug ("Agent %p : %d:%d Adding valid source" ++ " candidate: %s:%d trans: %d", component->agent, ++ candidate->stream_id, candidate->component_id, str, ++ nice_address_get_port (&candidate->addr), candidate->transport); ++ } ++ ++ component->valid_candidates = g_list_prepend ( ++ component->valid_candidates, nice_candidate_copy (candidate)); ++ ++ /* Delete the last one to make sure we don't have a list that is too long, ++ * the candidates are not freed on ICE restart as this would be more complex, ++ * we just keep the list not too long. ++ */ ++ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) { ++ NiceCandidate *cand = last->data; ++ ++ component->valid_candidates = g_list_delete_link ( ++ component->valid_candidates, last); ++ nice_candidate_free (cand); ++ } ++} ++ ++gboolean ++nice_component_verify_remote_candidate (NiceComponent *component, ++ const NiceAddress *address, NiceSocket *nicesock) ++{ ++ GList *item; ++ ++ if (component->fallback_mode) ++ return TRUE; ++ ++ for (item = component->valid_candidates; item; item = item->next) { ++ NiceCandidate *cand = item->data; ++ ++ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD && ++ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) || ++ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) && ++ nice_address_equal (address, &cand->addr)) { ++ /* fast return if it's already the first */ ++ if (item == component->valid_candidates) ++ return TRUE; ++ ++ /* Put the current candidate at the top so that in the normal use-case, ++ * this function becomes O(1). ++ */ ++ component->valid_candidates = g_list_remove_link ( ++ component->valid_candidates, item); ++ component->valid_candidates = g_list_concat (item, ++ component->valid_candidates); ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} +diff --git a/agent/component.h b/agent/component.h +index 6712794..a8a1222 100644 +--- a/agent/component.h ++++ b/agent/component.h +@@ -159,12 +159,14 @@ struct _NiceComponent { + NiceComponentState state; + GSList *local_candidates; /* list of NiceCandidate objs */ + GSList *remote_candidates; /* list of NiceCandidate objs */ ++ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */ + GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */ + guint socket_sources_age; /* incremented when socket_sources changes */ + GSList *incoming_checks; /* list of IncomingCheck objs */ + GList *turn_servers; /* List of TurnServer objs */ + CandidatePair selected_pair; /* independent from checklists, + see ICE 11.1. "Sending Media" (ID-19) */ ++ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */ + NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */ + NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */ + /* I/O handling. The main context must always be non-NULL, and is used for all +@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn); + void + turn_server_unref (TurnServer *turn); + ++void ++nice_component_add_valid_candidate (NiceComponent *component, ++ const NiceCandidate *candidate); ++ ++gboolean ++nice_component_verify_remote_candidate (NiceComponent *component, ++ const NiceAddress *address, NiceSocket *nicesock); ++ + + G_END_DECLS + +diff --git a/agent/conncheck.c b/agent/conncheck.c +index dda2f2f..c8a4edf 100644 +--- a/agent/conncheck.c ++++ b/agent/conncheck.c +@@ -64,8 +64,8 @@ + + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); + static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); +-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); +-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); ++static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id); ++static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand); + static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); + static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, + guint component_id, NiceCandidate *remote, NiceCandidate *local, +@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data); + static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + NiceAgent *agent, guint stream_id, NiceComponent *component, + NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); ++static gboolean priv_update_selected_pair (NiceAgent *agent, ++ NiceComponent *component, CandidateCheckPair *pair); + + static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) + { +@@ -84,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) + now->tv_sec >= timer->tv_sec; + } + ++static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now) ++{ ++ unsigned int delay; ++ if (now->tv_sec > timer->tv_sec || ++ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec)) ++ return 0; ++ delay = (timer->tv_sec - now->tv_sec) * 1000; ++ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000; ++ return delay; ++} ++ + static gchar + priv_state_to_gchar (NiceCheckState state) + { +@@ -98,8 +111,6 @@ priv_state_to_gchar (NiceCheckState state) + return 'F'; + case NICE_CHECK_FROZEN: + return 'Z'; +- case NICE_CHECK_CANCELLED: +- return 'C'; + case NICE_CHECK_DISCOVERED: + return 'D'; + default: +@@ -107,6 +118,54 @@ priv_state_to_gchar (NiceCheckState state) + } + } + ++static const gchar * ++priv_state_to_string (NiceCheckState state) ++{ ++ switch (state) { ++ case NICE_CHECK_WAITING: ++ return "waiting"; ++ case NICE_CHECK_IN_PROGRESS: ++ return "in progress"; ++ case NICE_CHECK_SUCCEEDED: ++ return "succeeded"; ++ case NICE_CHECK_FAILED: ++ return "failed"; ++ case NICE_CHECK_FROZEN: ++ return "frozen"; ++ case NICE_CHECK_DISCOVERED: ++ return "discovered"; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ ++static const gchar * ++priv_ice_return_to_string (StunUsageIceReturn ice_return) ++{ ++ switch (ice_return) { ++ case STUN_USAGE_ICE_RETURN_SUCCESS: ++ return "success"; ++ case STUN_USAGE_ICE_RETURN_ERROR: ++ return "error"; ++ case STUN_USAGE_ICE_RETURN_INVALID: ++ return "invalid"; ++ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT: ++ return "role conflict"; ++ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST: ++ return "invalid request"; ++ case STUN_USAGE_ICE_RETURN_INVALID_METHOD: ++ return "invalid method"; ++ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR: ++ return "memory error"; ++ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS: ++ return "invalid address"; ++ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS: ++ return "no mapped address"; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ + static const gchar * + priv_candidate_type_to_string (NiceCandidateType type) + { +@@ -130,12 +189,15 @@ priv_candidate_type_to_string (NiceCandidateType type) + static void + priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) + { +- GSList *i, *k; +- guint j; ++ GSList *i, *k, *l; ++ guint j, m; ++ GTimeVal now; + + if (!nice_debug_is_verbose ()) + return; + ++ g_get_current_time (&now); ++ + #define PRIORITY_LEN 32 + + nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", +@@ -148,26 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar * + if (pair->component_id == j) { + gchar local_addr[INET6_ADDRSTRLEN]; + gchar remote_addr[INET6_ADDRSTRLEN]; +- StunTimer *timer = &pair->timer; + + nice_address_to_string (&pair->local->addr, local_addr); + nice_address_to_string (&pair->remote->addr, remote_addr); + + nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " +- "f=%s t=%s:%s timer=%d/%d %d/%dms " +- "[%s]:%u > [%s]:%u state=%c%s%s%s", ++ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s", + agent, pair->stream_id, pair->component_id, pair, + pair->foundation, + priv_candidate_type_to_string (pair->local->type), + priv_candidate_type_to_string (pair->remote->type), +- timer->retransmissions, timer->max_retransmissions, +- timer->delay - stun_timer_remainder (timer), timer->delay, + local_addr, nice_address_get_port (&pair->local->addr), + remote_addr, nice_address_get_port (&pair->remote->addr), + priv_state_to_gchar (pair->state), + pair->valid ? "V" : "", + pair->nominated ? "N" : "", + g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); ++ ++ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) { ++ StunTransaction *stun = l->data; ++ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " ++ "stun#=%d timer=%d/%d %d/%dms buf=%p %s", ++ agent, pair->stream_id, pair->component_id, pair, m, ++ stun->timer.retransmissions, stun->timer.max_retransmissions, ++ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now), ++ stun->timer.delay, ++ stun->message.buffer, ++ (m == 0 && pair->retransmit) ? "(R)" : ""); ++ } + } + } + } +@@ -181,6 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa + { + g_assert (pair); + ++ pair->state = NICE_CHECK_IN_PROGRESS; ++ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); + if (agent->triggered_check_queue == NULL || + g_slist_find (agent->triggered_check_queue, pair) == NULL) + agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); +@@ -209,6 +281,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent) + return pair; + } + ++/* ++ * Check if the conncheck list if Active according to ++ * ICE spec, 5.7.4 (Computing States) ++ * ++ * note: the ICE spec in unclear about that, but the check list should ++ * be considered active when there is at least a pair in Waiting state ++ * OR a pair in In-Progress state. ++ */ ++static gboolean ++priv_is_checklist_active (NiceStream *stream) ++{ ++ GSList *i; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS) ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++/* ++ * Check if the conncheck list if Frozen according to ++ * ICE spec, 5.7.4 (Computing States) ++ */ ++static gboolean ++priv_is_checklist_frozen (NiceStream *stream) ++{ ++ GSList *i; ++ ++ if (stream->conncheck_list == NULL) ++ return FALSE; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state != NICE_CHECK_FROZEN) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* ++ * Check if all components of the stream have ++ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.) ++ */ ++static gboolean ++priv_all_components_have_valid_pair (NiceStream *stream) ++{ ++ guint i; ++ GSList *j; ++ ++ for (i = 1; i <= stream->n_components; i++) { ++ for (j = stream->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (p->component_id == i && p->valid) ++ break; ++ } ++ if (j == NULL) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* ++ * Check if the foundation in parameter matches the foundation ++ * of a valid pair in the conncheck list [of stream] (used for ICE spec, ++ * 7.1.3.2.3, point 2.) ++ */ ++static gboolean ++priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream) ++{ ++ GSList *i; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->valid && ++ strncmp (p->foundation, foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) ++ return TRUE; ++ } ++ return FALSE; ++} ++ + /* + * Finds the next connectivity check in WAITING state. + */ +@@ -218,7 +373,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + + /* note: list is sorted in priority order to first waiting check has + * the highest priority */ +- + for (i = conn_check_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; + if (p->state == NICE_CHECK_WAITING) +@@ -228,6 +382,57 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + return NULL; + } + ++/* ++ * Finds the next connectivity check in FROZEN state. ++ */ ++static CandidateCheckPair * ++priv_conn_check_find_next_frozen (GSList *conn_check_list) ++{ ++ GSList *i; ++ ++ /* note: list is sorted in priority order to first frozen check has ++ * the highest priority */ ++ for (i = conn_check_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_FROZEN) ++ return p; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Returns the number of active check lists of the agent ++ */ ++static guint ++priv_number_of_active_check_lists (NiceAgent *agent) ++{ ++ guint n = 0; ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) ++ if (priv_is_checklist_active (i->data)) ++ n++; ++ return n; ++} ++ ++/* ++ * Returns the first stream of the agent having a Frozen ++ * connection check list ++ */ ++static NiceStream * ++priv_find_first_frozen_check_list (NiceAgent *agent) ++{ ++ GSList *i; ++ ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (priv_is_checklist_frozen (stream)) ++ return stream; ++ } ++ return NULL; ++} ++ + /* + * Initiates a new connectivity check for a ICE candidate pair. + * +@@ -235,8 +440,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check + */ + static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) + { +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); + pair->state = NICE_CHECK_IN_PROGRESS; + nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair); + conn_check_send (agent, pair); +@@ -246,58 +449,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair * + /* + * Unfreezes the next connectivity check in the list. Follows the + * algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec +- * (ID-19), with some exceptions (see comments in code). ++ * (RFC5245) + * + * See also sect 7.1.2.2.3 (Updating Pair States), and + * priv_conn_check_unfreeze_related(). + * + * @return TRUE on success, and FALSE if no frozen candidates were found. + */ +-static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) ++static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream) + { +- CandidateCheckPair *pair = NULL; + GSList *i, *j; +- +- /* XXX: the unfreezing is implemented a bit differently than in the +- * current ICE spec, but should still be interoperate: +- * - checks are not grouped by foundation +- * - one frozen check is unfrozen (lowest component-id, highest +- * priority) +- */ ++ GSList *found_list = NULL; ++ gboolean result = FALSE; + + priv_print_conn_check_lists (agent, G_STRFUNC, NULL); + +- for (i = agent->streams; i; i = i->next) { +- NiceStream *stream = i->data; +- guint64 max_frozen_priority = 0; ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p1 = i->data; ++ CandidateCheckPair *pair = NULL; ++ guint lowest_component_id = stream->n_components + 1; ++ guint64 highest_priority = 0; + ++ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp)) ++ continue; ++ found_list = g_slist_prepend (found_list, p1->foundation); + + for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *p = j->data; +- +- /* XXX: the prio check could be removed as the pairs are sorted +- * already */ +- +- if (p->state == NICE_CHECK_FROZEN) { +- if (p->priority > max_frozen_priority) { +- max_frozen_priority = p->priority; +- pair = p; +- } ++ CandidateCheckPair *p2 = i->data; ++ if (strncmp (p2->foundation, p1->foundation, ++ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) { ++ if (p2->component_id < lowest_component_id || ++ (p2->component_id == lowest_component_id && ++ p2->priority > highest_priority)) { ++ pair = p2; ++ lowest_component_id = p2->component_id; ++ highest_priority = p2->priority; ++ } + } + } + +- if (pair) +- break; +- } +- +- if (pair) { +- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation); +- pair->state = NICE_CHECK_WAITING; +- nice_debug ("Agent %p : pair %p state WAITING", agent, pair); +- return TRUE; ++ if (pair) { ++ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", ++ agent, pair, pair->stream_id, pair->component_id, pair->foundation); ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ result = TRUE; ++ } + } +- +- return FALSE; ++ g_slist_free (found_list); ++ return result; + } + + /* +@@ -314,7 +514,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) + static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) + { + GSList *i, *j; +- guint unfrozen = 0; + + g_assert (ok_check); + g_assert (ok_check->state == NICE_CHECK_SUCCEEDED); +@@ -334,60 +533,147 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre + nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check); + p->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- ++unfrozen; + } + } + } + + /* step: perform the step (2) of 'Updating Pair States' */ + stream = agent_find_stream (agent, ok_check->stream_id); +- if (nice_stream_all_components_ready (stream)) { +- /* step: unfreeze checks from other streams */ ++ if (priv_all_components_have_valid_pair (stream)) { + for (i = agent->streams; i ; i = i->next) { ++ /* the agent examines the check list for each other ++ * media stream in turn ++ */ + NiceStream *s = i->data; +- for (j = stream->conncheck_list; j ; j = j->next) { +- CandidateCheckPair *p = j->data; +- +- if (p->stream_id == s->id && +- p->stream_id != ok_check->stream_id) { +- if (p->state == NICE_CHECK_FROZEN && +- strcmp (p->foundation, ok_check->foundation) == 0) { ++ if (s->id == ok_check->stream_id) ++ continue; ++ if (priv_is_checklist_active (s)) { ++ /* checklist is Active ++ */ ++ for (j = s->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (p->state == NICE_CHECK_FROZEN && ++ priv_foundation_matches_a_valid_pair (p->foundation, stream)) { + nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); + p->state = NICE_CHECK_WAITING; + nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- ++unfrozen; +- +- } +- } ++ } ++ } ++ } else if (priv_is_checklist_frozen (s)) { ++ /* checklist is Frozen ++ */ ++ gboolean match_found = FALSE; ++ /* check if there is one pair in the check list whose ++ * foundation matches a pair in the valid list under ++ * consideration ++ */ ++ for (j = s->conncheck_list; j ; j = j->next) { ++ CandidateCheckPair *p = j->data; ++ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) { ++ match_found = TRUE; ++ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check); ++ p->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, p); ++ } ++ } ++ ++ if (!match_found) { ++ /* set the pair with the lowest component ID ++ * and highest priority to Waiting ++ */ ++ priv_conn_check_unfreeze_next (agent, s); ++ } + } +- /* note: only unfreeze check from one stream at a time */ +- if (unfrozen) +- break; + } + } ++} + +- if (unfrozen == 0) +- priv_conn_check_unfreeze_next (agent); ++/* ++ * Create a new STUN transaction and add it to the list ++ * of ongoing stun transactions of a pair. ++ * ++ * @pair the pair the new stun transaction should be added to. ++ * @return the created stun transaction. ++ */ ++static StunTransaction * ++priv_add_stun_transaction (CandidateCheckPair *pair) ++{ ++ StunTransaction *stun = g_slice_new0 (StunTransaction); ++ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun); ++ pair->retransmit = TRUE; ++ return stun; + } + ++/* ++ * Forget a STUN transaction. ++ * ++ * @data the stun transaction to be forgotten. ++ * @user_data the component contained the concerned stun agent. ++ */ + static void +-candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) ++priv_forget_stun_transaction (gpointer data, gpointer user_data) + { ++ StunTransaction *stun = data; ++ NiceComponent *component = user_data; + StunTransactionId id; ++ ++ if (stun->message.buffer != NULL) { ++ stun_message_id (&stun->message, id); ++ stun_agent_forget_transaction (&component->stun_agent, id); ++ } ++} ++ ++static void ++priv_free_stun_transaction (gpointer data) ++{ ++ g_slice_free (StunTransaction, data); ++} ++ ++/* ++ * Remove a STUN transaction from a pair, and forget it ++ * from the related component stun agent. ++ * ++ * @pair the pair the stun transaction should be removed from. ++ * @stun the stun transaction to be removed. ++ * @component the component containing the stun agent used to ++ * forget the stun transaction. ++ */ ++static void ++priv_remove_stun_transaction (CandidateCheckPair *pair, ++ StunTransaction *stun, NiceComponent *component) ++{ ++ priv_forget_stun_transaction (stun, component); ++ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun); ++ priv_free_stun_transaction (stun); ++} ++ ++/* ++ * Remove all STUN transactions from a pair, and forget them ++ * from the related component stun agent. ++ * ++ * @pair the pair the stun list should be cleared. ++ * @component the component containing the stun agent used to ++ * forget the stun transactions. ++ */ ++static void ++priv_free_all_stun_transactions (CandidateCheckPair *pair, ++ NiceComponent *component) ++{ ++ if (component) ++ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component); ++ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction); ++ pair->stun_transactions = NULL; ++} ++ ++static void ++candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) ++{ + NiceComponent *component; + + component = nice_stream_find_component_by_id (stream, p->component_id); +- + p->state = NICE_CHECK_FAILED; + nice_debug ("Agent %p : pair %p state FAILED", agent, p); +- +- if (p->stun_message.buffer != NULL) { +- stun_message_id (&p->stun_message, id); +- stun_agent_forget_transaction (&component->stun_agent, id); +- } +- +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; ++ priv_free_all_stun_transactions (p, component); + } + + /* +@@ -398,78 +684,183 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP + * + * @return will return FALSE when no more pending timers. + */ +-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) ++static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent) + { + gboolean keep_timer_going = FALSE; +- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, +- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; +- guint frozen = 0, waiting = 0; +- GSList *i, *k; ++ GSList *i, *j; ++ CandidateCheckPair *pair; ++ unsigned int timeout; ++ GTimeVal now; + ++ g_get_current_time (&now); ++ ++ /* step: process ongoing STUN transactions */ + for (i = stream->conncheck_list; i ; i = i->next) { + CandidateCheckPair *p = i->data; ++ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; ++ NiceComponent *component; ++ StunTransaction *stun; + +- if (p->state == NICE_CHECK_IN_PROGRESS) { +- if (p->stun_message.buffer == NULL) { +- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); +- } else if (priv_timer_expired (&p->next_tick, now)) { +- switch (stun_timer_refresh (&p->timer)) { +- case STUN_USAGE_TIMER_RETURN_TIMEOUT: +- { +- /* case: error, abort processing */ +- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; +- nice_address_to_string (&p->local->addr, tmpbuf1); +- nice_address_to_string (&p->remote->addr, tmpbuf2); +- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); +- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, +- tmpbuf1, nice_address_get_port (&p->local->addr), +- tmpbuf2, nice_address_get_port (&p->remote->addr)); +- candidate_check_pair_fail (stream, agent, p); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", retransmission failed"); +- +- break; +- } +- case STUN_USAGE_TIMER_RETURN_RETRANSMIT: +- { +- /* case: not ready, so schedule a new timeout */ +- unsigned int timeout = stun_timer_remainder (&p->timer); +- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " +- "(timeout %dms, delay=%dms, retrans=%d).", +- agent, p, timeout, p->timer.delay, p->timer.retransmissions); ++ if (p->stun_transactions == NULL) ++ continue; + +- agent_socket_send (p->sockptr, &p->remote->addr, +- stun_message_length (&p->stun_message), +- (gchar *)p->stun_buffer); ++ if (!agent_find_component (agent, p->stream_id, p->component_id, ++ NULL, &component)) ++ continue; + ++ /* The first stun transaction of the list may eventually be ++ * retransmitted, other stun transactions just have their ++ * timer updated. ++ */ + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); ++ j = p->stun_transactions->next; + +- *stun_transmitted = TRUE; +- return TRUE; +- } +- case STUN_USAGE_TIMER_RETURN_SUCCESS: +- { +- unsigned int timeout = stun_timer_remainder (&p->timer); ++ /* process all stun transactions except the first one */ ++ while (j) { ++ StunTransaction *s = j->data; ++ GSList *next = j->next; + +- /* note: convert from milli to microseconds for g_time_val_add() */ +- p->next_tick = *now; +- g_time_val_add (&p->next_tick, timeout * 1000); +- +- keep_timer_going = TRUE; +- break; +- } ++ if (priv_timer_expired (&s->next_tick, &now)) ++ switch (stun_timer_refresh (&s->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: ++ priv_remove_stun_transaction (p, s, component); ++ break; ++ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: ++ timeout = stun_timer_remainder (&s->timer); ++ s->next_tick = now; ++ g_time_val_add (&s->next_tick, timeout * 1000); ++ break; + default: +- /* Nothing to do. */ + break; +- } + } ++ j = next; + } + ++ if (p->state != NICE_CHECK_IN_PROGRESS) ++ continue; ++ ++ /* process the first stun transaction of the list */ ++ stun = p->stun_transactions->data; ++ if (!priv_timer_expired (&stun->next_tick, &now)) ++ continue; ++ ++ switch (stun_timer_refresh (&stun->timer)) { ++ case STUN_USAGE_TIMER_RETURN_TIMEOUT: ++timer_return_timeout: ++ /* case: error, abort processing */ ++ nice_address_to_string (&p->local->addr, tmpbuf1); ++ nice_address_to_string (&p->remote->addr, tmpbuf2); ++ nice_debug ("Agent %p : Retransmissions failed, giving up on " ++ "connectivity check %p", agent, p); ++ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, ++ tmpbuf1, nice_address_get_port (&p->local->addr), ++ tmpbuf2, nice_address_get_port (&p->remote->addr)); ++ candidate_check_pair_fail (stream, agent, p); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", retransmission failed"); ++ ++ /* perform a check if a transition state from connected to ++ * ready can be performed. This may happen here, when the last ++ * in-progress pair has expired its retransmission count ++ * in priv_conn_check_tick_stream(), which is a condition to ++ * make the transition connected to ready. ++ */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ break; ++ case STUN_USAGE_TIMER_RETURN_RETRANSMIT: ++ /* case: retransmission stopped, due to the nomination of ++ * a pair with a higher priority than this in-progress pair, ++ * ICE spec, sect 8.1.2 "Updating States", item 2.2 ++ */ ++ if (!p->retransmit) ++ goto timer_return_timeout; ++ ++ /* case: not ready, so schedule a new timeout */ ++ timeout = stun_timer_remainder (&stun->timer); ++ ++ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " ++ "(timer=%d/%d %d/%dms).", ++ agent, p, ++ stun->timer.retransmissions, stun->timer.max_retransmissions, ++ stun->timer.delay - timeout, stun->timer.delay); ++ ++ agent_socket_send (p->sockptr, &p->remote->addr, ++ stun_message_length (&stun->message), ++ (gchar *)stun->buffer); ++ ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); ++ ++ return TRUE; ++ case STUN_USAGE_TIMER_RETURN_SUCCESS: ++ timeout = stun_timer_remainder (&stun->timer); ++ ++ /* note: convert from milli to microseconds for g_time_val_add() */ ++ stun->next_tick = now; ++ g_time_val_add (&stun->next_tick, timeout * 1000); ++ ++ keep_timer_going = TRUE; ++ break; ++ default: ++ /* Nothing to do. */ ++ break; ++ } ++ } ++ ++ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks" ++ * note: This code is executed when the triggered checks list is ++ * empty, and when no STUN message has been sent (pacing constraint) ++ */ ++ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); ++ if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Waiting state"); ++ priv_conn_check_initiate (agent, pair); ++ return TRUE; ++ } ++ ++ /* note: this is unclear in the ICE spec, but a check list in Frozen ++ * state (where all pairs are in Frozen state) is not supposed to ++ * change its state by an ordinary check, but only by the success of ++ * checks in other check lists, in priv_conn_check_unfreeze_related(). ++ * The underlying idea is to concentrate the checks on a single check ++ * list initially. ++ */ ++ if (priv_is_checklist_frozen (stream)) ++ return keep_timer_going; ++ ++ /* step: ordinary check continued, if there's no pair in the waiting ++ * state, pick a pair in the frozen state ++ */ ++ pair = priv_conn_check_find_next_frozen (stream->conncheck_list); ++ if (pair) { ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a pair in Frozen state"); ++ pair->state = NICE_CHECK_WAITING; ++ nice_debug ("Agent %p : pair %p state WAITING", agent, pair); ++ priv_conn_check_initiate (agent, pair); ++ return TRUE; ++ } ++ return keep_timer_going; ++} ++ ++static gboolean ++priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent) ++{ ++ gboolean keep_timer_going = FALSE; ++ guint s_inprogress = 0; ++ guint s_succeeded = 0; ++ guint s_discovered = 0; ++ guint s_nominated = 0; ++ guint s_waiting_for_nomination = 0; ++ guint s_valid = 0; ++ guint frozen = 0; ++ guint waiting = 0; ++ GSList *i, *k; ++ ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; + if (p->state == NICE_CHECK_FROZEN) + ++frozen; + else if (p->state == NICE_CHECK_IN_PROGRESS) +@@ -495,12 +886,102 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + if (s_inprogress) + keep_timer_going = TRUE; + +- /* note: if some components have established connectivity, +- * but yet no nominated pair, keep timer going */ + if (s_nominated < stream->n_components && + s_waiting_for_nomination) { +- keep_timer_going = TRUE; +- if (agent->controlling_mode) { ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR && ++ agent->controlling_mode) { ++#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2 ++ /* ICE 8.1.1.1 Regular nomination ++ * we choose to nominate the valid pair of a component if ++ * - there is no pair left frozen, waiting or in-progress, or ++ * - if there are at least two valid pairs, or ++ * - if there is at least one valid pair of type HOST-HOST ++ * ++ * This is the "stopping criterion" described in 8.1.1.1, and is ++ * a "local optimization" between accumulating more valid pairs, ++ * and limiting the time spent waiting for in-progress connections ++ * checks until they finally fail. ++ */ ++ GSList *component_item; ++ ++ for (component_item = stream->components; component_item; ++ component_item = component_item->next) { ++ NiceComponent *component = component_item->data; ++ gboolean already_done = FALSE; ++ gboolean stopping_criterion = FALSE; ++ guint p_valid = 0; ++ guint p_frozen = 0; ++ guint p_waiting = 0; ++ guint p_inprogress = 0; ++ guint p_host_host_valid = 0; ++ ++ /* verify that the choice of the pair to be nominated ++ * has not already been done ++ */ ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ if (p->component_id == component->id) { ++ if (p->use_candidate_on_next_check) ++ already_done = TRUE; ++ if (p->state == NICE_CHECK_FROZEN) ++ p_frozen++; ++ else if (p->state == NICE_CHECK_WAITING) ++ p_waiting++; ++ else if (p->state == NICE_CHECK_IN_PROGRESS) ++ p_inprogress++; ++ if (p->valid) ++ p_valid++; ++ if (p->valid && ++ p->local->type == NICE_CANDIDATE_TYPE_HOST && ++ p->remote->type == NICE_CANDIDATE_TYPE_HOST) ++ p_host_host_valid++; ++ } ++ } ++ ++ if (already_done) ++ continue; ++ ++ stopping_criterion = ++ (p_host_host_valid > 0 || ++ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS || ++ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0)); ++ ++ if (!stopping_criterion) ++ continue; ++ ++ /* when the stopping criterion is satisfied, we choose ++ * a pair to be nominated in the list of valid pairs, ++ * and add it to the triggered checks list ++ */ ++ for (k = stream->conncheck_list; k ; k = k->next) { ++ CandidateCheckPair *p = k->data; ++ /* note: highest priority item selected (list always sorted) */ ++ if (p->component_id == component->id && ++ !p->nominated && ++ !p->use_candidate_on_next_check && ++ p->valid) { ++ /* According a ICE spec, sect 8.1.1.1. "Regular ++ * Nomination", we enqueue the check that produced this ++ * valid pair. When this pair has been discovered, we want ++ * to test its parent pair instead. ++ */ ++ if (p->succeeded_pair != NULL) { ++ g_assert (p->state == NICE_CHECK_DISCOVERED); ++ p = p->succeeded_pair; ++ } ++ g_assert (p->state == NICE_CHECK_SUCCEEDED); ++ nice_debug ("Agent %p : restarting check %p with " ++ "USE-CANDIDATE attrib (regular nomination)", agent, p); ++ p->use_candidate_on_next_check = TRUE; ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ keep_timer_going = TRUE; ++ break; /* move to the next component */ ++ } ++ } ++ } ++ } ++ } else if (agent->controlling_mode) { + GSList *component_item; + + for (component_item = stream->components; component_item; +@@ -515,7 +996,9 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen + p->state == NICE_CHECK_DISCOVERED)) { + nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); + p->nominated = TRUE; ++ priv_update_selected_pair (agent, component, p); + priv_add_pair_to_triggered_check_queue (agent, p); ++ keep_timer_going = TRUE; + break; /* move to the next component */ + } + } +@@ -542,6 +1025,7 @@ conn_check_stop (NiceAgent *agent) + g_source_destroy (agent->conncheck_timer_source); + g_source_unref (agent->conncheck_timer_source); + agent->conncheck_timer_source = NULL; ++ agent->conncheck_timer_grace_period = 0; + } + + +@@ -557,75 +1041,83 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + { + CandidateCheckPair *pair = NULL; + gboolean keep_timer_going = FALSE; +- gboolean res; +- /* note: we try to only generate a single stun transaction per timer +- * callback, to respect some pacing of STUN transaction, as per +- * appendix B.1 of ICE spec. +- */ +- gboolean stun_transmitted = FALSE; + GSList *i, *j; +- GTimeVal now; + +- /* step: process ongoing STUN transactions */ +- g_get_current_time (&now); +- +- for (j = agent->streams; j; j = j->next) { +- NiceStream *stream = j->data; +- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); +- if (res) +- keep_timer_going = res; +- if (stun_transmitted) +- return TRUE; ++ /* configure the initial state of the check lists of the agent ++ * as described in ICE spec, 5.7.4 ++ * ++ * if all pairs in all check lists are in frozen state, then ++ * we are in the initial state (5.7.4, point 1.) ++ */ ++ if (priv_number_of_active_check_lists (agent) == 0) { ++ /* set some pairs of the first stream in the waiting state ++ * ICE spec, 5.7.4, point 2. ++ * ++ * note: we adapt the ICE spec here, by selecting the first ++ * frozen check list, which is not necessarily the check ++ * list of the first stream (the first stream may be completed) ++ */ ++ NiceStream *stream = priv_find_first_frozen_check_list (agent); ++ if (stream) ++ priv_conn_check_unfreeze_next (agent, stream); + } + +- /* step: first initiate a conncheck with a pair from the triggered list */ ++ /* step: perform a test from the triggered checks list, ++ * ICE spec, 5.8 "Scheduling Checks" ++ */ + pair = priv_get_pair_from_triggered_check_queue (agent); + + if (pair) { + priv_print_conn_check_lists (agent, G_STRFUNC, + ", got a pair from triggered check list"); +- priv_conn_check_initiate (agent, pair); ++ conn_check_send (agent, pair); + return TRUE; + } + +- /* step: when the triggered list is empty, +- * find the highest priority waiting check and send it */ ++ /* step: process ongoing STUN transactions and ++ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks" ++ */ + for (i = agent->streams; i ; i = i->next) { + NiceStream *stream = i->data; +- +- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); +- if (pair) +- break; ++ if (priv_conn_check_tick_stream (stream, agent)) ++ keep_timer_going = TRUE; ++ if (priv_conn_check_tick_stream_nominate (stream, agent)) ++ keep_timer_going = TRUE; + } + +- if (pair) { +- priv_conn_check_initiate (agent, pair); +- return TRUE; ++ /* step: if no work left and a conncheck list of a stream is still ++ * frozen, set the pairs to waiting, according to ICE SPEC, sect ++ * 7.1.3.3. "Check List and Timer State Updates" ++ */ ++ if (!keep_timer_going) { ++ for (i = agent->streams; i ; i = i->next) { ++ NiceStream *stream = i->data; ++ if (priv_is_checklist_frozen (stream)) { ++ nice_debug ("Agent %p : stream %d conncheck list is still " ++ "frozen, while other lists are completed. Unfreeze it.", ++ agent, stream->id); ++ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream); ++ } ++ } + } + +- /* step: when there's no pair in the Waiting state, +- * unfreeze a new pair and check it ++ /* note: we provide a grace period before declaring a component as ++ * failed. Components marked connected, and then ready follow another ++ * code path, and are not concerned by this grace period. + */ +- res = priv_conn_check_unfreeze_next (agent); +- +- for (i = agent->streams; i ; i = i->next) { +- NiceStream *stream = i->data; ++ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0) ++ nice_debug ("Agent %p : waiting %d msecs before checking " ++ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD); + +- pair = priv_conn_check_find_next_waiting (stream->conncheck_list); +- if (pair) +- break; +- } +- +- if (pair) { +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a pair in Waiting state"); +- priv_conn_check_initiate (agent, pair); +- return TRUE; +- } ++ if (keep_timer_going) ++ agent->conncheck_timer_grace_period = 0; ++ else ++ agent->conncheck_timer_grace_period += agent->timer_ta; + + /* step: stop timer if no work left */ +- if (keep_timer_going != TRUE) { +- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); ++ if (!keep_timer_going && ++ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) { ++ nice_debug ("Agent %p : checking for failed components now.", agent); + for (i = agent->streams; i; i = i->next) { + NiceStream *stream = i->data; + priv_update_check_list_failed_components (agent, stream); +@@ -635,6 +1127,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + } + } + ++ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); + priv_print_conn_check_lists (agent, G_STRFUNC, + ", conncheck timer stopped"); + +@@ -645,9 +1138,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) + + /* XXX: what to signal, is all processing now really done? */ + nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); ++ return FALSE; + } + +- return keep_timer_going; ++ return TRUE; + } + + static gboolean priv_conn_check_tick (gpointer pointer) +@@ -888,8 +1382,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) + agent, buf_len, p->keepalive.stun_message.buffer); + + if (buf_len > 0) { +- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&p->keepalive.timer, ++ agent->stun_initial_timeout, ++ agent->stun_max_retransmissions); + + agent->media_after_tick = FALSE; + +@@ -1116,8 +1611,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand) + } + + if (buffer_len > 0) { +- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&cand->timer, ++ cand->agent->stun_initial_timeout, ++ cand->agent->stun_max_retransmissions); + + /* send the refresh */ + agent_socket_send (cand->nicesock, &cand->server, +@@ -1213,154 +1709,136 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea + if (nice_address_equal (&icheck->from, &pair->remote->addr) && + icheck->local_socket == pair->sockptr) { + nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, pair->remote); + if (icheck->use_candidate) + priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); + } + } + } + + +-static GSList *prune_cancelled_conn_check (GSList *conncheck_list) +-{ +- GSList *item = conncheck_list; +- +- while (item) { +- CandidateCheckPair *pair = item->data; +- GSList *next = item->next; +- +- if (pair->state == NICE_CHECK_CANCELLED) { +- conn_check_free_item (pair); +- conncheck_list = g_slist_delete_link (conncheck_list, item); +- } +- +- item = next; +- } +- +- return conncheck_list; +-} +- +- + /* + * Handle any processing steps for connectivity checks after +- * remote candidates have been set. This function handles ++ * remote credentials have been set. This function handles + * the special case where answerer has sent us connectivity +- * checks before the answer (containing candidate information), ++ * checks before the answer (containing credentials information), + * reaches us. The special case is documented in sect 7.2 + * if ICE spec (ID-19). + */ +-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) ++void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream) + { +- GSList *j, *k, *l, *m, *n; ++ GSList *j, *k, *l, *m, *n, *o; + + for (j = stream->conncheck_list; j ; j = j->next) { + CandidateCheckPair *pair = j->data; +- if (pair->component_id == component->id) { +- gboolean match = FALSE; +- +- /* performn delayed processing of spec steps section 7.2.1.4, +- and section 7.2.1.5 */ +- priv_preprocess_conn_check_pending_data (agent, stream, component, pair); +- +- for (k = component->incoming_checks; k; k = k->next) { +- IncomingCheck *icheck = k->data; +- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to +- * be handled separately */ +- for (l = component->remote_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&icheck->from, &cand->addr)) { +- match = TRUE; +- break; +- } +- } +- if (match != TRUE) { +- /* note: we have gotten an incoming connectivity check from +- * an address that is not a known remote candidate */ +- +- NiceCandidate *local_candidate = NULL; +- NiceCandidate *remote_candidate = NULL; +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || +- agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- /* We need to find which local candidate was used */ +- uint8_t uname[NICE_STREAM_MAX_UNAME]; +- guint uname_len; +- +- nice_debug ("Agent %p: We have a peer-reflexive candidate in a " +- "stored pending check", agent); +- +- for (m = component->remote_candidates; +- m != NULL && remote_candidate == NULL; m = m->next) { +- for (n = component->local_candidates; n; n = n->next) { +- NiceCandidate *rcand = m->data; +- NiceCandidate *lcand = n->data; +- +- uname_len = priv_create_username (agent, stream, +- component->id, rcand, lcand, +- uname, sizeof (uname), TRUE); +- +- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", +- icheck->username_len, uname_len, +- icheck->username && uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0); +- stun_debug_bytes (" first username: ", +- icheck->username, +- icheck->username? icheck->username_len : 0); +- stun_debug_bytes (" second username: ", uname, uname_len); +- +- if (icheck->username && +- uname_len == icheck->username_len && +- memcmp (uname, icheck->username, icheck->username_len) == 0) { +- local_candidate = lcand; +- remote_candidate = rcand; +- break; +- } +- } +- } +- } else { +- for (l = component->local_candidates; l; l = l->next) { +- NiceCandidate *cand = l->data; +- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { +- local_candidate = cand; ++ NiceComponent *component = ++ nice_stream_find_component_by_id (stream, pair->component_id); ++ gboolean match = FALSE; ++ ++ /* performn delayed processing of spec steps section 7.2.1.4, ++ and section 7.2.1.5 */ ++ priv_preprocess_conn_check_pending_data (agent, stream, component, pair); ++ ++ for (k = component->incoming_checks; k; k = k->next) { ++ IncomingCheck *icheck = k->data; ++ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to ++ * be handled separately */ ++ for (l = component->remote_candidates; l; l = l->next) { ++ NiceCandidate *cand = l->data; ++ if (nice_address_equal (&icheck->from, &cand->addr)) { ++ match = TRUE; ++ break; ++ } ++ } ++ if (match != TRUE) { ++ /* note: we have gotten an incoming connectivity check from ++ * an address that is not a known remote candidate */ ++ ++ NiceCandidate *local_candidate = NULL; ++ NiceCandidate *remote_candidate = NULL; ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || ++ agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ /* We need to find which local candidate was used */ ++ uint8_t uname[NICE_STREAM_MAX_UNAME]; ++ guint uname_len; ++ ++ nice_debug ("Agent %p: We have a peer-reflexive candidate in a " ++ "stored pending check", agent); ++ ++ for (m = component->remote_candidates; ++ m != NULL && remote_candidate == NULL; m = m->next) { ++ for (n = component->local_candidates; n; n = n->next) { ++ NiceCandidate *rcand = m->data; ++ NiceCandidate *lcand = n->data; ++ ++ uname_len = priv_create_username (agent, stream, ++ component->id, rcand, lcand, ++ uname, sizeof (uname), TRUE); ++ ++ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d", ++ icheck->username_len, uname_len, ++ icheck->username && uname_len == icheck->username_len && ++ memcmp (uname, icheck->username, icheck->username_len) == 0); ++ stun_debug_bytes (" first username: ", ++ icheck->username, ++ icheck->username? icheck->username_len : 0); ++ stun_debug_bytes (" second username: ", uname, uname_len); ++ ++ if (icheck->username && ++ uname_len == icheck->username_len && ++ memcmp (uname, icheck->username, icheck->username_len) == 0) { ++ local_candidate = lcand; ++ remote_candidate = rcand; + break; + } + } + } +- +- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && +- local_candidate == NULL) { +- /* if we couldn't match the username, then the matching remote +- * candidate hasn't been received yet.. we must wait */ +- nice_debug ("Agent %p : Username check failed. pending check has " +- "to wait to be processed", agent); +- } else { +- NiceCandidate *candidate; +- +- nice_debug ("Agent %p : Discovered peer reflexive from early i-check", +- agent); +- candidate = +- discovery_learn_remote_peer_reflexive_candidate (agent, +- stream, +- component, +- icheck->priority, +- &icheck->from, +- icheck->local_socket, +- local_candidate, remote_candidate); +- if (candidate) { +- if (local_candidate && +- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) +- priv_conn_check_add_for_candidate_pair_matched (agent, +- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); +- else +- conn_check_add_for_candidate (agent, stream->id, component, candidate); +- +- if (icheck->use_candidate) +- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); +- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); ++ } else { ++ for (l = component->local_candidates; l; l = l->next) { ++ NiceCandidate *cand = l->data; ++ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) { ++ local_candidate = cand; ++ break; + } + } + } ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE && ++ local_candidate == NULL) { ++ /* if we couldn't match the username, then the matching remote ++ * candidate hasn't been received yet.. we must wait */ ++ nice_debug ("Agent %p : Username check failed. pending check has " ++ "to wait to be processed", agent); ++ } else { ++ NiceCandidate *candidate; ++ ++ nice_debug ("Agent %p : Discovered peer reflexive from early i-check", ++ agent); ++ candidate = ++ discovery_learn_remote_peer_reflexive_candidate (agent, ++ stream, ++ component, ++ icheck->priority, ++ &icheck->from, ++ icheck->local_socket, ++ local_candidate, remote_candidate); ++ if (candidate) { ++ if (local_candidate && ++ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) ++ priv_conn_check_add_for_candidate_pair_matched (agent, ++ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED); ++ else ++ conn_check_add_for_candidate (agent, stream->id, component, candidate); ++ ++ priv_schedule_triggered_check (agent, stream, component, ++ icheck->local_socket, candidate); ++ if (icheck->use_candidate) ++ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); ++ } ++ } + } + } + } +@@ -1368,12 +1846,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice + /* Once we process the pending checks, we should free them to avoid + * reprocessing them again if a dribble-mode set_remote_candidates + * is called */ +- g_slist_free_full (component->incoming_checks, +- (GDestroyNotify) incoming_check_free); +- component->incoming_checks = NULL; +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); ++ for (o = stream->components; o; o = o->next) { ++ NiceComponent *component = o->data; ++ g_slist_free_full (component->incoming_checks, ++ (GDestroyNotify) incoming_check_free); ++ component->incoming_checks = NULL; ++ } + } + + /* +@@ -1381,7 +1859,7 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice + * in ICE spec section 5.7.3 (ID-19). See also + * conn_check_add_for_candidate(). + */ +-static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) ++static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit) + { + guint valid = 0; + guint cancelled = 0; +@@ -1389,22 +1867,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper + + while (item) { + CandidateCheckPair *pair = item->data; ++ GSList *next = item->next; + +- if (pair->state != NICE_CHECK_CANCELLED) { +- valid++; +- if (valid > upper_limit) { +- pair->state = NICE_CHECK_CANCELLED; ++ valid++; ++ if (valid > upper_limit) { ++ conn_check_free_item (pair); ++ conncheck_list = g_slist_delete_link (conncheck_list, item); + cancelled++; +- } + } +- +- item = item->next; ++ item = next; + } + + if (cancelled > 0) + nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements" + " left. Maximum connchecks allowed : %d", cancelled, valid, + upper_limit); ++ return conncheck_list; + } + + /* +@@ -1444,15 +1922,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp + * Updates the check list state. + * + * Implements parts of the algorithm described in +- * ICE sect 8.1.2. "Updating States" (ID-19): if for any ++ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any + * component, all checks have been completed and have +- * failed, mark that component's state to NICE_CHECK_FAILED. ++ * failed to produce a nominated pair, mark that component's ++ * state to NICE_CHECK_FAILED. + * + * Sends a component state changesignal via 'agent'. + */ + static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) + { + GSList *i; ++ gboolean completed; ++ guint nominated; + /* note: emitting a signal might cause the client + * to remove the stream, thus the component count + * must be fetched before entering the loop*/ +@@ -1476,6 +1957,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) + continue; + ++ nominated = 0; ++ completed = TRUE; + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +@@ -1483,16 +1966,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre + g_assert (p->stream_id == stream->id); + + if (p->component_id == (c + 1)) { +- if (p->state != NICE_CHECK_FAILED) +- break; ++ if (p->nominated) ++ ++nominated; ++ if (p->state != NICE_CHECK_FAILED && ++ p->state != NICE_CHECK_SUCCEEDED && ++ p->state != NICE_CHECK_DISCOVERED) ++ completed = FALSE; + } + } + +- /* note: all checks have failed ++ /* note: all pairs are either failed or succeeded, and the component ++ * has not produced a nominated pair. + * Set the component to FAILED only if it actually had remote candidates + * that failed.. */ +- if (i == NULL && comp != NULL && comp->remote_candidates != NULL) +- agent_signal_component_state_change (agent, ++ if (completed && nominated == 0 && ++ comp != NULL && comp->remote_candidates != NULL) ++ agent_signal_component_state_change (agent, + stream->id, + (c + 1), /* component-id */ + NICE_COMPONENT_STATE_FAILED); +@@ -1525,7 +2014,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream + ++valid; + if (p->nominated == TRUE) { + ++nominated; +- priv_update_selected_pair (agent, component, p); + } + } + } +@@ -1535,7 +2023,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream + /* Only go to READY if no checks are left in progress. If there are + * any that are kept, then this function will be called again when the + * conncheck tick timer finishes them all */ +- if (priv_prune_pending_checks (stream, component->id) == 0) { ++ if (priv_prune_pending_checks (agent, stream, component->id) == 0) { + /* Continue through the states to give client code a nice + * logical progression. See
http://phabricator.freedesktop.org/D218
for + * discussion. */ +@@ -1564,24 +2052,59 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice + + g_assert (component); + ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && ++ agent->controlling_mode) ++ return; ++ + /* step: search for at least one nominated pair */ + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *pair = i->data; + if (pair->local == localcand && pair->remote == remotecand) { +- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); +- pair->nominated = TRUE; ++ /* ICE, 7.2.1.5. Updating the Nominated Flag */ ++ /* note: TCP candidates typically produce peer reflexive ++ * candidate, generating a "discovered" pair that can be ++ * nominated. ++ */ ++ if (pair->state == NICE_CHECK_SUCCEEDED && ++ pair->discovered_pair != NULL) { ++ pair = pair->discovered_pair; ++ g_assert (pair->state == NICE_CHECK_DISCOVERED); ++ } ++ ++ /* If the state of this pair is In-Progress, [...] the resulting ++ * valid pair has its nominated flag set when the response ++ * arrives. ++ */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) && ++ pair->state == NICE_CHECK_IN_PROGRESS) { ++ pair->mark_nominated_on_response_arrival = TRUE; ++ nice_debug ("Agent %p : pair %p (%s) is in-progress, " ++ "will be nominated on response receipt.", ++ agent, pair, pair->foundation); ++ } ++ ++ if (pair->valid || ++ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated", ++ agent, pair, pair->foundation); ++ pair->nominated = TRUE; ++ } ++ + if (pair->valid) { +- priv_update_selected_pair (agent, component, pair); + /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { ++ if (component->state == NICE_COMPONENT_STATE_FAILED) ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING); ++ priv_update_selected_pair (agent, component, pair); ++ if (component->state == NICE_COMPONENT_STATE_CONNECTING) + /* step: notify the client of a new component state (must be done + * before the possible check list state update step */ + agent_signal_component_state_change (agent, + stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } +- + } +- priv_update_check_list_state_for_ready (agent, stream, component); ++ ++ if (pair->nominated) ++ priv_update_check_list_state_for_ready (agent, stream, component); + } + } + } +@@ -1624,7 +2147,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority) + */ + static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + guint stream_id, NiceComponent *component, NiceCandidate *local, +- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) ++ NiceCandidate *remote, NiceCheckState initial_state) + { + NiceStream *stream; + CandidateCheckPair *pair; +@@ -1662,8 +2185,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr)); + } +- pair->nominated = use_candidate; +- pair->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local)); + +@@ -1675,7 +2196,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, + /* implement the hard upper limit for number of + checks (see sect 5.7.3 ICE ID-19): */ + if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { +- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); ++ stream->conncheck_list = priv_limit_conn_check_list_size ( ++ stream->conncheck_list, agent->max_conn_checks); + } + + return pair; +@@ -1709,7 +2231,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( + agent, local->foundation, remote->foundation, + stream_id, component->id); + pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, +- initial_state, FALSE); ++ initial_state); + if (component->state == NICE_COMPONENT_STATE_CONNECTED || + component->state == NICE_COMPONENT_STATE_READY) { + agent_signal_component_state_change (agent, +@@ -1781,6 +2303,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone + + g_assert (remote != NULL); + ++ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates", ++ * the agent does not pair this candidate with any local candidates. ++ */ ++ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && ++ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) ++ { ++ return added; ++ } ++ + for (i = component->local_candidates; i ; i = i->next) { + NiceCandidate *local = i->data; + +@@ -1815,6 +2346,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC + + g_assert (local != NULL); + ++ /* ++ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive ++ * Candidates", the peer reflexive candidate is not paired ++ * with other remote candidates ++ */ ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 && ++ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) ++ { ++ return added; ++ } ++ + for (i = component->remote_candidates; i ; i = i->next) { + + NiceCandidate *remote = i->data; +@@ -1838,8 +2381,7 @@ static void conn_check_free_item (gpointer data) + + if (pair->agent) + priv_remove_pair_from_triggered_check_queue (pair->agent, pair); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; ++ priv_free_all_stun_transactions (pair, NULL); + g_slice_free (CandidateCheckPair, pair); + } + +@@ -2057,30 +2599,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream, + + /* Implement the computation specific in RFC 5245 section 16 */ + +-static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) ++static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream) + { +- GSList *item1, *item2; ++ GSList *i; + guint waiting_and_in_progress = 0; ++ guint n = 0; + unsigned int rto = 0; + +- +- for (item1 = agent->streams; item1; item1 = item1->next) { +- NiceStream *stream = item1->data;; +- for (item2 = stream->conncheck_list; item2; item2 = item2->next) { +- CandidateCheckPair *pair = item2->data; +- +- if (pair->state == NICE_CHECK_IN_PROGRESS || +- pair->state == NICE_CHECK_WAITING) +- waiting_and_in_progress++; +- } ++ for (i = stream->conncheck_list; i ; i = i->next) { ++ CandidateCheckPair *p = i->data; ++ if (p->state == NICE_CHECK_IN_PROGRESS || ++ p->state == NICE_CHECK_WAITING) ++ waiting_and_in_progress++; + } + +- rto = agent->timer_ta * waiting_and_in_progress; ++ n = priv_number_of_active_check_lists (agent); ++ rto = agent->timer_ta * n * waiting_and_in_progress; + + /* We assume non-reliable streams are RTP, so we use 100 as the max */ +- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", ++ nice_debug ("Agent %p : timer set to %dms, " ++ "waiting+in_progress=%d, nb_active=%d", + agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), +- waiting_and_in_progress); ++ waiting_and_in_progress, n); + if (agent->reliable) + return MAX (rto, 500); + else +@@ -2114,6 +2654,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + bool cand_use = controlling; + size_t buffer_len; + unsigned int timeout; ++ StunTransaction *stun; + + if (!agent_find_component (agent, pair->stream_id, pair->component_id, + &stream, &component)) +@@ -2135,99 +2676,119 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + nice_address_to_string (&pair->local->addr, tmpbuf1); + nice_address_to_string (&pair->remote->addr, tmpbuf2); + nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " +- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " +- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, ++ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " ++ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent, + tmpbuf1, nice_address_get_port (&pair->local->addr), + tmpbuf2, nice_address_get_port (&pair->remote->addr), + pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, +- pair->foundation, pair->component_id, ++ pair, pair->component_id, + (unsigned long long)agent->tie_breaker, + (int) uname_len, uname, uname_len, + (int) password_len, password, password_len, +- pair->prflx_priority, controlling); ++ pair->prflx_priority, ++ controlling ? "controlling" : "controlled"); + } + +- if (cand_use) ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ /* We are doing regular nomination, so we set the use-candidate ++ * attrib, when the controlling agent decided which valid pair to ++ * resend with this flag in priv_conn_check_tick_stream() ++ */ ++ cand_use = pair->use_candidate_on_next_check; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(regular nomination).", agent, G_STRFUNC, cand_use); ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ /* We are doing aggressive nomination, we set the use-candidate ++ * attrib in every check we send, when we are the controlling ++ * agent, RFC 5245, 8.1.1.2 ++ */ ++ cand_use = controlling; ++ nice_debug ("Agent %p : %s: set cand_use=%d " ++ "(aggressive nomination).", agent, G_STRFUNC, cand_use); ++ break; ++ default: ++ /* Nothing to do. */ ++ break; ++ } ++ } else if (cand_use) + pair->nominated = controlling; + +- if (uname_len > 0) { +- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, +- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), +- uname, uname_len, password, password_len, +- cand_use, controlling, pair->prflx_priority, +- agent->tie_breaker, +- pair->local->foundation, +- agent_to_ice_compatibility (agent)); ++ if (uname_len == 0) { ++ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); ++ return -1; ++ } + +- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, +- pair->stun_message.buffer); ++ stun = priv_add_stun_transaction (pair); + +- if (agent->compatibility == NICE_COMPATIBILITY_MSN || +- agent->compatibility == NICE_COMPATIBILITY_OC2007) { +- g_free (password); +- } ++ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, ++ &stun->message, stun->buffer, sizeof(stun->buffer), ++ uname, uname_len, password, password_len, ++ cand_use, controlling, pair->prflx_priority, ++ agent->tie_breaker, ++ pair->local->foundation, ++ agent_to_ice_compatibility (agent)); + +- if (buffer_len > 0) { +- if (nice_socket_is_reliable(pair->sockptr)) { +- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); +- } else { +- stun_timer_start (&pair->timer, +- priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +- } ++ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len, ++ stun->message.buffer); + +- /* TCP-ACTIVE candidate must create a new socket before sending +- * by connecting to the peer. The new socket is stored in the candidate +- * check pair, until we discover a new local peer reflexive */ +- if (pair->sockptr->fileno == NULL && +- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && +- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { +- NiceStream *stream2 = NULL; +- NiceComponent *component2 = NULL; +- NiceSocket *new_socket; +- +- if (agent_find_component (agent, pair->stream_id, pair->component_id, +- &stream2, &component2)) { +- new_socket = nice_tcp_active_socket_connect (pair->sockptr, +- &pair->remote->addr); +- if (new_socket) { +- pair->sockptr = new_socket; +- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); +- +- if (agent->reliable) { +- nice_socket_set_writable_callback (pair->sockptr, +- _tcp_sock_is_writable, component2); +- } ++ if (agent->compatibility == NICE_COMPATIBILITY_MSN || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007) { ++ g_free (password); ++ } + +- nice_component_attach_socket (component2, new_socket); +- } +- } +- } +- /* send the conncheck */ +- agent_socket_send (pair->sockptr, &pair->remote->addr, +- buffer_len, (gchar *)pair->stun_buffer); ++ if (buffer_len == 0) { ++ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); ++ priv_remove_stun_transaction (pair, stun, component); ++ return -1; ++ } + +- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { +- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, +- &pair->remote->addr); +- } ++ if (nice_socket_is_reliable(pair->sockptr)) { ++ timeout = agent->stun_reliable_timeout; ++ stun_timer_start_reliable(&stun->timer, timeout); ++ } else { ++ timeout = priv_compute_conncheck_timer (agent, stream); ++ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions); ++ } + +- timeout = stun_timer_remainder (&pair->timer); +- /* note: convert from milli to microseconds for g_time_val_add() */ +- g_get_current_time (&pair->next_tick); +- g_time_val_add (&pair->next_tick, timeout * 1000); +- } else { +- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; +- return -1; ++ g_get_current_time (&stun->next_tick); ++ g_time_val_add (&stun->next_tick, timeout * 1000); ++ ++ /* TCP-ACTIVE candidate must create a new socket before sending ++ * by connecting to the peer. The new socket is stored in the candidate ++ * check pair, until we discover a new local peer reflexive */ ++ if (pair->sockptr->fileno == NULL && ++ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && ++ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { ++ NiceStream *stream2 = NULL; ++ NiceComponent *component2 = NULL; ++ NiceSocket *new_socket; ++ ++ if (agent_find_component (agent, pair->stream_id, pair->component_id, ++ &stream2, &component2)) { ++ new_socket = nice_tcp_active_socket_connect (pair->sockptr, ++ &pair->remote->addr); ++ if (new_socket) { ++ pair->sockptr = new_socket; ++ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); ++ ++ if (agent->reliable) ++ nice_socket_set_writable_callback (pair->sockptr, ++ _tcp_sock_is_writable, component2); ++ ++ nice_component_attach_socket (component2, new_socket); ++ } + } +- } else { +- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent); +- pair->stun_message.buffer = NULL; +- pair->stun_message.buffer_len = 0; +- return -1; + } ++ /* send the conncheck */ ++ agent_socket_send (pair->sockptr, &pair->remote->addr, ++ buffer_len, (gchar *)stun->buffer); ++ ++ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ++ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr, ++ &pair->remote->addr); + + return 0; + } +@@ -2238,14 +2799,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) + * + * @see priv_update_check_list_state_failed_components() + */ +-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) ++static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id) + { + GSList *i; + guint64 highest_nominated_priority = 0; + guint in_progress = 0; + +- nice_debug ("Agent XXX: Finding highest priority for component %d", +- component_id); ++ nice_debug ("Agent %p: Finding highest priority for component %d", ++ agent, component_id); + + for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; +@@ -2257,37 +2818,40 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + } + } + +- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority " +- "is %" G_GUINT64_FORMAT, highest_nominated_priority); ++ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority " ++ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority); + + /* step: cancel all FROZEN and WAITING pairs for the component */ +- for (i = stream->conncheck_list; i; i = i->next) { ++ i = stream->conncheck_list; ++ while (i) { + CandidateCheckPair *p = i->data; ++ GSList *next = i->next; ++ + if (p->component_id == component_id) { +- if (p->state == NICE_CHECK_FROZEN || +- p->state == NICE_CHECK_WAITING) { +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) { ++ nice_debug ("Agent %p : pair %p removed.", agent, p); ++ conn_check_free_item (p); ++ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i); + } + + /* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */ +- if (p->state == NICE_CHECK_IN_PROGRESS) { +- if (highest_nominated_priority != 0 && +- p->priority < highest_nominated_priority) { +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_CANCELLED; +- nice_debug ("Agent XXX : pair %p state CANCELED", p); ++ else if (p->state == NICE_CHECK_IN_PROGRESS) { ++ if (p->priority < highest_nominated_priority) { ++ p->retransmit = FALSE; ++ nice_debug ("Agent %p : pair %p will not be retransmitted.", ++ agent, p); + } else { + /* We must keep the higher priority pairs running because if a udp + * packet was lost, we might end up using a bad candidate */ +- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %" ++ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %" + G_GUINT64_FORMAT " is higher than currently nominated pair %" +- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority); ++ G_GUINT64_FORMAT, agent, ++ p, p->priority, highest_nominated_priority); + in_progress++; + } + } + } ++ i = next; + } + + return in_progress; +@@ -2301,17 +2865,17 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) + * @param component the check is related to + * @param local_socket socket from which the inbound check was received + * @param remote_cand remote candidate from which the inbound check was sent +- * @param use_candidate whether the original check had USE-CANDIDATE attribute set + */ +-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) ++static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand) + { + GSList *i; + NiceCandidate *local = NULL; ++ CandidateCheckPair *p; + + g_assert (remote_cand != NULL); + + for (i = stream->conncheck_list; i ; i = i->next) { +- CandidateCheckPair *p = i->data; ++ p = i->data; + if (p->component_id == component->id && + p->remote == remote_cand && + ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && +@@ -2322,61 +2886,67 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + * tcp-active we don't want to retrigger a check on a pair that + * was FAILED when a peer-reflexive pair was created */ + +- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p); ++ if (p->succeeded_pair != NULL) { ++ g_assert (p->state == NICE_CHECK_DISCOVERED); ++ p = p->succeeded_pair; ++ } ++ ++ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...", ++ agent, p, p->foundation, priv_state_to_string (p->state)); + +- if (p->state == NICE_CHECK_WAITING || +- p->state == NICE_CHECK_FROZEN) +- priv_add_pair_to_triggered_check_queue (agent, p); +- else if (p->state == NICE_CHECK_IN_PROGRESS) { +- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), +- * we should cancel the existing one, instead we reset our timer, so +- * we'll resend the exiting transactions faster if needed...? :P +- */ +- nice_debug ("Agent %p : check already in progress, " +- "restarting the timer again?: %s ..", agent, +- p->timer_restarted ? "no" : "yes"); +- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { +- stun_timer_start (&p->timer, +- priv_compute_conncheck_timer (agent), +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +- p->timer_restarted = TRUE; +- } +- } +- else if (p->state == NICE_CHECK_SUCCEEDED || +- p->state == NICE_CHECK_DISCOVERED) { +- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); +- /* note: this is a bit unsure corner-case -- let's do the +- same state update as for processing responses to our own checks */ +- /* note: this update is required by the dribble test, to +- * ensure the transition ready -> connected -> ready, because +- * an incoming stun request generates a discovered peer reflexive, +- * that causes the ready -> connected transition. +- */ +- priv_update_check_list_state_for_ready (agent, stream, component); +- +- /* note: this new check is required by the new-dribble test, +- * when early icheck on the peer controlled agent causes an +- * incoming stun request to an already succeeded (and +- * nominated) pair on the controlling agent. If the +- * controlling agent doesn't retrigger a check with +- * USE-CANDIDATE=1, the peer agent has no way to nominate it. +- * +- * This behavior differs from ICE spec 7.2.1.4 +- */ +- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || +- agent->compatibility == NICE_COMPATIBILITY_WLM2009 || +- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && +- agent->controlling_mode) { ++ switch (p->state) { ++ case NICE_CHECK_WAITING: ++ case NICE_CHECK_FROZEN: ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); + priv_add_pair_to_triggered_check_queue (agent, p); +- conn_check_schedule_next(agent); +- } +- } else if (p->state == NICE_CHECK_FAILED) { +- /* 7.2.1.4 Triggered Checks +- * If the state of the pair is Failed, it is changed to Waiting +- and the agent MUST create a new connectivity check for that +- pair (representing a new STUN Binding request transaction), by +- enqueueing the pair in the triggered check queue. */ +- priv_add_pair_to_triggered_check_queue (agent, p); ++ break; ++ case NICE_CHECK_IN_PROGRESS: ++ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks" ++ * we cancel the in-progress transaction, and after the ++ * retransmission timeout, we create a new connectivity check ++ * for that pair. The controlling role of this new check may ++ * be different from the role of this cancelled check. ++ * ++ * note: the flag retransmit unset means that ++ * another pair, with a higher priority is already nominated, ++ * so there's no reason to recheck this pair, since it can in ++ * no way replace the nominated one. ++ */ ++ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ } ++ break; ++ case NICE_CHECK_FAILED: ++ if (p->retransmit) { ++ nice_debug ("Agent %p : pair %p added for a triggered check.", ++ agent, p); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ /* If the component for this pair is in failed state, move it ++ * back to connecting, and reinitiate the timers ++ */ ++ if (component->state == NICE_COMPONENT_STATE_FAILED) { ++ agent_signal_component_state_change (agent, stream->id, ++ component->id, NICE_COMPONENT_STATE_CONNECTING); ++ conn_check_schedule_next (agent); ++ } ++ } ++ break; ++ case NICE_CHECK_SUCCEEDED: ++ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p); ++ /* note: this is a bit unsure corner-case -- let's do the ++ same state update as for processing responses to our own checks */ ++ /* note: this update is required by the dribble test, to ++ * ensure the transition ready -> connected -> ready, because ++ * an incoming stun request generates a discovered peer reflexive, ++ * that causes the ready -> connected transition. ++ */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ break; ++ default: ++ break; + } + + /* note: the spec says the we SHOULD retransmit in-progress +@@ -2394,7 +2964,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str + + if (i) { + nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local); +- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate); ++ p = priv_add_new_check_pair (agent, stream->id, component, ++ local, remote_cand, NICE_CHECK_WAITING); ++ priv_add_pair_to_triggered_check_queue (agent, p); + return TRUE; + } + else { +@@ -2447,9 +3019,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + } + + if (rcand) { +- /* note: upon successful check, make the reserve check immediately */ +- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); +- ++ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand); + if (use_candidate) + priv_mark_pair_nominated (agent, stream, component, lcand, rcand); + } +@@ -2509,6 +3079,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->remote = parent_pair->remote; + pair->sockptr = local_cand->sockptr; + pair->state = NICE_CHECK_DISCOVERED; ++ parent_pair->discovered_pair = pair; ++ pair->succeeded_pair = parent_pair; + nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); + { + gchar tmpbuf1[INET6_ADDRSTRLEN]; +@@ -2528,7 +3100,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint + pair->priority = nice_candidate_pair_priority (pair->remote->priority, + pair->local->priority); + pair->nominated = FALSE; +- pair->controlling = agent->controlling_mode; + pair->prflx_priority = ensure_unique_priority (component, + peer_reflexive_candidate_priority (agent, local_cand)); + nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); +@@ -2567,14 +3138,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) + { + /* role conflict, change mode; wait for a new conn. check */ + if (control != agent->controlling_mode) { +- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control); ++ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".", ++ agent, control ? "controlling" : "controlled"); + agent->controlling_mode = control; + /* the pair priorities depend on the roles, so recalculation + * is needed */ + priv_recalculate_pair_priorities (agent); + } + else +- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control); ++ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".", ++ agent, control ? "controlling" : "controlled"); + } + + /* +@@ -2621,13 +3194,25 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + } + + if (new_pair) { ++ /* note: when new_pair is distinct from p, it means new_pair is a ++ * previously discovered peer-reflexive candidate pair, so we don't ++ * set the valid flag on p in this case, because the valid flag is ++ * already set on the discovered pair. ++ */ ++ if (new_pair == p) ++ p->valid = TRUE; + p->state = NICE_CHECK_SUCCEEDED; ++ priv_remove_pair_from_triggered_check_queue (agent, p); ++ priv_free_all_stun_transactions (p, component); + nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); +- priv_conn_check_unfreeze_related (agent, stream, p); ++ nice_component_add_valid_candidate (component, remote_candidate); + } + else { + if (!local_cand) { +- if (!agent->force_relay) ++ if (!agent->force_relay) { ++ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1. ++ * "Discovering Peer Reflexive Candidates" ++ */ + local_cand = discovery_add_peer_reflexive_candidate (agent, + stream->id, + component->id, +@@ -2635,8 +3220,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + sockptr, + local_candidate, + remote_candidate); +- p->state = NICE_CHECK_FAILED; +- nice_debug ("Agent %p : pair %p state FAILED", agent, p); ++ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p", ++ agent, local_cand); ++ } + } + + /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 +@@ -2644,13 +3230,23 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent * + if (local_cand) + new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, + local_cand, p); +- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); ++ /* note: this is same as "adding to VALID LIST" in the spec ++ text */ ++ if (new_pair) ++ new_pair->valid = TRUE; ++ /* step: The agent sets the state of the pair that *generated* the check to ++ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States" ++ */ ++ p->state = NICE_CHECK_SUCCEEDED; ++ priv_remove_pair_from_triggered_check_queue (agent, p); ++ priv_free_all_stun_transactions (p, component); ++ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.", ++ agent, p, new_pair); + } + +- /* note: this is same as "adding to VALID LIST" in the spec +- text */ +- if (new_pair) +- new_pair->valid = TRUE; ++ if (new_pair && new_pair->valid) ++ nice_component_add_valid_candidate (component, remote_candidate); ++ + + return new_pair; + } +@@ -2669,134 +3265,199 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre + struct sockaddr addr; + } sockaddr; + socklen_t socklen = sizeof (sockaddr); +- GSList *i; ++ GSList *i, *j; ++ guint k; + StunUsageIceReturn res; +- gboolean trans_found = FALSE; + StunTransactionId discovery_id; + StunTransactionId response_id; + stun_message_id (resp, response_id); + +- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) { ++ for (i = stream->conncheck_list; i; i = i->next) { + CandidateCheckPair *p = i->data; + +- if (p->stun_message.buffer) { +- stun_message_id (&p->stun_message, discovery_id); +- +- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) { +- res = stun_usage_ice_conncheck_process (resp, +- &sockaddr.storage, &socklen, +- agent_to_ice_compatibility (agent)); +- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d " +- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode); +- +- if (res == STUN_USAGE_ICE_RETURN_SUCCESS || +- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* case: found a matching connectivity check request */ +- +- CandidateCheckPair *ok_pair = NULL; +- +- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- +- /* step: verify that response came from the same IP address we +- * sent the original request to (see 7.1.2.1. "Failure +- * Cases") */ +- if (nice_address_equal (from, &p->remote->addr) != TRUE) { +- +- p->state = NICE_CHECK_FAILED; +- if (nice_debug_is_enabled ()) { +- gchar tmpbuf[INET6_ADDRSTRLEN]; +- gchar tmpbuf2[INET6_ADDRSTRLEN]; +- nice_debug ("Agent %p : conncheck %p FAILED" +- " (mismatch of source address).", agent, p); +- nice_address_to_string (&p->remote->addr, tmpbuf); +- nice_address_to_string (from, tmpbuf2); +- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, +- tmpbuf, nice_address_get_port (&p->remote->addr), +- tmpbuf2, nice_address_get_port (from)); +- } +- trans_found = TRUE; +- break; +- } +- +- /* note: CONNECTED but not yet READY, see docs */ +- +- /* step: handle the possible case of a peer-reflexive +- * candidate where the mapped-address in response does +- * not match any local candidate, see 7.1.2.2.1 +- * "Discovering Peer Reflexive Candidates" ICE ID-19) */ +- +- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { +- /* note: this is same as "adding to VALID LIST" in the spec +- text */ +- p->state = NICE_CHECK_SUCCEEDED; +- p->valid = TRUE; +- g_assert_not_reached (); +- nice_debug ("Agent %p : Mapped address not found." +- " conncheck %p SUCCEEDED.", agent, p); +- priv_conn_check_unfreeze_related (agent, stream, p); +- } else { +- ok_pair = priv_process_response_check_for_reflexive (agent, +- stream, component, p, sockptr, &sockaddr.addr, +- local_candidate, remote_candidate); +- } +- +- +- if (!ok_pair) +- ok_pair = p; +- +- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the +- Nominated Flag" (ID-19) */ +- if (ok_pair->nominated == TRUE) { +- priv_update_selected_pair (agent, component, ok_pair); +- priv_print_conn_check_lists (agent, G_STRFUNC, +- ", got a nominated pair"); ++ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) { ++ StunTransaction *stun = j->data; ++ ++ stun_message_id (&stun->message, discovery_id); ++ ++ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId))) ++ continue; ++ ++ res = stun_usage_ice_conncheck_process (resp, ++ &sockaddr.storage, &socklen, ++ agent_to_ice_compatibility (agent)); ++ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: " ++ "%s,res=%s,stun#=%d.", ++ agent, p, ++ agent->controlling_mode ? "controlling" : "controlled", ++ priv_ice_return_to_string (res), k); ++ ++ if (res == STUN_USAGE_ICE_RETURN_SUCCESS || ++ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ /* case: found a matching connectivity check request */ ++ ++ CandidateCheckPair *ok_pair = NULL; ++ ++ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p); ++ priv_remove_stun_transaction (p, stun, component); ++ ++ /* step: verify that response came from the same IP address we ++ * sent the original request to (see 7.1.2.1. "Failure ++ * Cases") */ ++ if (nice_address_equal (from, &p->remote->addr) == FALSE) { ++ candidate_check_pair_fail (stream, agent, p); ++ if (nice_debug_is_enabled ()) { ++ gchar tmpbuf[INET6_ADDRSTRLEN]; ++ gchar tmpbuf2[INET6_ADDRSTRLEN]; ++ nice_debug ("Agent %p : conncheck %p FAILED" ++ " (mismatch of source address).", agent, p); ++ nice_address_to_string (&p->remote->addr, tmpbuf); ++ nice_address_to_string (from, tmpbuf2); ++ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent, ++ tmpbuf, nice_address_get_port (&p->remote->addr), ++ tmpbuf2, nice_address_get_port (from)); ++ } ++ return TRUE; ++ } + +- /* Do not step down to CONNECTED if we're already at state READY*/ +- if (component->state != NICE_COMPONENT_STATE_READY) { +- /* step: notify the client of a new component state (must be done +- * before the possible check list state update step */ +- agent_signal_component_state_change (agent, +- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); +- } +- } ++ /* note: CONNECTED but not yet READY, see docs */ ++ ++ /* step: handle the possible case of a peer-reflexive ++ * candidate where the mapped-address in response does ++ * not match any local candidate, see 7.1.2.2.1 ++ * "Discovering Peer Reflexive Candidates" ICE ID-19) */ ++ ++ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) { ++ p->state = NICE_CHECK_SUCCEEDED; ++ p->valid = TRUE; ++ nice_debug ("Agent %p : Mapped address not found." ++ " conncheck %p SUCCEEDED.", agent, p); ++ nice_component_add_valid_candidate (component, p->remote); ++ } else ++ ok_pair = priv_process_response_check_for_reflexive (agent, ++ stream, component, p, sockptr, &sockaddr.addr, ++ local_candidate, remote_candidate); ++ ++ /* note: The success of this check might also ++ * cause the state of other checks to change as well, ICE ++ * spec 7.1.3.2.3 ++ */ ++ priv_conn_check_unfreeze_related (agent, stream, p); ++ ++ /* Note: this assignment helps to reduce the numbers of cases ++ * to be tested. If ok_pair and p refer to distinct pairs, it ++ * means that ok_pair is a discovered peer reflexive one, ++ * caused by the check made on pair p. In that case, the ++ * flags to be tested are on p, but the nominated flag will be ++ * set on ok_pair. When there's no discovered pair, p and ++ * ok_pair refer to the same pair. ++ * To summarize : p is a SUCCEEDED pair, ok_pair is a ++ * DISCOVERED, VALID, and eventually NOMINATED pair. ++ */ ++ if (!ok_pair) ++ ok_pair = p; ++ ++ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the ++ Nominated Flag" (ID-19) */ ++ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) { ++ nice_debug ("Agent %p : Updating nominated flag (%s): " ++ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)", ++ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ? ++ "UDP" : "TCP", ++ ok_pair, ok_pair->use_candidate_on_next_check, ++ ok_pair->mark_nominated_on_response_arrival, ++ p, p->use_candidate_on_next_check, ++ p->mark_nominated_on_response_arrival); ++ ++ if (agent->controlling_mode) { ++ switch (agent->nomination_mode) { ++ case NICE_NOMINATION_MODE_REGULAR: ++ if (p->use_candidate_on_next_check) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(regular nomination, controlling, " ++ "use_cand_on_next_check=1).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ case NICE_NOMINATION_MODE_AGGRESSIVE: ++ if (!p->nominated) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(aggressive nomination, controlling).", ++ agent, ok_pair, ok_pair->foundation); ++ ok_pair->nominated = TRUE; ++ } ++ break; ++ default: ++ /* Nothing to do */ ++ break; ++ } ++ } else { ++ if (p->mark_nominated_on_response_arrival) { ++ nice_debug ("Agent %p : marking pair %p (%s) as nominated " ++ "(%s nomination, controlled, mark_on_response=1).", ++ agent, ok_pair, ok_pair->foundation, ++ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ? ++ "aggressive" : "regular"); ++ ok_pair->nominated = TRUE; ++ } ++ } ++ } + +- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair +- states" and 8.1.2 "Updating States", ID-19) */ +- priv_update_check_list_state_for_ready (agent, stream, component); ++ if (ok_pair->nominated == TRUE) { ++ priv_update_selected_pair (agent, component, ok_pair); ++ priv_print_conn_check_lists (agent, G_STRFUNC, ++ ", got a nominated pair"); ++ ++ /* Do not step down to CONNECTED if we're already at state READY*/ ++ if (component->state != NICE_COMPONENT_STATE_READY) ++ /* step: notify the client of a new component state (must be done ++ * before the possible check list state update step */ ++ agent_signal_component_state_change (agent, ++ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); ++ } + +- trans_found = TRUE; +- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { +- /* case: role conflict error, need to restart with new role */ +- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); +- /* note: our role might already have changed due to an +- * incoming request, but if not, change role now; +- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */ +- priv_check_for_role_conflict (agent, !p->controlling); +- +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- p->state = NICE_CHECK_WAITING; +- priv_add_pair_to_triggered_check_queue (agent, p); +- nice_debug ("Agent %p : pair %p state WAITING", agent, p); +- trans_found = TRUE; +- } else { +- /* case: STUN error, the check STUN context was freed */ +- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); +- p->stun_message.buffer = NULL; +- p->stun_message.buffer_len = 0; +- trans_found = TRUE; +- } ++ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair ++ states" and 8.1.2 "Updating States", ID-19) */ ++ priv_update_check_list_state_for_ready (agent, stream, component); ++ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) { ++ guint64 tie; ++ gboolean controlled_mode; ++ ++ /* case: role conflict error, need to restart with new role */ ++ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p); ++ ++ /* note: this res value indicates that the role of the peer ++ * agent has not changed after the tie-breaker comparison, so ++ * this is our role that must change. see ICE sect. 7.1.3.1 ++ * "Failure Cases". Our role might already have changed due to ++ * an earlier incoming request, but if not, change role now. ++ * ++ * Sect. 7.1.3.1 is not clear on this point, but we choose to ++ * put the candidate pair in the triggered check list even ++ * when the agent did not switch its role. The reason for this ++ * interpretation is that the reception of the stun reply, even ++ * an error reply, is a good sign that this pair will be ++ * valid, if we retry the check after the role of both peers ++ * has been fixed. ++ */ ++ controlled_mode = (stun_message_find64 (&stun->message, ++ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) == ++ STUN_MESSAGE_RETURN_SUCCESS); ++ ++ priv_check_for_role_conflict (agent, controlled_mode); ++ priv_remove_stun_transaction (p, stun, component); ++ priv_add_pair_to_triggered_check_queue (agent, p); ++ } else { ++ /* case: STUN error, the check STUN context was freed */ ++ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p); ++ candidate_check_pair_fail (stream, agent, p); + } ++ return TRUE; + } + } + +- +- stream->conncheck_list = +- prune_cancelled_conn_check (stream->conncheck_list); +- +- return trans_found; ++ return FALSE; + } + + /* +@@ -2846,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa + d->server = niceaddr; + + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { + /* case: successful binding discovery, create a new local candidate */ + +@@ -2930,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand + return cand; + } + ++static void priv_handle_turn_alternate_server (NiceAgent *agent, ++ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate) ++{ ++ /* We need to cancel and reset all candidate discovery turn for the same ++ stream and type if there is an alternate server. Otherwise, we might end up ++ with two relay components on different servers, creating candidates with ++ unique foundations that only contain one component. ++ */ ++ GSList *i; ++ ++ for (i = agent->discovery_list; i; i = i->next) { ++ CandidateDiscovery *d = i->data; ++ ++ if (!d->done && ++ d->type == disco->type && ++ d->stream == disco->stream && ++ d->turn->type == disco->turn->type && ++ nice_address_equal (&d->server, &server)) { ++ gchar ip[INET6_ADDRSTRLEN]; ++ // Cancel the pending request to avoid a race condition with another ++ // component responding with another altenrate-server ++ d->stun_message.buffer = NULL; ++ d->stun_message.buffer_len = 0; ++ ++ nice_address_to_string (&server, ip); ++ nice_debug ("Agent %p : Cancelling and setting alternate server %s for " ++ "CandidateDiscovery %p", agent, ip, d); ++ d->server = alternate; ++ d->turn->server = alternate; ++ d->pending = FALSE; ++ agent->discovery_unsched_items++; ++ } ++ } ++} ++ + /* + * Tries to match STUN reply in 'buf' to an existing STUN discovery + * transaction. If found, a reply is sent. +@@ -2982,11 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + agent, d, (int)res); + + if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) { +- /* handle alternate server */ +- nice_address_set_from_sockaddr (&d->server, &alternate.addr); +- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr); ++ NiceAddress addr; + +- d->pending = FALSE; ++ /* handle alternate server */ ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); + } else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS || + res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) { + /* case: successful allocate, create a new local candidate */ +@@ -3103,6 +3800,17 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + recv_realm = (uint8_t *) stun_message_find (resp, + STUN_ATTRIBUTE_REALM, &recv_realm_len); + ++ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 || ++ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && ++ alternatelen != sizeof(alternate)) { ++ NiceAddress addr; ++ ++ nice_address_set_from_sockaddr (&addr, &alternate.addr); ++ ++ if (!nice_address_equal (&addr, &d->server)) { ++ priv_handle_turn_alternate_server (agent, d, d->server, addr); ++ } ++ } + /* check for unauthorized error response */ + if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || + agent->compatibility == NICE_COMPATIBILITY_OC2007 || +@@ -3123,6 +3831,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * + d->stun_resp_msg.buffer = d->stun_resp_buffer; + d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer); + d->pending = FALSE; ++ agent->discovery_unsched_items++; + } else { + /* case: a real unauthorized error */ + d->stun_message.buffer = NULL; +@@ -3619,8 +4328,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + stun_usage_ice_conncheck_use_candidate (&req); + uint32_t priority = stun_usage_ice_conncheck_priority (&req); + +- if (agent->controlling_mode || +- agent->compatibility == NICE_COMPATIBILITY_GOOGLE || ++ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || + agent->compatibility == NICE_COMPATIBILITY_MSN || + agent->compatibility == NICE_COMPATIBILITY_OC2007) + use_candidate = TRUE; +@@ -3628,21 +4336,21 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + if (stream->initial_binding_request_received != TRUE) + agent_signal_initial_binding_request_received (agent, stream); + +- if (component->remote_candidates && remote_candidate == NULL) { ++ if (remote_candidate == NULL) { + nice_debug ("Agent %p : No matching remote candidate for incoming check ->" + "peer-reflexive candidate.", agent); + remote_candidate = discovery_learn_remote_peer_reflexive_candidate ( + agent, stream, component, priority, from, nicesock, + local_candidate, + remote_candidate2 ? remote_candidate2 : remote_candidate); +- if(remote_candidate) { ++ if(remote_candidate && stream->remote_ufrag[0]) { + if (local_candidate && + local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { + CandidateCheckPair *pair; + + pair = priv_conn_check_add_for_candidate_pair_matched (agent, + stream->id, component, local_candidate, remote_candidate, +- NICE_CHECK_DISCOVERED); ++ NICE_CHECK_SUCCEEDED); + if (pair) { + pair->valid = TRUE; + } +@@ -3651,13 +4359,15 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, + } + } + ++ nice_component_add_valid_candidate (component, remote_candidate); ++ + priv_reply_to_conn_check (agent, stream, component, local_candidate, + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); + +- if (component->remote_candidates == NULL) { ++ if (stream->remote_ufrag[0] == 0) { + /* case: We've got a valid binding request to a local candidate +- * but we do not yet know remote credentials nor +- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply ++ * but we do not yet know remote credentials. ++ * As per sect 7.2 of ICE (ID-19), we send a reply + * immediately but postpone all other processing until + * we get information about the remote candidates */ + +diff --git a/agent/conncheck.h b/agent/conncheck.h +index 431c606..e16dc67 100644 +--- a/agent/conncheck.h ++++ b/agent/conncheck.h +@@ -56,7 +56,6 @@ + * @NICE_CHECK_SUCCEEDED: Connection successfully checked. + * @NICE_CHECK_FAILED: No connectivity; retransmissions ceased. + * @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING. +- * @NICE_CHECK_CANCELLED: Check cancelled. + * @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list. + * + * States for checking a candidate pair. +@@ -68,11 +67,19 @@ typedef enum + NICE_CHECK_SUCCEEDED, + NICE_CHECK_FAILED, + NICE_CHECK_FROZEN, +- NICE_CHECK_CANCELLED, + NICE_CHECK_DISCOVERED, + } NiceCheckState; + + typedef struct _CandidateCheckPair CandidateCheckPair; ++typedef struct _StunTransaction StunTransaction; ++ ++struct _StunTransaction ++{ ++ GTimeVal next_tick; /* next tick timestamp */ ++ StunTimer timer; ++ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; ++ StunMessage message; ++}; + + struct _CandidateCheckPair + { +@@ -85,15 +92,15 @@ struct _CandidateCheckPair + gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION]; + NiceCheckState state; + gboolean nominated; +- gboolean controlling; +- gboolean timer_restarted; + gboolean valid; ++ gboolean use_candidate_on_next_check; ++ gboolean mark_nominated_on_response_arrival; ++ gboolean retransmit; /* if the first stun request must be retransmitted */ ++ CandidateCheckPair *discovered_pair; ++ CandidateCheckPair *succeeded_pair; + guint64 priority; + guint32 prflx_priority; +- GTimeVal next_tick; /* next tick timestamp */ +- StunTimer timer; +- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; +- StunMessage stun_message; ++ GSList *stun_transactions; /* a list of ongoing stun requests */ + }; + + int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); +@@ -105,7 +112,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); + void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); + gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); + gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); +-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); ++void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream); + NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); + void + conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, +diff --git a/agent/debug.c b/agent/debug.c +index e1a298c..84fee94 100644 +--- a/agent/debug.c ++++ b/agent/debug.c +@@ -102,7 +102,7 @@ void nice_debug_init (void) + flags |= g_parse_debug_string (gflags_string, gkeys, 4); + if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) + flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; +- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { ++ if (gflags_string && strstr (gflags_string, "libnice-verbose")) { + flags |= NICE_DEBUG_NICE_VERBOSE; + } + +diff --git a/agent/discovery.c b/agent/discovery.c +index 7a890a0..4cc99c2 100644 +--- a/agent/discovery.c ++++ b/agent/discovery.c +@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) + + if (buffer_len > 0) { + if (nice_socket_is_reliable (cand->nicesock)) { +- stun_timer_start_reliable (&cand->timer, +- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); ++ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); + } else { +- stun_timer_start (&cand->timer, 200, +- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); ++ stun_timer_start (&cand->timer, ++ agent->stun_initial_timeout, ++ agent->stun_max_retransmissions); + } + + /* send the conncheck */ +diff --git a/agent/interfaces.c b/agent/interfaces.c +index 0fa2fd7..a81888e 100644 +--- a/agent/interfaces.c ++++ b/agent/interfaces.c +@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback) + nice_debug ("Ignoring loopback interface"); + g_free (addr_string); + } ++#ifdef IGNORED_IFACE_PREFIX ++ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) { ++ nice_debug ("Ignoring interface %s as it matches prefix %s", ++ ifa->ifa_name, IGNORED_IFACE_PREFIX); ++ g_free (addr_string); ++#endif + } else { + if (nice_interfaces_is_private_ip (ifa->ifa_addr)) + ips = add_ip_to_list (ips, addr_string, TRUE); +diff --git a/agent/stream.c b/agent/stream.c +index 8121e12..533ff15 100644 +--- a/agent/stream.c ++++ b/agent/stream.c +@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id) + return NULL; + } + +-/* +- * Returns true if all components of the stream are either +- * 'CONNECTED' or 'READY' (connected plus nominated). +- */ +-gboolean +-nice_stream_all_components_ready (NiceStream *stream) +-{ +- GSList *i; +- +- for (i = stream->components; i; i = i->next) { +- NiceComponent *component = i->data; +- if (component && +- !(component->state == NICE_COMPONENT_STATE_CONNECTED || +- component->state == NICE_COMPONENT_STATE_READY)) +- return FALSE; +- } +- +- return TRUE; +-} +- +- + /* + * Initialized the local crendentials for the stream. + */ +diff --git a/agent/stream.h b/agent/stream.h +index f9188cb..954ba66 100644 +--- a/agent/stream.h ++++ b/agent/stream.h +@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent); + void + nice_stream_close (NiceStream *stream); + +-gboolean +-nice_stream_all_components_ready (NiceStream *stream); +- + NiceComponent * + nice_stream_find_component_by_id (NiceStream *stream, guint id); + +diff --git a/common.mk b/common.mk +index e2ca3f4..b16380d 100644 +--- a/common.mk ++++ b/common.mk +@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice + + + check-valgrind: +- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check ++ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check ++ ++LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver + + .PHONY: check-valgrind +diff --git a/configure.ac b/configure.ac +index b39bfe3..16988ad 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -57,6 +57,7 @@ AC_PROG_CC + AM_PROG_AR + LT_PREREQ([2.2.6]) + LT_INIT([dlopen win32-dll disable-static]) ++AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) + + # Check Operating System + AC_MSG_CHECKING([operating system]) +@@ -153,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \ + ]) + AS_IF([test "$enable_compile_warnings" = "maximum" -o \ + "$enable_compile_warnings" = "error"],[ +- NICE_ADD_FLAG([-Wswitch-enum]) + NICE_ADD_FLAG([-Wswitch-default]) + NICE_ADD_FLAG([-Waggregate-return]) + ]) +@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false) + # GObject introspection + GOBJECT_INTROSPECTION_CHECK([1.30.0]) + ++dnl Ignore a specific network interface name prefix from the connection check ++AC_MSG_CHECKING([whether to ignore a specific network interface name prefix]) ++AC_ARG_WITH([ignored-network-interface-prefix], ++ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string], ++ [Ignore network interfaces whose name starts with "string" from the ICE connection ++ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge ++ handled by libvirtd, do not help in finding connectivity.])], ++ [interface_prefix="$withval"]) ++AS_IF([test -n "$interface_prefix"], ++ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"], ++ [Ignore this network interface prefix from the connection check]) ++ AC_MSG_RESULT([yes, $interface_prefix])], ++ [AC_MSG_RESULT([no])]) ++ + AC_CONFIG_MACRO_DIR(m4) + + AC_OUTPUT +diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml +index 53487bc..391be1a 100644 +--- a/docs/reference/libnice/libnice-docs.xml ++++ b/docs/reference/libnice/libnice-docs.xml +@@ -105,6 +105,10 @@ + <title>Index of new symbols in 0.1.14</title> + <xi:include href="xml/api-index-0.1.14.xml"><xi:fallback/></xi:include> + </index> ++ <index role="0.1.15"> ++ <title>Index of new symbols in 0.1.15</title> ++ <xi:include href="xml/api-index-0.1.15.xml"><xi:fallback/></xi:include> ++ </index> + <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + </part> + </book> +diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt +index d377257..a481106 100644 +--- a/docs/reference/libnice/libnice-sections.txt ++++ b/docs/reference/libnice/libnice-sections.txt +@@ -5,6 +5,7 @@ NiceAgent + NiceComponentState + NiceComponentType + NiceProxyType ++NiceNominationMode + NiceCompatibility + NiceAgentRecvFunc + NiceInputMessage +@@ -12,6 +13,7 @@ NiceOutputMessage + NICE_AGENT_MAX_REMOTE_CANDIDATES + nice_agent_new + nice_agent_new_reliable ++nice_agent_new_full + nice_agent_add_local_address + nice_agent_set_port_range + nice_agent_add_stream +@@ -76,6 +78,7 @@ NICE_CANDIDATE_MAX_FOUNDATION + nice_candidate_new + nice_candidate_free + nice_candidate_copy ++nice_candidate_equal_target + <SUBSECTION Standard> + NICE_TYPE_CANDIDATE + nice_candidate_get_type +diff --git a/examples/Makefile.am b/examples/Makefile.am +index 1e7decf..9c80854 100644 +--- a/examples/Makefile.am ++++ b/examples/Makefile.am +@@ -18,7 +18,7 @@ AM_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(GUPNP_CFLAGS) + +-bin_PROGRAMS = simple-example threaded-example sdp-example ++noinst_PROGRAMS = simple-example threaded-example sdp-example + + simple_example_SOURCES = simple-example.c + simple_example_LDADD = $(top_builddir)/agent/libagent.la \ +diff --git a/nice/libnice.sym b/nice/libnice.sym +index b04bb95..1e522ad 100644 +--- a/nice/libnice.sym ++++ b/nice/libnice.sym +@@ -58,6 +58,7 @@ nice_agent_set_software + nice_agent_set_stream_name + nice_agent_set_stream_tos + nice_candidate_copy ++nice_candidate_equal_target + nice_candidate_free + nice_candidate_new + nice_component_state_to_string +diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver +new file mode 100755 +index 0000000..5b660ee +--- /dev/null ++++ b/scripts/valgrind-test-driver +@@ -0,0 +1,162 @@ ++#! /bin/sh ++# test-driver - basic testsuite driver script. ++ ++scriptversion=2017-04-04.22; # UTC ++ ++# Copyright (C) 2011-2014 Free Software Foundation, Inc. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2, or (at your option) ++# any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <
http://www.gnu.org/licenses/
>. ++ ++# As a special exception to the GNU General Public License, if you ++# distribute this file as part of a program that contains a ++# configuration script generated by Autoconf, you may include it under ++# the same distribution terms that you use for the rest of that program. ++ ++# This file is maintained in Automake, please report ++# bugs to <bug-automake(a)gnu.org> or send patches to ++# <automake-patches(a)gnu.org>. ++ ++# Make unconditional expansion of undefined variables an error. This ++# helps a lot in preventing typo-related bugs. ++set -u ++ ++usage_error () ++{ ++ echo "$0: $*" >&2 ++ print_usage >&2 ++ exit 2 ++} ++ ++print_usage () ++{ ++ cat <<END ++Usage: ++ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH ++ [--expect-failure={yes|no}] [--color-tests={yes|no}] ++ [--enable-hard-errors={yes|no}] [--] ++ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] ++The '--test-name', '--log-file' and '--trs-file' options are mandatory. ++END ++} ++ ++test_name= # Used for reporting. ++log_file= # Where to save the output of the test script. ++trs_file= # Where to save the metadata of the test run. ++expect_failure=no ++color_tests=no ++enable_hard_errors=yes ++while test $# -gt 0; do ++ case $1 in ++ --help) print_usage; exit $?;; ++ --version) echo "test-driver $scriptversion"; exit $?;; ++ --test-name) test_name=$2; shift;; ++ --log-file) log_file=$2; shift;; ++ --trs-file) trs_file=$2; shift;; ++ --color-tests) color_tests=$2; shift;; ++ --expect-failure) expect_failure=$2; shift;; ++ --enable-hard-errors) enable_hard_errors=$2; shift;; ++ --) shift; break;; ++ -*) usage_error "invalid option: '$1'";; ++ *) break;; ++ esac ++ shift ++done ++ ++missing_opts= ++test x"$test_name" = x && missing_opts="$missing_opts --test-name" ++test x"$log_file" = x && missing_opts="$missing_opts --log-file" ++test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" ++if test x"$missing_opts" != x; then ++ usage_error "the following mandatory options are missing:$missing_opts" ++fi ++ ++if test $# -eq 0; then ++ usage_error "missing argument" ++fi ++ ++if test $color_tests = yes; then ++ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. ++ red='[0;31m' # Red. ++ grn='[0;32m' # Green. ++ lgn='[1;32m' # Light green. ++ blu='[1;34m' # Blue. ++ mgn='[0;35m' # Magenta. ++ std='[m' # No color. ++else ++ red= grn= lgn= blu= mgn= std= ++fi ++ ++do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' ++trap "st=129; $do_exit" 1 ++trap "st=130; $do_exit" 2 ++trap "st=141; $do_exit" 13 ++trap "st=143; $do_exit" 15 ++ ++# Test script is run here. ++top_srcdir="`dirname $0`/.." ++tests_dir="${top_srcdir}/tests" ++ ++USE_VALGRIND="`printenv USE_VALGRIND`" ++ ++if test "x${USE_VALGRIND}" = "x1"; then ++ ${top_srcdir}/libtool --mode=execute valgrind \ ++ --leak-check=full \ ++ --show-reachable=no \ ++ --error-exitcode=1 \ ++ --suppressions=$tests_dir/libnice.supp \ ++ --num-callers=30 "$@" >$log_file 2>&1 ++else ++ "$@" >$log_file 2>&1 ++fi ++estatus=$? ++ ++if test $enable_hard_errors = no && test $estatus -eq 99; then ++ tweaked_estatus=1 ++else ++ tweaked_estatus=$estatus ++fi ++ ++case $tweaked_estatus:$expect_failure in ++ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; ++ 0:*) col=$grn res=PASS recheck=no gcopy=no;; ++ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; ++ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; ++ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; ++ *:*) col=$red res=FAIL recheck=yes gcopy=yes;; ++esac ++ ++# Report the test outcome and exit status in the logs, so that one can ++# know whether the test passed or failed simply by looking at the '.log' ++# file, without the need of also peaking into the corresponding '.trs' ++# file (automake bug#11814). ++echo "$res $test_name (exit status: $estatus)" >>$log_file ++ ++# Report outcome to console. ++echo "${col}${res}${std}: $test_name" ++ ++# Register the test result, and other relevant metadata. ++echo ":test-result: $res" > $trs_file ++echo ":global-test-result: $res" >> $trs_file ++echo ":recheck: $recheck" >> $trs_file ++echo ":copy-in-global-log: $gcopy" >> $trs_file ++ ++# Local Variables: ++# mode: shell-script ++# sh-indentation: 2 ++# eval: (add-hook 'write-file-hooks 'time-stamp) ++# time-stamp-start: "scriptversion=" ++# time-stamp-format: "%:y-%02m-%02d.%02H" ++# time-stamp-time-zone: "UTC" ++# time-stamp-end: "; # UTC" ++# End: +diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh +deleted file mode 100755 +index 2864b6f..0000000 +--- a/scripts/valgrind.sh ++++ /dev/null +@@ -1,28 +0,0 @@ +-#!/bin/sh +- +-export G_SLICE=always-malloc +-export G_DEBUG=gc-friendly +- +-tests_dir="`dirname $0`/../tests" +- +-report=`libtool --mode=execute valgrind \ +- --leak-check=full \ +- --show-reachable=no \ +- --error-exitcode=1 \ +- --suppressions=$tests_dir/libnice.supp \ +- --num-callers=30 \ +- $1 2>&1` +- +-#if echo "$report" | grep -q ==; then +-if test $? != 0; then +- echo "$report" +- exit 1 +-fi +- +-if echo "$report" | grep -q "definitely lost"; then +- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then +- echo "$report" +- exit 1 +- fi +-fi +- +diff --git a/socket/udp-turn.c b/socket/udp-turn.c +index cc3409b..190a9ea 100644 +--- a/socket/udp-turn.c ++++ b/socket/udp-turn.c +@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data) + + NiceSocket * + nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, +- NiceSocket *base_socket, NiceAddress *server_addr, +- gchar *username, gchar *password, ++ NiceSocket *base_socket, const NiceAddress *server_addr, ++ const gchar *username, const gchar *password, + NiceTurnSocketCompatibility compatibility) + { + UdpTurnPriv *priv; +@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock, + + /* Split up the monolithic buffer again into the caller-provided buffers. */ + if (parsed_buffer_length > 0 && allocated_buffer) { +- parsed_buffer_length = +- memcpy_buffer_to_input_message (message, buffer, +- parsed_buffer_length); ++ memcpy_buffer_to_input_message (message, buffer, ++ parsed_buffer_length); + } + + if (allocated_buffer) +@@ -1185,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc + gsize + nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + NiceAddress *from, gsize len, guint8 *buf, +- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len) ++ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len) + { + + UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv; +@@ -1195,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + ChannelBinding *binding = NULL; + + union { +- guint8 *u8; +- guint16 *u16; ++ const guint8 *u8; ++ const guint16 *u16; + } recv_buf; + + /* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN) +diff --git a/socket/udp-turn.h b/socket/udp-turn.h +index b1eeeb4..df10a1c 100644 +--- a/socket/udp-turn.h ++++ b/socket/udp-turn.h +@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc + gsize + nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, + NiceAddress *from, gsize len, guint8 *buf, +- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len); ++ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len); + + gboolean + nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer); + + NiceSocket * + nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, +- NiceSocket *base_socket, NiceAddress *server_addr, +- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility); ++ NiceSocket *base_socket, const NiceAddress *server_addr, ++ const gchar *username, const gchar *password, ++ NiceTurnSocketCompatibility compatibility); + + void + nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); +diff --git a/stun/stunmessage.c b/stun/stunmessage.c +index e8184c4..4cc3392 100644 +--- a/stun/stunmessage.c ++++ b/stun/stunmessage.c +@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type, + /* Only fingerprint may come after M-I */ + if (type == STUN_ATTRIBUTE_FINGERPRINT) + break; ++ return NULL; + + case STUN_ATTRIBUTE_FINGERPRINT: + /* Nothing may come after FPR */ +diff --git a/stun/usages/bind.c b/stun/usages/bind.c +index 8dd7afc..ee600a0 100644 +--- a/stun/usages/bind.c ++++ b/stun/usages/bind.c +@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + size_t len; + StunUsageTransReturn ret; + int val; +- struct sockaddr_storage alternate_server; ++ struct sockaddr_storage alternate_server = { AF_UNSPEC } ; + socklen_t alternate_server_len = sizeof (alternate_server); + StunUsageBindReturn bind_ret; + +@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen); + if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { + stun_debug ("STUN transaction failed: couldn't create transport."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) { + stun_debug ("STUN transaction failed: couldn't send request."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, +@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + switch (stun_timer_refresh (&timer)) { + case STUN_USAGE_TIMER_RETURN_TIMEOUT: + stun_debug ("STUN transaction failed: time out."); +- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! ++ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error! ++ goto done; + case STUN_USAGE_TIMER_RETURN_RETRANSMIT: + stun_debug ("STUN transaction retransmitted (timeout %dms).", + stun_timer_remainder (&timer)); + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) { + stun_debug ("STUN transaction failed: couldn't resend request."); +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + continue; + case STUN_USAGE_TIMER_RETURN_SUCCESS: +@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + + valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL); + if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE) +- return STUN_USAGE_BIND_RETURN_ERROR; ++ { ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; ++ } + + if (valid != STUN_VALIDATION_SUCCESS) { + ret = STUN_USAGE_TRANS_RETURN_RETRY; +@@ -548,16 +555,22 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) { + stun_trans_deinit (&trans); + ++ assert (alternate_server.ss_family != AF_UNSPEC); ++ + ret = stun_trans_create (&trans, SOCK_DGRAM, 0, + (struct sockaddr *) &alternate_server, alternate_server_len); + + if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) { +- return STUN_USAGE_BIND_RETURN_ERROR; ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; + } + + val = stun_trans_send (&trans, req_buf, len); + if (val < -1) +- return STUN_USAGE_BIND_RETURN_ERROR; ++ { ++ bind_ret = STUN_USAGE_BIND_RETURN_ERROR; ++ goto done; ++ } + + stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT, + STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); +@@ -571,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv, + } + while (ret == STUN_USAGE_TRANS_RETURN_RETRY); + ++done: ++ if (trans.fd != -1) ++ stun_trans_deinit (&trans); ++ + return bind_ret; + } +diff --git a/stun/usages/timer.c b/stun/usages/timer.c +index 2862ab8..5370cba 100644 +--- a/stun/usages/timer.c ++++ b/stun/usages/timer.c +@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer) + if (timer->retransmissions >= timer->max_retransmissions) + return STUN_USAGE_TIMER_RETURN_TIMEOUT; + +- add_delay (&timer->deadline, timer->delay *= 2); ++ if (timer->retransmissions == timer->max_retransmissions - 1) ++ timer->delay = timer->delay / 2; ++ else ++ timer->delay = timer->delay * 2; ++ add_delay (&timer->deadline, timer->delay); + timer->retransmissions++; + return STUN_USAGE_TIMER_RETURN_RETRANSMIT; + } +diff --git a/stun/usages/timer.h b/stun/usages/timer.h +index e74353b..097e75b 100644 +--- a/stun/usages/timer.h ++++ b/stun/usages/timer.h +@@ -132,7 +132,11 @@ struct stun_timer_s { + * The default intial timeout to use for the timer + * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most + * cases as it is also what is used by SIP style VoIP when sending A-Law and +- * mu-Law audio, so 200ms should be hyper safe. ++ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout ++ * of 200ms, a default of 7 transmissions, the last timeout will be ++ * 16 * 200ms, and we expect to receive a response from the stun server ++ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial ++ * stun request has been sent. + */ + #define STUN_TIMER_DEFAULT_TIMEOUT 200 + +diff --git a/stun/usages/turn.c b/stun/usages/turn.c +index 3b94959..ec12642 100644 +--- a/stun/usages/turn.c ++++ b/stun/usages/turn.c +@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, + stun_debug (" STUN error message received (code: %d)", code); + + /* ALTERNATE-SERVER mechanism */ ++ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 && ++ alternate_server && alternate_server_len && ++ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER, ++ alternate_server, ++ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) { ++ stun_debug ("Found alternate server"); ++ /* The ALTERNATE_SERVER will always be returned by the MS turn server. ++ * We need to check if the ALTERNATE_SERVER is the same as the current ++ * server to decide whether we need to switch servers or not. ++ */ ++ } + if ((code / 100) == 3) { + if (alternate_server && alternate_server_len) { + if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, +diff --git a/stun/usages/turn.h b/stun/usages/turn.h +index 7a2d4e6..83fa00a 100644 +--- a/stun/usages/turn.h ++++ b/stun/usages/turn.h +@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, + * Allocate request, in case the currently used TURN server is requesting the use + * of an alternate server. This argument will only be filled if the return value + * of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER ++ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the ++ * @alternate_server could be filled at any time, and should only be considered ++ * if the request was sent to a different server than the address returned ++ * in the @alternate_server field + * @alternate_server_len: The length of @alternate_server + * @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us + * @lifetime: A pointer to fill with the lifetime of the allocation +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 7bfe075..30d6f8e 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" + + AM_TESTS_ENVIRONMENT = \ + G_MESSAGES_DEBUG=all \ +- NICE_DEBUG=all; ++ NICE_DEBUG=all + + COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) + +@@ -45,8 +45,8 @@ check_PROGRAMS = \ + test-send-recv \ + test-socket-is-based-on \ + test-priority \ +- test-mainloop \ + test-fullmode \ ++ test-different-number-streams \ + test-restart \ + test-fallback \ + test-thread \ +@@ -55,7 +55,9 @@ check_PROGRAMS = \ + test-tcp \ + test-icetcp \ + test-credentials \ +- test-turn ++ test-turn \ ++ test-drop-invalid \ ++ test-nomination + + dist_check_SCRIPTS = \ + check-test-fullmode-with-stun.sh \ +@@ -113,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD) + + test_fullmode_LDADD = $(COMMON_LDADD) + ++test_different_number_streams_LDADD = $(COMMON_LDADD) ++ + test_restart_LDADD = $(COMMON_LDADD) + + test_fallback_LDADD = $(COMMON_LDADD) +@@ -129,6 +133,10 @@ test_credentials_LDADD = $(COMMON_LDADD) + + test_turn_LDADD = $(COMMON_LDADD) + ++test_drop_invalid_LDADD = $(COMMON_LDADD) ++ ++test_nomination_LDADD = $(COMMON_LDADD) ++ + test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) + test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) + +diff --git a/tests/test-credentials.c b/tests/test-credentials.c +index 1de4e49..f1ea89d 100644 +--- a/tests/test-credentials.c ++++ b/tests/test-credentials.c +@@ -184,6 +184,8 @@ int main (void) + nice_agent_get_local_credentials (lagent, 1, &ufrag, &password); + g_assert (g_strcmp0("unicorns", ufrag) == 0); + g_assert (g_strcmp0("awesome", password) == 0); ++ g_free (ufrag); ++ g_free (password); + + nice_agent_gather_candidates (lagent, 1); + nice_agent_gather_candidates (ragent, 1); +diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c +new file mode 100644 +index 0000000..7fd4763 +--- /dev/null ++++ b/tests/test-different-number-streams.c +@@ -0,0 +1,208 @@ ++#ifdef HAVE_CONFIG_H ++# include <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#define ADD_2_STREAMS TRUE ++#define USE_SECOND_STREAM TRUE ++ ++static GMainLoop *global_mainloop = NULL; ++ ++static guint global_components_ready = 0; ++static guint global_components_ready_exit = 0; ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id); ++} ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)", ++ agent, stream_id, component_id, nice_component_state_to_string (state)); ++ ++ if (state == NICE_COMPONENT_STATE_READY) { ++ global_components_ready++; ++ } ++ ++ /* signal status via a global variable */ ++ if (global_components_ready == global_components_ready_exit) { ++ g_debug ("Components ready/failed achieved. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ } ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL, *i; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id); ++} ++ ++int main (void) ++{ ++ NiceAgent *lagent, *ragent; ++ guint timer_id; ++ guint ls_id, rs_id_1, rs_id_2; ++ gchar *lufrag = NULL, *lpassword = NULL; ++ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL; ++ ++#ifdef G_OS_WIN32 ++ WSADATA w; ++ ++ WSAStartup(0x0202, &w); ++#endif ++ ++ global_mainloop = g_main_loop_new (NULL, FALSE); ++ ++ /* step: create the agents L and R */ ++ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_GOOGLE); ++ g_debug ("lagent: %p", lagent); ++ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent"); ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), NULL); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), NULL); ++ ++ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_GOOGLE); ++ g_debug ("ragent: %p", ragent); ++ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent"); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), NULL); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), NULL); ++ ++ /* step: add a timer to catch state changes triggered by signals */ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ ls_id = nice_agent_add_stream (lagent, 2); ++ g_assert (ls_id > 0); ++ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword); ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ ++ global_components_ready_exit = 4; ++ ++ if (ADD_2_STREAMS) { ++ rs_id_1 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_1 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); ++ ++ rs_id_2 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_2 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2); ++ ++ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword); ++ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2); ++ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); ++ ++ if (USE_SECOND_STREAM) { ++ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP); ++ } else { ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); ++ } ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ } else { ++ rs_id_1 = nice_agent_add_stream (ragent, 2); ++ g_assert (rs_id_1 > 0); ++ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1); ++ ++ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword); ++ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1); ++ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE); ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL); ++ ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP); ++ } ++ ++ /* step: run the mainloop until connectivity checks succeed ++ * (see timer_cb() above) */ ++ g_main_loop_run (global_mainloop); ++ ++ g_free (lufrag); ++ g_free (lpassword); ++ g_free (rufrag1); ++ g_free (rpassword1); ++ g_free (rufrag2); ++ g_free (rpassword2); ++ g_object_unref (lagent); ++ g_object_unref (ragent); ++ ++ g_main_loop_unref (global_mainloop); ++ global_mainloop = NULL; ++ ++ g_source_remove (timer_id); ++ ++#ifdef G_OS_WIN32 ++ WSACleanup(); ++#endif ++ ++ return 0; ++} +diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c +new file mode 100644 +index 0000000..97e3586 +--- /dev/null ++++ b/tests/test-drop-invalid.c +@@ -0,0 +1,517 @@ ++/* ++ * This file is part of the Nice GLib ICE library. ++ * ++ * Unit test for ICE full-mode related features. ++ * ++ * (C) 2007 Nokia Corporation. All rights reserved. ++ * Contact: Kai Vehmanen ++ * (C) 2017 Collabora Ltd ++ * Contact: Olivier Crete <olivier.crete(a)collabora.com> ++ * ++ * The contents of this file are subject to the Mozilla Public License Version ++ * 1.1 (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ *
http://www.mozilla.org/MPL/
++ * ++ * Software distributed under the License is distributed on an "AS IS" basis, ++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ++ * for the specific language governing rights and limitations under the ++ * License. ++ * ++ * The Original Code is the Nice GLib ICE library. ++ * ++ * The Initial Developers of the Original Code are Collabora Ltd and Nokia ++ * Corporation. All Rights Reserved. ++ * ++ * Contributors: ++ * Kai Vehmanen, Nokia ++ * ++ * Alternatively, the contents of this file may be used under the terms of the ++ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which ++ * case the provisions of LGPL are applicable instead of those above. If you ++ * wish to allow use of your version of this file only under the terms of the ++ * LGPL and not to allow others to use your version of this file under the ++ * MPL, indicate your decision by deleting the provisions above and replace ++ * them with the notice and other provisions required by the LGPL. If you do ++ * not delete the provisions above, a recipient may use your version of this ++ * file under either the MPL or the LGPL. ++ */ ++#ifdef HAVE_CONFIG_H ++# include <config.h> ++#endif ++ ++#include "agent.h" ++ ++#include "socket/socket.h" ++ ++#include <stdlib.h> ++#include <string.h> ++ ++ ++ ++static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static guint global_components_ready = 0; ++static guint global_components_ready_exit = 0; ++static guint global_components_failed = 0; ++static guint global_components_failed_exit = 0; ++static GMainLoop *global_mainloop = NULL; ++static gboolean global_lagent_gathering_done = FALSE; ++static gboolean global_ragent_gathering_done = FALSE; ++static gboolean global_lagent_ibr_received = FALSE; ++static gboolean global_ragent_ibr_received = FALSE; ++static int global_lagent_cands = 0; ++static int global_ragent_cands = 0; ++static gint global_ragent_read = 0; ++static guint global_exit_when_ibr_received = 0; ++ ++static void priv_print_global_status (void) ++{ ++ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done); ++ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]); ++ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]); ++ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands); ++} ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)buf; ++ ++ /* Core of the test ++ * Assert on any unreleated packet received. This would include anything ++ * send before the negotiation is over. ++ */ ++ g_assert (len == 16); ++ g_assert (strncmp ("1234567812345678", buf, 16) == 0); ++ ++ if (component_id == 2) ++ return; ++ ++ if (GPOINTER_TO_UINT (user_data) == 2) { ++ g_debug ("right agent received %d bytes, stopping mainloop", len); ++ global_ragent_read = len; ++ g_main_loop_quit (global_mainloop); ++ } ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_gathering_done = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_gathering_done = TRUE; ++ ++ if (global_lagent_gathering_done && ++ global_ragent_gathering_done) ++ g_main_loop_quit (global_mainloop); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; ++} ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ gboolean ready_to_connected = FALSE; ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) { ++ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_lagent_state[component_id - 1] = state; ++ } else if (GPOINTER_TO_UINT (data) == 2) { ++ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_ragent_state[component_id - 1] = state; ++ } ++ ++ if (state == NICE_COMPONENT_STATE_READY) ++ global_components_ready++; ++ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) ++ global_components_ready--; ++ if (state == NICE_COMPONENT_STATE_FAILED) ++ global_components_failed++; ++ ++ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit); ++ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit); ++ ++ /* signal status via a global variable */ ++ if (global_components_ready == global_components_ready_exit && ++ global_components_failed == global_components_failed_exit) { ++ g_debug ("Components ready/failed achieved. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ return; ++ } ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; (void)component_id; ++} ++ ++static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, ++ gchar *lfoundation, gchar* rfoundation, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ ++global_lagent_cands; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ ++global_ragent_cands; ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; ++} ++ ++static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, ++ gchar *foundation, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation; ++} ++ ++static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_ibr_received = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_ibr_received = TRUE; ++ ++ if (global_exit_when_ibr_received) { ++ g_debug ("Received initial binding request. Stopping mailoop"); ++ g_main_loop_quit (global_mainloop); ++ } ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)data; ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL; ++ GSList *peer_cands = NULL; ++ GSList *item1, *item2; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ peer_cands = nice_agent_get_local_candidates (to, to_stream, component); ++ ++ /* ++ * Core of the test: ++ * ++ * Send packets that shoudl be dropped. ++ */ ++ ++ for (item1 = cands; item1; item1 = item1->next) { ++ NiceCandidate *cand = item1->data; ++ NiceSocket *nicesock = cand->sockptr; ++ ++ g_assert (nicesock); ++ ++ for (item2 = peer_cands; item2; item2 = item2->next) { ++ NiceCandidate *target_cand = item2->data; ++ ++ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB"); ++ } ++ ++ } ++ ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free); ++ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free); ++} ++ ++static void set_credentials (NiceAgent *lagent, guint lstream, ++ NiceAgent *ragent, guint rstream) ++{ ++ gchar *ufrag = NULL, *password = NULL; ++ ++ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++} ++ ++static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed) ++{ ++ guint ls_id, rs_id; ++ gint ret; ++ ++ /* XXX: dear compiler, this is for you */ ++ (void)baseaddr; ++ ++ /* step: initialize variables modified by the callbacks */ ++ global_components_ready = 0; ++ global_components_ready_exit = ready; ++ global_components_failed = 0; ++ global_components_failed_exit = failed; ++ global_lagent_gathering_done = FALSE; ++ global_ragent_gathering_done = FALSE; ++ global_lagent_ibr_received = ++ global_ragent_ibr_received = FALSE; ++ global_lagent_cands = ++ global_ragent_cands = 0; ++ ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ ++ /* step: add one stream, with RTP+RTCP components, to each agent */ ++ ls_id = nice_agent_add_stream (lagent, 2); ++ ++ rs_id = nice_agent_add_stream (ragent, 2); ++ g_assert (ls_id > 0); ++ g_assert (rs_id > 0); ++ ++ /* Gather candidates and test nice_agent_set_port_range */ ++ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000); ++ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001); ++ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345); ++ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001); ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE); ++ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL); ++ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL); ++ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); ++ ++ { ++ GSList *cands = NULL, *i; ++ NiceCandidate *cand = NULL; ++ ++ cands = nice_agent_get_local_candidates (lagent, ls_id, 1); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10000); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (lagent, ls_id, 2); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10001); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (ragent, rs_id, 1); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 12345); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ cands = nice_agent_get_local_candidates (ragent, rs_id, 2); ++ g_assert (g_slist_length (cands) == 1); ++ cand = cands->data; ++ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST); ++ g_assert (nice_address_get_port (&cand->addr) == 10002); ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++ ++ } ++ ++ /* step: attach to mainloop (needed to register the fds) */ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (2)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, ++ g_main_loop_get_context (global_mainloop), cb_nice_recv, ++ GUINT_TO_POINTER (2)); ++ ++ /* step: run mainloop until local candidates are ready ++ * (see timer_cb() above) */ ++ if (global_lagent_gathering_done != TRUE || ++ global_ragent_gathering_done != TRUE) { ++ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'..."); ++ g_main_loop_run (global_mainloop); ++ g_assert (global_lagent_gathering_done == TRUE); ++ g_assert (global_ragent_gathering_done == TRUE); ++ } ++ ++ set_credentials (lagent, ls_id, ragent, rs_id); ++ ++ /* step: pass the remote candidates to agents */ ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP); ++ ++ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed..."); ++ ++ /* step: run the mainloop until connectivity checks succeed ++ * (see timer_cb() above) */ ++ g_main_loop_run (global_mainloop); ++ ++ /* note: verify that STUN binding requests were sent */ ++ g_assert (global_lagent_ibr_received == TRUE); ++ g_assert (global_ragent_ibr_received == TRUE); ++ ++ /* note: Send a packet from another address */ ++ /* These should also be ignored */ ++ { ++ NiceCandidate *local_cand = NULL; ++ NiceCandidate *remote_cand = NULL; ++ NiceSocket *tmpsock; ++ ++ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand, ++ &remote_cand)); ++ g_assert (local_cand); ++ g_assert (remote_cand); ++ ++ tmpsock = nice_udp_bsd_socket_new (NULL); ++ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD"); ++ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE"); ++ nice_socket_free (tmpsock); ++ } ++ ++ /* note: test payload send and receive */ ++ global_ragent_read = 0; ++ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678"); ++ g_assert (ret != -1); ++ g_debug ("Sent %d bytes", ret); ++ g_assert (ret == 16); ++ while (global_ragent_read != 16) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_ragent_read == 16); ++ ++ g_debug ("test-drop-invalid: Ran mainloop, removing streams..."); ++ ++ /* step: clean up resources and exit */ ++ ++ nice_agent_remove_stream (lagent, ls_id); ++ nice_agent_remove_stream (ragent, rs_id); ++ ++ return 0; ++} ++ ++int main (void) ++{ ++ NiceAgent *lagent, *ragent; /* agent's L and R */ ++ NiceAddress baseaddr; ++ int result; ++ guint timer_id; ++ ++#ifdef G_OS_WIN32 ++ WSADATA w; ++ ++ WSAStartup(0x0202, &w); ++#endif ++ ++ global_mainloop = g_main_loop_new (NULL, FALSE); ++ ++ /* step: create the agents L and R */ ++ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_RFC5245); ++ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), ++ NICE_COMPATIBILITY_RFC5245); ++ ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ ++ ++ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent"); ++ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent"); ++ ++ /* step: add a timer to catch state changes triggered by signals */ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ /* step: specify which local interface to use */ ++ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1")) ++ g_assert_not_reached (); ++ nice_agent_add_local_address (lagent, &baseaddr); ++ nice_agent_add_local_address (ragent, &baseaddr); ++ ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-candidate", ++ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "new-candidate", ++ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received", ++ G_CALLBACK (cb_initial_binding_request_received), ++ GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received", ++ G_CALLBACK (cb_initial_binding_request_received), ++ GUINT_TO_POINTER (2)); ++ ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ ++ ++ /* step: run test the first time */ ++ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time"); ++ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0); ++ priv_print_global_status (); ++ g_assert (result == 0); ++ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY); ++ /* When using TURN, we get peer reflexive candidates for the host cands ++ that we removed so we can get another new_selected_pair signal later ++ depending on timing/racing, we could double (or not) the amount we expected ++ */ ++ ++ /* note: verify that correct number of local candidates were reported */ ++ g_assert (global_lagent_cands == 2); ++ g_assert (global_ragent_cands == 2); ++ ++ g_object_unref (lagent); ++ g_object_unref (ragent); ++ ++ g_main_loop_unref (global_mainloop); ++ global_mainloop = NULL; ++ ++ g_source_remove (timer_id); ++#ifdef G_OS_WIN32 ++ WSACleanup(); ++#endif ++ return result; ++} +diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c +index 74d7133..f060efc 100644 +--- a/tests/test-gstreamer.c ++++ b/tests/test-gstreamer.c +@@ -1,7 +1,7 @@ + /* + * This file is part of the Nice GLib ICE library. + * +- * (C) 1015 Kurento. ++ * (C) 2015 Kurento. + * Contact: Jose Antonio Santos Cadenas + * + * The contents of this file are subject to the Mozilla Public License Version +@@ -34,7 +34,7 @@ + */ + + #include <gst/check/gstcheck.h> +-#include <nice/agent.h> ++#include "agent.h" + + #define RTP_HEADER_SIZE 12 + #define RTP_PAYLOAD_SIZE 1024 +diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c +deleted file mode 100644 +index 7c52daa..0000000 +--- a/tests/test-mainloop.c ++++ /dev/null +@@ -1,108 +0,0 @@ +-/* +- * This file is part of the Nice GLib ICE library. +- * +- * (C) 2006, 2007 Collabora Ltd. +- * Contact: Dafydd Harries +- * (C) 2006, 2007 Nokia Corporation. All rights reserved. +- * Contact: Kai Vehmanen +- * +- * The contents of this file are subject to the Mozilla Public License Version +- * 1.1 (the "License"); you may not use this file except in compliance with +- * the License. You may obtain a copy of the License at +- *
http://www.mozilla.org/MPL/
+- * +- * Software distributed under the License is distributed on an "AS IS" basis, +- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +- * for the specific language governing rights and limitations under the +- * License. +- * +- * The Original Code is the Nice GLib ICE library. +- * +- * The Initial Developers of the Original Code are Collabora Ltd and Nokia +- * Corporation. All Rights Reserved. +- * +- * Contributors: +- * Dafydd Harries, Collabora Ltd. +- * Kai Vehmanen, Nokia +- * +- * Alternatively, the contents of this file may be used under the terms of the +- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which +- * case the provisions of LGPL are applicable instead of those above. If you +- * wish to allow use of your version of this file only under the terms of the +- * LGPL and not to allow others to use your version of this file under the +- * MPL, indicate your decision by deleting the provisions above and replace +- * them with the notice and other provisions required by the LGPL. If you do +- * not delete the provisions above, a recipient may use your version of this +- * file under either the MPL or the LGPL. +- */ +-#ifdef HAVE_CONFIG_H +-# include <config.h> +-#endif +- +-#include <string.h> +- +-#include <nice/nice.h> +-#include "socket/socket.h" +- +-static GMainLoop *loop = NULL; +- +-static void +-recv_cb ( +- NiceAgent *agent, +- guint stream_id, +- guint component_id, +- guint len, +- gchar *buf, +- gpointer data) +-{ +- g_assert (agent != NULL); +- g_assert (stream_id == 1); +- g_assert (component_id == 1); +- g_assert (len == 6); +- g_assert (0 == strncmp (buf, "\x80hello", len)); +- g_assert (42 == GPOINTER_TO_UINT (data)); +- g_main_loop_quit (loop); +-} +- +-int +-main (void) +-{ +- NiceAgent *agent; +- NiceAddress addr; +- guint stream; +- +- nice_address_init (&addr); +- +- loop = g_main_loop_new (NULL, FALSE); +- +- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245); +- nice_address_set_ipv4 (&addr, 0x7f000001); +- nice_agent_add_local_address (agent, &addr); +- stream = nice_agent_add_stream (agent, 1); +- nice_agent_gather_candidates (agent, stream); +- +- // attach to default main context +- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP, +- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42)); +- +- { +- NiceCandidate *candidate; +- GSList *candidates, *i; +- +- candidates = nice_agent_get_local_candidates (agent, 1, 1); +- candidate = candidates->data; +- +- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello"); +- for (i = candidates; i; i = i->next) +- nice_candidate_free ((NiceCandidate *) i->data); +- g_slist_free (candidates); +- } +- +- g_main_loop_run (loop); +- +- nice_agent_remove_stream (agent, stream); +- g_object_unref (agent); +- +- return 0; +-} +- +diff --git a/tests/test-nomination.c b/tests/test-nomination.c +new file mode 100644 +index 0000000..bf21557 +--- /dev/null ++++ b/tests/test-nomination.c +@@ -0,0 +1,263 @@ ++#include <stdlib.h> ++#include <stdio.h> ++#include <string.h> ++ ++#include <gio/gio.h> ++#include <agent.h> ++ ++static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; ++static guint global_components_ready = 0; ++static gboolean global_lagent_gathering_done = FALSE; ++static gboolean global_ragent_gathering_done = FALSE; ++static int global_lagent_cands = 0; ++static int global_ragent_cands = 0; ++ ++static gboolean timer_cb (gpointer pointer) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer); ++ ++ /* signal status via a global variable */ ++ ++ /* note: should not be reached, abort */ ++ g_error ("ERROR: test has got stuck, aborting..."); ++ ++ return FALSE; ++} ++ ++static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data); ++ ++ /* XXX: dear compiler, these are for you: */ ++ (void)agent; (void)stream_id; (void)component_id; (void)buf; ++ ++ /* ++ * Lets ignore stun packets that got through ++ */ ++ if (len < 8) ++ return; ++ if (strncmp ("12345678", buf, 8)) ++ return; ++ ++ if (component_id != 1) ++ return; ++} ++ ++static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ global_lagent_gathering_done = TRUE; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ global_ragent_gathering_done = TRUE; ++} ++ ++ ++static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) ++{ ++ gboolean ready_to_connected = FALSE; ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) { ++ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_lagent_state[component_id - 1] = state; ++ } else if (GPOINTER_TO_UINT (data) == 2) { ++ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && ++ state == NICE_COMPONENT_STATE_CONNECTED) ++ ready_to_connected = TRUE; ++ global_ragent_state[component_id - 1] = state; ++ } ++ ++ if (state == NICE_COMPONENT_STATE_READY) ++ global_components_ready++; ++ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) ++ global_components_ready--; ++ g_assert (state != NICE_COMPONENT_STATE_FAILED); ++ ++ g_debug ("test-nomination: checks READY %u.", global_components_ready); ++} ++ ++static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, ++ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) ++{ ++ g_debug ("test-nomination:%s: %p", G_STRFUNC, data); ++ ++ if (GPOINTER_TO_UINT (data) == 1) ++ ++global_lagent_cands; ++ else if (GPOINTER_TO_UINT (data) == 2) ++ ++global_ragent_cands; ++} ++ ++static void set_candidates (NiceAgent *from, guint from_stream, ++ NiceAgent *to, guint to_stream, guint component) ++{ ++ GSList *cands = NULL, *i; ++ ++ cands = nice_agent_get_local_candidates (from, from_stream, component); ++ nice_agent_set_remote_candidates (to, to_stream, component, cands); ++ ++ for (i = cands; i; i = i->next) ++ nice_candidate_free ((NiceCandidate *) i->data); ++ g_slist_free (cands); ++} ++ ++static void set_credentials (NiceAgent *lagent, guint lstream, ++ NiceAgent *ragent, guint rstream) ++{ ++ gchar *ufrag = NULL, *password = NULL; ++ ++ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); ++ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); ++ g_free (ufrag); ++ g_free (password); ++} ++ ++static void ++run_test(NiceNominationMode l_nomination_mode, ++ NiceNominationMode r_nomination_mode) ++{ ++ NiceAgent *lagent, *ragent; /* agent's L and R */ ++ const gchar *localhost; ++ NiceAddress localaddr; ++ guint ls_id, rs_id; ++ gulong timer_id; ++ ++ localhost = "127.0.0.1"; ++ ++ /* step: initialize variables modified by the callbacks */ ++ global_components_ready = 0; ++ global_lagent_gathering_done = FALSE; ++ global_ragent_gathering_done = FALSE; ++ global_lagent_cands = global_ragent_cands = 0; ++ ++ lagent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); ++ ++ ragent = nice_agent_new_full (NULL, ++ NICE_COMPATIBILITY_RFC5245, ++ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ? ++ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0); ++ ++ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL); ++ ++ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); ++ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); ++ nice_agent_set_software (lagent, "Test-nomination, Left Agent"); ++ nice_agent_set_software (ragent, "Test-nomination, Right Agent"); ++ ++ timer_id = g_timeout_add (30000, timer_cb, NULL); ++ ++ if (!nice_address_set_from_string (&localaddr, localhost)) ++ g_assert_not_reached (); ++ nice_agent_add_local_address (lagent, &localaddr); ++ nice_agent_add_local_address (ragent, &localaddr); ++ ++ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", ++ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); ++ g_signal_connect (G_OBJECT (ragent), "component-state-changed", ++ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); ++ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); ++ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", ++ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); ++ ++ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); ++ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); ++ ++ ls_id = nice_agent_add_stream (lagent, 1); ++ rs_id = nice_agent_add_stream (ragent, 1); ++ g_assert (ls_id > 0); ++ g_assert (rs_id > 0); ++ ++ /* Gather candidates and test nice_agent_set_port_range */ ++ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); ++ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); ++ ++ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); ++ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, ++ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); ++ ++ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'..."); ++ while (!global_lagent_gathering_done) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_lagent_gathering_done == TRUE); ++ while (!global_ragent_gathering_done) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_ragent_gathering_done == TRUE); ++ ++ set_credentials (lagent, ls_id, ragent, rs_id); ++ ++ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP); ++ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP); ++ ++ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || ++ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) ++ g_main_context_iteration (NULL, TRUE); ++ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); ++ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); ++ ++ nice_agent_remove_stream (lagent, ls_id); ++ nice_agent_remove_stream (ragent, rs_id); ++ ++ g_source_remove (timer_id); ++ ++ g_clear_object(&lagent); ++ g_clear_object(&ragent); ++} ++ ++static void ++regular (void) ++{ ++ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR); ++} ++ ++static void ++aggressive (void) ++{ ++ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE); ++} ++ ++static void ++mixed_ra (void) ++{ ++ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE); ++} ++ ++static void ++mixed_ar (void) ++{ ++ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int ret; ++ ++ g_test_init (&argc, &argv, NULL); ++ ++ g_test_add_func ("/nice/nomination/regular", regular); ++ g_test_add_func ("/nice/nomination/aggressive", aggressive); ++ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra); ++ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar); ++ ++ ret = g_test_run (); ++ ++ return ret; ++} +diff --git a/tests/test-restart.c b/tests/test-restart.c +index c2cbe9a..afc51b6 100644 +--- a/tests/test-restart.c ++++ b/tests/test-restart.c +@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands); + cdes.addr = laddr_rtcp; + nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands); ++ /* This role switch request will be effective after restart. We test ++ * here that the role cannot be externally modified after conncheck ++ * has started. */ ++ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL); ++ g_assert (ragent->controlling_mode == FALSE); + + g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed..."); + +@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + global_ragent_read = 0; + g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16); + ++ /* Both agent have a distinct role at the end of the conncheck */ ++ g_assert (lagent->controlling_mode == TRUE); ++ g_assert (ragent->controlling_mode == FALSE); + /* step: restart agents, exchange updated credentials */ + tie_breaker = ragent->tie_breaker; + nice_agent_restart (ragent); + g_assert (tie_breaker != ragent->tie_breaker); ++ /* This role switch of ragent should be done now, and both agents ++ * have now the same role, which should generate a role conflict ++ * resolution situation */ ++ g_assert (lagent->controlling_mode == TRUE); ++ g_assert (ragent->controlling_mode == TRUE); + nice_agent_restart (lagent); + { + gchar *ufrag = NULL, *password = NULL; +@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress * + /* note: verify binding requests were resent after restart */ + g_assert (global_lagent_ibr_received == TRUE); + g_assert (global_ragent_ibr_received == TRUE); ++ /* note: verify that a role switch occured for one of the agents */ ++ g_assert (ragent->controlling_mode != lagent->controlling_mode); + + g_debug ("test-restart: Ran mainloop, removing streams..."); + diff --git a/libnice.spec b/libnice.spec index 7c8ffb1..e5944fc 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,14 +3,17 @@ Name: libnice Version: 0.1.14 -Release: 2%{?dist} +Release: 3%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL:
https://nice.freedesktop.org/wiki/
Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
+Patch1: libnice-0.1.14-20171128.patch +BuildRequires: autoconf +BuildRequires: automake BuildRequires: glib2-devel BuildRequires: gnutls-devel >= 2.12.0 BuildRequires: gobject-introspection-devel @@ -52,15 +55,6 @@ Requires: %{name}%{?_isa} = %{version}-%{release} The %{name}-gstreamer1 package contains a gstreamer 1.0 plugin for %{name}. -%package examples -Summary: Simple %{name} usage examples -Group: Development/Libraries -Requires: %{name}%{?_isa} = %{version}-%{release} - -%description examples -The %{name}-examples package contains usage (simple, threaded and sdp) examples. - - %package devel Summary: Development files for %{name} Group: Development/Libraries @@ -75,12 +69,14 @@ developing applications that use %{name}. %prep %setup -q +%patch1 -p1 %check #make check %build +autoreconf -f -i %configure --disable-static sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool @@ -117,12 +113,6 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %{_libdir}/gstreamer-1.0/libgstnice.so -%files examples -%{_bindir}/sdp-example -%{_bindir}/simple-example -%{_bindir}/threaded-example - - %files devel %{_includedir}/* %{_libdir}/*.so @@ -132,6 +122,11 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog +* Fri Feb 09 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-3 +- update to 0.1.14-70-gfb2f1f7 with alternate server fixes for SIPE +- add autoreconf build step +- remove examples subpackage as examples are no longer installed + * Wed Feb 07 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 0.1.14-2 - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
commit b17a790f177dfb96d128af068bb7b391da8162df Author: Kamil Dudka <kdudka(a)redhat.com> Date: Fri Feb 9 16:47:14 2018 +0100 minor tweak of the description ... to make vim's syntax highlighting work properly on libnice.spec diff --git a/libnice.spec b/libnice.spec index 74f0bee..7c8ffb1 100644 --- a/libnice.spec +++ b/libnice.spec @@ -24,7 +24,7 @@ BuildRequires: gupnp-igd-devel >= 0.1.2 %description -%{name} is an implementation of the IETF's draft Interactive Connectivity +%{name} is an implementation of the IETF draft Interactive Connectivity Establishment standard (ICE). ICE is useful for applications that want to establish peer-to-peer UDP data streams. It automates the process of traversing NATs and provides security against some attacks. Existing standards that use commit 69cde2cab6ad89282caf3bd90253ee8dfe01ee7f Author: Fedora Release Engineering <releng(a)fedoraproject.org> Date: Wed Feb 7 22:49:23 2018 +0000 - Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org> diff --git a/libnice.spec b/libnice.spec index f69c824..74f0bee 100644 --- a/libnice.spec +++ b/libnice.spec @@ -3,7 +3,7 @@ Name: libnice Version: 0.1.14 -Release: 1%{?dist} +Release: 2%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries @@ -132,6 +132,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog +* Wed Feb 07 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 0.1.14-2 +- Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+ * Mon Jan 29 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-1 - Update to 0.1.14 commit 3feabe3b2d1f84cb307dff64326af6681d69f183 Author: Stefan Becker <chemobejk(a)gmail.com> Date: Thu May 4 09:40:36 2017 +0300 Update to 0.1.14 diff --git a/libnice-0.1.13-20160610.patch b/libnice-0.1.13-20160610.patch deleted file mode 100644 index a5f2edb..0000000 --- a/libnice-0.1.13-20160610.patch +++ /dev/null @@ -1,10771 +0,0 @@ -commit 30a0c230ae9b70c572060ad3037f68e102e4759a -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Jun 6 18:31:22 2016 -0400 - - conncheck: Remove pairs before freeing candidate - - Remove the whole pair before the candidate is - to be freed. - -
https://phabricator.freedesktop.org/T7460
- -commit 71f7ed3eda829c3dc6afe9ed013c0ab826a1aa40 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri Feb 19 15:01:03 2016 -0500 - - stun timer: Do 7 retransmissions as recommended - - Also reduce the normal timeout to make the test bearable. - - This is what RFC 5389 section 7.2.1 - - Differential Revision:
https://phabricator.freedesktop.org/D1056
- Maniphest Task:
https://phabricator.freedesktop.org/T3339
- -commit dc1e1b7a1b258fb54ba582d2fe77ccd159c9fe88 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Jun 6 16:21:54 2016 -0400 - - timer: Maximum retransmission should include the original one - - We really care about the maximum transmissions, the first one counts. - -commit fad72879fa4a0896c55ac6fc5f77f6c05e369a2b -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri Jun 3 18:42:59 2016 -0400 - - pseudotcp: it's still a GObject - -commit f645ea6b11c167b1d7f4c5034f79664bdb8706d6 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Apr 14 13:32:51 2016 +0200 - - pseudotcp: Make sure duplicate ack representing losses have no data - - If they have data in them, they won't be recognized as duplicate acks by - the sender. - -commit adba0d4a51f4e0deac888ab08f7976cef70a8e99 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Apr 14 09:50:09 2016 +0200 - - pseudotcp: Implement NewReno timestamp heuristic - - This allows the sender to enter fast retransmit after a timeout because - it can now detect that three duplicate acks are caused by a packet loss. - - As specific in RFC 6582 section 4.2. - -commit 1f532aeb6bf5b5b3042c445e677988f3327b1cb5 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Apr 6 10:46:46 2016 +0300 - - pseudotcp: Set min RTO to 1 second - - This is recommended by RFC 6298 - -commit b5952012bc5b403550f8dd9945d92747323acfc4 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Apr 6 01:59:36 2016 +0300 - - pseudotcp: Implement full NewReno - -commit e31932bdf25ce545a88fe6078b9557bc4d9e6365 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Feb 2 16:59:18 2016 -0500 - - pseudotcp: Make debug more useful - -commit 8ccb2c1711a2e5cdebf9411764fd92d5a089ffbf -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Jan 12 20:14:48 2016 -0500 - - pseudotcp: Separate default and maximum MTU - - Accept packets much beyond the default MTU, but - set a reasonable default MTU for sending of 1400 - -commit cb644b2baa681f510a79e158cd50c490dcfa5186 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Dec 24 01:15:59 2015 -0500 - - pseudotcp: close local socket on initial transmission error - - This is required as no retransmissions will happen - -commit 23331ff2add5a60d611eee2093614d1fb8749164 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Sep 17 21:26:36 2015 -0400 - - pseudotcp: Export more symbols for PseudoTCP - -commit 026c15a838554c30ea96a59b08a2064b61d62736 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Sep 17 15:00:27 2015 -0400 - - pseudotcp: Make structs definitions private - -commit 11d4bb9783a69363de80ff49638030ba892a93fe -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Jun 23 15:42:33 2015 +0100 - - pseudotcp: Correct behaviour of buffer size methods when part-closed - - Correct the behaviour of pseudo_tcp_socket_get_available_bytes() and - pseudo_tcp_get_available_send_space() when the socket is not in - TCP_ESTABLISHED state. It’s still permissible to send and receive up - until the local side calls pseudo_tcp_socket_close(), which means we - may be in state TCP_ESTABLISHED *or TCP_CLOSE_WAIT*. - -commit a9a149f529b3165543b52260d40a7855401841da -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Fri Jul 31 14:28:51 2015 +0100 - - pseudotcp: Fix EOS checks in high packet loss situations - - The state tracking previously assumed that if a FIN packet was sent, the - other side received it and the preceding packets, and hence it was - correct to sent an RST if an unexpected packet (such as a delayed - SYN-ACK) was received. - - In cases where there is high packet loss, this won’t work. For example, - peer A sends a SYN, it is received and peer B replies with a SYN-ACK - which is also received; then peer A sends its data and a FIN, which are - both dropped. Since it hasn’t received anything since the original SYN, - peer B resends its SYN-ACK. If that is received, peer A was incorrectly - treating it as an erroneous packet, and would then send a RST. In actual - fact, it should take this as a signal that the data and FIN packets were - dropped, and should resend them. - - TODO: Add unit tests - -commit 4dc2b5d9a01e3314d229fb9aa80884d84c45c1f0 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Fri Jul 31 14:19:30 2015 +0100 - - pseudotcp: Propagate error codes from transmit() to callers - - Otherwise we can’t easily differentiate between different transmission - failures; for example: underlying socket failures, versus retransmission - timeouts. - -commit 8a6bc000a5d7395bd9c4ff9942be26bd4f7d2e44 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Jun 23 15:40:13 2015 +0100 - - pseudotcp: Add more debug info on closing down a pseudo-TCP socket - -commit a72a93e51dba5d239e0607380bb4799cf1b0caca -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Jun 24 14:06:05 2015 +0100 - - pseudotcp: Fix pseudo_tcp_socket_recv() in state TCP_CLOSE_WAIT - - Previously, pseudo_tcp_socket_recv() would start returning 0 (EOS) as - soon as a FIN segment was received from the peer, even if there was - unread data already in the receive buffer. - - Instead, the unread data should all be accessible before - pseudo_tcp_socket_recv() starts returning 0. - -commit 02699917641922c9f1d337e3102f13a1ea1d83c4 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Jun 24 13:52:16 2015 +0100 - - pseudotcp: Fix retransmission of segments before handling a FIN - - Previously, if peer A transmitted one or more data segments (1), - followed by a FIN segment (2) to peer B, and segments 1 were - dropped, peer B would not request retransmission of them and would - instead continue with the FIN handshake. This effectively meant - segments 1 were lost without peer B realising. - - Fix this by only handling the FIN segment once its sequence number is - acknowledged in the receive window. - -commit b58e852de6183f2bda4e7d322a35d18edf5cbbed -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Jun 2 19:22:50 2016 -0400 - - socket: Assert trying to use free'd socket - - Cleanly returnign makes no sense and may hide - worse problems. - -commit baab2c3c7049f984cdca6ed622059c62ce8cebf7 -Author: Misha Uliutin <mishau(a)microsoft.com> -Date: Mon Apr 25 09:59:48 2016 +0300 - - component: Fix set TCP selected remote candidate - -
https://phabricator.freedesktop.org/T7407
- -commit 6329509b86f3a6877a39fb59b7a1b535408db0ce -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Jun 2 19:00:17 2016 -0400 - - agent: Parse TURN packet on the right socket - -
https://phabricator.freedesktop.org/T99
- -commit 2f0daa030a69ebb2dea4c1a6fc47699d0f6828aa -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Jun 2 17:34:27 2016 -0400 - - tests: Add TURN test - - This test depends on rfc5766-turn-server which must - be installed for this test to run. - -commit 75d332cc4b7d9ee76bdf92b38f9cc3f6dd94b796 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Tue May 31 11:42:44 2016 +0000 - - conncheck: mark discovered pairs with TCP passive as valid - - Doing so similarly to priv_process_response_check_for_reflexive(), - which also sets valid flag on discovered peer reflexive pairs. - - Fixes a regression in previously working scenario. - Differential Revision:
https://phabricator.freedesktop.org/D1035
- -commit 1a23476513d487bb09afbc7fb4853169399312d7 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Jun 1 08:52:41 2016 +0000 - - test-icetcp: don't be sensitive to the signal order - - "new-selected-pair" may be emitted after "component-state-changed" - to READY, by which time the main loop might have gotten quit in - cb_component_state_changed(). Consequently, cb_new_selected_pair() could - miss to register the selected pair, ultimately leading to an assertion - failure in main(). - - We should wait for both selected pair and state change events to occur - before stopping the main loop. - - Differential Revision:
https://phabricator.freedesktop.org/D1044
- -commit b559384734deb9ec934f5ff69814f3d90c6a36c1 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue May 31 17:31:18 2016 -0400 - - Revert "WIP" - - This reverts commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a. - -commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue May 31 17:31:12 2016 -0400 - - WIP - -commit 955323915c43b1a066399e61d0ab091f0b1d112b -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Tue May 31 09:27:03 2016 +0000 - - conncheck: fix pruning conn checks with TCP active sockets - - TCP active socket makes a NiceSocket for each peer in conn_check_send() - and this new socket is then stored as CandidateCheckPair's 'sockptr'. - We thus have to look also at the 'sockptr' value when eliminating - sockets which have received HUP from connection checks. - Differential Revision:
https://phabricator.freedesktop.org/D1034
- -commit 2112ebba886d15fd96cc36b9fe3196834acaa892 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Mar 8 15:37:05 2016 -0500 - - agent: Remove socket on read error - - If a socket returned an error, remove it. - -commit 1949b89f3de6e45616187e86f542d26a003ea7a6 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri Jan 15 22:40:27 2016 -0500 - - component: Add API to cleanly remove a base socket - -commit 93330f1f97e4f2a9ff09b602765e620cb279574b -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Sat Feb 27 03:35:27 2016 -0500 - - agent: Fix udp-turn-over-tcp - - The TCP-based turns don't come pre-parsed unlike - the UDP variants! - -commit 7f6ddac880ee07530ae59f4c64a0a17417fb9e24 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Jan 27 18:56:13 2016 -0500 - - agent: Add force-relay property to force messages through the relay - - This allows implementing WebRTC privacy mode. - -commit c69d479edfaeb461ff2bc61cf7257ce0c2d273da -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Feb 10 16:29:57 2016 -0500 - - conncheck: Start conncheck on server reply if needed - - This only really applies in the force relay mode where there are - no local candidates. - -commit 1513ce23ff4279dad16e177a3fc779cb61074fa1 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 8 16:44:47 2016 -0500 - - Replace g_malloc/g_new with g_alloca where possible - - This should reduce the overhead a bit. - -commit 524c1090cc2813bcb1be6f7f29a894c742a608f7 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Wed Apr 20 10:17:05 2016 +0000 - - conncheck: explain some corner cases - - This patch give details why some exceptions to the ICE spec are needed. - - Differential Revision:
https://phabricator.freedesktop.org/D876
- -commit b05debeb95c13d162286c0a5c4076eee2ae7cf51 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Fri May 27 19:15:39 2016 -0400 - - conncheck: add a debug dump of the whole stream check list - -
https://phabricator.freedesktop.org/D814
- -commit 71e271095032bd50ac2be2b5d60f4beb15801fa0 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri May 27 18:50:59 2016 -0400 - - conncheck: fix the replay of early incoming connchecks - - This patch fixes a bug in the way the list of incoming checks - is handled. The code purges this list too early, before all ichecks - for a given component are processed. It happens because the component - is computed from each pair of the check list, instead of being passed - as a fixed parameter of the function. - - Differential Revision:
https://phabricator.freedesktop.org/D882
- -commit acdc0b8bb3cd2d48afe82c8dd8396a3c245191d9 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Wed Apr 20 09:23:14 2016 +0000 - - stun: fix ice role conflict handling - - This patch fixes the role conflict handling in stun ICE usage, - according to RFC 5245, by adding including missing cases in the - test. The role switch not only depends of the comparison of the - stun ice-controlling/controlled attrib with the agent tie breaker - value, but it also depends on the current role of the agent. - - This patch also changes the value returned by - stun_usage_ice_conncheck_create_reply() when a role conflict exists - but doesn't change the role of the agent, causing an error stun - response. Previously, this case could not be differenciated by the - caller from a case with no role conflict. Now by examinating the - return value, and whether the control param changed, the caller - can check the four possibles situations. The stun test suite is - updated to match this change. - - Differential Revision:
https://phabricator.freedesktop.org/D873
- -commit c90f93838db6a315ab2cbedaa92fed3f277b2103 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri May 27 17:26:06 2016 -0400 - - conncheck: Make previous commit compile - -
https://phabricator.freedesktop.org/T3324
- -commit d252feb553a423ee81482ff6b87e0033d9c046a6 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 8 18:49:42 2016 -0500 - - discovery: Make sure each candidate has a unique priority - - This should fix compliance with RFC 5245 Section 4.1.2 - -
https://phabricator.freedesktop.org/T3324
- -commit b72b9153d91a93a550c4ec40fce5f9a18e7eaac6 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Sun Sep 20 16:53:26 2015 -0400 - - agent: Restrict transitions to gathering - - Only allow transitions to gathering from disconnected or - failed states. - -commit 059a0e33c973a54c44f7c4fd1e766155f6078f80 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri May 27 14:06:24 2016 -0400 - - conncheck: fix TCP active relay handling - - TCP active relay candidates use UDP TURN for their underlying socket. - Since 0a6c779f1f, socket->fileno of UDP TURN sockets is always NULL, - which caused a wrong code path to be chosen in conn_check_send(). - - We have to update the if-expression accordingly. - - Maniphest Tasks: T7442 - Differential Revision:
https://phabricator.freedesktop.org/D1017
- -commit fc4d3aab5392f855dbda7ad0225bf0ad4e5fafb6 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri May 27 11:29:22 2016 -0400 - - agent: ignore gathering failures on auto-generated IPs - - Candidate gathering is stopped when discovery_add_local_host_candidate() - returns HOST_CANDIDATE_CANT_CREATE_SOCKET. This may be too radical - a measure when other local addresses can still be able to generate - usable candidates. - - The issue was observed by a user who had an IPv6 address with tentative - flag on one of the interfaces. That single failing address was causing - the whole gathering process to end with no candidates found. - - Still, don't do this if nice_agent_add_local_address() has been called. - In that case, the user really cares about the addresses and if there's - any problem, the process should fail. - -
https://phabricator.freedesktop.org/D1016
- -commit 1fb6401d9d5dbee8ba28a20f3d787a95f13d1883 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Mar 1 15:27:46 2016 -0500 - - candidate: Give lower priority to TCP relayed candidates - -commit 8ee6d1bbed87fda37ade7b5c5d9483f41037b06a -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 29 16:11:18 2016 -0500 - - udp-turn: Fix binding timeout leak - -commit 8f1f615e92cd56ad4d8487457c2fde2c4aaa51d9 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Feb 24 22:53:08 2016 -0500 - - conncheck: Update selected pair if necessary - -commit 65f2eda04c1c73cc7ebc3df2032d528eedc236e1 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 22 19:36:58 2016 -0500 - - conncheck: Don't reset keepalive timer on next keepalive - - If the keepalive is still being re-send, just let the retries do their - job. If they don't get a reply, then declare the attempt failed. - -commit 1ab9d7c104978ea1904aaaad708c1c8c23c77592 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu May 26 16:05:36 2016 -0400 - - conncheck: Separate valid and succeded states - - RFC 5245 specifies that when a mapped-address differs from the address - from the request was sent, the mapped-address is used to select the - valid pair, but the source address of the check is used to select the - pair that succeeded, so they are not the same. - -commit 0a6c779f1f24099db2c1cd34cd339e240682525d -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri Feb 19 20:47:08 2016 -0500 - - udp-turn: Don't expose GSocket - - UDP turn sockets should never be read frm directly. - Because they may share the same socket with the non-relay, - so the incoming data may not be relayed and then the NiceSocket - API doesn't allow returning the base socket as the source. - -commit 5b27b028d8ad89214dc7b1ecd018f56aa0333b9c -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Feb 18 14:20:52 2016 -0500 - - conncheck: Make very frequent debug verbose-only - -commit b7c2eabfd0bd8c1321d9e8450caa8fa6b6ecb5ab -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 15 19:09:58 2016 -0500 - - debug: Enable based on G_MESSAGES_DEBUG - -commit acfe2b1f366d5f33314db7e9878225f2a70358ef -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Fri Feb 12 01:01:37 2016 -0500 - - agent: Don't emit signal in the middle of recv call - -commit 716c5805d5b73e94bc6af3637dfd50266150734e -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Wed Feb 10 19:38:52 2016 -0500 - - agent: Update type of peer-reflexive candidate on trickled candidate - - If a remote candidate matches an already discovered peer-reflexive candidate, - then the type can be updated to the real type and the foundation - can be set correctly. - -commit fc0d3744ebc03f8137866170594968ba61e6be30 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Feb 9 12:52:45 2016 -0500 - - agent: Only try to use the address of the same family to connect to TURN - - Using a IPv6 local address to connect to a IPv4 relay just creates an - extra discovery attempt that will not provide something useful. - -commit c129b05a469b59b576f4700fe9bfe3adca0a48dc -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Sun Feb 7 19:48:07 2016 -0500 - - stun turn usage: Only send the username if short term creds or nonce present - - This is recommended by the STUN RFC 5389. - -commit 501f9a82e47076cda0deab8cf54758b608e899aa -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Sun Feb 7 19:41:52 2016 -0500 - - turn: Cache the nonce & realm to remove useless round trips - - Instead of re-discovering the nonce and realm for every request, cache them - in th socket. - -commit 82ea4d71728af95cf0c7bff478f69342a461134b -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Feb 9 11:18:30 2016 -0500 - - conncheck: Stay READY if a new nominated pairs comes in - -commit 729bd3bb215f0a9a67293dea6df3f8f234eea0ac -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 8 21:04:24 2016 -0500 - - conncheck: Deduplicate conncheck stopping code - -commit f122e4174d19c60bc434f3986f5c08f8673344bd -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Mon Feb 8 19:41:28 2016 -0500 - - Reset to connecting if reconnected after failed - -commit 9c1a41b06ab459ce33f32d25ce258fb21ba49047 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Jan 14 17:59:16 2016 -0500 - - agent: Add warning on ignored result - -commit a357b17f5f3415320b9ec7122738396ccd998521 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Apr 4 23:02:52 2016 +0100 - - conncheck: display controlling mode of stun requests - - This patch makes the debug log more explicit about the agent - controlling role for each stun request sent. It helps to debug - role conflict resolution. - - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D877
- -commit b986d6e5f2ee0b7b0e09031c1a369bf89153e4c5 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Apr 4 22:38:07 2016 +0100 - - agent: remove newline from debug output - - Just a cosmetic fix. - - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D872
- -commit e0ed4fb3a236710846d9129438e0077782569633 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Mon Apr 4 21:52:29 2016 +0100 - - socket: refactor nice_socket_is_base_of() - - • rename to nice_socket_is_based_on() and swap the order of arguments - accordingly; the implementation doesn't have to use the confusing - 'return other->is_base_of()' pattern anymore - • fix potential NULL dereferences - - The argument order in agent_recv_message_unlocked() was already wrongly - swapped in 1732c7d6 and thus this commit isn't changing it back because - that order has become the correct one. - - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D866
- -commit 38268e53fde8cd97055d88d2066c0016fe04b31b -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Mon Apr 4 21:46:05 2016 +0100 - - socket: fix wrong function called in nice_socket_is_base_of() - - We have to call is_base_of "virtual function pointer" of 'other' - object, not 'sock', since 'other' is the structure whose base - NiceSocket we need to get from its private data. - - For instance calling nice_socket_is_base_of() with 'sock' and 'other' - being respectively pseudo-SSL and UDP-TURN-over-TCP invoked is_base_of - variant for pseudo-SSL, casting other->priv into PseudoSSLPriv *, but - other->priv is actually TurnTcpPriv *. It must be called the other way - around. - -
https://phabricator.freedesktop.org/T7335
-
https://phabricator.freedesktop.org/T7336
- - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Reviewed-by: José Antonio Santos Cadenas <santoscadenas(a)gmail.com> - Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk> - Reviewed-by: José Antonio Santos Cadenas <santoscadenas(a)gmail.com> - Differential Revision:
https://phabricator.freedesktop.org/D785
- -commit 7037ab4cf384edd9f700bc221a9d980b30d9c64f -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Mon Apr 4 21:38:59 2016 +0100 - - conncheck: implement a "triggered queue" list - - The checks should not be sent immediately in priv_conn_check_initiate(), - but be put into the "triggered queue", see "7.2.1.4 Triggered Checks". - This patch implements this triggered checks list, and uses it to enforce a - pacing of STUN transactions, no more than one per Ta ms, according to - "B.1. Pacing of STUN Transactions". - - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D802
- -commit 1732c7d6a7a104438412309373818e493a2504c9 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Sun Mar 6 15:16:18 2016 -0500 - - agent: Fix argument order - - Fixes crash reported on
https://phabricator.freedesktop.org/D786
- -commit aac283e0fa75b226fe2431403761ebd45e4f5614 -Author: Fabrice Bellet <fabrice(a)bellet.info> -Date: Sat Mar 5 18:46:48 2016 +0000 - - ice: fix the debug of the presence of the controlling/controlled attrib - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D807
- -commit ed75d55cf279613bb736f7646d3010d816797ddf -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Mar 2 00:01:19 2016 +0000 - - agent: fix relay candidate discovery on hosts having several IPs - - When a message is received from a TURN server and we manage to find a - local relay candidate with matching stream and component IDs, we should - also check whether the message came from the candidate's respective - socket. - - We should do this because there might still be some pending TURN - candidate discovery with the same server from a different local host IP - and the message may be a response to our allocate request. If - nice_udp_turn_socket_parse_recv_message() is passed such request, it can - make some wrong assumptions and modify it like in the case of reliable - UDP-TURN-OVER-TCP by removing (supposed) RFC4571 framing, which in turn - causes the reply to be unrecognized and discarded. - - Because of this, any subsequent replies following the first successful - allocate response from that server couldn't create any additional relay - candidates. - - Maniphest Tasks:
https://phabricator.freedesktop.org/T7336
- - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D786
- -commit 1493a381d5bf6e15348c2bc17270f45b69cb70d2 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 23:50:11 2016 +0000 - - build: Update autogen.sh from GNOME template - -
https://wiki.gnome.org/Projects/GnomeCommon/Migration#autogen.sh
- -commit bc620c0de966b47d761bbcb1279dac6221a5a30e -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Tue Mar 1 23:29:10 2016 +0000 - - component.c: Fix memory leak - - If nicesocket is not added to a component it will be leaked. - - This is the case of active tcp sockets - - Change-Id: I57fefffef71d35ce9871139ee1064181f6fe125b - Reviewed-by: José Antonio Santos Cadenas <santoscadenas(a)gmail.com> - Differential Revision:
https://phabricator.freedesktop.org/D822
- -commit 38c5e66886cb8138d6be57b8a4721d9b42a358b7 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Mar 1 23:23:14 2016 +0000 - - agent: Use provided CandidatePair rather than re-finding a pair - - In priv_update_selected_pair(), commit 57393333 changed the code to - re-find a CandidatePair matching the given lfoundation and rfoundation. - However, the foundation does not uniquely identify candidate pairs, - and if we’re aiming to set a specific candidate pair as the selected - pair, this could result in the wrong pair being selected. - - This can happen when handling multiple similar candidate pairs, such as - when generating peer reflexive candidates from multiple sources. - - See
https://tools.ietf.org/html/rfc5245#section-2.4
. - - Originally spotted by Fabrice Bellet in -
https://phabricator.freedesktop.org/T3557
. - - Reviewed-by: José Antonio Santos Cadenas <santoscadenas(a)gmail.com> - Differential Revision:
https://phabricator.freedesktop.org/D742
- -commit 70981d41edf46a09393017f3de34748fcecea046 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 23:05:20 2016 +0000 - - simple-example: transmission can begin earlier than in ready state - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D819
- -commit 47eaf50a99d91c4a666f05c4de24613706847c88 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 23:04:14 2016 +0000 - - test-new-dribble: wait until ragent reaches state completed - - The test didn't let enough time for ragent to reach the completed state - after obtaining its remote candidates and switching to connecting state. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D817
- -commit 4e68244a51698aefdf44dd1ceb17b95275e655bf -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 23:02:52 2016 +0000 - - conncheck: reorder the connection list when priorities are updated - - The update of pairs priorities due to agent role change requires the - conncheck list to be reordered to reflect this modification. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D806
- -commit 0f1e6c64515871298b115b497b019506b8065235 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 23:01:14 2016 +0000 - - conncheck: fix keepalive stun agent initialisation - - With this patch, we send keepalive binding requests using agent - compatibility flags, instead of RFC 3489 classic stun. The peer stun - agent will known how to handle it, and won't be confused by the - uncompatible RFC 3489 message, causing "no cookie" errors in the debug - log. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D804
- -commit 17488a20d8db8ea1f8fa2d7a44090070407d6db8 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 22:58:15 2016 +0000 - - conncheck: foundations are shared across streams - - This patch fixes a bug where the foundation definition shouldn't take - into account the stream the pair belongs to. This is important, because - the ordinary checks algorithm will change pair state from Frozen to - Waiting, by selecting pairs from other streams sharing the same - foundation than already succeeded pairs. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D815
- -commit 3ce45c25af238fb4d9a040abb44597531140db2d -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 22:37:33 2016 +0000 - - test-priority: ignore the local preference - - The local preference depends on the rank of the IP address in the list - of all IP addresses available of the box running the test. As this value - is not fixed we ignore it in the test. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D818
- -commit c309905ff446bed2dc47811023b98bc586a02d63 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 22:33:51 2016 +0000 - - conncheck: nominate only one matching pair - - This patch fixes a bug in priv_mark_pair_nominated(), where the local - candidate was not passed to the function, so removing the possibility to - find which local candidate the check was sent to. - - Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D808
- -commit 80973c096de872983bc60cd28a84653931f8d601 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Tue Mar 1 22:28:35 2016 +0000 - - conncheck: add more debug information - - Add a more debug details, specifically in some places, it is interesting - to have the src and dst IP addresses of the pairs being checked, and - also to make the difference between log related to different stream ids. - - Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D803
- -commit 41ab61def82aa275649afd5f4a3fcb43e75fb360 -Author: Mike Ruprecht <cmaiku(a)gmail.com> -Date: Mon Jan 18 12:42:46 2016 +0000 - - agent: Fix not setting UPnP timeout on second gather_candidates() - - If the first call to nice_agent_gather_candidates() partially succeeds - (setting a UPnP agent and timeout), then fails before starting - gathering, a second call to nice_agent_gather_candidates() would fail to - set a new UPnP timeout because the UPnP initialisation block would be - skipped. That means gathering would never succeed due to timing out on - UPnP. - - Fix that by setting the UPnP timeout whenever a new pending UPnP mapping - is added. - -
https://phabricator.freedesktop.org/T3534
- - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - -commit 9638bc132f909177f6ecfb86cdd17af557a35c56 -Author: Jose Antonio Santos Cadenas <santoscadenas(a)gmail.com> -Date: Thu Dec 3 15:01:35 2015 +0100 - - configure.ac: Update glib version - - As udp-bsd.ccode is using G_IO_ERROR_CONNECTION_CLOSED glib 2.44 - is required. - - Change-Id: I1bb63f2484c513c58eeec312ba0835164604c40c - Reviewed-by: Philip Withnall <philip(a)tecnocode.co.uk> -
https://phabricator.freedesktop.org/T3492
- -commit 3e71f42c8b15790e252d850ba42a7ae7e7cc69a9 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Oct 7 19:03:58 2015 +0100 - - pseudotcp: Use labs() rather than abs() for handling long integers - - This fixes a compiler warning and prevents a possible truncation. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D345
- -commit 53283c218a5eb7a29e7019ac320e74f9dbe4b3fc -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Mon Jun 22 11:30:31 2015 +0100 - - tests: Enable G_MESSAGES_DEBUG for all unit tests - - Now that we’re using automake’s parallel test harness, it automatically - redirects all the debug log spew away from the console, so we should - always have it enabled. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D292
- -commit aef748ec7d0b06067312e5cc761a6375cd0f699c -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Thu Oct 1 17:52:08 2015 +0100 - - build: Set repository callsign in .arcconfig - - This fixes `arc diff` to select the right repository when submitting - patches. - -commit 008739a5a60e591629e38da9b8b7065dbc2c746f -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 14:57:10 2015 +0100 - - agent: Correctly namespace Component and its methods - - Remove all references to the old, unnamespaced versions. This should - cause no functional changes. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D309
- -commit 529bc193d56522b10a4de83409396b3316863934 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 14:34:34 2015 +0100 - - agent: Correctly namespace Stream and its methods - - Remove all references to the old, unnamespaced versions. This should - cause no functional changes. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D308
- -commit ef2b58f64887546e426dd8cda382f2908f84caca -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 14:11:14 2015 +0100 - - agent: Turn Component into a GObject - - This makes it reference-counted. This will be useful for allowing - GDatagramBased and GIOStream objects to hold references to the stream - and component they are interested in, allowing removal of the global - NiceAgent lock previously needed to look up the component for every I/O - operation. - - Deprecate all the old methods until it’s properly namespaced. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D307
- -commit 1f08419382c52c7e796da06c9271a362aa60333d -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 14:10:32 2015 +0100 - - agent: Turn Stream into a GObject - - This makes it reference-counted. This will be useful for allowing - GDatagramBased and GIOStream objects to hold references to the stream - and component they are interested in, allowing removal of the global - NiceAgent lock previously needed to look up the component for every I/O - operation. - - It also means that nice_stream_close() could eventually become - asynchronous, which would fix a few race conditions. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D306
- -commit da70716162b2085ca4db6e7efd446fcda7bd488d -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 17:59:04 2015 +0100 - - tests: Update expected priority values in test-priority - - This is a follow up to T3324, to update the test case to match the new - values generated. - - Bug:
https://phabricator.freedesktop.org/T3324
- Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D301
- -commit 66e47aa39f9cd3666e610fab78caa0c7d8f9c410 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 17:54:21 2015 +0100 - - tests: Use g_assert_cmpuint() to make test failures easier to diagnose - - Now we can actually see the priority numbers which are unequal. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D300
- -commit 1ae15f66af2af17e991ab028ca16a1200fd5f4e5 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 17:53:14 2015 +0100 - - tests: Set candidate addresses in test-priority - - This avoids an assertion failure in nice_address_to_string() when the - addresses are compared for priority. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D299
- -commit ea3348020da586f20e1a64c9732405e730563616 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 17:46:47 2015 +0100 - - agent: Remove redundant GLIB_CHECK_VERSION macros - - We depend on GLib 2.36.0, which is a higher version than any of these - version checks cared about, so they were all trivially true or false. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D298
- -commit 9f10231fc787e4683e896bf35f086906a5b16c03 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 17:44:44 2015 +0100 - - tests: Remove g_thread_init() calls - - We depend on GLib 2.36.0; g_thread_init() has been deprecated since - 2.32.0, when thread initialisation was changed to happen automatically. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D297
- -commit dac3e280d6f3fd792f1d79313debd03c0df282c4 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 30 17:41:36 2015 +0100 - - tests: Remove g_type_init() calls - - We depend on GLib 2.36.0, which deprecated g_type_init() since GType - initialisation is now done automatically. - - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - Differential Revision:
https://phabricator.freedesktop.org/D296
- -commit fae0f9d37c6e34f34c92dee85384c29e565bdbce -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Fri Sep 11 11:57:54 2015 +0100 - - conncheck: rename priv_process_response_check_for_peer_reflexive() - - Renamed the function to priv_process_response_check_for_reflexive() - because it now checks also for server reflexive candidates. - - Updated the documentation to indicate that the function never returns - NULL. - - Maniphest Tasks:
https://phabricator.freedesktop.org/T115
- Differential Revision:
https://phabricator.freedesktop.org/D243
- Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - -commit 02852728ef347f5cb4c9227848b266c72b5fe38b -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Fri Sep 11 11:56:02 2015 +0100 - - conncheck: generate candidate pair for valid srflx candidate - - In priv_process_response_check_for_peer_reflexive(), mere presence of a candidate in local_candidates doesn't mean there's also some candidate - pair in conncheck_list using it - for instance that candidate may be server reflexive, for which no check pairs are initially created (see - conn_check_add_for_candidate_pair()). - - If we fail to find corresponding pair upon receiving such candidate's IP in a conncheck response's XOR-MAPPED-ADDRESS attribute, we shall add a - new one in a similar way we would add a new pair for a just discovered peer reflexive candidate. - - Previous priv_process_response_check_for_peer_reflexive() implementation would return NULL, causing a CandidateCheckPair with local candidate of - type HOST to be wrongly selected even though the local host IP might not be directly accessible by the remote counterpart (e.g. it's an address - on a private network segment). In practice this was coming through as a duplex connection that libnice was reporting as properly established, - but only one direction of the communication was actually working. - - Maniphest Tasks:
https://phabricator.freedesktop.org/T115
- Differential Revision:
https://phabricator.freedesktop.org/D242
- Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - -commit 84eaa12b0b1a76c45ce2d77294e0477c10552cd4 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Fri Sep 11 11:33:51 2015 +0100 - - agent: check for base socket in _tcp_sock_is_writable() - - The argument passed into the callback is always a base (TCP/UDP) socket, - which can't be directly compared with local candidate's sockptr (may be - TURN, http, or other socket wrapping another one). We're in fact - interested whether sock is a base socket of sockptr. - - Maniphest Tasks: T114 - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D241
- -commit 837c8953fe87bdd5d5bccc444e72739100578ef8 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Fri Sep 11 11:29:39 2015 +0100 - - socket: add nice_socket_is_base_of() - - This will be used in the next commit. - - Maniphest Tasks: T114 - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Differential Revision:
https://phabricator.freedesktop.org/D240
- -commit c6bc33c031493e0db11a1f57055a656f6428c60a -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Thu Jul 10 08:32:04 2014 +0100 - - build: Bump GLib dependency to 2.36 - - This is needed for G_IO_ERROR_BROKEN_PIPE, which is used in the I/O - stream code. - - Reported by Emanuele Bizzarri <emabiz76(a)gmail.com> on the mailing list. - -commit 757f8aecdb4fda86b2bbac828a3acc528d8eb8bc -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Fri Sep 4 08:14:08 2015 +0100 - - stun: Disable debug by default - - To match debug_enable in agent/debug.c. Debug can still be enabled by - calling stun_debug_enable() or nice_debug_enable(). - - Spotted on the mailing list by Tom Chen. - -commit 2eaa8b3277f4f39515ff5dc7b512a44fd79e7275 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Mon Jun 29 16:30:12 2015 +0100 - - agent: Add assertions to check component state transitions are valid - - There is no point in the NiceComponents having a state machine if the - state transition graph is not documented or enforced. Document and - enforce it. - -
http://phabricator.freedesktop.org/T120
- -commit 3f54b333525e2a4ae35e0be439062900fb8ab7c3 -Merge: 1034832 181ad3a -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 2 16:44:45 2015 +0100 - - ms-ice: ensure distinct candidate priority for multihomed hosts - - Summary: - Offering multiple host candidates with equal priorities could lead to unpredictable candidate pair selection by our counterparty. - - Fixes call disconnection by MS Lync client after 30 seconds while VPN (2nd IP) was active on libnice host. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D234
- -commit 181ad3a9bf54b9d6c4e0921ae148bab6d9fd0b3a -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Sep 2 16:43:02 2015 +0100 - - candidate: use distinct priority also for non-MS compatibilities - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D239
- -commit 799b322d6c654b864b5442cdaaa62aaee81d4901 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Sep 2 16:41:49 2015 +0100 - - conncheck: give temporary candidate_priority a base_addr - - Summary: - Fixes "(nice_address_to_string): should not be reached" errors when calling nice_candidate_ms_ice_priority() because of invalid NiceAddress. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D238
- -commit 85cd01c1396ef244f90e0d9c995fab29ed121f23 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Sep 2 16:41:49 2015 +0100 - - ms-ice: ensure distinct candidate priority for multihomed hosts - - Summary: - Offering multiple host candidates with equal priorities could lead to unpredictable candidate pair selection by our counterparty. - - Fixes call disconnection by MS Lync client after 30 seconds while VPN (2nd IP) was active on libnice host. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D234
- -commit 88ed42619049ac1e3fe3a6e481df8aeb95033ac0 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Sep 2 16:41:48 2015 +0100 - - discovery: assign candidate priority after base_addr is set - - Summary: So that we can take the base address into account in the calculation. - - Maniphest Tasks: T3324 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D235
- -commit 10348322a960258043363e7c84e78c4821c90412 -Merge: abdab05 ab4ced5 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Sep 2 16:34:01 2015 +0100 - - ms-turn: don't wait for a reply to STUN_SEND request - - Maniphest Tasks: T126 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D223
- -commit ab4ced5a46a7edba7cd49c0495bc41cd8fff7cc2 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Wed Sep 2 16:32:05 2015 +0100 - - ms-turn: don't wait for a reply to STUN_SEND request - - As per [MS-TURN] Section 2.2.1, TURN message type 0x0104 "Send request - response" isn't supported and the TURN server MUST NOT send them. Thus, - libnice should not remember Send requests in agent->sent_ids because - without replies coming, the number of allowed pending transaction gets - quickly exhausted, causing our data packets to be dropped until a - request timeout frees some space in the queue. - - This behavior resulted in choppy reception of our audio on a Lync client - when connected via Lync Edge (TURN) Server. - - Maniphest Tasks: T126 - - Reviewers: pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall - - Differential Revision:
https://phabricator.freedesktop.org/D223
- -commit abdab053af41068406caf95e80a64c512fd3db90 -Merge: 03d11b4 490b16f -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Sat Aug 29 23:26:00 2015 +0100 - - Creating TCP sockets with TCP_NODELAY option set to TRUE - - Summary: - Disable Nagling for underlying TCP sockets used by libnice, because they - are typically used for streaming applications, or for pseudo-TCP; the - bandwidth in both cases is harmed by Nagling. - - Based on a patch by Vadim Genkin. - - Maniphest Tasks: T3317 - - Reviewers: vadimgenkin, pwithnall - - Projects: #libnice - - Reviewed By: pwithnall - - Subscribers: pwithnall, vadimgenkin - - Differential Revision:
https://phabricator.freedesktop.org/D230
- -commit 490b16f400284c5df6508fd095d592efdfbc62ae -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Sat Aug 29 22:39:56 2015 +0100 - - Creating TCP sockets with TCP_NODELAY option set to TRUE - - Disable Nagling for underlying TCP sockets used by libnice, because they - are typically used for streaming applications, or for pseudo-TCP; the - bandwidth in both cases is harmed by Nagling. - - Based on a patch by Vadim Genkin. - - Maniphest Tasks: T3317 - - Reviewers: pwithnall - - Projects: #libnice - - Subscribers: pwithnall, vadimgenkin - - Differential Revision:
https://phabricator.freedesktop.org/D230
- -commit 03d11b49b0ac14ff320192562df57898ea9f94b4 -Merge: dddca10 1c34734 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Sat Aug 29 20:05:00 2015 +0100 - - Fix agent leak in case component socket is reset remotely - - Summary: The patch fixes the issue where agent reference count is not properly decremented causing instance leak in cases where component's socket is reset remotely. - - Reviewers: #libnice, pwithnall - - Projects: #libnice - - Reviewed By: #libnice, pwithnall - - Subscribers: pwithnall, maximgolunov - - Differential Revision:
https://phabricator.freedesktop.org/D236
- -commit 1c34734cd105c6cca0ec8bbb908002c4bfb007b3 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Sat Aug 29 20:04:03 2015 +0100 - - Fix agent leak in case component socket is reset remotely - - Summary: The patch fixes the issue where agent reference count is not properly decremented causing instance leak in cases where component's socket is reset remotely. - - Reviewers: #libnice, pwithnall - - Projects: #libnice - - Reviewed By: #libnice, pwithnall - - Subscribers: pwithnall, maximgolunov - - Differential Revision:
https://phabricator.freedesktop.org/D236
- -commit dddca10ceff20e3578fc901b9919c3d20f87b7b6 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Aug 19 09:24:27 2015 +0100 - - build: Update .gitignore - - Add stun/usages/.dirstamp. - -commit 6c9afb4db35168cc699ff0aa2e56b127f3706083 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Aug 19 09:22:49 2015 +0100 - - build: Fix multiple definition of CLEANFILES - - It’s already defined in common.mk. - -commit 041158c4fe36c4af6d56cd4445946d7f5e5be57d -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Aug 19 09:20:01 2015 +0100 - - build: Add .arcconfig file - - This completes the transition to Phabricator; everyone should be using - the same project settings now. - -
https://phabricator.freedesktop.org/tag/libnice/
- -commit 8f2a14b1d6af73e91c5712898e8a3e97308920a6 -Merge: e5e77b6 ad0003b -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Aug 19 09:19:18 2015 +0100 - - socket: Handle ECONNRESET as EWOULDBLOCK on Windows - - Summary: - Some versions of Windows can return ECONNRESET for UDP recvmsg() calls - if they would otherwise block. Hence, handle the two equivalently; this - should not affect behaviour on Linux, which apparently does not return - ECONNRESET for UDP recvmsg() calls at all. - -
https://phabricator.freedesktop.org/T121
- - Maniphest Tasks: T121 - - Reviewers: ocrete - - Projects: #libnice - - Reviewed By: ocrete - - Subscribers: stwiname, felixSchl - - Differential Revision:
https://phabricator.freedesktop.org/D227
- -commit e5e77b67fb6152dd9fb0af3d7410a428299504d3 -Merge: 6835e7b a2e25cf -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Wed Aug 19 09:16:53 2015 +0100 - - socket: Close base socket for a TCP passive socket when closing parent - - Summary: - Otherwise the base socket will leak. Spotted by Vadim Genkin. - -
https://phabricator.freedesktop.org/T125
- - Maniphest Tasks: T125 - - Reviewers: ocrete - - Projects: #libnice - - Reviewed By: ocrete - - Subscribers: vadimgenkin - - Differential Revision:
https://phabricator.freedesktop.org/D228
- -commit a2e25cf49b24c2012e8e9058ecc7e62f1e027cb3 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Aug 18 14:58:23 2015 +0100 - - socket: Close base socket for a TCP passive socket when closing parent - - Otherwise the base socket will leak. Spotted by Vadim Genkin. - -
https://phabricator.freedesktop.org/T125
- -commit 6835e7b7f4a20078d508444659c636fc98e680fc -Merge: e1f748c 6b3e59c -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Aug 18 14:03:41 2015 +0100 - - agent: Remove unused inet_pton() function - - Summary: - As spotted by Felix <felixschlitter(a)gmail.com>. This is a static - function which is totally unused in this compilation unit and is causing - build failures with `-Werror=unused-function`. - - Maniphest Tasks: T123 - - Reviewers: felixSchl, ocrete - - Projects: #libnice - - Differential Revision:
https://phabricator.freedesktop.org/D221
- -commit 6b3e59c5a670d5117ed293ae612082dcd047ba8a -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Jun 30 14:39:51 2015 +0100 - - agent: Remove unused inet_pton() function - - As spotted by Felix <felixschlitter(a)gmail.com>. This is a static - function which is totally unused in this compilation unit and is causing - build failures with -Werror=unused-function. - -
http://phabricator.freedesktop.org/T123
- -commit ad0003b48414c789a1191fd5f44fec41e694dfa0 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Aug 18 13:33:23 2015 +0100 - - socket: Handle ECONNRESET as EWOULDBLOCK on Windows - - Some versions of Windows can return ECONNRESET for UDP recvmsg() calls - if they would otherwise block. Hence, handle the two equivalently; this - should not affect behaviour on Linux, which apparently does not return - ECONNRESET for UDP recvmsg() calls at all. - -
https://phabricator.freedesktop.org/T121
- -commit e1f748cccacd81cce4db338d0203dc49bc915a30 -Author: Jakub Adam <jakub.adam(a)ktknet.cz> -Date: Thu Jun 18 09:05:21 2015 +0200 - - conncheck: set writable callback to socket from TCP active connect - - A new socket created in nice_tcp_active_socket_connect() should have its - writable callback set, because it's possible for it to become a base - socket of a peer reflexive candidate, if some is discovered by - connection checks on that TCP active candidate. - - Previously, when such prflx candidate became selected, without write_cb - on the socket the agent was never notified about it becoming writable - again after the socket's buffer got filled up. This caused the data flow - to hang permanently. - - Reviewed-by: Philip Withnall <philip.withnall(a)collabora.co.uk> - Reviewed-by: Olivier Crête <olivier.crete(a)collabora.com> - -
http://phabricator.freedesktop.org/T117
- -commit cd61af3114a51df53619fb0460ace2b3660fc5da -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Tue Jun 30 14:29:43 2015 +0100 - - pseudotcp: Only define errnos on Windows if not already defined - - Recent versions of MinGW define at least ECONNABORTED and EAFNOSUPPORT, - so only define the various socket errnos if they are not defined - already. - - Based on a patch by Alexey Pawlow <alexey.pawlow(a)gmail.com> and Felix - <felixschlitter(a)gmail.com>. - - Reviewed-by: Olivier Crete <olivier.crete(a)collabora.com> - Reviewed-by: Felix Schlitter <felixschlitter(a)gmail.com> - -
http://phabricator.freedesktop.org/T122
- -commit 3cccc311b1becf4307f5a4004734d8f7b2cf84f6 -Author: Felix Schlitter <felixschlitter(a)gmail.com> -Date: Tue Jun 30 07:37:31 2015 +1200 - - Use G_GSIZE_FORMAT instead of %zu - - The C runtime on windows does not implement a printf that understands - %zu and other C99 format specifiers. Use G_GSIZE_FORMAT instead. This - is further consistent with the rest of the code base that makes use of - G_GSIZE_FORMAT throughout. - -
https://github.com/libnice/libnice/pull/3
- -commit c4d5ec572ae0ede14d13d9ce5b193cdcb36b07a1 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Sep 18 19:33:10 2014 -0400 - - Split "verbose" on-every-packet messages to a verbose log - - This way, the regular log will only contain connection-time information. - -commit 2d24ef532d1b6ec684f28d52aa0592dfdfb7e067 -Author: Olivier Crête <olivier.crete(a)collabora.com> -Date: Thu Sep 18 19:33:06 2014 -0400 - - stun: Remove annoying non-error on non-STUN packet - -commit 81a929ac141aae66b6450e8ce93cb357ed404cda -Author: Timo Gurr <timo.gurr(a)gmail.com> -Date: Mon Jun 1 16:10:16 2015 +0200 - - configure: Fix configure failure when building without gstreamer support - - Error introduced in 20ea22e0a11a9bdfe4d8125b68083249b694338a, resulting in a - configure/build error when building without gstreamer: - - configure: error: conditional "HAVE_GST_CHECK" was never defined. - Usually this means the macro was only invoked conditionally. - -
https://bugs.freedesktop.org/show_bug.cgi?id=90801
- -commit d3a7b315ec708ac9ecb8df53bcc8d108016bd508 -Author: Philip Withnall <philip.withnall(a)collabora.co.uk> -Date: Fri May 8 10:13:39 2015 +0100 - - build: Auto-generate win32 .def file from libnice.sym - - We’ve neglected to manually update this file once too often — it’s been - out of date for important new symbols (for example, - nice_agent_get_io_stream()) since at least 0.1.11. - - Since the format is a simple extension of libnice.sym, we might as well - automatically generate it at dist time. - -commit 6a8c63219c632c27707267b6510dca096c6fd511 -Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue May 5 15:07:10 2015 -0400 - - Removing no-op assignment - -commit 91a7b9324244844baf35d8fcef019a4ea3872d30 -Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue May 5 15:00:30 2015 -0400 - - Do not compare scope for IPv6 address when scope is 0 - - This caused issues with thinking local host candidates were peer-reflexive - candidates because the nice_address_equal would fail since the scope - would be 6 (or some other value) but locally created NiceAddress from - a stun response would have the scope set to 0. - We ignore the scope when comparing ipv6 candidates when scope is 0 - to avoid these kinds of issues. - Thanks to ikonst_ for finding these issues - -commit 93862c1e1940618e06143d4788f54bffd4d1c5da -Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net> -Date: Tue May 5 14:24:15 2015 -0400 - - Do not update a remote candidate's type - - When adding a remote candidate, if it's the same ip:port, we should - also check its type, otherwise it's a new candidate. We can't allow - a candidate type to be updated. This caused issues to ikonst_ on IRC - where for some reason a host candidate appeared as both host and prflx - and the update caused a remote host candidate to be updated to prflx - causing a crash when the sockptr was being accessed. - -commit 7b7d2d986876fc53a23af7b516d78f82f2a546e9 -Author: Philip Withnall <philip(a)tecnocode.co.uk> -Date: Sun May 3 16:05:30 2015 +0100 - - agent: Remove unnecessary NULL check - - With the changes in commit 483bdcf8, @name is now guaranteed to be - non-NULL. Spotted by Coverity. - - CID: #109878 -diff --git a/.arcconfig b/.arcconfig -new file mode 100644 -index 0000000..ba3d1ea ---- /dev/null -+++ b/.arcconfig -@@ -0,0 +1,6 @@ -+{ -+ "phabricator.uri" : "https:\/\/phabricator.freedesktop.org\/", -+ "history.immutable" : true, -+ "project.name" : "libnice", -+ "repository.callsign" : "LIBNICE" -+} -diff --git a/Makefile.am b/Makefile.am -index 432a14d..896c751 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -33,6 +33,7 @@ EXTRA_DIST = \ - scripts/lcov.sh \ - scripts/valgrind.sh \ - win32 \ -+ win32/vs9/libnice.def \ - m4/introspection.m4 - - MAINTAINERCLEANFILES = ar-lib -@@ -41,6 +42,22 @@ dist_check_SCRIPTS = \ - scripts/check-symbols.sh \ - scripts/make-symbol-list.sh - -+# Generate the win32 DLL symbol export file. -+# The stun_*() symbols at the end have historically been exported on Windows -+# but not Linux, for no particular reason. They can’t be removed without -+# breaking ABI. FIXME: Remove them when we next break ABI. -+win32/vs9/libnice.def: nice/libnice.sym -+ $(AM_V_GEN)(echo "LIBRARY libnice"; \ -+ echo ""; \ -+ echo "EXPORTS"; \ -+ echo ""; \ -+ cat $<; \ -+ echo "stun_debug"; \ -+ echo "stun_debug_bytes"; \ -+ echo "stun_hash_creds") > $@ -+ -+CLEANFILES += win32/vs9/libnice.def -+ - lcov: - find -name '*.gcda' -delete - $(MAKE) $(AM_MAKEFLAGS) check -diff --git a/agent/address.c b/agent/address.c -index a8d9c76..3c20220 100644 ---- a/agent/address.c -+++ b/agent/address.c -@@ -51,7 +51,6 @@ - #include "address.h" - - #ifdef G_OS_WIN32 --#define inet_pton inet_pton_win32 - #define inet_ntop inet_ntop_win32 - - /* Defined in recent versions of mingw: -@@ -86,36 +85,6 @@ inet_ntop_win32 (int af, const void *src, char *dst, socklen_t cnt) - return NULL; - } - --static int --inet_pton_win32(int af, const char *src, void *dst) --{ -- struct addrinfo hints, *res, *ressave; -- -- memset(&hints, 0, sizeof(struct addrinfo)); -- hints.ai_family = af; -- -- if (getaddrinfo(src, NULL, &hints, &res) != 0) { -- return 0; -- } -- -- ressave = res; -- -- while (res) { -- if( res->ai_addr->sa_family == AF_INET) { -- memcpy(dst, &((struct sockaddr_in *) res->ai_addr)->sin_addr, -- sizeof(struct in_addr)); -- res = res->ai_next; -- } else if(res->ai_addr->sa_family == AF_INET6) { -- memcpy(dst, &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr, -- sizeof(struct in_addr6)); -- res = res->ai_next; -- } -- } -- -- freeaddrinfo(ressave); -- return 1; --} -- - #endif - - -@@ -297,7 +266,8 @@ nice_address_equal (const NiceAddress *a, const NiceAddress *b) - case AF_INET6: - return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr) - && (a->s.ip6.sin6_port == b->s.ip6.sin6_port) -- && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id); -+ && (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 || -+ (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id)); - - default: - g_return_val_if_reached (FALSE); -@@ -412,7 +382,8 @@ nice_address_equal_no_port (const NiceAddress *a, const NiceAddress *b) - - case AF_INET6: - return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr) -- && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id); -+ && (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 || -+ (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id)); - - default: - g_return_val_if_reached (FALSE); -diff --git a/agent/agent-priv.h b/agent/agent-priv.h -index c413bc1..d66a9ef 100644 ---- a/agent/agent-priv.h -+++ b/agent/agent-priv.h -@@ -130,6 +130,7 @@ struct _NiceAgent - gboolean controlling_mode; /* property: controlling-mode */ - guint timer_ta; /* property: timer Ta */ - guint max_conn_checks; /* property: max connectivity checks */ -+ gboolean force_relay; /* property: force relay */ - - GSList *local_addresses; /* list of NiceAddresses for local - interfaces */ -@@ -139,6 +140,7 @@ struct _NiceAgent - guint next_stream_id; /* id of next created candidate */ - NiceRNG *rng; /* random number generator */ - GSList *discovery_list; /* list of CandidateDiscovery items */ -+ GSList *triggered_check_queue; /* pairs in the triggered check list */ - guint discovery_unsched_items; /* number of discovery items unscheduled */ - GSource *discovery_timer_source; /* source of discovery timer */ - GSource *conncheck_timer_source; /* source of conncheck timer */ -@@ -171,10 +173,10 @@ agent_find_component ( - NiceAgent *agent, - guint stream_id, - guint component_id, -- Stream **stream, -- Component **component); -+ NiceStream **stream, -+ NiceComponent **component) G_GNUC_WARN_UNUSED_RESULT; - --Stream *agent_find_stream (NiceAgent *agent, guint stream_id); -+NiceStream *agent_find_stream (NiceAgent *agent, guint stream_id); - - void agent_gathering_done (NiceAgent *agent); - void agent_signal_gathering_done (NiceAgent *agent); -@@ -202,7 +204,7 @@ void agent_signal_new_candidate ( - - void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate); - --void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream); -+void agent_signal_initial_binding_request_received (NiceAgent *agent, NiceStream *stream); - - guint64 agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote); - -@@ -220,6 +222,8 @@ void nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent); - - void _priv_set_socket_tos (NiceAgent *agent, NiceSocket *sock, gint tos); - -+void _tcp_sock_is_writable (NiceSocket *sock, gpointer user_data); -+ - gboolean - component_io_cb ( - GSocket *gsocket, -@@ -274,10 +278,14 @@ void nice_debug_init (void); - - #ifdef NDEBUG - static inline gboolean nice_debug_is_enabled (void) { return FALSE; } -+static inline gboolean nice_debug_is_verbose (void) { return FALSE; } - static inline void nice_debug (const char *fmt, ...) { } -+static inline void nice_debug_verbose (const char *fmt, ...) { } - #else - gboolean nice_debug_is_enabled (void); -+gboolean nice_debug_is_verbose (void); - void nice_debug (const char *fmt, ...) G_GNUC_PRINTF (1, 2); -+void nice_debug_verbose (const char *fmt, ...) G_GNUC_PRINTF (1, 2); - #endif - - #endif /*_NICE_AGENT_PRIV_H */ -diff --git a/agent/agent.c b/agent/agent.c -index 259fdc9..f6c7a36 100644 ---- a/agent/agent.c -+++ b/agent/agent.c -@@ -86,6 +86,7 @@ - static void - nice_debug_input_message_composition (const NiceInputMessage *messages, - guint n_messages); -+static const gchar *_cand_type_to_sdp (NiceCandidateType type); - - G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT); - -@@ -110,7 +111,8 @@ enum - PROP_ICE_UDP, - PROP_ICE_TCP, - PROP_BYTESTREAM_TCP, -- PROP_KEEPALIVE_CONNCHECK -+ PROP_KEEPALIVE_CONNCHECK, -+ PROP_FORCE_RELAY, - }; - - -@@ -133,11 +135,7 @@ enum - - static guint signals[N_SIGNALS]; - --#if GLIB_CHECK_VERSION(2,31,8) - static GMutex agent_mutex; /* Mutex used for thread-safe lib */ --#else --static GStaticMutex agent_mutex = G_STATIC_MUTEX_INIT; --#endif - - static void priv_stop_upnp (NiceAgent *agent); - -@@ -148,7 +146,7 @@ static void pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err, - gpointer user_data); - static PseudoTcpWriteResult pseudo_tcp_socket_write_packet (PseudoTcpSocket *sock, - const gchar *buffer, guint32 len, gpointer user_data); --static void adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component); -+static void adjust_tcp_clock (NiceAgent *agent, NiceStream *stream, NiceComponent *component); - - static void nice_agent_dispose (GObject *object); - static void nice_agent_get_property (GObject *object, -@@ -156,7 +154,6 @@ static void nice_agent_get_property (GObject *object, - static void nice_agent_set_property (GObject *object, - guint property_id, const GValue *value, GParamSpec *pspec); - --#if GLIB_CHECK_VERSION(2,31,8) - void agent_lock (void) - { - g_mutex_lock (&agent_mutex); -@@ -167,19 +164,6 @@ void agent_unlock (void) - g_mutex_unlock (&agent_mutex); - } - --#else --void agent_lock(void) --{ -- g_static_mutex_lock (&agent_mutex); --} -- --void agent_unlock(void) --{ -- g_static_mutex_unlock (&agent_mutex); --} -- --#endif -- - static GType _nice_agent_stream_ids_get_type (void); - - G_DEFINE_POINTER_TYPE (_NiceAgentStreamIds, _nice_agent_stream_ids); -@@ -314,13 +298,13 @@ agent_to_turn_socket_compatibility (NiceAgent *agent) - NICE_TURN_SOCKET_COMPATIBILITY_RFC5766; - } - --Stream *agent_find_stream (NiceAgent *agent, guint stream_id) -+NiceStream *agent_find_stream (NiceAgent *agent, guint stream_id) - { - GSList *i; - - for (i = agent->streams; i; i = i->next) - { -- Stream *s = i->data; -+ NiceStream *s = i->data; - - if (s->id == stream_id) - return s; -@@ -335,18 +319,18 @@ agent_find_component ( - NiceAgent *agent, - guint stream_id, - guint component_id, -- Stream **stream, -- Component **component) -+ NiceStream **stream, -+ NiceComponent **component) - { -- Stream *s; -- Component *c; -+ NiceStream *s; -+ NiceComponent *c; - - s = agent_find_stream (agent, stream_id); - - if (s == NULL) - return FALSE; - -- c = stream_find_component_by_id (s, component_id); -+ c = nice_stream_find_component_by_id (s, component_id); - - if (c == NULL) - return FALSE; -@@ -706,6 +690,24 @@ nice_agent_class_init (NiceAgentClass *klass) - FALSE, - G_PARAM_READWRITE)); - -+ /** -+ * NiceAgent:force-relay -+ * -+ * Force all traffic to go through a relay for added privacy, this -+ * allows hiding the local IP address. When this is enabled, so -+ * local candidates are available before relay servers have been set -+ * with nice_agent_set_relay_info(). -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (gobject_class, PROP_FORCE_RELAY, -+ g_param_spec_boolean ( -+ "force-relay", -+ "Force Relay", -+ "Force all traffic to go through a relay for added privacy.", -+ FALSE, -+ G_PARAM_READWRITE)); -+ - /* install signals */ - - /** -@@ -713,9 +715,12 @@ nice_agent_class_init (NiceAgentClass *klass) - * @agent: The #NiceAgent object - * @stream_id: The ID of the stream - * @component_id: The ID of the component -- * @state: The #NiceComponentState of the component -+ * @state: The new #NiceComponentState of the component -+ * -+ * This signal is fired whenever a component’s state changes. There are many -+ * valid state transitions. - * -- * This signal is fired whenever a component's state changes -+ *  - */ - signals[SIGNAL_COMPONENT_STATE_CHANGED] = - g_signal_new ( -@@ -1178,6 +1183,10 @@ nice_agent_get_property ( - g_value_set_boolean (value, agent->keepalive_conncheck); - break; - -+ case PROP_FORCE_RELAY: -+ g_value_set_boolean (value, agent->force_relay); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1231,11 +1240,11 @@ nice_agent_reset_all_stun_agents (NiceAgent *agent, gboolean only_software) - - for (stream_item = agent->streams; stream_item; - stream_item = stream_item->next) { -- Stream *stream = stream_item->data; -+ NiceStream *stream = stream_item->data; - - for (component_item = stream->components; component_item; - component_item = component_item->next) { -- Component *component = component_item->data; -+ NiceComponent *component = component_item->data; - - if (only_software) - stun_agent_set_software (&component->stun_agent, -@@ -1361,6 +1370,10 @@ nice_agent_set_property ( - agent->keepalive_conncheck = g_value_get_boolean (value); - break; - -+ case PROP_FORCE_RELAY: -+ agent->force_relay = g_value_get_boolean (value); -+ break; -+ - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -@@ -1371,7 +1384,7 @@ nice_agent_set_property ( - - - static void -- agent_signal_socket_writable (NiceAgent *agent, Component *component) -+ agent_signal_socket_writable (NiceAgent *agent, NiceComponent *component) - { - g_cancellable_cancel (component->tcp_writable_cancellable); - -@@ -1380,7 +1393,7 @@ static void - } - - static void --pseudo_tcp_socket_create (NiceAgent *agent, Stream *stream, Component *component) -+pseudo_tcp_socket_create (NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { - PseudoTcpCallbacks tcp_callbacks = {component, - pseudo_tcp_socket_opened, -@@ -1394,8 +1407,8 @@ pseudo_tcp_socket_create (NiceAgent *agent, Stream *stream, Component *component - agent, component->id); - } - --static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream, -- Component *component) -+static void priv_pseudo_tcp_error (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component) - { - if (component->tcp_writable_cancellable) { - g_cancellable_cancel (component->tcp_writable_cancellable); -@@ -1405,7 +1418,7 @@ static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream, - if (component->tcp) { - agent_signal_component_state_change (agent, stream->id, - component->id, NICE_COMPONENT_STATE_FAILED); -- component_detach_all_sockets (component); -+ nice_component_detach_all_sockets (component); - pseudo_tcp_socket_close (component->tcp, TRUE); - } - -@@ -1419,9 +1432,9 @@ static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream, - static void - pseudo_tcp_socket_opened (PseudoTcpSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - - nice_debug ("Agent %p: s%d:%d pseudo Tcp socket Opened", agent, - stream->id, component->id); -@@ -1533,7 +1546,7 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self, - (gchar *) buffer->buffer + iter->offset, - buffer->size - iter->offset); - -- nice_debug ("%s: Received %" G_GSSIZE_FORMAT " bytes into " -+ nice_debug_verbose ("%s: Received %" G_GSSIZE_FORMAT " bytes into " - "buffer %p (offset %" G_GSIZE_FORMAT ", length %" G_GSIZE_FORMAT - ").", G_STRFUNC, len, buffer->buffer, iter->offset, buffer->size); - -@@ -1580,25 +1593,25 @@ done: - static void - pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - gboolean has_io_callback; - guint stream_id = stream->id; - guint component_id = component->id; - - g_object_ref (agent); - -- nice_debug ("Agent %p: s%d:%d pseudo Tcp socket readable", agent, -+ nice_debug_verbose ("Agent %p: s%d:%d pseudo Tcp socket readable", agent, - stream->id, component->id); - - component->tcp_readable = TRUE; - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - - /* Only dequeue pseudo-TCP data if we can reliably inform the client. The - * agent lock is held here, so has_io_callback can only change during -- * component_emit_io_callback(), after which it’s re-queried. This ensures -+ * nice_component_emit_io_callback(), after which it’s re-queried. This ensures - * no data loss of packets already received and dequeued. */ - if (has_io_callback) { - do { -@@ -1641,7 +1654,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - break; - } - -- component_emit_io_callback (component, buf, len); -+ nice_component_emit_io_callback (component, buf, len); - - if (!agent_find_component (agent, stream_id, component_id, - &stream, &component)) { -@@ -1653,7 +1666,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - goto out; - } - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - } while (has_io_callback); - } else if (component->recv_messages != NULL) { - gint n_valid_messages; -@@ -1667,7 +1680,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - component->recv_messages, component->n_recv_messages, - &component->recv_messages_iter, &child_error); - -- nice_debug ("%s: Client buffers case: Received %d valid messages:", -+ nice_debug_verbose ("%s: Client buffers case: Received %d valid messages:", - G_STRFUNC, n_valid_messages); - nice_debug_input_message_composition (component->recv_messages, - component->n_recv_messages); -@@ -1700,17 +1713,16 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data) - out: - - g_object_unref (agent); -- - } - - static void - pseudo_tcp_socket_writable (PseudoTcpSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - -- nice_debug ("Agent %p: s%d:%d pseudo Tcp socket writable", agent, -+ nice_debug_verbose ("Agent %p: s%d:%d pseudo Tcp socket writable", agent, - stream->id, component->id); - - agent_signal_socket_writable (agent, component); -@@ -1720,9 +1732,9 @@ static void - pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err, - gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - - nice_debug ("Agent %p: s%d:%d pseudo Tcp socket closed. " - "Calling priv_pseudo_tcp_error().", agent, stream->id, component->id); -@@ -1734,7 +1746,7 @@ static PseudoTcpWriteResult - pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket, - const gchar *buffer, guint32 len, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - - if (component->selected_pair.local != NULL) { - NiceSocket *sock; -@@ -1747,7 +1759,7 @@ pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket, - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (addr, tmpbuf); - -- nice_debug ( -+ nice_debug_verbose ( - "Agent %p : s%d:%d: sending %d bytes on socket %p (FD %d) to [%s]:%d", - component->agent, component->stream->id, component->id, len, - sock->fileno, g_socket_get_fd (sock->fileno), tmpbuf, -@@ -1775,8 +1787,8 @@ pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket, - static gboolean - notify_pseudo_tcp_socket_clock (gpointer user_data) - { -- Component *component = user_data; -- Stream *stream; -+ NiceComponent *component = user_data; -+ NiceStream *stream; - NiceAgent *agent; - - agent_lock(); -@@ -1800,7 +1812,7 @@ notify_pseudo_tcp_socket_clock (gpointer user_data) - } - - static void --adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component) -+adjust_tcp_clock (NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { - if (!pseudo_tcp_socket_is_closed (component->tcp)) { - guint64 timeout = component->last_clock_timeout; -@@ -1809,13 +1821,7 @@ adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component) - if (timeout != component->last_clock_timeout) { - component->last_clock_timeout = timeout; - if (component->tcp_clock) { --#if GLIB_CHECK_VERSION (2, 36, 0) - g_source_set_ready_time (component->tcp_clock, timeout * 1000); --#else -- g_source_destroy (component->tcp_clock); -- g_source_unref (component->tcp_clock); -- component->tcp_clock = NULL; --#endif - } - if (!component->tcp_clock) { - long interval = timeout - (guint32) (g_get_monotonic_time () / 1000); -@@ -1837,19 +1843,19 @@ adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component) - } - } - --static void -+void - _tcp_sock_is_writable (NiceSocket *sock, gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - NiceAgent *agent = component->agent; -- Stream *stream = component->stream; -+ NiceStream *stream = component->stream; - - agent_lock (); - - /* Don't signal writable if the socket that has become writable is not - * the selected pair */ - if (component->selected_pair.local == NULL || -- component->selected_pair.local->sockptr != sock) { -+ !nice_socket_is_based_on (component->selected_pair.local->sockptr, sock)) { - agent_unlock (); - return; - } -@@ -1883,12 +1889,17 @@ void agent_gathering_done (NiceAgent *agent) - GSList *i, *j, *k, *l, *m; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *local_candidate = k->data; -+ -+ if (agent->force_relay && -+ local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (&local_candidate->addr, tmpbuf); -@@ -1933,7 +1944,7 @@ void agent_signal_gathering_done (NiceAgent *agent) - GSList *i; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - if (stream->gathering) { - stream->gathering = FALSE; - agent_queue_signal (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], -@@ -1942,7 +1953,9 @@ void agent_signal_gathering_done (NiceAgent *agent) - } - } - --void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream) -+void -+agent_signal_initial_binding_request_received (NiceAgent *agent, -+ NiceStream *stream) - { - if (stream->initial_binding_request_received != TRUE) { - stream->initial_binding_request_received = TRUE; -@@ -1957,8 +1970,8 @@ void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *st - * - * Must be called with the agent lock held. */ - static void --process_queued_tcp_packets (NiceAgent *agent, Stream *stream, -- Component *component) -+process_queued_tcp_packets (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component) - { - GOutputVector *vec; - guint stream_id = stream->id; -@@ -1972,7 +1985,7 @@ process_queued_tcp_packets (NiceAgent *agent, Stream *stream, - return; - } - -- nice_debug ("%s: Sending outstanding packets for agent %p.", G_STRFUNC, -+ nice_debug_verbose ("%s: Sending outstanding packets for agent %p.", G_STRFUNC, - agent); - - while ((vec = g_queue_peek_head (&component->queued_tcp_packets)) != NULL) { -@@ -2011,8 +2024,8 @@ process_queued_tcp_packets (NiceAgent *agent, Stream *stream, - void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id, - guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - - if (!agent_find_component (agent, stream_id, component_id, - &stream, &component)) -@@ -2122,28 +2135,67 @@ nice_component_state_to_string (NiceComponentState state) - } - } - --void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state) -+void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState new_state) - { -- Component *component; -- Stream *stream; -+ NiceComponentState old_state; -+ NiceComponent *component; -+ NiceStream *stream; -+ -+ g_return_if_fail (new_state < NICE_COMPONENT_STATE_LAST); - - if (!agent_find_component (agent, stream_id, component_id, - &stream, &component)) - return; - -- if (component->state != state && state < NICE_COMPONENT_STATE_LAST) { -- nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %s -> %s.", agent, -- stream_id, component_id, nice_component_state_to_string (component->state), -- nice_component_state_to_string (state)); -+ /* Validate the state change. */ -+ old_state = component->state; - -- component->state = state; -+ if (new_state == old_state) { -+ return; -+ } - -- if (agent->reliable) -- process_queued_tcp_packets (agent, stream, component); -+ nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %s -> %s.", agent, -+ stream_id, component_id, nice_component_state_to_string (old_state), -+ nice_component_state_to_string (new_state)); -+ -+ /* Check whether it’s a valid state transition. */ -+#define TRANSITION(OLD, NEW) \ -+ (old_state == NICE_COMPONENT_STATE_##OLD && \ -+ new_state == NICE_COMPONENT_STATE_##NEW) -+ -+ g_assert (/* Can (almost) always transition to FAILED (including -+ * DISCONNECTED → FAILED which happens if one component fails -+ * before another leaves DISCONNECTED): */ -+ TRANSITION (DISCONNECTED, FAILED) || -+ TRANSITION (GATHERING, FAILED) || -+ TRANSITION (CONNECTING, FAILED) || -+ TRANSITION (CONNECTED, FAILED) || -+ TRANSITION (READY, FAILED) || -+ /* Standard progression towards a ready connection: */ -+ TRANSITION (DISCONNECTED, GATHERING) || -+ TRANSITION (GATHERING, CONNECTING) || -+ TRANSITION (CONNECTING, CONNECTED) || -+ TRANSITION (CONNECTED, READY) || -+ /* priv_conn_check_add_for_candidate_pair_matched(): */ -+ TRANSITION (READY, CONNECTED) || -+ /* If set_remote_candidates() is called with new candidates after -+ * reaching FAILED: */ -+ TRANSITION (FAILED, CONNECTING) || -+ /* if new relay servers are added to a failed connection */ -+ TRANSITION (FAILED, GATHERING) || -+ /* Possible by calling set_remote_candidates() without calling -+ * nice_agent_gather_candidates(): */ -+ TRANSITION (DISCONNECTED, CONNECTING)); -+ -+#undef TRANSITION -+ -+ component->state = new_state; - -- agent_queue_signal (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], -- stream_id, component_id, state); -- } -+ if (agent->reliable) -+ process_queued_tcp_packets (agent, stream, component); -+ -+ agent_queue_signal (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], -+ stream_id, component_id, new_state); - } - - guint64 -@@ -2158,7 +2210,7 @@ agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandi - static void - priv_add_new_candidate_discovery_stun (NiceAgent *agent, - NiceSocket *nicesock, NiceAddress server, -- Stream *stream, guint component_id) -+ NiceStream *stream, guint component_id) - { - CandidateDiscovery *cdisco; - -@@ -2171,7 +2223,7 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent, - cdisco->nicesock = nicesock; - cdisco->server = server; - cdisco->stream = stream; -- cdisco->component = stream_find_component_by_id (stream, component_id); -+ cdisco->component = nice_stream_find_component_by_id (stream, component_id); - cdisco->agent = agent; - stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES, - STUN_COMPATIBILITY_RFC3489, -@@ -2179,7 +2231,7 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent, - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ? - STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES : 0); - -- nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n", -+ nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p", - agent, cdisco); - - agent->discovery_list = g_slist_append (agent->discovery_list, cdisco); -@@ -2189,10 +2241,10 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent, - static void - priv_add_new_candidate_discovery_turn (NiceAgent *agent, - NiceSocket *nicesock, TurnServer *turn, -- Stream *stream, guint component_id, gboolean turn_tcp) -+ NiceStream *stream, guint component_id, gboolean turn_tcp) - { - CandidateDiscovery *cdisco; -- Component *component = stream_find_component_by_id (stream, component_id); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, component_id); - NiceAddress local_address; - - /* note: no need to check for redundant candidates, as this is -@@ -2214,7 +2266,7 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - new_socket = nice_udp_bsd_socket_new (&addr); - if (new_socket) { - _priv_set_socket_tos (agent, new_socket, stream->tos); -- component_attach_socket (component, new_socket); -+ nice_component_attach_socket (component, new_socket); - nicesock = new_socket; - } - } -@@ -2308,14 +2360,14 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - cdisco->nicesock = nice_udp_turn_over_tcp_socket_new (nicesock, - agent_to_turn_socket_compatibility (agent)); - -- component_attach_socket (component, cdisco->nicesock); -+ nice_component_attach_socket (component, cdisco->nicesock); - } - - cdisco->turn = turn_server_ref (turn); - cdisco->server = turn->server; - - cdisco->stream = stream; -- cdisco->component = stream_find_component_by_id (stream, component_id); -+ cdisco->component = nice_stream_find_component_by_id (stream, component_id); - cdisco->agent = agent; - - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { -@@ -2342,7 +2394,7 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent, - } - stun_agent_set_software (&cdisco->stun_agent, agent->software_attribute); - -- nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p\n", -+ nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p", - agent, cdisco); - agent->discovery_list = g_slist_append (agent->discovery_list, cdisco); - ++agent->discovery_unsched_items; -@@ -2353,7 +2405,7 @@ nice_agent_add_stream ( - NiceAgent *agent, - guint n_components) - { -- Stream *stream; -+ NiceStream *stream; - guint ret = 0; - guint i; - -@@ -2361,7 +2413,7 @@ nice_agent_add_stream ( - g_return_val_if_fail (n_components >= 1, 0); - - agent_lock(); -- stream = stream_new (n_components, agent); -+ stream = nice_stream_new (n_components, agent); - - agent->streams = g_slist_append (agent->streams, stream); - stream->id = agent->next_stream_id++; -@@ -2369,7 +2421,7 @@ nice_agent_add_stream ( - if (agent->reliable) { - nice_debug ("Agent %p : reliable stream", agent); - for (i = 0; i < n_components; i++) { -- Component *component = stream_find_component_by_id (stream, i + 1); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, i + 1); - if (component) { - pseudo_tcp_socket_create (agent, stream, component); - } else { -@@ -2378,7 +2430,7 @@ nice_agent_add_stream ( - } - } - -- stream_initialize_credentials (stream, agent->rng); -+ nice_stream_initialize_credentials (stream, agent->rng); - - ret = stream->id; - -@@ -2395,8 +2447,8 @@ nice_agent_set_relay_info(NiceAgent *agent, - NiceRelayType type) - { - -- Component *component = NULL; -- Stream *stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *stream = NULL; - gboolean ret = TRUE; - TurnServer *turn; - -@@ -2439,7 +2491,9 @@ nice_agent_set_relay_info(NiceAgent *agent, - NiceCandidate *candidate = i->data; - - if (candidate->type == NICE_CANDIDATE_TYPE_HOST && -- candidate->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -+ candidate->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && -+ nice_address_ip_version (&candidate->addr) == -+ nice_address_ip_version (&turn->server)) - priv_add_new_candidate_discovery_turn (agent, - candidate->sockptr, turn, stream, component_id, - candidate->transport != NICE_CANDIDATE_TRANSPORT_UDP); -@@ -2547,12 +2601,16 @@ static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto, - nice_address_set_port (&externaddr, external_port); - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *local_candidate = k->data; - -+ if (agent->force_relay && -+ local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - if (nice_address_equal (&localaddr, &local_candidate->base_addr)) { - discovery_add_server_reflexive_candidate ( - agent, -@@ -2613,7 +2671,7 @@ nice_agent_gather_candidates ( - { - guint cid; - GSList *i; -- Stream *stream; -+ NiceStream *stream; - GSList *local_addresses = NULL; - gboolean ret = TRUE; - -@@ -2638,13 +2696,10 @@ nice_agent_gather_candidates ( - agent->full_mode ? "ICE-FULL" : "ICE-LITE"); - - #ifdef HAVE_GUPNP -- if (agent->upnp_enabled && agent->upnp == NULL) { -+ if (agent->upnp_enabled && agent->upnp == NULL && !agent->force_relay) { - agent->upnp = gupnp_simple_igd_thread_new (); - - if (agent->upnp) { -- agent_timeout_add_with_context (agent, &agent->upnp_timer_source, -- "UPnP timeout", agent->upnp_timeout, priv_upnp_timeout_cb, agent); -- - g_signal_connect (agent->upnp, "mapped-external-port", - G_CALLBACK (_upnp_mapped_external_port), agent); - g_signal_connect (agent->upnp, "error-mapping-port", -@@ -2698,7 +2753,7 @@ nice_agent_gather_candidates ( - #endif - - for (cid = 1; cid <= stream->n_components; cid++) { -- Component *component = stream_find_component_by_id (stream, cid); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, cid); - enum { - ADD_HOST_MIN = 0, - ADD_HOST_UDP = ADD_HOST_MIN, -@@ -2769,6 +2824,10 @@ nice_agent_gather_candidates ( - " s%d:%d. Invalid interface?", agent, ip, stream->id, - component->id); - } -+ if (agent->local_addresses == NULL) { -+ /* Ignore when an auto-generated address fails. */ -+ continue; -+ } - ret = FALSE; - goto error; - } -@@ -2791,11 +2850,14 @@ nice_agent_gather_candidates ( - 0, local_ip, nice_address_get_port (base_addr), - 0, PACKAGE_STRING); - agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, base_addr); -+ -+ agent_timeout_add_with_context (agent, &agent->upnp_timer_source, -+ "UPnP timeout", agent->upnp_timeout, priv_upnp_timeout_cb, agent); - } - #endif - - /* TODO: Add server-reflexive support for TCP candidates */ -- if (agent->full_mode && agent->stun_server_ip && -+ if (agent->full_mode && agent->stun_server_ip && !agent->force_relay && - transport == NICE_CANDIDATE_TRANSPORT_UDP) { - NiceAddress stun_server; - if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) { -@@ -2834,9 +2896,13 @@ nice_agent_gather_candidates ( - /* Only signal the new candidates after we're sure that the gathering was - * succesfful. But before sending gathering-done */ - for (cid = 1; cid <= stream->n_components; cid++) { -- Component *component = stream_find_component_by_id (stream, cid); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, cid); - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; -+ -+ if (agent->force_relay && candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - agent_signal_new_candidate (agent, candidate); - } - } -@@ -2863,9 +2929,9 @@ nice_agent_gather_candidates ( - if (ret == FALSE) { - priv_stop_upnp (agent); - for (cid = 1; cid <= stream->n_components; cid++) { -- Component *component = stream_find_component_by_id (stream, cid); -+ NiceComponent *component = nice_stream_find_component_by_id (stream, cid); - -- component_free_socket_sources (component); -+ nice_component_free_socket_sources (component); - - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *candidate = i->data; -@@ -2938,7 +3004,7 @@ nice_agent_remove_stream ( - - /* note that streams/candidates can be in use by other threads */ - -- Stream *stream; -+ NiceStream *stream; - - g_return_if_fail (NICE_IS_AGENT (agent)); - g_return_if_fail (stream_id >= 1); -@@ -2958,7 +3024,7 @@ nice_agent_remove_stream ( - - /* Remove the stream and signal its removal. */ - agent->streams = g_slist_remove (agent->streams, stream); -- stream_close (stream); -+ nice_stream_close (stream); - - if (!agent->streams) - priv_remove_keepalive_timer (agent); -@@ -2971,7 +3037,7 @@ nice_agent_remove_stream ( - /* Actually free the stream. This should be done with the lock released, as - * it could end up disposing of a NiceIOStream, which tries to take the - * agent lock itself. */ -- stream_free (stream); -+ g_object_unref (stream); - - return; - } -@@ -2980,8 +3046,8 @@ NICEAPI_EXPORT void - nice_agent_set_port_range (NiceAgent *agent, guint stream_id, guint component_id, - guint min_port, guint max_port) - { -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - - g_return_if_fail (NICE_IS_AGENT (agent)); - g_return_if_fail (stream_id >= 1); -@@ -3033,15 +3099,28 @@ static gboolean priv_add_remote_candidate ( - const gchar *password, - const gchar *foundation) - { -- Component *component; -+ NiceComponent *component; - NiceCandidate *candidate; - - if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) - return FALSE; - - /* step: check whether the candidate already exists */ -- candidate = component_find_remote_candidate(component, addr, transport); -- if (candidate) { -+ candidate = nice_component_find_remote_candidate (component, addr, transport); -+ -+ /* If it was a discovered remote peer reflexive candidate, then it should -+ * be updated according to RFC 5245 section 7.2.1.3 */ -+ if (candidate && candidate->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE && -+ candidate->priority == priority) { -+ nice_debug ("Agent %p : Updating existing peer-rfx remote candidate to %s", -+ agent, _cand_type_to_sdp (type)); -+ candidate->type = type; -+ /* If it got there, the next one will also be ran, so the foundation -+ * will be set. -+ */ -+ } -+ -+ if (candidate && candidate->type == type) { - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (addr, tmpbuf); -@@ -3051,7 +3130,6 @@ static gboolean priv_add_remote_candidate ( - username, password, priority); - } - /* case 1: an existing candidate, update the attributes */ -- candidate->type = type; - if (base_addr) - candidate->base_addr = *base_addr; - candidate->priority = priority; -@@ -3136,7 +3214,7 @@ nice_agent_set_remote_credentials ( - guint stream_id, - const gchar *ufrag, const gchar *pwd) - { -- Stream *stream; -+ NiceStream *stream; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -3167,7 +3245,7 @@ nice_agent_set_local_credentials ( - const gchar *ufrag, - const gchar *pwd) - { -- Stream *stream; -+ NiceStream *stream; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -3198,7 +3276,7 @@ nice_agent_get_local_credentials ( - guint stream_id, - gchar **ufrag, gchar **pwd) - { -- Stream *stream; -+ NiceStream *stream; - gboolean ret = TRUE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -3226,8 +3304,8 @@ nice_agent_get_local_credentials ( - } - - static int --_set_remote_candidates_locked (NiceAgent *agent, Stream *stream, -- Component *component, const GSList *candidates) -+_set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component, const GSList *candidates) - { - const GSList *i; - int added = 0; -@@ -3253,12 +3331,10 @@ _set_remote_candidates_locked (NiceAgent *agent, Stream *stream, - } - } - -- conn_check_remote_candidates_set(agent); -+ conn_check_remote_candidates_set(agent, stream, component); - - if (added > 0) { -- gboolean res = conn_check_schedule_next (agent); -- if (res != TRUE) -- nice_debug ("Agent %p : Warning: unable to schedule any conn checks!", agent); -+ conn_check_schedule_next (agent); - } - - return added; -@@ -3269,8 +3345,8 @@ NICEAPI_EXPORT int - nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint component_id, const GSList *candidates) - { - int added = 0; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - - g_return_val_if_fail (NICE_IS_AGENT (agent), 0); - g_return_val_if_fail (stream_id >= 1, 0); -@@ -3329,14 +3405,15 @@ typedef enum { - static RecvStatus - agent_recv_message_unlocked ( - NiceAgent *agent, -- Stream *stream, -- Component *component, -+ NiceStream *stream, -+ NiceComponent *component, - NiceSocket *nicesock, - NiceInputMessage *message) - { - NiceAddress from; - GList *item; - gint retval; -+ gboolean is_turn = FALSE; - - /* We need an address for packet parsing, below. */ - if (message->from == NULL) { -@@ -3383,7 +3460,7 @@ agent_recv_message_unlocked ( - n_bufs = message->n_buffers; - } - -- local_bufs = g_malloc_n (n_bufs + 1, sizeof (GInputVector)); -+ local_bufs = g_alloca ((n_bufs + 1) * sizeof (GInputVector)); - local_message.buffers = local_bufs; - local_message.n_buffers = n_bufs + 1; - local_message.from = message->from; -@@ -3400,7 +3477,6 @@ agent_recv_message_unlocked ( - if (retval == 1) { - message->length = ntohs (rfc4571_frame); - } -- g_free (local_bufs); - } else { - if (nicesock->type == NICE_SOCKET_TYPE_TCP_PASSIVE) { - NiceSocket *new_socket; -@@ -3411,7 +3487,7 @@ agent_recv_message_unlocked ( - new_socket = nice_tcp_passive_socket_accept (nicesock); - if (new_socket) { - _priv_set_socket_tos (agent, new_socket, stream->tos); -- component_attach_socket (component, new_socket); -+ nice_component_attach_socket (component, new_socket); - } - retval = 0; - } else { -@@ -3481,7 +3557,7 @@ agent_recv_message_unlocked ( - n_bufs = message->n_buffers; - } - -- local_bufs = g_malloc_n (n_bufs, sizeof (GInputVector)); -+ local_bufs = g_alloca (n_bufs * sizeof (GInputVector)); - local_message.buffers = local_bufs; - local_message.from = message->from; - local_message.length = 0; -@@ -3508,7 +3584,6 @@ agent_recv_message_unlocked ( - message->length = local_message.length; - agent->rfc4571_expecting_length -= local_message.length; - } -- g_free (local_bufs); - } - } - } -@@ -3516,7 +3591,7 @@ agent_recv_message_unlocked ( - retval = nice_socket_recv_messages (nicesock, message, 1); - } - -- nice_debug ("%s: Received %d valid messages of length %" G_GSIZE_FORMAT -+ nice_debug_verbose ("%s: Received %d valid messages of length %" G_GSIZE_FORMAT - " from base socket %p.", G_STRFUNC, retval, message->length, nicesock); - - if (retval == 0) { -@@ -3535,30 +3610,45 @@ agent_recv_message_unlocked ( - goto done; - } - -- if (nice_debug_is_enabled ()) { -+ if (nice_debug_is_verbose ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (message->from, tmpbuf); -- nice_debug ("Agent %p : Packet received on local socket %d from [%s]:%u (%" G_GSSIZE_FORMAT " octets).", agent, -+ nice_debug_verbose ("Agent %p : Packet received on local socket %d from [%s]:%u (%" G_GSSIZE_FORMAT " octets).", agent, - g_socket_get_fd (nicesock->fileno), tmpbuf, - nice_address_get_port (message->from), message->length); - } - -- for (item = component->turn_servers; item; item = g_list_next (item)) { -+ if (nicesock->type == NICE_SOCKET_TYPE_UDP_TURN) -+ is_turn = TRUE; -+ -+ if (!is_turn && component->turn_candidate && -+ nice_socket_is_based_on (component->turn_candidate->sockptr, nicesock) && -+ nice_address_equal (message->from, -+ &component->turn_candidate->turn->server)) { -+ is_turn = TRUE; -+ retval = nice_udp_turn_socket_parse_recv_message ( -+ component->turn_candidate->sockptr, &nicesock, message); -+ } -+ -+ for (item = component->turn_servers; item && !is_turn; -+ item = g_list_next (item)) { - TurnServer *turn = item->data; - GSList *i = NULL; - - if (!nice_address_equal (message->from, &turn->server)) - continue; - -- nice_debug ("Agent %p : Packet received from TURN server candidate.", -+ nice_debug_verbose ("Agent %p : Packet received from TURN server candidate.", - agent); -+ is_turn = TRUE; - - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *cand = i->data; - - if (cand->type == NICE_CANDIDATE_TYPE_RELAYED && -+ cand->turn == turn && - cand->stream_id == stream->id && -- cand->component_id == component->id) { -+ nice_socket_is_based_on (cand->sockptr, nicesock)) { - retval = nice_udp_turn_socket_parse_recv_message (cand->sockptr, &nicesock, - message); - break; -@@ -3567,6 +3657,12 @@ agent_recv_message_unlocked ( - break; - } - -+ if (agent->force_relay && !is_turn) { -+ /* Ignore messages not from TURN if TURN is required */ -+ retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */ -+ goto done; -+ } -+ - if (retval == RECV_OOB) - goto done; - -@@ -3638,7 +3734,7 @@ agent_recv_message_unlocked ( - - /* Received data on a reliable connection. */ - -- nice_debug ("%s: notifying pseudo-TCP of packet, length %" G_GSIZE_FORMAT, -+ nice_debug_verbose ("%s: notifying pseudo-TCP of packet, length %" G_GSIZE_FORMAT, - G_STRFUNC, message->length); - pseudo_tcp_socket_notify_message (component->tcp, message); - -@@ -3672,14 +3768,14 @@ nice_debug_input_message_composition (const NiceInputMessage *messages, - { - guint i; - -- if (!nice_debug_is_enabled ()) -+ if (!nice_debug_is_verbose ()) - return; - - for (i = 0; i < n_messages; i++) { - const NiceInputMessage *message = &messages[i]; - guint j; - -- nice_debug ("Message %p (from: %p, length: %" G_GSIZE_FORMAT ")", message, -+ nice_debug_verbose ("Message %p (from: %p, length: %" G_GSIZE_FORMAT ")", message, - message->from, message->length); - - for (j = 0; -@@ -3688,7 +3784,7 @@ nice_debug_input_message_composition (const NiceInputMessage *messages, - j++) { - GInputVector *buffer = &message->buffers[j]; - -- nice_debug ("\tBuffer %p (length: %" G_GSIZE_FORMAT ")", buffer->buffer, -+ nice_debug_verbose ("\tBuffer %p (length: %" G_GSIZE_FORMAT ")", buffer->buffer, - buffer->size); - } - } -@@ -3724,7 +3820,7 @@ compact_message (const NiceOutputMessage *message, gsize buffer_length) - guint8 * - compact_input_message (const NiceInputMessage *message, gsize *buffer_length) - { -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - nice_debug_input_message_composition (message, 1); - - /* This works as long as NiceInputMessage is a subset of eNiceOutputMessage */ -@@ -3742,7 +3838,7 @@ memcpy_buffer_to_input_message (NiceInputMessage *message, - { - guint i; - -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - - message->length = 0; - -@@ -3912,7 +4008,7 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, - * - * Must be called with the io_mutex held. */ - static gint --pending_io_messages_recv_messages (Component *component, gboolean reliable, -+pending_io_messages_recv_messages (NiceComponent *component, gboolean reliable, - NiceInputMessage *messages, guint n_messages, NiceInputMessageIter *iter) - { - gsize len; -@@ -3986,8 +4082,8 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - GCancellable *cancellable, GError **error) - { - GMainContext *context; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - gint n_valid_messages = -1; - GSource *cancellable_source = NULL; - gboolean received_enough = FALSE, error_reported = FALSE; -@@ -4041,7 +4137,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - goto done; - } - -- nice_debug ("%s: %p: (%s):", G_STRFUNC, agent, -+ nice_debug_verbose ("%s: %p: (%s):", G_STRFUNC, agent, - blocking ? "blocking" : "non-blocking"); - nice_debug_input_message_composition (messages, n_messages); - -@@ -4050,8 +4146,8 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - component->recv_messages == NULL); - - /* Set the component’s receive buffer. */ -- context = component_dup_io_context (component); -- component_set_io_callback (component, NULL, NULL, messages, n_messages, -+ context = nice_component_dup_io_context (component); -+ nice_component_set_io_callback (component, NULL, NULL, messages, n_messages, - &child_error); - - /* Add the cancellable as a source. */ -@@ -4074,7 +4170,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - component->recv_messages, component->n_recv_messages, - &component->recv_messages_iter); - -- nice_debug ("%s: %p: Received %d valid messages from pending I/O buffer.", -+ nice_debug_verbose ("%s: %p: Received %d valid messages from pending I/O buffer.", - G_STRFUNC, agent, - nice_input_message_iter_get_n_valid_messages ( - &component->recv_messages_iter)); -@@ -4095,7 +4191,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - &component->recv_messages_iter, &child_error); - adjust_tcp_clock (agent, stream, component); - -- nice_debug ("%s: %p: Received %d valid messages from pseudo-TCP read " -+ nice_debug_verbose ("%s: %p: Received %d valid messages from pseudo-TCP read " - "buffer.", G_STRFUNC, agent, - nice_input_message_iter_get_n_valid_messages ( - &component->recv_messages_iter)); -@@ -4125,7 +4221,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - memcpy (&prev_recv_messages_iter, &component->recv_messages_iter, - sizeof (NiceInputMessageIter)); - -- agent_unlock_and_emit (agent); -+ agent_unlock (); - g_main_context_iteration (context, blocking); - agent_lock (); - -@@ -4159,7 +4255,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent, - nice_input_message_iter_get_n_valid_messages ( - &component->recv_messages_iter); /* grab before resetting the iter */ - -- component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); -+ nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); - - recv_error: - /* Tidy up. Below this point, @component may be %NULL. */ -@@ -4179,7 +4275,7 @@ recv_error: - n_valid_messages = -1; - } - -- nice_debug ("%s: %p: n_valid_messages: %d, n_messages: %u", G_STRFUNC, agent, -+ nice_debug_verbose ("%s: %p: n_valid_messages: %d, n_messages: %u", G_STRFUNC, agent, - n_valid_messages, n_messages); - - done: -@@ -4311,8 +4407,8 @@ nice_agent_send_messages_nonblocking_internal ( - gboolean allow_partial, - GError **error) - { -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - gint n_sent = -1; /* is in bytes if allow_partial is TRUE, - otherwise in messages */ - GError *child_error = NULL; -@@ -4335,7 +4431,7 @@ nice_agent_send_messages_nonblocking_internal ( - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (&component->selected_pair.remote->addr, tmpbuf); - -- nice_debug ("Agent %p : s%d:%d: sending %u messages to " -+ nice_debug_verbose ("Agent %p : s%d:%d: sending %u messages to " - "[%s]:%d", agent, stream_id, component_id, n_messages, tmpbuf, - nice_address_get_port (&component->selected_pair.remote->addr)); - } -@@ -4485,7 +4581,7 @@ nice_agent_send_messages_nonblocking_internal ( - n_sent = -1; - } - -- nice_debug ("%s: n_sent: %d, n_messages: %u", G_STRFUNC, -+ nice_debug_verbose ("%s: n_sent: %d, n_messages: %u", G_STRFUNC, - n_sent, n_messages); - - done: -@@ -4558,7 +4654,7 @@ nice_agent_get_local_candidates ( - guint stream_id, - guint component_id) - { -- Component *component; -+ NiceComponent *component; - GSList * ret = NULL; - GSList * item = NULL; - -@@ -4572,8 +4668,14 @@ nice_agent_get_local_candidates ( - goto done; - } - -- for (item = component->local_candidates; item; item = item->next) -- ret = g_slist_append (ret, nice_candidate_copy (item->data)); -+ for (item = component->local_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (agent->force_relay && cand->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ -+ ret = g_slist_append (ret, nice_candidate_copy (cand)); -+ } - - done: - agent_unlock_and_emit (agent); -@@ -4587,7 +4689,7 @@ nice_agent_get_remote_candidates ( - guint stream_id, - guint component_id) - { -- Component *component; -+ NiceComponent *component; - GSList *ret = NULL, *item = NULL; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); -@@ -4620,11 +4722,11 @@ nice_agent_restart ( - priv_generate_tie_breaker (agent); - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - /* step: reset local credentials for the stream and - * clean up the list of remote candidates */ -- stream_restart (agent, stream); -+ nice_stream_restart (stream, agent); - } - - agent_unlock_and_emit (agent); -@@ -4637,7 +4739,7 @@ nice_agent_restart_stream ( - guint stream_id) - { - gboolean res = FALSE; -- Stream *stream; -+ NiceStream *stream; - - agent_lock(); - -@@ -4649,7 +4751,7 @@ nice_agent_restart_stream ( - - /* step: reset local credentials for the stream and - * clean up the list of remote candidates */ -- stream_restart (agent, stream); -+ nice_stream_restart (stream, agent); - - res = TRUE; - done: -@@ -4688,10 +4790,10 @@ nice_agent_dispose (GObject *object) - - for (i = agent->streams; i; i = i->next) - { -- Stream *s = i->data; -+ NiceStream *s = i->data; - -- stream_close (s); -- stream_free (s); -+ nice_stream_close (s); -+ g_object_unref (s); - } - - g_slist_free (agent->streams); -@@ -4739,9 +4841,9 @@ gboolean - component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - { - SocketSource *socket_source = user_data; -- Component *component; -+ NiceComponent *component; - NiceAgent *agent; -- Stream *stream; -+ NiceStream *stream; - gboolean has_io_callback; - gboolean remove_source = FALSE; - -@@ -4774,20 +4876,22 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - stream->id, component->id, NICE_COMPONENT_STATE_FAILED); - } - -- component_detach_socket (component, socket_source->socket); -+ nice_component_remove_socket (component, socket_source->socket); - agent_unlock (); -+ g_object_unref (agent); - return G_SOURCE_REMOVE; - } - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - - /* Choose which receive buffer to use. If we’re reading for - * nice_agent_attach_recv(), use a local static buffer. If we’re reading for - * nice_agent_recv_messages(), use the buffer provided by the client. - * - * has_io_callback cannot change throughout this function, as we operate -- * entirely with the agent lock held, and component_set_io_callback() would -- * need to take the agent lock to change the Component’s io_callback. */ -+ * entirely with the agent lock held, and nice_component_set_io_callback() -+ * would need to take the agent lock to change the Component’s -+ * io_callback. */ - g_assert (!has_io_callback || component->recv_messages == NULL); - - if (agent->reliable && !nice_socket_is_reliable (socket_source->socket)) { -@@ -4835,7 +4939,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - retval = agent_recv_message_unlocked (agent, stream, component, - socket_source->socket, &local_message); - -- nice_debug ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT -+ nice_debug_verbose ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT - " bytes", G_STRFUNC, agent, retval, local_message.length); - - /* Don’t expect any valid messages to escape pseudo_tcp_socket_readable() -@@ -4852,7 +4956,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - break; - } - -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - } - } else if (has_io_callback) { - while (has_io_callback) { -@@ -4865,7 +4969,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - retval = agent_recv_message_unlocked (agent, stream, component, - socket_source->socket, &local_message); - -- nice_debug ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT -+ nice_debug_verbose ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT - " bytes", G_STRFUNC, agent, retval, local_message.length); - - if (retval == RECV_WOULD_BLOCK) { -@@ -4879,13 +4983,13 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - } - - if (retval == RECV_SUCCESS && local_message.length > 0) -- component_emit_io_callback (component, local_buf, local_message.length); -+ nice_component_emit_io_callback (component, local_buf, local_message.length); - - if (g_source_is_destroyed (g_main_current_source ())) { - nice_debug ("Component IO source disappeared during the callback"); - goto out; - } -- has_io_callback = component_has_io_callback (component); -+ has_io_callback = nice_component_has_io_callback (component); - } - } else if (component->recv_messages != NULL) { - RecvStatus retval; -@@ -4906,7 +5010,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - socket_source->socket, - &component->recv_messages[component->recv_messages_iter.message]); - -- nice_debug ("%s: %p: received %d valid messages", G_STRFUNC, agent, -+ nice_debug_verbose ("%s: %p: received %d valid messages", G_STRFUNC, agent, - retval); - - if (retval == RECV_SUCCESS) { -@@ -4931,6 +5035,10 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data) - } - - done: -+ -+ if (remove_source) -+ nice_component_remove_socket (component, socket_source->socket); -+ - /* If we’re in the middle of a read, don’t emit any signals, or we could cause - * re-entrancy by (e.g.) emitting component-state-changed and having the - * client perform a read. */ -@@ -4946,6 +5054,7 @@ done: - - out: - g_object_unref (agent); -+ - agent_unlock_and_emit (agent); - return G_SOURCE_REMOVE; - } -@@ -4959,8 +5068,8 @@ nice_agent_attach_recv ( - NiceAgentRecvFunc func, - gpointer data) - { -- Component *component = NULL; -- Stream *stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *stream = NULL; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -4982,8 +5091,8 @@ nice_agent_attach_recv ( - ctx = g_main_context_default (); - - /* Set the component’s I/O context. */ -- component_set_io_context (component, ctx); -- component_set_io_callback (component, func, data, NULL, 0, NULL); -+ nice_component_set_io_context (component, ctx); -+ nice_component_set_io_callback (component, func, data, NULL, 0, NULL); - ret = TRUE; - - if (func) { -@@ -5011,8 +5120,8 @@ nice_agent_set_selected_pair ( - const gchar *lfoundation, - const gchar *rfoundation) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - CandidatePair pair; - gboolean ret = FALSE; - -@@ -5029,7 +5138,7 @@ nice_agent_set_selected_pair ( - goto done; - } - -- if (!component_find_pair (component, agent, lfoundation, rfoundation, &pair)){ -+ if (!nice_component_find_pair (component, agent, lfoundation, rfoundation, &pair)){ - goto done; - } - -@@ -5044,11 +5153,22 @@ nice_agent_set_selected_pair ( - goto done; - } - -- /* step: change component state */ -- agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY); -+ /* step: change component state; we could be in STATE_DISCONNECTED; skip -+ * STATE_GATHERING and continue through the states to give client code a nice -+ * logical progression. See
http://phabricator.freedesktop.org/D218
for -+ * discussion. */ -+ if (component->state < NICE_COMPONENT_STATE_CONNECTING || -+ component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state < NICE_COMPONENT_STATE_CONNECTED) -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_CONNECTED); -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_READY); - - /* step: set the selected pair */ -- component_update_selected_pair (component, &pair); -+ nice_component_update_selected_pair (component, &pair); - agent_signal_new_selected_pair (agent, stream_id, component_id, - pair.local, pair.remote); - -@@ -5063,8 +5183,8 @@ NICEAPI_EXPORT gboolean - nice_agent_get_selected_pair (NiceAgent *agent, guint stream_id, - guint component_id, NiceCandidate **local, NiceCandidate **remote) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - gboolean ret = FALSE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -5096,8 +5216,8 @@ NICEAPI_EXPORT GSocket * - nice_agent_get_selected_socket (NiceAgent *agent, guint stream_id, - guint component_id) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceSocket *nice_socket; - GSocket *g_socket = NULL; - -@@ -5176,8 +5296,8 @@ nice_agent_set_selected_remote_candidate ( - guint component_id, - NiceCandidate *candidate) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceCandidate *lcandidate = NULL; - gboolean ret = FALSE; - NiceCandidate *local = NULL, *remote = NULL; -@@ -5204,7 +5324,7 @@ nice_agent_set_selected_remote_candidate ( - priority = component->selected_pair.priority; - - /* step: set the selected pair */ -- lcandidate = component_set_selected_remote_candidate (agent, component, -+ lcandidate = nice_component_set_selected_remote_candidate (component, agent, - candidate); - if (!lcandidate) - goto done; -@@ -5222,8 +5342,19 @@ nice_agent_set_selected_remote_candidate ( - goto done; - } - -- /* step: change component state */ -- agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY); -+ /* step: change component state; we could be in STATE_DISCONNECTED; skip -+ * STATE_GATHERING and continue through the states to give client code a nice -+ * logical progression. See
http://phabricator.freedesktop.org/D218
for -+ * discussion. */ -+ if (component->state < NICE_COMPONENT_STATE_CONNECTING || -+ component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state < NICE_COMPONENT_STATE_CONNECTED) -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_CONNECTED); -+ agent_signal_component_state_change (agent, stream_id, component_id, -+ NICE_COMPONENT_STATE_READY); - - agent_signal_new_selected_pair (agent, stream_id, component_id, - lcandidate, candidate); -@@ -5261,7 +5392,7 @@ nice_agent_set_stream_tos (NiceAgent *agent, - guint stream_id, gint tos) - { - GSList *i, *j; -- Stream *stream; -+ NiceStream *stream; - - g_return_if_fail (NICE_IS_AGENT (agent)); - g_return_if_fail (stream_id >= 1); -@@ -5274,7 +5405,7 @@ nice_agent_set_stream_tos (NiceAgent *agent, - - stream->tos = tos; - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - - for (j = component->local_candidates; j; j = j->next) { - NiceCandidate *local_candidate = j->data; -@@ -5308,7 +5439,7 @@ NICEAPI_EXPORT gboolean - nice_agent_set_stream_name (NiceAgent *agent, guint stream_id, - const gchar *name) - { -- Stream *stream_to_name = NULL; -+ NiceStream *stream_to_name = NULL; - GSList *i; - gboolean ret = FALSE; - -@@ -5329,16 +5460,14 @@ nice_agent_set_stream_name (NiceAgent *agent, guint stream_id, - - agent_lock(); - -- if (name != NULL) { -- for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ for (i = agent->streams; i; i = i->next) { -+ NiceStream *stream = i->data; - -- if (stream->id != stream_id && -- g_strcmp0 (stream->name, name) == 0) -- goto done; -- else if (stream->id == stream_id) -- stream_to_name = stream; -- } -+ if (stream->id != stream_id && -+ g_strcmp0 (stream->name, name) == 0) -+ goto done; -+ else if (stream->id == stream_id) -+ stream_to_name = stream; - } - - if (stream_to_name == NULL) -@@ -5358,7 +5487,7 @@ nice_agent_set_stream_name (NiceAgent *agent, guint stream_id, - NICEAPI_EXPORT const gchar * - nice_agent_get_stream_name (NiceAgent *agent, guint stream_id) - { -- Stream *stream; -+ NiceStream *stream; - gchar *name = NULL; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); -@@ -5379,14 +5508,14 @@ nice_agent_get_stream_name (NiceAgent *agent, guint stream_id) - - static NiceCandidate * - _get_default_local_candidate_locked (NiceAgent *agent, -- Stream *stream, Component *component) -+ NiceStream *stream, NiceComponent *component) - { - GSList *i; - NiceCandidate *default_candidate = NULL; - NiceCandidate *default_rtp_candidate = NULL; - - if (component->id != NICE_COMPONENT_TYPE_RTP) { -- Component *rtp_component; -+ NiceComponent *rtp_component; - - if (!agent_find_component (agent, stream->id, NICE_COMPONENT_TYPE_RTP, - NULL, &rtp_component)) -@@ -5402,6 +5531,10 @@ _get_default_local_candidate_locked (NiceAgent *agent, - for (i = component->local_candidates; i; i = i->next) { - NiceCandidate *local_candidate = i->data; - -+ if (agent->force_relay && -+ local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - /* Only check for ipv4 candidates */ - if (nice_address_ip_version (&local_candidate->addr) != 4) - continue; -@@ -5426,8 +5559,8 @@ NICEAPI_EXPORT NiceCandidate * - nice_agent_get_default_local_candidate (NiceAgent *agent, - guint stream_id, guint component_id) - { -- Stream *stream = NULL; -- Component *component = NULL; -+ NiceStream *stream = NULL; -+ NiceComponent *component = NULL; - NiceCandidate *default_candidate = NULL; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); -@@ -5526,7 +5659,7 @@ _generate_candidate_sdp (NiceAgent *agent, - } - - static void --_generate_stream_sdp (NiceAgent *agent, Stream *stream, -+_generate_stream_sdp (NiceAgent *agent, NiceStream *stream, - GString *sdp, gboolean include_non_ice) - { - GSList *i, *j; -@@ -5542,7 +5675,7 @@ _generate_stream_sdp (NiceAgent *agent, Stream *stream, - - /* Find default candidates */ - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - NiceCandidate *default_candidate; - - if (component->id == NICE_COMPONENT_TYPE_RTP) { -@@ -5571,11 +5704,14 @@ _generate_stream_sdp (NiceAgent *agent, Stream *stream, - g_string_append_printf (sdp, "a=ice-pwd:%s\n", stream->local_password); - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - - for (j = component->local_candidates; j; j = j->next) { - NiceCandidate *candidate = j->data; - -+ if (agent->force_relay && candidate->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - _generate_candidate_sdp (agent, candidate, sdp); - g_string_append (sdp, "\n"); - } -@@ -5593,7 +5729,7 @@ nice_agent_generate_local_sdp (NiceAgent *agent) - agent_lock(); - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - _generate_stream_sdp (agent, stream, sdp, TRUE); - } -@@ -5609,7 +5745,7 @@ nice_agent_generate_local_stream_sdp (NiceAgent *agent, guint stream_id, - { - GString *sdp = NULL; - gchar *ret = NULL; -- Stream *stream; -+ NiceStream *stream; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); - g_return_val_if_fail (stream_id >= 1, NULL); -@@ -5652,7 +5788,7 @@ nice_agent_generate_local_candidate_sdp (NiceAgent *agent, - NICEAPI_EXPORT gint - nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp) - { -- Stream *current_stream = NULL; -+ NiceStream *current_stream = NULL; - gchar **sdp_lines = NULL; - GSList *l, *stream_item = NULL; - gint i; -@@ -5664,7 +5800,7 @@ nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp) - agent_lock(); - - for (l = agent->streams; l; l = l->next) { -- Stream *stream = l->data; -+ NiceStream *stream = l->data; - - if (stream->name == NULL) { - ret = -1; -@@ -5701,7 +5837,7 @@ nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp) - NICE_STREAM_MAX_PWD); - } else if (g_str_has_prefix (sdp_lines[i], "a=candidate:")) { - NiceCandidate *candidate = NULL; -- Component *component = NULL; -+ NiceComponent *component = NULL; - GSList *cands = NULL; - gint added; - -@@ -5744,7 +5880,7 @@ NICEAPI_EXPORT GSList * - nice_agent_parse_remote_stream_sdp (NiceAgent *agent, guint stream_id, - const gchar *sdp, gchar **ufrag, gchar **pwd) - { -- Stream *stream = NULL; -+ NiceStream *stream = NULL; - gchar **sdp_lines = NULL; - GSList *candidates = NULL; - gint i; -@@ -5924,7 +6060,7 @@ nice_agent_get_io_stream (NiceAgent *agent, guint stream_id, - guint component_id) - { - GIOStream *iostream = NULL; -- Component *component; -+ NiceComponent *component; - - g_return_val_if_fail (NICE_IS_AGENT (agent), NULL); - g_return_val_if_fail (stream_id >= 1, NULL); -@@ -5951,7 +6087,7 @@ nice_agent_get_io_stream (NiceAgent *agent, guint stream_id, - NICEAPI_EXPORT gboolean - nice_agent_forget_relays (NiceAgent *agent, guint stream_id, guint component_id) - { -- Component *component; -+ NiceComponent *component; - gboolean ret = TRUE; - - g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); -@@ -5965,7 +6101,7 @@ nice_agent_forget_relays (NiceAgent *agent, guint stream_id, guint component_id) - goto done; - } - -- component_clean_turn_servers (component); -+ nice_component_clean_turn_servers (component); - - done: - agent_unlock_and_emit (agent); -@@ -6013,7 +6149,7 @@ nice_agent_get_component_state (NiceAgent *agent, - guint stream_id, guint component_id) - { - NiceComponentState state = NICE_COMPONENT_STATE_FAILED; -- Component *component; -+ NiceComponent *component; - - agent_lock (); - -diff --git a/agent/candidate.c b/agent/candidate.c -index a472c4a..68c7714 100644 ---- a/agent/candidate.c -+++ b/agent/candidate.c -@@ -52,6 +52,7 @@ - - #include "agent.h" - #include "component.h" -+#include "interfaces.h" - - G_DEFINE_BOXED_TYPE (NiceCandidate, nice_candidate, nice_candidate_copy, - nice_candidate_free); -@@ -144,6 +145,43 @@ nice_candidate_ice_local_preference_full (guint direction_preference, - other_preference); - } - -+static guint8 -+nice_candidate_ip_local_preference (const NiceCandidate *candidate) -+{ -+ guint8 preference = 0; -+ gchar ip_string[INET6_ADDRSTRLEN]; -+ GList/*<owned gchar*>*/ *ips = NULL; -+ GList/*<unowned gchar*>*/ *iter; -+ -+ /* Ensure otherwise identical host candidates with only different IP addresses -+ * (multihomed host) get assigned different priorities. Position of the IP in -+ * the list obtained from nice_interfaces_get_local_ips() serves here as the -+ * distinguishing value of other_preference. Reflexive and relayed candidates -+ * are likewise differentiated by their base address. -+ * -+ * This is required by RFC 5245 Section 4.1.2.1: -+ *
https://tools.ietf.org/html/rfc5245#section-4.1.2.1
-+ */ -+ if (candidate->type == NICE_CANDIDATE_TYPE_HOST) { -+ nice_address_to_string (&candidate->addr, ip_string); -+ } else { -+ nice_address_to_string (&candidate->base_addr, ip_string); -+ } -+ -+ ips = nice_interfaces_get_local_ips (TRUE); -+ -+ for (iter = ips; iter; iter = g_list_next (iter)) { -+ if (g_strcmp0 (ip_string, iter->data) == 0) { -+ break; -+ } -+ ++preference; -+ } -+ -+ g_list_free_full (ips, g_free); -+ -+ return preference; -+} -+ - static guint16 - nice_candidate_ice_local_preference (const NiceCandidate *candidate) - { -@@ -178,7 +216,8 @@ nice_candidate_ice_local_preference (const NiceCandidate *candidate) - break; - } - -- return nice_candidate_ice_local_preference_full (direction_preference, 1); -+ return nice_candidate_ice_local_preference_full (direction_preference, -+ nice_candidate_ip_local_preference (candidate)); - } - - static guint32 -@@ -214,7 +253,7 @@ nice_candidate_ms_ice_local_preference (const NiceCandidate *candidate) - } - - return nice_candidate_ms_ice_local_preference_full(transport_preference, -- direction_preference, 0); -+ direction_preference, nice_candidate_ip_local_preference (candidate)); - } - - static guint8 -@@ -238,7 +277,10 @@ nice_candidate_ice_type_preference (const NiceCandidate *candidate, - type_preference = NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE; - break; - case NICE_CANDIDATE_TYPE_RELAYED: -- type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED; -+ if (candidate->turn->type == NICE_RELAY_TYPE_TURN_UDP) -+ type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP; -+ else -+ type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED; - break; - default: - type_preference = 0; -diff --git a/agent/candidate.h b/agent/candidate.h -index 5e0eaf3..fadfce3 100644 ---- a/agent/candidate.h -+++ b/agent/candidate.h -@@ -64,8 +64,8 @@ G_BEGIN_DECLS - #define NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE 110 - #define NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED 105 - #define NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE 100 --#define NICE_CANDIDATE_TYPE_PREF_UDP_TUNNELED 75 --#define NICE_CANDIDATE_TYPE_PREF_RELAYED 10 -+#define NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP 30 -+#define NICE_CANDIDATE_TYPE_PREF_RELAYED 20 - - /* Priority preference constants for MS-ICE compatibility */ - #define NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP 15 -diff --git a/agent/component.c b/agent/component.c -index 1a1f84a..d38d3e5 100644 ---- a/agent/component.c -+++ b/agent/component.c -@@ -59,11 +59,33 @@ static volatile unsigned int n_components_destroyed = 0; - #include "discovery.h" - #include "agent-priv.h" - -+G_DEFINE_TYPE (NiceComponent, nice_component, G_TYPE_OBJECT); - -+typedef enum { -+ PROP_ID = 1, -+ PROP_AGENT, -+ PROP_STREAM, -+} NiceComponentProperty; -+ -+static void -+nice_component_constructed (GObject *obj); - static void --component_schedule_io_callback (Component *component); -+nice_component_get_property (GObject *obj, -+ guint property_id, GValue *value, GParamSpec *pspec); - static void --component_deschedule_io_callback (Component *component); -+nice_component_set_property (GObject *obj, -+ guint property_id, const GValue *value, GParamSpec *pspec); -+static void -+nice_component_finalize (GObject *obj); -+ -+static void -+nice_component_schedule_io_callback (NiceComponent *component); -+static void -+nice_component_deschedule_io_callback (NiceComponent *component); -+static void -+nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock); -+static void -+nice_component_clear_selected_pair (NiceComponent *component); - - - void -@@ -74,12 +96,15 @@ incoming_check_free (IncomingCheck *icheck) - } - - /* Must *not* take the agent lock, since it’s called from within -- * component_set_io_context(), which holds the Component’s I/O lock. */ -+ * nice_component_set_io_context(), which holds the Component’s I/O lock. */ - static void - socket_source_attach (SocketSource *socket_source, GMainContext *context) - { - GSource *source; - -+ if (socket_source->socket->fileno == NULL) -+ return; -+ - /* Create a source. */ - source = g_socket_create_source (socket_source->socket->fileno, - G_IO_IN, NULL); -@@ -121,50 +146,57 @@ socket_source_free (SocketSource *source) - g_slice_free (SocketSource, source); - } - --Component * --component_new (guint id, NiceAgent *agent, Stream *stream) -+NiceComponent * -+nice_component_new (guint id, NiceAgent *agent, NiceStream *stream) - { -- Component *component; -- -- g_atomic_int_inc (&n_components_created); -- nice_debug ("Created NiceComponent (%u created, %u destroyed)", -- n_components_created, n_components_destroyed); -+ return g_object_new (NICE_TYPE_COMPONENT, -+ "id", id, -+ "agent", agent, -+ "stream", stream, -+ NULL); -+} - -- component = g_slice_new0 (Component); -- component->id = id; -- component->state = NICE_COMPONENT_STATE_DISCONNECTED; -- component->restart_candidate = NULL; -- component->tcp = NULL; -- component->agent = agent; -- component->stream = stream; -+void -+nice_component_remove_socket (NiceComponent *cmp, NiceSocket *nsocket) -+{ -+ GSList *i; - -- nice_agent_init_stun_agent (agent, &component->stun_agent); -+ for (i = cmp->local_candidates; i;) { -+ NiceCandidate *candidate = i->data; -+ GSList *next = i->next; - -- g_mutex_init (&component->io_mutex); -- g_queue_init (&component->pending_io_messages); -- component->io_callback_id = 0; -+ if (!nice_socket_is_based_on (candidate->sockptr, nsocket)) { -+ i = next; -+ continue; -+ } - -- component->own_ctx = g_main_context_new (); -- component->stop_cancellable = g_cancellable_new (); -- component->stop_cancellable_source = -- g_cancellable_source_new (component->stop_cancellable); -- g_source_set_dummy_callback (component->stop_cancellable_source); -- g_source_attach (component->stop_cancellable_source, component->own_ctx); -- component->ctx = g_main_context_ref (component->own_ctx); -+ if (candidate == cmp->selected_pair.local) { -+ nice_component_clear_selected_pair (cmp); -+ agent_signal_component_state_change (cmp->agent, cmp->stream->id, -+ cmp->id, NICE_COMPONENT_STATE_FAILED); -+ } - -- /* Start off with a fresh main context and all I/O paused. This -- * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages() -- * are called. */ -- component_set_io_context (component, NULL); -- component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); -+ refresh_prune_candidate (cmp->agent, candidate); -+ if (candidate->sockptr != nsocket) { -+ discovery_prune_socket (cmp->agent, candidate->sockptr); -+ conn_check_prune_socket (cmp->agent, cmp->stream, cmp, -+ candidate->sockptr); -+ nice_component_detach_socket (cmp, candidate->sockptr); -+ } -+ agent_remove_local_candidate (cmp->agent, candidate); -+ nice_candidate_free (candidate); - -- g_queue_init (&component->queued_tcp_packets); -+ cmp->local_candidates = g_slist_delete_link (cmp->local_candidates, i); -+ i = next; -+ } - -- return component; -+ discovery_prune_socket (cmp->agent, nsocket); -+ conn_check_prune_socket (cmp->agent, cmp->stream, cmp, nsocket); -+ nice_component_detach_socket (cmp, nsocket); - } - - void --component_clean_turn_servers (Component *cmp) -+nice_component_clean_turn_servers (NiceComponent *cmp) - { - GSList *i; - -@@ -195,7 +227,7 @@ component_clean_turn_servers (Component *cmp) - discovery_prune_socket (cmp->agent, cmp->turn_candidate->sockptr); - conn_check_prune_socket (cmp->agent, cmp->stream, cmp, - cmp->turn_candidate->sockptr); -- component_detach_socket (cmp, cmp->turn_candidate->sockptr); -+ nice_component_detach_socket (cmp, cmp->turn_candidate->sockptr); - nice_candidate_free (cmp->turn_candidate); - } - /* Bring the priority down to 0, so that it will be replaced -@@ -208,7 +240,7 @@ component_clean_turn_servers (Component *cmp) - discovery_prune_socket (cmp->agent, candidate->sockptr); - conn_check_prune_socket (cmp->agent, cmp->stream, cmp, - candidate->sockptr); -- component_detach_socket (cmp, candidate->sockptr); -+ nice_component_detach_socket (cmp, candidate->sockptr); - agent_remove_local_candidate (cmp->agent, candidate); - nice_candidate_free (candidate); - } -@@ -218,7 +250,7 @@ component_clean_turn_servers (Component *cmp) - } - - static void --component_clear_selected_pair (Component *component) -+nice_component_clear_selected_pair (NiceComponent *component) - { - if (component->selected_pair.keepalive.tick_source != NULL) { - g_source_destroy (component->selected_pair.keepalive.tick_source); -@@ -232,7 +264,7 @@ component_clear_selected_pair (Component *component) - /* Must be called with the agent lock held as it touches internal Component - * state. */ - void --component_close (Component *cmp) -+nice_component_close (NiceComponent *cmp) - { - IOCallbackData *data; - GOutputVector *vec; -@@ -240,13 +272,13 @@ component_close (Component *cmp) - /* Start closing the pseudo-TCP socket first. FIXME: There is a very big and - * reliably triggerable race here. pseudo_tcp_socket_close() does not block - * on the socket closing — it only sends the first packet of the FIN -- * handshake. component_close() will immediately afterwards close the -+ * handshake. nice_component_close() will immediately afterwards close the - * underlying component sockets, aborting the handshake. - * - * On the principle that starting the FIN handshake is better than not - * starting it, even if it’s later truncated, call pseudo_tcp_socket_close(). -- * A long-term fix is needed in the form of making component_close() (and all -- * its callers) async, so we can properly block on closure. */ -+ * A long-term fix is needed in the form of making nice_component_close() (and -+ * all its callers) async, so we can properly block on closure. */ - if (cmp->tcp) { - pseudo_tcp_socket_close (cmp->tcp, TRUE); - } -@@ -269,12 +301,12 @@ component_close (Component *cmp) - g_slist_free_full (cmp->remote_candidates, - (GDestroyNotify) nice_candidate_free); - cmp->remote_candidates = NULL; -- component_free_socket_sources (cmp); -+ nice_component_free_socket_sources (cmp); - g_slist_free_full (cmp->incoming_checks, - (GDestroyNotify) incoming_check_free); - cmp->incoming_checks = NULL; - -- component_clean_turn_servers (cmp); -+ nice_component_clean_turn_servers (cmp); - - if (cmp->tcp_clock) { - g_source_destroy (cmp->tcp_clock); -@@ -289,7 +321,7 @@ component_close (Component *cmp) - while ((data = g_queue_pop_head (&cmp->pending_io_messages)) != NULL) - io_callback_data_free (data); - -- component_deschedule_io_callback (cmp); -+ nice_component_deschedule_io_callback (cmp); - - g_cancellable_cancel (cmp->stop_cancellable); - -@@ -299,47 +331,13 @@ component_close (Component *cmp) - } - } - --/* Must be called with the agent lock released as it could dispose of -- * NiceIOStreams. */ --void --component_free (Component *cmp) --{ -- /* Component should have been closed already. */ -- g_warn_if_fail (cmp->local_candidates == NULL); -- g_warn_if_fail (cmp->remote_candidates == NULL); -- g_warn_if_fail (cmp->incoming_checks == NULL); -- -- g_clear_object (&cmp->tcp); -- g_clear_object (&cmp->stop_cancellable); -- g_clear_object (&cmp->iostream); -- g_mutex_clear (&cmp->io_mutex); -- -- if (cmp->stop_cancellable_source != NULL) { -- g_source_destroy (cmp->stop_cancellable_source); -- g_source_unref (cmp->stop_cancellable_source); -- } -- -- if (cmp->ctx != NULL) { -- g_main_context_unref (cmp->ctx); -- cmp->ctx = NULL; -- } -- -- g_main_context_unref (cmp->own_ctx); -- -- g_slice_free (Component, cmp); -- -- g_atomic_int_inc (&n_components_destroyed); -- nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)", -- n_components_created, n_components_destroyed); --} -- - /* - * Finds a candidate pair that has matching foundation ids. - * - * @return TRUE if pair found, pointer to pair stored at 'pair' - */ - gboolean --component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair) -+nice_component_find_pair (NiceComponent *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair) - { - GSList *i; - CandidatePair result = { 0, }; -@@ -375,7 +373,7 @@ component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, - * session. - */ - void --component_restart (Component *cmp) -+nice_component_restart (NiceComponent *cmp) - { - GSList *i; - -@@ -410,7 +408,8 @@ component_restart (Component *cmp) - * Changes the selected pair for the component to 'pair'. Does not - * emit the "selected-pair-changed" signal. - */ --void component_update_selected_pair (Component *component, const CandidatePair *pair) -+void -+nice_component_update_selected_pair (NiceComponent *component, const CandidatePair *pair) - { - g_assert (component); - g_assert (pair); -@@ -425,17 +424,17 @@ void component_update_selected_pair (Component *component, const CandidatePair * - component->turn_candidate->sockptr); - conn_check_prune_socket (component->agent, component->stream, component, - component->turn_candidate->sockptr); -- component_detach_socket (component, component->turn_candidate->sockptr); -+ nice_component_detach_socket (component, component->turn_candidate->sockptr); - nice_candidate_free (component->turn_candidate); - component->turn_candidate = NULL; - } - -- component_clear_selected_pair (component); -+ nice_component_clear_selected_pair (component); - - component->selected_pair.local = pair->local; - component->selected_pair.remote = pair->remote; - component->selected_pair.priority = pair->priority; -- -+ component->selected_pair.prflx_priority = pair->prflx_priority; - } - - /* -@@ -445,7 +444,7 @@ void component_update_selected_pair (Component *component, const CandidatePair * - * @return pointer to candidate or NULL if not found - */ - NiceCandidate * --component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport) -+nice_component_find_remote_candidate (NiceComponent *component, const NiceAddress *addr, NiceCandidateTransport transport) - { - GSList *i; - -@@ -469,8 +468,8 @@ component_find_remote_candidate (const Component *component, const NiceAddress * - */ - - NiceCandidate * --component_set_selected_remote_candidate (NiceAgent *agent, Component *component, -- NiceCandidate *candidate) -+nice_component_set_selected_remote_candidate (NiceComponent *component, -+ NiceAgent *agent, NiceCandidate *candidate) - { - NiceCandidate *local = NULL; - NiceCandidate *remote = NULL; -@@ -483,7 +482,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component, - NiceCandidate *tmp = item->data; - guint64 tmp_prio = 0; - -- if (tmp->transport != candidate->transport || -+ if (tmp->transport != conn_check_match_transport(candidate->transport) || - tmp->addr.s.addr.sa_family != candidate->addr.s.addr.sa_family || - tmp->type != NICE_CANDIDATE_TYPE_HOST) - continue; -@@ -499,7 +498,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component, - if (local == NULL) - return NULL; - -- remote = component_find_remote_candidate (component, &candidate->addr, -+ remote = nice_component_find_remote_candidate (component, &candidate->addr, - candidate->transport); - - if (!remote) { -@@ -509,7 +508,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component, - agent_signal_new_remote_candidate (agent, remote); - } - -- component_clear_selected_pair (component); -+ nice_component_clear_selected_pair (component); - - component->selected_pair.local = local; - component->selected_pair.remote = remote; -@@ -530,7 +529,7 @@ _find_socket_source (gconstpointer a, gconstpointer b) - /* This takes ownership of the socket. - * It creates and attaches a source to the component’s context. */ - void --component_attach_socket (Component *component, NiceSocket *nicesock) -+nice_component_attach_socket (NiceComponent *component, NiceSocket *nicesock) - { - GSList *l; - SocketSource *socket_source; -@@ -540,9 +539,6 @@ component_attach_socket (Component *component, NiceSocket *nicesock) - - g_assert (component->ctx != NULL); - -- if (nicesock->fileno == NULL) -- return; -- - /* Find an existing SocketSource in the component which contains @socket, or - * create a new one. - * -@@ -559,7 +555,8 @@ component_attach_socket (Component *component, NiceSocket *nicesock) - socket_source->component = component; - component->socket_sources = - g_slist_prepend (component->socket_sources, socket_source); -- component->socket_sources_age++; -+ if (nicesock->fileno != NULL) -+ component->socket_sources_age++; - } - - /* Create and attach a source */ -@@ -571,9 +568,9 @@ component_attach_socket (Component *component, NiceSocket *nicesock) - /* Reattaches socket handles of @component to the main context. - * - * Must *not* take the agent lock, since it’s called from within -- * component_set_io_context(), which holds the Component’s I/O lock. */ -+ * nice_component_set_io_context(), which holds the Component’s I/O lock. */ - static void --component_reattach_all_sockets (Component *component) -+nice_component_reattach_all_sockets (NiceComponent *component) - { - GSList *i; - -@@ -586,8 +583,8 @@ component_reattach_all_sockets (Component *component) - } - - /** -- * component_detach_socket: -- * @component: a #Component -+ * nice_component_detach_socket: -+ * @component: a #NiceComponent - * @socket: the socket to detach the source for - * - * Detach the #GSource for the single specified @socket. It also closes it -@@ -595,8 +592,8 @@ component_reattach_all_sockets (Component *component) - * - * If the @socket doesn’t exist in this @component, do nothing. - */ --void --component_detach_socket (Component *component, NiceSocket *nicesock) -+static void -+nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock) - { - GSList *l; - SocketSource *socket_source; -@@ -637,10 +634,10 @@ component_detach_socket (Component *component, NiceSocket *nicesock) - * sockets themselves untouched. - * - * Must *not* take the agent lock, since it’s called from within -- * component_set_io_context(), which holds the Component’s I/O lock. -+ * nice_component_set_io_context(), which holds the Component’s I/O lock. - */ - void --component_detach_all_sockets (Component *component) -+nice_component_detach_all_sockets (NiceComponent *component) - { - GSList *i; - -@@ -653,7 +650,7 @@ component_detach_all_sockets (Component *component) - } - - void --component_free_socket_sources (Component *component) -+nice_component_free_socket_sources (NiceComponent *component) - { - nice_debug ("Free socket sources for component %p.", component); - -@@ -662,11 +659,11 @@ component_free_socket_sources (Component *component) - component->socket_sources = NULL; - component->socket_sources_age++; - -- component_clear_selected_pair (component); -+ nice_component_clear_selected_pair (component); - } - - GMainContext * --component_dup_io_context (Component *component) -+nice_component_dup_io_context (NiceComponent *component) - { - return g_main_context_ref (component->own_ctx); - } -@@ -674,7 +671,7 @@ component_dup_io_context (Component *component) - /* If @context is %NULL, it's own context is used, so component->ctx is always - * guaranteed to be non-%NULL. */ - void --component_set_io_context (Component *component, GMainContext *context) -+nice_component_set_io_context (NiceComponent *component, GMainContext *context) - { - g_mutex_lock (&component->io_mutex); - -@@ -684,11 +681,11 @@ component_set_io_context (Component *component, GMainContext *context) - else - g_main_context_ref (context); - -- component_detach_all_sockets (component); -+ nice_component_detach_all_sockets (component); - g_main_context_unref (component->ctx); - - component->ctx = context; -- component_reattach_all_sockets (component); -+ nice_component_reattach_all_sockets (component); - } - - g_mutex_unlock (&component->io_mutex); -@@ -705,7 +702,7 @@ component_set_io_context (Component *component, GMainContext *context) - * emitted for it (which could cause data loss if the I/O callback function was - * unset in that time). */ - void --component_set_io_callback (Component *component, -+nice_component_set_io_callback (NiceComponent *component, - NiceAgentRecvFunc func, gpointer user_data, - NiceInputMessage *recv_messages, guint n_recv_messages, - GError **error) -@@ -722,14 +719,14 @@ component_set_io_callback (Component *component, - component->recv_messages = NULL; - component->n_recv_messages = 0; - -- component_schedule_io_callback (component); -+ nice_component_schedule_io_callback (component); - } else { - component->io_callback = NULL; - component->io_user_data = NULL; - component->recv_messages = recv_messages; - component->n_recv_messages = n_recv_messages; - -- component_deschedule_io_callback (component); -+ nice_component_deschedule_io_callback (component); - } - - nice_input_message_iter_reset (&component->recv_messages_iter); -@@ -739,7 +736,7 @@ component_set_io_callback (Component *component, - } - - gboolean --component_has_io_callback (Component *component) -+nice_component_has_io_callback (NiceComponent *component) - { - gboolean has_io_callback; - -@@ -775,7 +772,7 @@ io_callback_data_free (IOCallbackData *data) - static gboolean - emit_io_callback_cb (gpointer user_data) - { -- Component *component = user_data; -+ NiceComponent *component = user_data; - IOCallbackData *data; - NiceAgentRecvFunc io_callback; - gpointer io_user_data; -@@ -792,7 +789,7 @@ emit_io_callback_cb (gpointer user_data) - g_mutex_lock (&component->io_mutex); - - /* The members of Component are guaranteed not to have changed since this -- * GSource was attached in component_emit_io_callback(). The Component’s agent -+ * GSource was attached in nice_component_emit_io_callback(). The Component’s agent - * and stream are immutable after construction, as are the stream and - * component IDs. The callback and its user data may have changed, but are - * guaranteed to be non-%NULL at the start as the idle source is removed when -@@ -802,7 +799,7 @@ emit_io_callback_cb (gpointer user_data) - * - * If the component is destroyed (which happens if the agent or stream are - * destroyed) between attaching the GSource and firing it, the GSource is -- * detached in component_free() and this callback is never invoked. If the -+ * detached during dispose and this callback is never invoked. If the - * agent is destroyed during an io_callback, its weak pointer will be - * nullified. Similarly, the Component needs to be re-queried for after every - * iteration, just in case the client has removed the stream in the -@@ -845,7 +842,7 @@ emit_io_callback_cb (gpointer user_data) - - /* This must be called with the agent lock *held*. */ - void --component_emit_io_callback (Component *component, -+nice_component_emit_io_callback (NiceComponent *component, - const guint8 *buf, gsize buf_len) - { - NiceAgent *agent; -@@ -897,7 +894,7 @@ component_emit_io_callback (Component *component, - - nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - -- component_schedule_io_callback (component); -+ nice_component_schedule_io_callback (component); - - g_mutex_unlock (&component->io_mutex); - } -@@ -905,7 +902,7 @@ component_emit_io_callback (Component *component, - - /* Note: Must be called with the io_mutex held. */ - static void --component_schedule_io_callback (Component *component) -+nice_component_schedule_io_callback (NiceComponent *component) - { - GSource *source; - -@@ -928,7 +925,7 @@ component_schedule_io_callback (Component *component) - - /* Note: Must be called with the io_mutex held. */ - static void --component_deschedule_io_callback (Component *component) -+nice_component_deschedule_io_callback (NiceComponent *component) - { - /* Already descheduled? */ - if (component->io_callback_id == 0) -@@ -938,6 +935,202 @@ component_deschedule_io_callback (Component *component) - component->io_callback_id = 0; - } - -+static void -+nice_component_class_init (NiceComponentClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ -+ object_class->constructed = nice_component_constructed; -+ object_class->get_property = nice_component_get_property; -+ object_class->set_property = nice_component_set_property; -+ object_class->finalize = nice_component_finalize; -+ -+ /** -+ * NiceComponent:id: -+ * -+ * The unique numeric ID of the component. -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (object_class, PROP_ID, -+ g_param_spec_uint ( -+ "id", -+ "ID", -+ "The unique numeric ID of the component.", -+ 1, G_MAXUINT, 1, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ -+ /** -+ * NiceComponent:agent: -+ * -+ * The #NiceAgent this component belongs to. -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (object_class, PROP_AGENT, -+ g_param_spec_object ( -+ "agent", -+ "Agent", -+ "The NiceAgent this component belongs to.", -+ NICE_TYPE_AGENT, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+ -+ /** -+ * NiceComponent:stream: -+ * -+ * The #NiceStream this component belongs to. -+ * -+ * Since: UNRELEASED -+ */ -+ g_object_class_install_property (object_class, PROP_STREAM, -+ g_param_spec_object ( -+ "stream", -+ "Stream", -+ "The NiceStream this component belongs to.", -+ NICE_TYPE_STREAM, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -+} -+ -+static void -+nice_component_init (NiceComponent *component) -+{ -+ g_atomic_int_inc (&n_components_created); -+ nice_debug ("Created NiceComponent (%u created, %u destroyed)", -+ n_components_created, n_components_destroyed); -+ -+ component->id = 0; -+ component->state = NICE_COMPONENT_STATE_DISCONNECTED; -+ component->restart_candidate = NULL; -+ component->tcp = NULL; -+ component->agent = NULL; -+ component->stream = NULL; -+ -+ g_mutex_init (&component->io_mutex); -+ g_queue_init (&component->pending_io_messages); -+ component->io_callback_id = 0; -+ -+ component->own_ctx = g_main_context_new (); -+ component->stop_cancellable = g_cancellable_new (); -+ component->stop_cancellable_source = -+ g_cancellable_source_new (component->stop_cancellable); -+ g_source_set_dummy_callback (component->stop_cancellable_source); -+ g_source_attach (component->stop_cancellable_source, component->own_ctx); -+ component->ctx = g_main_context_ref (component->own_ctx); -+ -+ /* Start off with a fresh main context and all I/O paused. This -+ * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages() -+ * are called. */ -+ nice_component_set_io_context (component, NULL); -+ nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL); -+ -+ g_queue_init (&component->queued_tcp_packets); -+} -+ -+static void -+nice_component_constructed (GObject *obj) -+{ -+ NiceComponent *component; -+ -+ component = NICE_COMPONENT (obj); -+ -+ g_assert (component->agent != NULL); -+ nice_agent_init_stun_agent (component->agent, &component->stun_agent); -+ -+ G_OBJECT_CLASS (nice_component_parent_class)->constructed (obj); -+} -+ -+static void -+nice_component_get_property (GObject *obj, -+ guint property_id, GValue *value, GParamSpec *pspec) -+{ -+ NiceComponent *component; -+ -+ component = NICE_COMPONENT (obj); -+ -+ switch ((NiceComponentProperty) property_id) -+ { -+ case PROP_ID: -+ g_value_set_uint (value, component->id); -+ break; -+ -+ case PROP_AGENT: -+ g_value_set_object (value, component->agent); -+ break; -+ -+ case PROP_STREAM: -+ g_value_set_object (value, component->stream); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); -+ } -+} -+ -+static void -+nice_component_set_property (GObject *obj, -+ guint property_id, const GValue *value, GParamSpec *pspec) -+{ -+ NiceComponent *component; -+ -+ component = NICE_COMPONENT (obj); -+ -+ switch ((NiceComponentProperty) property_id) -+ { -+ case PROP_ID: -+ component->id = g_value_get_uint (value); -+ break; -+ -+ case PROP_AGENT: -+ component->agent = g_value_get_object (value); -+ break; -+ -+ case PROP_STREAM: -+ component->stream = g_value_get_object (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); -+ } -+} -+ -+/* Must be called with the agent lock released as it could dispose of -+ * NiceIOStreams. */ -+static void -+nice_component_finalize (GObject *obj) -+{ -+ NiceComponent *cmp; -+ -+ cmp = NICE_COMPONENT (obj); -+ -+ /* Component should have been closed already. */ -+ g_warn_if_fail (cmp->local_candidates == NULL); -+ g_warn_if_fail (cmp->remote_candidates == NULL); -+ g_warn_if_fail (cmp->incoming_checks == NULL); -+ -+ g_clear_object (&cmp->tcp); -+ g_clear_object (&cmp->stop_cancellable); -+ g_clear_object (&cmp->iostream); -+ g_mutex_clear (&cmp->io_mutex); -+ -+ if (cmp->stop_cancellable_source != NULL) { -+ g_source_destroy (cmp->stop_cancellable_source); -+ g_source_unref (cmp->stop_cancellable_source); -+ } -+ -+ if (cmp->ctx != NULL) { -+ g_main_context_unref (cmp->ctx); -+ cmp->ctx = NULL; -+ } -+ -+ g_main_context_unref (cmp->own_ctx); -+ -+ g_atomic_int_inc (&n_components_destroyed); -+ nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)", -+ n_components_created, n_components_destroyed); -+ -+ G_OBJECT_CLASS (nice_component_parent_class)->finalize (obj); -+} -+ - /** - * ComponentSource: - * -@@ -980,7 +1173,7 @@ component_source_prepare (GSource *source, gint *timeout_) - { - ComponentSource *component_source = (ComponentSource *) source; - NiceAgent *agent; -- Component *component; -+ NiceComponent *component; - GSList *parentl, *childl; - - agent = g_weak_ref_get (&component_source->agent_ref); -@@ -1011,6 +1204,9 @@ component_source_prepare (GSource *source, gint *timeout_) - SocketSource *parent_socket_source = parentl->data; - SocketSource *child_socket_source; - -+ if (parent_socket_source->socket->fileno == NULL) -+ continue; -+ - /* Iterating the list of socket sources every time isn't a big problem - * because the number of pairs is limited ~100 normally, so there will - * rarely be more than 10. -@@ -1128,7 +1324,7 @@ static GSourceFuncs component_source_funcs = { - }; - - /** -- * component_source_new: -+ * nice_component_source_new: - * @agent: a #NiceAgent - * @stream_id: The stream's id - * @component_id: The component's number -@@ -1150,7 +1346,7 @@ static GSourceFuncs component_source_funcs = { - * Returns: (transfer full): a new #ComponentSource; unref with g_source_unref() - */ - GSource * --component_input_source_new (NiceAgent *agent, guint stream_id, -+nice_component_input_source_new (NiceAgent *agent, guint stream_id, - guint component_id, GPollableInputStream *pollable_istream, - GCancellable *cancellable) - { -diff --git a/agent/component.h b/agent/component.h -index 7ded710..6712794 100644 ---- a/agent/component.h -+++ b/agent/component.h -@@ -42,7 +42,7 @@ - - #include <glib.h> - --typedef struct _Component Component; -+typedef struct _NiceComponent NiceComponent; - - #include "agent.h" - #include "agent-priv.h" -@@ -83,6 +83,7 @@ struct _CandidatePair - NiceCandidate *local; - NiceCandidate *remote; - guint64 priority; /* candidate pair priority */ -+ guint32 prflx_priority; - CandidatePairKeepalive keepalive; - }; - -@@ -110,7 +111,7 @@ incoming_check_free (IncomingCheck *icheck); - typedef struct { - NiceSocket *socket; - GSource *source; -- Component *component; -+ NiceComponent *component; - } SocketSource; - - -@@ -137,9 +138,22 @@ io_callback_data_new (const guint8 *buf, gsize buf_len); - void - io_callback_data_free (IOCallbackData *data); - -+#define NICE_TYPE_COMPONENT nice_component_get_type() -+#define NICE_COMPONENT(obj) \ -+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), NICE_TYPE_COMPONENT, NiceComponent)) -+#define NICE_COMPONENT_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_CAST ((klass), NICE_TYPE_COMPONENT, NiceComponentClass)) -+#define NICE_IS_COMPONENT(obj) \ -+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NICE_TYPE_COMPONENT)) -+#define NICE_IS_COMPONENT_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_TYPE ((klass), NICE_TYPE_COMPONENT)) -+#define NICE_COMPONENT_GET_CLASS(obj) \ -+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_COMPONENT, NiceComponentClass)) -+ -+struct _NiceComponent { -+ /*< private >*/ -+ GObject parent; - --struct _Component --{ - NiceComponentType type; - guint id; /* component id */ - NiceComponentState state; -@@ -186,8 +200,8 @@ struct _Component - - NiceAgent *agent; /* unowned, immutable: can be accessed without holding the - * agent lock */ -- Stream *stream; /* unowned, immutable: can be accessed without holding the -- * agent lock */ -+ NiceStream *stream; /* unowned, immutable: can be accessed without holding -+ * the agent lock */ - - StunAgent stun_agent; /* This stun agent is used to validate all stun requests */ - -@@ -212,63 +226,69 @@ struct _Component - GQueue queued_tcp_packets; - }; - --Component * --component_new (guint component_id, NiceAgent *agent, Stream *stream); -+typedef struct { -+ GObjectClass parent_class; -+} NiceComponentClass; -+ -+GType nice_component_get_type (void); - --void --component_close (Component *cmp); -+NiceComponent * -+nice_component_new (guint component_id, NiceAgent *agent, NiceStream *stream); - - void --component_free (Component *cmp); -+nice_component_close (NiceComponent *component); - - gboolean --component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair); -+nice_component_find_pair (NiceComponent *component, NiceAgent *agent, -+ const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair); - - void --component_restart (Component *cmp); -+nice_component_restart (NiceComponent *component); - - void --component_update_selected_pair (Component *component, const CandidatePair *pair); -+nice_component_update_selected_pair (NiceComponent *component, -+ const CandidatePair *pair); - - NiceCandidate * --component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport); -+nice_component_find_remote_candidate (NiceComponent *component, -+ const NiceAddress *addr, NiceCandidateTransport transport); - - NiceCandidate * --component_set_selected_remote_candidate (NiceAgent *agent, Component *component, -- NiceCandidate *candidate); -+nice_component_set_selected_remote_candidate (NiceComponent *component, -+ NiceAgent *agent, NiceCandidate *candidate); - - void --component_attach_socket (Component *component, NiceSocket *nsocket); -+nice_component_attach_socket (NiceComponent *component, NiceSocket *nsocket); -+ - void --component_detach_socket (Component *component, NiceSocket *nsocket); -+nice_component_remove_socket (NiceComponent *component, NiceSocket *nsocket); - void --component_detach_all_sockets (Component *component); -+nice_component_detach_all_sockets (NiceComponent *component); -+ - void --component_free_socket_sources (Component *component); -+nice_component_free_socket_sources (NiceComponent *component); - - GSource * --component_input_source_new (NiceAgent *agent, guint stream_id, -+nice_component_input_source_new (NiceAgent *agent, guint stream_id, - guint component_id, GPollableInputStream *pollable_istream, - GCancellable *cancellable); - - GMainContext * --component_dup_io_context (Component *component); -+nice_component_dup_io_context (NiceComponent *component); - void --component_set_io_context (Component *component, GMainContext *context); -+nice_component_set_io_context (NiceComponent *component, GMainContext *context); - void --component_set_io_callback (Component *component, -+nice_component_set_io_callback (NiceComponent *component, - NiceAgentRecvFunc func, gpointer user_data, - NiceInputMessage *recv_messages, guint n_recv_messages, - GError **error); - void --component_emit_io_callback (Component *component, -+nice_component_emit_io_callback (NiceComponent *component, - const guint8 *buf, gsize buf_len); -- - gboolean --component_has_io_callback (Component *component); -- -+nice_component_has_io_callback (NiceComponent *component); - void --component_clean_turn_servers (Component *component); -+nice_component_clean_turn_servers (NiceComponent *component); - - - TurnServer * -diff --git a/agent/conncheck.c b/agent/conncheck.c -index 057fc81..7e03985 100644 ---- a/agent/conncheck.c -+++ b/agent/conncheck.c -@@ -61,18 +61,20 @@ - #include "stun/usages/bind.h" - #include "stun/usages/turn.h" - --static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream); --static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component); --static guint priv_prune_pending_checks (Stream *stream, guint component_id); --static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); --static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand); --static size_t priv_create_username (NiceAgent *agent, Stream *stream, -+static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream); -+static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component); -+static guint priv_prune_pending_checks (NiceStream *stream, guint component_id); -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate); -+static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand); -+static size_t priv_create_username (NiceAgent *agent, NiceStream *stream, - guint component_id, NiceCandidate *remote, NiceCandidate *local, - uint8_t *dest, guint dest_len, gboolean inbound); --static size_t priv_get_password (NiceAgent *agent, Stream *stream, -+static size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - NiceCandidate *remote, uint8_t **password); - static void conn_check_free_item (gpointer data); --static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); -+static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( -+ NiceAgent *agent, guint stream_id, NiceComponent *component, -+ NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state); - - static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - { -@@ -81,6 +83,140 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now) - now->tv_sec >= timer->tv_sec; - } - -+static gchar -+priv_state_to_gchar (NiceCheckState state) -+{ -+ switch (state) { -+ case NICE_CHECK_WAITING: -+ return 'W'; -+ case NICE_CHECK_IN_PROGRESS: -+ return 'I'; -+ case NICE_CHECK_SUCCEEDED: -+ return 'S'; -+ case NICE_CHECK_FAILED: -+ return 'F'; -+ case NICE_CHECK_FROZEN: -+ return 'Z'; -+ case NICE_CHECK_CANCELLED: -+ return 'C'; -+ case NICE_CHECK_DISCOVERED: -+ return 'D'; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+static const gchar * -+priv_candidate_type_to_string (NiceCandidateType type) -+{ -+ switch (type) { -+ case NICE_CANDIDATE_TYPE_HOST: -+ return "host"; -+ case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: -+ return "srflx"; -+ case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: -+ return "prflx"; -+ case NICE_CANDIDATE_TYPE_RELAYED: -+ return "relay"; -+ default: -+ g_assert_not_reached (); -+ } -+} -+ -+/* -+ * Dump the conncheck lists of the agent -+ */ -+static void -+priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail) -+{ -+ GSList *i, *k; -+ guint j; -+ -+ if (!nice_debug_is_verbose ()) -+ return; -+ -+#define PRIORITY_LEN 32 -+ -+ nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)", -+ agent, where, detail ? detail : ""); -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ for (j = 1; j <= stream->n_components; j++) { -+ for (k = stream->conncheck_list; k ; k = k->next) { -+ CandidateCheckPair *pair = k->data; -+ if (pair->component_id == j) { -+ gchar priority[PRIORITY_LEN]; -+ guint p1, p2, p3; -+ gchar local_addr[INET6_ADDRSTRLEN]; -+ gchar remote_addr[INET6_ADDRSTRLEN]; -+ -+ p1 = (pair->priority >> 32); -+ p2 = (pair->priority >> 1) & 0x7fffffff; -+ p3 = (pair->priority & 1); -+ -+ g_snprintf (priority, PRIORITY_LEN, -+ "%02x:%04x:%02x:%02x:%04x:%02x:%1x", -+ (p1 >> 24) & 0x7f, (p1 >> 8) & 0xffff, (p1 & 0xff), -+ (p2 >> 24) & 0x7f, (p2 >> 8) & 0xffff, (p2 & 0xff), -+ p3); -+ -+ nice_address_to_string (&pair->local->addr, local_addr); -+ nice_address_to_string (&pair->remote->addr, remote_addr); -+ -+ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : " -+ "f=%s t=%s:%s p=%s [%s]:%u > [%s]:%u state=%c%s%s%s", -+ agent, pair->stream_id, pair->component_id, pair, -+ pair->foundation, -+ priv_candidate_type_to_string (pair->local->type), -+ priv_candidate_type_to_string (pair->remote->type), -+ priority, -+ local_addr, nice_address_get_port (&pair->local->addr), -+ remote_addr, nice_address_get_port (&pair->remote->addr), -+ priv_state_to_gchar (pair->state), -+ pair->valid ? "V" : "", -+ pair->nominated ? "N" : "", -+ g_slist_find (agent->triggered_check_queue, pair) ? "T" : ""); -+ } -+ } -+ } -+ } -+} -+ -+/* Add the pair to the triggered checks list, if not already present -+ */ -+static void -+priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) -+{ -+ g_assert (pair); -+ -+ if (agent->triggered_check_queue == NULL || -+ g_slist_find (agent->triggered_check_queue, pair) == NULL) -+ agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair); -+} -+ -+/* Remove the pair from the triggered checks list -+ */ -+static void -+priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair) -+{ -+ g_assert (pair); -+ agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair); -+} -+ -+/* Get the pair from the triggered checks list -+ */ -+static CandidateCheckPair * -+priv_get_pair_from_triggered_check_queue (NiceAgent *agent) -+{ -+ CandidateCheckPair *pair = NULL; -+ -+ if (agent->triggered_check_queue) { -+ pair = (CandidateCheckPair *)agent->triggered_check_queue->data; -+ priv_remove_pair_from_triggered_check_queue (agent, pair); -+ } -+ return pair; -+} -+ - /* - * Finds the next connectivity check in WAITING state. - */ -@@ -107,10 +243,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check - */ - static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair) - { -- /* XXX: from ID-16 onwards, the checks should not be sent -- * immediately, but be put into the "triggered queue", -- * see "7.2.1.4 Triggered Checks" -- */ - g_get_current_time (&pair->next_tick); - g_time_val_add (&pair->next_tick, agent->timer_ta * 1000); - pair->state = NICE_CHECK_IN_PROGRESS; -@@ -142,7 +274,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - */ - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - guint64 max_frozen_priority = 0; - - -@@ -185,7 +317,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent) - * - * @return TRUE on success, and FALSE if no frozen candidates were found. - */ --static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check) -+static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check) - { - GSList *i, *j; - guint unfrozen = 0; -@@ -212,10 +344,10 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, - - /* step: perform the step (2) of 'Updating Pair States' */ - stream = agent_find_stream (agent, ok_check->stream_id); -- if (stream_all_components_ready (stream)) { -+ if (nice_stream_all_components_ready (stream)) { - /* step: unfreeze checks from other streams */ - for (i = agent->streams; i ; i = i->next) { -- Stream *s = i->data; -+ NiceStream *s = i->data; - for (j = stream->conncheck_list; j ; j = j->next) { - CandidateCheckPair *p = j->data; - -@@ -242,12 +374,12 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, - } - - static void --candidate_check_pair_fail (Stream *stream, NiceAgent *agent, CandidateCheckPair *p) -+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p) - { - StunTransactionId id; -- Component *component; -+ NiceComponent *component; - -- component = stream_find_component_by_id (stream, p->component_id); -+ component = nice_stream_find_component_by_id (stream, p->component_id); - - p->state = NICE_CHECK_FAILED; - nice_debug ("Agent %p : pair %p state FAILED", agent, p); -@@ -269,14 +401,16 @@ candidate_check_pair_fail (Stream *stream, NiceAgent *agent, CandidateCheckPair - * - * @return will return FALSE when no more pending timers. - */ --static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now) -+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted) - { - gboolean keep_timer_going = FALSE; - guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0, -- s_nominated = 0, s_waiting_for_nomination = 0; -+ s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0; - guint frozen = 0, waiting = 0; - GSList *i, *k; - -+ priv_print_conn_check_lists (agent, G_STRFUNC, NULL); -+ - for (i = stream->conncheck_list; i ; i = i->next) { - CandidateCheckPair *p = i->data; - -@@ -290,7 +424,13 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - case STUN_USAGE_TIMER_RETURN_TIMEOUT: - { - /* case: error, abort processing */ -+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&p->local->addr, tmpbuf1); -+ nice_address_to_string (&p->remote->addr, tmpbuf2); - nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p); -+ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent, -+ tmpbuf1, nice_address_get_port (&p->local->addr), -+ tmpbuf2, nice_address_get_port (&p->remote->addr)); - candidate_check_pair_fail (stream, agent, p); - - break; -@@ -299,8 +439,9 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - { - /* case: not ready, so schedule a new timeout */ - unsigned int timeout = stun_timer_remainder (&p->timer); -- nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).", -- agent, timeout); -+ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p " -+ "(timeout %dms, delay=%dms, retrans=%d).", -+ agent, p, timeout, p->timer.delay, p->timer.retransmissions); - - agent_socket_send (p->sockptr, &p->remote->addr, - stun_message_length (&p->stun_message), -@@ -311,8 +452,8 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - p->next_tick = *now; - g_time_val_add (&p->next_tick, timeout * 1000); - -- keep_timer_going = TRUE; -- break; -+ *stun_transmitted = TRUE; -+ return TRUE; - } - case STUN_USAGE_TIMER_RETURN_SUCCESS: - { -@@ -342,6 +483,8 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - ++s_succeeded; - else if (p->state == NICE_CHECK_DISCOVERED) - ++s_discovered; -+ if (p->valid) -+ ++s_valid; - - if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED) - && p->nominated) -@@ -365,7 +508,7 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - - for (component_item = stream->components; component_item; - component_item = component_item->next) { -- Component *component = component_item->data; -+ NiceComponent *component = component_item->data; - - for (k = stream->conncheck_list; k ; k = k->next) { - CandidateCheckPair *p = k->data; -@@ -375,7 +518,7 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - p->state == NICE_CHECK_DISCOVERED)) { - nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p); - p->nominated = TRUE; -- priv_conn_check_initiate (agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); - break; /* move to the next component */ - } - } -@@ -385,17 +528,28 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G - { - static int tick_counter = 0; - if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE) -- nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, " -+ nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, %u in-progress, " - "%u waiting, %u succeeded, %u discovered, %u nominated, " -- "%u waiting-for-nom.", agent, -+ "%u waiting-for-nom, %u valid.", agent, stream->id, - tick_counter, frozen, s_inprogress, waiting, s_succeeded, -- s_discovered, s_nominated, s_waiting_for_nomination); -+ s_discovered, s_nominated, s_waiting_for_nomination, s_valid); - } - - return keep_timer_going; - - } - -+static void -+conn_check_stop (NiceAgent *agent) -+{ -+ if (agent->conncheck_timer_source == NULL) -+ return; -+ -+ g_source_destroy (agent->conncheck_timer_source); -+ g_source_unref (agent->conncheck_timer_source); -+ agent->conncheck_timer_source = NULL; -+} -+ - - /* - * Timer callback that handles initiating and managing connectivity -@@ -409,15 +563,39 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - { - CandidateCheckPair *pair = NULL; - gboolean keep_timer_going = FALSE; -+ gboolean res; -+ /* note: we try to only generate a single stun transaction per timer -+ * callback, to respect some pacing of STUN transaction, as per -+ * appendix B.1 of ICE spec. -+ */ -+ gboolean stun_transmitted = FALSE; - GSList *i, *j; - GTimeVal now; - - /* step: process ongoing STUN transactions */ - g_get_current_time (&now); - -- /* step: find the highest priority waiting check and send it */ -+ for (j = agent->streams; j; j = j->next) { -+ NiceStream *stream = j->data; -+ res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted); -+ if (res) -+ keep_timer_going = res; -+ if (stun_transmitted) -+ return TRUE; -+ } -+ -+ /* step: first initiate a conncheck with a pair from the triggered list */ -+ pair = priv_get_pair_from_triggered_check_queue (agent); -+ -+ if (pair) { -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; -+ } -+ -+ /* step: when the triggered list is empty, -+ * find the highest priority waiting check and send it */ - for (i = agent->streams; i ; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - pair = priv_conn_check_find_next_waiting (stream->conncheck_list); - if (pair) -@@ -426,27 +604,37 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - - if (pair) { - priv_conn_check_initiate (agent, pair); -- keep_timer_going = TRUE; -- } else { -- keep_timer_going = priv_conn_check_unfreeze_next (agent); -+ return TRUE; - } - -- for (j = agent->streams; j; j = j->next) { -- Stream *stream = j->data; -- gboolean res = -- priv_conn_check_tick_stream (stream, agent, &now); -- if (res) -- keep_timer_going = res; -+ /* step: when there's no pair in the Waiting state, -+ * unfreeze a new pair and check it -+ */ -+ res = priv_conn_check_unfreeze_next (agent); -+ -+ for (i = agent->streams; i ; i = i->next) { -+ NiceStream *stream = i->data; -+ -+ pair = priv_conn_check_find_next_waiting (stream->conncheck_list); -+ if (pair) -+ break; -+ } -+ -+ if (pair) { -+ priv_print_conn_check_lists (agent, G_STRFUNC, -+ ", got a pair in Waiting state"); -+ priv_conn_check_initiate (agent, pair); -+ return TRUE; - } - - /* step: stop timer if no work left */ - if (keep_timer_going != TRUE) { - nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC); - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - priv_update_check_list_failed_components (agent, stream); - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - priv_update_check_list_state_for_ready (agent, stream, component); - } - } -@@ -454,11 +642,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent) - /* Stopping the timer so destroy the source.. this will allow - the timer to be reset if we get a set_remote_candidates after this - point */ -- if (agent->conncheck_timer_source != NULL) { -- g_source_destroy (agent->conncheck_timer_source); -- g_source_unref (agent->conncheck_timer_source); -- agent->conncheck_timer_source = NULL; -- } -+ conn_check_stop (agent); - - /* XXX: what to signal, is all processing now really done? */ - nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent); -@@ -512,7 +696,7 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer) - { - /* Time out */ - StunTransactionId id; -- Component *component; -+ NiceComponent *component; - - if (!agent_find_component (pair->keepalive.agent, - pair->keepalive.stream_id, pair->keepalive.component_id, -@@ -525,13 +709,12 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer) - - stun_message_id (&pair->keepalive.stun_message, id); - stun_agent_forget_transaction (&component->stun_agent, id); -+ pair->keepalive.stun_message.buffer = NULL; - - if (pair->keepalive.agent->media_after_tick) { - nice_debug ("Agent %p : Keepalive conncheck timed out!! " - "but media was received. Suspecting keepalive lost because of " - "network bottleneck", pair->keepalive.agent); -- -- pair->keepalive.stun_message.buffer = NULL; - } else { - nice_debug ("Agent %p : Keepalive conncheck timed out!! " - "peer probably lost connection", pair->keepalive.agent); -@@ -561,7 +744,7 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer) - priv_conn_keepalive_retransmissions_tick, pair); - break; - default: -- /* Nothing to do. */ -+ g_assert_not_reached(); - break; - } - -@@ -579,6 +762,7 @@ static guint32 peer_reflexive_candidate_priority (NiceAgent *agent, - - candidate_priority->transport = local_candidate->transport; - candidate_priority->component_id = local_candidate->component_id; -+ candidate_priority->base_addr = local_candidate->addr; - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - priority = nice_candidate_jingle_priority (candidate_priority); - } else if (agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -617,9 +801,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - * (ref ICE sect 10 "Keepalives" ID-19) */ - for (i = agent->streams; i; i = i->next) { - -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - if (component->selected_pair.local != NULL) { - CandidatePair *p = &component->selected_pair; - -@@ -629,7 +813,6 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || - agent->keepalive_conncheck) { -- guint32 priority; - uint8_t uname[NICE_STREAM_MAX_UNAME]; - size_t uname_len = - priv_create_username (agent, agent_find_stream (agent, stream->id), -@@ -639,25 +822,31 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - size_t password_len = priv_get_password (agent, - agent_find_stream (agent, stream->id), p->remote, &password); - -- priority = peer_reflexive_candidate_priority (agent, p->local); -+ if (p->keepalive.stun_message.buffer != NULL) { -+ nice_debug ("Agent %p: Keepalive for s%u:c%u still" -+ " retransmitting, not restarting", agent, stream->id, -+ component->id); -+ continue; -+ } - - if (nice_debug_is_enabled ()) { - gchar tmpbuf[INET6_ADDRSTRLEN]; - nice_address_to_string (&p->remote->addr, tmpbuf); - nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', " -- "socket=%u (c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), " -+ "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), " - "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%u.", agent, - tmpbuf, nice_address_get_port (&p->remote->addr), -- g_socket_get_fd(((NiceSocket *)p->local->sockptr)->fileno), - component->id, (int) uname_len, uname, uname_len, -- (int) password_len, password, password_len, priority); -+ (int) password_len, password, password_len, -+ p->prflx_priority); - } - if (uname_len > 0) { - buf_len = stun_usage_ice_conncheck_create (&component->stun_agent, - &p->keepalive.stun_message, p->keepalive.stun_buffer, - sizeof(p->keepalive.stun_buffer), - uname, uname_len, password, password_len, -- agent->controlling_mode, agent->controlling_mode, priority, -+ agent->controlling_mode, agent->controlling_mode, -+ p->prflx_priority, - agent->tie_breaker, - NULL, - agent_to_ice_compatibility (agent)); -@@ -709,9 +898,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - /* case 2: connectivity establishment ongoing - * (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19) */ - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - if (component->state < NICE_COMPONENT_STATE_READY && - agent->stun_server_ip) { - NiceAddress stun_server; -@@ -723,12 +912,7 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) - - nice_address_set_port (&stun_server, agent->stun_server_port); - -- /* FIXME: This will cause the stun response to arrive on the socket -- * but the stun agent will not be able to parse it due to an invalid -- * stun message since RFC3489 will not be compatible, and the response -- * will be forwarded to the application as user data */ -- stun_agent_init (&stun_agent, STUN_ALL_KNOWN_ATTRIBUTES, -- STUN_COMPATIBILITY_RFC3489, 0); -+ nice_agent_init_stun_agent (agent, &stun_agent); - - buffer_len = stun_usage_bind_create (&stun_agent, - &stun_message, stun_buffer, sizeof(stun_buffer)); -@@ -937,23 +1121,14 @@ static gboolean priv_turn_allocate_refresh_tick (gpointer pointer) - - /* - * Initiates the next pending connectivity check. -- * -- * @return TRUE if a pending check was scheduled - */ --gboolean conn_check_schedule_next (NiceAgent *agent) -+void conn_check_schedule_next (NiceAgent *agent) - { -- gboolean res = priv_conn_check_unfreeze_next (agent); -- nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res); -- - if (agent->discovery_unsched_items > 0) - nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent); - -- /* step: call once imediately */ -- res = priv_conn_check_tick_unlocked (agent); -- nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res); -- - /* step: schedule timer if not running yet */ -- if (res && agent->conncheck_timer_source == NULL) { -+ if (agent->conncheck_timer_source == NULL) { - agent_timeout_add_with_context (agent, &agent->conncheck_timer_source, - "Connectivity check schedule", agent->timer_ta, - priv_conn_check_tick, agent); -@@ -965,9 +1140,6 @@ gboolean conn_check_schedule_next (NiceAgent *agent) - "Connectivity keepalive timeout", NICE_AGENT_TIMER_TR_DEFAULT, - priv_conn_keepalive_tick, agent); - } -- -- nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res); -- return res; - } - - /* -@@ -995,7 +1167,7 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair * - * @param component pointer to component object to which 'pair'has been added - * @param pair newly added connectivity check - */ --static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *pair) -+static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *pair) - { - GSList *i; - for (i = component->incoming_checks; i; i = i->next) { -@@ -1004,7 +1176,7 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *s - icheck->local_socket == pair->sockptr) { - nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id); - if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, pair->remote); -+ priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote); - priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate); - } - } -@@ -1039,15 +1211,13 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list) - * reaches us. The special case is documented in sect 7.2 - * if ICE spec (ID-19). - */ --void conn_check_remote_candidates_set(NiceAgent *agent) -+void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { -- GSList *i, *j, *k, *l, *m, *n; -+ GSList *j, *k, *l, *m, *n; - -- for (i = agent->streams; i ; i = i->next) { -- Stream *stream = i->data; -- for (j = stream->conncheck_list; j ; j = j->next) { -- CandidateCheckPair *pair = j->data; -- Component *component = stream_find_component_by_id (stream, pair->component_id); -+ for (j = stream->conncheck_list; j ; j = j->next) { -+ CandidateCheckPair *pair = j->data; -+ if (pair->component_id == component->id) { - gboolean match = FALSE; - - /* performn delayed processing of spec steps section 7.2.1.4, -@@ -1148,24 +1318,24 @@ void conn_check_remote_candidates_set(NiceAgent *agent) - conn_check_add_for_candidate (agent, stream->id, component, candidate); - - if (icheck->use_candidate) -- priv_mark_pair_nominated (agent, stream, component, candidate); -+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate); - priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate); - } - } - } - } -- -- /* Once we process the pending checks, we should free them to avoid -- * reprocessing them again if a dribble-mode set_remote_candidates -- * is called */ -- g_slist_free_full (component->incoming_checks, -- (GDestroyNotify) incoming_check_free); -- component->incoming_checks = NULL; - } -- -- stream->conncheck_list = -- prune_cancelled_conn_check (stream->conncheck_list); - } -+ -+ /* Once we process the pending checks, we should free them to avoid -+ * reprocessing them again if a dribble-mode set_remote_candidates -+ * is called */ -+ g_slist_free_full (component->incoming_checks, -+ (GDestroyNotify) incoming_check_free); -+ component->incoming_checks = NULL; -+ -+ stream->conncheck_list = -+ prune_cancelled_conn_check (stream->conncheck_list); - } - - /* -@@ -1204,20 +1374,23 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper - * and has higher priority than the currently selected pair. See - * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19). - */ --static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair) -+static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *component, CandidateCheckPair *pair) - { -- CandidatePair cpair; -+ CandidatePair cpair = { 0, }; - - g_assert (component); - g_assert (pair); -- if (pair->priority > component->selected_pair.priority && -- component_find_pair (component, agent, pair->local->foundation, -- pair->remote->foundation, &cpair)) { -+ if (pair->priority > component->selected_pair.priority) { - nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s " - "(prio:%" G_GUINT64_FORMAT ").", agent, component->id, - pair->local->foundation, pair->remote->foundation, pair->priority); - -- component_update_selected_pair (component, &cpair); -+ cpair.local = pair->local; -+ cpair.remote = pair->remote; -+ cpair.priority = pair->priority; -+ /* cpair.keepalive is not used by nice_component_update_selected_pair() */ -+ -+ nice_component_update_selected_pair (component, &cpair); - - priv_conn_keepalive_tick_unlocked (agent); - -@@ -1239,7 +1412,7 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, Component *componen - * - * Sends a component state changesignal via 'agent'. - */ --static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream) -+static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream) - { - GSList *i; - /* note: emitting a signal might cause the client -@@ -1261,7 +1434,7 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, Stream * - - /* note: iterate the conncheck list for each component separately */ - for (c = 0; c < components; c++) { -- Component *comp = NULL; -+ NiceComponent *comp = NULL; - if (!agent_find_component (agent, stream->id, c+1, NULL, &comp)) - continue; - -@@ -1299,10 +1472,10 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, Stream * - * - * Sends a component state changesignal via 'agent'. - */ --static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component) -+static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component) - { - GSList *i; -- guint succeeded = 0, nominated = 0; -+ guint valid = 0, nominated = 0; - - g_assert (component); - -@@ -1310,26 +1483,36 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *st - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; - if (p->component_id == component->id) { -- if (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) { -- ++succeeded; -+ if (p->valid) { -+ ++valid; - if (p->nominated == TRUE) { - ++nominated; -+ priv_update_selected_pair (agent, component, p); - } - } - } - } - -- if (nominated > 0) { -+ if (valid > 0) { - /* Only go to READY if no checks are left in progress. If there are - * any that are kept, then this function will be called again when the - * conncheck tick timer finishes them all */ - if (priv_prune_pending_checks (stream, component->id) == 0) { -+ /* Continue through the states to give client code a nice -+ * logical progression. See
http://phabricator.freedesktop.org/D218
for -+ * discussion. */ -+ if (component->state < NICE_COMPONENT_STATE_CONNECTING || -+ component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, stream->id, component->id, -+ NICE_COMPONENT_STATE_CONNECTING); -+ if (component->state < NICE_COMPONENT_STATE_CONNECTED) -+ agent_signal_component_state_change (agent, stream->id, component->id, -+ NICE_COMPONENT_STATE_CONNECTED); - agent_signal_component_state_change (agent, stream->id, - component->id, NICE_COMPONENT_STATE_READY); - } - } -- nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id); -+ nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id); - } - - /* -@@ -1337,7 +1520,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *st - * described by 'component' and 'remotecand' is nominated - * for use. - */ --static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand) -+static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand) - { - GSList *i; - -@@ -1346,27 +1529,66 @@ static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Componen - /* step: search for at least one nominated pair */ - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *pair = i->data; -- /* XXX: hmm, how to figure out to which local candidate the -- * check was sent to? let's mark all matching pairs -- * as nominated instead */ -- if (pair->remote == remotecand) { -+ if (pair->local == localcand && pair->remote == remotecand) { - nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation); - pair->nominated = TRUE; -- if (pair->state == NICE_CHECK_SUCCEEDED || -- pair->state == NICE_CHECK_DISCOVERED) -+ if (pair->valid) { - priv_update_selected_pair (agent, component, pair); -+ /* Do not step down to CONNECTED if we're already at state READY*/ -+ if (component->state != NICE_COMPONENT_STATE_READY) { -+ /* step: notify the client of a new component state (must be done -+ * before the possible check list state update step */ -+ agent_signal_component_state_change (agent, -+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED); -+ } -+ -+ } - priv_update_check_list_state_for_ready (agent, stream, component); - } - } - } - -+guint32 -+ensure_unique_priority (NiceComponent *component, guint32 priority) -+{ -+ GSList *item; -+ -+ again: -+ if (priority == 0) -+ priority--; -+ -+ for (item = component->local_candidates; item; item = item->next) { -+ NiceCandidate *cand = item->data; -+ -+ if (cand->priority == priority) { -+ priority--; -+ goto again; -+ } -+ } -+ -+ for (item = component->stream->conncheck_list; item; item = item->next) { -+ CandidateCheckPair *p = item->data; -+ -+ if (p->component_id == component->id && -+ p->prflx_priority == priority) { -+ priority--; -+ goto again; -+ } -+ } -+ -+ return priority; -+} -+ -+ - /* - * Creates a new connectivity check pair and adds it to - * the agent's list of checks. - */ --static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) -+static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent, -+ guint stream_id, NiceComponent *component, NiceCandidate *local, -+ NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate) - { -- Stream *stream; -+ NiceStream *stream; - CandidateCheckPair *pair; - - g_assert (local != NULL); -@@ -1389,8 +1611,19 @@ static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Componen - pair->priority = agent_candidate_pair_priority (agent, local, remote); - pair->state = initial_state; - nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state); -+ { -+ gchar tmpbuf1[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&pair->local->addr, tmpbuf1); -+ nice_address_to_string (&pair->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair, -+ tmpbuf1, nice_address_get_port (&pair->local->addr), -+ tmpbuf2, nice_address_get_port (&pair->remote->addr)); -+ } - pair->nominated = use_candidate; - pair->controlling = agent->controlling_mode; -+ pair->prflx_priority = ensure_unique_priority (component, -+ peer_reflexive_candidate_priority (agent, local)); - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, - (GCompareFunc)conn_check_compare); -@@ -1402,6 +1635,8 @@ static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Componen - if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) { - priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks); - } -+ -+ return pair; - } - - NiceCandidateTransport -@@ -1422,13 +1657,16 @@ conn_check_match_transport (NiceCandidateTransport transport) - } - } - --static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, -- guint stream_id, Component *component, NiceCandidate *local, -- NiceCandidate *remote, NiceCheckState initial_state) -+static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched ( -+ NiceAgent *agent, guint stream_id, NiceComponent *component, -+ NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state) - { -- nice_debug ("Agent %p, Adding check pair between %s and %s", agent, -- local->foundation, remote->foundation); -- priv_add_new_check_pair (agent, stream_id, component, local, remote, -+ CandidateCheckPair *pair; -+ -+ nice_debug ("Agent %p : Adding check pair between %s and %s for s%d/c%d", -+ agent, local->foundation, remote->foundation, -+ stream_id, component->id); -+ pair = priv_add_new_check_pair (agent, stream_id, component, local, remote, - initial_state, FALSE); - if (component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY) { -@@ -1442,10 +1680,12 @@ static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, - component->id, - NICE_COMPONENT_STATE_CONNECTING); - } -+ -+ return pair; - } - - gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, -- guint stream_id, Component *component, NiceCandidate *local, -+ guint stream_id, NiceComponent *component, NiceCandidate *local, - NiceCandidate *remote) - { - gboolean ret = FALSE; -@@ -1491,7 +1731,7 @@ gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, - * - * @return number of checks added, negative on fatal errors - */ --int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote) -+int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote) - { - GSList *i; - int added = 0; -@@ -1500,8 +1740,11 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component * - g_assert (remote != NULL); - - for (i = component->local_candidates; i ; i = i->next) { -- - NiceCandidate *local = i->data; -+ -+ if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED) -+ continue; -+ - ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote); - - if (ret) { -@@ -1522,7 +1765,7 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component * - * - * @return number of checks added, negative on fatal errors - */ --int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local) -+int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local) - { - GSList *i; - int added = 0; -@@ -1551,22 +1794,13 @@ static void conn_check_free_item (gpointer data) - { - CandidateCheckPair *pair = data; - -+ if (pair->agent) -+ priv_remove_pair_from_triggered_check_queue (pair->agent, pair); - pair->stun_message.buffer = NULL; - pair->stun_message.buffer_len = 0; - g_slice_free (CandidateCheckPair, pair); - } - --static void --conn_check_stop (NiceAgent *agent) --{ -- if (agent->conncheck_timer_source == NULL) -- return; -- -- g_source_destroy (agent->conncheck_timer_source); -- g_source_unref (agent->conncheck_timer_source); -- agent->conncheck_timer_source = NULL; --} -- - /* - * Frees all resources of all connectivity checks. - */ -@@ -1574,7 +1808,7 @@ void conn_check_free (NiceAgent *agent) - { - GSList *i; - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - - if (stream->conncheck_list) { - nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent, -@@ -1593,7 +1827,7 @@ void conn_check_free (NiceAgent *agent) - * - * @return TRUE on success, FALSE on a fatal error - */ --void conn_check_prune_stream (NiceAgent *agent, Stream *stream) -+void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream) - { - GSList *i; - gboolean keep_going = FALSE; -@@ -1606,7 +1840,7 @@ void conn_check_prune_stream (NiceAgent *agent, Stream *stream) - } - - for (i = agent->streams; i; i = i->next) { -- Stream *s = i->data; -+ NiceStream *s = i->data; - if (s->conncheck_list) { - keep_going = TRUE; - break; -@@ -1717,7 +1951,7 @@ size_t priv_gen_username (NiceAgent *agent, guint component_id, - * NULL) is ever written to the 'dest'. - */ - static --size_t priv_create_username (NiceAgent *agent, Stream *stream, -+size_t priv_create_username (NiceAgent *agent, NiceStream *stream, - guint component_id, NiceCandidate *remote, NiceCandidate *local, - uint8_t *dest, guint dest_len, gboolean inbound) - { -@@ -1760,7 +1994,7 @@ size_t priv_create_username (NiceAgent *agent, Stream *stream, - * check. - */ - static --size_t priv_get_password (NiceAgent *agent, Stream *stream, -+size_t priv_get_password (NiceAgent *agent, NiceStream *stream, - NiceCandidate *remote, uint8_t **password) - { - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) -@@ -1781,26 +2015,30 @@ size_t priv_get_password (NiceAgent *agent, Stream *stream, - - /* Implement the computation specific in RFC 5245 section 16 */ - --static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, -- Stream *stream) -+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent) - { -- GSList *item; -+ GSList *item1, *item2; - guint waiting_and_in_progress = 0; - unsigned int rto = 0; - -- for (item = stream->conncheck_list; item; item = item->next) { -- CandidateCheckPair *pair = item->data; - -- if (pair->state == NICE_CHECK_IN_PROGRESS || -- pair->state == NICE_CHECK_WAITING) -- waiting_and_in_progress++; -+ for (item1 = agent->streams; item1; item1 = item1->next) { -+ NiceStream *stream = item1->data;; -+ for (item2 = stream->conncheck_list; item2; item2 = item2->next) { -+ CandidateCheckPair *pair = item2->data; -+ -+ if (pair->state == NICE_CHECK_IN_PROGRESS || -+ pair->state == NICE_CHECK_WAITING) -+ waiting_and_in_progress++; -+ } - } - -- /* FIXME: This should also be multiple by "N", which I believe is the -- * number of Streams currently in the conncheck state. */ - rto = agent->timer_ta * waiting_and_in_progress; - - /* We assume non-reliable streams are RTP, so we use 100 as the max */ -+ nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)", -+ agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100), -+ waiting_and_in_progress); - if (agent->reliable) - return MAX (rto, 500); - else -@@ -1822,11 +2060,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts) - * - USE-CANDIDATE (if sent by the controlling agent) - */ -- guint32 priority; - - uint8_t uname[NICE_STREAM_MAX_UNAME]; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - gsize uname_len; - uint8_t *password = NULL; - gsize password_len; -@@ -1844,8 +2081,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - pair->remote, pair->local, uname, sizeof (uname), FALSE); - password_len = priv_get_password (agent, stream, pair->remote, &password); - -- priority = peer_reflexive_candidate_priority (agent, pair->local); -- - if (password != NULL && - (agent->compatibility == NICE_COMPATIBILITY_MSN || - agent->compatibility == NICE_COMPATIBILITY_OC2007)) { -@@ -1853,20 +2088,21 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - } - - if (nice_debug_is_enabled ()) { -- gchar tmpbuf[INET6_ADDRSTRLEN]; -- nice_address_to_string (&pair->remote->addr, tmpbuf); -- nice_debug ("Agent %p : STUN-CC REQ to '%s:%u', socket=%u, " -+ gchar tmpbuf1[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&pair->local->addr, tmpbuf1); -+ nice_address_to_string (&pair->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, " - "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), " -- "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%u.", agent, -- tmpbuf, -- nice_address_get_port (&pair->remote->addr), -+ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent, -+ tmpbuf1, nice_address_get_port (&pair->local->addr), -+ tmpbuf2, nice_address_get_port (&pair->remote->addr), - pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1, - pair->foundation, pair->component_id, - (unsigned long long)agent->tie_breaker, - (int) uname_len, uname, uname_len, - (int) password_len, password, password_len, -- priority); -- -+ pair->prflx_priority, controlling); - } - - if (cand_use) -@@ -1876,7 +2112,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent, - &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer), - uname, uname_len, password, password_len, -- cand_use, controlling, priority, -+ cand_use, controlling, pair->prflx_priority, - agent->tie_breaker, - pair->local->foundation, - agent_to_ice_compatibility (agent)); -@@ -1894,7 +2130,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT); - } else { - stun_timer_start (&pair->timer, -- priv_compute_conncheck_timer (agent, stream), -+ priv_compute_conncheck_timer (agent), - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); - } - -@@ -1902,9 +2138,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * by connecting to the peer. The new socket is stored in the candidate - * check pair, until we discover a new local peer reflexive */ - if (pair->sockptr->fileno == NULL && -+ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN && - pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) { -- Stream *stream2 = NULL; -- Component *component2 = NULL; -+ NiceStream *stream2 = NULL; -+ NiceComponent *component2 = NULL; - NiceSocket *new_socket; - - if (agent_find_component (agent, pair->stream_id, pair->component_id, -@@ -1914,7 +2151,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - if (new_socket) { - pair->sockptr = new_socket; - _priv_set_socket_tos (agent, pair->sockptr, stream2->tos); -- component_attach_socket (component2, new_socket); -+ -+ if (agent->reliable) { -+ nice_socket_set_writable_callback (pair->sockptr, -+ _tcp_sock_is_writable, component2); -+ } -+ -+ nice_component_attach_socket (component2, new_socket); - } - } - } -@@ -1948,7 +2191,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) - * - * @see priv_update_check_list_state_failed_components() - */ --static guint priv_prune_pending_checks (Stream *stream, guint component_id) -+static guint priv_prune_pending_checks (NiceStream *stream, guint component_id) - { - GSList *i; - guint64 highest_nominated_priority = 0; -@@ -1959,10 +2202,8 @@ static guint priv_prune_pending_checks (Stream *stream, guint component_id) - - for (i = stream->conncheck_list; i; i = i->next) { - CandidateCheckPair *p = i->data; -- if (p->component_id == component_id && -- (p->state == NICE_CHECK_SUCCEEDED || -- p->state == NICE_CHECK_DISCOVERED) && -- p->nominated == TRUE){ -+ if (p->component_id == component_id && p->valid == TRUE && -+ p->nominated == TRUE) { - if (p->priority > highest_nominated_priority) { - highest_nominated_priority = p->priority; - } -@@ -2015,7 +2256,7 @@ static guint priv_prune_pending_checks (Stream *stream, guint component_id) - * @param remote_cand remote candidate from which the inbound check was sent - * @param use_candidate whether the original check had USE-CANDIDATE attribute set - */ --static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) -+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate) - { - GSList *i; - NiceCandidate *local = NULL; -@@ -2038,7 +2279,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - - if (p->state == NICE_CHECK_WAITING || - p->state == NICE_CHECK_FROZEN) -- priv_conn_check_initiate (agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); - else if (p->state == NICE_CHECK_IN_PROGRESS) { - /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19), - * we should cancel the existing one, instead we reset our timer, so -@@ -2049,7 +2290,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - p->timer_restarted ? "no" : "yes"); - if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) { - stun_timer_start (&p->timer, -- priv_compute_conncheck_timer (agent, stream), -+ priv_compute_conncheck_timer (agent), - STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS); - p->timer_restarted = TRUE; - } -@@ -2059,23 +2300,36 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - nice_debug ("Agent %p : Skipping triggered check, already completed..", agent); - /* note: this is a bit unsure corner-case -- let's do the - same state update as for processing responses to our own checks */ -+ /* note: this update is required by the dribble test, to -+ * ensure the transition ready -> connected -> ready, because -+ * an incoming stun request generates a discovered peer reflexive, -+ * that causes the ready -> connected transition. -+ */ - priv_update_check_list_state_for_ready (agent, stream, component); - -- /* note: to take care of the controlling-controlling case in -- * aggressive nomination mode, send a new triggered -- * check to nominate the pair */ -+ /* note: this new check is required by the new-dribble test, -+ * when early icheck on the peer controlled agent causes an -+ * incoming stun request to an already succeeded (and -+ * nominated) pair on the controlling agent. If the -+ * controlling agent doesn't retrigger a check with -+ * USE-CANDIDATE=1, the peer agent has no way to nominate it. -+ * -+ * This behavior differs from ICE spec 7.2.1.4 -+ */ - if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 || - agent->compatibility == NICE_COMPATIBILITY_WLM2009 || - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) && -- agent->controlling_mode) -- priv_conn_check_initiate (agent, p); -+ agent->controlling_mode) { -+ priv_add_pair_to_triggered_check_queue (agent, p); -+ conn_check_schedule_next(agent); -+ } - } else if (p->state == NICE_CHECK_FAILED) { - /* 7.2.1.4 Triggered Checks - * If the state of the pair is Failed, it is changed to Waiting - and the agent MUST create a new connectivity check for that - pair (representing a new STUN Binding request transaction), by - enqueueing the pair in the triggered check queue. */ -- priv_conn_check_initiate (agent, p); -+ priv_add_pair_to_triggered_check_queue (agent, p); - } - - /* note: the spec says the we SHOULD retransmit in-progress -@@ -2121,7 +2375,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, - * - * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE) - */ --static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len, uint8_t *rbuf, gboolean use_candidate) -+static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len, uint8_t *rbuf, gboolean use_candidate) - { - g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE); - -@@ -2144,7 +2398,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Componen - priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate); - - if (use_candidate) -- priv_mark_pair_nominated (agent, stream, component, rcand); -+ priv_mark_pair_nominated (agent, stream, component, lcand, rcand); - } - } - -@@ -2156,7 +2410,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Componen - * - * @return non-zero on error, zero on success - */ --static int priv_store_pending_check (NiceAgent *agent, Component *component, -+static int priv_store_pending_check (NiceAgent *agent, NiceComponent *component, - const NiceAddress *from, NiceSocket *sockptr, uint8_t *username, - uint16_t username_len, uint32_t priority, gboolean use_candidate) - { -@@ -2190,19 +2444,28 @@ static int priv_store_pending_check (NiceAgent *agent, Component *component, - * - * @return created pair, or NULL on fatal (memory allocation) errors - */ --static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair) -+static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local_cand, CandidateCheckPair *parent_pair) - { - CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair); -- Stream *stream = agent_find_stream (agent, stream_id); -+ NiceStream *stream = agent_find_stream (agent, stream_id); - - pair->agent = agent; - pair->stream_id = stream_id; -- pair->component_id = component_id;; -+ pair->component_id = component->id;; - pair->local = local_cand; - pair->remote = parent_pair->remote; - pair->sockptr = local_cand->sockptr; - pair->state = NICE_CHECK_DISCOVERED; -- nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair); -+ nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair); -+ { -+ gchar tmpbuf1[INET6_ADDRSTRLEN]; -+ gchar tmpbuf2[INET6_ADDRSTRLEN]; -+ nice_address_to_string (&pair->local->addr, tmpbuf1); -+ nice_address_to_string (&pair->remote->addr, tmpbuf2); -+ nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair, -+ tmpbuf1, nice_address_get_port (&pair->local->addr), -+ tmpbuf2, nice_address_get_port (&pair->remote->addr)); -+ } - g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", - local_cand->foundation, parent_pair->remote->foundation); - if (agent->controlling_mode == TRUE) -@@ -2213,6 +2476,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint - pair->local->priority); - pair->nominated = FALSE; - pair->controlling = agent->controlling_mode; -+ pair->prflx_priority = ensure_unique_priority (component, -+ peer_reflexive_candidate_priority (agent, local_cand)); - nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation); - - stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair, -@@ -2230,11 +2495,13 @@ static void priv_recalculate_pair_priorities (NiceAgent *agent) - GSList *i, *j; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->conncheck_list; j; j = j->next) { - CandidateCheckPair *p = j->data; - p->priority = agent_candidate_pair_priority (agent, p->local, p->remote); - } -+ stream->conncheck_list = g_slist_sort (stream->conncheck_list, -+ (GCompareFunc)conn_check_compare); - } - } - -@@ -2269,21 +2536,21 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control) - * @param socketptr socket used to send the reply - * @param mapped_sockaddr mapped address in the response - * -- * @return pointer to a new pair if one was created, otherwise NULL -+ * @return pointer to a candidate pair, found in conncheck list or newly created - */ --static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate) -+static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate) - { - CandidateCheckPair *new_pair = NULL; - NiceAddress mapped; - GSList *i, *j; -- gboolean local_cand_matches = FALSE; -+ NiceCandidate *local_cand = NULL; - - nice_address_set_from_sockaddr (&mapped, mapped_sockaddr); - - for (j = component->local_candidates; j; j = j->next) { - NiceCandidate *cand = j->data; - if (nice_address_equal (&mapped, &cand->addr)) { -- local_cand_matches = TRUE; -+ local_cand = cand; - - /* We always need to select the peer-reflexive Candidate Pair in the case - * of a TCP-ACTIVE local candidate, so we find it even if an incoming -@@ -2300,31 +2567,38 @@ static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAg - } - } - -- if (local_cand_matches == TRUE) { -- /* note: this is same as "adding to VALID LIST" in the spec -- text */ -+ if (new_pair) { - p->state = NICE_CHECK_SUCCEEDED; - nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); - } - else { -- NiceCandidate *cand = -- discovery_add_peer_reflexive_candidate (agent, -- stream->id, -- component->id, -- &mapped, -- sockptr, -- local_candidate, -- remote_candidate); -- p->state = NICE_CHECK_FAILED; -- nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ if (!local_cand) { -+ if (!agent->force_relay) -+ local_cand = discovery_add_peer_reflexive_candidate (agent, -+ stream->id, -+ component->id, -+ &mapped, -+ sockptr, -+ local_candidate, -+ remote_candidate); -+ p->state = NICE_CHECK_FAILED; -+ nice_debug ("Agent %p : pair %p state FAILED", agent, p); -+ } - - /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2 - "Constructing a Valid Pair") */ -- new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p); -+ if (local_cand) -+ new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component, -+ local_cand, p); - nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair); - } - -+ /* note: this is same as "adding to VALID LIST" in the spec -+ text */ -+ if (new_pair) -+ new_pair->valid = TRUE; -+ - return new_pair; - } - -@@ -2335,7 +2609,7 @@ static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAg - * - * @return TRUE if a matching transaction is found - */ --static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp) -+static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp) - { - union { - struct sockaddr_storage storage; -@@ -2404,11 +2678,13 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream * - /* note: this is same as "adding to VALID LIST" in the spec - text */ - p->state = NICE_CHECK_SUCCEEDED; -+ p->valid = TRUE; -+ g_assert_not_reached (); - nice_debug ("Agent %p : Mapped address not found." - " conncheck %p SUCCEEDED.", agent, p); - priv_conn_check_unfreeze_related (agent, stream, p); - } else { -- ok_pair = priv_process_response_check_for_peer_reflexive (agent, -+ ok_pair = priv_process_response_check_for_reflexive (agent, - stream, component, p, sockptr, &sockaddr.addr, - local_candidate, remote_candidate); - } -@@ -2431,6 +2707,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream * - } - } - -+ priv_print_conn_check_lists (agent, G_STRFUNC, NULL); -+ - /* step: update pair states (ICE 7.1.2.2.3 "Updating pair - states" and 8.1.2 "Updating States", ID-19) */ - priv_update_check_list_state_for_ready (agent, stream, component); -@@ -2447,6 +2725,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream * - p->stun_message.buffer = NULL; - p->stun_message.buffer_len = 0; - p->state = NICE_CHECK_WAITING; -+ priv_add_pair_to_triggered_check_queue (agent, p); - nice_debug ("Agent %p : pair %p state WAITING", agent, p); - trans_found = TRUE; - } else { -@@ -2516,25 +2795,27 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa - d->pending = FALSE; - } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) { - /* case: successful binding discovery, create a new local candidate */ -- NiceAddress niceaddr; -- nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr); -- -- discovery_add_server_reflexive_candidate ( -- d->agent, -- d->stream->id, -- d->component->id, -- &niceaddr, -- NICE_CANDIDATE_TRANSPORT_UDP, -- d->nicesock, -- FALSE); -- if (d->agent->use_ice_tcp) -- discovery_discover_tcp_server_reflexive_candidates ( -+ -+ if (!agent->force_relay) { -+ NiceAddress niceaddr; -+ -+ nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr); -+ discovery_add_server_reflexive_candidate ( - d->agent, - d->stream->id, - d->component->id, - &niceaddr, -- d->nicesock); -- -+ NICE_CANDIDATE_TRANSPORT_UDP, -+ d->nicesock, -+ FALSE); -+ if (d->agent->use_ice_tcp) -+ discovery_discover_tcp_server_reflexive_candidates ( -+ d->agent, -+ d->stream->id, -+ d->component->id, -+ &niceaddr, -+ d->nicesock); -+ } - d->stun_message.buffer = NULL; - d->stun_message.buffer_len = 0; - d->done = TRUE; -@@ -2667,7 +2948,8 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - * on a TCP connection, which cannot be used for server-reflexive - * discovery of candidates. - */ -- if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP) { -+ if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP && -+ !agent->force_relay) { - discovery_add_server_reflexive_candidate ( - d->agent, - d->stream->id, -@@ -2729,6 +3011,9 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - } - - if (relay_cand) { -+ if (d->stun_resp_msg.buffer) -+ nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr, -+ &d->stun_resp_msg); - if (agent->compatibility == NICE_COMPATIBILITY_OC2007 || - agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { - /* These data are needed on TURN socket when sending requests, -@@ -2744,6 +3029,9 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage * - } else { - priv_add_new_turn_refresh (d, relay_cand, lifetime); - } -+ -+ /* In case a new candidate has been added */ -+ conn_check_schedule_next (agent); - } - - d->stun_message.buffer = NULL; -@@ -2889,7 +3177,7 @@ static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage * - - - static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent, -- Component *component, StunMessage *resp) -+ NiceComponent *component, StunMessage *resp) - { - StunTransactionId conncheck_id; - StunTransactionId response_id; -@@ -2917,8 +3205,8 @@ static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent, - - typedef struct { - NiceAgent *agent; -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - uint8_t *password; - } conncheck_validater_data; - -@@ -2957,7 +3245,7 @@ static bool conncheck_stun_validater (StunAgent *agent, - if (ufrag == NULL) - continue; - -- stun_debug ("Comparing username/ufrag of len %d and %zu, equal=%d", -+ stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d", - username_len, ufrag_len, username_len >= ufrag_len ? - memcmp (username, ufrag, ufrag_len) : 0); - stun_debug_bytes (" username: ", username, username_len); -@@ -3014,8 +3302,8 @@ static bool conncheck_stun_validater (StunAgent *agent, - * - * @return XXX (what FALSE means exactly?) - */ --gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, -- Component *component, NiceSocket *nicesock, const NiceAddress *from, -+gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, -+ NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from, - gchar *buf, guint len) - { - union { -@@ -3080,9 +3368,11 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, - valid == STUN_VALIDATION_UNMATCHED_RESPONSE) { - for (i = agent->refresh_list; i; i = i->next) { - CandidateRefresh *r = i->data; -- nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream, -- stream, r->component, component, r->nicesock, r->candidate->sockptr, -- nicesock); -+ -+ nice_debug_verbose ("Comparing %p to %p, %p to %p and %p and %p to %p", -+ r->stream, stream, r->component, component, r->nicesock, -+ r->candidate->sockptr, nicesock); -+ - if (r->stream == stream && r->component == component && - (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) { - valid = stun_agent_validate (&r->stun_agent, &req, -@@ -3294,16 +3584,22 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, - remote_candidate2 ? remote_candidate2 : remote_candidate); - if(remote_candidate) { - if (local_candidate && -- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) -- priv_conn_check_add_for_candidate_pair_matched (agent, -- stream->id, component, local_candidate, remote_candidate, NICE_CHECK_DISCOVERED); -- else -+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) { -+ CandidateCheckPair *pair; -+ -+ pair = priv_conn_check_add_for_candidate_pair_matched (agent, -+ stream->id, component, local_candidate, remote_candidate, -+ NICE_CHECK_DISCOVERED); -+ if (pair) { -+ pair->valid = TRUE; -+ } -+ } else - conn_check_add_for_candidate (agent, stream->id, component, remote_candidate); - } - } - -- priv_reply_to_conn_check (agent, stream, component, remote_candidate, -- from, nicesock, rbuf_len, rbuf, use_candidate); -+ priv_reply_to_conn_check (agent, stream, component, local_candidate, -+ remote_candidate, from, nicesock, rbuf_len, rbuf, use_candidate); - - if (component->remote_candidates == NULL) { - /* case: We've got a valid binding request to a local candidate -@@ -3360,7 +3656,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, - /* Remove all pointers to the given @sock from the connection checking process. - * These are entirely NiceCandidates pointed to from various places. */ - void --conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, -+conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, - NiceSocket *sock) - { - GSList *l; -@@ -3375,14 +3671,20 @@ conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, - } - - /* Prune from the candidate check pairs. */ -- for (l = stream->conncheck_list; l != NULL; l = l->next) { -+ for (l = stream->conncheck_list; l != NULL;) { - CandidateCheckPair *p = l->data; -+ GSList *next = l->next; - - if ((p->local != NULL && p->local->sockptr == sock) || -- (p->remote != NULL && p->remote->sockptr == sock)) { -+ (p->remote != NULL && p->remote->sockptr == sock) || -+ (p->sockptr == sock)) { - nice_debug ("Agent %p : Retransmissions failed, giving up on " - "connectivity check %p", agent, p); - candidate_check_pair_fail (stream, agent, p); -+ conn_check_free_item (p); -+ stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l); - } -+ -+ l = next; - } - } -diff --git a/agent/conncheck.h b/agent/conncheck.h -index e6c2c62..431c606 100644 ---- a/agent/conncheck.h -+++ b/agent/conncheck.h -@@ -87,26 +87,30 @@ struct _CandidateCheckPair - gboolean nominated; - gboolean controlling; - gboolean timer_restarted; -+ gboolean valid; - guint64 priority; -+ guint32 prflx_priority; - GTimeVal next_tick; /* next tick timestamp */ - StunTimer timer; - uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6]; - StunMessage stun_message; - }; - --int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote); --int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local); --gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote); -+int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote); -+int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local); -+gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local, NiceCandidate *remote); - void conn_check_free (NiceAgent *agent); --gboolean conn_check_schedule_next (NiceAgent *agent); -+void conn_check_schedule_next (NiceAgent *agent); - int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair); --void conn_check_prune_stream (NiceAgent *agent, Stream *stream); --gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); -+void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream); -+gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len); - gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b); --void conn_check_remote_candidates_set(NiceAgent *agent); -+void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component); - NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport); - void --conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, -+conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component, - NiceSocket *sock); - -+guint32 ensure_unique_priority (NiceComponent *component, guint32 priority); -+ - #endif /*_NICE_CONNCHECK_H */ -diff --git a/agent/debug.c b/agent/debug.c -index 6e69f9b..e1a298c 100644 ---- a/agent/debug.c -+++ b/agent/debug.c -@@ -48,17 +48,29 @@ - #include "agent-priv.h" - - static int debug_enabled = 0; -+static int debug_verbose_enabled = 0; - - #define NICE_DEBUG_STUN 1 - #define NICE_DEBUG_NICE 2 - #define NICE_DEBUG_PSEUDOTCP 4 - #define NICE_DEBUG_PSEUDOTCP_VERBOSE 8 -+#define NICE_DEBUG_NICE_VERBOSE 16 - - static const GDebugKey keys[] = { - { (gchar *)"stun", NICE_DEBUG_STUN }, - { (gchar *)"nice", NICE_DEBUG_NICE }, - { (gchar *)"pseudotcp", NICE_DEBUG_PSEUDOTCP }, - { (gchar *)"pseudotcp-verbose", NICE_DEBUG_PSEUDOTCP_VERBOSE }, -+ { (gchar *)"nice-verbose", NICE_DEBUG_NICE_VERBOSE }, -+ { NULL, 0}, -+}; -+ -+static const GDebugKey gkeys[] = { -+ { (gchar *)"libnice-stun", NICE_DEBUG_STUN }, -+ { (gchar *)"libnice", NICE_DEBUG_NICE }, -+ { (gchar *)"libnice-pseudotcp", NICE_DEBUG_PSEUDOTCP }, -+ { (gchar *)"libnice-pseudotcp-verbose", NICE_DEBUG_PSEUDOTCP_VERBOSE }, -+ { (gchar *)"libnice-verbose", NICE_DEBUG_NICE_VERBOSE }, - { NULL, 0}, - }; - -@@ -86,18 +98,30 @@ void nice_debug_init (void) - - if (flags_string) - flags = g_parse_debug_string (flags_string, keys, 4); -+ if (gflags_string) -+ flags |= g_parse_debug_string (gflags_string, gkeys, 4); - if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose")) - flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE; -+ if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) { -+ flags |= NICE_DEBUG_NICE_VERBOSE; -+ } - - stun_set_debug_handler (stun_handler); -- nice_debug_enable (TRUE); -+ debug_enabled = !!(flags & NICE_DEBUG_NICE); -+ if (flags & NICE_DEBUG_STUN) -+ stun_debug_enable (); -+ else -+ stun_debug_disable (); -+ -+ if (flags & NICE_DEBUG_NICE_VERBOSE) -+ debug_verbose_enabled = TRUE; - - /* Set verbose before normal so that if we use 'all', then only - normal debug is enabled, we'd need to set pseudotcp-verbose without the - pseudotcp flag in order to actually enable verbose pseudotcp */ - if (flags & NICE_DEBUG_PSEUDOTCP_VERBOSE) - pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE); -- else -+ else if (flags & NICE_DEBUG_PSEUDOTCP) - pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_NORMAL); - } - } -@@ -107,6 +131,10 @@ gboolean nice_debug_is_enabled (void) - { - return debug_enabled; - } -+gboolean nice_debug_is_verbose (void) -+{ -+ return debug_verbose_enabled; -+} - #else - /* Defined in agent-priv.h. */ - #endif -@@ -136,6 +164,15 @@ void nice_debug (const char *fmt, ...) - va_end (ap); - } - } -+void nice_debug_verbose (const char *fmt, ...) -+{ -+ va_list ap; -+ if (debug_verbose_enabled) { -+ va_start (ap, fmt); -+ g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fmt, ap); -+ va_end (ap); -+ } -+} - #else - /* Defined in agent-priv.h. */ - #endif -diff --git a/agent/discovery.c b/agent/discovery.c -index f3a702d..7a890a0 100644 ---- a/agent/discovery.c -+++ b/agent/discovery.c -@@ -305,7 +305,7 @@ void refresh_cancel (CandidateRefresh *refresh) - * defined in ICE spec section 4.1.3 "Eliminating Redundant - * Candidates" (ID-19). - */ --static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *candidate) -+static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *candidate) - { - GSList *i; - -@@ -329,7 +329,7 @@ static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_ - return TRUE; - } - --static guint priv_highest_remote_foundation (Component *component) -+static guint priv_highest_remote_foundation (NiceComponent *component) - { - GSList *i; - guint highest = 1; -@@ -382,9 +382,9 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate) - GSList *i, *j, *k; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *component = j->data; -+ NiceComponent *component = j->data; - for (k = component->local_candidates; k; k = k->next) { - NiceCandidate *n = k->data; - -@@ -393,7 +393,6 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate) - - if (candidate->type == n->type && - candidate->transport == n->transport && -- candidate->stream_id == n->stream_id && - nice_address_equal_no_port (&candidate->base_addr, &n->base_addr) && - (candidate->type != NICE_CANDIDATE_TYPE_RELAYED || - priv_compare_turn_servers (candidate->turn, n->turn)) && -@@ -427,12 +426,12 @@ static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *cand - { - GSList *i, *j, *k; - guint next_remote_id; -- Component *component = NULL; -+ NiceComponent *component = NULL; - - for (i = agent->streams; i; i = i->next) { -- Stream *stream = i->data; -+ NiceStream *stream = i->data; - for (j = stream->components; j; j = j->next) { -- Component *c = j->data; -+ NiceComponent *c = j->data; - - if (c->id == candidate->component_id) - component = c; -@@ -523,8 +522,8 @@ HostCandidateResult discovery_add_local_host_candidate ( - NiceCandidate **outcandidate) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceSocket *nicesock = NULL; - HostCandidateResult res = HOST_CANDIDATE_FAILED; - -@@ -550,6 +549,8 @@ HostCandidateResult discovery_add_local_host_candidate ( - agent->reliable, FALSE); - } - -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_generate_candidate_credentials (agent, candidate); - priv_assign_foundation (agent, candidate); - -@@ -580,7 +581,7 @@ HostCandidateResult discovery_add_local_host_candidate ( - } - - _priv_set_socket_tos (agent, nicesock, stream->tos); -- component_attach_socket (component, nicesock); -+ nice_component_attach_socket (component, nicesock); - - *outcandidate = candidate; - -@@ -610,8 +611,8 @@ discovery_add_server_reflexive_candidate ( - gboolean nat_assisted) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - gboolean result = FALSE; - - if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) -@@ -623,6 +624,10 @@ discovery_add_server_reflexive_candidate ( - candidate->component_id = component_id; - candidate->addr = *address; - -+ /* step: link to the base candidate+socket */ -+ candidate->sockptr = base_socket; -+ candidate->base_addr = base_socket->addr; -+ - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - candidate->priority = nice_candidate_jingle_priority (candidate); - } else if (agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -636,10 +641,8 @@ discovery_add_server_reflexive_candidate ( - agent->reliable, nat_assisted); - } - -- /* step: link to the base candidate+socket */ -- candidate->sockptr = base_socket; -- candidate->base_addr = base_socket->addr; -- -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_generate_candidate_credentials (agent, candidate); - priv_assign_foundation (agent, candidate); - -@@ -670,8 +673,8 @@ discovery_discover_tcp_server_reflexive_candidates ( - NiceAddress *address, - NiceSocket *base_socket) - { -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceAddress base_addr = base_socket->addr; - GSList *i; - -@@ -718,8 +721,8 @@ discovery_add_relay_candidate ( - TurnServer *turn) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - NiceSocket *relay_socket = NULL; - - if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) -@@ -732,6 +735,17 @@ discovery_add_relay_candidate ( - candidate->addr = *address; - candidate->turn = turn_server_ref (turn); - -+ /* step: link to the base candidate+socket */ -+ relay_socket = nice_udp_turn_socket_new (agent->main_context, address, -+ base_socket, &turn->server, -+ turn->username, turn->password, -+ agent_to_turn_socket_compatibility (agent)); -+ if (!relay_socket) -+ goto errors; -+ -+ candidate->sockptr = relay_socket; -+ candidate->base_addr = base_socket->addr; -+ - if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) { - candidate->priority = nice_candidate_jingle_priority (candidate); - } else if (agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -745,17 +759,8 @@ discovery_add_relay_candidate ( - agent->reliable, FALSE); - } - -- /* step: link to the base candidate+socket */ -- relay_socket = nice_udp_turn_socket_new (agent->main_context, address, -- base_socket, &turn->server, -- turn->username, turn->password, -- agent_to_turn_socket_compatibility (agent)); -- if (!relay_socket) -- goto errors; -- -- candidate->sockptr = relay_socket; -- candidate->base_addr = base_socket->addr; -- -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_generate_candidate_credentials (agent, candidate); - - /* Google uses the turn username as the candidate username */ -@@ -769,7 +774,7 @@ discovery_add_relay_candidate ( - if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate)) - goto errors; - -- component_attach_socket (component, relay_socket); -+ nice_component_attach_socket (component, relay_socket); - agent_signal_new_candidate (agent, candidate); - - return candidate; -@@ -798,8 +803,8 @@ discovery_add_peer_reflexive_candidate ( - NiceCandidate *remote) - { - NiceCandidate *candidate; -- Component *component; -- Stream *stream; -+ NiceComponent *component; -+ NiceStream *stream; - gboolean result; - - if (!agent_find_component (agent, stream_id, component_id, &stream, &component)) -@@ -836,6 +841,8 @@ discovery_add_peer_reflexive_candidate ( - agent->reliable, FALSE); - } - -+ candidate->priority = ensure_unique_priority (component, -+ candidate->priority); - priv_assign_foundation (agent, candidate); - - if ((agent->compatibility == NICE_COMPATIBILITY_MSN || -@@ -891,8 +898,8 @@ discovery_add_peer_reflexive_candidate ( - */ - NiceCandidate *discovery_learn_remote_peer_reflexive_candidate ( - NiceAgent *agent, -- Stream *stream, -- Component *component, -+ NiceStream *stream, -+ NiceComponent *component, - guint32 priority, - const NiceAddress *remote_address, - NiceSocket *nicesock, -@@ -1023,10 +1030,12 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer) - (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || - cand->type == NICE_CANDIDATE_TYPE_RELAYED)) { - -- agent_signal_component_state_change (agent, -- cand->stream->id, -- cand->component->id, -- NICE_COMPONENT_STATE_GATHERING); -+ if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED || -+ cand->component->state == NICE_COMPONENT_STATE_FAILED) -+ agent_signal_component_state_change (agent, -+ cand->stream->id, -+ cand->component->id, -+ NICE_COMPONENT_STATE_GATHERING); - - if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) { - buffer_len = stun_usage_bind_create (&cand->stun_agent, -diff --git a/agent/discovery.h b/agent/discovery.h -index c22ea6a..67e2186 100644 ---- a/agent/discovery.h -+++ b/agent/discovery.h -@@ -53,8 +53,8 @@ typedef struct - GTimeVal next_tick; /* next tick timestamp */ - gboolean pending; /* is discovery in progress? */ - gboolean done; /* is discovery complete? */ -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - TurnServer *turn; - StunAgent stun_agent; - StunTimer timer; -@@ -70,8 +70,8 @@ typedef struct - NiceSocket *nicesock; /* existing socket to use */ - NiceAddress server; /* STUN/TURN server address */ - NiceCandidate *candidate; /* candidate to refresh */ -- Stream *stream; -- Component *component; -+ NiceStream *stream; -+ NiceComponent *component; - StunAgent stun_agent; - GSource *timer_source; - GSource *tick_source; -@@ -151,8 +151,8 @@ discovery_add_peer_reflexive_candidate ( - NiceCandidate * - discovery_learn_remote_peer_reflexive_candidate ( - NiceAgent *agent, -- Stream *stream, -- Component *component, -+ NiceStream *stream, -+ NiceComponent *component, - guint32 priority, - const NiceAddress *remote_address, - NiceSocket *udp_socket, -diff --git a/agent/inputstream.c b/agent/inputstream.c -index b9c5369..58a4a0d 100644 ---- a/agent/inputstream.c -+++ b/agent/inputstream.c -@@ -332,8 +332,8 @@ nice_input_stream_close (GInputStream *stream, GCancellable *cancellable, - GError **error) - { - NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - NiceAgent *agent; /* owned */ - - /* Has the agent disappeared? */ -@@ -361,8 +361,8 @@ static gboolean - nice_input_stream_is_readable (GPollableInputStream *stream) - { - NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - gboolean retval = FALSE; - GSList *i; - NiceAgent *agent; /* owned */ -@@ -458,7 +458,7 @@ nice_input_stream_create_source (GPollableInputStream *stream, - if (agent == NULL) - goto dummy_source; - -- component_source = component_input_source_new (agent, priv->stream_id, -+ component_source = nice_component_input_source_new (agent, priv->stream_id, - priv->component_id, stream, cancellable); - - g_object_unref (agent); -diff --git a/agent/outputstream.c b/agent/outputstream.c -index d479aa5..4c918a7 100644 ---- a/agent/outputstream.c -+++ b/agent/outputstream.c -@@ -476,8 +476,8 @@ nice_output_stream_close (GOutputStream *stream, GCancellable *cancellable, - GError **error) - { - NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - NiceAgent *agent; /* owned */ - - /* Has the agent disappeared? */ -@@ -505,8 +505,8 @@ static gboolean - nice_output_stream_is_writable (GPollableOutputStream *stream) - { - NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - gboolean retval = FALSE; - NiceAgent *agent; /* owned */ - -@@ -595,8 +595,8 @@ nice_output_stream_create_source (GPollableOutputStream *stream, - { - NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; - GSource *component_source = NULL; -- Component *component = NULL; -- Stream *_stream = NULL; -+ NiceComponent *component = NULL; -+ NiceStream *_stream = NULL; - NiceAgent *agent; /* owned */ - - component_source = g_pollable_source_new (G_OBJECT (stream)); -diff --git a/agent/pseudotcp.c b/agent/pseudotcp.c -index eb91e3c..3160c34 100644 ---- a/agent/pseudotcp.c -+++ b/agent/pseudotcp.c -@@ -77,8 +77,19 @@ - #include "pseudotcp.h" - #include "agent-priv.h" - --G_DEFINE_TYPE (PseudoTcpSocket, pseudo_tcp_socket, G_TYPE_OBJECT); -+struct _PseudoTcpSocketClass { -+ GObjectClass parent_class; -+}; -+ -+typedef struct _PseudoTcpSocketPrivate PseudoTcpSocketPrivate; -+ -+ -+struct _PseudoTcpSocket { -+ GObject parent; -+ PseudoTcpSocketPrivate *priv; -+}; - -+G_DEFINE_TYPE (PseudoTcpSocket, pseudo_tcp_socket, G_TYPE_OBJECT); - - ////////////////////////////////////////////////////////////////////// - // Network Constants -@@ -107,7 +118,9 @@ const guint16 PACKET_MAXIMUMS[] = { - 0, // End of list marker - }; - --#define MAX_PACKET 65535 -+// FIXME: This is a reasonable MTU, but we should get it from the lower layer -+#define DEF_MTU 1400 -+#define MAX_PACKET 65532 - // Note: we removed lowest level because packet overhead was larger! - #define MIN_PACKET 296 - -@@ -151,8 +164,8 @@ const guint16 PACKET_MAXIMUMS[] = { - #define PACKET_OVERHEAD (HEADER_SIZE + UDP_HEADER_SIZE + \ - IP_HEADER_SIZE + JINGLE_HEADER_SIZE) - --// MIN_RTO = 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second") --#define MIN_RTO 250 -+// MIN_RTO = 1 second (RFC6298, Sec 2.4) -+#define MIN_RTO 1000 - #define DEF_RTO 1000 /* 1 seconds (RFC 6298 sect 2.1) */ - #define MAX_RTO 60000 /* 60 seconds */ - #define DEFAULT_ACK_DELAY 100 /* 100 milliseconds */ -@@ -416,6 +429,7 @@ typedef enum { - sfImmediateAck, - sfFin, - sfRst, -+ sfDuplicateAck, - } SendFlags; - - typedef struct { -@@ -471,6 +485,7 @@ struct _PseudoTcpSocketPrivate { - guint32 rbuf_len, rcv_nxt, rcv_wnd, lastrecv; - guint8 rwnd_scale; // Window scale factor - PseudoTcpFifo rbuf; -+ guint32 rcv_fin; /* sequence number of the received FIN octet, or 0 */ - - // Outgoing data - GQueue slist; -@@ -495,7 +510,9 @@ struct _PseudoTcpSocketPrivate { - guint32 ssthresh, cwnd; - guint8 dup_acks; - guint32 recover; -+ gboolean fast_recovery; - guint32 t_ack; /* time a delayed ack was scheduled; 0 if no acks scheduled */ -+ guint32 last_acked_ts; - - gboolean use_nagling; - guint32 ack_delay; -@@ -550,7 +567,7 @@ static gboolean parse (PseudoTcpSocket *self, - const guint8 *_header_buf, gsize header_buf_len, - const guint8 *data_buf, gsize data_buf_len); - static gboolean process(PseudoTcpSocket *self, Segment *seg); --static gboolean transmit(PseudoTcpSocket *self, SSegment *sseg, guint32 now); -+static int transmit(PseudoTcpSocket *self, SSegment *sseg, guint32 now); - static void attempt_send(PseudoTcpSocket *self, SendFlags sflags); - static void closedown (PseudoTcpSocket *self, guint32 err, - ClosedownSource source); -@@ -566,6 +583,7 @@ static void set_state_closed (PseudoTcpSocket *self, guint32 err); - static const gchar *pseudo_tcp_state_get_name (PseudoTcpState state); - static gboolean pseudo_tcp_state_has_sent_fin (PseudoTcpState state); - static gboolean pseudo_tcp_state_has_received_fin (PseudoTcpState state); -+static gboolean pseudo_tcp_state_has_received_fin_ack (PseudoTcpState state); - - // The following logging is for detailed (packet-level) pseudotcp analysis only. - static PseudoTcpDebugLevel debug_level = PSEUDO_TCP_DEBUG_NONE; -@@ -809,12 +827,14 @@ pseudo_tcp_socket_init (PseudoTcpSocket *obj) - priv->snd_una = priv->rcv_nxt = 0; - priv->bReadEnable = TRUE; - priv->bWriteEnable = FALSE; -+ priv->rcv_fin = 0; -+ - priv->t_ack = 0; - - priv->msslevel = 0; - priv->largest = 0; - priv->mss = MIN_PACKET - PACKET_OVERHEAD; -- priv->mtu_advise = MAX_PACKET; -+ priv->mtu_advise = DEF_MTU; - - priv->rto_base = 0; - -@@ -825,6 +845,7 @@ pseudo_tcp_socket_init (PseudoTcpSocket *obj) - - priv->dup_acks = 0; - priv->recover = 0; -+ priv->last_acked_ts = 0; - - priv->ts_recent = priv->ts_lastack = 0; - -@@ -959,18 +980,24 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self) - // retransmit segments - guint32 nInFlight; - guint32 rto_limit; -+ int transmit_status; - - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "timeout retransmit (rto: %u) " - "(rto_base: %u) (now: %u) (dup_acks: %u)", - priv->rx_rto, priv->rto_base, now, (guint) priv->dup_acks); - -- if (!transmit(self, g_queue_peek_head (&priv->slist), now)) { -- closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); -+ transmit_status = transmit(self, g_queue_peek_head (&priv->slist), now); -+ if (transmit_status != 0) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Error transmitting segment. Closing down."); -+ closedown (self, transmit_status, CLOSEDOWN_LOCAL); - return; - } - - nInFlight = priv->snd_nxt - priv->snd_una; - priv->ssthresh = max(nInFlight / 2, 2 * priv->mss); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "ssthresh: %u = (nInFlight: %u / 2) + " -+ "2 * mss: %u", priv->ssthresh, nInFlight, priv->mss); - //LOG(LS_INFO) << "priv->ssthresh: " << priv->ssthresh << " nInFlight: " << nInFlight << " priv->mss: " << priv->mss; - priv->cwnd = priv->mss; - -@@ -978,6 +1005,13 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self) - rto_limit = (priv->state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO; - priv->rx_rto = min(rto_limit, priv->rx_rto * 2); - priv->rto_base = now; -+ -+ priv->recover = priv->snd_nxt; -+ if (priv->dup_acks >= 3) { -+ priv->dup_acks = 0; -+ priv->fast_recovery = FALSE; -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery on timeout"); -+ } - } - } - -@@ -985,6 +1019,7 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self) - if ((priv->snd_wnd == 0) - && (time_diff(priv->lastsend + priv->rx_rto, now) <= 0)) { - if (time_diff(now, priv->lastrecv) >= 15000) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Receive window closed. Closing down."); - closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); - return; - } -@@ -1012,9 +1047,11 @@ pseudo_tcp_socket_notify_packet(PseudoTcpSocket *self, - - if (len > MAX_PACKET) { - //LOG_F(WARNING) << "packet too large"; -+ self->priv->error = EMSGSIZE; - return FALSE; - } else if (len < HEADER_SIZE) { - //LOG_F(WARNING) << "packet too small"; -+ self->priv->error = EINVAL; - return FALSE; - } - -@@ -1149,9 +1186,7 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len) - gsize available_space; - - /* Received a FIN from the peer, so return 0. RFC 793, §3.5, Case 2. */ -- if (priv->support_fin_ack && -- (priv->shutdown_reads || -- pseudo_tcp_state_has_received_fin (priv->state))) { -+ if (priv->support_fin_ack && priv->shutdown_reads) { - return 0; - } - -@@ -1173,7 +1208,9 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len) - bytesread = pseudo_tcp_fifo_read (&priv->rbuf, (guint8 *) buffer, len); - - // If there's no data in |m_rbuf|. -- if (bytesread == 0) { -+ if (bytesread == 0 && -+ !(pseudo_tcp_state_has_received_fin (priv->state) || -+ pseudo_tcp_state_has_received_fin_ack (priv->state))) { - priv->bReadEnable = TRUE; - priv->error = EWOULDBLOCK; - return -1; -@@ -1407,7 +1444,7 @@ packet(PseudoTcpSocket *self, guint32 seq, TcpFlags flags, - g_assert (bytes_read == len); - } - -- DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "<-- <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Sending <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" - "<WND=%u><TS=%u><TSR=%u><LEN=%u>", - priv->conv, (unsigned)flags, seq, seq + len, priv->rcv_nxt, priv->rcv_wnd, - now % 10000, priv->ts_recent % 10000, len); -@@ -1460,7 +1497,8 @@ parse (PseudoTcpSocket *self, const guint8 *_header_buf, gsize header_buf_len, - seg.data = (const gchar *) data_buf; - seg.len = data_buf_len; - -- DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "--> <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, -+ "Received <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>" - "<WND=%u><TS=%u><TSR=%u><LEN=%u>", - seg.conv, (unsigned)seg.flags, seg.seq, seg.seq + seg.len, seg.ack, - seg.wnd, seg.tsval % 10000, seg.tsecr % 10000, seg.len); -@@ -1516,6 +1554,30 @@ pseudo_tcp_state_has_received_fin (PseudoTcpState state) - } - } - -+/* True iff the @state requires that a FIN-ACK has already been received from -+ * the peer. */ -+static gboolean -+pseudo_tcp_state_has_received_fin_ack (PseudoTcpState state) -+{ -+ switch (state) { -+ case TCP_LISTEN: -+ case TCP_SYN_SENT: -+ case TCP_SYN_RECEIVED: -+ case TCP_ESTABLISHED: -+ case TCP_FIN_WAIT_1: -+ case TCP_FIN_WAIT_2: -+ case TCP_CLOSING: -+ case TCP_CLOSE_WAIT: -+ case TCP_LAST_ACK: -+ return FALSE; -+ case TCP_CLOSED: -+ case TCP_TIME_WAIT: -+ return TRUE; -+ default: -+ return FALSE; -+ } -+} -+ - static gboolean - process(PseudoTcpSocket *self, Segment *seg) - { -@@ -1529,6 +1591,7 @@ process(PseudoTcpSocket *self, Segment *seg) - gsize available_space; - guint32 kIdealRefillSize; - gboolean is_valuable_ack, is_duplicate_ack, is_fin_ack = FALSE; -+ gboolean received_fin = FALSE; - - /* If this is the wrong conversation, send a reset!?! - (with the correct conversation?) */ -@@ -1545,17 +1608,23 @@ process(PseudoTcpSocket *self, Segment *seg) - priv->bOutgoing = FALSE; - - if (priv->state == TCP_CLOSED || -- (pseudo_tcp_state_has_sent_fin (priv->state) && seg->len > 0)) { -- /* Send an RST segment. See: RFC 1122, §4.2.2.13. */ -+ (pseudo_tcp_state_has_received_fin_ack (priv->state) && seg->len > 0)) { -+ /* Send an RST segment. See: RFC 1122, §4.2.2.13; RFC 793, §3.4, point 3, -+ * page 37. We can only send RST if we know the peer knows we’re closed; -+ * otherwise this could be a timeout retransmit from them, due to our -+ * packets from data through to FIN being dropped. */ -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Segment received while closed; sending RST."); - if ((seg->flags & FLAG_RST) == 0) { - closedown (self, 0, CLOSEDOWN_LOCAL); - } -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Segment received while closed; sent RST."); -+ - return FALSE; - } - - // Check if this is a reset segment - if (seg->flags & FLAG_RST) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Received RST segment; closing down."); - closedown (self, ECONNRESET, CLOSEDOWN_REMOTE); - return FALSE; - } -@@ -1607,18 +1676,20 @@ process(PseudoTcpSocket *self, Segment *seg) - priv->rx_rttvar = rtt / 2; - } else { - priv->rx_rttvar = (3 * priv->rx_rttvar + -- abs((long)(rtt - priv->rx_srtt))) / 4; -+ labs((long)(rtt - priv->rx_srtt))) / 4; - priv->rx_srtt = (7 * priv->rx_srtt + rtt) / 8; - } - priv->rx_rto = bound(MIN_RTO, - priv->rx_srtt + max(1LU, 4 * priv->rx_rttvar), MAX_RTO); - -- DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "rtt: %ld srtt: %u rto: %u", -- rtt, priv->rx_srtt, priv->rx_rto); -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "rtt: %ld srtt: %u rttvar: %u rto: %u", -+ rtt, priv->rx_srtt, priv->rx_rttvar, priv->rx_rto); - } else { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Invalid RTT: %ld", rtt); - return FALSE; - } -+ -+ priv->last_acked_ts = seg->tsecr; - } - - priv->snd_wnd = seg->wnd << priv->swnd_scale; -@@ -1663,16 +1734,24 @@ process(PseudoTcpSocket *self, Segment *seg) - if (LARGER_OR_EQUAL (priv->snd_una, priv->recover)) { // NewReno - guint32 nInFlight = priv->snd_nxt - priv->snd_una; - // (Fast Retransmit) -- priv->cwnd = min(priv->ssthresh, nInFlight + priv->mss); -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery"); -+ priv->cwnd = min(priv->ssthresh, -+ max (nInFlight, priv->mss) + priv->mss); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery cwnd=%d ssthresh=%d nInFlight=%d mss: %d", priv->cwnd, priv->ssthresh, nInFlight, priv->mss); -+ priv->fast_recovery = FALSE; - priv->dup_acks = 0; - } else { -+ int transmit_status; -+ - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit"); -- if (!transmit(self, g_queue_peek_head (&priv->slist), now)) { -- closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); -+ transmit_status = transmit(self, g_queue_peek_head (&priv->slist), now); -+ if (transmit_status != 0) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Error transmitting recovery retransmit segment. Closing down."); -+ closedown (self, transmit_status, CLOSEDOWN_LOCAL); - return FALSE; - } -- priv->cwnd += priv->mss - min(nAcked, priv->cwnd); -+ priv->cwnd += (nAcked > priv->mss ? priv->mss : 0) - -+ min(nAcked, priv->cwnd); - } - } else { - priv->dup_acks = 0; -@@ -1695,20 +1774,43 @@ process(PseudoTcpSocket *self, Segment *seg) - guint32 nInFlight; - - priv->dup_acks += 1; -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Received dup ack (dups: %u)", -+ priv->dup_acks); - if (priv->dup_acks == 3) { // (Fast Retransmit) -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "enter recovery"); -- DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit"); -- if (!transmit(self, g_queue_peek_head (&priv->slist), now)) { -- closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); -- return FALSE; -+ int transmit_status; -+ -+ -+ if (LARGER_OR_EQUAL (priv->snd_una, priv->recover) || -+ seg->tsecr == priv->last_acked_ts) { /* NewReno */ -+ /* Invoke fast retransmit RFC3782 section 3 step 1A*/ -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "enter recovery"); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit"); -+ -+ transmit_status = transmit(self, g_queue_peek_head (&priv->slist), -+ now); -+ if (transmit_status != 0) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "Error transmitting recovery retransmit segment. Closing down."); -+ -+ closedown (self, transmit_status, CLOSEDOWN_LOCAL); -+ return FALSE; -+ } -+ priv->recover = priv->snd_nxt; -+ nInFlight = priv->snd_nxt - priv->snd_una; -+ priv->ssthresh = max(nInFlight / 2, 2 * priv->mss); -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, -+ "ssthresh: %u = max((nInFlight: %u / 2), 2 * mss: %u)", -+ priv->ssthresh, nInFlight, priv->mss); -+ priv->cwnd = priv->ssthresh + 3 * priv->mss; -+ priv->fast_recovery = TRUE; -+ } else { -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, -+ "Skipping fast recovery: recover: %u snd_una: %u", priv->recover, -+ priv->snd_una); - } -- priv->recover = priv->snd_nxt; -- nInFlight = priv->snd_nxt - priv->snd_una; -- priv->ssthresh = max(nInFlight / 2, 2 * priv->mss); -- //LOG(LS_INFO) << "priv->ssthresh: " << priv->ssthresh << " nInFlight: " << nInFlight << " priv->mss: " << priv->mss; -- priv->cwnd = priv->ssthresh + 3 * priv->mss; - } else if (priv->dup_acks > 3) { -- priv->cwnd += priv->mss; -+ if (priv->fast_recovery) -+ priv->cwnd += priv->mss; - } - } else { - priv->dup_acks = 0; -@@ -1720,19 +1822,34 @@ process(PseudoTcpSocket *self, Segment *seg) - set_state_established (self); - } - -- /* Check for connection closure. */ -+ /* Check for connection closure. Only pay attention to FIN segments if they -+ * are in sequence; otherwise we’ve missed a packet earlier in the stream and -+ * need to request retransmission first. */ - if (priv->support_fin_ack) { -+ /* @received_fin is set when, and only when, all segments preceding the FIN -+ * have been acknowledged. This is to handle the case where the FIN arrives -+ * out of order with a preceding data segment. */ -+ if (seg->flags & FLAG_FIN && priv->rcv_fin == 0) { -+ priv->rcv_fin = seg->seq; -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Setting rcv_fin = %u", priv->rcv_fin); -+ } else if (seg->flags & FLAG_FIN && seg->seq != priv->rcv_fin) { -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Second FIN segment received; ignored"); -+ return FALSE; -+ } -+ - /* For the moment, FIN segments must not contain data. */ - if (seg->flags & FLAG_FIN && seg->len != 0) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "FIN segment contained data; ignored"); - return FALSE; - } - -+ received_fin = (priv->rcv_nxt != 0 && priv->rcv_nxt + seg->len == priv->rcv_fin); -+ - /* Update the state machine, implementing all transitions on ‘rcv FIN’ or - * ‘rcv ACK of FIN’ from RFC 793, Figure 6; and RFC 1122, §4.2.2.8. */ - switch (priv->state) { - case TCP_ESTABLISHED: -- if (seg->flags & FLAG_FIN) { -+ if (received_fin) { - /* Received a FIN from the network, RFC 793, §3.5, Case 2. - * The code below will send an ACK for the FIN. */ - set_state (self, TCP_CLOSE_WAIT); -@@ -1751,20 +1868,20 @@ process(PseudoTcpSocket *self, Segment *seg) - } - break; - case TCP_FIN_WAIT_1: -- if (is_fin_ack && seg->flags & FLAG_FIN) { -+ if (is_fin_ack && received_fin) { - /* Simultaneous close with an ACK for a FIN previously sent, - * RFC 793, §3.5, Case 3. */ - set_state (self, TCP_TIME_WAIT); - } else if (is_fin_ack) { - /* Handle the ACK of a locally-sent FIN flag. RFC 793, §3.5, Case 1. */ - set_state (self, TCP_FIN_WAIT_2); -- } else if (seg->flags & FLAG_FIN) { -+ } else if (received_fin) { - /* Simultaneous close, RFC 793, §3.5, Case 3. */ - set_state (self, TCP_CLOSING); - } - break; - case TCP_FIN_WAIT_2: -- if (seg->flags & FLAG_FIN) { -+ if (received_fin) { - /* Local user closed the connection, RFC 793, §3.5, Case 1. */ - set_state (self, TCP_TIME_WAIT); - } -@@ -1776,7 +1893,7 @@ process(PseudoTcpSocket *self, Segment *seg) - case TCP_CLOSED: - case TCP_CLOSE_WAIT: - /* Shouldn’t ever hit these cases. */ -- if (seg->flags & FLAG_FIN) { -+ if (received_fin) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, - "Unexpected state %u when FIN received", priv->state); - } else if (is_fin_ack) { -@@ -1820,19 +1937,20 @@ process(PseudoTcpSocket *self, Segment *seg) - * see RFC 793, §3.3. Also see: RFC 793, §3.5. - */ - if (seg->seq != priv->rcv_nxt) { -- sflags = sfImmediateAck; // (Fast Recovery) -+ sflags = sfDuplicateAck; // (Fast Recovery) - } else if (seg->len != 0) { - if (priv->ack_delay == 0) { - sflags = sfImmediateAck; - } else { - sflags = sfDelayedAck; - } -- } else if (seg->flags & FLAG_FIN) { -+ } else if (received_fin) { -+ /* FIN flags have a sequence number. Only acknowledge them after all -+ * preceding octets have been acknowledged. */ - sflags = sfImmediateAck; -- priv->rcv_nxt += 1; - } - -- if (sflags == sfImmediateAck) { -+ if (sflags == sfDuplicateAck) { - if (seg->seq > priv->rcv_nxt) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "too new"); - } else if (SMALLER_OR_EQUAL(seg->seq + seg->len, priv->rcv_nxt)) { -@@ -1869,12 +1987,7 @@ process(PseudoTcpSocket *self, Segment *seg) - - bNewData = FALSE; - -- if (seg->flags & FLAG_FIN) { -- /* FIN flags have a sequence number. */ -- if (seg->seq == priv->rcv_nxt) { -- priv->rcv_nxt++; -- } -- } else if (seg->len > 0) { -+ if (seg->len > 0) { - if (bIgnoreData) { - if (seg->seq == priv->rcv_nxt) { - priv->rcv_nxt += seg->len; -@@ -1929,6 +2042,12 @@ process(PseudoTcpSocket *self, Segment *seg) - } - } - -+ if (received_fin) { -+ /* FIN flags have a sequence number. */ -+ priv->rcv_nxt++; -+ } -+ -+ - attempt_send(self, sflags); - - // If we have new data, notify the user -@@ -1952,7 +2071,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - - if (segment->xmit >= ((priv->state == TCP_ESTABLISHED) ? 15 : 30)) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "too many retransmits"); -- return FALSE; -+ return ETIMEDOUT; - } - - while (TRUE) { -@@ -1972,7 +2091,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - - if (wres == WR_FAIL) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "packet failed"); -- return FALSE; -+ return ECONNABORTED; /* FIXME: This error code doesn’t quite seem right */ - } - - g_assert(wres == WR_TOO_LARGE); -@@ -1980,7 +2099,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - while (TRUE) { - if (PACKET_MAXIMUMS[priv->msslevel + 1] == 0) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "MTU too small"); -- return FALSE; -+ return EMSGSIZE; - } - /* !?! We need to break up all outstanding and pending packets - and then retransmit!?! */ -@@ -2029,7 +2148,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now) - priv->rto_base = now; - } - -- return TRUE; -+ return 0; - } - - static void -@@ -2039,6 +2158,8 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - guint32 now = get_current_time (self); - gboolean bFirst = TRUE; - -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Attempting send with flags %u.", sflags); -+ - if (time_diff(now, priv->lastsend) > (long) priv->rx_rto) { - priv->cwnd = priv->mss; - } -@@ -2053,6 +2174,7 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - gsize snd_buffered; - GList *iter; - SSegment *sseg; -+ int transmit_status; - - cwnd = priv->cwnd; - if ((priv->dup_acks == 1) || (priv->dup_acks == 2)) { // Limited Transmit -@@ -2078,12 +2200,19 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - - if (bFirst) { - gsize available_space = pseudo_tcp_fifo_get_write_remaining (&priv->sbuf); -+ - bFirst = FALSE; - DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "[cwnd: %u nWindow: %u nInFlight: %u " - "nAvailable: %u nQueued: %" G_GSIZE_FORMAT " nEmpty: %" G_GSIZE_FORMAT -- " ssthresh: %u]", -+ " nWaiting: %zu ssthresh: %u]", - priv->cwnd, nWindow, nInFlight, nAvailable, snd_buffered, -- available_space, priv->ssthresh); -+ available_space, snd_buffered - nInFlight, priv->ssthresh); -+ } -+ -+ if (sflags == sfDuplicateAck) { -+ packet(self, priv->snd_nxt, 0, 0, 0, now); -+ sflags = sfNone; -+ continue; - } - - if (nAvailable == 0 && sflags != sfFin && sflags != sfRst) { -@@ -2091,7 +2220,8 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - return; - - // If this is an immediate ack, or the second delayed ack -- if ((sflags == sfImmediateAck) || priv->t_ack) { -+ if ((sflags == sfImmediateAck || sflags == sfDuplicateAck) || -+ priv->t_ack) { - packet(self, priv->snd_nxt, 0, 0, 0, now); - } else { - priv->t_ack = now; -@@ -2128,9 +2258,12 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags) - subseg); - } - -- if (!transmit(self, sseg, now)) { -+ transmit_status = transmit(self, sseg, now); -+ if (transmit_status != 0) { - DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "transmit failed"); -- // TODO: consider closing socket -+ -+ // TODO: Is this the right thing ? -+ closedown (self, transmit_status, CLOSEDOWN_REMOTE); - return; - } - -@@ -2147,6 +2280,9 @@ closedown (PseudoTcpSocket *self, guint32 err, ClosedownSource source) - { - PseudoTcpSocketPrivate *priv = self->priv; - -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Closing down socket %p with %s error %u.", -+ self, (source == CLOSEDOWN_LOCAL) ? "local" : "remote", err); -+ - if (source == CLOSEDOWN_LOCAL && priv->support_fin_ack) { - queue_rst_message (self); - attempt_send (self, sfRst); -@@ -2211,6 +2347,7 @@ apply_window_scale_option (PseudoTcpSocket *self, guint8 scale_factor) - PseudoTcpSocketPrivate *priv = self->priv; - - priv->swnd_scale = scale_factor; -+ DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Setting scale factor to %u", scale_factor); - } - - static void -@@ -2375,10 +2512,6 @@ pseudo_tcp_socket_get_available_bytes (PseudoTcpSocket *self) - { - PseudoTcpSocketPrivate *priv = self->priv; - -- if (priv->state != TCP_ESTABLISHED) { -- return -1; -- } -- - return pseudo_tcp_fifo_get_buffered (&priv->rbuf); - } - -@@ -2394,11 +2527,11 @@ pseudo_tcp_socket_get_available_send_space (PseudoTcpSocket *self) - PseudoTcpSocketPrivate *priv = self->priv; - gsize ret; - -- -- if (priv->state == TCP_ESTABLISHED) -+ if (!pseudo_tcp_state_has_sent_fin (priv->state)) { - ret = pseudo_tcp_fifo_get_write_remaining (&priv->sbuf); -- else -+ } else { - ret = 0; -+ } - - if (ret == 0) - priv->bWriteEnable = TRUE; -diff --git a/agent/pseudotcp.h b/agent/pseudotcp.h -index 879276e..e7c8eaa 100644 ---- a/agent/pseudotcp.h -+++ b/agent/pseudotcp.h -@@ -62,12 +62,24 @@ - #ifndef __GTK_DOC_IGNORE__ - #ifdef G_OS_WIN32 - # include <winsock2.h> -+ -+#ifndef ECONNABORTED - # define ECONNABORTED WSAECONNABORTED -+#endif -+ -+#ifndef ENOTCONN - # define ENOTCONN WSAENOTCONN -+#endif -+ -+#ifndef EWOULDBLOCK - # define EWOULDBLOCK WSAEWOULDBLOCK -+#endif -+ -+#ifndef ECONNRESET - # define ECONNRESET WSAECONNRESET - #endif - #endif -+#endif - - #include "agent.h" - -@@ -103,17 +115,6 @@ GType pseudo_tcp_socket_get_type (void); - (G_TYPE_INSTANCE_GET_CLASS ((obj), PSEUDO_TCP_SOCKET_TYPE, \ - PseudoTcpSocketClass)) - --struct _PseudoTcpSocketClass { -- GObjectClass parent_class; --}; -- --typedef struct _PseudoTcpSocketPrivate PseudoTcpSocketPrivate; -- --struct _PseudoTcpSocket { -- GObject parent; -- PseudoTcpSocketPrivate *priv; --}; -- - /** - * PseudoTcpDebugLevel: - * @PSEUDO_TCP_DEBUG_NONE: Disable debug messages -diff --git a/agent/stream.c b/agent/stream.c -index 09f79b5..8121e12 100644 ---- a/agent/stream.c -+++ b/agent/stream.c -@@ -48,63 +48,54 @@ - static volatile unsigned int n_streams_created = 0; - static volatile unsigned int n_streams_destroyed = 0; - -+G_DEFINE_TYPE (NiceStream, nice_stream, G_TYPE_OBJECT); -+ -+static void -+nice_stream_finalize (GObject *obj); -+ - /* - * @file stream.c - * @brief ICE stream functionality - */ --Stream * --stream_new (guint n_components, NiceAgent *agent) -+NiceStream * -+nice_stream_new (guint n_components, NiceAgent *agent) - { -- Stream *stream; -+ NiceStream *stream = NULL; - guint n; -- Component *component; - -- g_atomic_int_inc (&n_streams_created); -- nice_debug ("Created NiceStream (%u created, %u destroyed)", -- n_streams_created, n_streams_destroyed); -+ stream = g_object_new (NICE_TYPE_STREAM, NULL); - -- stream = g_slice_new0 (Stream); -+ /* Create the components. */ - for (n = 0; n < n_components; n++) { -- component = component_new (n + 1, agent, stream); -+ NiceComponent *component = NULL; -+ -+ component = nice_component_new (n + 1, agent, stream); - stream->components = g_slist_append (stream->components, component); - } - - stream->n_components = n_components; -- stream->initial_binding_request_received = FALSE; - - return stream; - } - - void --stream_close (Stream *stream) -+nice_stream_close (NiceStream *stream) - { - GSList *i; - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -- component_close (component); -+ NiceComponent *component = i->data; -+ nice_component_close (component); - } - } - --void --stream_free (Stream *stream) --{ -- g_free (stream->name); -- g_slist_free_full (stream->components, (GDestroyNotify) component_free); -- g_slice_free (Stream, stream); -- -- g_atomic_int_inc (&n_streams_destroyed); -- nice_debug ("Destroyed NiceStream (%u created, %u destroyed)", -- n_streams_created, n_streams_destroyed); --} -- --Component * --stream_find_component_by_id (const Stream *stream, guint id) -+NiceComponent * -+nice_stream_find_component_by_id (NiceStream *stream, guint id) - { - GSList *i; - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - if (component && component->id == id) - return component; - } -@@ -117,12 +108,12 @@ stream_find_component_by_id (const Stream *stream, guint id) - * 'CONNECTED' or 'READY' (connected plus nominated). - */ - gboolean --stream_all_components_ready (const Stream *stream) -+nice_stream_all_components_ready (NiceStream *stream) - { - GSList *i; - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - if (component && - !(component->state == NICE_COMPONENT_STATE_CONNECTED || - component->state == NICE_COMPONENT_STATE_READY)) -@@ -136,7 +127,8 @@ stream_all_components_ready (const Stream *stream) - /* - * Initialized the local crendentials for the stream. - */ --void stream_initialize_credentials (Stream *stream, NiceRNG *rng) -+void -+nice_stream_initialize_credentials (NiceStream *stream, NiceRNG *rng) - { - /* note: generate ufrag/pwd for the stream (see ICE 15.4. - * '"ice-ufrag" and "ice-pwd" Attributes', ID-19) */ -@@ -149,7 +141,7 @@ void stream_initialize_credentials (Stream *stream, NiceRNG *rng) - * session. - */ - void --stream_restart (NiceAgent *agent, Stream *stream) -+nice_stream_restart (NiceStream *stream, NiceAgent *agent) - { - GSList *i; - -@@ -158,12 +150,49 @@ stream_restart (NiceAgent *agent, Stream *stream) - - stream->initial_binding_request_received = FALSE; - -- stream_initialize_credentials (stream, agent->rng); -+ nice_stream_initialize_credentials (stream, agent->rng); - - for (i = stream->components; i; i = i->next) { -- Component *component = i->data; -+ NiceComponent *component = i->data; - -- component_restart (component); -+ nice_component_restart (component); - } - } - -+static void -+nice_stream_class_init (NiceStreamClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ -+ object_class->finalize = nice_stream_finalize; -+} -+ -+static void -+nice_stream_init (NiceStream *stream) -+{ -+ g_atomic_int_inc (&n_streams_created); -+ nice_debug ("Created NiceStream (%u created, %u destroyed)", -+ n_streams_created, n_streams_destroyed); -+ -+ stream->n_components = 0; -+ stream->initial_binding_request_received = FALSE; -+} -+ -+/* Must be called with the agent lock released as it could dispose of -+ * NiceIOStreams. */ -+static void -+nice_stream_finalize (GObject *obj) -+{ -+ NiceStream *stream; -+ -+ stream = NICE_STREAM (obj); -+ -+ g_free (stream->name); -+ g_slist_free_full (stream->components, (GDestroyNotify) g_object_unref); -+ -+ g_atomic_int_inc (&n_streams_destroyed); -+ nice_debug ("Destroyed NiceStream (%u created, %u destroyed)", -+ n_streams_created, n_streams_destroyed); -+ -+ G_OBJECT_CLASS (nice_stream_parent_class)->finalize (obj); -+} -diff --git a/agent/stream.h b/agent/stream.h -index e220f43..e524f62 100644 ---- a/agent/stream.h -+++ b/agent/stream.h -@@ -42,7 +42,7 @@ - - #include <glib.h> - --typedef struct _Stream Stream; -+typedef struct _NiceStream NiceStream; - - #include "component.h" - #include "random.h" -@@ -59,13 +59,27 @@ G_BEGIN_DECLS - #define NICE_STREAM_DEF_UFRAG 4 + 1 /* ufrag + NULL */ - #define NICE_STREAM_DEF_PWD 22 + 1 /* pwd + NULL */ - --struct _Stream --{ -+#define NICE_TYPE_STREAM nice_stream_get_type() -+#define NICE_STREAM(obj) \ -+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), NICE_TYPE_STREAM, NiceStream)) -+#define NICE_STREAM_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_CAST ((klass), NICE_TYPE_STREAM, NiceStreamClass)) -+#define NICE_IS_STREAM(obj) \ -+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NICE_TYPE_STREAM)) -+#define NICE_IS_STREAM_CLASS(klass) \ -+ (G_TYPE_CHECK_CLASS_TYPE ((klass), NICE_TYPE_STREAM)) -+#define NICE_STREAM_GET_CLASS(obj) \ -+ (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_STREAM, NiceStreamClass)) -+ -+struct _NiceStream { -+ /*< private >*/ -+ GObject parent; -+ - gchar *name; - guint id; - guint n_components; - gboolean initial_binding_request_received; -- GSList *components; /* list of 'Component' structs */ -+ GSList *components; /* list of 'NiceComponent' objects */ - GSList *conncheck_list; /* list of CandidateCheckPair items */ - gchar local_ufrag[NICE_STREAM_MAX_UFRAG]; - gchar local_password[NICE_STREAM_MAX_PWD]; -@@ -76,27 +90,29 @@ struct _Stream - gint tos; - }; - -+typedef struct { -+ GObjectClass parent_class; -+} NiceStreamClass; - --Stream * --stream_new (guint n_components, NiceAgent *agent); -+GType nice_stream_get_type (void); - --void --stream_close (Stream *stream); -+NiceStream * -+nice_stream_new (guint n_components, NiceAgent *agent); - - void --stream_free (Stream *stream); -+nice_stream_close (NiceStream *stream); - - gboolean --stream_all_components_ready (const Stream *stream); -+nice_stream_all_components_ready (NiceStream *stream); - --Component * --stream_find_component_by_id (const Stream *stream, guint id); -+NiceComponent * -+nice_stream_find_component_by_id (NiceStream *stream, guint id); - - void --stream_initialize_credentials (Stream *stream, NiceRNG *rng); -+nice_stream_initialize_credentials (NiceStream *stream, NiceRNG *rng); - - void --stream_restart (NiceAgent *agent, Stream *stream); -+nice_stream_restart (NiceStream *stream, NiceAgent *agent); - - G_END_DECLS - -diff --git a/autogen.sh b/autogen.sh -index 2f58146..b6efba6 100755 ---- a/autogen.sh -+++ b/autogen.sh -@@ -1,27 +1,38 @@ - #!/bin/sh --set -e -- --test -d m4 || mkdir m4 --gtkdocize || exit 1 -- --autoreconf -fi -- --# Honor NOCONFIGURE for compatibility with gnome-autogen.sh --if test x"$NOCONFIGURE" = x; then -- run_configure=true -- for arg in $*; do -- case $arg in -- --no-configure) -- run_configure=false -- ;; -- *) -- ;; -- esac -- done --else -- run_configure=false -+# Run this to generate all the initial makefiles, etc. -+test -n "$srcdir" || srcdir=$(dirname "$0") -+test -n "$srcdir" || srcdir=. -+ -+olddir=$(pwd) -+ -+cd $srcdir -+ -+(test -f configure.ac) || { -+ echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***" -+ exit 1 -+} -+ -+# shellcheck disable=SC2016 -+PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac) -+ -+if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then -+ echo "*** WARNING: I am going to run 'configure' with no arguments." >&2 -+ echo "*** If you wish to pass any to it, please specify them on the" >&2 -+ echo "*** '$0' command line." >&2 -+ echo "" >&2 - fi - --if test $run_configure = true; then -- ./configure "$@" -+aclocal --install || exit 1 -+gtkdocize --copy || exit 1 -+autoreconf --verbose --force --install || exit 1 -+ -+cd "$olddir" -+if [ "$NOCONFIGURE" = "" ]; then -+ $srcdir/configure "$@" || exit 1 -+ -+ if [ "$1" = "--help" ]; then exit 0 else -+ echo "Now type 'make' to compile $PKG_NAME" || exit 1 -+ fi -+else -+ echo "Skipping configure process." - fi -diff --git a/configure.ac b/configure.ac -index 6031cec..6be4010 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -93,9 +93,9 @@ AC_CHECK_HEADERS([ifaddrs.h], \ - AC_CHECK_TYPES([size_t, ssize_t]) - - # Also put matching version in LIBNICE_CFLAGS --GLIB_REQ=2.30 -+GLIB_REQ=2.44 - --LIBNICE_CFLAGS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_30 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36" -+LIBNICE_CFLAGS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_44 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_44" - - dnl Support different levels of compiler error reporting. - dnl This configure flag is designed to mimic one from gnome-common, -@@ -231,9 +231,6 @@ AS_IF([test "$with_gstreamer" != no], [ - [ - have_gst_check=no - ]) -- -- AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes) -- - ]) - - AS_IF([test "$with_gstreamer010" != no], [ -@@ -260,6 +257,7 @@ AC_SUBST(gstplugindir) - AC_SUBST(gstplugin010dir) - - AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes) -+AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes) - AM_CONDITIONAL(WITH_GSTREAMER010, test "$with_gstreamer010" = yes) - - GUPNP_IGD_REQUIRED=0.2.4 -diff --git a/docs/design.txt b/docs/design.txt -index 4f43724..6a3bf12 100644 ---- a/docs/design.txt -+++ b/docs/design.txt -@@ -90,7 +90,6 @@ NiceAgent GObject interface defined in 'nice/agent.h'. - - The rough order of control follow is as follows: - --- client should initialize glib with g_type_init() - - creation of NiceAgent object instance - - setting agent properties such as STUN and TURN server addresses - - connecting the GObject signals with g_signal_connect() to application -diff --git a/docs/reference/libnice/Makefile.am b/docs/reference/libnice/Makefile.am -index 1d53e3b..19e479e 100644 ---- a/docs/reference/libnice/Makefile.am -+++ b/docs/reference/libnice/Makefile.am -@@ -62,7 +62,7 @@ IGNORE_HFILES= conncheck.h discovery.h stream.h component.h agent-priv.h \ - - # Images to copy into HTML directory. - # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png --HTML_IMAGES= -+HTML_IMAGES = states.png - - # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). - # e.g. content_files=running.sgml building.sgml changes-2.0.sgml -@@ -94,13 +94,19 @@ include $(top_srcdir)/gtk-doc.make - - # Other files to distribute - # e.g. EXTRA_DIST += version.xml.in --#EXTRA_DIST += -+EXTRA_DIST += states.gv - - # Files not to distribute - # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types - # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt - #DISTCLEANFILES += - -+# If we ever need to regenerate this diagram. -+# Since it’s not expected to change much, let’s not depend on GraphViz to -+# build the docs. -+states.png: states.gv -+ dot -Tpng -Gsize=9.6,2.9\! -Gdpi=200 $^ > $@ -+ - if ENABLE_GTK_DOC - TESTS_ENVIRONMENT = cd $(builddir) && - TESTS = $(GTKDOC_CHECK) -diff --git a/docs/reference/libnice/states.gv b/docs/reference/libnice/states.gv -new file mode 100644 -index 0000000..609be2e ---- /dev/null -+++ b/docs/reference/libnice/states.gv -@@ -0,0 +1,25 @@ -+/* libnice state transition diagram for NiceComponentState. */ -+digraph NiceComponentState { -+ rankdir=TB; -+ node [shape = doublecircle]; DISCONNECTED; -+ node [shape = circle]; -+ -+ /* Colour the normal control flow in green. */ -+ DISCONNECTED -> GATHERING [ label = "nice_agent_gather_candidates()", color = chartreuse3 ]; -+ GATHERING -> CONNECTING [ label = "nice_agent_set_remote_candidates()", color = chartreuse3 ]; -+ CONNECTING -> CONNECTED [ label = "At least one candidate pair succeeds", color = chartreuse3 ]; -+ CONNECTED -> READY [ label = "All candidate pairs checks finished", color = chartreuse3 ]; -+ -+ READY -> CONNECTED [ label = "Selected candidate pair fails" ]; -+ -+ FAILED -> CONNECTING [ label = "nice_agent_set_remote_candidates()" ]; -+ -+ DISCONNECTED -> CONNECTING [ label = "nice_agent_set_remote_candidates()" ]; -+ -+ /* Colour the failure paths in grey. */ -+ DISCONNECTED -> FAILED [ label = "Failure", color = gray ]; -+ GATHERING -> FAILED [ label = "Failure", color = gray ]; -+ CONNECTING -> FAILED [ label = "Failure", color = gray ]; -+ CONNECTED -> FAILED [ label = "Failure", color = gray ]; -+ READY -> FAILED [ label = "Failure", color = gray ]; -+} -diff --git a/docs/reference/libnice/states.png b/docs/reference/libnice/states.png -new file mode 100644 -index 0000000..ba23739 -Binary files /dev/null and b/docs/reference/libnice/states.png differ -diff --git a/examples/sdp-example.c b/examples/sdp-example.c -index 246341e..b6dd80a 100644 ---- a/examples/sdp-example.c -+++ b/examples/sdp-example.c -@@ -44,9 +44,7 @@ - - #include <agent.h> - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include <gio/gnetworking.h> --#endif - - static GMainLoop *gloop; - static gchar *stun_addr = NULL; -@@ -95,11 +93,7 @@ main(int argc, char *argv[]) - g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port); - } - --#if GLIB_CHECK_VERSION(2, 36, 0) - g_networking_init(); --#else -- g_type_init(); --#endif - - gloop = g_main_loop_new(NULL, FALSE); - -diff --git a/examples/simple-example.c b/examples/simple-example.c -index 6e13dc6..a511d29 100644 ---- a/examples/simple-example.c -+++ b/examples/simple-example.c -@@ -44,9 +44,7 @@ - - #include <agent.h> - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include <gio/gnetworking.h> --#endif - - static GMainLoop *gloop; - static GIOChannel* io_stdin; -@@ -105,11 +103,7 @@ main(int argc, char *argv[]) - g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port); - } - --#if GLIB_CHECK_VERSION(2, 36, 0) - g_networking_init(); --#else -- g_type_init(); --#endif - - gloop = g_main_loop_new(NULL, FALSE); - #ifdef G_OS_WIN32 -@@ -226,7 +220,7 @@ cb_component_state_changed(NiceAgent *agent, guint _stream_id, - g_debug("SIGNAL: state changed %d %d %s[%d]\n", - _stream_id, component_id, state_name[state], state); - -- if (state == NICE_COMPONENT_STATE_READY) { -+ if (state == NICE_COMPONENT_STATE_CONNECTED) { - NiceCandidate *local, *remote; - - // Get current selected candidate pair and print IP address used -diff --git a/examples/threaded-example.c b/examples/threaded-example.c -index 79eda8d..575b4dc 100644 ---- a/examples/threaded-example.c -+++ b/examples/threaded-example.c -@@ -44,9 +44,7 @@ - - #include <agent.h> - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include <gio/gnetworking.h> --#endif - - static GMainLoop *gloop; - static gchar *stun_addr = NULL; -@@ -104,11 +102,6 @@ main(int argc, char *argv[]) - g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port); - } - --#if GLIB_CHECK_VERSION(2, 36, 0) -- g_networking_init(); --#else -- g_type_init(); --#endif - g_networking_init(); - - gloop = g_main_loop_new(NULL, FALSE); -diff --git a/nice/libnice.sym b/nice/libnice.sym -index efcfdc3..b04bb95 100644 ---- a/nice/libnice.sym -+++ b/nice/libnice.sym -@@ -74,12 +74,16 @@ pseudo_tcp_socket_close - pseudo_tcp_socket_connect - pseudo_tcp_socket_get_error - pseudo_tcp_socket_get_next_clock -+pseudo_tcp_socket_get_type -+pseudo_tcp_socket_is_closed -+pseudo_tcp_socket_is_closed_remotely - pseudo_tcp_socket_new - pseudo_tcp_socket_notify_clock - pseudo_tcp_socket_notify_mtu - pseudo_tcp_socket_notify_packet - pseudo_tcp_socket_recv - pseudo_tcp_socket_send -+pseudo_tcp_socket_shutdown - stun_agent_build_unknown_attributes_error - stun_agent_default_validater - stun_agent_finish_message -diff --git a/socket/http.c b/socket/http.c -index 404d378..96ddfd8 100644 ---- a/socket/http.c -+++ b/socket/http.c -@@ -95,6 +95,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - NiceSocket * - nice_http_socket_new (NiceSocket *base_socket, -@@ -126,6 +127,7 @@ nice_http_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - /* Send HTTP CONNECT */ -@@ -281,9 +283,8 @@ socket_recv_messages (NiceSocket *sock, - HttpPriv *priv = sock->priv; - gint ret = -1; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->state == HTTP_STATE_CONNECTED) { - guint i; -@@ -576,9 +577,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - HttpPriv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->state == HTTP_STATE_CONNECTED) { - /* Fast path. */ -@@ -642,3 +642,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ HttpPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/pseudossl.c b/socket/pseudossl.c -index 5ad4f97..052725c 100644 ---- a/socket/pseudossl.c -+++ b/socket/pseudossl.c -@@ -116,6 +116,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - NiceSocket * - nice_pseudossl_socket_new (NiceSocket *base_socket, -@@ -152,6 +153,7 @@ nice_pseudossl_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - /* We send 'to' NULL because it will always be to an already connected -@@ -204,9 +206,8 @@ socket_recv_messages (NiceSocket *sock, - { - PseudoSSLPriv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->handshaken) { - if (priv->base_socket) { -@@ -256,9 +257,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - PseudoSSLPriv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->handshaken) { - /* Fast path: pass directly through to the base socket once the handshake is -@@ -319,3 +319,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ PseudoSSLPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/socket.c b/socket/socket.c -index 9c0d978..08ae31a 100644 ---- a/socket/socket.c -+++ b/socket/socket.c -@@ -265,6 +265,14 @@ nice_socket_set_writable_callback (NiceSocket *sock, - sock->set_writable_callback (sock, callback, user_data); - } - -+gboolean -+nice_socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ if (sock->is_based_on) -+ return sock->is_based_on (sock, other); -+ return (sock == other); -+} -+ - void - nice_socket_free (NiceSocket *sock) - { -diff --git a/socket/socket.h b/socket/socket.h -index 41ea07b..fadcbc1 100644 ---- a/socket/socket.h -+++ b/socket/socket.h -@@ -88,6 +88,7 @@ struct _NiceSocket - gboolean (*can_send) (NiceSocket *sock, NiceAddress *addr); - void (*set_writable_callback) (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+ gboolean (*is_based_on) (NiceSocket *sock, NiceSocket *other); - void (*close) (NiceSocket *sock); - void *priv; - }; -@@ -124,6 +125,23 @@ void - nice_socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); - -+/** -+ * nice_socket_is_based_on: -+ * @sock: a #NiceSocket -+ * @other: another #NiceSocket -+ * -+ * Checks whether @sock wraps @other as a source and destination of its read and -+ * write operations. The function traverses the whole chain of @sock's base -+ * sockets until @other is found or the end is reached. -+ * -+ * Returns: %TRUE if @sock is based on @other or if @sock and @other are -+ * the same socket, %FALSE otherwise. -+ * -+ * Since: UNRELEASED -+ */ -+gboolean -+nice_socket_is_based_on (NiceSocket *sock, NiceSocket *other); -+ - void - nice_socket_free (NiceSocket *sock); - -diff --git a/socket/socks5.c b/socket/socks5.c -index 46d17fb..d15fc29 100644 ---- a/socket/socks5.c -+++ b/socket/socks5.c -@@ -81,6 +81,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - - NiceSocket * -@@ -108,6 +109,7 @@ nice_socks5_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - /* Send SOCKS5 handshake */ -@@ -167,9 +169,8 @@ socket_recv_messages (NiceSocket *sock, - guint i; - gint ret = -1; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - switch (priv->state) { - case SOCKS_STATE_CONNECTED: -@@ -423,9 +424,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - Socks5Priv *priv = sock->priv; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->state == SOCKS_STATE_CONNECTED) { - /* Fast path: pass through to the base socket once connected. */ -@@ -488,3 +488,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ Socks5Priv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/tcp-active.c b/socket/tcp-active.c -index 5144678..5402806 100644 ---- a/socket/tcp-active.c -+++ b/socket/tcp-active.c -@@ -50,6 +50,11 @@ - #include <unistd.h> - #endif - -+/* FIXME: This should be defined in gio/gnetworking.h, which we should include; -+ * but we cannot do that without refactoring. -+ * (See:
https://phabricator.freedesktop.org/D230
) */ -+#define TCP_NODELAY 1 -+ - typedef struct { - GSocketAddress *local_addr; - GMainContext *context; -@@ -225,6 +230,9 @@ nice_tcp_active_socket_connect (NiceSocket *sock, NiceAddress *addr) - /* GSocket: All socket file descriptors are set to be close-on-exec. */ - g_socket_set_blocking (gsock, false); - -+ /* setting TCP_NODELAY to TRUE in order to avoid packet batching */ -+ g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); -+ - /* Allow g_socket_bind to fail */ - g_socket_bind (gsock, priv->local_addr, FALSE, NULL); - -diff --git a/socket/tcp-bsd.c b/socket/tcp-bsd.c -index 20dd698..3e5f5a8 100644 ---- a/socket/tcp-bsd.c -+++ b/socket/tcp-bsd.c -@@ -54,6 +54,11 @@ - #include <unistd.h> - #endif - -+/* FIXME: This should be defined in gio/gnetworking.h, which we should include; -+ * but we cannot do that without refactoring. -+ * (See:
https://phabricator.freedesktop.org/D230
) */ -+#define TCP_NODELAY 1 -+ - typedef struct { - NiceAddress remote_addr; - GQueue send_queue; -@@ -168,6 +173,9 @@ nice_tcp_bsd_socket_new (GMainContext *ctx, NiceAddress *local_addr, - /* GSocket: All socket file descriptors are set to be close-on-exec. */ - g_socket_set_blocking (gsock, false); - -+ /* setting TCP_NODELAY to TRUE in order to avoid packet batching */ -+ g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); -+ - gret = g_socket_connect (gsock, gaddr, NULL, &gerr); - g_object_unref (gaddr); - -@@ -229,9 +237,8 @@ socket_recv_messages (NiceSocket *sock, - TcpPriv *priv = sock->priv; - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Don't try to access the socket if it had an error */ - if (priv->error) -@@ -283,9 +290,8 @@ socket_send_message (NiceSocket *sock, - GError *gerr = NULL; - gsize message_len; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Don't try to access the socket if it had an error, otherwise we risk a - * crash with SIGPIPE (Broken pipe) */ -@@ -344,9 +350,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -diff --git a/socket/tcp-passive.c b/socket/tcp-passive.c -index 30bfba8..131ff4b 100644 ---- a/socket/tcp-passive.c -+++ b/socket/tcp-passive.c -@@ -50,6 +50,11 @@ - #include <unistd.h> - #endif - -+/* FIXME: This should be defined in gio/gnetworking.h, which we should include; -+ * but we cannot do that without refactoring. -+ * (See:
https://phabricator.freedesktop.org/D230
) */ -+#define TCP_NODELAY 1 -+ - typedef struct { - GMainContext *context; - GHashTable *connections; -@@ -176,6 +181,12 @@ socket_close (NiceSocket *sock) - { - TcpPassivePriv *priv = sock->priv; - -+ if (sock->fileno != NULL) { -+ g_socket_close (sock->fileno, NULL); -+ g_object_unref (sock->fileno); -+ sock->fileno = NULL; -+ } -+ - if (priv->context) - g_main_context_unref (priv->context); - g_hash_table_unref (priv->connections); -@@ -278,6 +289,9 @@ nice_tcp_passive_socket_accept (NiceSocket *sock) - /* GSocket: All socket file descriptors are set to be close-on-exec. */ - g_socket_set_blocking (gsock, false); - -+ /* setting TCP_NODELAY to TRUE in order to avoid packet batching */ -+ g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); -+ - gaddr = g_socket_get_remote_address (gsock, NULL); - if (gaddr == NULL || - !g_socket_address_to_native (gaddr, &name.addr, sizeof (name), NULL)) { -diff --git a/socket/udp-bsd.c b/socket/udp-bsd.c -index d56f093..3fac544 100644 ---- a/socket/udp-bsd.c -+++ b/socket/udp-bsd.c -@@ -183,9 +183,8 @@ socket_recv_messages (NiceSocket *sock, - guint i; - gboolean error = FALSE; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Read messages into recv_messages until one fails or would block, or we - * reach the end. */ -@@ -204,7 +203,10 @@ socket_recv_messages (NiceSocket *sock, - recv_message->length = MAX (recvd, 0); - - if (recvd < 0) { -- if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) -+ /* Handle ECONNRESET here as if it were EWOULDBLOCK; see -+ *
https://phabricator.freedesktop.org/T121
*/ -+ if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || -+ g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED)) - recvd = 0; - else - error = TRUE; -@@ -245,9 +247,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - GError *child_error = NULL; - gssize len; - -- /* Socket has been closed: */ -- if (priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (!nice_address_is_valid (&priv->niceaddr) || - !nice_address_equal (&priv->niceaddr, to)) { -@@ -289,9 +290,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -diff --git a/socket/udp-turn-over-tcp.c b/socket/udp-turn-over-tcp.c -index d97fa04..2b91f92 100644 ---- a/socket/udp-turn-over-tcp.c -+++ b/socket/udp-turn-over-tcp.c -@@ -86,6 +86,7 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - NiceSocket * - nice_udp_turn_over_tcp_socket_new (NiceSocket *base_socket, -@@ -107,6 +108,7 @@ nice_udp_turn_over_tcp_socket_new (NiceSocket *base_socket, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - - return sock; -@@ -134,9 +136,8 @@ socket_recv_message (NiceSocket *sock, NiceInputMessage *recv_message) - GInputVector local_recv_buf; - NiceInputMessage local_recv_message; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - if (priv->expecting_len == 0) { - guint headerlen = 0; -@@ -241,9 +242,8 @@ socket_recv_messages (NiceSocket *nicesock, - guint i; - gboolean error = FALSE; - -- /* Socket has been closed: */ -- if (nicesock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (nicesock->priv != NULL); - - for (i = 0; i < n_recv_messages; i++) { - gssize len; -@@ -285,9 +285,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - } header_buf; - guint offset = 0; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - /* Count the number of buffers. */ - if (message->n_buffers == -1) { -@@ -301,7 +300,7 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - - /* Allocate a new array of buffers, covering all the buffers in the input - * @message, but with an additional one for a header and one for a footer. */ -- local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector)); -+ local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector)); - local_message.buffers = local_bufs; - local_message.n_buffers = n_bufs + 1; - -@@ -377,8 +376,6 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - if (ret == 1) - ret = output_message_get_size (&local_message); - -- g_free (local_bufs); -- - return ret; - } - -@@ -388,9 +385,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -@@ -458,3 +454,12 @@ socket_set_writable_callback (NiceSocket *sock, - - nice_socket_set_writable_callback (priv->base_socket, callback, user_data); - } -+ -+static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ TurnTcpPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -diff --git a/socket/udp-turn.c b/socket/udp-turn.c -index e640363..617e4f3 100644 ---- a/socket/udp-turn.c -+++ b/socket/udp-turn.c -@@ -98,6 +98,11 @@ typedef struct { - GHashTable *send_data_queues; /* stores a send data queue for per peer */ - GSource *permission_timeout_source; /* timer used to invalidate - permissions */ -+ -+ guint8 *cached_realm; -+ uint16_t cached_realm_len; -+ guint8 *cached_nonce; -+ uint16_t cached_nonce_len; - } UdpTurnPriv; - - -@@ -125,15 +130,16 @@ static gboolean socket_is_reliable (NiceSocket *sock); - static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr); - static void socket_set_writable_callback (NiceSocket *sock, - NiceSocketWritableCb callback, gpointer user_data); -+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other); - - static void priv_process_pending_bindings (UdpTurnPriv *priv); - static gboolean priv_retransmissions_tick_unlocked (UdpTurnPriv *priv); - static gboolean priv_retransmissions_tick (gpointer pointer); - static void priv_schedule_tick (UdpTurnPriv *priv); - static void priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg); --static gboolean priv_send_create_permission (UdpTurnPriv *priv, StunMessage *resp, -+static gboolean priv_send_create_permission (UdpTurnPriv *priv, - const NiceAddress *peer); --static gboolean priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp, -+static gboolean priv_send_channel_bind (UdpTurnPriv *priv, - uint16_t channel, - const NiceAddress *peer); - static gboolean priv_add_channel_binding (UdpTurnPriv *priv, -@@ -235,7 +241,7 @@ nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, - priv_send_data_queue_destroy); - - sock->type = NICE_SOCKET_TYPE_UDP_TURN; -- sock->fileno = base_socket->fileno; -+ sock->fileno = NULL; - sock->addr = *addr; - sock->send_messages = socket_send_messages; - sock->send_messages_reliable = socket_send_messages_reliable; -@@ -243,6 +249,7 @@ nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr, - sock->is_reliable = socket_is_reliable; - sock->can_send = socket_can_send; - sock->set_writable_callback = socket_set_writable_callback; -+ sock->is_based_on = socket_is_based_on; - sock->close = socket_close; - sock->priv = (void *) priv; - -@@ -317,6 +324,8 @@ socket_close (NiceSocket *sock) - g_list_free(priv->pending_permissions); - g_free (priv->username); - g_free (priv->password); -+ g_free (priv->cached_realm); -+ g_free (priv->cached_nonce); - g_free (priv); - - sock->priv = NULL; -@@ -332,11 +341,10 @@ socket_recv_messages (NiceSocket *sock, - gboolean error = FALSE; - guint n_valid_messages; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return 0; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - -- nice_debug ("received message on TURN socket"); -+ nice_debug_verbose ("received message on TURN socket"); - - n_messages = nice_socket_recv_messages (priv->base_socket, - recv_messages, n_recv_messages); -@@ -374,7 +382,7 @@ socket_recv_messages (NiceSocket *sock, - buffer = message->buffers[0].buffer; - buffer_length = message->length; - } else { -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - - buffer = compact_input_message (message, &buffer_length); - allocated_buffer = TRUE; -@@ -575,7 +583,7 @@ _socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to, - n_bufs = message->n_buffers; - } - -- local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector)); -+ local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector)); - local_message.buffers = local_bufs; - local_message.n_buffers = n_bufs + 1; - -@@ -598,8 +606,6 @@ _socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to, - if (ret == 1) - ret = message_len; - -- g_free (local_bufs); -- - return ret; - } - } -@@ -663,7 +669,7 @@ socket_dequeue_all_data (UdpTurnPriv *priv, const NiceAddress *to) - SendData *data = - (SendData *) g_queue_pop_head(send_queue); - -- nice_debug ("dequeuing data"); -+ nice_debug_verbose ("dequeuing data"); - _socket_send_wrapped (priv->base_socket, &priv->server_addr, - data->data_len, data->data, data->reliable); - -@@ -693,9 +699,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - ChannelBinding *binding = NULL; - gint ret; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = priv->channels; i; i = i->next) { - ChannelBinding *b = i->data; -@@ -816,7 +821,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - /* Finish the message. */ - msg_len = stun_agent_finish_message (&priv->agent, &msg, - priv->password, priv->password_len); -- if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) { -+ if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST && -+ priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_OC2007) { - SendRequest *req = g_slice_new0 (SendRequest); - - req->priv = priv; -@@ -831,11 +837,11 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to, - if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 && - !priv_has_permission_for_peer (priv, to)) { - if (!priv_has_sent_permission_for_peer (priv, to)) { -- priv_send_create_permission (priv, NULL, to); -+ priv_send_create_permission (priv, to); - } - - /* enque data */ -- nice_debug ("enqueuing data"); -+ nice_debug_verbose ("enqueuing data"); - socket_enqueue_data(priv, to, msg_len, (gchar *)buffer, reliable); - - return msg_len; -@@ -868,9 +874,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to, - { - guint i; - -- /* Socket has been closed: */ -- if (sock->priv == NULL) -- return -1; -+ /* Make sure socket has not been freed: */ -+ g_assert (sock->priv != NULL); - - for (i = 0; i < n_messages; i++) { - const NiceOutputMessage *message = &messages[i]; -@@ -951,6 +956,15 @@ socket_set_writable_callback (NiceSocket *sock, - } - - static gboolean -+socket_is_based_on (NiceSocket *sock, NiceSocket *other) -+{ -+ UdpTurnPriv *priv = sock->priv; -+ -+ return (sock == other) || -+ (priv && nice_socket_is_based_on (priv->base_socket, other)); -+} -+ -+static gboolean - priv_forget_send_request (gpointer pointer) - { - SendRequest *req = pointer; -@@ -1079,13 +1093,20 @@ priv_binding_timeout (gpointer data) - ChannelBinding *b = i->data; - if (b->timeout_source == source) { - b->renew = TRUE; -+ -+ /* Remove any existing timer */ -+ if (b->timeout_source) { -+ g_source_destroy (b->timeout_source); -+ g_source_unref (b->timeout_source); -+ } -+ - /* Install timer to expire the permission */ - b->timeout_source = priv_timeout_add_with_context (priv, - STUN_EXPIRE_TIMEOUT, TRUE, priv_binding_expired_timeout, priv); - - /* Send renewal */ - if (!priv->current_binding_msg) -- priv_send_channel_bind (priv, NULL, b->channel, &b->peer); -+ priv_send_channel_bind (priv, b->channel, &b->peer); - break; - } - } -@@ -1095,6 +1116,31 @@ priv_binding_timeout (gpointer data) - return FALSE; - } - -+void -+nice_udp_turn_socket_cache_realm_nonce (NiceSocket *sock, StunMessage *msg) -+{ -+ UdpTurnPriv *priv = sock->priv; -+ gconstpointer tmp; -+ -+ g_assert (sock->type == NICE_SOCKET_TYPE_UDP_TURN); -+ -+ g_free (priv->cached_realm); -+ priv->cached_realm = NULL; -+ priv->cached_realm_len = 0; -+ -+ g_free (priv->cached_nonce); -+ priv->cached_nonce = NULL; -+ priv->cached_nonce_len = 0; -+ -+ tmp = stun_message_find (msg, STUN_ATTRIBUTE_REALM, &priv->cached_realm_len); -+ if (tmp && priv->cached_realm_len < 764) -+ priv->cached_realm = g_memdup (tmp, priv->cached_realm_len); -+ -+ tmp = stun_message_find (msg, STUN_ATTRIBUTE_NONCE, &priv->cached_nonce_len); -+ if (tmp && priv->cached_nonce_len < 764) -+ priv->cached_nonce = g_memdup (tmp, priv->cached_nonce_len); -+} -+ - guint - nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_sock, - NiceInputMessage *message) -@@ -1121,7 +1167,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc - } - - /* Slow path. */ -- nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); -+ nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC); - - buf = compact_input_message (message, &buf_len); - len = nice_udp_turn_socket_parse_recv (sock, from_sock, -@@ -1298,8 +1344,9 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - - g_free (priv->current_binding_msg); - priv->current_binding_msg = NULL; -+ nice_udp_turn_socket_cache_realm_nonce (sock, &msg); - if (binding) -- priv_send_channel_bind (priv, &msg, binding->channel, -+ priv_send_channel_bind (priv, binding->channel, - &binding->peer); - } else { - g_free (priv->current_binding); -@@ -1357,12 +1404,17 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - } peer; - socklen_t peer_len = sizeof(peer); - NiceAddress to; -+ gchar tmpbuf[INET6_ADDRSTRLEN]; - -- nice_debug ("got response for CreatePermission"); - stun_message_find_xor_addr ( - ¤t_create_permission_msg->message, - STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &peer.storage, &peer_len); - nice_address_set_from_sockaddr (&to, &peer.addr); -+ nice_address_to_string (&to, tmpbuf); -+ nice_debug ("TURN: got response for CreatePermission " -+ "with XOR_PEER_ADDRESS=[%s]:%u : %s", -+ tmpbuf, nice_address_get_port (&to), -+ stun_message_get_class (&msg) == STUN_ERROR ? "unauthorized" : "ok"); - - /* unathorized => resend with realm and nonce */ - if (stun_message_get_class (&msg) == STUN_ERROR) { -@@ -1395,8 +1447,10 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - priv->pending_permissions, i); - g_free (current_create_permission_msg); - current_create_permission_msg = NULL; -+ -+ nice_udp_turn_socket_cache_realm_nonce (sock, &msg); - /* resend CreatePermission */ -- priv_send_create_permission (priv, &msg, &to); -+ priv_send_create_permission (priv, &to); - return 0; - } - } -@@ -1463,7 +1517,7 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock, - if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 && - !priv_has_permission_for_peer (priv, from)) { - if (!priv_has_sent_permission_for_peer (priv, from)) { -- priv_send_create_permission (priv, NULL, from); -+ priv_send_create_permission (priv, from); - } - } - -@@ -1549,7 +1603,7 @@ priv_process_pending_bindings (UdpTurnPriv *priv) - for (i = priv->channels ; i; i = i->next) { - ChannelBinding *b = i->data; - if (b->renew) { -- priv_send_channel_bind (priv, NULL, b->channel, &b->peer); -+ priv_send_channel_bind (priv, b->channel, &b->peer); - break; - } - } -@@ -1804,7 +1858,7 @@ priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg) - } - - static gboolean --priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, -+priv_send_create_permission(UdpTurnPriv *priv, - const NiceAddress *peer) - { - guint msg_buf_len; -@@ -1814,17 +1868,6 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, - struct sockaddr_storage storage; - struct sockaddr addr; - } addr; -- uint8_t *realm = NULL; -- uint16_t realm_len = 0; -- uint8_t *nonce = NULL; -- uint16_t nonce_len = 0; -- -- if (resp) { -- realm = (uint8_t *) stun_message_find (resp, -- STUN_ATTRIBUTE_REALM, &realm_len); -- nonce = (uint8_t *) stun_message_find (resp, -- STUN_ATTRIBUTE_NONCE, &nonce_len); -- } - - /* register this peer as being pening a permission (if not already pending) */ - if (!priv_has_sent_permission_for_peer (priv, peer)) { -@@ -1841,8 +1884,8 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, - priv->username_len, - priv->password, - priv->password_len, -- realm, realm_len, -- nonce, nonce_len, -+ priv->cached_realm, priv->cached_realm_len, -+ priv->cached_nonce, priv->cached_nonce_len, - &addr.storage, - STUN_USAGE_TURN_COMPATIBILITY_RFC5766); - -@@ -1876,8 +1919,8 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp, - } - - static gboolean --priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp, -- uint16_t channel, const NiceAddress *peer) -+priv_send_channel_bind (UdpTurnPriv *priv, uint16_t channel, -+ const NiceAddress *peer) - { - uint32_t channel_attr = channel << 16; - size_t stun_len; -@@ -1910,37 +1953,29 @@ priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp, - return FALSE; - } - -- if (priv->username != NULL && priv->username_len > 0) { -+ if (priv->username != NULL && priv->username_len > 0 && -+ priv->cached_realm != NULL && priv->cached_realm_len > 0 && -+ priv->cached_nonce != NULL && priv->cached_nonce_len > 0) { -+ - if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME, - priv->username, priv->username_len) - != STUN_MESSAGE_RETURN_SUCCESS) { - g_free (msg); - return FALSE; - } -- } -- -- if (resp) { -- uint8_t *realm; -- uint8_t *nonce; -- uint16_t len; - -- realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len); -- if (realm != NULL) { -- if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM, -- realm, len) -- != STUN_MESSAGE_RETURN_SUCCESS) { -- g_free (msg); -- return 0; -- } -+ if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM, -+ priv->cached_realm, priv->cached_realm_len) -+ != STUN_MESSAGE_RETURN_SUCCESS) { -+ g_free (msg); -+ return 0; - } -- nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len); -- if (nonce != NULL) { -- if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE, -- nonce, len) -- != STUN_MESSAGE_RETURN_SUCCESS) { -- g_free (msg); -- return 0; -- } -+ -+ if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE, -+ priv->cached_nonce, priv->cached_nonce_len) -+ != STUN_MESSAGE_RETURN_SUCCESS) { -+ g_free (msg); -+ return 0; - } - } - -@@ -1988,7 +2023,7 @@ priv_add_channel_binding (UdpTurnPriv *priv, const NiceAddress *peer) - } - - if (channel >= 0x4000 && channel < 0xffff) { -- gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer); -+ gboolean ret = priv_send_channel_bind (priv, channel, peer); - if (ret) { - priv->current_binding = g_new0 (ChannelBinding, 1); - priv->current_binding->channel = channel; -diff --git a/socket/udp-turn.h b/socket/udp-turn.h -index ba31636..b1eeeb4 100644 ---- a/socket/udp-turn.h -+++ b/socket/udp-turn.h -@@ -75,6 +75,9 @@ nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg); - void - nice_udp_turn_socket_set_ms_connection_id (NiceSocket *sock, StunMessage *msg); - -+void -+nice_udp_turn_socket_cache_realm_nonce (NiceSocket *sock, StunMessage *msg); -+ - - G_END_DECLS - -diff --git a/stun/debug.c b/stun/debug.c -index 2b4ec59..8efb576 100644 ---- a/stun/debug.c -+++ b/stun/debug.c -@@ -46,7 +46,7 @@ - #include "debug.h" - - --static int debug_enabled = 1; -+static int debug_enabled = 0; - - void stun_debug_enable (void) { - debug_enabled = 1; -diff --git a/stun/stunagent.c b/stun/stunagent.c -index 2abcc29..bd243cb 100644 ---- a/stun/stunagent.c -+++ b/stun/stunagent.c -@@ -513,8 +513,20 @@ size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg, - uint32_t fpr; - int saved_id_idx = 0; - uint8_t md5[16]; -+ bool remember_transaction; - -- if (stun_message_get_class (msg) == STUN_REQUEST) { -+ remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST); -+ -+ if (agent->compatibility == STUN_COMPATIBILITY_OC2007 && -+ stun_message_get_method (msg) == STUN_SEND) { -+ /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to -+ * STUN_SEND requests, so don't bother waiting for them. More details at -+ *
https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx
. -+ */ -+ remember_transaction = FALSE; -+ } -+ -+ if (remember_transaction) { - for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) { - if (agent->sent_ids[saved_id_idx].valid == FALSE) { - break; -@@ -620,7 +632,7 @@ size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg, - } - - -- if (stun_message_get_class (msg) == STUN_REQUEST) { -+ if (remember_transaction) { - stun_message_id (msg, agent->sent_ids[saved_id_idx].id); - agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg); - agent->sent_ids[saved_id_idx].key = (uint8_t *) key; -diff --git a/stun/stunmessage.c b/stun/stunmessage.c -index 9faa64b..558fe5e 100644 ---- a/stun/stunmessage.c -+++ b/stun/stunmessage.c -@@ -550,7 +550,6 @@ ssize_t stun_message_validate_buffer_length_fast (StunInputVector *buffers, - - if (buffers[0].buffer[0] >> 6) - { -- stun_debug ("STUN error: RTP or other non-protocol packet!"); - return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet - } - -diff --git a/stun/tests/test-bind.c b/stun/tests/test-bind.c -index 0c7646f..2cf4feb 100644 ---- a/stun/tests/test-bind.c -+++ b/stun/tests/test-bind.c -@@ -438,7 +438,7 @@ static void keepalive (void) - - static void test (void (*func) (void), const char *name) - { -- alarm (20); -+ alarm (30); - - printf ("%s test... ", name); - func (); -diff --git a/stun/tests/test-conncheck.c b/stun/tests/test-conncheck.c -index 610d43a..92b947c 100644 ---- a/stun/tests/test-conncheck.c -+++ b/stun/tests/test-conncheck.c -@@ -213,7 +213,7 @@ int main (void) - - addr.ip4.sin_family = AF_INET; - -- /* Lost role conflict */ -+ /* Role conflict, controlling + ICE-CONTROLLING, switching controlled */ - assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); - val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLING, tie + 1); - assert (val == STUN_MESSAGE_RETURN_SUCCESS); -@@ -223,7 +223,6 @@ int main (void) - rlen = stun_agent_finish_message (&agent, &req, pass, pass_len); - assert (rlen > 0); - -- - len = sizeof (resp_buf); - control = true; - val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, -@@ -236,7 +235,7 @@ int main (void) - stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); - assert (stun_message_get_class (&resp) == STUN_RESPONSE); - -- /* Won role conflict */ -+ /* Role conflict, controlled + ICE-CONTROLLED, switching controlling */ - assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); - val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLED, tie - 1); - assert (val == STUN_MESSAGE_RETURN_SUCCESS); -@@ -251,15 +250,60 @@ int main (void) - val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, - &resp, resp_buf, &len, &addr.storage, - sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245); -- assert (val2 == STUN_USAGE_ICE_RETURN_SUCCESS); -+ assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT); - assert (len > 0); -- assert (control == false); -+ assert (control == true); -+ assert (stun_agent_validate (&agent, &resp, resp_buf, len, -+ stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); -+ assert (stun_message_get_class (&resp) == STUN_RESPONSE); -+ -+ /* Role conflict, controlling + ICE-CONTROLLING, staying controlling */ -+ assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); -+ val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLING, tie - 1); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ val = stun_message_append_string (&req, STUN_ATTRIBUTE_USERNAME, -+ (char *) ufrag); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ rlen = stun_agent_finish_message (&agent, &req, pass, pass_len); -+ assert (rlen > 0); -+ -+ len = sizeof (resp_buf); -+ control = true; -+ val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, -+ &resp, resp_buf, &len, &addr.storage, -+ sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245); -+ assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT); -+ assert (len > 0); -+ assert (control == true); - assert (stun_agent_validate (&agent, &resp, resp_buf, len, - stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); - assert (stun_message_get_class (&resp) == STUN_ERROR); - stun_message_find_error (&resp, &code); - assert (code == STUN_ERROR_ROLE_CONFLICT); - -+ /* Role conflict, controlled + ICE-CONTROLLED, staying controlling */ -+ assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING)); -+ val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLED, tie + 1); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ val = stun_message_append_string (&req, STUN_ATTRIBUTE_USERNAME, -+ (char *) ufrag); -+ assert (val == STUN_MESSAGE_RETURN_SUCCESS); -+ rlen = stun_agent_finish_message (&agent, &req, pass, pass_len); -+ assert (rlen > 0); -+ -+ len = sizeof (resp_buf); -+ control = false; -+ val2 = stun_usage_ice_conncheck_create_reply (&agent, &req, -+ &resp, resp_buf, &len, &addr.storage, -+ sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245); -+ assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT); -+ assert (len > 0); -+ assert (control == false); -+ assert (stun_agent_validate (&agent, &resp, resp_buf, len, -+ stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS); -+ assert (stun_message_get_class (&resp) == STUN_ERROR); -+ stun_message_find_error (&resp, &code); -+ assert (code == STUN_ERROR_ROLE_CONFLICT); - - return 0; - } -diff --git a/stun/usages/ice.c b/stun/usages/ice.c -index a628791..a7d0d19 100644 ---- a/stun/usages/ice.c -+++ b/stun/usages/ice.c -@@ -265,9 +265,17 @@ stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, - if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING - : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS) - { -+ /* we have the ice-controlling/controlled attribute, -+ * and there's a role conflict -+ */ - stun_debug ("STUN Role Conflict detected:"); - -- if (tie < q) -+ /* According to ICE RFC 5245, section 7.2.1.1, we consider the four -+ * possible cases when a role conflict is detected: two cases are -+ * resolved by switching role locally, and the two other cases are -+ * handled by responding with a STUN error. -+ */ -+ if ((tie < q && *control) || (tie >= q && !*control)) - { - stun_debug (" switching role from \"controll%s\" to \"controll%s\"", - *control ? "ing" : "ed", *control ? "ed" : "ing"); -@@ -279,10 +287,21 @@ stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, - stun_debug (" staying \"controll%s\" (sending error)", - *control ? "ing" : "ed"); - err (STUN_ERROR_ROLE_CONFLICT); -- return STUN_USAGE_ICE_RETURN_SUCCESS; -+ return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; - } - } else { -- stun_debug ("STUN Role not specified by peer!"); -+ if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED -+ : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS) -+ { -+ /* we don't have the expected ice-controlling/controlled -+ * attribute -+ */ -+ if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 || -+ compatibility == STUN_USAGE_ICE_COMPATIBILITY_WLM2009) -+ { -+ stun_debug ("STUN Role not specified by peer!"); -+ } -+ } - } - - if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) { -diff --git a/stun/usages/timer.c b/stun/usages/timer.c -index 82f3ea2..2862ab8 100644 ---- a/stun/usages/timer.c -+++ b/stun/usages/timer.c -@@ -104,7 +104,7 @@ void stun_timer_start (StunTimer *timer, unsigned int initial_timeout, - unsigned int max_retransmissions) - { - stun_gettime (&timer->deadline); -- timer->retransmissions = 0; -+ timer->retransmissions = 1; - timer->delay = initial_timeout; - timer->max_retransmissions = max_retransmissions; - add_delay (&timer->deadline, timer->delay); -diff --git a/stun/usages/timer.h b/stun/usages/timer.h -index e6501cb..e74353b 100644 ---- a/stun/usages/timer.h -+++ b/stun/usages/timer.h -@@ -130,15 +130,18 @@ struct stun_timer_s { - * STUN_TIMER_DEFAULT_TIMEOUT: - * - * The default intial timeout to use for the timer -+ * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most -+ * cases as it is also what is used by SIP style VoIP when sending A-Law and -+ * mu-Law audio, so 200ms should be hyper safe. - */ --#define STUN_TIMER_DEFAULT_TIMEOUT 600 -+#define STUN_TIMER_DEFAULT_TIMEOUT 200 - - /** - * STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS: - * - * The default maximum retransmissions allowed before a timer decides to timeout - */ --#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 3 -+#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 7 - - /** - * STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT: -diff --git a/stun/usages/turn.c b/stun/usages/turn.c -index f242650..3b94959 100644 ---- a/stun/usages/turn.c -+++ b/stun/usages/turn.c -@@ -152,7 +152,9 @@ size_t stun_usage_turn_create (StunAgent *agent, StunMessage *msg, - } - } - -- if (username != NULL && username_len > 0) { -+ if (username != NULL && username_len > 0 && -+ (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS || -+ previous_response)) { - if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, - username, username_len) != STUN_MESSAGE_RETURN_SUCCESS) - return 0; -@@ -205,7 +207,9 @@ size_t stun_usage_turn_create_refresh (StunAgent *agent, StunMessage *msg, - } - - -- if (username != NULL && username_len > 0) { -+ if (username != NULL && username_len > 0 && -+ (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS || -+ previous_response)) { - if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, - username, username_len) != STUN_MESSAGE_RETURN_SUCCESS) - return 0; -@@ -251,7 +255,9 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg, - } - - /* username */ -- if (username != NULL) { -+ if (username != NULL && -+ (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS || -+ (nonce != NULL && realm != NULL))) { - if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, - username, username_len) != STUN_MESSAGE_RETURN_SUCCESS) - return 0; -diff --git a/tests/Makefile.am b/tests/Makefile.am -index 3644091..7bfe075 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -22,6 +22,10 @@ AM_CFLAGS = \ - -I $(top_srcdir)/stun - AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\" - -+AM_TESTS_ENVIRONMENT = \ -+ G_MESSAGES_DEBUG=all \ -+ NICE_DEBUG=all; -+ - COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS) - - check_PROGRAMS = \ -@@ -39,6 +43,7 @@ check_PROGRAMS = \ - test-io-stream-cancelling \ - test-io-stream-pollable \ - test-send-recv \ -+ test-socket-is-based-on \ - test-priority \ - test-mainloop \ - test-fullmode \ -@@ -49,7 +54,8 @@ check_PROGRAMS = \ - test-new-dribble \ - test-tcp \ - test-icetcp \ -- test-credentials -+ test-credentials \ -+ test-turn - - dist_check_SCRIPTS = \ - check-test-fullmode-with-stun.sh \ -@@ -99,6 +105,8 @@ test_io_stream_pollable_LDADD = $(COMMON_LDADD) - test_send_recv_SOURCES = test-send-recv.c test-io-stream-common.c - test_send_recv_LDADD = $(COMMON_LDADD) - -+test_socket_is_based_on_LDADD = $(COMMON_LDADD) -+ - test_priority_LDADD = $(COMMON_LDADD) - - test_mainloop_LDADD = $(COMMON_LDADD) -@@ -119,6 +127,8 @@ test_icetcp_LDADD = $(COMMON_LDADD) - - test_credentials_LDADD = $(COMMON_LDADD) - -+test_turn_LDADD = $(COMMON_LDADD) -+ - test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS) - test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS) - -diff --git a/tests/test-add-remove-stream.c b/tests/test-add-remove-stream.c -index 5ed3463..c97bc7b 100644 ---- a/tests/test-add-remove-stream.c -+++ b/tests/test-add-remove-stream.c -@@ -54,8 +54,6 @@ main (void) - WSAStartup(0x0202, &w); - #endif - nice_address_init (&addr); -- g_type_init (); -- g_thread_init (NULL); - - if (!nice_address_set_from_string (&addr, "127.0.0.1")) - g_assert_not_reached (); -diff --git a/tests/test-bsd.c b/tests/test-bsd.c -index 6b747d0..c1ddf53 100644 ---- a/tests/test-bsd.c -+++ b/tests/test-bsd.c -@@ -368,8 +368,6 @@ test_multi_message_recv (guint n_sends, guint n_receives, - int - main (void) - { -- g_type_init (); -- - test_socket_initial_properties (); - test_socket_address_properties (); - test_simple_send_recv (); -diff --git a/tests/test-build-io-stream.c b/tests/test-build-io-stream.c -index 0c9c593..a3478ed 100644 ---- a/tests/test-build-io-stream.c -+++ b/tests/test-build-io-stream.c -@@ -440,8 +440,6 @@ main (void) - WSAStartup (0x0202, &w); - #endif - nice_address_init (&addr); -- g_type_init (); -- g_thread_init (NULL); - - g_assert (nice_address_set_from_string (&addr, "127.0.0.1")); - -diff --git a/tests/test-credentials.c b/tests/test-credentials.c -index f678afa..1de4e49 100644 ---- a/tests/test-credentials.c -+++ b/tests/test-credentials.c -@@ -172,8 +172,6 @@ int main (void) - WSADATA w; - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - loop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-dribble.c b/tests/test-dribble.c -index 6603129..d9de07b 100644 ---- a/tests/test-dribble.c -+++ b/tests/test-dribble.c -@@ -215,9 +215,6 @@ int main (void) - WSAStartup(0x0202, &w); - #endif - -- g_type_init (); -- g_thread_init (NULL); -- - global_mainloop = g_main_loop_new (NULL, FALSE); - - /* step: create the agents L and R */ -diff --git a/tests/test-fallback.c b/tests/test-fallback.c -index f97cb0d..34fd1d1 100644 ---- a/tests/test-fallback.c -+++ b/tests/test-fallback.c -@@ -491,8 +491,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-fullmode.c b/tests/test-fullmode.c -index 03778f7..b599a24 100644 ---- a/tests/test-fullmode.c -+++ b/tests/test-fullmode.c -@@ -834,8 +834,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init(NULL); - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-icetcp.c b/tests/test-icetcp.c -index 7ab3563..5b2b4b2 100644 ---- a/tests/test-icetcp.c -+++ b/tests/test-icetcp.c -@@ -125,6 +125,14 @@ static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpoin - (void)agent; - } - -+static void check_loop_quit_condition (void) -+{ -+ if (global_ready_reached && -+ global_lagent_cands >= 2 && global_ragent_cands >= 2) { -+ g_main_loop_quit (global_mainloop); -+ } -+} -+ - static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) - { - gboolean ready_to_connected = FALSE; -@@ -158,10 +166,10 @@ static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint - global_ready_reached == FALSE) { - g_debug ("Components ready/failed achieved. Stopping mailoop"); - global_ready_reached = TRUE; -- g_main_loop_quit (global_mainloop); -- return; - } - -+ check_loop_quit_condition (); -+ - #if 0 - /* signal status via a global variable */ - if (global_components_failed == global_components_failed_exit) { -@@ -184,6 +192,8 @@ static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint compon - else if (GPOINTER_TO_UINT (data) == 2) - ++global_ragent_cands; - -+ check_loop_quit_condition (); -+ - /* XXX: dear compiler, these are for you: */ - (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation; - } -@@ -397,10 +407,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); --#if !GLIB_CHECK_VERSION(2,31,8) -- g_thread_init(NULL); --#endif - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-io-stream-cancelling.c b/tests/test-io-stream-cancelling.c -index 55fc1f3..d1b9a89 100644 ---- a/tests/test-io-stream-cancelling.c -+++ b/tests/test-io-stream-cancelling.c -@@ -112,8 +112,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - l_data.cancellable = g_cancellable_new (); - l_data.blocking = FALSE; -diff --git a/tests/test-io-stream-closing-read.c b/tests/test-io-stream-closing-read.c -index ec434dd..5acddec 100644 ---- a/tests/test-io-stream-closing-read.c -+++ b/tests/test-io-stream-closing-read.c -@@ -127,8 +127,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL); - -diff --git a/tests/test-io-stream-closing-write.c b/tests/test-io-stream-closing-write.c -index 97e8347..6f92f28 100644 ---- a/tests/test-io-stream-closing-write.c -+++ b/tests/test-io-stream-closing-write.c -@@ -127,8 +127,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL); - -diff --git a/tests/test-io-stream-common.c b/tests/test-io-stream-common.c -index bd0f3b5..efa6160 100644 ---- a/tests/test-io-stream-common.c -+++ b/tests/test-io-stream-common.c -@@ -335,12 +335,7 @@ spawn_thread (const gchar *thread_name, GThreadFunc thread_func, - { - GThread *thread; - --#if !GLIB_CHECK_VERSION(2, 31, 8) -- thread = g_thread_create (thread_func, user_data, TRUE, NULL); --#else - thread = g_thread_new (thread_name, thread_func, user_data); --#endif -- - g_assert (thread); - - return thread; -diff --git a/tests/test-io-stream-pollable.c b/tests/test-io-stream-pollable.c -index 614a546..a543f88 100644 ---- a/tests/test-io-stream-pollable.c -+++ b/tests/test-io-stream-pollable.c -@@ -159,8 +159,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - l_data = g_malloc0 (sizeof (ThreadData)); - r_data = g_malloc0 (sizeof (ThreadData)); -diff --git a/tests/test-io-stream-thread.c b/tests/test-io-stream-thread.c -index db14fe4..73ecd76 100644 ---- a/tests/test-io-stream-thread.c -+++ b/tests/test-io-stream-thread.c -@@ -124,8 +124,6 @@ int main (void) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - l_data = g_malloc0 (sizeof (ThreadData)); - r_data = g_malloc0 (sizeof (ThreadData)); -diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c -index b4e4d05..7c52daa 100644 ---- a/tests/test-mainloop.c -+++ b/tests/test-mainloop.c -@@ -72,8 +72,6 @@ main (void) - guint stream; - - nice_address_init (&addr); -- g_type_init (); -- g_thread_init(NULL); - - loop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-new-dribble.c b/tests/test-new-dribble.c -index 395e275..3e60ae3 100644 ---- a/tests/test-new-dribble.c -+++ b/tests/test-new-dribble.c -@@ -56,21 +56,14 @@ - #define LEFT_AGENT GINT_TO_POINTER(1) - #define RIGHT_AGENT GINT_TO_POINTER(2) - --#if !GLIB_CHECK_VERSION(2,31,8) -- static GMutex *stun_mutex_ptr = NULL; -- static GCond *stun_signal_ptr = NULL; -- static GMutex *stun_thread_mutex_ptr = NULL; -- static GCond *stun_thread_signal_ptr = NULL --#else -- static GMutex stun_mutex; -- static GMutex *stun_mutex_ptr = &stun_mutex; -- static GCond stun_signal; -- static GCond *stun_signal_ptr = &stun_signal; -- static GMutex stun_thread_mutex; -- static GMutex *stun_thread_mutex_ptr = &stun_thread_mutex; -- static GCond stun_thread_signal; -- static GCond *stun_thread_signal_ptr = &stun_thread_signal; --#endif -+static GMutex stun_mutex; -+static GMutex *stun_mutex_ptr = &stun_mutex; -+static GCond stun_signal; -+static GCond *stun_signal_ptr = &stun_signal; -+static GMutex stun_thread_mutex; -+static GMutex *stun_thread_mutex_ptr = &stun_thread_mutex; -+static GCond stun_thread_signal; -+static GCond *stun_thread_signal_ptr = &stun_thread_signal; - - static NiceComponentState global_lagent_state = NICE_COMPONENT_STATE_LAST; - static NiceComponentState global_ragent_state = NICE_COMPONENT_STATE_LAST; -@@ -530,6 +523,10 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent) - - g_assert (lagent_candidate_gathering_done); - -+ while (global_ragent_state < NICE_COMPONENT_STATE_CONNECTED) -+ g_main_context_iteration (NULL, TRUE); -+ g_cancellable_reset (global_cancellable); -+ - g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY); - g_assert (global_ragent_state >= NICE_COMPONENT_STATE_CONNECTED); - -@@ -725,8 +722,6 @@ int main(void) - GSource *src; - int sock; - -- g_type_init(); -- - global_cancellable = g_cancellable_new (); - src = g_cancellable_source_new (global_cancellable); - g_source_set_dummy_callback (src); -@@ -739,16 +734,8 @@ int main(void) - } - - --#if !GLIB_CHECK_VERSION(2,31,8) -- g_thread_init (NULL); -- stun_thread = g_thread_create (stun_thread_func, GINT_TO_POINTER (sock), -- TRUE, NULL); -- stun_mutex_ptr = g_mutex_new (); -- stun_signal_ptr = g_cond_new (); --#else - stun_thread = g_thread_new ("listen for STUN requests", - stun_thread_func, GINT_TO_POINTER (sock)); --#endif - - // Once the the thread is forked, we want to listen for a signal - // that the socket was opened successfully -@@ -803,10 +790,6 @@ int main(void) - g_object_unref (ragent); - - g_thread_join (stun_thread); --#if !GLIB_CHECK_VERSION(2,31,8) -- g_mutex_free (stun_mutex_ptr); -- g_cond_free (stun_signal_ptr); --#endif - g_object_unref (global_cancellable); - - g_source_destroy (src); -diff --git a/tests/test-priority.c b/tests/test-priority.c -index f7d3273..1700dd0 100644 ---- a/tests/test-priority.c -+++ b/tests/test-priority.c -@@ -47,38 +47,41 @@ main (void) - { - NiceCandidate *candidate; - -- /* test 1 */ - candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST); -- g_assert (nice_candidate_jingle_priority (candidate) == 1000); -+ nice_address_set_from_string (&candidate->addr, "127.0.0.1"); -+ nice_address_set_from_string (&candidate->base_addr, "127.0.0.1"); -+ -+ /* test 1 */ -+ g_assert_cmpuint (nice_candidate_jingle_priority (candidate), ==, 1000); - /* Host UDP */ - candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP; - candidate->component_id = 1; -- g_assert (nice_candidate_ice_priority (candidate, FALSE, FALSE) == 0x780001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, FALSE, FALSE), ==, 0x780001FF); - /* Host UDP reliable */ -- g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x3C0001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE), ==, 0x3C0001FF); - /* Host tcp-active unreliable */ - candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE; -- g_assert (nice_candidate_ice_priority (candidate, FALSE, FALSE) == 0x3CC001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, FALSE, FALSE) & 0xFFE000FF, ==, 0x3CC000FF); - /* Host tcp-active reliable */ - candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE; - /* Host tcp-active reliable */ -- g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x78C001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE) & 0xFFE000FF, ==, 0x78C000FF); - /* srv-reflexive tcp-active reliable */ - candidate->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE; - candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE; -- g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x648001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE) & 0xFFE000FF, ==, 0x648000FF); - /* nat-assisted srv-reflexive tcp-active reliable */ -- g_assert (nice_candidate_ice_priority (candidate, TRUE, TRUE) == 0x698001FF); -+ g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, TRUE) & 0xFFE000FF, ==, 0x698000FF); - nice_candidate_free (candidate); - - /* test 2 */ - /* 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0) - = 2^32*1 + 2*5000 + 0 - = 4294977296 */ -- g_assert (nice_candidate_pair_priority (1,5000) == 4294977296LL); -+ g_assert_cmpuint (nice_candidate_pair_priority (1,5000), ==, 4294977296LL); - - /* 2^32*1 + 2*5000 + 1 = 4294977297 */ -- g_assert (nice_candidate_pair_priority (5000, 1) == 4294977297LL); -+ g_assert_cmpuint (nice_candidate_pair_priority (5000, 1), ==, 4294977297LL); - - return 0; - } -diff --git a/tests/test-pseudotcp-fin.c b/tests/test-pseudotcp-fin.c -index b769161..d240c96 100644 ---- a/tests/test-pseudotcp-fin.c -+++ b/tests/test-pseudotcp-fin.c -@@ -1,7 +1,7 @@ - /* - * This file is part of the Nice GLib ICE library. - * -- * (C) 2014 Collabora Ltd. -+ * © 2014, 2015 Collabora Ltd. - * Contact: Philip Withnall - * - * The contents of this file are subject to the Mozilla Public License Version -@@ -269,6 +269,13 @@ expect_syn_received (Data *data) - expect_segment (data->right, data->right_sent, 0, 7, 7, FLAG_SYN); - } - -+static void -+assert_empty_queues (Data *data) -+{ -+ g_assert_cmpuint (g_queue_get_length (data->left_sent), ==, 0); -+ g_assert_cmpuint (g_queue_get_length (data->right_sent), ==, 0); -+} -+ - /* Return whether the socket accepted the packet. */ - static gboolean - forward_segment (GQueue/*<owned GBytes>*/ *from, PseudoTcpSocket *to) -@@ -453,6 +460,8 @@ establish_connection (Data *data) - expect_ack (data->left, data->left_sent, 7, 7); - forward_segment_ltr (data); - expect_sockets_connected (data); -+ -+ assert_empty_queues (data); - } - - /* Helper to close the LHS of a socket pair which has not transmitted any -@@ -638,7 +647,7 @@ pseudotcp_close_normal_recovery1 (void) - expect_fin (data.left, data.left_sent, 7, 7); - drop_segment (data.left, data.left_sent); - -- increment_time_both (&data, 300); /* retransmit timeout */ -+ increment_time_both (&data, 1100); /* retransmit timeout */ - - expect_fin (data.left, data.left_sent, 7, 7); - forward_segment_ltr (&data); -@@ -673,7 +682,7 @@ pseudotcp_close_normal_recovery2 (void) - - expect_ack (data.right, data.right_sent, 7, 8); - drop_segment (data.right, data.right_sent); -- increment_time_both (&data, 300); /* retransmit timeout */ -+ increment_time_both (&data, 1100); /* retransmit timeout */ - expect_fin (data.left, data.left_sent, 7, 7); - forward_segment_ltr (&data); - expect_ack (data.right, data.right_sent, 7, 8); -@@ -753,6 +762,76 @@ pseudotcp_close_normal_recovery4 (void) - data_clear (&data); - } - -+/* Check that closing a connection recovers from a data segment being dropped -+ * immediately before the first FIN is sent. Based on: RFC 793, Figure 13. */ -+static void -+pseudotcp_close_normal_recovery_data (void) -+{ -+ Data data = { 0, }; -+ -+ /* Establish a connection. */ -+ establish_connection (&data); -+ -+ /* Send some data from LHS to RHS, but drop the segment. */ -+ g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3); -+ expect_data (data.left, data.left_sent, 7, 7, 3); -+ drop_segment (data.left, data.left_sent); -+ -+ assert_empty_queues(&data); -+ -+ /* Close the LHS. */ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 0); -+ close_socket (data.left); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_1); -+ expect_fin (data.left, data.left_sent, 10, 7); -+ forward_segment_ltr (&data); -+ -+ expect_socket_state (data.right, TCP_ESTABLISHED); -+ expect_ack (data.right, data.right_sent, 7, 7); -+ forward_segment_rtl (&data); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_1); -+ -+ assert_empty_queues(&data); -+ -+ /* Close the RHS. */ -+ close_socket (data.right); -+ -+ expect_socket_state (data.right, TCP_FIN_WAIT_1); -+ -+ expect_fin (data.right, data.right_sent, 7, 7); -+ forward_segment_rtl (&data); -+ -+ expect_socket_state (data.left, TCP_CLOSING); -+ -+ expect_ack (data.left, data.left_sent, 11, 8); -+ forward_segment_ltr (&data); -+ -+ expect_socket_state (data.right, TCP_FIN_WAIT_2); -+ -+ expect_data (data.right, data.right_sent, 8, 7, 0); -+ forward_segment_rtl (&data); -+ expect_socket_state (data.left, TCP_CLOSING); -+ -+ expect_data (data.left, data.left_sent, 7, 8, 3); -+ forward_segment_ltr (&data); -+ expect_socket_state (data.right, TCP_TIME_WAIT); -+ -+ increment_time_both (&data, 100); /* Delayed ACK */ -+ -+ expect_ack (data.right, data.right_sent, 8, 11); -+ forward_segment_rtl (&data); -+ expect_socket_state (data.left, TCP_TIME_WAIT); -+ -+ increment_time_both (&data, 10); /* TIME-WAIT */ -+ -+ expect_sockets_closed (&data); -+ -+ data_clear (&data); -+} -+ - /* Check that if both FIN segments from a simultaneous FIN handshake are - * dropped, the handshake recovers and completes successfully. - * See: RFC 793, Figure 14. */ -@@ -773,7 +852,7 @@ pseudotcp_close_simultaneous_recovery1 (void) - drop_segment (data.left, data.left_sent); - drop_segment (data.right, data.right_sent); - -- increment_time_both (&data, 400); /* retransmit timeout */ -+ increment_time_both (&data, 1200); /* retransmit timeout */ - - expect_fin (data.left, data.left_sent, 7, 7); - expect_fin (data.right, data.right_sent, 7, 7); -@@ -817,7 +896,7 @@ pseudotcp_close_simultaneous_recovery2 (void) - drop_segment (data.left, data.left_sent); - drop_segment (data.right, data.right_sent); - -- increment_time_both (&data, 400); /* retransmit timeout */ -+ increment_time_both (&data, 1200); /* retransmit timeout */ - - expect_fin (data.left, data.left_sent, 7, 8); - expect_fin (data.right, data.right_sent, 7, 8); -@@ -977,11 +1056,14 @@ pseudotcp_close_rst_afterwards (void) - - /* Close the LHS. */ - g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ pseudo_tcp_socket_close (data.left, TRUE); - close_socket (data.left); - -- expect_fin (data.left, data.left_sent, 7, 7); -+ expect_rst (data.left, data.left_sent, 7, 7); - drop_segment (data.left, data.left_sent); /* just to get it out of the way */ - -+ assert_empty_queues(&data); -+ - /* Send some data from RHS to LHS, which should result in an RST. */ - g_assert_cmpint (pseudo_tcp_socket_send (data.right, "foo", 3), ==, 3); - expect_data (data.right, data.right_sent, 7, 7, 3); -@@ -1059,6 +1141,63 @@ pseudotcp_compatibility (void) - data_clear (&data); - } - -+ -+/* Check that after receiving a FIN, queued data can still be read */ -+static void -+pseudotcp_close_recv_queued (void) -+{ -+ Data data = { 0, }; -+ guint8 buf[100]; -+ -+ /* Establish a connection. */ -+ establish_connection (&data); -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.right), >, -+ 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.left), >, -+ 0); -+ -+ g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3); -+ expect_data (data.left, data.left_sent, 7, 7, 3); -+ forward_segment_ltr (&data); -+ -+ increment_time_both (&data, 100); /* Delayed ACK */ -+ expect_ack (data.right, data.right_sent, 7, 10); -+ forward_segment_rtl (&data); -+ -+ close_socket (data.left); -+ expect_fin (data.left, data.left_sent, 10, 7); -+ forward_segment_ltr (&data); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_1); -+ expect_socket_state (data.right, TCP_CLOSE_WAIT); -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0); -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.left), ==, -+ 0); -+ -+ expect_ack (data.right, data.right_sent, 7, 11); -+ forward_segment_rtl (&data); -+ -+ expect_socket_state (data.left, TCP_FIN_WAIT_2); -+ -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 3); -+ -+ g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.right), >, -+ 0); -+ -+ /* Check that the data can be read */ -+ g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 3); -+ -+ /* Now the socket should be empty */ -+ g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 0); -+ -+ data_clear (&data); -+} -+ - int - main (int argc, char *argv[]) - { -@@ -1109,6 +1248,8 @@ main (int argc, char *argv[]) - pseudotcp_close_normal_recovery3); - g_test_add_func ("/pseudotcp/close/normal/recovery4", - pseudotcp_close_normal_recovery4); -+ g_test_add_func ("/pseudotcp/close/normal/recovery-data", -+ pseudotcp_close_normal_recovery_data); - g_test_add_func ("/pseudotcp/close/simultaneous/recovery1", - pseudotcp_close_simultaneous_recovery1); - g_test_add_func ("/pseudotcp/close/simultaneous/recovery2", -@@ -1125,6 +1266,9 @@ main (int argc, char *argv[]) - g_test_add_func ("/pseudotcp/close/rst-afterwards", - pseudotcp_close_rst_afterwards); - -+ g_test_add_func ("/pseudotcp/close/recv-queued", -+ pseudotcp_close_recv_queued); -+ - g_test_add_func ("/pseudotcp/compatibility", - pseudotcp_compatibility); - -diff --git a/tests/test-pseudotcp-fuzzy.c b/tests/test-pseudotcp-fuzzy.c -index fdee222..4211248 100644 ---- a/tests/test-pseudotcp-fuzzy.c -+++ b/tests/test-pseudotcp-fuzzy.c -@@ -395,7 +395,6 @@ int main (int argc, char *argv[]) - GError *error = NULL; - - setlocale (LC_ALL, ""); -- g_type_init (); - - /* Configuration. */ - context = g_option_context_new ("— fuzz-test the pseudotcp socket"); -diff --git a/tests/test-pseudotcp.c b/tests/test-pseudotcp.c -index e4dd613..1a8391a 100644 ---- a/tests/test-pseudotcp.c -+++ b/tests/test-pseudotcp.c -@@ -259,8 +259,6 @@ int main (int argc, char *argv[]) - - mainloop = g_main_loop_new (NULL, FALSE); - -- g_type_init (); -- - pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE); - - left_closed = right_closed = FALSE; -diff --git a/tests/test-restart.c b/tests/test-restart.c -index c7f2f25..c2cbe9a 100644 ---- a/tests/test-restart.c -+++ b/tests/test-restart.c -@@ -400,8 +400,6 @@ int main (void) - - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init(NULL); - - global_mainloop = g_main_loop_new (NULL, FALSE); - -diff --git a/tests/test-send-recv.c b/tests/test-send-recv.c -index 55e6002..5841639 100644 ---- a/tests/test-send-recv.c -+++ b/tests/test-send-recv.c -@@ -1202,8 +1202,6 @@ main (int argc, char *argv[]) - WSADATA w; - WSAStartup (0x0202, &w); - #endif -- g_type_init (); -- g_thread_init (NULL); - - if (!long_mode) { - /* Quick mode. Just test each of the stream APIs in reliable and -diff --git a/tests/test-socket-is-based-on.c b/tests/test-socket-is-based-on.c -new file mode 100644 -index 0000000..6080fe3 ---- /dev/null -+++ b/tests/test-socket-is-based-on.c -@@ -0,0 +1,122 @@ -+/* -+ * This file is part of the Nice GLib ICE library. -+ * -+ * (C) 2016 Jakub Adam <jakub.adam(a)ktknet.cz> -+ * -+ * The contents of this file are subject to the Mozilla Public License Version -+ * 1.1 (the "License"); you may not use this file except in compliance with -+ * the License. You may obtain a copy of the License at -+ *
http://www.mozilla.org/MPL/
-+ * -+ * Software distributed under the License is distributed on an "AS IS" basis, -+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -+ * for the specific language governing rights and limitations under the -+ * License. -+ * -+ * The Original Code is the Nice GLib ICE library. -+ * -+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia -+ * Corporation. All Rights Reserved. -+ * -+ * Alternatively, the contents of this file may be used under the terms of the -+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which -+ * case the provisions of LGPL are applicable instead of those above. If you -+ * wish to allow use of your version of this file only under the terms of the -+ * LGPL and not to allow others to use your version of this file under the -+ * MPL, indicate your decision by deleting the provisions above and replace -+ * them with the notice and other provisions required by the LGPL. If you do -+ * not delete the provisions above, a recipient may use your version of this -+ * file under either the MPL or the LGPL. -+ */ -+#ifdef HAVE_CONFIG_H -+# include "config.h" -+#endif -+ -+#include <locale.h> -+ -+#include "socket.h" -+ -+static NiceSocket *udp_bsd; -+static NiceSocket *tcp_active; -+static NiceSocket *pseudossl; -+static NiceSocket *udp_turn_over_tcp; -+ -+static void -+socket_base_udp_bsd (void) -+{ -+ g_assert (nice_socket_is_based_on (udp_bsd, udp_bsd)); -+ g_assert (!nice_socket_is_based_on (udp_bsd, tcp_active)); -+ g_assert (!nice_socket_is_based_on (udp_bsd, pseudossl)); -+ g_assert (!nice_socket_is_based_on (udp_bsd, udp_turn_over_tcp)); -+} -+ -+static void -+socket_base_tcp_active (void) -+{ -+ g_assert (!nice_socket_is_based_on (tcp_active, udp_bsd)); -+ g_assert (nice_socket_is_based_on (tcp_active, tcp_active)); -+ g_assert (!nice_socket_is_based_on (tcp_active, pseudossl)); -+ g_assert (!nice_socket_is_based_on (tcp_active, udp_turn_over_tcp)); -+} -+ -+static void -+socket_base_pseudossl (void) -+{ -+ g_assert (!nice_socket_is_based_on (pseudossl, udp_bsd)); -+ g_assert (nice_socket_is_based_on (pseudossl, tcp_active)); -+ g_assert (nice_socket_is_based_on (pseudossl, pseudossl)); -+ g_assert (!nice_socket_is_based_on (pseudossl, udp_turn_over_tcp)); -+} -+ -+static void -+socket_base_udp_turn_over_tcp (void) -+{ -+ g_assert (!nice_socket_is_based_on (udp_turn_over_tcp, udp_bsd)); -+ g_assert (nice_socket_is_based_on (udp_turn_over_tcp, tcp_active)); -+ g_assert (nice_socket_is_based_on (udp_turn_over_tcp, pseudossl)); -+ g_assert (nice_socket_is_based_on (udp_turn_over_tcp, udp_turn_over_tcp)); -+} -+ -+int -+main (int argc, char *argv[]) -+{ -+ GMainLoop *mainloop = NULL; -+ -+ NiceAddress addr; -+ -+ setlocale (LC_ALL, ""); -+ g_test_init (&argc, &argv, NULL); -+ -+ mainloop = g_main_loop_new (NULL, TRUE); -+ -+ nice_address_set_from_string (&addr, "127.0.0.1"); -+ -+ /* Standalone socket */ -+ udp_bsd = nice_udp_bsd_socket_new (&addr); -+ -+ /* tcp_passive -> pseudossl -> udp_turn_over_tcp */ -+ tcp_active = nice_tcp_active_socket_new (g_main_loop_get_context (mainloop), -+ &addr); -+ pseudossl = nice_pseudossl_socket_new (tcp_active, -+ NICE_PSEUDOSSL_SOCKET_COMPATIBILITY_GOOGLE); -+ udp_turn_over_tcp = nice_udp_turn_over_tcp_socket_new (pseudossl, -+ NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE); -+ -+ g_test_add_func ("/socket/is-base-of/udp-bsd", -+ socket_base_udp_bsd); -+ g_test_add_func ("/socket/is-base-of/tcp-active", -+ socket_base_tcp_active); -+ g_test_add_func ("/socket/is-base-of/pseudossl", -+ socket_base_pseudossl); -+ g_test_add_func ("/socket/is-base-of/udp-turn-over-tcp", -+ socket_base_udp_turn_over_tcp); -+ -+ g_test_run (); -+ -+ nice_socket_free (udp_bsd); -+ nice_socket_free (udp_turn_over_tcp); -+ -+ g_main_loop_unref (mainloop); -+ -+ return 0; -+} -diff --git a/tests/test-tcp.c b/tests/test-tcp.c -index dd259a4..e2a1bfd 100644 ---- a/tests/test-tcp.c -+++ b/tests/test-tcp.c -@@ -88,8 +88,6 @@ main (void) - NiceAddress active_bind_addr, passive_bind_addr; - GSource *srv_listen_source, *srv_input_source, *cli_input_source; - -- g_type_init (); -- - mainloop = g_main_loop_new (NULL, FALSE); - - nice_address_init (&active_bind_addr); -diff --git a/tests/test-thread.c b/tests/test-thread.c -index df0b145..7493f97 100644 ---- a/tests/test-thread.c -+++ b/tests/test-thread.c -@@ -211,8 +211,6 @@ int main (void) - WSADATA w; - WSAStartup(0x0202, &w); - #endif -- g_type_init (); -- g_thread_init(NULL); - - lmainctx = g_main_context_new (); - rmainctx = g_main_context_new (); -@@ -291,13 +289,8 @@ int main (void) - /* step: run test the first time */ - g_debug ("test-thread: TEST STARTS / running test for the 1st time"); - --#if !GLIB_CHECK_VERSION(2,31,8) -- lthread = g_thread_create (mainloop_thread, lmainloop, TRUE, NULL); -- rthread = g_thread_create (mainloop_thread, rmainloop, TRUE, NULL); --#else - lthread = g_thread_new ("lthread libnice", mainloop_thread, lmainloop); - rthread = g_thread_new ("rthread libnice", mainloop_thread, rmainloop); --#endif - - g_assert (lthread); - g_assert (rthread); -@@ -318,13 +311,9 @@ int main (void) - nice_agent_attach_recv (ragent, rs_id, 1, rdmainctx, cb_nice_recv, - GUINT_TO_POINTER (2)); - --#if !GLIB_CHECK_VERSION(2,31,8) -- ldthread = g_thread_create (mainloop_thread, ldmainloop, TRUE, NULL); -- rdthread = g_thread_create (mainloop_thread, rdmainloop, TRUE, NULL); --#else - ldthread = g_thread_new ("ldthread libnice", mainloop_thread, ldmainloop); - rdthread = g_thread_new ("rdthread libnice", mainloop_thread, rdmainloop); --#endif -+ - g_assert (ldthread); - g_assert (rdthread); - -diff --git a/tests/test-turn.c b/tests/test-turn.c -new file mode 100644 -index 0000000..46d1bf2 ---- /dev/null -+++ b/tests/test-turn.c -@@ -0,0 +1,379 @@ -+#include <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <gio/gio.h> -+#include <agent.h> -+ -+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST }; -+static guint global_components_ready = 0; -+static gboolean global_lagent_gathering_done = FALSE; -+static gboolean global_ragent_gathering_done = FALSE; -+static int global_lagent_cands = 0; -+static int global_ragent_cands = 0; -+ -+#define TURN_USER "toto" -+#define TURN_PASS "password" -+ -+static gboolean timer_cb (gpointer pointer) -+{ -+ g_debug ("test-turn:%s: %p", G_STRFUNC, pointer); -+ -+ /* signal status via a global variable */ -+ -+ /* note: should not be reached, abort */ -+ g_error ("ERROR: test has got stuck, aborting..."); -+ -+ return FALSE; -+} -+ -+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) -+{ -+ g_debug ("test-fullmode:%s: %p", G_STRFUNC, user_data); -+ -+ /* XXX: dear compiler, these are for you: */ -+ (void)agent; (void)stream_id; (void)component_id; (void)buf; -+ -+ /* -+ * Lets ignore stun packets that got through -+ */ -+ if (len < 8) -+ return; -+ if (strncmp ("12345678", buf, 8)) -+ return; -+ -+ if (component_id != 1) -+ return; -+ -+#if 0 -+ if (GPOINTER_TO_UINT (user_data) == 2) { -+ global_ragent_read += len; -+ } -+#endif -+} -+ -+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data) -+{ -+ g_debug ("test-fullmode:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ global_lagent_gathering_done = TRUE; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ global_ragent_gathering_done = TRUE; -+} -+ -+ -+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data) -+{ -+ gboolean ready_to_connected = FALSE; -+ g_debug ("test-fullmode:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) { -+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_lagent_state[component_id - 1] = state; -+ } else if (GPOINTER_TO_UINT (data) == 2) { -+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY && -+ state == NICE_COMPONENT_STATE_CONNECTED) -+ ready_to_connected = TRUE; -+ global_ragent_state[component_id - 1] = state; -+ } -+ -+ if (state == NICE_COMPONENT_STATE_READY) -+ global_components_ready++; -+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected) -+ global_components_ready--; -+ g_assert (state != NICE_COMPONENT_STATE_FAILED); -+ -+ g_debug ("test-turn: checks READY %u.", global_components_ready); -+} -+ -+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, -+ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data) -+{ -+ g_debug ("test-turn:%s: %p", G_STRFUNC, data); -+ -+ if (GPOINTER_TO_UINT (data) == 1) -+ ++global_lagent_cands; -+ else if (GPOINTER_TO_UINT (data) == 2) -+ ++global_ragent_cands; -+} -+ -+static void set_candidates (NiceAgent *from, guint from_stream, -+ NiceAgent *to, guint to_stream, guint component, gboolean remove_non_relay, -+ gboolean force_relay) -+{ -+ GSList *cands = NULL, *i; -+ -+ cands = nice_agent_get_local_candidates (from, from_stream, component); -+ if (remove_non_relay) { -+ restart: -+ for (i = cands; i; i = i->next) { -+ NiceCandidate *cand = i->data; -+ if (force_relay) -+ g_assert (cand->type == NICE_CANDIDATE_TYPE_RELAYED); -+ if (cand->type != NICE_CANDIDATE_TYPE_RELAYED) { -+ cands = g_slist_remove (cands, cand); -+ nice_candidate_free (cand); -+ goto restart; -+ } -+ } -+ } -+ nice_agent_set_remote_candidates (to, to_stream, component, cands); -+ -+ for (i = cands; i; i = i->next) -+ nice_candidate_free ((NiceCandidate *) i->data); -+ g_slist_free (cands); -+} -+ -+static void set_credentials (NiceAgent *lagent, guint lstream, -+ NiceAgent *ragent, guint rstream) -+{ -+ gchar *ufrag = NULL, *password = NULL; -+ -+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password); -+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password); -+ g_free (ufrag); -+ g_free (password); -+} -+ -+static void -+run_test(guint turn_port, gboolean is_ipv6, -+ gboolean ice_udp, gboolean ice_tcp, gboolean force_relay, -+ gboolean remove_non_relay, -+ NiceRelayType turn_type) -+{ -+ NiceAgent *lagent, *ragent; /* agent's L and R */ -+ const gchar *localhost; -+ NiceAddress localaddr; -+ guint ls_id, rs_id; -+ gulong timer_id; -+ -+ if (is_ipv6) -+ localhost = "::1"; -+ else -+ localhost = "127.0.0.1"; -+ -+ /* step: initialize variables modified by the callbacks */ -+ global_components_ready = 0; -+ global_lagent_gathering_done = FALSE; -+ global_ragent_gathering_done = FALSE; -+ global_lagent_cands = global_ragent_cands = 0; -+ -+ lagent = nice_agent_new (NULL, NICE_COMPATIBILITY_RFC5245); -+ ragent = nice_agent_new (NULL, NICE_COMPATIBILITY_RFC5245); -+ -+ g_object_set (G_OBJECT (lagent), "ice-tcp", ice_tcp, "ice-udp", ice_udp, -+ "force-relay", force_relay, NULL); -+ g_object_set (G_OBJECT (ragent), "ice-tcp", ice_tcp, "ice-udp", ice_udp, -+ "force-relay", force_relay, NULL); -+ -+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL); -+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL); -+ nice_agent_set_software (lagent, "Test-turn, Left Agent"); -+ nice_agent_set_software (ragent, "Test-turn, Right Agent"); -+ -+ timer_id = g_timeout_add (30000, timer_cb, NULL); -+ -+ -+ if (!nice_address_set_from_string (&localaddr, localhost)) -+ g_assert_not_reached (); -+ nice_agent_add_local_address (lagent, &localaddr); -+ nice_agent_add_local_address (ragent, &localaddr); -+ -+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done", -+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1)); -+ g_signal_connect (G_OBJECT (ragent), "component-state-changed", -+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2)); -+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1)); -+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair", -+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2)); -+ -+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL); -+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL); -+ -+ ls_id = nice_agent_add_stream (lagent, 1); -+ rs_id = nice_agent_add_stream (ragent, 1); -+ g_assert (ls_id > 0); -+ g_assert (rs_id > 0); -+ nice_agent_set_relay_info(lagent, ls_id, 1, -+ localhost, turn_port, TURN_USER, TURN_PASS, turn_type); -+ nice_agent_set_relay_info(ragent, rs_id, 1, -+ localhost, turn_port, TURN_USER, TURN_PASS, turn_type); -+ -+ /* Gather candidates and test nice_agent_set_port_range */ -+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE); -+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE); -+ -+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1)); -+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2)); -+ -+ g_assert (global_lagent_gathering_done == FALSE); -+ g_assert (global_ragent_gathering_done == FALSE); -+ g_debug ("test-turn: Added streams, running context until 'candidate-gathering-done'..."); -+ while (!global_lagent_gathering_done && !global_ragent_gathering_done) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_gathering_done == TRUE); -+ g_assert (global_ragent_gathering_done == TRUE); -+ -+ set_credentials (lagent, ls_id, ragent, rs_id); -+ -+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP, -+ remove_non_relay, force_relay); -+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP, -+ remove_non_relay, force_relay); -+ -+ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY || -+ global_ragent_state[0] != NICE_COMPONENT_STATE_READY) -+ g_main_context_iteration (NULL, TRUE); -+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY); -+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY); -+ -+ nice_agent_remove_stream (lagent, ls_id); -+ nice_agent_remove_stream (ragent, rs_id); -+ -+ g_source_remove (timer_id); -+ -+ g_clear_object(&lagent); -+ g_clear_object(&ragent); -+} -+ -+guint global_turn_port; -+ -+static void -+udp_no_force_no_remove_udp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_UDP); -+} -+ -+static void -+udp_no_force_remove_udp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ TRUE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_UDP); -+} -+ -+static void -+udp_force_no_remove_udp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ TRUE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_UDP); -+} -+ -+static void -+udp_no_force_no_remove_tcp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_TCP); -+} -+ -+static void -+udp_no_force_remove_tcp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ FALSE /* force_relay */, -+ TRUE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_TCP); -+} -+ -+static void -+udp_force_no_remove_tcp (void) -+{ -+ run_test(global_turn_port, FALSE /* is_ipv6 */, -+ TRUE /* ice_udp */, -+ FALSE /* ice_tcp */, -+ TRUE /* force_relay */, -+ FALSE /* remove_non_relay */, -+ NICE_RELAY_TYPE_TURN_TCP); -+} -+ -+ -+ -+ -+ -+int -+main (int argc, char **argv) -+{ -+ GSubprocess *sp; -+ GError *error = NULL; -+ gchar portstr[10]; -+ int ret; -+ gchar *out_str = NULL; -+ gchar *err_str = NULL; -+ -+ g_test_init (&argc, &argv, NULL); -+ -+ global_turn_port = g_random_int_range (10000, 60000); -+ snprintf(portstr, 9, "%u", global_turn_port); -+ -+ if (g_spawn_command_line_sync ("turnserver --help", &out_str, &err_str, NULL, -+ NULL) && err_str) { -+ if (!strstr(err_str, "--user")) { -+ g_print ("rfc5766-turn-server not installed, skipping turn test\n"); -+ return 0; -+ } -+ } else { -+ g_print ("rfc5766-turn-server not installed, skipping turn test\n"); -+ return 0; -+ } -+ g_free (err_str); -+ g_free (out_str); -+ -+ sp = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, &error, -+ "turnserver", -+ "--user", "toto:0xaae440b3348d50265b63703117c7bfd5", -+ "--realm", "realm", -+ "--listening-port", portstr, -+ NULL); -+ -+ g_test_add_func ("/nice/turn/udp", udp_no_force_no_remove_udp); -+ g_test_add_func ("/nice/turn/udp/remove_non_turn", -+ udp_no_force_remove_udp); -+ g_test_add_func ("/nice/turn/udp/force_relay", -+ udp_force_no_remove_udp); -+ g_test_add_func ("/nice/turn/udp/over-tcp", udp_no_force_no_remove_tcp); -+ g_test_add_func ("/nice/turn/udp/over-tcp/remove_non_turn", -+ udp_no_force_remove_tcp); -+ g_test_add_func ("/nice/turn/udp/over-tcp/force_relay", -+ udp_force_no_remove_tcp); -+ -+ ret = g_test_run (); -+ -+ g_subprocess_force_exit (sp); -+ g_subprocess_wait (sp, NULL, NULL); -+ g_clear_object (&sp); -+ -+ return ret; -+} -diff --git a/tests/test.c b/tests/test.c -index 31a9fc7..a92b33c 100644 ---- a/tests/test.c -+++ b/tests/test.c -@@ -75,9 +75,6 @@ main (void) - - nice_address_init (&addr_local); - nice_address_init (&addr_remote); -- g_type_init (); -- -- g_thread_init(NULL); - - g_assert (nice_address_set_from_string (&addr_local, "127.0.0.1")); - g_assert (nice_address_set_from_string (&addr_remote, "127.0.0.1")); -diff --git a/win32/vs9/libnice.def b/win32/vs9/libnice.def -deleted file mode 100644 -index 7065330..0000000 ---- a/win32/vs9/libnice.def -+++ /dev/null -@@ -1,137 +0,0 @@ --LIBRARY libnice -- --EXPORTS -- --nice_address_copy_to_sockaddr --nice_address_dup --nice_address_equal --nice_address_equal_no_port --nice_address_free --nice_address_get_port --nice_address_init --nice_address_ip_version --nice_address_is_private --nice_address_is_valid --nice_address_new --nice_address_set_from_sockaddr --nice_address_set_from_string --nice_address_set_ipv4 --nice_address_set_ipv6 --nice_address_set_port --nice_address_to_string --nice_agent_add_local_address --nice_agent_add_stream --nice_agent_attach_recv --nice_agent_forget_relays --nice_agent_gather_candidates --nice_agent_generate_local_candidate_sdp --nice_agent_generate_local_sdp --nice_agent_generate_local_stream_sdp --nice_agent_get_default_local_candidate --nice_agent_get_local_candidates --nice_agent_get_local_credentials --nice_agent_get_remote_candidates --nice_agent_get_selected_pair --nice_agent_get_selected_socket --nice_agent_get_stream_name --nice_agent_get_type --nice_agent_new --nice_agent_new_reliable --nice_agent_parse_remote_candidate_sdp --nice_agent_parse_remote_sdp --nice_agent_parse_remote_stream_sdp --nice_agent_remove_stream --nice_agent_restart --nice_agent_send --nice_agent_set_port_range --nice_agent_set_relay_info --nice_agent_set_remote_candidates --nice_agent_set_remote_credentials --nice_agent_set_local_credentials --nice_agent_set_selected_pair --nice_agent_set_selected_remote_candidate --nice_agent_set_software --nice_agent_set_stream_name --nice_agent_set_stream_tos --nice_candidate_copy --nice_candidate_free --nice_candidate_new --nice_component_state_to_string --nice_debug_disable --nice_debug_enable --nice_interfaces_get_ip_for_interface --nice_interfaces_get_local_interfaces --nice_interfaces_get_local_ips --pseudo_tcp_set_debug_level --pseudo_tcp_socket_close --pseudo_tcp_socket_connect --pseudo_tcp_socket_get_error --pseudo_tcp_socket_get_next_clock --pseudo_tcp_socket_new --pseudo_tcp_socket_notify_clock --pseudo_tcp_socket_notify_mtu --pseudo_tcp_socket_notify_packet --pseudo_tcp_socket_recv --pseudo_tcp_socket_send --stun_agent_build_unknown_attributes_error --stun_agent_default_validater --stun_agent_finish_message --stun_agent_forget_transaction --stun_agent_init --stun_agent_init_error --stun_agent_init_indication --stun_agent_init_request --stun_agent_init_response --stun_agent_set_software --stun_agent_validate --stun_debug_disable --stun_debug_enable --stun_debug --stun_debug_bytes --stun_hash_creds --stun_message_append --stun_message_append32 --stun_message_append64 --stun_message_append_addr --stun_message_append_bytes --stun_message_append_error --stun_message_append_flag --stun_message_append_string --stun_message_append_xor_addr --stun_message_append_xor_addr_full --stun_message_find --stun_message_find32 --stun_message_find64 --stun_message_find_addr --stun_message_find_error --stun_message_find_flag --stun_message_find_string --stun_message_find_xor_addr --stun_message_find_xor_addr_full --stun_message_get_class --stun_message_get_method --stun_message_has_attribute --stun_message_has_cookie --stun_message_id --stun_message_init --stun_message_length --stun_message_validate_buffer_length --stun_optional --stun_strerror --stun_timer_refresh --stun_timer_remainder --stun_timer_start --stun_timer_start_reliable --stun_usage_bind_create --stun_usage_bind_keepalive --stun_usage_bind_process --stun_usage_bind_run --stun_usage_ice_conncheck_create --stun_usage_ice_conncheck_create_reply --stun_usage_ice_conncheck_priority --stun_usage_ice_conncheck_process --stun_usage_ice_conncheck_use_candidate --stun_usage_turn_create --stun_usage_turn_create_refresh --stun_usage_turn_process --stun_usage_turn_refresh_process diff --git a/libnice.spec b/libnice.spec index 8231dc4..f69c824 100644 --- a/libnice.spec +++ b/libnice.spec @@ -2,17 +2,17 @@ %bcond_with gst010 Name: libnice -Version: 0.1.13 -Release: 11%{?dist} +Version: 0.1.14 +Release: 1%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL:
https://nice.freedesktop.org/wiki/
Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
-Patch1: libnice-0.1.13-20160610.patch BuildRequires: glib2-devel +BuildRequires: gnutls-devel >= 2.12.0 BuildRequires: gobject-introspection-devel %if %{with gst010} BuildRequires: gstreamer-devel @@ -75,7 +75,6 @@ developing applications that use %{name}. %prep %setup -q -%patch1 -p1 %check #make check @@ -133,6 +132,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog +* Mon Jan 29 2018 Stefan Becker <chemobejk(a)gmail.com> - 0.1.14-1 +- Update to 0.1.14 + * Wed Jan 24 2018 Tomas Hoger <thoger(a)redhat.com> - 0.1.13-11 - Add conditional for building with(out) gst010 / GStreamer 0.10 support. - Disable gst010 plugin by default. diff --git a/sources b/sources index 111369c..d14a570 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -3226faeaf48a9150ada00da2e2865959 libnice-0.1.13.tar.gz +SHA512 (libnice-0.1.14.tar.gz) = 58bd0e0f630f4f14fe4765f2dab24215d71d61a4e7dc260cdb34eb0254b3dcdc9ce3b6fb010800250fb85f91e06b1c48cfcdd6e18867324609ead17a9106d4dd commit dbce58ce3ca1376450d4aab31e5107a8c1f062aa Author: Stefan Becker <chemobejk(a)gmail.com> Date: Thu May 4 09:40:36 2017 +0300 expand tabs in libnice.spec diff --git a/libnice.spec b/libnice.spec index d9fbb06..8231dc4 100644 --- a/libnice.spec +++ b/libnice.spec @@ -12,15 +12,15 @@ URL:
https://nice.freedesktop.org/wiki/
Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
Patch1: libnice-0.1.13-20160610.patch -BuildRequires: glib2-devel -BuildRequires: gobject-introspection-devel +BuildRequires: glib2-devel +BuildRequires: gobject-introspection-devel %if %{with gst010} BuildRequires: gstreamer-devel -BuildRequires: gstreamer-plugins-base-devel +BuildRequires: gstreamer-plugins-base-devel %endif BuildRequires: gstreamer1-devel >= 0.11.91 -BuildRequires: gstreamer1-plugins-base-devel >= 0.11.91 -BuildRequires: gupnp-igd-devel >= 0.1.2 +BuildRequires: gstreamer1-plugins-base-devel >= 0.11.91 +BuildRequires: gupnp-igd-devel >= 0.1.2 %description @@ -65,8 +65,8 @@ The %{name}-examples package contains usage (simple, threaded and sdp) examples. Summary: Development files for %{name} Group: Development/Libraries Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: glib2-devel -Requires: pkgconfig +Requires: glib2-devel +Requires: pkgconfig %description devel The %{name}-devel package contains libraries and header files for commit a4ee9cf78ac3fe4c30a7e5f3d58de87864d5ccd0 Author: Kamil Dudka <kdudka(a)redhat.com> Date: Mon Jan 29 16:10:57 2018 +0100 use https:// URLs in specfile ... and a more generic glob in .gitignore diff --git a/.gitignore b/.gitignore index def842a..97f428b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1 @@ -libnice-0.0.12.tar.gz -libnice-0.0.13.tar.gz -/libnice-0.1.0.tar.gz -/libnice-0.1.1.tar.gz -/libnice-0.1.2.tar.gz -/libnice-0.1.3.tar.gz -/libnice-0.1.4.tar.gz -/libnice-0.1.8.tar.gz -/libnice-0.1.10.tar.gz -/libnice-0.1.11.tar.gz -/libnice-0.1.13.tar.gz +/libnice-0.1.[0-9][0-9].tar.gz diff --git a/libnice.spec b/libnice.spec index 9abb00a..d9fbb06 100644 --- a/libnice.spec +++ b/libnice.spec @@ -8,8 +8,8 @@ Summary: GLib ICE implementation Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 -URL:
http://nice.freedesktop.org/wiki/
-Source0:
http://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
+URL:
https://nice.freedesktop.org/wiki/
+Source0:
https://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz
Patch1: libnice-0.1.13-20160610.patch BuildRequires: glib2-devel commit 746455c9c6e7b6e406e588b4f7e7a3429b50e8ed Author: Tomas Hoger <thoger(a)redhat.com> Date: Wed Jan 24 17:14:05 2018 +0100 Disable gst010 support everywhere GStreamer 0.10 plugin is not required by any other package any more, so stop building it even in Fedora. diff --git a/libnice.spec b/libnice.spec index 3097910..9abb00a 100644 --- a/libnice.spec +++ b/libnice.spec @@ -1,9 +1,5 @@ -# enable/disable building of plugin for gstreamer 0.10 -%if 0%{?rhel} > 7 +# disable building of plugin for gstreamer 0.10 %bcond_with gst010 -%else -%bcond_without gst010 -%endif Name: libnice Version: 0.1.13 @@ -138,7 +134,8 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog * Wed Jan 24 2018 Tomas Hoger <thoger(a)redhat.com> - 0.1.13-11 -- Add conditional for building with(out) gst010 / GStreamer 0.10 support +- Add conditional for building with(out) gst010 / GStreamer 0.10 support. +- Disable gst010 plugin by default. * Thu Aug 03 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 0.1.13-10 - Rebuilt for
https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
1
0
0
0
Architecture specific change in rpms/libsbml.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/libsbml.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/libsbml.git/commit/?id=7a810b9ef5ee…
. Change: -%ifarch %{ix86} Thanks. Full change: ============ commit 7a810b9ef5ee78ee20a835e62d87229d43185a2f Author: sagitter <sagitter(a)fedoraproject.org> Date: Thu Jun 28 12:15:26 2018 +0200 Fix upstream bug #463 diff --git a/libsbml-5.17.0-fix_bug463.patch b/libsbml-5.17.0-fix_bug463.patch new file mode 100644 index 0000000..6a82ca1 --- /dev/null +++ b/libsbml-5.17.0-fix_bug463.patch @@ -0,0 +1,18 @@ +--- a/src/sbml/Compartment.cpp ++++ b/src/sbml/Compartment.cpp +@@ -283,7 +283,14 @@ + } + else + { +- return static_cast<unsigned int>(mSpatialDimensionsDouble); ++ if (util_isNaN(mSpatialDimensionsDouble)) ++ { ++ return 0; ++ } ++ else ++ { ++ return static_cast<unsigned int>(mSpatialDimensionsDouble); ++ } + } + } + } diff --git a/libsbml.spec b/libsbml.spec index 64877e6..4d154e1 100644 --- a/libsbml.spec +++ b/libsbml.spec @@ -34,7 +34,7 @@ Name: libsbml Version: 5.17.0 -Release: 3%{?dist} +Release: 4%{?dist} Summary: Systems Biology Markup Language library License: LGPLv2+ @@ -51,6 +51,9 @@ Patch1: libsbml-fix_install_libpaths.patch ##
https://sourceforge.net/p/sbml/libsbml/461/
Patch2: libsbml-%{version}-swigdoc_fix.patch +##
https://sourceforge.net/p/sbml/libsbml/463/
+Patch3: libsbml-%{version}-fix_bug463.patch + BuildRequires: cmake BuildRequires: zlib-devel BuildRequires: bzip2-devel @@ -210,6 +213,7 @@ This package contains %{summary}. %autosetup -n libSBML-%{version}-Source -N %patch0 -p0 %patch1 -p1 +%patch3 -p1 %if %{with python3} %patch2 -p1 @@ -370,12 +374,6 @@ make -C build-docs install-pc DESTDIR=%{buildroot} install -Dm0644 src/bindings/ruby/README.txt %{buildroot}%{_pkgdocdir}/README-ruby.txt %endif -##test_csharp_bindings_full fails on various architectures -##https://sourceforge.net/p/sbml/libsbml/429/ -##https://bugzilla.redhat.com/show_bug.cgi?id=1402549 -## -##test_sbml_sbml_run fails on i686 -##https://sourceforge.net/p/sbml/libsbml/463/ %if %{with check} %check @@ -383,21 +381,13 @@ install -Dm0644 src/bindings/ruby/README.txt %{buildroot}%{_pkgdocdir}/README-ru export CK_FORK=no pushd build2 ctest --force-new-ctest-process -VV \ -%ifarch %{ix86} - -E "test_ruby_binding|test_sbml_sbml_run" -%else -E "test_ruby_binding" -%endif popd %endif pushd build ctest --force-new-ctest-process -VV \ -%ifarch %{ix86} - -E "test_csharp_bindings_full|test_ruby_binding|test_sbml_sbml_run" -%else -E "test_ruby_binding" -%endif popd %endif @@ -487,6 +477,9 @@ popd %endif %changelog +* Thu Jun 28 2018 Antonio Trande <
sagitterATfedoraproject.org
> - 5.17.0-4 +- Fix upstream bug #463 + * Wed Jun 27 2018 Antonio Trande <
sagitterATfedoraproject.org
> - 5.17.0-3 - Fix Python37 compiling error (upstream bug #461) (bz#1594498)
1
0
0
0
Architecture specific change in rpms/python-astropy.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/python-astropy.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/python-astropy.git/commit/?id=663da…
. Change: -%ifnarch s390x Thanks. Full change: ============ commit 663daca6716ee5ca27a6005ca87365c0ea975bf6 Author: Christian Dersch <lupinix(a)fedoraproject.org> Date: Thu Jun 28 10:12:46 2018 +0200 Disable tests until we have the pyyaml fix
https://github.com/yaml/pyyaml/pull/181
diff --git a/python-astropy.spec b/python-astropy.spec index 18b4886..caf42e1 100644 --- a/python-astropy.spec +++ b/python-astropy.spec @@ -12,7 +12,7 @@ Name: python-astropy Version: 3.0.3 -Release: 2%{?dist} +Release: 3%{?dist} Summary: A Community Python Library for Astronomy License: BSD @@ -176,14 +176,17 @@ rm -f docs/_build/html/.buildinfo find %{buildroot} -name "*.so" | xargs chmod 755 %check +# Disable tests until we have that fix in Fedoray pyyaml package +# check:
https://github.com/yaml/pyyaml/pull/181
+# # Tests on s390x tend to stuck (already for scipy used by astropy) -%ifnarch s390x -pushd %{buildroot}/%{python3_sitearch} -py.test-%{python3_version} -k "not test_write_read_roundtrip" astropy +#%%ifnarch s390x +#pushd %%{buildroot}/%%{python3_sitearch} +#py.test-%%{python3_version} -k "not test_write_read_roundtrip" astropy # Remove spurious test relict -rm -fr .pytest_cache -popd -%endif # ifnarch s390x +#rm -fr .pytest_cache +#popd +#%%endif # ifnarch s390x %files -n %{srcname}-tools @@ -199,6 +202,10 @@ popd %license LICENSE.rst %changelog +* Thu Jun 28 2018 Christian Dersch <lupinix(a)fedoraproject.org> - 3.0.3-3 +- Disable tests until we have the pyyaml fix +
https://github.com/yaml/pyyaml/pull/181
+ * Tue Jun 19 2018 Miro Hrončok <mhroncok(a)redhat.com> - 3.0.3-2 - Rebuilt for Python 3.7
1
0
0
0
Architecture specific change in rpms/opencryptoki.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/opencryptoki.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/opencryptoki.git/commit/?id=140630b…
. Change: -%ifarch s390 s390x Thanks. Full change: ============ commit 140630b25fceff9ab98761cec38f75b4be5ad1f7 Author: Dan Horák <dan(a)danny.cz> Date: Thu Jun 28 10:09:00 2018 +0200 spec modernization diff --git a/opencryptoki.spec b/opencryptoki.spec index 65bc161..412dfe3 100644 --- a/opencryptoki.spec +++ b/opencryptoki.spec @@ -200,26 +200,6 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/*.la rm -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/stdll/*.la -%post libs -p /sbin/ldconfig -%post swtok -p /sbin/ldconfig -%post tpmtok -p /sbin/ldconfig -%post icsftok -p /sbin/ldconfig -%ifarch s390 s390x -%post icatok -p /sbin/ldconfig -%post ccatok -p /sbin/ldconfig -%post ep11tok -p /sbin/ldconfig -%endif - -%postun libs -p /sbin/ldconfig -%postun swtok -p /sbin/ldconfig -%postun tpmtok -p /sbin/ldconfig -%postun icsftok -p /sbin/ldconfig -%ifarch s390 s390x -%postun icatok -p /sbin/ldconfig -%postun ccatok -p /sbin/ldconfig -%postun ep11tok -p /sbin/ldconfig -%endif - %pre libs getent group pkcs11 >/dev/null || groupadd -r pkcs11 exit 0
1
0
0
0
Architecture specific change in rpms/ceph.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/ceph.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/ceph.git/commit/?id=9094835645403e6…
. Change: +%ifarch %{arm} Thanks. Full change: ============ commit 9094835645403e63f2e8a34a33e8fc9672d9f64d Author: Kaleb S. KEITHLEY <kkeithle(a)redhat.com> Date: Thu Jun 28 02:04:51 2018 -0400 New release (1:13.2.0-3) Signed-off-by: Kaleb S. KEITHLEY <kkeithle(a)redhat.com> diff --git a/0001-9df56dc509a6cbb7c1da8073e82fc1d2a284418d.patch b/0001-9df56dc509a6cbb7c1da8073e82fc1d2a284418d.patch deleted file mode 100644 index ffc38da..0000000 --- a/0001-9df56dc509a6cbb7c1da8073e82fc1d2a284418d.patch +++ /dev/null @@ -1,165 +0,0 @@ ---- ceph-13.1.0/src/mon/LogMonitor.cc.orig 2018-05-08 08:12:50.101933925 -0400 -+++ ceph-13.1.0/src/mon/LogMonitor.cc 2018-05-08 08:12:59.470933925 -0400 -@@ -62,7 +62,6 @@ - { - dout(10) << "create_initial -- creating initial map" << dendl; - LogEntry e; -- memset(&e.who, 0, sizeof(e.who)); - e.name = g_conf->name; - e.stamp = ceph_clock_now(); - e.prio = CLOG_INFO; ---- ceph-13.1.0/src/mon/MonMap.h.orig 2018-05-08 08:13:08.173933925 -0400 -+++ ceph-13.1.0/src/mon/MonMap.h 2018-05-08 08:13:17.391933925 -0400 -@@ -118,7 +118,6 @@ - - MonMap() - : epoch(0) { -- memset(&fsid, 0, sizeof(fsid)); - } - - uuid_d& get_fsid() { return fsid; } ---- ceph-13.1.0/src/os/filestore/HashIndex.h.orig 2018-05-08 08:13:35.377933925 -0400 -+++ ceph-13.1.0/src/os/filestore/HashIndex.h 2018-05-08 08:13:59.853933925 -0400 -@@ -395,7 +395,7 @@ - - struct CmpPairBitwise { - bool operator()(const pair<string, ghobject_t>& l, -- const pair<string, ghobject_t>& r) -+ const pair<string, ghobject_t>& r) const - { - if (l.first < r.first) - return true; -@@ -408,7 +408,7 @@ - }; - - struct CmpHexdigitStringBitwise { -- bool operator()(const string& l, const string& r) { -+ bool operator()(const string& l, const string& r) const { - return reverse_hexdigit_bits_string(l) < reverse_hexdigit_bits_string(r); - } - }; ---- ceph-13.1.0/src/os/filestore/LFNIndex.h.orig 2018-05-08 08:14:06.721933925 -0400 -+++ ceph-13.1.0/src/os/filestore/LFNIndex.h 2018-05-08 08:14:34.671933925 -0400 -@@ -63,7 +63,7 @@ - out: \ - complete_inject_failure(); \ - return r; \ -- } catch (RetryException) { \ -+ } catch (RetryException&) { \ - failed = true; \ - } catch (...) { \ - ceph_abort(); \ ---- ceph-13.1.0/src/client/Inode.h.orig 2018-05-08 08:08:26.305933925 -0400 -+++ ceph-13.1.0/src/client/Inode.h 2018-05-08 08:08:38.029933925 -0400 -@@ -279,7 +279,6 @@ - _ref(0), ll_ref(0) - { - memset(&dir_layout, 0, sizeof(dir_layout)); -- memset("a, 0, sizeof(quota)); - } - ~Inode(); - ---- ceph-13.1.0/src/osd/OSDMap.h.orig 2018-05-08 08:14:51.066933925 -0400 -+++ ceph-13.1.0/src/osd/OSDMap.h 2018-05-08 08:15:14.148933925 -0400 -@@ -427,7 +427,6 @@ - encode_features(0), - epoch(e), new_pool_max(-1), new_flags(-1), new_max_osd(-1), - have_crc(false), full_crc(0), inc_crc(0) { -- memset(&fsid, 0, sizeof(fsid)); - } - explicit Incremental(bufferlist &bl) { - bufferlist::iterator p = bl.begin(); -@@ -607,7 +606,6 @@ - cached_up_osd_features(0), - crc_defined(false), crc(0), - crush(std::make_shared<CrushWrapper>()) { -- memset(&fsid, 0, sizeof(fsid)); - } - - private: ---- ceph-13.1.0/src/common/cmdparse.h.orig 2018-05-08 08:09:17.772933925 -0400 -+++ ceph-13.1.0/src/common/cmdparse.h 2018-05-08 08:09:36.500933925 -0400 -@@ -54,7 +54,7 @@ - try { - val = boost::get<T>(cmdmap.find(k)->second); - return true; -- } catch (boost::bad_get) { -+ } catch (boost::bad_get&) { - handle_bad_get(cct, k, typeid(T).name()); - } - } ---- ceph-13.1.0/src/messages/MClientReply.h.orig 2018-05-08 08:10:22.281933925 -0400 -+++ ceph-13.1.0/src/messages/MClientReply.h 2018-05-08 08:10:46.660933925 -0400 -@@ -187,7 +187,7 @@ - if (features & CEPH_FEATURE_MDS_QUOTA) - decode(quota, p); - else -- memset("a, 0, sizeof(quota)); -+ quota = quota_info_t{}; - - if ((features & CEPH_FEATURE_FS_FILE_LAYOUT_V2)) - decode(layout.pool_ns, p); ---- ceph-13.1.0/src/messages/MMonSubscribeAck.h.orig 2018-05-08 08:10:58.478933925 -0400 -+++ ceph-13.1.0/src/messages/MMonSubscribeAck.h 2018-05-08 08:11:17.484933925 -0400 -@@ -23,7 +23,6 @@ - - MMonSubscribeAck() : Message(CEPH_MSG_MON_SUBSCRIBE_ACK), - interval(0) { -- memset(&fsid, 0, sizeof(fsid)); - } - MMonSubscribeAck(uuid_d& f, int i) : Message(CEPH_MSG_MON_SUBSCRIBE_ACK), - interval(i), fsid(f) { } ---- ceph-13.1.0/src/mgr/DaemonState.h.orig 2018-05-08 08:12:16.521933925 -0400 -+++ ceph-13.1.0/src/mgr/DaemonState.h 2018-05-08 08:12:40.995933925 -0400 -@@ -130,7 +130,7 @@ - auto p = config_defaults_bl.begin(); - try { - decode(config_defaults, p); -- } catch (buffer::error e) { -+ } catch (buffer::error& e) { - } - } - return config_defaults; ---- ceph-13.1.0/cmake/modules/BuildDPDK.cmake.orig 2018-05-08 08:41:02.168933925 -0400 -+++ ceph-13.1.0/cmake/modules/BuildDPDK.cmake 2018-05-08 08:41:47.411933925 -0400 -@@ -71,7 +71,7 @@ - BUILD_IN_SOURCE 1 - INSTALL_COMMAND "true") - ExternalProject_Add_Step(dpdk-ext patch-config -- COMMAND ${CMAKE_MODULE_PATH}/patch-dpdk-conf.sh ${dpdk_dir} ${machine} -+ COMMAND ${CMAKE_MODULE_PATH}/patch-dpdk-conf.sh ${dpdk_dir} ${machine} ${arch} - DEPENDEES configure - DEPENDERS build) - # easier to adjust the config -@@ -86,7 +86,7 @@ - # target - file(MAKE_DIRECTORY ${DPDK_INCLUDE_DIR}) - foreach(c -- pci bus_pci -+ bus_pci pci - eal - mempool mempool_ring mempool_stack ring) - add_library(dpdk::${c} STATIC IMPORTED) ---- ceph-13.1.0/cmake/modules/patch-dpdk-conf.sh.orig 2018-05-08 08:42:01.089933925 -0400 -+++ ceph-13.1.0/cmake/modules/patch-dpdk-conf.sh 2018-05-08 08:43:11.781933925 -0400 -@@ -15,8 +15,12 @@ - shift - machine=$1 - shift -+arch=$1 -+shift - - setconf CONFIG_RTE_MACHINE "${machine}" -+setconf CONFIG_RTE_ARCH "${arch}" -+ - # Disable experimental features - setconf CONFIG_RTE_NEXT_ABI n - setconf CONFIG_RTE_LIBRTE_MBUF_OFFLOAD n -@@ -38,6 +42,7 @@ - setconf CONFIG_RTE_LIBRTE_VMXNET3_PMD n - setconf CONFIG_RTE_LIBRTE_PMD_VHOST n - setconf CONFIG_RTE_APP_EVENTDEV n -+setconf CONFIG_RTE_MAX_VFIO_GROUPS 64 - - # no test - setconf CONFIG_RTE_APP_TEST n diff --git a/0001-blobstore.patch b/0001-blobstore.patch new file mode 100644 index 0000000..97462f0 --- /dev/null +++ b/0001-blobstore.patch @@ -0,0 +1,11 @@ +--- ceph-13.1.0/src/spdk/lib/blob/blobstore.c.orig 2018-05-08 16:04:30.329933925 -0400 ++++ ceph-13.1.0/src/spdk/lib/blob/blobstore.c 2018-05-08 16:04:55.240933925 -0400 +@@ -2947,7 +2947,7 @@ + + /* START spdk_blob_resize */ + int +-spdk_blob_resize(struct spdk_blob *_blob, uint64_t sz) ++spdk_blob_resize(struct spdk_blob *_blob, size_t sz) + { + struct spdk_blob_data *blob = __blob_to_data(_blob); + int rc; diff --git a/ceph.spec b/ceph.spec index 8b0de11..96ba41f 100644 --- a/ceph.spec +++ b/ceph.spec @@ -16,6 +16,7 @@ # %global _hardened_build 1 +%bcond_with python3 %bcond_without ocf %bcond_with make_check %ifarch s390 s390x @@ -28,6 +29,7 @@ %bcond_without ceph_test_package %bcond_without cephfs_java %bcond_without lttng +%global _remote_tarball_prefix
https://download.ceph.com/tarballs/
%endif %if 0%{?suse_version} %bcond_with selinux @@ -75,22 +77,23 @@ %global _find_debuginfo_dwz_opts %{nil} %if ( 0%{?rhel} && 0%{?rhel} < 7 ) -%global _rundir %{_localstatedir}/run +%global _rundir %{_localstatedir}/run/ %else -%global _rundir /run +%global _rundir /run/ %endif ################################################################################# # main package definition ################################################################################# Name: ceph -Version: 13.1.0 -Release: 2%{?dist} +Version: 13.2.0 +Release: 3%{?dist} %if 0%{?fedora} || 0%{?rhel} Epoch: 1 %endif -# define %%_epoch_prefix macro which will expand to the empty string if %%epoch is undefined +# define _epoch_prefix macro which will expand to the empty string if epoch is +# undefined %global _epoch_prefix %{?epoch:%{epoch}:} Summary: User space components of the Ceph file system @@ -99,14 +102,9 @@ License: LGPL-2.1 and CC-BY-SA-3.0 and GPL-2.0 and BSL-1.0 and BSD-3-Clause and Group: System/Filesystems %endif URL:
http://ceph.com/
-Source0:
http://download.ceph.com/tarballs/%{name}-%{version}.tar.gz
+Source0: %{?_remote_tarball_prefix}%{name}-%{version}.tar.gz #
https://bugzilla.redhat.com/show_bug.cgi?id=1474773
-Patch001: 0001-9df56dc509a6cbb7c1da8073e82fc1d2a284418d.patch -#Patch001: 0001-src-rocksdb-util-murmurhash.patch -#
https://bugzilla.redhat.com/show_bug.cgi?id=1474774
-#Patch002: 0002-cmake-Support-ppc64.patch -#Patch003: 0003-librbd-Conditionally-import-TrimRequest.cc.patch -#Patch005: 0005-src-rocksdb-table-block.h.patch +Patch001: 0001-blobstore.patch %if 0%{?suse_version} # _insert_obs_source_lines_here %if 0%{?is_opensuse} @@ -136,6 +134,7 @@ BuildRequires: gperf BuildRequires: cmake BuildRequires: cryptsetup BuildRequires: fuse-devel +BuildRequires: cryptopp-devel %if 0%{?rhel} == 7 # devtoolset offers newer make and valgrind-devel, but the old ones are good # enough. @@ -204,7 +203,6 @@ BuildRequires: python%{_python_buildid}-PrettyTable BuildRequires: python%{_python_buildid}-Sphinx BuildRequires: rdma-core-devel BuildRequires: liblz4-devel >= 1.7 -BuildRequires: rdma-core-devel %endif %if 0%{?fedora} || 0%{?rhel} Requires: systemd @@ -212,7 +210,7 @@ BuildRequires: boost-random BuildRequires: btrfs-progs BuildRequires: nss-devel BuildRequires: keyutils-libs-devel -# RDMA is no longer built on 32-bit ARM: see #1484155 +# RDMA is no longer built on 32-bit ARM: see rhbz#1484155 %ifnarch %{arm} BuildRequires: rdma-core-devel %endif @@ -226,7 +224,8 @@ BuildRequires: python%{_python_buildid}-sphinx BuildRequires: lz4-devel >= 1.7 %endif # python34-... for RHEL, python3-... for all other supported distros -%if ( 0%{?rhel} && 0%{?rhel} <= 7 ) +%if %{with python3} +%if 0%{?rhel} BuildRequires: python34-devel BuildRequires: python34-setuptools BuildRequires: python34-Cython @@ -235,6 +234,7 @@ BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-Cython %endif +%endif # distro-conditional make check dependencies %if 0%{with make_check} %if 0%{?fedora} || 0%{?rhel} @@ -394,6 +394,12 @@ Requires: python%{_python_buildid}-jinja2 Requires: python%{_python_buildid}-pecan Requires: python%{_python_buildid}-werkzeug Requires: pyOpenSSL%{_python_buildid} +%if 0%{?fedora} +Requires: python%{_python_buildid}-bcrypt +%endif +%if 0%{?rhel} +Requires: py-bcrypt +%endif %endif %if 0%{?suse_version} Requires: python%{_python_buildid}-CherryPy @@ -562,6 +568,7 @@ This package contains Python 2 libraries for interacting with Cephs RADOS gateway. %endif +%if 0%{with python3} %package -n python%{python3_pkgversion}-rgw Summary: Python 3 libraries for the RADOS gateway %if 0%{?suse_version} @@ -572,6 +579,7 @@ Requires: python%{python3_pkgversion}-rados = %{_epoch_prefix}%{version}-%{relea %description -n python%{python3_pkgversion}-rgw This package contains Python 3 libraries for interacting with Cephs RADOS gateway. +%endif %if 0%{with python2} %package -n python-rados @@ -586,6 +594,7 @@ This package contains Python 2 libraries for interacting with Cephs RADOS object store. %endif +%if 0%{with python3} %package -n python%{python3_pkgversion}-rados Summary: Python 3 libraries for the RADOS object store %if 0%{?suse_version} @@ -596,6 +605,7 @@ Requires: librados2 = %{_epoch_prefix}%{version}-%{release} %description -n python%{python3_pkgversion}-rados This package contains Python 3 libraries for interacting with Cephs RADOS object store. +%endif %package -n libradosstriper1 Summary: RADOS striping interface @@ -669,6 +679,7 @@ This package contains Python 2 libraries for interacting with Cephs RADOS block device. %endif +%if 0%{with python3} %package -n python%{python3_pkgversion}-rbd Summary: Python 3 libraries for the RADOS block device %if 0%{?suse_version} @@ -679,6 +690,7 @@ Requires: python%{python3_pkgversion}-rados = %{_epoch_prefix}%{version}-%{relea %description -n python%{python3_pkgversion}-rbd This package contains Python 3 libraries for interacting with Cephs RADOS block device. +%endif %package -n libcephfs2 Summary: Ceph distributed file system client library @@ -727,6 +739,7 @@ This package contains Python 2 libraries for interacting with Cephs distributed file system. %endif +%if 0%{with python3} %package -n python%{python3_pkgversion}-cephfs Summary: Python 3 libraries for Ceph distributed file system %if 0%{?suse_version} @@ -737,8 +750,9 @@ Requires: python%{python3_pkgversion}-rados = %{_epoch_prefix}%{version}-%{relea %description -n python%{python3_pkgversion}-cephfs This package contains Python 3 libraries for interacting with Cephs distributed file system. +%endif -%if 0%{with python2} +%if 0%{with python3} %package -n python%{python3_pkgversion}-ceph-argparse Summary: Python 3 utility libraries for Ceph CLI %if 0%{?suse_version} @@ -919,7 +933,11 @@ cmake .. \ -DCMAKE_INSTALL_INCLUDEDIR=%{_includedir} \ -DWITH_EMBEDDED=OFF \ -DWITH_MANPAGE=ON \ +%if %{with python3} -DWITH_PYTHON3=ON \ +%else + -DWITH_PYTHON3=OFF \ +%endif -DWITH_MGR_DASHBOARD_FRONTEND=OFF \ %if %{with python2} -DWITH_PYTHON2=ON \ @@ -927,7 +945,7 @@ cmake .. \ -DWITH_PYTHON2=OFF \ -DMGR_PYTHON_VERSION=3 \ %endif -%if ( ( 0%{?rhel} && 0%{?rhel} <= 7) && ! 0%{?centos} ) +%if ( ( 0%{?rhel} && 0%{?rhel} < 8) && ! 0%{?centos} ) -DWITH_SUBMAN=ON \ %endif %if 0%{without ceph_test_package} @@ -955,13 +973,15 @@ cmake .. \ %else -DWITH_BOOST_CONTEXT=OFF \ %endif -%ifnarch %{arm} +%ifarch %{arm} -DWITH_RDMA=OFF \ %endif + -DWITH_DPDK=ON \ -DBOOST_J=$CEPH_SMP_NCPUS make "$CEPH_MFLAGS_JOBS" + %if 0%{with make_check} %check # run in-tree unittests @@ -1006,7 +1026,7 @@ install -m 0644 -D udev/95-ceph-osd.rules %{buildroot}%{_udevrulesdir}/95-ceph-o #set up placeholder directories mkdir -p %{buildroot}%{_sysconfdir}/ceph -mkdir -p %{buildroot}%{_rundir}/ceph +mkdir -p %{buildroot}%{_rundir}ceph mkdir -p %{buildroot}%{_localstatedir}/log/ceph mkdir -p %{buildroot}%{_localstatedir}/lib/ceph/tmp mkdir -p %{buildroot}%{_localstatedir}/lib/ceph/mon @@ -1025,6 +1045,9 @@ mkdir -p %{buildroot}%{_localstatedir}/lib/ceph/bootstrap-rbd %py3_compile %{buildroot}%{python3_sitelib} %endif +%clean +rm -rf %{buildroot} + ################################################################################# # files and systemd scriptlets ################################################################################# @@ -1071,7 +1094,8 @@ mkdir -p %{buildroot}%{_localstatedir}/lib/ceph/bootstrap-rbd %if 0%{with python2} %{python_sitelib}/ceph_detect_init* %{python_sitelib}/ceph_disk* -%else +%endif +%if 0%{with python3} %{python3_sitelib}/ceph_detect_init* %{python3_sitelib}/ceph_disk* %endif @@ -1079,7 +1103,8 @@ mkdir -p %{buildroot}%{_localstatedir}/lib/ceph/bootstrap-rbd %dir %{python_sitelib}/ceph_volume %{python_sitelib}/ceph_volume/* %{python_sitelib}/ceph_volume-* -%else +%endif +%if 0%{with python3} %dir %{python3_sitelib}/ceph_volume %{python3_sitelib}/ceph_volume/* %{python3_sitelib}/ceph_volume-* @@ -1102,6 +1127,7 @@ mkdir -p %{buildroot}%{_localstatedir}/lib/ceph/bootstrap-rbd %attr(750,ceph,ceph) %dir %{_localstatedir}/lib/ceph/bootstrap-rbd %post base +/sbin/ldconfig %if 0%{?suse_version} %fillup_only if [ $1 -eq 1 ] ; then @@ -1124,6 +1150,7 @@ fi %endif %postun base +/sbin/ldconfig test -n "$FIRST_ARG" || FIRST_ARG=$1 %if 0%{?suse_version} DISABLE_RESTART_ON_UPDATE="yes" @@ -1171,7 +1198,6 @@ fi %{_bindir}/rbd-replay-prep %endif %{_bindir}/ceph-post-file -%{_bindir}/ceph-brag %{_tmpfilesdir}/ceph-common.conf %{_mandir}/man8/ceph-authtool.8* %{_mandir}/man8/ceph-conf.8* @@ -1202,7 +1228,8 @@ fi %if 0%{with python2} %{python_sitelib}/ceph_argparse.py* %{python_sitelib}/ceph_daemon.py* -%else +%endif +%if 0%{with python3} %{python3_sitelib}/ceph_argparse.py %{python3_sitelib}/__pycache__/ceph_argparse.cpython*.py* %{python3_sitelib}/ceph_daemon.py @@ -1529,7 +1556,7 @@ fi %{_mandir}/man8/ceph-bluestore-tool.8* %{_mandir}/man8/ceph-volume.8* %{_mandir}/man8/ceph-volume-systemd.8* -%if ( ( 0%{?rhel} && 0%{?rhel} <= 7) && ! 0%{?centos} ) +%if ( ( 0%{?rhel} && 0%{?rhel} < 8) && ! 0%{?centos} ) %attr(0755,-,-) %{_sysconfdir}/cron.hourly/subman %endif %{_unitdir}/ceph-osd@.service @@ -1543,11 +1570,6 @@ fi if [ $1 -eq 1 ] ; then /usr/bin/systemctl preset ceph-osd(a)\*.service ceph-volume(a)\*.service ceph-osd.target >/dev/null 2>&1 || : fi -%if 0%{?sysctl_apply} - %sysctl_apply 90-ceph-osd.conf -%else - /usr/lib/systemd/systemd-sysctl %{_sysctldir}/90-ceph-osd.conf > /dev/null 2>&1 || : -%endif %endif %if 0%{?fedora} || 0%{?rhel} %systemd_post ceph-osd(a)\*.service ceph-volume(a)\*.service ceph-osd.target @@ -1555,6 +1577,11 @@ fi if [ $1 -eq 1 ] ; then /usr/bin/systemctl start ceph-osd.target >/dev/null 2>&1 || : fi +%if 0%{?sysctl_apply} + %sysctl_apply 90-ceph-osd.conf +%else + /usr/lib/systemd/systemd-sysctl %{_sysctldir}/90-ceph-osd.conf > /dev/null 2>&1 || : +%endif %preun osd %if 0%{?suse_version} @@ -1629,9 +1656,11 @@ fi %{python_sitearch}/rados-*.egg-info %endif +%if 0%{with python3} %files -n python%{python3_pkgversion}-rados %{python3_sitearch}/rados.cpython*.so %{python3_sitearch}/rados-*.egg-info +%endif %ldconfig_scriptlets -n libradosstriper1 %files -n libradosstriper1 @@ -1680,9 +1709,11 @@ fi %{python_sitearch}/rgw-*.egg-info %endif +%if 0%{with python3} %files -n python%{python3_pkgversion}-rgw %{python3_sitearch}/rgw.cpython*.so %{python3_sitearch}/rgw-*.egg-info +%endif %if 0%{with python2} %files -n python-rbd @@ -1690,9 +1721,11 @@ fi %{python_sitearch}/rbd-*.egg-info %endif +%if 0%{with python3} %files -n python%{python3_pkgversion}-rbd %{python3_sitearch}/rbd.cpython*.so %{python3_sitearch}/rbd-*.egg-info +%endif %ldconfig_scriptlets -n libcephfs2 %files -n libcephfs2 @@ -1711,13 +1744,15 @@ fi %{python_sitelib}/ceph_volume_client.py* %endif +%if 0%{with python3} %files -n python%{python3_pkgversion}-cephfs %{python3_sitearch}/cephfs.cpython*.so %{python3_sitearch}/cephfs-*.egg-info %{python3_sitelib}/ceph_volume_client.py %{python3_sitelib}/__pycache__/ceph_volume_client.cpython*.py* +%endif -%if 0%{with python2} +%if 0%{with python3} %files -n python%{python3_pkgversion}-ceph-argparse %{python3_sitelib}/ceph_argparse.py %{python3_sitelib}/__pycache__/ceph_argparse.cpython*.py* @@ -1815,7 +1850,7 @@ fi rm -f ${FILE_CONTEXT}.pre # The fixfiles command won't fix label for /var/run/ceph -/usr/sbin/restorecon -R %{_rundir}/ceph > /dev/null 2>&1 +/usr/sbin/restorecon -R %{_rundir}ceph > /dev/null 2>&1 # Start the daemons iff they were running before if test $STATUS -eq 0; then @@ -1851,7 +1886,7 @@ if [ $1 -eq 0 ]; then /usr/sbin/fixfiles -C ${FILE_CONTEXT}.pre restore 2> /dev/null rm -f ${FILE_CONTEXT}.pre # The fixfiles command won't fix label for /var/run/ceph - /usr/sbin/restorecon -R %{_rundir}/ceph > /dev/null 2>&1 + /usr/sbin/restorecon -R %{_rundir}ceph > /dev/null 2>&1 # Start the daemons if they were running before if test $STATUS -eq 0; then @@ -1870,9 +1905,18 @@ exit 0 %changelog +* Thu Jun 28 2018 Kaleb S. KEITHLEY <
kkeithle[at]redhat.com
> - 1:13.2.0-3 +- New release (1:13.2.0-3) + * Tue Jun 19 2018 Miro Hrončok <mhroncok(a)redhat.com> - 1:13.1.0-2 - Rebuilt for Python 3.7 +* Thu May 31 2018 Kaleb S. KEITHLEY <
kkeithle[at]redhat.com
> - 1:13.2.0-1 +- New release (1:13.2.0-1) + +* Tue May 8 2018 Kaleb S. KEITHLEY <
kkeithle[at]redhat.com
> - 1:13.1.0-2 +- New release (1:13.1.0-2) +crypto_plugins + * Tue May 8 2018 Kaleb S. KEITHLEY <
kkeithle[at]redhat.com
> - 1:13.1.0-1 - New release (1:13.1.0-1) diff --git a/sources b/sources index e0ac550..ae5b656 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (ceph-13.1.0.tar.gz) = d4feb0b0bac5e30232eddcc3f69c1d939f0b5268491acecba9a58dcadbbdaab6367f415ede924ca84de9a89a686410613cdc631f8aff15aec0eb4531138eec7a +SHA512 (ceph-13.2.0.tar.gz) = 2524ad5a868024463e1a1edc5bd9c3f7bc1f58e47bf8d331902ed53206120abc6a427dd9c1d657131e67efd955d6ed8052b6c3fed8658aa905d58ac58ee62973
1
0
0
0
Architecture specific change in rpms/mame.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/mame.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/mame.git/commit/?id=ec8386fd4cf39b1…
. Change: +%ifarch %{arm} Thanks. Full change: ============ commit ec8386fd4cf39b187361de6586cfab0a92aa79b8 Author: Julian Sikorski <belegdol(a)fedoraproject.org> Date: Thu Jun 28 07:12:41 2018 +0200 Updated to 0.199 Dropped upstreamed riscv64 patches Ensured $RPM_OPT_FLAGS are used when building m68kmake Ensured memory-saving compiler/linker flags are only used when needed diff --git a/.gitignore b/.gitignore index baaa367..9ffb638 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ /whatsnew_0197.txt /mame0198s.exe /whatsnew_0198.txt +/mame0199s.exe +/whatsnew_0199.txt diff --git a/mame.spec b/mame.spec index 6efc958..6d2943f 100644 --- a/mame.spec +++ b/mame.spec @@ -1,7 +1,7 @@ #The debug build is disabled by default, please use # --with debug to override %bcond_with debug -%global baseversion 198 +%global baseversion 199 Name: mame Version: 0.%{baseversion} @@ -22,12 +22,13 @@ Source0:
https://github.com/mamedev/%{name}/releases/download/%{name}0%{b
Source1:
http://mamedev.org/releases/whatsnew_0%{baseversion}.txt
Patch0: %{name}-fortify.patch Patch1: %{name}-genie-systemlua.patch -# ppc64: +# %%{power64}: #
https://github.com/mamedev/mame/issues/3157
#
https://bugzilla.redhat.com/show_bug.cgi?id=1541613
-# armv7hl: +# %%{arm}: +#
https://github.com/mamedev/mame/issues/3639
#
https://bugzilla.redhat.com/show_bug.cgi?id=1584711
-ExcludeArch: ppc64 ppc64le armv7hl +ExcludeArch: %{power64} %{arm} #asio in Fedora repositories is too old (1.11.x is needed) #BuildRequires: asio-devel @@ -196,6 +197,10 @@ mv artwork/LICENSE LICENSE.CC0 mv bgfx/LICENSE LICENSE.BSD3 mv plugins/json/LICENSE LICENSE.MIT +#fix m68kmake build flags +sed -i "s@-std=c++11@-std=c++11 $RPM_OPT_FLAGS@" src/devices/cpu/m68000/makefile +sed -i "s@-lstdc++@-lstdc++ $RPM_LD_FLAGS@" src/devices/cpu/m68000/makefile + %build #save some space MAME_FLAGS="NOWERROR=1 OPTIMIZE=2 PYTHON_EXECUTABLE=python3 VERBOSE=1 \ @@ -214,7 +219,13 @@ MAME_FLAGS="NOWERROR=1 OPTIMIZE=2 PYTHON_EXECUTABLE=python3 VERBOSE=1 \ SDL_INI_PATH=%{_sysconfdir}/%{name};" #standard -g causes builder to run out of memory +%ifarch %{arm} %{ix86} s390x RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS | sed "s@-g@-g1@") +#%%arm needs even more measures +%ifarch %{arm} +RPM_LD_FLAGS="$RPM_LD_FLAGS -Wl,--no-keep-memory -Wl,--reduce-memory-overheads" +%endif +%endif %if %{with debug} %make_build $MAME_FLAGS DEBUG=1 TOOLS=1 OPT_FLAGS="$RPM_OPT_FLAGS" \ @@ -344,6 +355,12 @@ find $RPM_BUILD_ROOT%{_datadir}/%{name} -name LICENSE -exec rm {} \; %changelog +* Wed Jun 27 2018 Julian Sikorski <belegdol(a)fedoraproject.org> - 0.199-1 +- Updated to 0.199 +- Dropped upstreamed riscv64 patches +- Ensured $RPM_OPT_FLAGS are used when building m68kmake +- Ensured memory-saving compiler/linker flags are only used when needed + * Wed May 30 2018 Julian Sikorski <belegdol(a)fedoraproject.org> - 0.198-1 - Updated to 0.198 - Ensured python3 is called explicitly as per
https://fedoraproject.org/wiki/Changes/Avoid_usr_bin_python_in_RPM_Build
diff --git a/sources b/sources index 57d19e0..e6e6110 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (mame0198s.exe) = 6753df0841a27c8c0505e6b63cd9d7c9c446eb14359bb3bb34fbfd8e8737885c2f6e93821cddf46bbac5de2368be74d650d95a61831b18d77dd63f155f61875c -SHA512 (whatsnew_0198.txt) = 9f0e6bd14fc4aa97aeed917406babcfb120ab3db042ab75f62d7fc05387431a6a256115c4b65f63d5ad46892098070f6591c20a303902f2fe8d2e24e8369053a +SHA512 (mame0199s.exe) = e6206a233675940ee41c73832e9d98dd99950e401d110c0a632d3d75a302afa1f94e02ae1f3dbff05037749d826ba4b73ca66783f059aec300504b590f4f6d7c +SHA512 (whatsnew_0199.txt) = 962ed76b13c47d484c927af51b8764bae76809ac879d76721d6b436092fa999ac4b06699e9e8db5dbb3d66075f2f57daeac142075d70f82ffe0c596a1e518760
1
0
0
0
Architecture specific change in rpms/mame.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/mame.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/mame.git/commit/?id=3968ca81f96a6e7…
. Change: +%ifarch %{arm} Thanks. Full change: ============ commit 3968ca81f96a6e7dd2ea1485dc8530a0adfdd345 Author: Julian Sikorski <belegdol(a)fedoraproject.org> Date: Wed Jun 27 20:15:31 2018 +0200 Updated to 0.199 Dropped upstreamed riscv64 patches Ensured $RPM_OPT_FLAGS are used when building m68kmake Ensured memory-saving compiler/linker flags are only used when needed diff --git a/.gitignore b/.gitignore index baaa367..9ffb638 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ /whatsnew_0197.txt /mame0198s.exe /whatsnew_0198.txt +/mame0199s.exe +/whatsnew_0199.txt diff --git a/0001-First-attempt-at-enabling-building-on-riscv64.patch b/0001-First-attempt-at-enabling-building-on-riscv64.patch deleted file mode 100644 index efe3eee..0000000 --- a/0001-First-attempt-at-enabling-building-on-riscv64.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0b04a32a946c316ef9e07bff746a5bfe0ba972d4 Mon Sep 17 00:00:00 2001 -From: Julian Sikorski <belegdol+github(a)gmail.com> -Date: Mon, 11 Jun 2018 20:12:18 +0200 -Subject: [PATCH] First attempt at enabling building on riscv64 - ---- - makefile | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/makefile b/makefile -index 119ef47816..37f5268a1d 100644 ---- a/makefile -+++ b/makefile -@@ -349,6 +349,13 @@ ifndef NOASM - endif - endif - -+ifeq ($(findstring riscv64,$(UNAME)),riscv64) -+ARCHITECTURE := -+ifndef NOASM -+ NOASM := 1 -+endif -+endif -+ - # Emscripten - ifeq ($(findstring emcc,$(CC)),emcc) - TARGETOS := asmjs --- -2.17.1 - diff --git a/0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch b/0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch deleted file mode 100644 index 18d0969..0000000 --- a/0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 46de3a0dfb5c5145986de837887b4b7a443477ae Mon Sep 17 00:00:00 2001 -From: Julian Sikorski <belegdol+github(a)gmail.com> -Date: Tue, 12 Jun 2018 17:54:30 +0200 -Subject: [PATCH 2/2] PTR64=1 needs to be defined on riscv64 otherwise build - fails - ---- - makefile | 6 ++++++ - scripts/genie.lua | 6 ++++++ - 2 files changed, 12 insertions(+) - -diff --git a/makefile b/makefile -index 37f5268a1d..5fb09182e9 100644 ---- a/makefile -+++ b/makefile -@@ -166,6 +166,12 @@ endif - ifneq ($(filter powerpc,$(UNAME_P)),) - PLATFORM := powerpc - endif -+ifneq ($(filter riscv64%,$(UNAME_M)),) -+PLATFORM := riscv64 -+endif -+ifneq ($(filter riscv64%,$(UNAME_P)),) -+PLATFORM := riscv64 -+endif - ifneq ($(filter mips64%,$(UNAME_M)),) - ifeq ($(shell getconf LONG_BIT),64) - PLATFORM := mips64 -diff --git a/scripts/genie.lua b/scripts/genie.lua -index 7cf0396b58..ed289f7339 100644 ---- a/scripts/genie.lua -+++ b/scripts/genie.lua -@@ -1061,6 +1061,12 @@ if (_OPTIONS["PLATFORM"]=="arm64") then - } - end - -+if (_OPTIONS["PLATFORM"]=="riscv64") then -+ defines { -+ "PTR64=1", -+ } -+end -+ - if (_OPTIONS["PLATFORM"]=="mips64") then - defines { - "PTR64=1", --- -2.17.1 - diff --git a/mame.spec b/mame.spec index 2524075..c23c735 100644 --- a/mame.spec +++ b/mame.spec @@ -1,7 +1,7 @@ #The debug build is disabled by default, please use # --with debug to override %bcond_with debug -%global baseversion 198 +%global baseversion 199 Name: mame Version: 0.%{baseversion} @@ -22,14 +22,13 @@ Source0:
https://github.com/mamedev/%{name}/releases/download/%{name}0%{b
Source1:
http://mamedev.org/releases/whatsnew_0%{baseversion}.txt
Patch0: %{name}-fortify.patch Patch1: %{name}-genie-systemlua.patch -Patch2: 0001-First-attempt-at-enabling-building-on-riscv64.patch -Patch3: 0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch -# ppc64: +# %%{power64}: #
https://github.com/mamedev/mame/issues/3157
#
https://bugzilla.redhat.com/show_bug.cgi?id=1541613
-# armv7hl: +# %%{arm}: +#
https://github.com/mamedev/mame/issues/3639
#
https://bugzilla.redhat.com/show_bug.cgi?id=1584711
-ExcludeArch: ppc64 ppc64le armv7hl +ExcludeArch: %{power64} %{arm} #asio in Fedora repositories is too old (1.11.x is needed) #BuildRequires: asio-devel @@ -156,8 +155,6 @@ find \( -regex '.*\.\(c\|cpp\|fsh\|fx\|h\|hpp\|lua\|make\|map\|md\|txt\|vsh\|xml %patch0 -p1 -b .fortify %patch1 -p1 -b .systemlua -%patch2 -p1 -%patch3 -p1 # Create ini files cat > %{name}.ini << EOF @@ -200,6 +197,10 @@ mv artwork/LICENSE LICENSE.CC0 mv bgfx/LICENSE LICENSE.BSD3 mv plugins/json/LICENSE LICENSE.MIT +#fix m68kmake build flags +sed -i "s@-std=c++11@-std=c++11 $RPM_OPT_FLAGS@" src/devices/cpu/m68000/makefile +sed -i "s@-lstdc++@-lstdc++ $RPM_LD_FLAGS@" src/devices/cpu/m68000/makefile + %build #save some space MAME_FLAGS="NOWERROR=1 OPTIMIZE=2 PYTHON_EXECUTABLE=python3 VERBOSE=1 \ @@ -218,7 +219,13 @@ MAME_FLAGS="NOWERROR=1 OPTIMIZE=2 PYTHON_EXECUTABLE=python3 VERBOSE=1 \ SDL_INI_PATH=%{_sysconfdir}/%{name};" #standard -g causes builder to run out of memory +%ifarch %{arm} %{ix86} s390x RPM_OPT_FLAGS=$(echo $RPM_OPT_FLAGS | sed "s@-g@-g1@") +#%%arm needs even more measures +%ifarch %{arm} +RPM_LD_FLAGS="$RPM_LD_FLAGS -Wl,--no-keep-memory -Wl,--reduce-memory-overheads" +%endif +%endif %if %{with debug} %make_build $MAME_FLAGS DEBUG=1 TOOLS=1 OPT_FLAGS="$RPM_OPT_FLAGS" \ @@ -348,6 +355,12 @@ find $RPM_BUILD_ROOT%{_datadir}/%{name} -name LICENSE -exec rm {} \; %changelog +* Wed Jun 27 2018 Julian Sikorski <belegdol(a)fedoraproject.org> - 0.199-1 +- Updated to 0.199 +- Dropped upstreamed riscv64 patches +- Ensured $RPM_OPT_FLAGS are used when building m68kmake +- Ensured memory-saving compiler/linker flags are only used when needed + * Wed May 30 2018 Julian Sikorski <belegdol(a)fedoraproject.org> - 0.198-1 - Updated to 0.198 - Ensured python3 is called explicitly as per
https://fedoraproject.org/wiki/Changes/Avoid_usr_bin_python_in_RPM_Build
diff --git a/sources b/sources index 57d19e0..e6e6110 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (mame0198s.exe) = 6753df0841a27c8c0505e6b63cd9d7c9c446eb14359bb3bb34fbfd8e8737885c2f6e93821cddf46bbac5de2368be74d650d95a61831b18d77dd63f155f61875c -SHA512 (whatsnew_0198.txt) = 9f0e6bd14fc4aa97aeed917406babcfb120ab3db042ab75f62d7fc05387431a6a256115c4b65f63d5ad46892098070f6591c20a303902f2fe8d2e24e8369053a +SHA512 (mame0199s.exe) = e6206a233675940ee41c73832e9d98dd99950e401d110c0a632d3d75a302afa1f94e02ae1f3dbff05037749d826ba4b73ca66783f059aec300504b590f4f6d7c +SHA512 (whatsnew_0199.txt) = 962ed76b13c47d484c927af51b8764bae76809ac879d76721d6b436092fa999ac4b06699e9e8db5dbb3d66075f2f57daeac142075d70f82ffe0c596a1e518760 commit 0882b9a5c646eed9a1bd4c1241f7261b012bc4f1 Author: Julian Sikorski <belegdol(a)fedoraproject.org> Date: Tue Jun 12 17:58:28 2018 +0200 Enable building on riscv64 diff --git a/0001-First-attempt-at-enabling-building-on-riscv64.patch b/0001-First-attempt-at-enabling-building-on-riscv64.patch new file mode 100644 index 0000000..efe3eee --- /dev/null +++ b/0001-First-attempt-at-enabling-building-on-riscv64.patch @@ -0,0 +1,30 @@ +From 0b04a32a946c316ef9e07bff746a5bfe0ba972d4 Mon Sep 17 00:00:00 2001 +From: Julian Sikorski <belegdol+github(a)gmail.com> +Date: Mon, 11 Jun 2018 20:12:18 +0200 +Subject: [PATCH] First attempt at enabling building on riscv64 + +--- + makefile | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/makefile b/makefile +index 119ef47816..37f5268a1d 100644 +--- a/makefile ++++ b/makefile +@@ -349,6 +349,13 @@ ifndef NOASM + endif + endif + ++ifeq ($(findstring riscv64,$(UNAME)),riscv64) ++ARCHITECTURE := ++ifndef NOASM ++ NOASM := 1 ++endif ++endif ++ + # Emscripten + ifeq ($(findstring emcc,$(CC)),emcc) + TARGETOS := asmjs +-- +2.17.1 + diff --git a/0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch b/0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch new file mode 100644 index 0000000..18d0969 --- /dev/null +++ b/0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch @@ -0,0 +1,48 @@ +From 46de3a0dfb5c5145986de837887b4b7a443477ae Mon Sep 17 00:00:00 2001 +From: Julian Sikorski <belegdol+github(a)gmail.com> +Date: Tue, 12 Jun 2018 17:54:30 +0200 +Subject: [PATCH 2/2] PTR64=1 needs to be defined on riscv64 otherwise build + fails + +--- + makefile | 6 ++++++ + scripts/genie.lua | 6 ++++++ + 2 files changed, 12 insertions(+) + +diff --git a/makefile b/makefile +index 37f5268a1d..5fb09182e9 100644 +--- a/makefile ++++ b/makefile +@@ -166,6 +166,12 @@ endif + ifneq ($(filter powerpc,$(UNAME_P)),) + PLATFORM := powerpc + endif ++ifneq ($(filter riscv64%,$(UNAME_M)),) ++PLATFORM := riscv64 ++endif ++ifneq ($(filter riscv64%,$(UNAME_P)),) ++PLATFORM := riscv64 ++endif + ifneq ($(filter mips64%,$(UNAME_M)),) + ifeq ($(shell getconf LONG_BIT),64) + PLATFORM := mips64 +diff --git a/scripts/genie.lua b/scripts/genie.lua +index 7cf0396b58..ed289f7339 100644 +--- a/scripts/genie.lua ++++ b/scripts/genie.lua +@@ -1061,6 +1061,12 @@ if (_OPTIONS["PLATFORM"]=="arm64") then + } + end + ++if (_OPTIONS["PLATFORM"]=="riscv64") then ++ defines { ++ "PTR64=1", ++ } ++end ++ + if (_OPTIONS["PLATFORM"]=="mips64") then + defines { + "PTR64=1", +-- +2.17.1 + diff --git a/mame.spec b/mame.spec index d7e83bf..2524075 100644 --- a/mame.spec +++ b/mame.spec @@ -22,6 +22,8 @@ Source0:
https://github.com/mamedev/%{name}/releases/download/%{name}0%{b
Source1:
http://mamedev.org/releases/whatsnew_0%{baseversion}.txt
Patch0: %{name}-fortify.patch Patch1: %{name}-genie-systemlua.patch +Patch2: 0001-First-attempt-at-enabling-building-on-riscv64.patch +Patch3: 0002-PTR64-1-needs-to-be-defined-on-riscv64-otherwise-bui.patch # ppc64: #
https://github.com/mamedev/mame/issues/3157
#
https://bugzilla.redhat.com/show_bug.cgi?id=1541613
@@ -154,6 +156,8 @@ find \( -regex '.*\.\(c\|cpp\|fsh\|fx\|h\|hpp\|lua\|make\|map\|md\|txt\|vsh\|xml %patch0 -p1 -b .fortify %patch1 -p1 -b .systemlua +%patch2 -p1 +%patch3 -p1 # Create ini files cat > %{name}.ini << EOF
1
0
0
0
Architecture specific change in rpms/sympy.git
by githook-noreply@fedoraproject.org
28 Jun '18
28 Jun '18
The package rpms/sympy.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/sympy.git/commit/?id=16724183cde3eb…
. Change: -%ifnarch i686 Thanks. Full change: ============ commit 16724183cde3eb9920cf54aae0637abb993715cf Author: Jerry James <loganjerry(a)gmail.com> Date: Wed Jun 27 22:02:27 2018 -0600 Adapt to more changes in python 3.7. diff --git a/sympy-float.patch b/sympy-float.patch new file mode 100644 index 0000000..60130da --- /dev/null +++ b/sympy-float.patch @@ -0,0 +1,24 @@ +--- sympy-sympy-1.1.1/sympy/core/tests/test_sympify.py.orig 2017-07-26 16:46:51.000000000 -0600 ++++ sympy-sympy-1.1.1/sympy/core/tests/test_sympify.py 2018-06-27 08:19:06.815713759 -0600 +@@ -582,8 +582,6 @@ def test_numpy(): + assert equal(sympify(np.float32(1.123456)), Float(1.123456, precision=24)) + assert equal(sympify(np.float64(1.1234567891234)), + Float(1.1234567891234, precision=53)) +- assert equal(sympify(np.longdouble(1.123456789)), +- Float(1.123456789, precision=80)) + assert equal(sympify(np.complex64(1 + 2j)), S(1.0 + 2.0*I)) + assert equal(sympify(np.complex128(1 + 2j)), S(1.0 + 2.0*I)) + assert equal(sympify(np.longcomplex(1 + 2j)), S(1.0 + 2.0*I)) +@@ -594,12 +592,6 @@ def test_numpy(): + except AttributeError: #float96 does not exist on all platforms + pass + +- try: +- assert equal(sympify(np.float128(1.123456789123)), +- Float(1.123456789123, precision=80)) +- except AttributeError: #float128 does not exist on all platforms +- pass +- + + @XFAIL + def test_sympify_rational_numbers_set(): diff --git a/sympy-python3.patch b/sympy-python3.patch index 114291f..6146390 100644 --- a/sympy-python3.patch +++ b/sympy-python3.patch @@ -9,3 +9,180 @@ from sympy.core import (Add, Mul, Pow, Integer, Number, NumberSymbol,) from sympy.core.numbers import ImaginaryUnit +--- python3/sympy/physics/mechanics/linearize.py.orig 2017-07-26 16:46:12.000000000 -0600 ++++ python3/sympy/physics/mechanics/linearize.py 2018-06-27 18:50:27.377496377 -0600 +@@ -8,6 +8,7 @@ from sympy.utilities.iterables import fl + from sympy.physics.vector import dynamicsymbols + from sympy.physics.mechanics.functions import msubs + import collections ++import collections.abc + + + class Linearizer(object): +@@ -262,7 +263,7 @@ class Linearizer(object): + # Compose dict of operating conditions + if isinstance(op_point, dict): + op_point_dict = op_point +- elif isinstance(op_point, collections.Iterable): ++ elif isinstance(op_point, collections.abc.Iterable): + op_point_dict = {} + for op in op_point: + op_point_dict.update(op) +--- python3/sympy/physics/units/util.py.orig 2017-07-26 16:46:12.000000000 -0600 ++++ python3/sympy/physics/units/util.py 2018-06-27 18:48:35.345734211 -0600 +@@ -6,7 +6,7 @@ Several methods to simplify expressions + + from __future__ import division + +-import collections ++import collections.abc + + from sympy.physics.units.quantities import Quantity + from sympy import Add, Mul, Pow, Function, Rational, Tuple, sympify +@@ -110,7 +110,7 @@ def convert_to(expr, target_units): + 7.62950196312651e-20*gravitational_constant**(-0.5)*hbar**0.5*speed_of_light**0.5 + + """ +- if not isinstance(target_units, (collections.Iterable, Tuple)): ++ if not isinstance(target_units, (collections.abc.Iterable, Tuple)): + target_units = [target_units] + + if isinstance(expr, Add): +--- python3/sympy/printing/conventions.py.orig 2017-07-26 16:46:12.000000000 -0600 ++++ python3/sympy/printing/conventions.py 2018-06-27 18:49:32.873611416 -0600 +@@ -5,7 +5,7 @@ A few practical conventions common to al + from __future__ import print_function, division + + import re +-import collections ++import collections.abc + + + _name_with_digits_p = re.compile(r'^([a-zA-Z]+)([0-9]+)$') +@@ -77,7 +77,7 @@ def requires_partial(expr): + get the context of the expression. + """ + +- if not isinstance(expr.free_symbols, collections.Iterable): ++ if not isinstance(expr.free_symbols, collections.abc.Iterable): + return len(set(expr.variables)) > 1 + + return sum(not s.is_integer for s in expr.free_symbols) > 1 +--- python3/sympy/tensor/array/arrayop.py.orig 2017-07-26 16:46:12.000000000 -0600 ++++ python3/sympy/tensor/array/arrayop.py 2018-06-27 18:51:28.657367034 -0600 +@@ -1,6 +1,6 @@ + import itertools + +-import collections ++import collections.abc + + from sympy import S, Tuple, diff + +@@ -103,7 +103,7 @@ def tensorcontraction(array, *contractio + # Verify contraction_axes: + taken_dims = set([]) + for axes_group in contraction_axes: +- if not isinstance(axes_group, collections.Iterable): ++ if not isinstance(axes_group, collections.abc.Iterable): + raise ValueError("collections of contraction axes expected") + + dim = array.shape[axes_group[0]] +@@ -190,7 +190,7 @@ def derive_by_array(expr, dx): + + """ + from sympy.matrices import MatrixBase +- array_types = (collections.Iterable, MatrixBase, NDimArray) ++ array_types = (collections.abc.Iterable, MatrixBase, NDimArray) + + if isinstance(dx, array_types): + dx = ImmutableDenseNDimArray(dx) +--- python3/sympy/tensor/array/ndim_array.py.orig 2017-07-26 16:46:12.000000000 -0600 ++++ python3/sympy/tensor/array/ndim_array.py 2018-06-27 18:47:21.848896952 -0600 +@@ -1,5 +1,5 @@ + from __future__ import print_function, division +-import collections ++import collections.abc + + from sympy import Basic + +@@ -100,13 +100,13 @@ class NDimArray(object): + + def _setter_iterable_check(self, value): + from sympy.matrices.matrices import MatrixBase +- if isinstance(value, (collections.Iterable, MatrixBase, NDimArray)): ++ if isinstance(value, (collections.abc.Iterable, MatrixBase, NDimArray)): + raise NotImplementedError + + @classmethod + def _scan_iterable_shape(cls, iterable): + def f(pointer): +- if not isinstance(pointer, collections.Iterable): ++ if not isinstance(pointer, collections.abc.Iterable): + return [pointer], () + + result = [] +@@ -131,7 +131,7 @@ class NDimArray(object): + shape = iterable.shape + iterable = list(iterable) + # Construct N-dim array from an iterable (numpy arrays included): +- elif shape is None and isinstance(iterable, collections.Iterable): ++ elif shape is None and isinstance(iterable, collections.abc.Iterable): + iterable, shape = cls._scan_iterable_shape(iterable) + + # Construct N-dim array from a Matrix: +@@ -310,7 +310,7 @@ class NDimArray(object): + def __mul__(self, other): + from sympy.matrices.matrices import MatrixBase + +- if isinstance(other, (collections.Iterable,NDimArray, MatrixBase)): ++ if isinstance(other, (collections.abc.Iterable,NDimArray, MatrixBase)): + raise ValueError("scalar expected, use tensorproduct(...) for tensorial product") + other = sympify(other) + result_list = [i*other for i in self] +@@ -319,7 +319,7 @@ class NDimArray(object): + def __rmul__(self, other): + from sympy.matrices.matrices import MatrixBase + +- if isinstance(other, (collections.Iterable,NDimArray, MatrixBase)): ++ if isinstance(other, (collections.abc.Iterable,NDimArray, MatrixBase)): + raise ValueError("scalar expected, use tensorproduct(...) for tensorial product") + other = sympify(other) + result_list = [other*i for i in self] +@@ -328,7 +328,7 @@ class NDimArray(object): + def __div__(self, other): + from sympy.matrices.matrices import MatrixBase + +- if isinstance(other, (collections.Iterable,NDimArray, MatrixBase)): ++ if isinstance(other, (collections.abc.Iterable,NDimArray, MatrixBase)): + raise ValueError("scalar expected") + other = sympify(other) + result_list = [i/other for i in self] +--- python3/sympy/tensor/indexed.py.orig 2017-07-26 16:46:12.000000000 -0600 ++++ python3/sympy/tensor/indexed.py 2018-06-27 18:48:03.513804692 -0600 +@@ -107,7 +107,7 @@ matrix element ``M[i, j]`` as in the fol + + from __future__ import print_function, division + +-import collections ++import collections.abc + + from sympy.core.sympify import _sympify + from sympy.functions.special.tensor_functions import KroneckerDelta +@@ -153,7 +153,7 @@ class Indexed(Expr): + raise TypeError(filldedent(""" + Indexed expects string, Symbol, or IndexedBase as base.""")) + args = list(map(sympify, args)) +- if isinstance(base, (NDimArray, collections.Iterable, Tuple, MatrixBase)) and all([i.is_number for i in args]): ++ if isinstance(base, (NDimArray, collections.abc.Iterable, Tuple, MatrixBase)) and all([i.is_number for i in args]): + if len(args) == 1: + return base[args[0]] + else: +@@ -376,7 +376,7 @@ class IndexedBase(Expr, NotIterable): + pass + elif isinstance(label, (MatrixBase, NDimArray)): + return label +- elif isinstance(label, collections.Iterable): ++ elif isinstance(label, collections.abc.Iterable): + return _sympify(label) + else: + label = _sympify(label) diff --git a/sympy.spec b/sympy.spec index 7072e14..3650009 100644 --- a/sympy.spec +++ b/sympy.spec @@ -5,8 +5,10 @@ Summary: A Python library for symbolic mathematics License: BSD URL:
http://sympy.org/
Source0:
https://github.com/%{name}/%{name}/archive/%{name}-%{version}.tar.gz
+# Remove tests that fail on non-x86 architectures +Patch0: %{name}-float.patch # Adapt to changes in python 3.7 -Patch0: %{name}-python3.patch +Patch1: %{name}-python3.patch BuildArch: noarch @@ -103,6 +105,7 @@ HTML documentation for sympy. %prep %setup -q -c +%patch0 # If running on a 32-bit system, disable a test that requires 64-bit integers. %global maxpyint %(python3 -c 'import sys;print("%x" % sys.maxsize)') @@ -111,18 +114,12 @@ if [ "%{maxpyint}" = "7fffffff" ]; then %{sympydir}/sympy/polys/tests/test_rootoftools.py fi -# If not running on x86 with a floating point coprocessor, disable a test that -# requires 80-bit long doubles. -%ifnarch i686 -sed -i '585,586d' %{sympydir}/sympy/core/tests/test_sympify.py -%endif - # Allow building with a numpy that has a release candidate version number sed -i 's/StrictVersion/LooseVersion/g' %{sympydir}/sympy/external/importtools.py # Make a copy for building the python3 version cp -a %{sympydir} python3 -%patch0 +%patch1 # One test gets an extra warning with python3 sed -i '/Adaptive meshing could not be applied/d' \
1
0
0
0
← Newer
1
2
3
4
5
6
...
54
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Results per page:
10
25
50
100
200