From 3a29f1e8a7b638b58d661b147b3118ea14a50e74 Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 8 Dec 2025 00:42:50 +0300 Subject: [PATCH] add jami --- dev-qt/qmsetup/Manifest | 1 + dev-qt/qmsetup/qmsetup-9999.ebuild | 41 + dev-qt/qwindowkit/Manifest | 2 + dev-qt/qwindowkit/qwindowkit-1.4.0.ebuild | 43 + metadata/md5-cache/dev-qt/qmsetup-9999 | 13 + metadata/md5-cache/dev-qt/qwindowkit-1.4.0 | 14 + metadata/md5-cache/net-libs/dhtnet-9999 | 14 + metadata/md5-cache/net-libs/opendht-3.5.4 | 15 + metadata/md5-cache/net-libs/opendht-3.5.5 | 15 + metadata/md5-cache/net-libs/opendht-3.6.0 | 15 + .../md5-cache/net-libs/pjproject-2.15.1-r2 | 15 + metadata/md5-cache/net-libs/restinio-0.7.7 | 14 + .../net-voip/jami-client-qt-20251003.0 | 14 + .../md5-cache/net-voip/jami-client-qt-9999 | 14 + .../md5-cache/net-voip/jami-daemon-20250929 | 16 + metadata/md5-cache/net-voip/jami-daemon-9999 | 15 + net-libs/dhtnet/Manifest | 1 + net-libs/dhtnet/dhtnet-9999.ebuild | 62 + net-libs/opendht/Manifest | 6 + net-libs/opendht/opendht-3.5.4.ebuild | 76 + net-libs/opendht/opendht-3.5.5.ebuild | 76 + net-libs/opendht/opendht-3.6.0.ebuild | 77 + net-libs/pjproject/Manifest | 3 + .../files/sfl-pjproject-2.15.1.patch | 11990 ++++++++++++++++ net-libs/pjproject/pjproject-2.15.1-r2.ebuild | 145 + net-libs/restinio/Manifest | 2 + net-libs/restinio/restinio-0.7.7.ebuild | 55 + net-voip/jami-client-qt/Manifest | 8 + .../files/drop-qt-version-check.patch | 12 + net-voip/jami-client-qt/files/fix-link.patch | 14 + .../files/missing-cmake-include.patch | 12 + net-voip/jami-client-qt/files/qt-6.6.patch | 13 + .../files/unbundle-qwindowkit.patch | 19 + .../files/unbundle-qwindowkit2.patch | 20 + .../jami-client-qt-20251003.0.ebuild | 86 + .../jami-client-qt/jami-client-qt-9999.ebuild | 84 + net-voip/jami-daemon/Manifest | 7 + net-voip/jami-daemon/files/cmake.patch | 15 + net-voip/jami-daemon/files/ffmpeg-7.patch | 63 + net-voip/jami-daemon/files/ffmpeg-8.patch | 110 + net-voip/jami-daemon/files/fmt-12.patch | 32 + .../jami-daemon/jami-daemon-20250929.ebuild | 111 + net-voip/jami-daemon/jami-daemon-9999.ebuild | 111 + 43 files changed, 13471 insertions(+) create mode 100644 dev-qt/qmsetup/Manifest create mode 100644 dev-qt/qmsetup/qmsetup-9999.ebuild create mode 100644 dev-qt/qwindowkit/Manifest create mode 100644 dev-qt/qwindowkit/qwindowkit-1.4.0.ebuild create mode 100644 metadata/md5-cache/dev-qt/qmsetup-9999 create mode 100644 metadata/md5-cache/dev-qt/qwindowkit-1.4.0 create mode 100644 metadata/md5-cache/net-libs/dhtnet-9999 create mode 100644 metadata/md5-cache/net-libs/opendht-3.5.4 create mode 100644 metadata/md5-cache/net-libs/opendht-3.5.5 create mode 100644 metadata/md5-cache/net-libs/opendht-3.6.0 create mode 100644 metadata/md5-cache/net-libs/pjproject-2.15.1-r2 create mode 100644 metadata/md5-cache/net-libs/restinio-0.7.7 create mode 100644 metadata/md5-cache/net-voip/jami-client-qt-20251003.0 create mode 100644 metadata/md5-cache/net-voip/jami-client-qt-9999 create mode 100644 metadata/md5-cache/net-voip/jami-daemon-20250929 create mode 100644 metadata/md5-cache/net-voip/jami-daemon-9999 create mode 100644 net-libs/dhtnet/Manifest create mode 100644 net-libs/dhtnet/dhtnet-9999.ebuild create mode 100644 net-libs/opendht/Manifest create mode 100644 net-libs/opendht/opendht-3.5.4.ebuild create mode 100644 net-libs/opendht/opendht-3.5.5.ebuild create mode 100644 net-libs/opendht/opendht-3.6.0.ebuild create mode 100644 net-libs/pjproject/Manifest create mode 100644 net-libs/pjproject/files/sfl-pjproject-2.15.1.patch create mode 100644 net-libs/pjproject/pjproject-2.15.1-r2.ebuild create mode 100644 net-libs/restinio/Manifest create mode 100644 net-libs/restinio/restinio-0.7.7.ebuild create mode 100644 net-voip/jami-client-qt/Manifest create mode 100644 net-voip/jami-client-qt/files/drop-qt-version-check.patch create mode 100644 net-voip/jami-client-qt/files/fix-link.patch create mode 100644 net-voip/jami-client-qt/files/missing-cmake-include.patch create mode 100644 net-voip/jami-client-qt/files/qt-6.6.patch create mode 100644 net-voip/jami-client-qt/files/unbundle-qwindowkit.patch create mode 100644 net-voip/jami-client-qt/files/unbundle-qwindowkit2.patch create mode 100644 net-voip/jami-client-qt/jami-client-qt-20251003.0.ebuild create mode 100644 net-voip/jami-client-qt/jami-client-qt-9999.ebuild create mode 100644 net-voip/jami-daemon/Manifest create mode 100644 net-voip/jami-daemon/files/cmake.patch create mode 100644 net-voip/jami-daemon/files/ffmpeg-7.patch create mode 100644 net-voip/jami-daemon/files/ffmpeg-8.patch create mode 100644 net-voip/jami-daemon/files/fmt-12.patch create mode 100644 net-voip/jami-daemon/jami-daemon-20250929.ebuild create mode 100644 net-voip/jami-daemon/jami-daemon-9999.ebuild diff --git a/dev-qt/qmsetup/Manifest b/dev-qt/qmsetup/Manifest new file mode 100644 index 0000000..cece58e --- /dev/null +++ b/dev-qt/qmsetup/Manifest @@ -0,0 +1 @@ +EBUILD qmsetup-9999.ebuild 685 BLAKE2B d23f9bc4cb60e3d7ab576b7c0b7e89d50af1bee21d01e5141573cdc0026076a415fbe1d82b529ce2cb8d5ce3794db667697821d27e9a940b5cef07849cbae789 SHA512 b179f2a6dd3ac0da5c80191656852042753410c6e540b94fe088c594e7413dfb0d34ff68ac5864eb843c5bd2f46f59f007844ee8329829d0211b6e8e211dcae8 diff --git a/dev-qt/qmsetup/qmsetup-9999.ebuild b/dev-qt/qmsetup/qmsetup-9999.ebuild new file mode 100644 index 0000000..3b72d91 --- /dev/null +++ b/dev-qt/qmsetup/qmsetup-9999.ebuild @@ -0,0 +1,41 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="QMSetup is a set of CMake Modules and Basic Libraries for C/C++ projects" +HOMEPAGE="https://github.com/stdware/qmsetup" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/stdware/${PN}" +else + SRC_URI="https://github.com/stdware/qmsetup/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + KEYWORDS="~amd64 ~x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="" + +DEPEND=" +" + +RDEPEND=" + ${DEPEND} +" + +src_configure() { + local mycmakeargs=( + -DCMAKE_BUILD_TYPE=Release + ) + cmake_src_configure +} + +src_install() { + cmake_src_install +} diff --git a/dev-qt/qwindowkit/Manifest b/dev-qt/qwindowkit/Manifest new file mode 100644 index 0000000..091f78d --- /dev/null +++ b/dev-qt/qwindowkit/Manifest @@ -0,0 +1,2 @@ +DIST 1.4.0.tar.gz 1042791 BLAKE2B f71dbd95ffc2e07640ac06805aaa46e109b1d096cfb129e385da6fac5e26a8b6c33f24e242e6aaf9d8de4ec77380b4491d52f388fce9e7f2d6ee782f308c4f7b SHA512 35a1c5aa6ca8771196cba97c1c37704a3953b3892b4e309c8d637dfb78d5ff9af729e9273f80934a7231ab7fe813ed0433630f74077ae440ac0e84423382df86 +EBUILD qwindowkit-1.4.0.ebuild 720 BLAKE2B b9eabdc1cd6df9789c65449d019b8c2bad2f67037905205c1b4c68233ca8e27a70e4d80c2e8188147ce6e98ca3beff5a3e3e9421c6d827591faed72cf8bb199d SHA512 fe59195c4780bfa33ff06a94458cd4a3009c4195dfe144eab74ea9fdb09b7001e27bdb62c3ecbc12588b131fbf9a1d3579e3786c45d94564b8ce1f499ef10097 diff --git a/dev-qt/qwindowkit/qwindowkit-1.4.0.ebuild b/dev-qt/qwindowkit/qwindowkit-1.4.0.ebuild new file mode 100644 index 0000000..bd95861 --- /dev/null +++ b/dev-qt/qwindowkit/qwindowkit-1.4.0.ebuild @@ -0,0 +1,43 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="RESTinio is a C++17 library that gives you an embedded HTTP/Websocket server" +HOMEPAGE="https://stiffstream.com" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/stdware/qwindowkit/${PN}" +else + SRC_URI="https://github.com/stdware/qwindowkit/archive/refs/tags/${PV}.tar.gz" + KEYWORDS="amd64 x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="" + +DEPEND=" + dev-qt/qmsetup +" + +RDEPEND=" + ${DEPEND} +" + +src_configure() { + local mycmakeargs=( + -DQWINDOWKIT_BUILD_QUICK=ON + -DCMAKE_BUILD_TYPE=Release + ) + cmake_src_configure +} + +src_install() { + cmake_src_install +} diff --git a/metadata/md5-cache/dev-qt/qmsetup-9999 b/metadata/md5-cache/dev-qt/qmsetup-9999 new file mode 100644 index 0000000..018d7d0 --- /dev/null +++ b/metadata/md5-cache/dev-qt/qmsetup-9999 @@ -0,0 +1,13 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 >=dev-vcs/git-1.8.2.1[curl] +DEFINED_PHASES=compile configure install prepare test unpack +DEPEND= +DESCRIPTION=QMSetup is a set of CMake Modules and Basic Libraries for C/C++ projects +EAPI=8 +HOMEPAGE=https://github.com/stdware/qmsetup +INHERIT=cmake git-r3 +LICENSE=GPL-3 +PROPERTIES=live +RDEPEND= +SLOT=0 +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 git-r3 875eb471682d3e1f18da124be97dcc81 +_md5_=0b87d2fe4405635376b64706d2cb8c0a diff --git a/metadata/md5-cache/dev-qt/qwindowkit-1.4.0 b/metadata/md5-cache/dev-qt/qwindowkit-1.4.0 new file mode 100644 index 0000000..ec48601 --- /dev/null +++ b/metadata/md5-cache/dev-qt/qwindowkit-1.4.0 @@ -0,0 +1,14 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 +DEFINED_PHASES=compile configure install prepare test +DEPEND=dev-qt/qmsetup +DESCRIPTION=RESTinio is a C++17 library that gives you an embedded HTTP/Websocket server +EAPI=8 +HOMEPAGE=https://stiffstream.com +INHERIT=cmake +KEYWORDS=amd64 x86 +LICENSE=GPL-3 +RDEPEND=dev-qt/qmsetup +SLOT=0 +SRC_URI=https://github.com/stdware/qwindowkit/archive/refs/tags/1.4.0.tar.gz +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 +_md5_=dcfa6e6b20b62712821f466bb031a947 diff --git a/metadata/md5-cache/net-libs/dhtnet-9999 b/metadata/md5-cache/net-libs/dhtnet-9999 new file mode 100644 index 0000000..ad0c92f --- /dev/null +++ b/metadata/md5-cache/net-libs/dhtnet-9999 @@ -0,0 +1,14 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 >=dev-vcs/git-1.8.2.1[curl] +DEFINED_PHASES=compile configure install prepare test unpack +DEPEND=dev-cpp/yaml-cpp net-libs/libnatpmp net-libs/libupnp app-crypt/argon2 net-libs/opendht net-libs/pjproject[sfl(+),speex(+),gsm(+),portaudio(+)] dev-libs/libfmt dev-cpp/asio dev-cpp/msgpack-cxx || ( net-libs/gnutls dev-libs/nettle ) tools? ( sys-libs/readline:0 ) +DESCRIPTION=The DHTNet library is designed to establish secure peer-to-peer connections using public-key authentication +EAPI=8 +HOMEPAGE=https://github.com/savoirfairelinux/dhtnet +INHERIT=cmake git-r3 +IUSE=tools +LICENSE=GPL-3 +PROPERTIES=live +RDEPEND=dev-cpp/yaml-cpp net-libs/libnatpmp net-libs/libupnp app-crypt/argon2 net-libs/opendht net-libs/pjproject[sfl(+),speex(+),gsm(+),portaudio(+)] dev-libs/libfmt dev-cpp/asio dev-cpp/msgpack-cxx || ( net-libs/gnutls dev-libs/nettle ) tools? ( sys-libs/readline:0 ) +SLOT=0 +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 git-r3 875eb471682d3e1f18da124be97dcc81 +_md5_=58c5479f422d3ab945151c52af4ecd3c diff --git a/metadata/md5-cache/net-libs/opendht-3.5.4 b/metadata/md5-cache/net-libs/opendht-3.5.4 new file mode 100644 index 0000000..3b93e41 --- /dev/null +++ b/metadata/md5-cache/net-libs/opendht-3.5.4 @@ -0,0 +1,15 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 +DEFINED_PHASES=compile configure install prepare test +DEPEND=app-crypt/argon2 dev-libs/libfmt dev-util/cppunit dev-cpp/asio dev-cpp/msgpack-cxx net-libs/restinio || ( net-libs/gnutls dev-libs/nettle ) python? ( dev-python/cython[python_targets_python3_13t(-)?,python_targets_python3_11(-)?,python_targets_python3_12(-)?,python_targets_python3_13(-)?] ) tools? ( sys-libs/readline:0 ) proxy-openssl? ( dev-libs/openssl:= ) doc? ( app-text/doxygen ) +DESCRIPTION=A lightweight C++11 Distributed Hash Table implementation +EAPI=8 +HOMEPAGE=https://github.com/savoirfairelinux/opendht +INHERIT=cmake python-r1 +IUSE=doc +dht-index +http-client +peer-discovery +proxy-client +proxy-server +proxy-server-identity +proxy-openssl +push-notifications python systemd +tools python_targets_python3_13t python_targets_python3_11 python_targets_python3_12 python_targets_python3_13 +KEYWORDS=~amd64 ~x86 +LICENSE=GPL-3 +RDEPEND=app-crypt/argon2 dev-libs/libfmt dev-util/cppunit dev-cpp/asio dev-cpp/msgpack-cxx net-libs/restinio || ( net-libs/gnutls dev-libs/nettle ) python? ( dev-python/cython[python_targets_python3_13t(-)?,python_targets_python3_11(-)?,python_targets_python3_12(-)?,python_targets_python3_13(-)?] ) tools? ( sys-libs/readline:0 ) proxy-openssl? ( dev-libs/openssl:= ) doc? ( app-text/doxygen ) python_targets_python3_13t? ( dev-lang/python:3.13t ) python_targets_python3_11? ( dev-lang/python:3.11 ) python_targets_python3_12? ( dev-lang/python:3.12 ) python_targets_python3_13? ( dev-lang/python:3.13 ) +SLOT=0 +SRC_URI=https://github.com/savoirfairelinux/opendht/archive/refs/tags/v3.5.4.tar.gz -> opendht-3.5.4.tar.gz +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 out-of-source-utils dbf9e34ee8964084651e25907fa8f52c multibuild 4650a65187015567b4e041bb9bfdb364 python-utils-r1 dbb8c4d794033ad7e7221eaf567a6c90 python-r1 0e15b2ab9cfc87d7474678201d6bca38 +_md5_=d1241acf7d9b6f39a45eab5175a60808 diff --git a/metadata/md5-cache/net-libs/opendht-3.5.5 b/metadata/md5-cache/net-libs/opendht-3.5.5 new file mode 100644 index 0000000..939bdc9 --- /dev/null +++ b/metadata/md5-cache/net-libs/opendht-3.5.5 @@ -0,0 +1,15 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 +DEFINED_PHASES=compile configure install prepare test +DEPEND=app-crypt/argon2 dev-libs/libfmt dev-util/cppunit dev-cpp/asio dev-cpp/msgpack-cxx net-libs/restinio || ( net-libs/gnutls dev-libs/nettle ) python? ( dev-python/cython[python_targets_python3_13t(-)?,python_targets_python3_11(-)?,python_targets_python3_12(-)?,python_targets_python3_13(-)?] ) tools? ( sys-libs/readline:0 ) proxy-openssl? ( dev-libs/openssl:= ) doc? ( app-text/doxygen ) +DESCRIPTION=A lightweight C++11 Distributed Hash Table implementation +EAPI=8 +HOMEPAGE=https://github.com/savoirfairelinux/opendht +INHERIT=cmake python-r1 +IUSE=doc +dht-index +http-client +peer-discovery +proxy-client +proxy-server +proxy-server-identity +proxy-openssl +push-notifications python systemd +tools python_targets_python3_13t python_targets_python3_11 python_targets_python3_12 python_targets_python3_13 +KEYWORDS=~amd64 ~x86 +LICENSE=GPL-3 +RDEPEND=app-crypt/argon2 dev-libs/libfmt dev-util/cppunit dev-cpp/asio dev-cpp/msgpack-cxx net-libs/restinio || ( net-libs/gnutls dev-libs/nettle ) python? ( dev-python/cython[python_targets_python3_13t(-)?,python_targets_python3_11(-)?,python_targets_python3_12(-)?,python_targets_python3_13(-)?] ) tools? ( sys-libs/readline:0 ) proxy-openssl? ( dev-libs/openssl:= ) doc? ( app-text/doxygen ) python_targets_python3_13t? ( dev-lang/python:3.13t ) python_targets_python3_11? ( dev-lang/python:3.11 ) python_targets_python3_12? ( dev-lang/python:3.12 ) python_targets_python3_13? ( dev-lang/python:3.13 ) +SLOT=0 +SRC_URI=https://github.com/savoirfairelinux/opendht/archive/refs/tags/v3.5.5.tar.gz -> opendht-3.5.5.tar.gz +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 out-of-source-utils dbf9e34ee8964084651e25907fa8f52c multibuild 4650a65187015567b4e041bb9bfdb364 python-utils-r1 dbb8c4d794033ad7e7221eaf567a6c90 python-r1 0e15b2ab9cfc87d7474678201d6bca38 +_md5_=d1241acf7d9b6f39a45eab5175a60808 diff --git a/metadata/md5-cache/net-libs/opendht-3.6.0 b/metadata/md5-cache/net-libs/opendht-3.6.0 new file mode 100644 index 0000000..5e3a3de --- /dev/null +++ b/metadata/md5-cache/net-libs/opendht-3.6.0 @@ -0,0 +1,15 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 +DEFINED_PHASES=compile configure install prepare test +DEPEND=app-crypt/argon2 dev-libs/libfmt dev-util/cppunit dev-cpp/asio dev-cpp/msgpack-cxx net-libs/restinio dev-cpp/simdutf || ( net-libs/gnutls dev-libs/nettle ) python? ( dev-python/cython[python_targets_python3_13t(-)?,python_targets_python3_11(-)?,python_targets_python3_12(-)?,python_targets_python3_13(-)?] ) tools? ( sys-libs/readline:0 ) proxy-openssl? ( dev-libs/openssl:= ) doc? ( app-text/doxygen ) +DESCRIPTION=A lightweight C++11 Distributed Hash Table implementation +EAPI=8 +HOMEPAGE=https://github.com/savoirfairelinux/opendht +INHERIT=cmake python-r1 +IUSE=doc +dht-index +http-client +peer-discovery +proxy-client +proxy-server +proxy-server-identity +proxy-openssl +push-notifications python systemd +tools python_targets_python3_13t python_targets_python3_11 python_targets_python3_12 python_targets_python3_13 +KEYWORDS=~amd64 ~x86 +LICENSE=GPL-3 +RDEPEND=app-crypt/argon2 dev-libs/libfmt dev-util/cppunit dev-cpp/asio dev-cpp/msgpack-cxx net-libs/restinio dev-cpp/simdutf || ( net-libs/gnutls dev-libs/nettle ) python? ( dev-python/cython[python_targets_python3_13t(-)?,python_targets_python3_11(-)?,python_targets_python3_12(-)?,python_targets_python3_13(-)?] ) tools? ( sys-libs/readline:0 ) proxy-openssl? ( dev-libs/openssl:= ) doc? ( app-text/doxygen ) python_targets_python3_13t? ( dev-lang/python:3.13t ) python_targets_python3_11? ( dev-lang/python:3.11 ) python_targets_python3_12? ( dev-lang/python:3.12 ) python_targets_python3_13? ( dev-lang/python:3.13 ) +SLOT=0 +SRC_URI=https://github.com/savoirfairelinux/opendht/archive/refs/tags/v3.6.0.tar.gz -> opendht-3.6.0.tar.gz +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 out-of-source-utils dbf9e34ee8964084651e25907fa8f52c multibuild 4650a65187015567b4e041bb9bfdb364 python-utils-r1 dbb8c4d794033ad7e7221eaf567a6c90 python-r1 0e15b2ab9cfc87d7474678201d6bca38 +_md5_=ddc0e73282ba49b0f52575b9ae60c6a0 diff --git a/metadata/md5-cache/net-libs/pjproject-2.15.1-r2 b/metadata/md5-cache/net-libs/pjproject-2.15.1-r2 new file mode 100644 index 0000000..97ab6c5 --- /dev/null +++ b/metadata/md5-cache/net-libs/pjproject-2.15.1-r2 @@ -0,0 +1,15 @@ +BDEPEND=virtual/pkgconfig sys-devel/gnuconfig >=app-portage/elt-patches-20250306 || ( >=dev-build/automake-1.18:1.18 >=dev-build/automake-1.17-r1:1.17 ) || ( >=dev-build/autoconf-2.72-r1:2.72 ) >=dev-build/libtool-2.4.7-r3 +DEFINED_PHASES=configure install prepare +DEPEND=sys-apps/util-linux alsa? ( media-libs/alsa-lib ) amr? ( media-libs/opencore-amr ) ffmpeg? ( media-video/ffmpeg:= ) g729? ( media-libs/bcg729 ) gsm? ( media-sound/gsm ) ilbc? ( media-libs/libilbc ) libyuv? ( media-libs/libyuv:= ) openh264? ( media-libs/openh264 ) opus? ( media-libs/opus ) portaudio? ( media-libs/portaudio ) resample? ( media-libs/libsamplerate ) sdl? ( media-libs/libsdl2 ) speex? ( media-libs/speex media-libs/speexdsp ) srtp? ( >=net-libs/libsrtp-2.3.0:= ) ssl? ( dev-libs/openssl:0= ) vpx? ( media-libs/libvpx:= ) +DESCRIPTION=Open source SIP, Media, and NAT Traversal Library +EAPI=8 +HOMEPAGE=https://github.com/pjsip/pjproject https://www.pjsip.org/ +INHERIT=autotools flag-o-matic toolchain-funcs +IUSE=amr debug epoll examples opus resample silk srtp ssl static-libs webrtc sfl g711 g722 g7221 gsm ilbc speex l16 g729 sdl ffmpeg v4l2 openh264 libyuv vpx alsa portaudio +KEYWORDS=~amd64 ~arm ~arm64 x86 +LICENSE=GPL-2 +RDEPEND=sys-apps/util-linux alsa? ( media-libs/alsa-lib ) amr? ( media-libs/opencore-amr ) ffmpeg? ( media-video/ffmpeg:= ) g729? ( media-libs/bcg729 ) gsm? ( media-sound/gsm ) ilbc? ( media-libs/libilbc ) libyuv? ( media-libs/libyuv:= ) openh264? ( media-libs/openh264 ) opus? ( media-libs/opus ) portaudio? ( media-libs/portaudio ) resample? ( media-libs/libsamplerate ) sdl? ( media-libs/libsdl2 ) speex? ( media-libs/speex media-libs/speexdsp ) srtp? ( >=net-libs/libsrtp-2.3.0:= ) ssl? ( dev-libs/openssl:0= ) vpx? ( media-libs/libvpx:= ) +SLOT=0/2.15.1 +SRC_URI=https://github.com/pjsip/pjproject/archive/refs/tags/2.15.1.tar.gz -> pjproject-2.15.1.tar.gz +_eclasses_=gnuconfig ddeb9f8caff1b5f71a09c75b7534df79 toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db libtool c81bd096be5f4c82f4e8f156ef112402 autotools 955b29ccd82c1df4755e5f37748d2fa6 flag-o-matic a7afe42e95fb46ce9691605acfb24672 +_md5_=6679aa653b7f34f73ba4817b0476ed45 diff --git a/metadata/md5-cache/net-libs/restinio-0.7.7 b/metadata/md5-cache/net-libs/restinio-0.7.7 new file mode 100644 index 0000000..8aa8eda --- /dev/null +++ b/metadata/md5-cache/net-libs/restinio-0.7.7 @@ -0,0 +1,14 @@ +BDEPEND=app-alternatives/ninja >=dev-build/cmake-3.28.5 +DEFINED_PHASES=compile configure install prepare test +DEPEND=dev-libs/libfmt dev-cpp/asio net-libs/llhttp dev-cpp/expected-lite dev-cpp/catch +DESCRIPTION=RESTinio is a C++17 library that gives you an embedded HTTP/Websocket server +EAPI=8 +HOMEPAGE=https://stiffstream.com +INHERIT=cmake +KEYWORDS=~amd64 ~x86 +LICENSE=GPL-3 +RDEPEND=dev-libs/libfmt dev-cpp/asio net-libs/llhttp dev-cpp/expected-lite dev-cpp/catch +SLOT=0 +SRC_URI=https://github.com/Stiffstream/restinio/archive/refs/tags/v0.7.7.tar.gz -> restinio-0.7.7.tar.gz +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 +_md5_=d600d685a4b211cc43d77e336aa00750 diff --git a/metadata/md5-cache/net-voip/jami-client-qt-20251003.0 b/metadata/md5-cache/net-voip/jami-client-qt-20251003.0 new file mode 100644 index 0000000..d7d9aa1 --- /dev/null +++ b/metadata/md5-cache/net-voip/jami-client-qt-20251003.0 @@ -0,0 +1,14 @@ +BDEPEND=doc? ( app-text/doxygen ) app-alternatives/ninja >=dev-build/cmake-3.28.5 +DEFINED_PHASES=compile configure install prepare test +DEPEND=net-voip/jami-daemon net-libs/libnma x11-libs/libnotify media-gfx/qrencode dev-libs/libayatana-appindicator dev-qt/qtbase dev-qt/qtdeclarative dev-qt/qtgraphicaleffects dev-qt/qtmultimedia[qml(+)] dev-qt/qtnetworkauth dev-qt/qtsvg dev-qt/qttools dev-qt/qtwebengine[qml(+)] dev-qt/qt5compat dev-qt/qtpositioning dev-qt/qtwebsockets[qml(+)] dev-qt/qwindowkit media-libs/zxing-cpp media-libs/zint app-text/htmltidy app-text/hunspell +DESCRIPTION=Jami clent QT +EAPI=8 +HOMEPAGE=https://git.jami.net/savoirfairelinux/jami-client-qt +INHERIT=cmake flag-o-matic +IUSE=doc +KEYWORDS=~amd64 ~x86 +LICENSE=GPL-3 +RDEPEND=net-voip/jami-daemon net-libs/libnma x11-libs/libnotify media-gfx/qrencode dev-libs/libayatana-appindicator dev-qt/qtbase dev-qt/qtdeclarative dev-qt/qtgraphicaleffects dev-qt/qtmultimedia[qml(+)] dev-qt/qtnetworkauth dev-qt/qtsvg dev-qt/qttools dev-qt/qtwebengine[qml(+)] dev-qt/qt5compat dev-qt/qtpositioning dev-qt/qtwebsockets[qml(+)] dev-qt/qwindowkit media-libs/zxing-cpp media-libs/zint app-text/htmltidy app-text/hunspell +SLOT=0 +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 +_md5_=0836da77f87c6f4690d9e848bd1c3789 diff --git a/metadata/md5-cache/net-voip/jami-client-qt-9999 b/metadata/md5-cache/net-voip/jami-client-qt-9999 new file mode 100644 index 0000000..77259f8 --- /dev/null +++ b/metadata/md5-cache/net-voip/jami-client-qt-9999 @@ -0,0 +1,14 @@ +BDEPEND=doc? ( app-text/doxygen ) app-alternatives/ninja >=dev-build/cmake-3.28.5 >=dev-vcs/git-1.8.2.1[curl] +DEFINED_PHASES=compile configure install prepare test unpack +DEPEND=net-voip/jami-daemon net-libs/libnma x11-libs/libnotify media-gfx/qrencode dev-libs/libayatana-appindicator dev-qt/qtbase dev-qt/qtdeclarative dev-qt/qtgraphicaleffects dev-qt/qtmultimedia[qml(+)] dev-qt/qtnetworkauth dev-qt/qtsvg dev-qt/qttools dev-qt/qtwebengine[qml(+)] dev-qt/qt5compat dev-qt/qtpositioning dev-qt/qtwebsockets[qml(+)] dev-qt/qwindowkit media-libs/zxing-cpp media-libs/zint app-text/htmltidy app-text/hunspell +DESCRIPTION=Jami clent QT +EAPI=8 +HOMEPAGE=https://git.jami.net/savoirfairelinux/jami-client-qt +INHERIT=cmake flag-o-matic git-r3 +IUSE=doc +LICENSE=GPL-3 +PROPERTIES=live +RDEPEND=net-voip/jami-daemon net-libs/libnma x11-libs/libnotify media-gfx/qrencode dev-libs/libayatana-appindicator dev-qt/qtbase dev-qt/qtdeclarative dev-qt/qtgraphicaleffects dev-qt/qtmultimedia[qml(+)] dev-qt/qtnetworkauth dev-qt/qtsvg dev-qt/qttools dev-qt/qtwebengine[qml(+)] dev-qt/qt5compat dev-qt/qtpositioning dev-qt/qtwebsockets[qml(+)] dev-qt/qwindowkit media-libs/zxing-cpp media-libs/zint app-text/htmltidy app-text/hunspell +SLOT=0 +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b xdg-utils 42869b3c8d86a70ef3cf75165a395e09 cmake 22e4e58d68692975dc74424dc9b12fb7 git-r3 875eb471682d3e1f18da124be97dcc81 +_md5_=12dcfabfb0871e5848b839296c9dcd8b diff --git a/metadata/md5-cache/net-voip/jami-daemon-20250929 b/metadata/md5-cache/net-voip/jami-daemon-20250929 new file mode 100644 index 0000000..733e9ea --- /dev/null +++ b/metadata/md5-cache/net-voip/jami-daemon-20250929 @@ -0,0 +1,16 @@ +BDEPEND=>=dev-build/meson-1.2.3 app-alternatives/ninja dev-build/meson-format-array +DEFINED_PHASES=compile configure install prepare test +DEPEND=>=dev-cpp/yaml-cpp-0.5.3 >=dev-libs/boost-1.61.0 >=dev-libs/crypto++-5.6.5 >=dev-libs/jsoncpp-1.7.2 >=media-video/ffmpeg-3.4[gsm?,libilbc?,opus?,speex?,v4l,vaapi?,vdpau?,vpx?,x264?,zlib] libilbc? ( media-libs/libilbc ) speex? ( >=media-libs/speex-1.2.0 ) speexdsp? ( >=media-libs/speexdsp-1.2_rc3 ) >=net-libs/gnutls-3.4.14 >=net-libs/opendht-1.10.1 >=sys-libs/zlib-1.2.8 media-libs/libva dev-libs/libsecp256k1 net-libs/restinio net-libs/dhtnet net-libs/http-parser dev-libs/libgit2 dev-cpp/sdbus-c++[tools(+)] <=media-libs/webrtc-audio-processing-1.0.0 dev-libs/msgpack alsa? ( media-libs/alsa-lib ) jack? ( virtual/jack ) portaudio? ( >=media-libs/portaudio-19_pre20140130 ) pulseaudio? ( media-libs/libpulse ) dbus? ( dev-libs/dbus-c++ ) sdes? ( >=dev-libs/libpcre-8.40 ) video? ( virtual/libudev ) nat-pmp? ( net-libs/libnatpmp ) pipewire? ( media-video/pipewire ) doc? ( graph? ( app-doc/doxygen[dot] ) !graph? ( app-doc/doxygen ) ) +DESCRIPTION=Jami (formerly Ring) daemon +EAPI=8 +HOMEPAGE=https://jami.net/ +INHERIT=meson +IUSE=+alsa +dbus doc graph +gsm +hwaccel ipv6 jack -libav +libilbc +nat-pmp +opus pipewire portaudio pulseaudio +sdes +speex +speexdsp +upnp +vaapi vdpau +video +vpx +x264 +KEYWORDS=~amd64 +LICENSE=GPL-3 +RDEPEND=>=dev-cpp/yaml-cpp-0.5.3 >=dev-libs/boost-1.61.0 >=dev-libs/crypto++-5.6.5 >=dev-libs/jsoncpp-1.7.2 >=media-video/ffmpeg-3.4[gsm?,libilbc?,opus?,speex?,v4l,vaapi?,vdpau?,vpx?,x264?,zlib] libilbc? ( media-libs/libilbc ) speex? ( >=media-libs/speex-1.2.0 ) speexdsp? ( >=media-libs/speexdsp-1.2_rc3 ) >=net-libs/gnutls-3.4.14 >=net-libs/opendht-1.10.1 >=sys-libs/zlib-1.2.8 media-libs/libva dev-libs/libsecp256k1 net-libs/restinio net-libs/dhtnet net-libs/http-parser dev-libs/libgit2 dev-cpp/sdbus-c++[tools(+)] <=media-libs/webrtc-audio-processing-1.0.0 dev-libs/msgpack alsa? ( media-libs/alsa-lib ) jack? ( virtual/jack ) portaudio? ( >=media-libs/portaudio-19_pre20140130 ) pulseaudio? ( media-libs/libpulse ) dbus? ( dev-libs/dbus-c++ ) sdes? ( >=dev-libs/libpcre-8.40 ) video? ( virtual/libudev ) nat-pmp? ( net-libs/libnatpmp ) pipewire? ( media-video/pipewire ) +REQUIRED_USE=dbus? ( sdes ) graph? ( doc ) hwaccel? ( video ) vaapi? ( hwaccel ) ?? ( dbus ) +SLOT=0 +SRC_URI=https://git.jami.net/savoirfairelinux/jami-daemon +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b python-utils-r1 dbb8c4d794033ad7e7221eaf567a6c90 meson 1a27c82364f611e149966d2c47cbb083 +_md5_=4a71a38371410ca8d91951569e659165 diff --git a/metadata/md5-cache/net-voip/jami-daemon-9999 b/metadata/md5-cache/net-voip/jami-daemon-9999 new file mode 100644 index 0000000..aa8cd19 --- /dev/null +++ b/metadata/md5-cache/net-voip/jami-daemon-9999 @@ -0,0 +1,15 @@ +BDEPEND=>=dev-build/meson-1.2.3 app-alternatives/ninja dev-build/meson-format-array >=dev-vcs/git-1.8.2.1[curl] +DEFINED_PHASES=compile configure install prepare test unpack +DEPEND=>=dev-cpp/yaml-cpp-0.5.3 >=dev-libs/boost-1.61.0 >=dev-libs/crypto++-5.6.5 >=dev-libs/jsoncpp-1.7.2 >=media-video/ffmpeg-3.4[gsm?,libilbc?,opus?,speex?,v4l,vaapi?,vdpau?,vpx?,x264?,zlib] libilbc? ( media-libs/libilbc ) speex? ( >=media-libs/speex-1.2.0 ) speexdsp? ( >=media-libs/speexdsp-1.2_rc3 ) >=net-libs/gnutls-3.4.14 >=net-libs/opendht-1.10.1 >=sys-libs/zlib-1.2.8 media-libs/libva dev-libs/libsecp256k1 net-libs/restinio net-libs/dhtnet net-libs/http-parser dev-libs/libgit2 dev-cpp/sdbus-c++[tools(+)] <=media-libs/webrtc-audio-processing-1.0.0 dev-libs/msgpack alsa? ( media-libs/alsa-lib ) jack? ( virtual/jack ) portaudio? ( >=media-libs/portaudio-19_pre20140130 ) pulseaudio? ( media-libs/libpulse ) dbus? ( dev-libs/dbus-c++ ) sdes? ( >=dev-libs/libpcre-8.40 ) video? ( virtual/libudev ) nat-pmp? ( net-libs/libnatpmp ) pipewire? ( media-video/pipewire ) doc? ( graph? ( app-doc/doxygen[dot] ) !graph? ( app-doc/doxygen ) ) +DESCRIPTION=Jami (formerly Ring) daemon +EAPI=8 +HOMEPAGE=https://jami.net/ +INHERIT=meson git-r3 +IUSE=+alsa +dbus doc graph +gsm +hwaccel ipv6 jack -libav +libilbc +nat-pmp +opus pipewire portaudio pulseaudio +sdes +speex +speexdsp +upnp +vaapi vdpau +video +vpx +x264 +LICENSE=GPL-3 +PROPERTIES=live +RDEPEND=>=dev-cpp/yaml-cpp-0.5.3 >=dev-libs/boost-1.61.0 >=dev-libs/crypto++-5.6.5 >=dev-libs/jsoncpp-1.7.2 >=media-video/ffmpeg-3.4[gsm?,libilbc?,opus?,speex?,v4l,vaapi?,vdpau?,vpx?,x264?,zlib] libilbc? ( media-libs/libilbc ) speex? ( >=media-libs/speex-1.2.0 ) speexdsp? ( >=media-libs/speexdsp-1.2_rc3 ) >=net-libs/gnutls-3.4.14 >=net-libs/opendht-1.10.1 >=sys-libs/zlib-1.2.8 media-libs/libva dev-libs/libsecp256k1 net-libs/restinio net-libs/dhtnet net-libs/http-parser dev-libs/libgit2 dev-cpp/sdbus-c++[tools(+)] <=media-libs/webrtc-audio-processing-1.0.0 dev-libs/msgpack alsa? ( media-libs/alsa-lib ) jack? ( virtual/jack ) portaudio? ( >=media-libs/portaudio-19_pre20140130 ) pulseaudio? ( media-libs/libpulse ) dbus? ( dev-libs/dbus-c++ ) sdes? ( >=dev-libs/libpcre-8.40 ) video? ( virtual/libudev ) nat-pmp? ( net-libs/libnatpmp ) pipewire? ( media-video/pipewire ) +REQUIRED_USE=dbus? ( sdes ) graph? ( doc ) hwaccel? ( video ) vaapi? ( hwaccel ) ?? ( dbus ) +SLOT=0 +_eclasses_=toolchain-funcs 98d9f464d912ae6b7316fb8a3721f5db flag-o-matic a7afe42e95fb46ce9691605acfb24672 multiprocessing 1e32df7deee68372153dca65f4a7c21f ninja-utils 3a59a39e97af0f7c03f49cf3c22f262b python-utils-r1 dbb8c4d794033ad7e7221eaf567a6c90 meson 1a27c82364f611e149966d2c47cbb083 git-r3 875eb471682d3e1f18da124be97dcc81 +_md5_=4a71a38371410ca8d91951569e659165 diff --git a/net-libs/dhtnet/Manifest b/net-libs/dhtnet/Manifest new file mode 100644 index 0000000..297c0ac --- /dev/null +++ b/net-libs/dhtnet/Manifest @@ -0,0 +1 @@ +EBUILD dhtnet-9999.ebuild 1241 BLAKE2B 927e694104a1fa3ce86b8385c88b678d040477df2e1ddd0333cba14de305238e737a0ba4ff18c1f97b4a778c98873a78fed60c7af9831aef3117f141a4ed4d4b SHA512 36d832b12e36a42219c8fb5cf9ccf25c8bbc0ee6d47a7e7563c63f068b13e4e7ef52b57a734167fe80061755c62174ae52edfb6073d4a2d600ad22ef92c4c318 diff --git a/net-libs/dhtnet/dhtnet-9999.ebuild b/net-libs/dhtnet/dhtnet-9999.ebuild new file mode 100644 index 0000000..8e1de11 --- /dev/null +++ b/net-libs/dhtnet/dhtnet-9999.ebuild @@ -0,0 +1,62 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake + +DESCRIPTION="The DHTNet library is designed to establish secure peer-to-peer connections using public-key authentication" +HOMEPAGE="https://github.com/savoirfairelinux/dhtnet" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/savoirfairelinux/${PN}" + EGIT_COMMIT="6c5ee3a21556d668d047cdedb5c4b746c3c6bdb2" +else + SRC_URI="https://github.com/savoirfairelinux/dhtnet/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + KEYWORDS="~amd64 ~x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="tools" + +DEPEND=" + dev-cpp/yaml-cpp + net-libs/libnatpmp + net-libs/libupnp + app-crypt/argon2 + net-libs/opendht + net-libs/pjproject[sfl(+),speex(+),gsm(+),portaudio(+)] + dev-libs/libfmt + dev-cpp/asio + dev-cpp/msgpack-cxx + || ( + net-libs/gnutls + dev-libs/nettle + ) + tools? ( sys-libs/readline:0 ) +" +RDEPEND=" + ${DEPEND} +" + +src_configure() { + local mycmakeargs=( + -DDHTNET_PUPNP=ON + -DDHTNET_NATPMP=ON + -DBUILD_TOOLS=$(usex tools) + -DBUILD_BENCHMARKS=OFF + -DBUILD_DEPENDENCIES=OFF + -DDNC_SYSTEMD=OFF + -DBUILD_EXAMPLE=OFF + -DBUILD_TESTING=OFF + ) + cmake_src_configure +} + +src_install() { + cmake_src_install +} diff --git a/net-libs/opendht/Manifest b/net-libs/opendht/Manifest new file mode 100644 index 0000000..e02277d --- /dev/null +++ b/net-libs/opendht/Manifest @@ -0,0 +1,6 @@ +DIST opendht-3.5.4.tar.gz 532306 BLAKE2B 2dd34abe704e6cf0c751474243d033de7b205ae0f598094e542b94c32944dedc83cdb34d22d19561af38fb01a426af91b8303a45855c3c1106809530b04f1600 SHA512 25d11e8bbadc844e449e2baccd38ae55e2b0a3c165b889c9a296ed39eb4c263a177a9171ede063cd493d52fea4ccedf3c2b012dad64af1c8bb96f6024ee56498 +DIST opendht-3.5.5.tar.gz 533772 BLAKE2B a4e824f4d0d91a271416ca083a41818b66883c4630ece78e8a5ba2ed877efb14d527c63f6e34dc9f47497fdbb05f0254ca7142962359f5ca5c705dfe48aae266 SHA512 0f3783be2637c72c3f5f1c1ded49dcbc84d1cb7ba338550c0b3d20995b56287eb37b4ff283773d7262202ec2f9e9df2160db717e4fdd9bfcb8331edc628b46cf +DIST opendht-3.6.0.tar.gz 533860 BLAKE2B fcc2a1fc6a11062fe2382568446ac876580c219e00b6931a3f70b2aca5df0bff9e23f40b5129169963ee6f254c778f010370fd18f2532f0731adcf129d291c5e SHA512 f7cd106d9d96b51ad9775486c09fdef3a147678aeda5a4a403f1a0d6cc324a79ed612a40abefff373420f02c016c2884b83a2105eb4388a06e76486346963235 +EBUILD opendht-3.5.4.ebuild 1931 BLAKE2B 80335158ab3f07fd1cc907f77eef22b76d33f4ca87775a4379a4d5e9c130115c2d3c35006a1728c3a9939b4b1e1dfbbf3db527b1b6cea85904d1de6cad085821 SHA512 06f9797d732069ab35f8b4764d3919091a5263a352a40aee8bb1937ee66c75c63e9436d1443ed0d54d8156fda73d395d9bd216e3a60da4c375327bf6249ce10c +EBUILD opendht-3.5.5.ebuild 1931 BLAKE2B 80335158ab3f07fd1cc907f77eef22b76d33f4ca87775a4379a4d5e9c130115c2d3c35006a1728c3a9939b4b1e1dfbbf3db527b1b6cea85904d1de6cad085821 SHA512 06f9797d732069ab35f8b4764d3919091a5263a352a40aee8bb1937ee66c75c63e9436d1443ed0d54d8156fda73d395d9bd216e3a60da4c375327bf6249ce10c +EBUILD opendht-3.6.0.ebuild 1948 BLAKE2B e7b4c81957b980adf43443c406b413949bcd4d827add4d8281a780bc408e683992f4dab73db7ae29bbb8a43227ee04de1969287c3c799b2a307cb6ba01833bae SHA512 8744a56953eb8db2bfda54a1843acd3e9f2c150d1a6c227bdbf3d7b71e27b5fc61f3de66d660378e69e5fa0ba4c3b64ded0c4639de592b9d0f8fa12b01ce397c diff --git a/net-libs/opendht/opendht-3.5.4.ebuild b/net-libs/opendht/opendht-3.5.4.ebuild new file mode 100644 index 0000000..5337df8 --- /dev/null +++ b/net-libs/opendht/opendht-3.5.4.ebuild @@ -0,0 +1,76 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{{8..13},13t} ) + +inherit cmake python-r1 + +DESCRIPTION="A lightweight C++11 Distributed Hash Table implementation" +HOMEPAGE="https://github.com/savoirfairelinux/opendht" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/savoirfairelinux/${PN}" +else + SRC_URI="https://github.com/savoirfairelinux/opendht/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + KEYWORDS="~amd64 ~x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="doc +dht-index +http-client +peer-discovery +proxy-client +proxy-server +proxy-server-identity +proxy-openssl +push-notifications python systemd +tools" + +DEPEND=" + app-crypt/argon2 + dev-libs/libfmt + dev-util/cppunit + dev-cpp/asio + dev-cpp/msgpack-cxx + net-libs/restinio + || ( + net-libs/gnutls + dev-libs/nettle + ) + python? ( dev-python/cython[${PYTHON_USEDEP}] ) + tools? ( sys-libs/readline:0 ) + proxy-openssl? ( dev-libs/openssl:= ) + doc? ( app-text/doxygen ) +" +RDEPEND=" + ${DEPEND} + ${PYTHON_DEPS} +" + +#REQUIRED_USE="http-client? ( !proxy-server !proxy-client ) ${PYTHON_REQUIRED_USE}" + +src_configure() { + local mycmakeargs=( + -DOPENDHT_PYTHON=$(usex python) + -DOPENDHT_TOOLS=$(usex tools) + -DOPENDHT_SYSTEMD=$(usex systemd) + -DOPENDHT_HTTP=$(usex http-client) + -DOPENDHT_INDEX=$(usex dht-index) + -DOPENDHT_PEER_DISCOVERY=$(usex peer-discovery) + -DOPENDHT_PROXY_SERVER=$(usex proxy-server) + -DOPENDHT_PROXY_SERVER_IDENTITY=$(usex proxy-server-identity) + -DOPENDHT_PROXY_CLIENT=$(usex proxy-client) + -DOPENDHT_PROXY_OPENSSL=$(usex proxy-openssl) + -DOPENDHT_PUSH_NOTIFICATIONS=$(usex push-notifications) + -DOPENDHT_DOCUMENTATION=$(usex doc) + -DOPENDHT_SANITIZE=OFF + -DOPENDHT_TESTS_NETWORK=OFF + -DOPENDHT_C=ON + -DOPENDHT_CPACK=ON + -DOPENDHT_DOWNLOAD_DEPS=OFF + ) + cmake_src_configure +} + +src_install() { + cmake_src_install + einstalldocs +} diff --git a/net-libs/opendht/opendht-3.5.5.ebuild b/net-libs/opendht/opendht-3.5.5.ebuild new file mode 100644 index 0000000..5337df8 --- /dev/null +++ b/net-libs/opendht/opendht-3.5.5.ebuild @@ -0,0 +1,76 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{{8..13},13t} ) + +inherit cmake python-r1 + +DESCRIPTION="A lightweight C++11 Distributed Hash Table implementation" +HOMEPAGE="https://github.com/savoirfairelinux/opendht" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/savoirfairelinux/${PN}" +else + SRC_URI="https://github.com/savoirfairelinux/opendht/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + KEYWORDS="~amd64 ~x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="doc +dht-index +http-client +peer-discovery +proxy-client +proxy-server +proxy-server-identity +proxy-openssl +push-notifications python systemd +tools" + +DEPEND=" + app-crypt/argon2 + dev-libs/libfmt + dev-util/cppunit + dev-cpp/asio + dev-cpp/msgpack-cxx + net-libs/restinio + || ( + net-libs/gnutls + dev-libs/nettle + ) + python? ( dev-python/cython[${PYTHON_USEDEP}] ) + tools? ( sys-libs/readline:0 ) + proxy-openssl? ( dev-libs/openssl:= ) + doc? ( app-text/doxygen ) +" +RDEPEND=" + ${DEPEND} + ${PYTHON_DEPS} +" + +#REQUIRED_USE="http-client? ( !proxy-server !proxy-client ) ${PYTHON_REQUIRED_USE}" + +src_configure() { + local mycmakeargs=( + -DOPENDHT_PYTHON=$(usex python) + -DOPENDHT_TOOLS=$(usex tools) + -DOPENDHT_SYSTEMD=$(usex systemd) + -DOPENDHT_HTTP=$(usex http-client) + -DOPENDHT_INDEX=$(usex dht-index) + -DOPENDHT_PEER_DISCOVERY=$(usex peer-discovery) + -DOPENDHT_PROXY_SERVER=$(usex proxy-server) + -DOPENDHT_PROXY_SERVER_IDENTITY=$(usex proxy-server-identity) + -DOPENDHT_PROXY_CLIENT=$(usex proxy-client) + -DOPENDHT_PROXY_OPENSSL=$(usex proxy-openssl) + -DOPENDHT_PUSH_NOTIFICATIONS=$(usex push-notifications) + -DOPENDHT_DOCUMENTATION=$(usex doc) + -DOPENDHT_SANITIZE=OFF + -DOPENDHT_TESTS_NETWORK=OFF + -DOPENDHT_C=ON + -DOPENDHT_CPACK=ON + -DOPENDHT_DOWNLOAD_DEPS=OFF + ) + cmake_src_configure +} + +src_install() { + cmake_src_install + einstalldocs +} diff --git a/net-libs/opendht/opendht-3.6.0.ebuild b/net-libs/opendht/opendht-3.6.0.ebuild new file mode 100644 index 0000000..e087ac2 --- /dev/null +++ b/net-libs/opendht/opendht-3.6.0.ebuild @@ -0,0 +1,77 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +PYTHON_COMPAT=( python3_{{8..13},13t} ) + +inherit cmake python-r1 + +DESCRIPTION="A lightweight C++11 Distributed Hash Table implementation" +HOMEPAGE="https://github.com/savoirfairelinux/opendht" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/savoirfairelinux/${PN}" +else + SRC_URI="https://github.com/savoirfairelinux/opendht/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + KEYWORDS="~amd64 ~x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="doc +dht-index +http-client +peer-discovery +proxy-client +proxy-server +proxy-server-identity +proxy-openssl +push-notifications python systemd +tools" + +DEPEND=" + app-crypt/argon2 + dev-libs/libfmt + dev-util/cppunit + dev-cpp/asio + dev-cpp/msgpack-cxx + net-libs/restinio + dev-cpp/simdutf + || ( + net-libs/gnutls + dev-libs/nettle + ) + python? ( dev-python/cython[${PYTHON_USEDEP}] ) + tools? ( sys-libs/readline:0 ) + proxy-openssl? ( dev-libs/openssl:= ) + doc? ( app-text/doxygen ) +" +RDEPEND=" + ${DEPEND} + ${PYTHON_DEPS} +" + +#REQUIRED_USE="http-client? ( !proxy-server !proxy-client ) ${PYTHON_REQUIRED_USE}" + +src_configure() { + local mycmakeargs=( + -DOPENDHT_PYTHON=$(usex python) + -DOPENDHT_TOOLS=$(usex tools) + -DOPENDHT_SYSTEMD=$(usex systemd) + -DOPENDHT_HTTP=$(usex http-client) + -DOPENDHT_INDEX=$(usex dht-index) + -DOPENDHT_PEER_DISCOVERY=$(usex peer-discovery) + -DOPENDHT_PROXY_SERVER=$(usex proxy-server) + -DOPENDHT_PROXY_SERVER_IDENTITY=$(usex proxy-server-identity) + -DOPENDHT_PROXY_CLIENT=$(usex proxy-client) + -DOPENDHT_PROXY_OPENSSL=$(usex proxy-openssl) + -DOPENDHT_PUSH_NOTIFICATIONS=$(usex push-notifications) + -DOPENDHT_DOCUMENTATION=$(usex doc) + -DOPENDHT_SANITIZE=OFF + -DOPENDHT_TESTS_NETWORK=OFF + -DOPENDHT_C=ON + -DOPENDHT_CPACK=ON + -DOPENDHT_DOWNLOAD_DEPS=OFF + ) + cmake_src_configure +} + +src_install() { + cmake_src_install + einstalldocs +} diff --git a/net-libs/pjproject/Manifest b/net-libs/pjproject/Manifest new file mode 100644 index 0000000..f2083db --- /dev/null +++ b/net-libs/pjproject/Manifest @@ -0,0 +1,3 @@ +AUX sfl-pjproject-2.15.1.patch 485727 BLAKE2B 9c5d94ac9aefd625c6aad055bca90bf64eb3fcd89ba3c110d06c483195e680fc8e7c67c309412c742bfa956df99897a87551863a5282fb6fb024f5d1f2a1b9ff SHA512 27f40e057218096dd1a5ce345643b2f6746c51b173ff5c363515b52a167ae9458e4686e23b779cb3d8ec536c5ca8b4d33ccfef147e459a3e5f09ed6e83f87189 +DIST pjproject-2.15.1.tar.gz 10425868 BLAKE2B cb4fdeca8559460f5335ffa7723e58fea3cb3f81cb55170ab7694b7828e3179c39c0fc376bebed566a212ec2b102349fc78593e414cb03864e4b111cdbf8b77c SHA512 2f83ed32f16c27808d3b9cc8f3b364c68fe88caae9765012b385a0fea70ba8ef4dcfebe3b130156047546720351a527e17d6a1e967877d6a44a6ff3a1f695599 +EBUILD pjproject-2.15.1-r2.ebuild 4035 BLAKE2B af7acfa7c32ef22939ba3908f6f57f14e4b550eacc56912bc10dfc1c98c22a66a7f366f12d678c2c50546b55804b09955ed61d7cd1d342ef38b0b88233d6d3fc SHA512 34809afb773e7f949befd363cd4bcb9e2e78da54455dd15450b9523265d6e21640ba028a18d858f21ffefd907193f5c2d5ee6961747837d354629db0766783a8 diff --git a/net-libs/pjproject/files/sfl-pjproject-2.15.1.patch b/net-libs/pjproject/files/sfl-pjproject-2.15.1.patch new file mode 100644 index 0000000..9c9bbe9 --- /dev/null +++ b/net-libs/pjproject/files/sfl-pjproject-2.15.1.patch @@ -0,0 +1,11990 @@ +diff --git a/.gitignore b/.gitignore +index 362b9c890..db310602b 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,4 +1,3 @@ +-pjlib/include/pj/config_site.h + lib/ + bin/ + output/ +diff --git a/aconfigure b/aconfigure +index b5d344631..6707ea11b 100755 +--- a/aconfigure ++++ b/aconfigure +@@ -6974,9 +6974,6 @@ case $target in + + # UUID + case $target in +- *android*) +- ac_os_objs="$ac_os_objs guid_android.o" +- ;; + *darwin*) + ac_os_objs="$ac_os_objs guid_darwin.o" + ;; +diff --git a/aconfigure.ac b/aconfigure.ac +index 279870e9d..43d69dc09 100644 +--- a/aconfigure.ac ++++ b/aconfigure.ac +@@ -675,9 +675,6 @@ case $target in + + # UUID + case $target in +- *android*) +- ac_os_objs="$ac_os_objs guid_android.o" +- ;; + *darwin*) + ac_os_objs="$ac_os_objs guid_darwin.o" + ;; +diff --git a/build/vs/pjproject-vs14-common-config.props b/build/vs/pjproject-vs14-common-config.props +index 456e4f02e..400439a78 100644 +--- a/build/vs/pjproject-vs14-common-config.props ++++ b/build/vs/pjproject-vs14-common-config.props +@@ -18,12 +18,12 @@ + + WinDesktop + +- v140 ++ v141 + + + + +- v140 ++ v141 + WIN32;PJ_WIN32=1;PJ_M_I386=1; + WIN64;PJ_WIN64=1;PJ_M_X86_64=1; + PJ_M_ARM64=1; +@@ -31,10 +31,10 @@ + + + +- v140 ++ v141 + PJ_WIN32_UWP;UNICODE;_UNICODE; + $(PreprocessorDef);PJ_M_ARMV7=1; +- 10.0.10586.0 ++ 10.0.16299.0 + 10.0.10240.0 + 10.0 + +diff --git a/build/vs/pjproject-vs14-common-defaults.props b/build/vs/pjproject-vs14-common-defaults.props +index 526f6c925..974447f43 100644 +--- a/build/vs/pjproject-vs14-common-defaults.props ++++ b/build/vs/pjproject-vs14-common-defaults.props +@@ -3,7 +3,7 @@ + + + +- 14 ++ 15 + + + <_ProjectFileVersion>14.0.22823.1 +diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h +new file mode 100644 +index 000000000..ba81c7f4d +--- /dev/null ++++ b/pjlib/include/pj/config_site.h +@@ -0,0 +1,26 @@ ++#include "config_site_sample.h" ++ ++/* ++* PJLIB settings. ++*/ ++#define PJ_HAS_IPV6 1 ++#define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION 1 ++ ++/* ++* PJSIP settings. ++*/ ++#define PJSIP_MAX_PKT_LEN 8000 ++#define PJSIP_TRANSPORT_SERVER_IDLE_TIME 3 ++ ++/* ++* PJNAT settings. ++*/ ++#define PJ_ICE_MAX_CAND 256 ++#define PJ_ICE_ST_MAX_CAND 32 ++#define PJ_ICE_MAX_STUN 6 ++#define PJ_ICE_MAX_TURN 4 ++#define PJ_ICE_COMP_BITS 5 ++#define PJ_ICE_MAX_CHECKS 1024 ++/* Set permanent permissions on the TURN ++ server for all peer candidates */ ++#define PJ_ICE_ST_USE_TURN_PERMANENT_PERM PJ_TRUE +diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h +index 88d679c5e..9af42ba3a 100644 +--- a/pjlib/include/pj/sock.h ++++ b/pjlib/include/pj/sock.h +@@ -320,6 +320,12 @@ extern const pj_uint16_t PJ_SO_REUSEADDR; + /** Do not generate SIGPIPE. @see pj_SO_NOSIGPIPE */ + extern const pj_uint16_t PJ_SO_NOSIGPIPE; + ++extern const pj_uint16_t PJ_SO_KEEPALIVE; ++extern const pj_uint16_t PJ_TCP_KEEPIDLE; ++extern const pj_uint16_t PJ_TCP_KEEPINTVL; ++extern const pj_uint16_t PJ_TCP_KEEPCNT; ++extern const pj_uint16_t PJ_TCP_USER_TIMEOUT; ++ + /** Set the protocol-defined priority for all packets to be sent on socket. + */ + extern const pj_uint16_t PJ_SO_PRIORITY; +@@ -350,9 +356,24 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; + /** Get #PJ_SO_SNDBUF constant */ + PJ_DECL(pj_uint16_t) pj_SO_SNDBUF(void); + ++ /** Get #PJ_SO_KEEPALIVE constant */ ++# define pj_SO_KEEPALIVE() PJ_SO_KEEPALIVE(void); ++ + /** Get #PJ_TCP_NODELAY constant */ + PJ_DECL(pj_uint16_t) pj_TCP_NODELAY(void); + ++ /** Get #PJ_TCP_KEEPIDLE constant */ ++# define pj_TCP_KEEPIDLE() PJ_TCP_KEEPIDLE(void); ++ ++ /** Get #PJ_TCP_KEEPINTVL constant */ ++# define pj_TCP_KEEPINTVL() PJ_TCP_KEEPINTVL(void); ++ ++ /** Get #PJ_TCP_USER_TIMEOUT constant */ ++ PJ_DECL(pj_uint16_t) PJ_TCP_USER_TIMEOUT(void); ++ ++ /** Get #PJ_TCP_KEEPCNT constant */ ++# define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT(void); ++ + /** Get #PJ_SO_REUSEADDR constant */ + PJ_DECL(pj_uint16_t) pj_SO_REUSEADDR(void); + +@@ -386,9 +407,24 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; + /** Get #PJ_SO_SNDBUF constant */ + # define pj_SO_SNDBUF() PJ_SO_SNDBUF + ++ /** Get #PJ_SO_KEEPALIVE constant */ ++# define pj_SO_KEEPALIVE() PJ_SO_KEEPALIVE ++ + /** Get #PJ_TCP_NODELAY constant */ + # define pj_TCP_NODELAY() PJ_TCP_NODELAY + ++ /** Get #PJ_TCP_KEEPIDLE constant */ ++# define pj_TCP_KEEPIDLE() PJ_TCP_KEEPIDLE ++ ++ /** Get #PJ_TCP_USER_TIMEOUT constant */ ++# define pj_TCP_USER_TIMEOUT() PJ_TCP_USER_TIMEOUT ++ ++ /** Get #PJ_TCP_KEEPINTVL constant */ ++# define pj_TCP_KEEPINTVL() PJ_TCP_KEEPINTVL ++ ++ /** Get #PJ_TCP_KEEPCNT constant */ ++# define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT ++ + /** Get #PJ_SO_REUSEADDR constant */ + # define pj_SO_REUSEADDR() PJ_SO_REUSEADDR + +diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c +index a9a6a9cfd..a0d17e72e 100644 +--- a/pjlib/src/pj/ioqueue_common_abs.c ++++ b/pjlib/src/pj/ioqueue_common_abs.c +@@ -1056,7 +1056,10 @@ retry_on_restart: + /* + * Check that address storage can hold the address parameter. + */ +- PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr), PJ_EBUG); ++ PJ_ASSERT_RETURN((((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET() && ++ addrlen <= (int)sizeof(pj_sockaddr_in)) || ++ (((pj_sockaddr*)addr)->addr.sa_family == pj_AF_INET6() && ++ addrlen <= (int)sizeof(pj_sockaddr_in6)), PJ_EBUG); + + /* + * Schedule asynchronous send. +diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c +index c90c5ef69..233bbbb00 100644 +--- a/pjlib/src/pj/os_core_unix.c ++++ b/pjlib/src/pj/os_core_unix.c +@@ -71,7 +71,7 @@ JavaVM *pj_jni_jvm = NULL; + JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) + { + pj_jni_jvm = vm; +- ++ + return JNI_VERSION_1_4; + } + +@@ -845,6 +845,18 @@ PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) + return rc; + } + ++#if PJ_DARWINOS ++static pthread_key_t key; ++static pthread_once_t key_once = PTHREAD_ONCE_INIT; ++ ++static void ++make_key() ++{ ++ (void) pthread_key_create(&key, free); ++} ++#endif ++ ++ + /* + * pj_thread_this() + */ +@@ -854,9 +866,26 @@ PJ_DEF(pj_thread_t*) pj_thread_this(void) + pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); + + if (rec == NULL) { +- pj_assert(!"Calling pjlib from unknown/external thread. You must " +- "register external threads with pj_thread_register() " +- "before calling any pjlib functions."); ++ ++ static pj_thread_t* dummy; ++ ++#if PJ_DARWINOS ++ (void) pthread_once(&key_once, make_key); ++ ++ pj_thread_t* desc; ++ ++ if ((desc = pthread_getspecific(key)) == NULL) { ++ desc = malloc(sizeof(pj_thread_desc)); ++ pj_bzero(desc, sizeof(pj_thread_desc)); ++ (void) pthread_setspecific(key, desc); ++ } ++#else ++ static __thread pj_thread_desc desc; ++#endif ++ ++ pj_thread_register(NULL, (long*)desc, &dummy); ++ ++ rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); + } + + /* +@@ -1049,7 +1078,7 @@ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) + pj_status_t status; + + PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); +- ++ + #if PJ_HAS_THREADS + status = pj_mutex_destroy( atomic_var->mutex ); + if (status == PJ_SUCCESS) { +diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c +index 68a538dcb..d2ee5b180 100644 +--- a/pjlib/src/pj/os_core_win32.c ++++ b/pjlib/src/pj/os_core_win32.c +@@ -655,9 +655,10 @@ PJ_DEF(pj_thread_t*) pj_thread_this(void) + pj_thread_t *rec = pj_thread_local_get(thread_tls_id); + + if (rec == NULL) { +- pj_assert(!"Calling pjlib from unknown/external thread. You must " +- "register external threads with pj_thread_register() " +- "before calling any pjlib functions."); ++ static __declspec(thread) pj_thread_desc desc; ++ static __declspec(thread) pj_thread_t* this_thread; ++ pj_thread_register(NULL, desc, &this_thread); ++ rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); + } + + /* +diff --git a/pjlib/src/pj/os_timestamp_posix.c b/pjlib/src/pj/os_timestamp_posix.c +index 07ef682a9..0371aad43 100644 +--- a/pjlib/src/pj/os_timestamp_posix.c ++++ b/pjlib/src/pj/os_timestamp_posix.c +@@ -202,7 +202,7 @@ PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) + return PJ_SUCCESS; + } + +-#elif defined(__ANDROID__) ++#elif defined(PJ_ANDROID) && PJ_ANDROID + + #include + #include +diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c +index 5f594efa7..ddc8cd9cc 100644 +--- a/pjlib/src/pj/sock_bsd.c ++++ b/pjlib/src/pj/sock_bsd.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -35,6 +35,15 @@ + + #define THIS_FILE "sock_bsd.c" + ++#if !defined(PJ_WIN32) && !defined(PJ_WIN64) ++# if !defined(SOL_TCP) && defined(IPPROTO_TCP) ++# define SOL_TCP IPPROTO_TCP ++# endif ++# if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) ++# define TCP_KEEPIDLE TCP_KEEPALIVE ++# endif ++#endif ++ + /* + * Address families conversion. + * The values here are indexed based on pj_addr_family. +@@ -172,7 +181,24 @@ const pj_uint16_t PJ_IPV6_TCLASS = 0xFFFF; + const pj_uint16_t PJ_SO_TYPE = SO_TYPE; + const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; + const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; ++const pj_uint16_t PJ_SO_KEEPALIVE = SO_KEEPALIVE; + const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY; ++#if !defined(PJ_WIN32) && !defined(PJ_WIN64) ++# ifdef TCP_KEEPIDLE ++const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE; ++# endif ++# ifdef TCP_KEEPINTVL ++const pj_uint16_t PJ_TCP_KEEPINTVL = TCP_KEEPINTVL; ++# endif ++# ifdef TCP_USER_TIMEOUT ++const pj_uint16_t PJ_TCP_USER_TIMEOUT = TCP_USER_TIMEOUT; ++#else ++const pj_uint16_t PJ_TCP_USER_TIMEOUT = 18; ++# endif ++# ifdef TCP_KEEPCNT ++const pj_uint16_t PJ_TCP_KEEPCNT = TCP_KEEPCNT; ++# endif ++#endif + const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR; + #ifdef SO_NOSIGPIPE + const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE; +@@ -270,7 +296,7 @@ PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) + /* + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure +- * that inp points to. ++ * that inp points to. + */ + PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, pj_in_addr *inp) + { +@@ -312,7 +338,7 @@ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) + PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); + PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); + +- /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be ++ /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be + * compatible with pj_inet_aton() + */ + if (af==PJ_AF_INET) { +@@ -357,7 +383,7 @@ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) + + sock_addr.addr.sa_family = (pj_uint16_t)af; + rc = WSAStringToAddress( +- PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), ++ PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), + af, NULL, (LPSOCKADDR)&sock_addr, &addr_len); + if (rc != 0) { + /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6, +@@ -505,8 +531,8 @@ PJ_DEF(const pj_str_t*) pj_gethostname(void) + /* + * Create new socket/endpoint for communication and returns a descriptor. + */ +-PJ_DEF(pj_status_t) pj_sock_socket(int af, +- int type, ++PJ_DEF(pj_status_t) pj_sock_socket(int af, ++ int type, + int proto, + pj_sock_t *sock) + { +@@ -514,14 +540,14 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); +- PJ_ASSERT_RETURN((SOCKET)PJ_INVALID_SOCKET==INVALID_SOCKET, ++ PJ_ASSERT_RETURN((SOCKET)PJ_INVALID_SOCKET==INVALID_SOCKET, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); + +- if (*sock == PJ_INVALID_SOCKET) ++ if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); +- ++ + #if PJ_SOCK_DISABLE_WSAECONNRESET && \ + (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0) + +@@ -555,9 +581,9 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + /* + * Create new socket/endpoint for communication and returns a descriptor. + */ +-PJ_DEF(pj_status_t) pj_sock_socket(int af, +- int type, +- int proto, ++PJ_DEF(pj_status_t) pj_sock_socket(int af, ++ int type, ++ int proto, + pj_sock_t *sock) + { + int type0 = type; +@@ -566,7 +592,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); +- PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, ++ PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + #if !defined(SOCK_CLOEXEC) +@@ -584,7 +610,22 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + pj_int32_t val = 1; + if ((type & 0xF) == pj_SOCK_STREAM()) { + pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(), +- &val, sizeof(val)); ++ &val, sizeof(val)); ++ pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_KEEPALIVE(), ++ &val, sizeof(val)); ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPCNT(), ++ &val, sizeof(val)); ++ val = 30; ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPIDLE(), ++ &val, sizeof(val)); ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_KEEPINTVL(), ++ &val, sizeof(val)); ++ val = 30000; ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_USER_TIMEOUT(), ++ &val, sizeof(val)); ++ val = 1; ++ pj_sock_setsockopt(*sock, pj_SOL_TCP(), pj_TCP_NODELAY(), ++ &val, sizeof(val)); + } + #if defined(PJ_SOCK_HAS_IPV6_V6ONLY) && PJ_SOCK_HAS_IPV6_V6ONLY != 0 + if (af == PJ_AF_INET6) { +@@ -595,7 +636,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 + if ((type & 0xF) == pj_SOCK_DGRAM()) { +- pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, ++ pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, + &val, sizeof(val)); + } + #endif +@@ -612,7 +653,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, + /* + * Bind socket. + */ +-PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, ++PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, + const pj_sockaddr_t *addr, + int len) + { +@@ -632,7 +673,7 @@ PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, + /* + * Bind socket. + */ +-PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, ++PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, + pj_uint32_t addr32, + pj_uint16_t port) + { +@@ -741,7 +782,7 @@ PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, + { + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); +- ++ + CHECK_ADDR_LEN(to, tolen); + + #ifdef MSG_NOSIGNAL +@@ -749,12 +790,12 @@ PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, + flags |= MSG_NOSIGNAL; + #endif + +- *len = sendto(sock, (const char*)buf, (int)(*len), flags, ++ *len = sendto(sock, (const char*)buf, (int)(*len), flags, + (const struct sockaddr*)to, tolen); + +- if (*len < 0) ++ if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); +- else ++ else + return PJ_SUCCESS; + } + +@@ -771,7 +812,7 @@ PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, + + *len = recv(sock, (char*)buf, (int)(*len), flags); + +- if (*len < 0) ++ if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +@@ -790,10 +831,10 @@ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + +- *len = recvfrom(sock, (char*)buf, (int)(*len), flags, ++ *len = recvfrom(sock, (char*)buf, (int)(*len), flags, + (struct sockaddr*)from, (socklen_t*)fromlen); + +- if (*len < 0) ++ if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { + if (from) { +@@ -832,12 +873,12 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, + { + int status; + PJ_CHECK_STACK(); +- ++ + #if (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_SUNOS) && PJ_SUNOS) + /* Some opt may still need int value (e.g:SO_EXCLUSIVEADDRUSE in win32). */ +- status = setsockopt(sock, +- level, +- ((optname&0xff00)==0xff00)?(int)optname|0xffff0000:optname, ++ status = setsockopt(sock, ++ level, ++ ((optname&0xff00)==0xff00)?(int)optname|0xffff0000:optname, + (const char*)optval, optlen); + #else + status = setsockopt(sock, level, optname, (const char*)optval, optlen); +@@ -861,12 +902,12 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt_params( pj_sock_t sockfd, + pj_status_t retval = PJ_SUCCESS; + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(params, PJ_EINVAL); +- ++ + for (;icnt && ioptions[i].level, + (pj_uint16_t)params->options[i].optname, +- params->options[i].optval, ++ params->options[i].optval, + params->options[i].optlen); + if (status != PJ_SUCCESS) { + retval = status; +@@ -937,18 +978,18 @@ PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + PJ_SOCKADDR_SET_LEN(addr, *addrlen); + } + #endif +- ++ + *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); + if (*newsock==PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else { +- ++ + #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 + if (addr) { + PJ_SOCKADDR_RESET_LEN(addr); + } + #endif +- ++ + return PJ_SUCCESS; + } + } +diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c +index 62b08bdea..e9f78ff4f 100644 +--- a/pjlib/src/pj/sock_common.c ++++ b/pjlib/src/pj/sock_common.c +@@ -1649,11 +1649,36 @@ PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void) + return PJ_SO_SNDBUF; + } + ++PJ_DEF(pj_uint16_t) pj_SO_KEEPALIVE(void) ++{ ++ return PJ_SO_KEEPALIVE; ++} ++ ++PJ_DEF(pj_uint16_t) pj_TCP_USER_TIMEOUT(void) ++{ ++ return PJ_TCP_USER_TIMEOUT; ++} ++ + PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void) + { + return PJ_TCP_NODELAY; + } + ++PJ_DEF(pj_uint16_t) pj_TCP_KEEPIDLE(void) ++{ ++ return PJ_TCP_KEEPIDLE ++} ++ ++PJ_DEF(pj_uint16_t) pj_TCP_KEEPINTVL(void) ++{ ++ return PJ_TCP_KEEPINTVL ++} ++ ++PJ_DEF(pj_uint16_t) pj_TCP_KEEPCNT(void) ++{ ++ return PJ_TCP_KEEPCNT ++} ++ + PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) + { + return PJ_SO_REUSEADDR; +diff --git a/pjlib/src/pj/sock_uwp.cpp b/pjlib/src/pj/sock_uwp.cpp +index 14ce05875..2230af9d1 100644 +--- a/pjlib/src/pj/sock_uwp.cpp ++++ b/pjlib/src/pj/sock_uwp.cpp +@@ -69,6 +69,24 @@ const pj_uint16_t PJ_SOL_IP = IPPROTO_IP; + const pj_uint16_t PJ_SOL_IP = 0; + #endif /* SOL_IP */ + ++#if defined(TCP_KEEPIDLE) ++const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE; ++#else ++const pj_uint16_t PJ_TCP_KEEPIDLE = 4; ++#endif ++ ++#if defined(TCP_KEEPINTVL) ++const pj_uint16_t PJ_TCP_KEEPINTVL = TCP_KEEPINTVL; ++#else ++const pj_uint16_t PJ_TCP_KEEPINTVL = 5; ++#endif ++ ++#if defined(TCP_KEEPCNT) ++const pj_uint16_t PJ_TCP_KEEPCNT = TCP_KEEPCNT; ++#else ++const pj_uint16_t PJ_TCP_KEEPCNT = 6; ++#endif ++ + #if defined(SOL_TCP) + const pj_uint16_t PJ_SOL_TCP = SOL_TCP; + #elif defined(IPPROTO_TCP) +@@ -79,6 +97,18 @@ const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; + const pj_uint16_t PJ_SOL_TCP = 6; + #endif /* SOL_TCP */ + ++#if defined(TCP_USER_TIMEOUT) ++const pj_uint16_t PJ_TCP_USER_TIMEOUT = TCP_USER_TIMEOUT; ++#else ++const pj_uint16_t PJ_TCP_USER_TIMEOUT = 18; ++#endif ++ ++#if defined(SOL_KEEPALIVE) ++const pj_uint16_t PJ_SOL_KEEPALIVE = SOL_KEEPALIVE; ++#else ++const pj_uint16_t PJ_SOL_KEEPALIVE = 9; ++#endif ++ + #ifdef SOL_UDP + const pj_uint16_t PJ_SOL_UDP = SOL_UDP; + #elif defined(IPPROTO_UDP) +diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c +index ad56c4f98..f224300c9 100644 +--- a/pjlib/src/pj/symbols.c ++++ b/pjlib/src/pj/symbols.c +@@ -258,6 +258,10 @@ PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) + PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) + PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) + PJ_EXPORT_SYMBOL(PJ_SOL_IP) ++PJ_EXPORT_SYMBOL(PJ_TCP_KEEPIDLE) ++PJ_EXPORT_SYMBOL(PJ_TCP_KEEPINTVL) ++PJ_EXPORT_SYMBOL(PJ_TCP_KEEPCNT) ++PJ_EXPORT_SYMBOL(PJ_TCP_USER_TIMEOUT) + PJ_EXPORT_SYMBOL(PJ_SOL_TCP) + PJ_EXPORT_SYMBOL(PJ_SOL_UDP) + PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) +diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h +index e904c3ac4..bd988d3d5 100644 +--- a/pjnath/include/pjnath/config.h ++++ b/pjnath/include/pjnath/config.h +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef __PJNATH_CONFIG_H__ + #define __PJNATH_CONFIG_H__ +@@ -65,9 +65,9 @@ + + /** + * The default initial STUN round-trip time estimation (the RTO value +- * in RFC 3489-bis), in miliseconds. +- * This value is used to control the STUN request +- * retransmit time. The initial value of retransmission interval ++ * in RFC 3489-bis), in miliseconds. ++ * This value is used to control the STUN request ++ * retransmit time. The initial value of retransmission interval + * would be set to this value, and will be doubled after each + * retransmission. + */ +@@ -78,7 +78,7 @@ + + /** + * The STUN transaction timeout value, in miliseconds. +- * After the last retransmission is sent and if no response is received ++ * After the last retransmission is sent and if no response is received + * after this time, the STUN transaction will be considered to have failed. + * + * The default value is 16x RTO (as per RFC 3489-bis). +@@ -201,8 +201,8 @@ + + + /** +- * Number of seconds to refresh the permission/channel binding before the +- * permission/channel binding expires. This value should be greater than ++ * Number of seconds to refresh the permission/channel binding before the ++ * permission/channel binding expires. This value should be greater than + * PJ_TURN_PERM_TIMEOUT setting. + */ + #ifndef PJ_TURN_REFRESH_SEC_BEFORE +@@ -211,7 +211,7 @@ + + + /** +- * The TURN session timer heart beat interval. When this timer occurs, the ++ * The TURN session timer heart beat interval. When this timer occurs, the + * TURN session will scan all the permissions/channel bindings to see which + * need to be refreshed. + */ +@@ -278,7 +278,7 @@ + * the maximum number of components (PJ_ICE_MAX_COMP) value. + */ + #ifndef PJ_ICE_COMP_BITS +-# define PJ_ICE_COMP_BITS 1 ++# define PJ_ICE_COMP_BITS 2 + #endif + + +@@ -310,10 +310,10 @@ + /** + * The number of bits to represent ICE candidate's local preference. The + * local preference is used to specify preference among candidates with +- * the same type, and ICE draft suggests 65535 as the default local +- * preference, which means we need 16 bits to represent the value. But ++ * the same type, and ICE draft suggests 65535 as the default local ++ * preference, which means we need 16 bits to represent the value. But + * since we don't have the facility to specify local preference, we'll +- * just disable this feature and let the preference sorted by the ++ * just disable this feature and let the preference sorted by the + * type only. + * + * Default: 0 +@@ -339,20 +339,20 @@ + * Default: 20 + */ + #ifndef PJ_ICE_TA_VAL +-# define PJ_ICE_TA_VAL 20 ++# define PJ_ICE_TA_VAL 50 + #endif + + + /** +- * According to ICE Section 8.2. Updating States, if an In-Progress pair in +- * the check list is for the same component as a nominated pair, the agent ++ * According to ICE Section 8.2. Updating States, if an In-Progress pair in ++ * the check list is for the same component as a nominated pair, the agent + * SHOULD cease retransmissions for its check if its pair priority is lower + * than the lowest priority nominated pair for that component. + * + * If a higher priority check is In Progress, this rule would cause that + * check to be performed even when it most likely will fail. + * +- * The macro here controls if ICE session should cancel all In Progress ++ * The macro here controls if ICE session should cancel all In Progress + * checks for the same component regardless of its priority. + * + * Default: 1 (yes, cancel all) +@@ -382,6 +382,42 @@ + # define ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT 10000 + #endif + ++/** ++ * For TCP transport, this timer is time that a controlling agent must wait for ++ * incoming checks if the local candidate is of type "passive" or "s-o". ++ * ++ * Default: 10000 (milliseconds) ++ */ ++#ifndef ICE_CONTROLLING_PASSIVE_TIMEOUT ++# define ICE_CONTROLLING_PASSIVE_TIMEOUT 10000 ++#endif ++ ++/** ++ * Allowed timeout for pending connections. TCP only. ++ * ++ * Default: 15000 (milliseconds) ++ */ ++#ifndef PJ_ICE_TCP_CONNECTION_TIMEOUT ++# define PJ_ICE_TCP_CONNECTION_TIMEOUT 15000 ++#endif ++ ++/** ++ * Delay between two reconnection attempts. TCP only. ++ * ++ * Default: 500 (milliseconds) ++ */ ++#ifndef PJ_ICE_TCP_RECONNECTION_DELAY ++# define PJ_ICE_TCP_RECONNECTION_DELAY 500 ++#endif ++ ++/** ++ * Maximum number of reconnection attempts. TCP only. ++ * ++ * Default: 24 ++ */ ++#ifndef PJ_ICE_TCP_MAX_RECONNECTION_COUNT ++# define PJ_ICE_TCP_MAX_RECONNECTION_COUNT 24 ++#endif + + /** + * For controlling agent if it uses regular nomination, specify the delay to +@@ -583,7 +619,7 @@ + /** Default duration for searching UPnP Internet Gateway Devices (in seconds). + * Default: 5 seconds + */ +-#ifndef PJ_UPNP_DEFAULT_SEARCH_TIME ++#ifndef PJ_UPNP_DEFAULT_SEARCH_TIME + # define PJ_UPNP_DEFAULT_SEARCH_TIME 5 + #endif + +diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h +index e796b2539..60e0564db 100644 +--- a/pjnath/include/pjnath/ice_session.h ++++ b/pjnath/include/pjnath/ice_session.h +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef __PJNATH_ICE_SESSION_H__ + #define __PJNATH_ICE_SESSION_H__ +@@ -41,7 +41,7 @@ PJ_BEGIN_DECL + * + * \section pj_ice_sess_sec ICE Session + * +- * An ICE session, represented by #pj_ice_sess structure, is the lowest ++ * An ICE session, represented by #pj_ice_sess structure, is the lowest + * abstraction of ICE in PJNATH, and it is used to perform and manage + * connectivity checks of transport address candidates within a + * single media stream (note: this differs from what is described +@@ -50,12 +50,12 @@ PJ_BEGIN_DECL + * + * The ICE session described here is independent from any transports, + * meaning that the actual network I/O for this session would have to +- * be performed by the application, or higher layer abstraction. ++ * be performed by the application, or higher layer abstraction. + * Using this framework, application would give any incoming packets to + * the ICE session, and it would provide the ICE session with a callback + * to send outgoing message. + * +- * For higher abstraction of ICE where transport is included, please ++ * For higher abstraction of ICE where transport is included, please + * see \ref PJNATH_ICE_STREAM_TRANSPORT. + * + * \subsection pj_ice_sess_using_sec Using The ICE Session +@@ -162,6 +162,52 @@ typedef enum pj_ice_cand_type + + } pj_ice_cand_type; + ++/** ++ * ICE candidates types like described by RFC 6544. ++ */ ++typedef enum pj_ice_cand_transport { ++ /** ++ * Candidates UDP compatible ++ */ ++ PJ_CAND_UDP, ++ /** ++ * Candidates sending outgoing TCP connections ++ */ ++ PJ_CAND_TCP_ACTIVE, ++ /** ++ * Candidates accepting incoming TCP connections ++ */ ++ PJ_CAND_TCP_PASSIVE, ++ /** ++ * Candidates capable of receiving incoming connections and sending ++ * connections ++ */ ++ PJ_CAND_TCP_SO ++} pj_ice_cand_transport; ++ ++/** ++ * ICE transport types, which will be used both to specify the connection ++ * type for reaching candidates and other client ++ */ ++typedef enum pj_ice_tp_type { ++ /** ++ * UDP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_ICE_TP_UDP = 17, ++ ++ /** ++ * TCP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_ICE_TP_TCP = 6, ++ ++ /** ++ * TLS transport. The TLS transport will only be used as the connection ++ * type to reach the server and never as the allocation transport type. ++ */ ++ PJ_ICE_TP_TLS = 255 ++ ++} pj_ice_tp_type; ++ + + /** Forward declaration for pj_ice_sess */ + typedef struct pj_ice_sess pj_ice_sess; +@@ -169,12 +215,9 @@ typedef struct pj_ice_sess pj_ice_sess; + /** Forward declaration for pj_ice_sess_check */ + typedef struct pj_ice_sess_check pj_ice_sess_check; + +-/** Forward declaration for pj_ice_sess_cand */ +-typedef struct pj_ice_sess_cand pj_ice_sess_cand; +- + /** +- * This structure describes ICE component. +- * A media stream may require multiple components, each of which has ++ * This structure describes ICE component. ++ * A media stream may require multiple components, each of which has + * to work for the media stream as a whole to work. For media streams + * based on RTP, there are two components per media stream - one for RTP, + * and one for RTCP. +@@ -204,32 +247,6 @@ typedef struct pj_ice_sess_comp + } pj_ice_sess_comp; + + +-/** +- * Data structure to be attached to internal message processing. +- */ +-typedef struct pj_ice_msg_data +-{ +- /** Transport ID for this message */ +- unsigned transport_id; +- +- /** Flag to indicate whether data.req contains data */ +- pj_bool_t has_req_data; +- +- /** The data */ +- union data { +- /** Request data */ +- struct request_data { +- pj_ice_sess *ice; /**< ICE session */ +- pj_ice_sess_checklist *clist; /**< Checklist */ +- unsigned ckid; /**< Check ID */ +- pj_ice_sess_cand *lcand; /**< Local cand */ +- pj_ice_sess_cand *rcand; /**< Remote cand */ +- } req; /**< Request data */ +- } data; /**< The data */ +- +-} pj_ice_msg_data; +- +- + /** + * This structure describes an ICE candidate. + * ICE candidate is a transport address that is to be tested by ICE +@@ -238,7 +255,7 @@ typedef struct pj_ice_msg_data + * (server reflexive, relayed or host), priority, foundation, and + * base. + */ +-struct pj_ice_sess_cand ++typedef struct pj_ice_sess_cand + { + /** + * The candidate ID. +@@ -250,10 +267,10 @@ struct pj_ice_sess_cand + */ + pj_ice_cand_type type; + +- /** ++ /** + * Status of this candidate. The value will be PJ_SUCCESS if candidate + * address has been resolved successfully, PJ_EPENDING when the address +- * resolution process is in progress, or other value when the address ++ * resolution process is in progress, or other value when the address + * resolution has completed with failure. + */ + pj_status_t status; +@@ -277,8 +294,8 @@ struct pj_ice_sess_cand + + /** + * The foundation string, which is an identifier which value will be +- * equivalent for two candidates that are of the same type, share the +- * same base, and come from the same STUN server. The foundation is ++ * equivalent for two candidates that are of the same type, share the ++ * same base, and come from the same STUN server. The foundation is + * used to optimize ICE performance in the Frozen algorithm. + */ + pj_str_t foundation; +@@ -295,16 +312,16 @@ struct pj_ice_sess_cand + * the local address of the socket. For reflexive candidates, the value + * will be the public address allocated in NAT router for the host + * candidate and as reported in MAPPED-ADDRESS or XOR-MAPPED-ADDRESS +- * attribute of STUN Binding request. For relayed candidate, the value ++ * attribute of STUN Binding request. For relayed candidate, the value + * will be the address allocated in the TURN server by STUN Allocate + * request. + */ + pj_sockaddr addr; + + /** +- * Base address of this candidate. "Base" refers to the address an agent ++ * Base address of this candidate. "Base" refers to the address an agent + * sends from for a particular candidate. For host candidates, the base +- * is the same as the host candidate itself. For reflexive candidates, ++ * is the same as the host candidate itself. For reflexive candidates, + * the base is the local IP address of the socket. For relayed candidates, + * the base address is the transport address allocated in the TURN server + * for this candidate. +@@ -317,7 +334,38 @@ struct pj_ice_sess_cand + */ + pj_sockaddr rel_addr; + +-}; ++ /** ++ * Transport used (TCP or UDP) ++ */ ++ pj_ice_cand_transport transport; ++ ++} pj_ice_sess_cand; ++ ++ ++/** ++ * Data structure to be attached to internal message processing. ++ */ ++typedef struct pj_ice_msg_data ++{ ++ /** Transport ID for this message */ ++ unsigned transport_id; ++ ++ /** Flag to indicate whether data.req contains data */ ++ pj_bool_t has_req_data; ++ ++ /** The data */ ++ union data { ++ /** Request data */ ++ struct request_data { ++ pj_ice_sess *ice; /**< ICE session */ ++ pj_ice_sess_checklist *clist; /**< Checklist */ ++ unsigned ckid; /**< Check ID */ ++ pj_ice_sess_cand *lcand; /**< Local cand */ ++ pj_ice_sess_cand *rcand; /**< Remote cand */ ++ } req; /**< Request data */ ++ } data; /**< The data */ ++ ++} pj_ice_msg_data; + + + /** +@@ -332,6 +380,22 @@ typedef enum pj_ice_sess_check_state + */ + PJ_ICE_SESS_CHECK_STATE_FROZEN, + ++ /** ++ * The following status is used when a packet sent via TURN got a ++ * "Connection reset by peer". This mean that the peer didn't allow ++ * us to connect yet. The socket will be reconnected during the next ++ * loop. ++ */ ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, ++ ++ /** ++ * TODO (sblin): REMOVE THIS! - https://github.com/coturn/coturn/issues/408 ++ * For now, this status is only used because sometimes, the first packet ++ * doesn't receive any response. So, we retry to send the packet every ++ * 50 loops. ++ */ ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ + /** + * A check has not been performed for this pair, and can be + * performed as soon as it is the highest priority Waiting pair on +@@ -339,6 +403,12 @@ typedef enum pj_ice_sess_check_state + */ + PJ_ICE_SESS_CHECK_STATE_WAITING, + ++ /** ++ * A check has not been performed for this pair, but TCP socket ++ * is currently connecting to the pair. Wait to finish the connection. ++ */ ++ PJ_ICE_SESS_CHECK_STATE_PENDING, ++ + /** + * A check has not been performed for this pair, and can be + * performed as soon as it is the highest priority Waiting pair on +@@ -365,9 +435,9 @@ typedef enum pj_ice_sess_check_state + + /** + * This structure describes an ICE connectivity check. An ICE check +- * contains a candidate pair, and will involve sending STUN Binding +- * Request transaction for the purposes of verifying connectivity. +- * A check is sent from the local candidate to the remote candidate ++ * contains a candidate pair, and will involve sending STUN Binding ++ * Request transaction for the purposes of verifying connectivity. ++ * A check is sent from the local candidate to the remote candidate + * of a candidate pair. + */ + struct pj_ice_sess_check +@@ -398,8 +468,8 @@ struct pj_ice_sess_check + pj_ice_sess_check_state state; + + /** +- * STUN transmit data containing STUN Binding request that was sent +- * as part of this check. The value will only be set when this check ++ * STUN transmit data containing STUN Binding request that was sent ++ * as part of this check. The value will only be set when this check + * has a pending transaction, and is used to cancel the transaction + * when other check has succeeded. + */ +@@ -416,6 +486,13 @@ struct pj_ice_sess_check + * STUN transaction. + */ + pj_status_t err_code; ++ ++#if PJ_HAS_TCP ++ /** ++ * TCP reconnection attemps counter. ++ */ ++ int reconnect_count; ++#endif + }; + + +@@ -445,7 +522,7 @@ typedef enum pj_ice_sess_checklist_state + + + /** +- * This structure represents ICE check list, that is an ordered set of ++ * This structure represents ICE check list, that is an ordered set of + * candidate pairs that an agent will use to generate checks. + */ + struct pj_ice_sess_checklist +@@ -509,7 +586,7 @@ typedef struct pj_ice_sess_cb + + /** + * A mandatory callback which will be called by the ICE session when +- * it needs to send outgoing STUN packet. ++ * it needs to send outgoing STUN packet. + * + * @param ice The ICE session. + * @param comp_id ICE component ID. +@@ -519,7 +596,7 @@ typedef struct pj_ice_sess_cb + * @param dst_addr Packet destination address. + * @param dst_addr_len Length of destination address. + */ +- pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, ++ pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, + unsigned transport_id, + const void *pkt, pj_size_t size, + const pj_sockaddr_t *dst_addr, +@@ -534,15 +611,49 @@ typedef struct pj_ice_sess_cb + * @param transport_id Transport ID. + * @param pkt The whole packet. + * @param size Size of the packet. +- * @param src_addr Source address where this packet was received ++ * @param src_addr Source address where this packet was received + * from. + * @param src_addr_len The length of source address. + */ +- void (*on_rx_data)(pj_ice_sess *ice, unsigned comp_id, +- unsigned transport_id, +- void *pkt, pj_size_t size, +- const pj_sockaddr_t *src_addr, +- unsigned src_addr_len); ++ void (*on_rx_data)(pj_ice_sess *ice, unsigned comp_id, ++ unsigned transport_id, ++ void *pkt, pj_size_t size, ++ const pj_sockaddr_t *src_addr, ++ unsigned src_addr_len); ++ ++ /** ++ * Wait for TCP and send connectivity check ++ * ++ * @param ice The ICE session. ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*wait_tcp_connection)(pj_ice_sess *ice, ++ unsigned check_id); ++ ++ /** ++ * Reconnect a resetted TCP connection and send connectivity check ++ * cf. PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY ++ * ++ * @param ice The ICE session. ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*reconnect_tcp_connection)(pj_ice_sess *ice, ++ unsigned check_id); ++ ++ /** ++ * Close TCP socket ++ * ++ * @param ice The ICE session. ++ * @param check_id The wanted check. ++ */ ++ pj_status_t (*close_tcp_connection)(pj_ice_sess *ice, ++ unsigned check_id); ++ ++ /** ++ * If an internal TCP keep alive, this mount the error to the application ++ */ ++ void (*on_ice_destroy)(pj_ice_sess *ice); ++ + } pj_ice_sess_cb; + + +@@ -630,7 +741,7 @@ typedef enum pj_ice_sess_trickle + + /** + * This structure describes various ICE session options. Application +- * configure the ICE session with these options by calling ++ * configure the ICE session with these options by calling + * #pj_ice_sess_set_options(). + */ + typedef struct pj_ice_sess_options +@@ -643,7 +754,7 @@ typedef struct pj_ice_sess_options + + /** + * For controlling agent if it uses regular nomination, specify the delay +- * to perform nominated check (connectivity check with USE-CANDIDATE ++ * to perform nominated check (connectivity check with USE-CANDIDATE + * attribute) after all components have a valid pair. + * + * Default value is PJ_ICE_NOMINATED_CHECK_DELAY. +@@ -651,14 +762,14 @@ typedef struct pj_ice_sess_options + unsigned nominated_check_delay; + + /** +- * For a controlled agent, specify how long it wants to wait (in +- * milliseconds) for the controlling agent to complete sending ++ * For a controlled agent, specify how long it wants to wait (in ++ * milliseconds) for the controlling agent to complete sending + * connectivity check with nominated flag set to true for all components + * after the controlled agent has found that all connectivity checks in + * its checklist have been completed and there is at least one successful + * (but not nominated) check for every component. + * +- * Default value for this option is ++ * Default value for this option is + * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable + * this timer. + */ +@@ -672,6 +783,13 @@ typedef struct pj_ice_sess_options + */ + pj_ice_sess_trickle trickle; + ++ /** ++ * For a controlling agent, specify how long it wants to wait ++ * in milliseconds for passive candidates and wait for connection ++ * attempts ++ */ ++ int agent_passive_timeout; ++ + } pj_ice_sess_options; + + +@@ -704,6 +822,7 @@ struct pj_ice_sess + pj_status_t ice_status; /**< Error status. */ + pj_timer_entry timer; /**< ICE timer. */ + pj_timer_entry timer_end_of_cand; /**< End-of-cand timer. */ ++ pj_timer_entry timer_connect; /**< ICE timer tcp timeout*/ + pj_ice_sess_cb cb; /**< Callback. */ + + pj_stun_config stun_cfg; /**< STUN settings. */ +@@ -741,10 +860,10 @@ struct pj_ice_sess + + /* Checklist */ + pj_ice_sess_checklist clist; /**< Active checklist */ +- ++ + /* Valid list */ + pj_ice_sess_checklist valid_list; /**< Valid list. */ +- ++ + /** Temporary buffer for misc stuffs to avoid using stack too much */ + union { + char txt[128]; +@@ -813,7 +932,7 @@ PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt); + * @param cb ICE callback. + * @param local_ufrag Optional string to be used as local username to + * authenticate incoming STUN binding request. If +- * the value is NULL, a random string will be ++ * the value is NULL, a random string will be + * generated. + * @param local_passwd Optional string to be used as local password. + * @param grp_lock Optional group lock to be used by this session. +@@ -911,8 +1030,8 @@ PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, + /** + * Assign a custom preference values for ICE candidate types. By assigning + * custom preference value, application can control the order of candidates +- * to be checked first. The default preference settings is to use 126 for +- * host candidates, 100 for server reflexive candidates, 110 for peer ++ * to be checked first. The default preference settings is to use 126 for ++ * host candidates, 100 for server reflexive candidates, 110 for peer + * reflexive candidates, an 0 for relayed candidates. + * + * Note that this function must be called before any candidates are added +@@ -932,7 +1051,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, + + /** + * Add a candidate to this ICE session. Application must add candidates for +- * each components ID before it can start pairing the candidates and ++ * each components ID before it can start pairing the candidates and + * performing connectivity checks. + * + * @param ice ICE session instance. +@@ -948,6 +1067,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, + * @param rel_addr Optional related address. + * @param addr_len Length of addresses. + * @param p_cand_id Optional pointer to receive the candidate ID. ++ * @param transport Candidate's type + * + * @return PJ_SUCCESS if candidate is successfully added. + */ +@@ -961,14 +1081,15 @@ PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + const pj_sockaddr_t *base_addr, + const pj_sockaddr_t *rel_addr, + int addr_len, +- unsigned *p_cand_id); ++ unsigned *p_cand_id, ++ pj_ice_cand_transport transport); + + /** + * Find default candidate for the specified component ID, using this + * rule: + * - if the component has a successful candidate pair, then the + * local candidate of this pair will be returned. +- * - otherwise a relay, reflexive, or host candidate will be selected ++ * - otherwise a relay, reflexive, or host candidate will be selected + * on that specified order. + * + * @param ice The ICE session instance. +@@ -991,18 +1112,18 @@ PJ_DECL(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, + * #pj_ice_sess_start_check(). + * + * @param ice ICE session instance. +- * @param rem_ufrag Remote ufrag, as seen in the SDP received from ++ * @param rem_ufrag Remote ufrag, as seen in the SDP received from + * the remote agent. + * @param rem_passwd Remote password, as seen in the SDP received from + * the remote agent. + * @param rem_cand_cnt Number of remote candidates. + * @param rem_cand Remote candidate array. Remote candidates are +- * gathered from the SDP received from the remote ++ * gathered from the SDP received from the remote + * agent. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +-PJ_DECL(pj_status_t) ++PJ_DECL(pj_status_t) + pj_ice_sess_create_check_list(pj_ice_sess *ice, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, +@@ -1020,13 +1141,13 @@ pj_ice_sess_create_check_list(pj_ice_sess *ice, + * This function is only applicable when trickle ICE is not disabled. + * + * @param ice ICE session instance. +- * @param rem_ufrag Remote ufrag, as seen in the SDP received from ++ * @param rem_ufrag Remote ufrag, as seen in the SDP received from + * the remote agent. + * @param rem_passwd Remote password, as seen in the SDP received from + * the remote agent. + * @param rem_cand_cnt Number of remote candidates. + * @param rem_cand Remote candidate array. Remote candidates are +- * gathered from the SDP received from the remote ++ * gathered from the SDP received from the remote + * agent. + * @param trickle_done Flag to indicate end of trickling, set to PJ_TRUE + * after all local candidates have been gathered AND +@@ -1035,7 +1156,7 @@ pj_ice_sess_create_check_list(pj_ice_sess *ice, + * + * @return PJ_SUCCESS or the appropriate error code. + */ +-PJ_DECL(pj_status_t) ++PJ_DECL(pj_status_t) + pj_ice_sess_update_check_list(pj_ice_sess *ice, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, +@@ -1108,6 +1229,44 @@ PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, + const pj_sockaddr_t *src_addr, + int src_addr_len); + ++/** ++ * Notification when ICE session get a new incoming connection ++ * ++ * @param ice The ICE session. ++ * @param transport_id Related transport ++ * @param status PJ_SUCCESS when connection is made, or any errors ++ * if the connection has failed (or if the peer has ++ * disconnected after an established connection). ++ * @param remote_addr Connected remove address ++ */ ++PJ_DECL(void) ice_sess_on_peer_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr); ++ ++/** ++ * Notification when ICE session get a new resetted connection ++ * cf PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY ++ * ++ * @param ice The ICE session. ++ * @param transport_id Related transport ++ * @param remote_addr Connected remove address ++ */ ++PJ_DECL(void) ice_sess_on_peer_reset_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr); ++ ++/** ++ * Notification when ICE session get a new packet ++ * Used to remove the PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET status ++ * ++ * @param ice The ICE session. ++ * @param transport_id Related transport ++ * @param remote_addr Connected remove address ++ */ ++PJ_DECL(void) ice_sess_on_peer_packet(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr); + + + /** +diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h +index 0f2510aa4..f8aa41eae 100644 +--- a/pjnath/include/pjnath/ice_strans.h ++++ b/pjnath/include/pjnath/ice_strans.h +@@ -218,6 +218,13 @@ typedef struct pj_ice_strans_cb + const pj_ice_sess_cand *cand, + pj_bool_t end_of_cand); + ++ /** ++ * This callback is called if an internal operation fails ++ * ++ * @param ice_st The ICE stream transport. ++ */ ++ void (*on_destroy)(pj_ice_strans *ice_st); ++ + } pj_ice_strans_cb; + + +@@ -300,6 +307,13 @@ typedef struct pj_ice_strans_stun_cfg + */ + pj_bool_t ignore_stun_error; + ++ /** ++ * Type of connection to the STUN server. ++ * ++ * Default is PJ_STUN_TP_UDP. ++ */ ++ pj_stun_tp_type conn_type; ++ + } pj_ice_strans_stun_cfg; + + +@@ -315,6 +329,13 @@ typedef struct pj_ice_strans_turn_cfg + */ + int af; + ++ /** ++ * If we want to use UDP or TCP as described by RFC 6544. ++ * This will discover candidates via TCP sockets. Then it will ++ * transfer messages on the transport via TCP. ++ */ ++ pj_ice_tp_type protocol; ++ + /** + * Optional TURN socket settings. The default values will be + * initialized by #pj_turn_sock_cfg_default(). This contains +@@ -394,6 +415,13 @@ typedef struct pj_ice_strans_cfg + */ + int af; + ++ /** ++ * If we want to use UDP or TCP as described by RFC 6544. ++ * This will discover candidates via TCP sockets. Then it will ++ * transfer messages on the transport via TCP. ++ */ ++ pj_ice_tp_type protocol; ++ + /** + * STUN configuration which contains the timer heap and + * ioqueue instance to be used, and STUN retransmission +diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h +index 4a5076bb1..56cc7dc3a 100644 +--- a/pjnath/include/pjnath/stun_session.h ++++ b/pjnath/include/pjnath/stun_session.h +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef __PJNATH_STUN_SESSION_H__ + #define __PJNATH_STUN_SESSION_H__ +@@ -40,26 +40,26 @@ PJ_BEGIN_DECL + * @addtogroup PJNATH_STUN_SESSION + * @{ + * +- * This is is a transport-independent object to manage a client or server ++ * This is is a transport-independent object to manage a client or server + * STUN session. It has the following features: +- * ++ * + * - transport independent:\n + * the object does not have it's own socket, but rather it provides + * functions and callbacks to send and receive packets. This way the +- * object can be used by different transport types (e.g. UDP, TCP, ++ * object can be used by different transport types (e.g. UDP, TCP, + * TLS, etc.) as well as better integration to application which + * already has its own means to send and receive packets. +- * ++ * + * - authentication management:\n + * the object manages STUN authentication throughout the lifetime of + * the session. For client sessions, once it's given a credential to + * authenticate itself with the server, the object will automatically + * add authentication info (the MESSAGE-INTEGRITY) to the request as +- * well as authenticate the response. It will also handle long-term ++ * well as authenticate the response. It will also handle long-term + * authentication challenges, including handling of nonce expiration, +- * and retry the request automatically. For server sessions, it can ++ * and retry the request automatically. For server sessions, it can + * be configured to authenticate incoming requests automatically. +- * ++ * + * - static or dynamic credential:\n + * application may specify static or dynamic credential to be used by + * the STUN session. Static credential means a static combination of +@@ -67,16 +67,16 @@ PJ_BEGIN_DECL + * duration), while dynamic credential provides callback to ask the + * application about which username/password to use everytime + * authentication is about to be performed. +- * ++ * + * - client transaction management:\n + * outgoing requests may be sent with a STUN transaction for reliability, + * and the object will manage the transaction internally (including + * performing retransmissions). Application will be notified about the + * result of the request when the response arrives (or the transaction + * times out). When the request is challenged with authentication, the +- * object will retry the request with new authentication info, and ++ * object will retry the request with new authentication info, and + * application will be notified about the final result of this request. +- * ++ * + * - server transaction management:\n + * application may ask response to incoming requests to be cached by + * the object, and in this case the object will check for cached +@@ -95,7 +95,7 @@ PJ_BEGIN_DECL + * + * - create the STUN session:\n + * by calling #pj_stun_session_create(). Among other things, this +- * function requires the instance of #pj_stun_config and also ++ * function requires the instance of #pj_stun_config and also + * #pj_stun_session_cb structure which stores callbacks to send + * outgoing packets as well as to notify application about incoming + * STUN requests, responses, and indicates and other events. +@@ -124,8 +124,8 @@ PJ_BEGIN_DECL + * use #pj_stun_session_send_msg() to send outgoing STUN messages (this + * includes STUN requests, indications, and responses). The function has + * options whether to retransmit the request (for non reliable transports) +- * or to cache the response if we're sending response. This function in +- * turn will call the \a on_send_msg() callback of #pj_stun_session_cb ++ * or to cache the response if we're sending response. This function in ++ * turn will call the \a on_send_msg() callback of #pj_stun_session_cb + * to request the application to send the packet. + * + * - handling incoming packet:\n +@@ -146,7 +146,7 @@ PJ_BEGIN_DECL + * + * - creating and sending response:\n + * create the STUN response with #pj_stun_session_create_res(). This will +- * create a transmit data buffer containing a blank STUN response. You ++ * create a transmit data buffer containing a blank STUN response. You + * will then typically need to add STUN attributes that are relevant to + * the response, but note that some default attributes will + * be added by the session later when the message is sent (such as +@@ -157,7 +157,7 @@ PJ_BEGIN_DECL + * - convenient way to send response:\n + * the #pj_stun_session_respond() is provided as a convenient way to + * create and send simple STUN responses, such as error responses. +- * ++ * + * - destroying the session:\n + * once the session is done, use #pj_stun_session_destroy() to destroy + * the session. +@@ -173,6 +173,29 @@ typedef struct pj_stun_rx_data pj_stun_rx_data; + /** Forward declaration for pj_stun_session */ + typedef struct pj_stun_session pj_stun_session; + ++/** ++ * STUN transport types, which will be used both to specify the connection ++ * type for reaching STUN server and the type of allocation transport to be ++ * requested to server (the REQUESTED-TRANSPORT attribute). ++ */ ++typedef enum pj_stun_tp_type { ++ /** ++ * UDP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_STUN_TP_UDP = 17, ++ ++ /** ++ * TCP transport, which value corresponds to IANA protocol number. ++ */ ++ PJ_STUN_TP_TCP = 6, ++ ++ /** ++ * TLS transport. The TLS transport will only be used as the connection ++ * type to reach the server and never as the allocation transport type. ++ */ ++ PJ_STUN_TP_TLS = 255 ++ ++} pj_stun_tp_type; + + /** + * This is the callback to be registered to pj_stun_session, to send +@@ -186,7 +209,7 @@ typedef struct pj_stun_session_cb + * + * @param sess The STUN session. + * @param token The token associated with this outgoing message +- * and was set by the application. This token was ++ * and was set by the application. This token was + * set by application in pj_stun_session_send_msg() + * for outgoing messages that are initiated by the + * application, or in pj_stun_session_on_rx_pkt() +@@ -209,11 +232,11 @@ typedef struct pj_stun_session_cb + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + +- /** ++ /** + * Callback to be called on incoming STUN request message. This function + * is called when application calls pj_stun_session_on_rx_pkt() and when + * the STUN session has detected that the incoming STUN message is a +- * STUN request message. In the ++ * STUN request message. In the + * callback processing, application MUST create a response by calling + * pj_stun_session_create_response() function and send the response + * with pj_stun_session_send_msg() function, before returning from +@@ -241,7 +264,7 @@ typedef struct pj_stun_session_cb + unsigned src_addr_len); + + /** +- * Callback to be called when response is received or the transaction ++ * Callback to be called when response is received or the transaction + * has timed out. This callback is called either when application calls + * pj_stun_session_on_rx_pkt() with the packet containing a STUN + * response for the client transaction, or when the internal timer of +@@ -254,7 +277,7 @@ typedef struct pj_stun_session_cb + * or other error has occurred, and the response + * argument may be NULL. + * Note that when the status is not success, the +- * response may contain non-NULL value if the ++ * response may contain non-NULL value if the + * response contains STUN ERROR-CODE attribute. + * @param token The token that was set by the application when + * calling pj_stun_session_send_msg() function. +@@ -264,9 +287,9 @@ typedef struct pj_stun_session_cb + * @param response The response message, on successful transaction, + * or otherwise MAY BE NULL if status is not success. + * Note that when the status is not success, this +- * argument may contain non-NULL value if the ++ * argument may contain non-NULL value if the + * response contains STUN ERROR-CODE attribute. +- * @param src_addr The source address where the response was ++ * @param src_addr The source address where the response was + * received, or NULL if the response is NULL. + * @param src_addr_len The length of the source address. + */ +@@ -306,6 +329,38 @@ typedef struct pj_stun_session_cb + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + ++ /** ++ * Notification when STUN session get a ConnectionAttempt indication. ++ * ++ * @param stun_session The STUN session. ++ * @param status PJ_SUCCESS when connection is made, or any errors ++ * if the connection has failed (or if the peer has ++ * disconnected after an established connection). ++ * @param remote_addr The remote connected ++ */ ++ void (*on_peer_connection)(pj_stun_session *sess, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr); ++ ++ /** ++ * Notification when STUN connection is resetted (TCP only). ++ * ++ * @param stun_session The STUN session. ++ * @param remote_addr The remote resetted ++ */ ++ void (*on_peer_reset_connection)(pj_stun_session *sess, ++ pj_sockaddr_t* ++ remote_addr); ++ ++ /** ++ * Notification when STUN connection is resetted (TCP only). ++ * ++ * @param stun_session The STUN session. ++ * @param remote_addr The remote resetted ++ */ ++ void (*on_peer_packet)(pj_stun_session *sess, ++ pj_sockaddr_t* remote_addr); ++ + } pj_stun_session_cb; + + +@@ -320,8 +375,8 @@ struct pj_stun_rx_data + pj_stun_msg *msg; + + /** +- * Credential information that is found and used to authenticate +- * incoming request. Application may use this information when ++ * Credential information that is found and used to authenticate ++ * incoming request. Application may use this information when + * generating authentication for the outgoing response. + */ + pj_stun_req_cred_info info; +@@ -348,7 +403,7 @@ struct pj_stun_tx_data + pj_bool_t retransmit; /**< Retransmit request? */ + pj_uint32_t msg_magic; /**< Message magic. */ + pj_uint8_t msg_key[12]; /**< Message/transaction key. */ +- ++ + pj_grp_lock_t *grp_lock; /**< Group lock (for resp cache). */ + + pj_stun_req_cred_info auth_info; /**< Credential info */ +@@ -390,6 +445,7 @@ typedef enum pj_stun_sess_msg_log_flag + * @param grp_lock Optional group lock to be used by this session. + * If NULL, the session will create one itself. + * @param p_sess Pointer to receive STUN session instance. ++ * @param conn_type If the session use UDP or TCP + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +@@ -398,7 +454,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg, + const pj_stun_session_cb *cb, + pj_bool_t fingerprint, + pj_grp_lock_t *grp_lock, +- pj_stun_session **p_sess); ++ pj_stun_session **p_sess, ++ pj_stun_tp_type conn_type); + + /** + * Destroy the STUN session and all objects created in the context of +@@ -499,7 +556,7 @@ PJ_DECL(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess, + + /** + * Create a STUN request message. After the message has been successfully +- * created, application can send the message by calling ++ * created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. +@@ -520,7 +577,7 @@ PJ_DECL(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, + + /** + * Create a STUN Indication message. After the message has been successfully +- * created, application can send the message by calling ++ * created, application can send the message by calling + * pj_stun_session_send_msg(). + * + * @param sess The STUN session instance. +@@ -537,8 +594,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + + /** +- * Create a STUN response message. After the message has been +- * successfully created, application can send the message by calling ++ * Create a STUN response message. After the message has been ++ * successfully created, application can send the message by calling + * pj_stun_session_send_msg(). Alternatively application may use + * pj_stun_session_respond() to create and send response in one function + * call. +@@ -576,8 +633,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess, + * @param sess The STUN session instance. + * @param token Optional token which will be given back to application in + * \a on_send_msg() callback and \a on_request_complete() +- * callback, if the message is a STUN request message. +- * Internally this function will put the token in the ++ * callback, if the message is a STUN request message. ++ * Internally this function will put the token in the + * \a token field of pj_stun_tx_data, hence it will + * overwrite any value that the application puts there. + * @param cache_res If the message is a response message for an incoming +@@ -595,8 +652,8 @@ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess, + * be sent. + * + * @return PJ_SUCCESS on success, or the appropriate error code. +- * This function will return PJNATH_ESTUNDESTROYED if +- * application has destroyed the session in ++ * This function will return PJNATH_ESTUNDESTROYED if ++ * application has destroyed the session in + * \a on_send_msg() callback. + */ + PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, +@@ -625,30 +682,30 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, + * be used. + * @param token Optional token which will be given back to application in + * \a on_send_msg() callback and \a on_request_complete() +- * callback, if the message is a STUN request message. +- * Internally this function will put the token in the ++ * callback, if the message is a STUN request message. ++ * Internally this function will put the token in the + * \a token field of pj_stun_tx_data, hence it will + * overwrite any value that the application puts there. + * @param cache Specify whether session should cache this response for + * future request retransmission. If TRUE, subsequent request +- * retransmission will be handled by the session and it ++ * retransmission will be handled by the session and it + * will not call request callback. + * @param dst_addr Destination address of the response (or equal to the + * source address of the original request). + * @param addr_len Address length. + * + * @return PJ_SUCCESS on success, or the appropriate error code. +- * This function will return PJNATH_ESTUNDESTROYED if +- * application has destroyed the session in ++ * This function will return PJNATH_ESTUNDESTROYED if ++ * application has destroyed the session in + * \a on_send_msg() callback. + */ +-PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, ++PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, + const pj_stun_rx_data *rdata, +- unsigned code, ++ unsigned code, + const char *err_msg, + void *token, +- pj_bool_t cache, +- const pj_sockaddr_t *dst_addr, ++ pj_bool_t cache, ++ const pj_sockaddr_t *dst_addr, + unsigned addr_len); + + /** +@@ -665,8 +722,8 @@ PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, + * callback. This error status MUST NOT be PJ_SUCCESS. + * + * @return PJ_SUCCESS if transaction is successfully cancelled. +- * This function will return PJNATH_ESTUNDESTROYED if +- * application has destroyed the session in ++ * This function will return PJNATH_ESTUNDESTROYED if ++ * application has destroyed the session in + * \a on_request_complete() callback. + */ + PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess, +@@ -685,7 +742,7 @@ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess, + * needs to be incremented. + * + * @return PJ_SUCCESS on success, or the appropriate error. +- * This function will return PJNATH_ESTUNDESTROYED if ++ * This function will return PJNATH_ESTUNDESTROYED if + * application has destroyed the session in \a on_send_msg() + * callback. + */ +@@ -716,8 +773,8 @@ PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, + * STUN message (useful if packet is received via a + * stream oriented protocol). + * @param token Optional token which will be given back to application +- * in the \a on_rx_request(), \a on_rx_indication() and +- * \a on_send_msg() callbacks. The token can be used to ++ * in the \a on_rx_request(), \a on_rx_indication() and ++ * \a on_send_msg() callbacks. The token can be used to + * associate processing or incoming request or indication + * with some context. + * @param src_addr The source address of the packet, which will also +@@ -726,7 +783,7 @@ PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, + * @param src_addr_len Length of the source address. + * + * @return PJ_SUCCESS on success, or the appropriate error code. +- * This function will return PJNATH_ESTUNDESTROYED if ++ * This function will return PJNATH_ESTUNDESTROYED if + * application has destroyed the session in one of the + * callback. + */ +@@ -751,6 +808,22 @@ PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess, + pj_stun_tx_data *tdata); + ++/** ++ * ++ * @param sess The STUN session. ++ * ++ * @return The callback linked to the STUN session ++ */ ++PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess); ++ ++/** ++ * ++ * @param sess The STUN session. ++ * ++ * @return The connection type linked to the STUN session ++ */ ++PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess); ++ + + /** + * @} +diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h +index a6610335e..b1601f65f 100644 +--- a/pjnath/include/pjnath/stun_sock.h ++++ b/pjnath/include/pjnath/stun_sock.h +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef __PJNATH_STUN_SOCK_H__ + #define __PJNATH_STUN_SOCK_H__ +@@ -23,10 +23,14 @@ + * @file stun_sock.h + * @brief STUN aware socket transport + */ ++#include + #include ++#include + #include ++#include + #include + #include ++#include + #include + #include + +@@ -86,7 +90,17 @@ typedef enum pj_stun_sock_op + /** + * IP address change notification from the keep-alive operation. + */ +- PJ_STUN_SOCK_MAPPED_ADDR_CHANGE ++ PJ_STUN_SOCK_MAPPED_ADDR_CHANGE, ++ ++ /** ++ * STUN session was destroyed. ++ */ ++ PJ_STUN_SESS_DESTROYED, ++ ++ /** ++ * TCP fails to connect ++ */ ++ PJ_STUN_TCP_CONNECT_ERROR + + + } pj_stun_sock_op; +@@ -143,7 +157,7 @@ typedef struct pj_stun_sock_cb + * callback may be called for the following conditions: + * - the first time the publicly mapped address has been resolved from + * the STUN server, this callback will be called with \a op argument +- * set to PJ_STUN_SOCK_BINDING_OP \a status argument set to ++ * set to PJ_STUN_SOCK_BINDING_OP \a status argument set to + * PJ_SUCCESS. + * - anytime when the transport has detected that the publicly mapped + * address has changed, this callback will be called with \a op +@@ -152,7 +166,7 @@ typedef struct pj_stun_sock_cb + * application will get the resolved public address in the + * #pj_stun_sock_info structure. + * - for any terminal error (such as STUN time-out, DNS resolution +- * failure, or keep-alive failure), this callback will be called ++ * failure, or keep-alive failure), this callback will be called + * with the \a status argument set to non-PJ_SUCCESS. + * + * @param stun_sock The STUN transport. +@@ -166,7 +180,7 @@ typedef struct pj_stun_sock_cb + * should return PJ_TRUE to let the STUN socket operation + * continues. + */ +- pj_bool_t (*on_status)(pj_stun_sock *stun_sock, ++ pj_bool_t (*on_status)(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status); + +@@ -196,6 +210,11 @@ typedef struct pj_stun_sock_info + */ + pj_sockaddr mapped_addr; + ++ /** ++ * If connected, the remote address will be stored here. ++ */ ++ pj_sockaddr outgoing_addr; ++ + /** + * Number of interface address aliases. The interface address aliases + * are list of all interface addresses in this host. +@@ -207,6 +226,11 @@ typedef struct pj_stun_sock_info + */ + pj_sockaddr aliases[PJ_ICE_ST_MAX_CAND]; + ++ /** ++ * The tranport type of the socket ++ */ ++ pj_stun_tp_type conn_type; ++ + } pj_stun_sock_info; + + +@@ -251,6 +275,28 @@ typedef struct pj_stun_sock_cfg + */ + pj_sockaddr bound_addr; + ++ /** ++ * This member holds a list of address mappings (internal/external) that ++ * the user (application) provides. These mappings are meant to be used ++ * to add server reflexive candidates that are not typically discovered ++ * by regular ICE operations. This is the case for mappings obtained ++ * through UPNP-IGD/NAT-PMP/PCP requests, or manually configured (port ++ * forward). ++ */ ++ struct { ++ pj_sockaddr local_addr; ++ pj_sockaddr mapped_addr; ++ int tp_type; ++ } user_mapping[PJ_ICE_MAX_COMP]; ++ ++ /** ++ * Holds the actual number of allocated ports. If the feature is used, ++ * this value should match the number of components of the ICE session. ++ * The feature is disabled if this variable is set to 0. ++ * Default value is 0. ++ */ ++ unsigned user_mapping_cnt; ++ + /** + * Specify the port range for STUN socket binding, relative to the start + * port number specified in \a bound_addr. Note that this setting is only +@@ -262,7 +308,7 @@ typedef struct pj_stun_sock_cfg + + /** + * Specify the STUN keep-alive duration, in seconds. The STUN transport +- * does keep-alive by sending STUN Binding request to the STUN server. ++ * does keep-alive by sending STUN Binding request to the STUN server. + * If this value is zero, the PJ_STUN_KEEP_ALIVE_SEC value will be used. + * If the value is negative, it will disable STUN keep-alive. + */ +@@ -341,9 +387,11 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); + * things the ioqueue and timer heap instance for + * the operation of this transport. + * @param af Address family of socket. Currently pj_AF_INET() +- * and pj_AF_INET6() are supported. ++ * and pj_AF_INET6() are supported. + * @param name Optional name to be given to this transport to + * assist debugging. ++ * @param conn_type Connection type to the STUN server. Both TCP and UDP are ++ * supported. + * @param cb Callback to receive events/data from the transport. + * @param cfg Optional transport settings. + * @param user_data Arbitrary application data to be associated with +@@ -356,6 +404,7 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); + PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg, + const char *name, + int af, ++ pj_stun_tp_type conn_type, + const pj_stun_sock_cb *cb, + const pj_stun_sock_cfg *cfg, + void *user_data, +@@ -475,7 +524,7 @@ PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock, + * this case the \a on_data_sent() callback will be + * called when data is actually sent. Any other return + * value indicates error condition. +- */ ++ */ + PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, + pj_ioqueue_op_key_t *send_key, + const void *pkt, +@@ -484,6 +533,51 @@ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + ++ ++#if PJ_HAS_TCP ++/** ++ * Connect active socket to remote address ++ * @param stun_sock ++ * @param remote_addr the destination ++ * @param af address family ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_connect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af); ++ ++/** ++ * Connect active socket to remote address ++ * @param stun_sock ++ * @param remote_addr the destination ++ * @param af address family ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af); ++ ++/** ++ * Close active socket ++ * @param stun_sock ++ * @param remote_addr The remote address linked ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_close(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr); ++ ++/** ++ * Close all active sockets except the one with remote_addr ++ * @param stun_sock ++ * @param remote_addr The remote address linked ++ */ ++PJ_DECL(pj_status_t) pj_stun_sock_close_all_except(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr); ++ ++#endif ++ ++/** ++ * Retrieve the linked session ++ * @param stun_sock ++ */ ++PJ_DECL(pj_stun_session *) pj_stun_sock_get_session(pj_stun_sock *stun_sock); + /** + * @} + */ +diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h +index 66acd9956..66a4afd1d 100644 +--- a/pjnath/include/pjnath/turn_session.h ++++ b/pjnath/include/pjnath/turn_session.h +@@ -253,6 +253,35 @@ typedef struct pj_turn_session_cb + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + ++ /** ++ * This callback will be called by the TURN session whenever it ++ * needs to send outgoing message. Since the TURN session doesn't ++ * have a socket on its own, this callback must be implemented. ++ * ++ * The difference with on_send_pkt is that this function returns ++ * the size of the packet actually sent to predict when a busy will ++ * occurs. Indeed, activesock send the data asynchronously. When the ++ * data are actually sent, on_data_sent will be triggered. ++ * ++ * @param sess The TURN session. ++ * @param pkt The packet/data to be sent. ++ * @param pkt_len Length of the packet/data. ++ * @param dst_addr Destination address of the packet. ++ * @param addr_len Length of the destination address. ++ * @param send_size Length sent. ++ * @param original_size The length of the packet without the HEADER ++ * ++ * @return The callback should return the status of the ++ * send operation. ++ */ ++ pj_status_t (*on_send_pkt2)(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *dst_addr, ++ unsigned addr_len, ++ unsigned* sent_size, ++ unsigned original_size); ++ + /** + * This callback will be called by the TURN session whenever it + * needs to send outgoing STUN requests/messages for TURN signalling +@@ -833,6 +862,42 @@ PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len); + ++/** ++ * Send a data to the specified peer address via the TURN relay. This ++ * function will encapsulate the data as STUN Send Indication or TURN ++ * ChannelData packet and send the message to the TURN server. The TURN ++ * server then will send the data to the peer. ++ * ++ * The allocation (pj_turn_session_alloc()) must have been successfully ++ * created before application can relay any data. ++ * ++ * Since TURN session is transport independent, this function will ++ * ultimately call \a on_send_pkt() callback to request the application ++ * to actually send the packet containing the data to the TURN server. ++ * ++ * The difference with pj_turn_session_sendto is that this function returns ++ * the size of the packet actually sent to predict when a busy will ++ * occurs. Indeed, activesock send the data asynchronously. When the ++ * data are actually sent, on_data_sent will be triggered. ++ * ++ * @param sess The TURN client session. ++ * @param pkt The data/packet to be sent to peer. ++ * @param pkt_len Length of the data. ++ * @param peer_addr The remote peer address (the ultimate destination ++ * of the data, and not the TURN server address). ++ * @param addr_len Length of the address. ++ * @param sent The size of the packet actually sent ++ * ++ * @return PJ_SUCCESS if the operation has been successful, ++ * or the appropriate error code on failure. ++ */ ++PJ_DECL(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *peer_addr, ++ unsigned addr_len, ++ unsigned *sent); ++ + /** + * Optionally establish channel binding for the specified a peer address. + * This function will assign a unique channel number for the peer address +diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h +index 2de6b4267..681ac32ba 100644 +--- a/pjnath/include/pjnath/turn_sock.h ++++ b/pjnath/include/pjnath/turn_sock.h +@@ -668,6 +668,44 @@ PJ_DECL(pj_status_t) pj_turn_sock_disconnect(pj_turn_sock *turn_sock, + const pj_sockaddr_t *peer, + unsigned addr_len); + ++/** ++ * Send a data to the specified peer address via the TURN relay. This ++ * function will encapsulate the data as STUN Send Indication or TURN ++ * ChannelData packet and send the message to the TURN server. The TURN ++ * server then will send the data to the peer. ++ * ++ * The allocation (pj_turn_sock_alloc()) must have been successfully ++ * created before application can relay any data. ++ * ++ * @param turn_sock The TURN transport instance. ++ * @param pkt The data/packet to be sent to peer. ++ * @param pkt_len Length of the data. ++ * @param peer_addr The remote peer address (the ultimate destination ++ * of the data, and not the TURN server address). ++ * @param addr_len Length of the address. ++ * @param sent Size actually sent. ++ * ++ * @return PJ_SUCCESS if the operation has been successful, ++ * or the appropriate error code on failure. ++ */ ++PJ_DECL(pj_status_t) pj_turn_sock_sendto2(pj_turn_sock *turn_sock, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *peer_addr, ++ unsigned addr_len, ++ unsigned* sent); ++ ++/** ++ * Check if peer is a dataconn ++ * ++ * @param turn_sock The turn sock ++ * @param peer The peer addr to check ++ * ++ * @return true if dataconn else false ++ */ ++PJ_DECL(pj_bool_t) pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, ++ const pj_sockaddr_t *peer); ++ + + /** + * @} +diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c +index 54ddb7b6c..f684097a6 100644 +--- a/pjnath/src/pjnath-test/concur_test.c ++++ b/pjnath/src/pjnath-test/concur_test.c +@@ -183,6 +183,7 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess) + char name[10]; + pj_ansi_snprintf(name, sizeof(name), "stun%02d", i); + status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(), ++ PJ_STUN_TP_UDP, + &stun_cb, NULL, test_sess, + &stun_sock[i]); + if (status != PJ_SUCCESS) { +diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c +index f0e308bba..b67049a49 100644 +--- a/pjnath/src/pjnath-test/sess_auth.c ++++ b/pjnath/src/pjnath-test/sess_auth.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include "test.h" + +@@ -81,7 +81,7 @@ static pj_status_t server_on_rx_request(pj_stun_session *sess, + PJ_UNUSED_ARG(pkt_len); + PJ_UNUSED_ARG(token); + +- return pj_stun_session_respond(sess, rdata, 0, NULL, NULL, PJ_TRUE, ++ return pj_stun_session_respond(sess, rdata, 0, NULL, NULL, PJ_TRUE, + src_addr, src_addr_len); + } + +@@ -106,7 +106,7 @@ static pj_status_t server_get_auth(void *user_data, + + + static pj_status_t server_get_password( const pj_stun_msg *msg, +- void *user_data, ++ void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + pj_pool_t *pool, +@@ -172,8 +172,8 @@ static int server_thread(void *unused) + PJ_FD_ZERO(&readset); + PJ_FD_SET(server->sock, &readset); + +- if (pj_sock_select((int)server->sock+1, &readset, NULL, NULL, &delay)==1 +- && PJ_FD_ISSET(server->sock, &readset)) ++ if (pj_sock_select((int)server->sock+1, &readset, NULL, NULL, &delay)==1 ++ && PJ_FD_ISSET(server->sock, &readset)) + { + char pkt[1000]; + pj_ssize_t len; +@@ -195,7 +195,7 @@ static int server_thread(void *unused) + if (!server->responding) + continue; + +- pj_stun_session_on_rx_pkt(server->sess, pkt, len, ++ pj_stun_session_on_rx_pkt(server->sess, pkt, len, + PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM, + NULL, NULL, &src_addr, src_addr_len); + } +@@ -235,7 +235,7 @@ static int create_std_server(pj_stun_auth_type auth_type, + pj_stun_session_cb sess_cb; + pj_stun_auth_cred cred; + pj_status_t status; +- ++ + /* Create server */ + pool = pj_pool_create(mem, "server", 1000, 1000, NULL); + server = PJ_POOL_ZALLOC_T(pool, struct server); +@@ -247,7 +247,8 @@ static int create_std_server(pj_stun_auth_type auth_type, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_rx_request = &server_on_rx_request; + sess_cb.on_send_msg = &server_send_msg; +- status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess); ++ status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, ++ NULL, &server->sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + destroy_server(); + return -10; +@@ -294,7 +295,7 @@ static int create_std_server(pj_stun_auth_type auth_type, + * 'no route to host' error, so let's just hardcode to [::1] + */ + pj_sockaddr_init(pj_AF_INET6(), &addr, NULL, 0); +- addr.ipv6.sin6_addr.s6_addr[15] = 1; ++ addr.ipv6.sin6_addr.s6_addr[15] = 1; + } else { + status = pj_gethostip(GET_AF(use_ipv6), &addr); + if (status != PJ_SUCCESS) { +@@ -394,8 +395,8 @@ static int client_thread(void *unused) + PJ_FD_ZERO(&readset); + PJ_FD_SET(client->sock, &readset); + +- if (pj_sock_select((int)client->sock+1, &readset, NULL, NULL, &delay)==1 +- && PJ_FD_ISSET(client->sock, &readset)) ++ if (pj_sock_select((int)client->sock+1, &readset, NULL, NULL, &delay)==1 ++ && PJ_FD_ISSET(client->sock, &readset)) + { + char pkt[1000]; + pj_ssize_t len; +@@ -417,11 +418,11 @@ static int client_thread(void *unused) + if (!client->responding) + continue; + +- pj_stun_session_on_rx_pkt(client->sess, pkt, len, ++ pj_stun_session_on_rx_pkt(client->sess, pkt, len, + PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM, + NULL, NULL, &src_addr, src_addr_len); + } +- ++ + } + + return 0; +@@ -465,7 +466,7 @@ static int run_client_test(const char *title, + pj_status_t expected_code, + const char *expected_realm, + const char *expected_nonce, +- ++ + int (*more_check)(void)) + { + pj_pool_t *pool; +@@ -475,7 +476,7 @@ static int run_client_test(const char *title, + pj_status_t status; + pj_sockaddr addr; + int rc = 0; +- ++ + PJ_LOG(3,(THIS_FILE, " %s test (%s)", title, use_ipv6?"IPv6":"IPv4")); + + /* Create client */ +@@ -488,7 +489,8 @@ static int run_client_test(const char *title, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &client_on_request_complete; + sess_cb.on_send_msg = &client_send_msg; +- status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess); ++ status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, ++ NULL, &client->sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -200; +@@ -545,7 +547,7 @@ static int run_client_test(const char *title, + } + + /* Create request */ +- status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, ++ status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); + if (status != PJ_SUCCESS) { + destroy_client_server(); +@@ -570,11 +572,11 @@ static int run_client_test(const char *title, + pj_stun_msgint_attr_create(tdata->pool, &mi); + pj_stun_msg_add_attr(tdata->msg, &mi->hdr); + } +- ++ + } + + /* Send the request */ +- status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr, ++ status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, (pj_stun_session_tp_type(client->sess) == PJ_STUN_TP_UDP), &server->addr, + pj_sockaddr_get_len(&server->addr), tdata); + if (status != PJ_SUCCESS) { + destroy_client_server(); +@@ -596,7 +598,7 @@ static int run_client_test(const char *title, + PJ_LOG(3,(THIS_FILE, " err: expecting %d (%s) but got %d (%s) response", + expected_code, e1, client->response_status, e2)); + rc = -500; +- } ++ } + + } else { + int res_code = 0; +@@ -604,17 +606,17 @@ static int run_client_test(const char *title, + pj_stun_nonce_attr *anonce; + + if (client->response_status != 0) { +- PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d", ++ PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d", + client->response_status)); + rc = -600; + goto done; +- } ++ } + + if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) { + pj_stun_errcode_attr *aerr = NULL; + + aerr = (pj_stun_errcode_attr*) +- pj_stun_msg_find_attr(client->response, ++ pj_stun_msg_find_attr(client->response, + PJ_STUN_ATTR_ERROR_CODE, 0); + if (aerr == NULL) { + PJ_LOG(3,(THIS_FILE, " err: received error response without ERROR-CODE")); +@@ -747,8 +749,8 @@ static int long_term_check1(void) + + static int long_term_check2(void) + { +- /* response SHOULD NOT include a USERNAME, NONCE, REALM or +- * MESSAGE-INTEGRITY attribute. ++ /* response SHOULD NOT include a USERNAME, NONCE, REALM or ++ * MESSAGE-INTEGRITY attribute. + */ + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0)) + return -900; +@@ -851,7 +853,7 @@ int sess_auth_test(void) + } + + /* If the USERNAME does not contain a username value currently valid +- * within the server: If the message is a request, the server MUST ++ * within the server: If the message is a request, the server MUST + * reject the request with an error response. This response MUST use + * an error code of 401 (Unauthorized). + */ +@@ -1083,7 +1085,7 @@ int sess_auth_test(void) + * MUST include a NONCE and REALM attribute and SHOULD NOT incude the + * USERNAME or MESSAGE-INTEGRITY attribute. Servers can invalidate + * nonces in order to provide additional security. See Section 4.3 +- * of [RFC2617] for guidelines. ++ * of [RFC2617] for guidelines. + */ + // how?? + +diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c +index f44988aee..76bcb241e 100644 +--- a/pjnath/src/pjnath-test/stun_sock_test.c ++++ b/pjnath/src/pjnath-test/stun_sock_test.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include "test.h" + +@@ -65,9 +65,9 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, + pj_stun_msg *req_msg, *res_msg; + + pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL); +- ++ + /* Parse request */ +- status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, ++ status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, + PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, + &req_msg, NULL, NULL); + if (status != PJ_SUCCESS) { +@@ -89,8 +89,8 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, + if (srv->flag & WITH_MAPPED) { + pj_sockaddr addr; + pj_bool_t use_ipv6 = (srv->addr.addr.sa_family == pj_AF_INET6()); +- +- pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, ++ ++ pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, + srv->port_to_send); + + pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR, +@@ -98,17 +98,17 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, + } else if (srv->flag & WITH_XOR_MAPPED) { + pj_sockaddr addr; + pj_bool_t use_ipv6 = (srv->addr.addr.sa_family == pj_AF_INET6()); +- +- pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, ++ ++ pj_sockaddr_init(GET_AF(use_ipv6), &addr, &srv->ip_to_send, + srv->port_to_send); + +- pj_stun_msg_add_sockaddr_attr(pool, res_msg, ++ pj_stun_msg_add_sockaddr_attr(pool, res_msg, + PJ_STUN_ATTR_XOR_MAPPED_ADDR, + PJ_TRUE, &addr, sizeof(addr)); + } + + /* Encode */ +- status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0, ++ status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0, + NULL, &size); + if (status != PJ_SUCCESS) { + app_perror(" pj_stun_msg_encode()", status); +@@ -118,7 +118,7 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, + + /* Send back */ + sent = size; +- pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, ++ pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, + src_addr, addr_len); + + pj_pool_release(pool); +@@ -126,7 +126,7 @@ static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock, + } else if (srv->flag & ECHO) { + /* Send back */ + sent = size; +- pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, ++ pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, + src_addr, addr_len); + + } +@@ -156,7 +156,7 @@ static pj_status_t create_server(pj_pool_t *pool, + pj_bzero(&activesock_cb, sizeof(activesock_cb)); + activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom; + status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue, +- &activesock_cb, srv, &srv->asock, ++ &activesock_cb, srv, &srv->asock, + &srv->addr); + if (status != PJ_SUCCESS) + return status; +@@ -194,7 +194,7 @@ struct stun_client + unsigned on_rx_data_cnt; + }; + +-static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, ++static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status) + { +@@ -253,7 +253,7 @@ static pj_status_t create_client(pj_stun_config *cfg, + pj_bzero(&cb, sizeof(cb)); + cb.on_status = &stun_sock_on_status; + cb.on_rx_data = &stun_sock_on_rx_data; +- status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), &cb, &sock_cfg, ++ status = pj_stun_sock_create(cfg, NULL, GET_AF(use_ipv6), PJ_STUN_TP_UDP, &cb, &sock_cfg, + client, &client->sock); + if (status != PJ_SUCCESS) { + app_perror(" pj_stun_sock_create()", status); +@@ -298,7 +298,7 @@ static void handle_events(pj_stun_config *cfg, unsigned msec_delay) + /* + * Timeout test: scenario when no response is received from server + */ +-static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, ++static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, + pj_bool_t use_ipv6) + { + struct stun_srv *srv; +@@ -308,7 +308,7 @@ static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, + int i, ret = 0; + pj_status_t status; + +- PJ_LOG(3,(THIS_FILE, " timeout test [%d] - (%s)", destroy_on_err, ++ PJ_LOG(3,(THIS_FILE, " timeout test [%d] - (%s)", destroy_on_err, + (use_ipv6)?"IPv6":"IPv4")); + + status = create_client(cfg, &client, destroy_on_err, use_ipv6); +@@ -323,7 +323,7 @@ static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, + + srv_addr = (use_ipv6)?pj_str("::1"):pj_str("127.0.0.1"); + +- status = pj_stun_sock_start(client->sock, &srv_addr, ++ status = pj_stun_sock_start(client->sock, &srv_addr, + pj_sockaddr_get_port(&srv->addr), NULL); + if (status != PJ_SUCCESS) { + destroy_server(srv); +@@ -382,7 +382,7 @@ on_return: + * Invalid response scenario: when server returns no MAPPED-ADDRESS or + * XOR-MAPPED-ADDRESS attribute. + */ +-static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, ++static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, + pj_bool_t use_ipv6) + { + struct stun_srv *srv; +@@ -392,14 +392,14 @@ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, + int i, ret = 0; + pj_status_t status; + +- PJ_LOG(3,(THIS_FILE, " missing attribute test [%d] - (%s)", ++ PJ_LOG(3,(THIS_FILE, " missing attribute test [%d] - (%s)", + destroy_on_err, (use_ipv6)?"IPv6":"IPv4")); + + status = create_client(cfg, &client, destroy_on_err, use_ipv6); + if (status != PJ_SUCCESS) + return -110; + +- status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, use_ipv6, ++ status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, use_ipv6, + &srv); + if (status != PJ_SUCCESS) { + destroy_client(client); +@@ -407,8 +407,8 @@ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err, + } + srv_addr = (use_ipv6)?pj_str("::1"):pj_str("127.0.0.1"); + +- status = pj_stun_sock_start(client->sock, &srv_addr, +- pj_sockaddr_get_port(&srv->addr), NULL); ++ status = pj_stun_sock_start(client->sock, &srv_addr, ++ pj_sockaddr_get_port(&srv->addr), NULL); + if (status != PJ_SUCCESS) { + destroy_server(srv); + destroy_client(client); +@@ -467,14 +467,14 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + int i, ret = 0; + pj_status_t status; + +- PJ_LOG(3,(THIS_FILE, " normal operation - (%s)", ++ PJ_LOG(3,(THIS_FILE, " normal operation - (%s)", + (use_ipv6)?"IPv6":"IPv4")); + + status = create_client(cfg, &client, PJ_TRUE, use_ipv6); + if (status != PJ_SUCCESS) + return -310; + +- status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, ++ status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, + use_ipv6, &srv); + if (status != PJ_SUCCESS) { + destroy_client(client); +@@ -488,7 +488,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + + srv_addr = (use_ipv6)?pj_str("::1"):pj_str("127.0.0.1"); + +- status = pj_stun_sock_start(client->sock, &srv_addr, ++ status = pj_stun_sock_start(client->sock, &srv_addr, + pj_sockaddr_get_port(&srv->addr), NULL); + if (status != PJ_SUCCESS) { + destroy_server(srv); +@@ -545,7 +545,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + goto on_return; + } + /* verify the mapped address */ +- pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, ++ pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, + &srv->ip_to_send, srv->port_to_send); + if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { + PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); +@@ -583,7 +583,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3))); + } + status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret), +- 0, &info.srv_addr, ++ 0, &info.srv_addr, + pj_sockaddr_get_len(&info.srv_addr)); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + app_perror(" error: server sending data", status); +@@ -683,7 +683,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + srv->flag = RESPOND_STUN | WITH_XOR_MAPPED; + + /* Change mapped address in the response */ +- srv->ip_to_send = (use_ipv6)?pj_str("2002:202:202::"):pj_str("2.2.2.2"); ++ srv->ip_to_send = (use_ipv6)?pj_str("2002:202:202::"):pj_str("2.2.2.2"); + srv->port_to_send++; + + /* Reset server */ +@@ -754,7 +754,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + goto on_return; + } + /* verify the mapped address */ +- pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, ++ pj_sockaddr_init(GET_AF(use_ipv6), &mapped_addr, + &srv->ip_to_send, srv->port_to_send); + if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) { + PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched")); +@@ -779,7 +779,7 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) + * Part 5: Failed keep-alive + */ + PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario")); +- ++ + /* Change server operation mode to respond without attribute */ + srv->flag = RESPOND_STUN; + +@@ -864,7 +864,7 @@ int stun_sock_test(void) + ret = -8; + goto on_return; + } +- ++ + pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap); + + DO_TEST(timeout_test(&stun_cfg, PJ_FALSE, USE_IPV6)); +diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c +index 8afe4d181..d0cbb0ce5 100644 +--- a/pjnath/src/pjnath/ice_session.c ++++ b/pjnath/src/pjnath/ice_session.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,9 +14,10 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include ++#include + #include + #include + #include +@@ -28,6 +29,22 @@ + #include + #include + ++#if defined(_WIN32) || defined(__APPLE__) ++/* TODO(sblin): find an alternative for these paltforms */ ++#else ++/* The following headers are used to get DEPRECATED addresses ++ * as specified in RFC 2462 Section 5.5.4 ++ * https://tools.ietf.org/html/rfc2462#section-5.5.4 ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++ + /* String names for candidate types */ + static const char *cand_type_names[] = + { +@@ -40,10 +57,13 @@ static const char *cand_type_names[] = + + /* String names for pj_ice_sess_check_state */ + #if PJ_LOG_MAX_LEVEL >= 4 +-static const char *check_state_name[] = ++static const char *check_state_name[] = + { + "Frozen", ++ "Needs Retry", ++ "Needs First Packet", + "Waiting", ++ "Pending", + "In Progress", + "Succeeded", + "Failed" +@@ -57,7 +77,7 @@ static const char *clist_state_name[] = + }; + #endif /* PJ_LOG_MAX_LEVEL >= 4 */ + +-static const char *role_names[] = ++static const char *role_names[] = + { + "Unknown", + "Controlled", +@@ -68,13 +88,15 @@ enum timer_type + { + TIMER_NONE, /**< Timer not active */ + TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */ +- TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for ++ TIMER_CONTROLLING_TCP_PASSIVE_TIMEOUT, /** < Controlling agent is waiting for passive TCP connection timeout **/ ++ TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for + controlling agent to send connectivity + check with nominated flag after it has + valid check for every components. */ + TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity + checks with USE-CANDIDATE flag. */ +- TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ ++ TIMER_KEEP_ALIVE, /**< ICE keep-alive timer. */ ++ TIMER_CONNECTION_TIMEOUT + + }; + +@@ -122,6 +144,8 @@ typedef struct timer_data + { + pj_ice_sess *ice; + pj_ice_sess_checklist *clist; ++ /* TODO (remove), for now, needed for the NEEDS_FIRST_PACKET state */ ++ unsigned first_packet_counter; + } timer_data; + + +@@ -132,15 +156,16 @@ typedef struct timer_data + + /* Forward declarations */ + static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te); ++static void on_tcp_connect_timeout(pj_ice_sess *ice); + static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); + static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now); + static void ice_on_destroy(void *obj); + static void destroy_ice(pj_ice_sess *ice, + pj_status_t reason); +-static pj_status_t start_periodic_check(pj_timer_heap_t *th, ++static pj_status_t start_periodic_check(pj_timer_heap_t *th, + pj_timer_entry *te); + static void start_nominated_check(pj_ice_sess *ice); +-static void periodic_timer(pj_timer_heap_t *th, ++static void periodic_timer(pj_timer_heap_t *th, + pj_timer_entry *te); + static void handle_incoming_check(pj_ice_sess *ice, + const pj_ice_rx_check *rcheck); +@@ -190,7 +215,7 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, + pj_stun_passwd_type *data_type, + pj_str_t *data); + static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, +- void *user_data, ++ void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + pj_pool_t *pool, +@@ -289,10 +314,11 @@ static pj_status_t init_comp(pj_ice_sess *ice, + sess_cb.on_send_msg = &on_stun_send_msg; + + /* Create STUN session for this candidate */ +- status = pj_stun_session_create(&ice->stun_cfg, NULL, ++ status = pj_stun_session_create(&ice->stun_cfg, NULL, + &sess_cb, PJ_TRUE, + ice->grp_lock, +- &comp->stun_sess); ++ &comp->stun_sess, ++ PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) + return status; + +@@ -322,9 +348,10 @@ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt) + { + opt->aggressive = PJ_TRUE; + opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY; +- opt->controlled_agent_want_nom_timeout = ++ opt->controlled_agent_want_nom_timeout = + ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; + opt->trickle = PJ_ICE_SESS_TRICKLE_DISABLED; ++ opt->agent_passive_timeout = ICE_CONTROLLING_PASSIVE_TIMEOUT; + } + + /* +@@ -350,7 +377,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, + if (name == NULL) + name = "icess%p"; + +- pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, ++ pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, + PJNATH_POOL_INC_ICE_SESS, NULL); + ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess); + ice->pool = pool; +@@ -361,6 +388,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, + pj_ice_sess_options_default(&ice->opt); + + pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer); ++ pj_timer_entry_init(&ice->timer_connect, TIMER_NONE, (void*)ice, &on_timer); + + pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name), + name, ice); +@@ -425,7 +453,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, + /* Done */ + *p_ice = ice; + +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "ICE session created, comp_cnt=%d, role is %s agent", + comp_cnt, role_names[ice->role])); + +@@ -507,6 +535,9 @@ static void destroy_ice(pj_ice_sess *ice, + pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, + &ice->timer, PJ_FALSE); + ++ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, ++ &ice->timer_connect, TIMER_NONE); ++ + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].stun_sess) { + pj_stun_session_destroy(ice->comp[i].stun_sess); +@@ -551,7 +582,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_detach_grp_lock(pj_ice_sess *ice, + + + /* +- * Change session role. ++ * Change session role. + */ + PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, + pj_ice_sess_role new_role) +@@ -651,7 +682,7 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, + + /* Get password to be used to authenticate incoming message */ + static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, +- void *user_data, ++ void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + pj_pool_t *pool, +@@ -680,8 +711,8 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, + /* The agent MUST accept a credential if the username consists + * of two values separated by a colon, where the first value is + * equal to the username fragment generated by the agent in an offer +- * or answer for a session in-progress, and the MESSAGE-INTEGRITY +- * is the output of a hash of the password and the STUN packet's ++ * or answer for a session in-progress, and the MESSAGE-INTEGRITY ++ * is the output of a hash of the password and the STUN packet's + * contents. + */ + const char *pos; +@@ -712,7 +743,7 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, + pj_uint32_t comp_id) + { + #if PJNATH_ICE_PRIO_STD +- return ((ice->prefs[type] & 0xFF) << 24) + ++ return ((ice->prefs[type] & 0xFF) << 24) + + ((local_pref & 0xFFFF) << 8) + + (((256 - comp_id) & 0xFF) << 0); + #else +@@ -728,12 +759,151 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, + max_comp = (2<prefs[type] & type_mask) << type_shift) + ++ return ((ice->prefs[type] & type_mask) << type_shift) + + ((local_pref & local_mask) << local_shift) + + (((max_comp - comp_id) & comp_mask) << comp_shift); + #endif + } + ++/* retrieve invalid addresses and store it in a string */ ++static PJ_DEF(void) get_invalid_addresses(char** addresses, size_t* size) ++{ ++#if defined(_WIN32) || defined(__APPLE__) ++ // PJ_TODO("sblin: find alternative for WIN32 and APPLE"); ++#else ++ struct { ++ struct nlmsghdr nlmsg_info; ++ struct ifaddrmsg ifaddrmsg_info; ++ } netlink_req; ++ ++ int fd; ++ ++ long pagesize = sysconf(_SC_PAGESIZE); ++ ++ if (!pagesize) ++ pagesize = 4096; /* Assume pagesize is 4096 if sysconf() failed */ ++ ++ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if(fd < 0) { ++ perror("socket initialization error: abort"); ++ return; ++ } ++ ++ int rtn; ++ ++ bzero(&netlink_req, sizeof(netlink_req)); ++ ++ netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); ++ netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; ++ netlink_req.nlmsg_info.nlmsg_type = RTM_GETADDR; ++ netlink_req.nlmsg_info.nlmsg_pid = getpid(); ++ ++ netlink_req.ifaddrmsg_info.ifa_family = AF_INET6; ++ ++ rtn = send(fd, &netlink_req, netlink_req.nlmsg_info.nlmsg_len, 0); ++ if(rtn < 0) { ++ perror("send error: abort"); ++ return; ++ } ++ ++ char read_buffer[pagesize]; ++ struct nlmsghdr *nlmsg_ptr; ++ int nlmsg_len; ++ ++ size_t idx = 0; ++ /* Will store all deprecated addresses into a string */ ++ char* deprecatedAddrs = malloc(256*sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ if (!deprecatedAddrs) { ++ perror("malloc error: abort"); ++ return; ++ } ++ ++ while(1) { ++ int rtn; ++ ++ bzero(read_buffer, pagesize); ++ rtn = recv(fd, read_buffer, pagesize, 0); ++ if(rtn < 0) { ++ perror ("recv(): "); ++ free(deprecatedAddrs); ++ return; ++ } ++ ++ nlmsg_ptr = (struct nlmsghdr *) read_buffer; ++ nlmsg_len = rtn; ++ ++ if (nlmsg_len < sizeof (struct nlmsghdr)) { ++ perror ("Received an incomplete netlink packet"); ++ free(deprecatedAddrs); ++ return; ++ } ++ ++ for(; NLMSG_OK(nlmsg_ptr, nlmsg_len); ++ nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len)) ++ { ++ if (nlmsg_ptr->nlmsg_type == NLMSG_DONE) ++ goto nlmsg_done; ++ ++ struct ifaddrmsg *ifaddrmsg_ptr; ++ struct rtattr *rtattr_ptr; ++ int ifaddrmsg_len; ++ ++ ifaddrmsg_ptr = (struct ifaddrmsg *) NLMSG_DATA(nlmsg_ptr); ++ ++ if (ifaddrmsg_ptr->ifa_flags & IFA_F_DEPRECATED || ++ ifaddrmsg_ptr->ifa_flags & IFA_F_TENTATIVE) ++ { ++ rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr); ++ ifaddrmsg_len = IFA_PAYLOAD(nlmsg_ptr); ++ ++ for(;RTA_OK(rtattr_ptr, ifaddrmsg_len); ++ rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) ++ { ++ switch(rtattr_ptr->rta_type) { ++ case IFA_ADDRESS: ++ /* Any 256 obsolete ips (should not happen), resize the array. */ ++ if (idx > 0 && idx % 256 == 0) { ++ char* newDeprecated = realloc(deprecatedAddrs, ++ (idx + 256)*sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ if (newDeprecated == NULL) { ++ perror("realloc error: abort"); ++ free(deprecatedAddrs); ++ return; ++ } ++ deprecatedAddrs = newDeprecated; ++ } ++ /* Store deprecated IP */ ++ inet_ntop(ifaddrmsg_ptr->ifa_family, ++ RTA_DATA(rtattr_ptr), ++ &deprecatedAddrs[idx*PJ_INET6_ADDRSTRLEN], ++ sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ ++idx; ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++nlmsg_done: ++ close(fd); ++ *size = idx; ++ if (idx > 0) { ++ char *final = realloc(deprecatedAddrs, ++ idx*sizeof(char)*PJ_INET6_ADDRSTRLEN); ++ if (final) { ++ *addresses = final; ++ } else { ++ perror("realloc error: abort"); ++ free(deprecatedAddrs); ++ } ++ } else { ++ free(deprecatedAddrs); ++ } ++#endif ++} + + /* + * Add ICE candidate +@@ -748,14 +918,38 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + const pj_sockaddr_t *base_addr, + const pj_sockaddr_t *rel_addr, + int addr_len, +- unsigned *p_cand_id) ++ unsigned *p_cand_id, ++ pj_ice_cand_transport transport) + { ++ /** ++ * RFC 2466: an ip address can have the status DEPRECATED and SHOULD NOT ++ * be used by new by applications unless they already use it. ++ * So, we should ignore these addresses. ++ * Also, ips with the TENTATIVE state are not ready and SHOULD NOT be ++ * used for now. Ignore these addresses too. ++ */ ++ char* deprecatedAddrs = NULL; ++ size_t size = 0; ++ get_invalid_addresses(&deprecatedAddrs, &size); ++ if (deprecatedAddrs != NULL) { ++ char tmpAddrStr[PJ_INET6_ADDRSTRLEN]; ++ pj_sockaddr_print(addr, tmpAddrStr, sizeof(tmpAddrStr), 0); ++ for (int i = 0; icomp_cnt, PJ_EINVAL); +@@ -794,6 +988,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + lcand->comp_id = (pj_uint8_t)comp_id; + lcand->transport_id = (pj_uint8_t)transport_id; + lcand->type = type; ++ lcand->transport = transport; + pj_strdup(ice->pool, &lcand->foundation, foundation); + lcand->local_pref = local_pref; + lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); +@@ -821,15 +1016,15 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, + pj_ansi_strxcpy(ice->tmp.txt, pj_sockaddr_print(&lcand->addr, address, + sizeof(address), 2), + sizeof(ice->tmp.txt)); +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, " + "addr=%s:%d, base=%s:%d, prio=0x%x (%u)", + lcand->id, +- lcand->comp_id, ++ lcand->comp_id, + cand_type_names[lcand->type], + (int)lcand->foundation.slen, + lcand->foundation.ptr, +- ice->tmp.txt, ++ ice->tmp.txt, + pj_sockaddr_get_port(&lcand->addr), + pj_sockaddr_print(&lcand->base_addr, address, sizeof(address), 2), + pj_sockaddr_get_port(&lcand->base_addr), +@@ -863,7 +1058,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, + /* First find in valid list if we have nominated pair */ + for (i=0; ivalid_list.count; ++i) { + pj_ice_sess_check *check = &ice->valid_list.checks[i]; +- ++ + if (check->lcand->comp_id == comp_id) { + *cand_id = GET_LCAND_ID(check->lcand); + pj_grp_lock_release(ice->grp_lock); +@@ -875,7 +1070,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, + for (i=0; ilcand_cnt; ++i) { + pj_ice_sess_cand *lcand = &ice->lcand[i]; + if (lcand->comp_id==comp_id && +- lcand->type == PJ_ICE_CAND_TYPE_RELAYED) ++ lcand->type == PJ_ICE_CAND_TYPE_RELAYED) + { + *cand_id = GET_LCAND_ID(lcand); + pj_grp_lock_release(ice->grp_lock); +@@ -888,7 +1083,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, + pj_ice_sess_cand *lcand = &ice->lcand[i]; + if (lcand->comp_id==comp_id && + (lcand->type == PJ_ICE_CAND_TYPE_SRFLX || +- lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) ++ lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) + { + *cand_id = GET_LCAND_ID(lcand); + pj_grp_lock_release(ice->grp_lock); +@@ -900,7 +1095,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, + for (i=0; ilcand_cnt; ++i) { + pj_ice_sess_cand *lcand = &ice->lcand[i]; + if (lcand->comp_id==comp_id && +- lcand->type == PJ_ICE_CAND_TYPE_HOST) ++ lcand->type == PJ_ICE_CAND_TYPE_HOST) + { + *cand_id = GET_LCAND_ID(lcand); + pj_grp_lock_release(ice->grp_lock); +@@ -924,7 +1119,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, + # define MAX(a,b) (a > b ? a : b) + #endif + +-static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, ++static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, + const pj_ice_sess_cand *lcand, + const pj_ice_sess_cand *rcand) + { +@@ -936,7 +1131,7 @@ static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, + */ + + if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { +- O = lcand->prio; ++ O = lcand->prio; + A = rcand->prio; + } else { + O = rcand->prio; +@@ -1013,7 +1208,7 @@ static const char *dump_check(char *buffer, unsigned bufsize, + return buffer; + } + +-static void dump_checklist(const char *title, pj_ice_sess *ice, ++static void dump_checklist(const char *title, pj_ice_sess *ice, + const pj_ice_sess_checklist *clist) + { + unsigned i; +@@ -1023,7 +1218,7 @@ static void dump_checklist(const char *title, pj_ice_sess *ice, + const pj_ice_sess_check *c = &clist->checks[i]; + LOG4((ice->obj_name, " %s (%s, state=%s)", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, c), +- (c->nominated ? "nominated" : "not nominated"), ++ (c->nominated ? "nominated" : "not nominated"), + check_state_name[c->state])); + } + } +@@ -1033,9 +1228,12 @@ static void dump_checklist(const char *title, pj_ice_sess *ice, + #endif + + static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check, +- pj_ice_sess_check_state st, ++ pj_ice_sess_check_state st, + pj_status_t err_code) + { ++ if (check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) ++ return; ++ + LOG5((ice->obj_name, "Check %s: state changed from %s to %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), + check_state_name[check->state], +@@ -1102,9 +1300,9 @@ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) + unsigned k; + + pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_sess_check)); +- pj_memcpy(&clist->checks[i], &clist->checks[highest], ++ pj_memcpy(&clist->checks[i], &clist->checks[highest], + sizeof(pj_ice_sess_check)); +- pj_memcpy(&clist->checks[highest], &tmp, ++ pj_memcpy(&clist->checks[highest], &tmp, + sizeof(pj_ice_sess_check)); + + /* Update valid and nominated check pointers, since we're moving +@@ -1138,7 +1336,7 @@ static void remove_check(pj_ice_sess *ice, pj_ice_sess_checklist *clist, + /* Prune checklist, this must have been done after the checklist + * is sorted. + */ +-static pj_status_t prune_checklist(pj_ice_sess *ice, ++static pj_status_t prune_checklist(pj_ice_sess *ice, + pj_ice_sess_checklist *clist) + { + unsigned i; +@@ -1151,7 +1349,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, + * the list. This is done by removing a pair if its local and remote + * candidates are identical to the local and remote candidates of a pair + * higher up on the priority list. The result is a sequence of ordered +- * candidate pairs, called the check list for that media stream. ++ * candidate pairs, called the check list for that media stream. + */ + /* First replace SRFLX candidates with their base */ + for (i=0; icount; ++i) { +@@ -1178,7 +1376,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, + if (j==ice->lcand_cnt) { + char baddr[PJ_INET6_ADDRSTRLEN]; + /* Host candidate not found this this srflx! */ +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Base candidate %s:%d not found for srflx candidate %d", + pj_sockaddr_print(&srflx->base_addr, baddr, + sizeof(baddr), 2), +@@ -1187,6 +1385,15 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, + return PJNATH_EICENOHOSTCAND; + } + } ++ ++ /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) ++ * When the agent prunes the check list, it MUST also remove any pair ++ * for which the local candidate is a passive TCP candidate ++ */ ++ if (clist->checks[i].lcand->transport == PJ_CAND_TCP_PASSIVE) { ++ remove_check(ice, clist, i, "local passive TCP"); ++ i--; ++ } + } + + /* Next remove a pair if its local and remote candidates are identical +@@ -1218,8 +1425,8 @@ static pj_status_t prune_checklist(pj_ice_sess *ice, + if ((licand == ljcand) && (ricand == rjcand)) { + reason = "duplicate found"; + } else if ((rjcand == ricand) && +- (pj_sockaddr_cmp(&ljcand->base_addr, +- &licand->base_addr)==0)) ++ (pj_sockaddr_cmp(&ljcand->base_addr, ++ &licand->base_addr)==0)) + { + reason = "equal base"; + } +@@ -1256,8 +1463,13 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) + } + + switch (type) { ++ case TIMER_CONTROLLING_TCP_PASSIVE_TIMEOUT: ++ LOG4((ice->obj_name, ++ "Controlling agent timed-out while waiting for incoming TCP checks. Set state to failed!")); ++ on_ice_complete(ice, PJNATH_EICEFAILED); ++ break; + case TIMER_CONTROLLED_WAIT_NOM: +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Controlled agent timed-out in waiting for the controlling " + "agent to send nominated check. Setting state to fail now..")); + on_ice_complete(ice, PJNATH_EICENOMTIMEOUT); +@@ -1289,6 +1501,9 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) + case TIMER_KEEP_ALIVE: + ice_keep_alive(ice, PJ_TRUE); + break; ++ case TIMER_CONNECTION_TIMEOUT: ++ on_tcp_connect_timeout(ice); ++ break; + case TIMER_NONE: + /* Nothing to do, just to get rid of gcc warning */ + break; +@@ -1315,7 +1530,7 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) + the_check = comp->nominated_check; + + /* Create the Binding Indication */ +- status = pj_stun_session_create_ind(comp->stun_sess, ++ status = pj_stun_session_create_ind(comp->stun_sess, + PJ_STUN_BINDING_INDICATION, + &tdata); + if (status != PJ_SUCCESS) +@@ -1335,9 +1550,15 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) + /* Send to session */ + addr_len = pj_sockaddr_get_len(&the_check->rcand->addr); + status = pj_stun_session_send_msg(comp->stun_sess, msg_data, +- PJ_FALSE, PJ_FALSE, +- &the_check->rcand->addr, ++ PJ_FALSE, PJ_FALSE, ++ &the_check->rcand->addr, + addr_len, tdata); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING && status != PJ_EBUSY) { ++ if (ice->cb.on_ice_destroy) { ++ ice->cb.on_ice_destroy(ice); ++ } ++ return; ++ } + + /* Restore FINGERPRINT usage */ + pj_stun_session_use_fingerprint(comp->stun_sess, saved); +@@ -1349,8 +1570,8 @@ done: + if (ice->timer.id == TIMER_NONE) { + pj_time_val delay = { 0, 0 }; + +- delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + +- (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / ++ delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + ++ (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / + ice->comp_cnt; + pj_time_val_normalize(&delay); + +@@ -1370,13 +1591,13 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) + if (!ice->is_complete) { + ice->is_complete = PJ_TRUE; + ice->ice_status = status; +- ++ + pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, + TIMER_NONE); + + /* Log message */ +- LOG4((ice->obj_name, "ICE process complete, status=%s", +- pj_strerror(status, ice->tmp.errmsg, ++ LOG4((ice->obj_name, "ICE process complete, status=%s", ++ pj_strerror(status, ice->tmp.errmsg, + sizeof(ice->tmp.errmsg)).ptr)); + + dump_checklist("Valid list", ice, &ice->valid_list); +@@ -1394,7 +1615,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) + } + + /* Update valid check and nominated check for the candidate */ +-static void update_comp_check(pj_ice_sess *ice, unsigned comp_id, ++static void update_comp_check(pj_ice_sess *ice, unsigned comp_id, + pj_ice_sess_check *check) + { + pj_ice_sess_comp *comp; +@@ -1443,18 +1664,18 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + pj_bool_t no_pending_check = PJ_FALSE; + + /* Still in 8.2. Updating States +- * ++ * + * o Once there is at least one nominated pair in the valid list for + * every component of at least one media stream and the state of the + * check list is Running: +- * ++ * + * * The agent MUST change the state of processing for its check + * list for that media stream to Completed. +- * ++ * + * * The agent MUST continue to respond to any checks it may still + * receive for that media stream, and MUST perform triggered + * checks if required by the processing of Section 7.2. +- * ++ * + * * The agent MAY begin transmitting media for this media stream as + * described in Section 11.1 + */ +@@ -1474,28 +1695,28 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + + /* Note: this is the stuffs that we don't do in 7.1.2.2.2, since our + * ICE session only supports one media stream for now: +- * ++ * + * 7.1.2.2.2. Updating Pair States + * + * 2. If there is a pair in the valid list for every component of this + * media stream (where this is the actual number of components being + * used, in cases where the number of components signaled in the SDP + * differs from offerer to answerer), the success of this check may +- * unfreeze checks for other media streams. ++ * unfreeze checks for other media streams. + */ + + /* 7.1.2.3. Check List and Timer State Updates + * Regardless of whether the check was successful or failed, the + * completion of the transaction may require updating of check list and + * timer states. +- * ++ * + * If all of the pairs in the check list are now either in the Failed or + * Succeeded state, and 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. ++ * Failed. + */ + +- /* ++ /* + * See if all checks in the checklist have completed. If we do, + * then mark ICE processing as failed. + */ +@@ -1508,14 +1729,64 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + } + no_pending_check = (i == ice->clist.count); + } ++#if PJ_HAS_TCP ++ pj_bool_t hasTCP = PJ_FALSE; ++#endif ++ for (i=0; iclist.count; ++i) { ++ pj_ice_sess_check *c = &ice->clist.checks[i]; ++ ++#if PJ_HAS_TCP ++ if (c && c->lcand && ++ ( ++ c->lcand->transport == PJ_CAND_TCP_ACTIVE ++ )) { ++ hasTCP = PJ_TRUE; ++ } ++#endif ++ } + + if (no_pending_check) { ++#if PJ_HAS_TCP ++ if (hasTCP) { ++ // STUN server procedure https://tools.ietf.org/html/rfc6544#section-7.2 ++ // An ICE TCP agent, full or lite, MUST be prepared to receive incoming ++ // TCP connection requests on the base of any TCP candidate that is ++ // simultaneous-open or passive. When the connection request is ++ // received, the agent MUST accept it. ++ // https://tools.ietf.org/html/rfc5245#section-2.6 ++ // In that case, allowing ICE to run a little longer might produce ++ // better results. ++ if (ice->timer.id == TIMER_NONE && ++ ice->opt.agent_passive_timeout >= 0) ++ { ++ pj_time_val delay; ++ ++ delay.sec = 0; ++ delay.msec = ice->opt.agent_passive_timeout; ++ pj_time_val_normalize(&delay); ++ ++ pj_timer_heap_schedule_w_grp_lock( ++ ice->stun_cfg.timer_heap, ++ &ice->timer, &delay, ++ TIMER_CONTROLLING_TCP_PASSIVE_TIMEOUT, ++ ice->grp_lock); ++ ++ LOG5((ice->obj_name, ++ "All checks have completed but failed. Just " ++ "wait for passive connections to timeout " ++ "(timeout=%d msec)", ++ ice->opt.agent_passive_timeout)); ++ return PJ_FALSE; ++ } ++ } ++#endif ++ + /* All checks have completed, but we don't have nominated pair. +- * If agent's role is controlled, check if all components have +- * valid pair. If it does, this means the controlled agent has +- * finished the check list and it's waiting for controlling +- * agent to send checks with USE-CANDIDATE flag set. +- */ ++ * If agent's role is controlled, check if all components have ++ * valid pair. If it does, this means the controlled agent has ++ * finished the check list and it's waiting for controlling ++ * agent to send checks with USE-CANDIDATE flag set. ++ */ + if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) { + for (i=0; i < ice->comp_cnt; ++i) { + if (ice->comp[i].valid_check == NULL) +@@ -1524,16 +1795,16 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + + if (i < ice->comp_cnt) { + /* This component ID doesn't have valid pair. +- * Mark ICE as failed. +- */ ++ * Mark ICE as failed. ++ */ + on_ice_complete(ice, PJNATH_EICEFAILED); + return PJ_TRUE; + } else { + /* All components have a valid pair. +- * We should wait until we receive nominated checks. +- */ ++ * We should wait until we receive nominated checks. ++ */ + if (ice->timer.id == TIMER_NONE && +- ice->opt.controlled_agent_want_nom_timeout >= 0) ++ ice->opt.controlled_agent_want_nom_timeout >= 0) + { + pj_time_val delay; + +@@ -1547,11 +1818,11 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + TIMER_CONTROLLED_WAIT_NOM, + ice->grp_lock); + +- LOG5((ice->obj_name, +- "All checks have completed. Controlled agent now " +- "waits for nomination from controlling agent " +- "(timeout=%d msec)", +- ice->opt.controlled_agent_want_nom_timeout)); ++ LOG5((ice->obj_name, ++ "All checks have completed. Controlled agent now " ++ "waits for nomination from controlling agent " ++ "(timeout=%d msec)", ++ ice->opt.controlled_agent_want_nom_timeout)); + } + return PJ_FALSE; + } +@@ -1560,17 +1831,16 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + + } else if (ice->is_nominating) { + /* We are controlling agent and all checks have completed but +- * there's at least one component without nominated pair (or +- * more likely we don't have any nominated pairs at all). +- */ ++ * there's at least one component without nominated pair (or ++ * more likely we don't have any nominated pairs at all). ++ */ + on_ice_complete(ice, PJNATH_EICEFAILED); + return PJ_TRUE; +- + } else { + /* We are controlling agent and all checks have completed. If +- * we have valid list for every component, then move on to +- * sending nominated check, otherwise we have failed. +- */ ++ * we have valid list for every component, then move on to ++ * sending nominated check, otherwise we have failed. ++ */ + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].valid_check == NULL) + break; +@@ -1578,17 +1848,17 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + + if (i < ice->comp_cnt) { + /* At least one component doesn't have a valid check. Mark +- * ICE as failed. +- */ ++ * ICE as failed. ++ */ + on_ice_complete(ice, PJNATH_EICEFAILED); + return PJ_TRUE; + } + +- /* Now it's time to send connectivity check with nomination +- * flag set. +- */ +- LOG4((ice->obj_name, +- "All checks have completed, starting nominated checks now")); ++ /* Now it's time to send connectivity check with nomination ++ * flag set. ++ */ ++ LOG4((ice->obj_name, ++ "All checks have completed, starting nominated checks now")); + start_nominated_check(ice); + return PJ_FALSE; + } +@@ -1602,7 +1872,7 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + if (/*check->err_code == PJ_SUCCESS && */ + ice->role==PJ_ICE_SESS_ROLE_CONTROLLING && + !ice->is_nominating && +- ice->timer.id == TIMER_NONE) ++ ice->timer.id == TIMER_NONE) + { + pj_time_val delay; + +@@ -1618,7 +1888,7 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice) + return PJ_FALSE; + } + +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Scheduling nominated check in %d ms", + ice->opt.nominated_check_delay)); + +@@ -1655,12 +1925,12 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + comp = find_comp(ice, check->lcand->comp_id); + + /* 7.1.2.2.2. Updating Pair States +- * ++ * + * The agent sets the state of the pair that generated the check to + * Succeeded. The success of this check might also cause the state of + * other checks to change as well. The agent MUST perform the following + * two steps: +- * ++ * + * 1. The agent changes the states for all other Frozen pairs for the + * same media stream and same foundation to Waiting. Typically + * these other pairs will have different component IDs but not +@@ -1692,7 +1962,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + } + + /* 8.2. Updating States +- * ++ * + * For both controlling and controlled agents, the state of ICE + * processing depends on the presence of nominated candidate pairs in + * the valid list and on the state of the check list: +@@ -1723,10 +1993,10 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + if (c->state < PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { + + /* Just fail Frozen/Waiting check */ +- LOG5((ice->obj_name, ++ LOG5((ice->obj_name, + "Check %s to be failed because state is %s", +- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), +- &ice->clist, c), ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), ++ &ice->clist, c), + check_state_name[c->state])); + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, + PJ_ECANCELLED); +@@ -1737,11 +2007,11 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + + /* State is IN_PROGRESS, cancel transaction */ + if (c->tdata) { +- LOG5((ice->obj_name, ++ LOG5((ice->obj_name, + "Cancelling check %s (In Progress)", +- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, c))); +- pj_stun_session_cancel_req(comp->stun_sess, ++ pj_stun_session_cancel_req(comp->stun_sess, + c->tdata, PJ_FALSE, 0); + c->tdata = NULL; + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, +@@ -1755,6 +2025,44 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + return check_ice_complete(ice); + } + ++static void on_tcp_connect_timeout(pj_ice_sess* ice) ++{ ++ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,&ice->timer_connect, ++ TIMER_NONE); ++ ++ pj_bool_t first_found = PJ_FALSE, set_timer = PJ_FALSE; ++ ++ for (int i = 0; iclist.count && !set_timer; ++i) { ++ pj_ice_sess_check *check = &ice->clist.checks[i]; ++ if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { ++ if (first_found) { ++ set_timer = PJ_TRUE; ++ } else { ++ first_found = PJ_TRUE; ++ if (*ice->cb.close_tcp_connection) ++ (*ice->cb.close_tcp_connection)(ice, i); ++ ++ check_set_state(ice, check, ++ PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); ++ on_check_complete(ice, check); ++ } ++ } ++ } ++ ++ if (set_timer && ice->timer_connect.id == TIMER_NONE) { ++ /* Reschedule */ ++ pj_time_val delay = { ++ .sec = 15, ++ .msec = 0 ++ }; ++ pj_time_val_normalize(&delay); ++ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, ++ &ice->timer_connect, &delay, ++ TIMER_CONNECTION_TIMEOUT, ++ ice->grp_lock); ++ } ++} ++ + + /* Get foundation index of a check pair. This function can also be used for + * adding a new foundation (combination of local & remote cands foundations) +@@ -1876,7 +2184,7 @@ static pj_status_t add_rcand_and_update_checklist( + if (j < ice->rcand_cnt) + continue; + } +- ++ + /* Available cand slot? */ + if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { + char tmp[PJ_INET6_ADDRSTRLEN + 10]; +@@ -1912,10 +2220,10 @@ static pj_status_t add_rcand_and_update_checklist( + if (discard_check(ice, clist, &max_prio) == 0) + continue; + } +- ++ + /* A local candidate is paired with a remote candidate if +- * and only if the two candidates have the same component ID +- * and have the same IP address version. ++ * and only if the two candidates have the same component ID ++ * and have the same IP address version. + */ + if ((lcand->comp_id != rcand->comp_id) || + (lcand->addr.addr.sa_family != rcand->addr.addr.sa_family)) +@@ -1923,6 +2231,29 @@ static pj_status_t add_rcand_and_update_checklist( + continue; + } + ++ /* Section 6.2, RFC 6544 (https://tools.ietf.org/html/rfc6544) ++ * As with UDP, check lists are formed only by full ICE implementations. ++ * When forming candidate pairs, the following types of TCP candidates ++ * can be paired with each other: ++ * ++ * Local Remote ++ * Candidate Candidate ++ * --------------------------- ++ * tcp-so tcp-so ++ * tcp-active tcp-passive ++ * tcp-passive tcp-active ++ */ ++ if ((lcand->transport == PJ_CAND_UDP && ++ rcand->transport != PJ_CAND_UDP) || ++ (lcand->transport == PJ_CAND_TCP_PASSIVE && ++ rcand->transport != PJ_CAND_TCP_ACTIVE) || ++ (lcand->transport == PJ_CAND_TCP_ACTIVE && ++ rcand->transport != PJ_CAND_TCP_PASSIVE) || ++ (lcand->transport == PJ_CAND_TCP_SO && ++ rcand->transport != PJ_CAND_TCP_SO)) ++ { ++ continue; ++ } + #if 0 + /* Trickle ICE: + * Make sure that pair has not been added to checklist +@@ -1952,6 +2283,9 @@ static pj_status_t add_rcand_and_update_checklist( + chk->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; + chk->foundation_idx = get_check_foundation_idx(ice, lcand, rcand, + PJ_TRUE); ++#if PJ_HAS_TCP ++ chk->reconnect_count = 0; ++#endif + + /* Check if foundation cannot be added (e.g: list is full) */ + if (chk->foundation_idx < 0) +@@ -2139,6 +2473,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( + td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); + td->ice = ice; + td->clist = clist; ++ td->first_packet_counter = 1; + clist->timer.user_data = (void*)td; + clist->timer.cb = &periodic_timer; + +@@ -2196,7 +2531,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_update_check_list( + pj_grp_lock_release(ice->grp_lock); + return PJ_SUCCESS; + } +- ++ + /* Verify remote ufrag & passwd, if remote candidate specified */ + if (rem_cand_cnt && (pj_strcmp(&ice->tx_ufrag, rem_ufrag) || + pj_strcmp(&ice->tx_pass, rem_passwd))) +@@ -2227,8 +2562,38 @@ PJ_DEF(pj_status_t) pj_ice_sess_update_check_list( + return status; + } + ++static pj_status_t send_connectivity_check(pj_ice_sess *ice, ++ pj_ice_sess_checklist *clist, ++ unsigned check_id, ++ pj_bool_t nominate, ++ pj_ice_msg_data *msg_data) ++{ ++ pj_ice_sess_check *check; ++ const pj_ice_sess_cand *lcand; ++ const pj_ice_sess_cand *rcand; ++ pj_ice_sess_comp *comp; ++ ++ check = &clist->checks[check_id]; ++ lcand = check->lcand; ++ rcand = check->rcand; ++ comp = find_comp(ice, lcand->comp_id); ++ ++ /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the ++ * STUN session. ++ */ ++ ++ /* Initiate STUN transaction to send the request */ ++ ++ return pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, ++ pj_stun_session_tp_type(comp->stun_sess)== ++ PJ_STUN_TP_UDP, ++ &rcand->addr, ++ pj_sockaddr_get_len(&rcand->addr), ++ check->tdata); ++} ++ + /* Perform check on the specified candidate pair. */ +-static pj_status_t perform_check(pj_ice_sess *ice, ++static pj_status_t perform_check(pj_ice_sess *ice, + pj_ice_sess_checklist *clist, + unsigned check_id, + pj_bool_t nominate) +@@ -2237,22 +2602,20 @@ static pj_status_t perform_check(pj_ice_sess *ice, + pj_ice_msg_data *msg_data; + pj_ice_sess_check *check; + const pj_ice_sess_cand *lcand; +- const pj_ice_sess_cand *rcand; + pj_uint32_t prio; + pj_status_t status; + + check = &clist->checks[check_id]; + lcand = check->lcand; +- rcand = check->rcand; + comp = find_comp(ice, lcand->comp_id); + +- LOG5((ice->obj_name, +- "Sending connectivity check for check %s", ++ LOG5((ice->obj_name, ++ "Sending connectivity check for check %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); + pj_log_push_indent(); + + /* Create request */ +- status = pj_stun_session_create_req(comp->stun_sess, ++ status = pj_stun_session_create_req(comp->stun_sess, + PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, + NULL, &check->tdata); + if (status != PJ_SUCCESS) { +@@ -2282,7 +2645,7 @@ static pj_status_t perform_check(pj_ice_sess *ice, + ((1 << PJ_ICE_LOCAL_PREF_BITS) - 1) - lcand->id, + lcand->comp_id); + #endif +- pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, ++ pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_PRIORITY, prio); + + /* Add USE-CANDIDATE and set this check to nominated. +@@ -2295,44 +2658,84 @@ static pj_status_t perform_check(pj_ice_sess *ice, + check->nominated = PJ_TRUE; + } + +- pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, ++ pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_ICE_CONTROLLING, + &ice->tie_breaker); + + } else { +- pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, +- PJ_STUN_ATTR_ICE_CONTROLLED, +- &ice->tie_breaker); +- } +- + +- /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the +- * STUN session. +- */ ++ pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, ++ PJ_STUN_ATTR_ICE_CONTROLLED, ++ &ice->tie_breaker); ++ } + +- /* Initiate STUN transaction to send the request */ +- status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, +- PJ_TRUE, &rcand->addr, +- pj_sockaddr_get_len(&rcand->addr), +- check->tdata); +- if (status != PJ_SUCCESS) { +- check->tdata = NULL; +- pjnath_perror(ice->obj_name, "Error sending STUN request", status); +- pj_log_pop_indent(); +- return status; ++#if PJ_HAS_TCP ++ switch (lcand->transport) { ++ case PJ_CAND_TCP_ACTIVE: ++ switch (check->state) { ++ case PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY: ++ status = (*ice->cb.reconnect_tcp_connection)(ice,check_id); ++ break; ++ case PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET: ++ status = send_connectivity_check(ice, clist, check_id, ++ nominate, msg_data); ++ break; ++ default: ++ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, ++ &ice->timer_connect, TIMER_NONE); ++ status = (*ice->cb.wait_tcp_connection)(ice, check_id); ++ if (ice->timer_connect.id != TIMER_NONE) { ++ pj_assert(!"Not expected any timer active"); ++ } else { ++ LOG5((ice->obj_name, ++ "Scheduling connection time-out for check %s", ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); ++ ++ pj_time_val delay = { ++ .sec = 0, ++ .msec = PJ_ICE_TCP_CONNECTION_TIMEOUT, ++ }; ++ pj_time_val_normalize(&delay); ++ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, ++ &ice->timer_connect, &delay, ++ TIMER_CONNECTION_TIMEOUT, ++ ice->grp_lock); ++ } ++ break; ++ } ++ break; ++ case PJ_CAND_TCP_PASSIVE: ++ case PJ_CAND_TCP_SO: ++ case PJ_CAND_UDP: ++ default: ++ status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); ++ break; + } ++#else ++ status = send_connectivity_check(ice, clist, check_id, nominate, msg_data); ++#endif + +- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, +- PJ_SUCCESS); ++ if (status == PJ_SUCCESS) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, ++ status); ++ } else if (status == PJ_EPENDING) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_PENDING, status); ++ } else if (check->rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ /* TODO (sblin) remove this - https://github.com/coturn/coturn/issues/408 */ ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ status); ++ } else { ++ check->tdata = NULL; ++ pjnath_perror(ice->obj_name, "Error sending STUN request (perform check)", status); ++ } + pj_log_pop_indent(); +- return PJ_SUCCESS; ++ return status; + } + +- + /* Start periodic check for the specified checklist. +- * This callback is called by timer on every Ta (20msec by default) ++ * This callback is called by timer on every Ta + */ +-static pj_status_t start_periodic_check(pj_timer_heap_t *th, ++static pj_status_t start_periodic_check(pj_timer_heap_t *th, + pj_timer_entry *te) + { + timer_data *td; +@@ -2345,6 +2748,8 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + td = (struct timer_data*) te->user_data; + ice = td->ice; + clist = td->clist; ++ pj_time_val timeout = {0, PJ_ICE_TA_VAL}; ++ pj_bool_t check_pending = PJ_FALSE; + + pj_grp_lock_acquire(ice->grp_lock); + +@@ -2401,10 +2806,53 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + } + } + ++#if PJ_HAS_TCP + /* If we don't have anything in Waiting state, find any pair with +- * highest priority in Frozen state. ++ * highest priority in Retry state. + */ ++ + if (!check) { ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *c = &clist->checks[i]; ++ // Reconnect closed TURN sockets ++ if (c->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) { ++ LOG5((ice->obj_name, "re-Starting periodic check for check %i (needs retry)", i)); ++ check = c; ++ check_idx = i; ++ ++ timeout.msec = PJ_ICE_TCP_RECONNECTION_DELAY; ++ timeout.sec = 0; ++ break; ++ } ++ } ++ } ++ ++ if (!check) { ++ // TODO (sblin) remove - https://github.com/coturn/coturn/issues/408 ++ pj_bool_t inc_counter = PJ_TRUE; ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *c = &clist->checks[i]; ++ if (c->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET) { ++ if (inc_counter) { ++ td->first_packet_counter += 1; ++ inc_counter = PJ_FALSE; ++ } ++ if (td->first_packet_counter % 50 == 0) { ++ LOG5((ice->obj_name, "re-Starting periodic check for check %i (needs 1st packet)", i)); ++ check = c; ++ check_idx = i; ++ } ++ check_pending = PJ_TRUE; ++ break; ++ } ++ } ++ } ++#endif ++ ++ /* If we don't have anything in Waiting or Retry state, find any pair with ++ * highest priority in Frozen state. ++ */ ++ if (!check && !check_pending) { + for (i=0; icount; ++i) { + pj_ice_sess_check *c = &clist->checks[i]; + if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { +@@ -2414,6 +2862,19 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + } + } + } ++ ++#if PJ_HAS_TCP ++ if (!check && !check_pending) { ++ // If all sockets are pending, do nothing ++ for (i = 0; i < clist->count; ++i) { ++ pj_ice_sess_check *c = &clist->checks[i]; ++ if (c->state == PJ_ICE_SESS_CHECK_STATE_PENDING) { ++ check_pending = PJ_TRUE; ++ break; ++ } ++ } ++ } ++#endif + } + + /* Perform check & schedule next check for next candidate pair, +@@ -2421,15 +2882,14 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + * or empty checklist). + */ + if (check) { +- pj_time_val timeout = {0, PJ_ICE_TA_VAL}; +- + status = perform_check(ice, clist, check_idx, ice->is_nominating); +- if (status != PJ_SUCCESS) { ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { + check_set_state(ice, check, + PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + } +- ++ } ++ if (check || check_pending) { + /* Schedule next check */ + pj_time_val_normalize(&timeout); + pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE, +@@ -2441,7 +2901,6 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th, + return PJ_SUCCESS; + } + +- + /* Start sending connectivity check with USE-CANDIDATE */ + static void start_nominated_check(pj_ice_sess *ice) + { +@@ -2483,7 +2942,7 @@ static void start_nominated_check(pj_ice_sess *ice) + { + pj_assert(c->err_code == PJ_SUCCESS); + c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; +- check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, ++ check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, + PJ_SUCCESS); + break; + } +@@ -2511,7 +2970,7 @@ static void start_nominated_check(pj_ice_sess *ice) + } + + /* Timer callback to perform periodic check */ +-static void periodic_timer(pj_timer_heap_t *th, ++static void periodic_timer(pj_timer_heap_t *th, + pj_timer_entry *te) + { + start_periodic_check(th, te); +@@ -2550,9 +3009,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) + * media stream is the first media stream when it is described by + * the first m-line in the SDP offer and answer). For that media + * stream, it: +- * ++ * + * - Groups together all of the pairs with the same foundation, +- * ++ * + * - For each group, sets the state of the pair with the lowest + * component ID to Waiting. If there is more than one such pair, + * the one with the highest priority is used. +@@ -2600,7 +3059,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) + /* First, perform all pending triggered checks, simultaneously. */ + rcheck = ice->early_check.next; + while (rcheck != &ice->early_check) { +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Performing delayed triggerred check for component %d", + rcheck->comp_id)); + pj_log_push_indent(); +@@ -2611,7 +3070,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice) + pj_list_init(&ice->early_check); + + /* Start periodic check */ +- /* We could start it immediately like below, but lets schedule timer ++ /* We could start it immediately like below, but lets schedule timer + * instead to reduce stack usage: + * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer); + */ +@@ -2663,7 +3122,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, + pj_ice_sess *ice = sd->ice; + pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; + pj_status_t status; +- ++ + pj_grp_lock_acquire(ice->grp_lock); + + if (ice->is_destroying) { +@@ -2680,6 +3139,252 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, + return status; + } + ++static pj_ice_sess_check* get_current_check_at_state(pj_ice_sess *ice, ++ pj_sockaddr_t *remote_addr, ++ pj_ice_sess_check_state state, ++ int *current_check) ++{ ++ if (!ice || !remote_addr) ++ return NULL; ++ // NOTE: Multiple checks can have the same remote, we only take care of the first ++ // First, check if the TCP is really connected. If not, abort ++ pj_ice_sess_check *check = NULL; ++ for (int i = 0; i < ice->clist.count; ++i) { ++ // Find related check ++ pj_ice_sess_check *c = &ice->clist.checks[i]; ++ /* Host candidate not found this this srflx! */ ++ if (pj_sockaddr_cmp(remote_addr, &c->rcand->addr) == 0) { ++ if (c->tdata == NULL || c->state != state) ++ continue; ++ /* Match */ ++ check = c; ++ if (current_check) *current_check = i; ++ break; ++ } ++ } ++ return check; ++} ++ ++void ice_sess_on_peer_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr) ++{ ++ // The TCP link is now ready. We can now send the first STUN message (send ++ // connectivity check) This should trigger on_stun_request_complete when ++ // finished ++ if (!remote_addr) ++ return; ++ ++ pj_grp_lock_acquire(ice->grp_lock); ++ ++ int current_check = -1; ++ pj_ice_sess_check *check = get_current_check_at_state(ice,remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_PENDING, ++ ¤t_check); ++ if (!check) { ++ // Handle peer reflexive candidates (incoming are still waiting here) ++ check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_WAITING, ++ ¤t_check); ++ if (!check) { ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } ++ } ++ ++ const pj_ice_sess_cand *rcand = check->rcand; ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED && ( ++ status == PJ_ERRNO_START_SYS + 104 || status == 130054 /* CONNECTION RESET BY PEER */ || ++ status == PJ_ERRNO_START_SYS + 111 /* Connection refused */ ++ )) { ++ /** ++ * This part of the code is triggered when using ICE over TCP via TURN ++ * In fact, the other peer has to authorize this peer to connect to ++ * the relayed candidate. This is done by set_perm from the other case. ++ * But from this side, we can't know if the peer has authorized us. If it's ++ * not the case, the connection will got a CONNECTION RESET BY PEER status. ++ * In this case, we try to reconnect few times with a delay between two ++ * attempts. ++ */ ++ if (check->reconnect_count < PJ_ICE_TCP_MAX_RECONNECTION_COUNT) { ++ check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; ++ check_set_state(ice, check,PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, ++ status); ++ check->reconnect_count++; ++ } else { ++ // Max attempts reached. Fail this check. ++ LOG4((ice->obj_name, "Check %s: connection failed after %d attempts", ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), ++ PJ_ICE_TCP_MAX_RECONNECTION_COUNT)); ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ on_check_complete(ice, check); ++ } ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } else if (status != PJ_SUCCESS) { ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ char raddr[PJ_INET6_ADDRSTRLEN + 10]; ++ PJ_LOG(4, (ice->obj_name, ++ "Connection to TURN (%s) failed with status %u", ++ pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 3), status)); ++ } ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ on_check_complete(ice, check); ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } ++ ++ // TCP is correctly connected. Craft the message to send ++ const pj_ice_sess_cand *lcand = check->lcand; ++ if (check->tdata == NULL) { ++ LOG5((ice->obj_name, "Error sending STUN request, empty data")); ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } ++ pj_ice_msg_data *msg_data = ++ PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data); ++ ++ msg_data->transport_id = transport_id; ++ msg_data->has_req_data = PJ_TRUE; ++ msg_data->data.req.ice = ice; ++ msg_data->data.req.clist = &ice->clist; ++ msg_data->data.req.ckid = current_check; ++ msg_data->data.req.lcand = check->lcand; ++ msg_data->data.req.rcand = check->rcand; ++ ++ pj_ice_sess_comp *comp = find_comp(ice, lcand->comp_id); ++ // Note that USERNAME and MESSAGE-INTEGRITY will be added by the ++ // STUN session. ++ ++ // Initiate STUN transaction to send the request ++ status = pj_stun_session_send_msg(comp->stun_sess, msg_data, ++ PJ_FALSE, PJ_FALSE, &rcand->addr, ++ pj_sockaddr_get_len(&rcand->addr), ++ check->tdata); ++ ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED && ( ++ status == PJ_ERRNO_START_SYS + 104 || status == 130054 || /* CONNECTION RESET BY PEER */ ++ status == PJ_ERRNO_START_SYS + 32 /* EPIPE */ || ++ status == PJ_ERRNO_START_SYS + 111 /* Connection refused */ ++ )) { ++ /** ++ * This part of the code is triggered when using ICE over TCP via TURN ++ * In fact, the other peer has to authorize this peer to connect to ++ * the relayed candidate. This is done by set_perm from the other case. ++ * But from this side, we can't know if the peer has authorized us. If it's ++ * not the case, the connection will got a CONNECTION RESET BY PEER status. ++ * In this case, we can try to reconnect a bit after and this until the check ++ * reached its timeout. ++ */ ++ ++ if (check->reconnect_count < PJ_ICE_TCP_MAX_RECONNECTION_COUNT) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, ++ status); ++ check->reconnect_count++; ++ } else { ++ // Max attempts reached. Fail this check. ++ LOG4((ice->obj_name, "Check %s: connection failed after %d attempts", ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), ++ PJ_ICE_TCP_MAX_RECONNECTION_COUNT)); ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ on_check_complete(ice, check); ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } ++ } else if (status == PJ_EBUSY /* EBUSY */) { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ status); ++ } else if (status != PJ_SUCCESS) { ++ ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ char raddr[PJ_INET6_ADDRSTRLEN + 10]; ++ PJ_LOG(5, (ice->obj_name, ++ "STUN send message to TURN (%s) failed with status %u", ++ pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 3), status)); ++ } ++ check->tdata = NULL; ++ pjnath_perror(ice->obj_name, "Error sending STUN request (on peer connection)", status); ++ pj_log_pop_indent(); ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); ++ on_check_complete(ice, check); ++ } else { ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, status); ++ } ++ pj_grp_lock_release(ice->grp_lock); ++} ++ ++void ice_sess_on_peer_reset_connection(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr) ++{ ++ // The TCP link is reset ++ if (!remote_addr) ++ return; ++ ++ pj_grp_lock_acquire(ice->grp_lock); ++ pj_ice_sess_check *check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_PENDING, ++ NULL); ++ if (!check) { ++ // Handle peer reflexive candidates (incoming are still waiting here) ++ check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, ++ NULL); ++ ++ if (!check) { ++ // Just check if it's not the first packet failing ++ check = get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ NULL); ++ if (!check) { ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } ++ } ++ } ++ ++ const pj_ice_sess_cand *rcand = check->rcand; ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ char raddr[PJ_INET6_ADDRSTRLEN + 10]; ++ PJ_LOG(5, (ice->obj_name, ++ "Connection to TURN (%s) is reset", ++ pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 3))); ++ ++ check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY; ++ check_set_state(ice, check, ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, 120104); ++ } ++ ++ pj_grp_lock_release(ice->grp_lock); ++} ++ ++void ice_sess_on_peer_packet(pj_ice_sess *ice, ++ pj_uint8_t transport_id, ++ pj_sockaddr_t* remote_addr) ++{ ++ // The TCP link received its bind request response ++ if (!ice || !remote_addr) { ++ return; ++ } ++ ++ pj_grp_lock_acquire(ice->grp_lock); ++ pj_ice_sess_check *check = ++ get_current_check_at_state(ice, remote_addr, ++ PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, ++ NULL); ++ if (!check) { ++ pj_grp_lock_release(ice->grp_lock); ++ return; ++ } ++ ++ const pj_ice_sess_cand *rcand = check->rcand; ++ if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) { ++ check_set_state(ice, check, ++ PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS); ++ } ++ pj_grp_lock_release(ice->grp_lock); ++} + + /* This callback is called when outgoing STUN request completed */ + static void on_stun_request_complete(pj_stun_session *stun_sess, +@@ -2765,13 +3470,13 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + * + * 7.1.2.1. Failure Cases: + * +- * If the request had contained the ICE-CONTROLLED attribute, ++ * If the request had contained the ICE-CONTROLLED attribute, + * the agent MUST switch to the controlling role if it has not +- * already done so. If the request had contained the +- * ICE-CONTROLLING attribute, the agent MUST switch to the ++ * already done so. If the request had contained the ++ * ICE-CONTROLLING attribute, the agent MUST switch to the + * controlled role if it has not already done so. Once it has + * switched, the agent MUST immediately retry the request with +- * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting ++ * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting + * its new role. + */ + pj_ice_sess_role new_role = PJ_ICE_SESS_ROLE_UNKNOWN; +@@ -2779,7 +3484,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + + if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLING, 0)) { + new_role = PJ_ICE_SESS_ROLE_CONTROLLED; +- } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, ++ } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, + 0)) { + new_role = PJ_ICE_SESS_ROLE_CONTROLLING; + } else { +@@ -2788,7 +3493,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + } + + if (new_role != ice->role) { +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Changing role because of role conflict response")); + pj_ice_sess_change_role(ice, new_role); + } +@@ -2805,9 +3510,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + } + + pj_strerror(status, errmsg, sizeof(errmsg)); +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Check %s%s: connectivity check FAILED: %s", +- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, check), + (check->nominated ? " (nominated)" : " (not nominated)"), + errmsg)); +@@ -2836,7 +3541,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + * is synthesized from IPv4). + */ + pj_sockaddr synth_addr; +- ++ + status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr, + &check->rcand->addr); + if (status == PJ_SUCCESS && +@@ -2848,9 +3553,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + + if (pj_sockaddr_cmp(&check->rcand->addr, source_addr) != 0) { + status = PJNATH_EICEINSRCADDR; +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Check %s%s: connectivity check FAILED: source address mismatch", +- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, check), + (check->nominated ? " (nominated)" : " (not nominated)"))); + pj_log_push_indent(); +@@ -2862,24 +3567,24 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + } + + /* 7.1.2.2. Success Cases +- * ++ * + * A check is considered to be a success if all of the following are + * true: +- * ++ * + * o the STUN transaction generated a success response +- * ++ * + * o the source IP address and port of the response equals the + * destination IP address and port that the Binding Request was sent + * to +- * ++ * + * o the destination IP address and port of the response match the + * source IP address and port that the Binding Request was sent from + */ + + +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Check %s%s: connectivity check SUCCESS", +- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, check), + (check->nominated ? " (nominated)" : " (not nominated)"))); + +@@ -2887,7 +3592,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + xaddr = (pj_stun_xor_mapped_addr_attr*) + pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0); + if (!xaddr) { +- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, + PJNATH_ESTUNNOMAPPEDADDR); + on_check_complete(ice, check); + pj_grp_lock_release(ice->grp_lock); +@@ -2960,7 +3665,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + + /* 7.1.2.2.1. Discovering Peer Reflexive Candidates + * If the transport address returned in XOR-MAPPED-ADDRESS does not match +- * any of the local candidates that the agent knows about, the mapped ++ * any of the local candidates that the agent knows about, the mapped + * address represents a new candidate - a peer reflexive candidate. + */ + if (lcand == NULL) { +@@ -2994,7 +3699,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + &check->lcand->base_addr, + &check->lcand->base_addr, + pj_sockaddr_get_len(&xaddr->sockaddr), +- &cand_id); ++ &cand_id, ++ check->rcand->transport == PJ_CAND_UDP ? ++ PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE); + // Note: for IPv6, pj_ice_sess_add_cand can return SUCCESS + // without adding any candidates if the candidate is + // deprecated (because the ICE MUST NOT fail) +@@ -3018,7 +3725,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + /* 7.1.2.2.3. Constructing a Valid Pair + * Next, the agent constructs a candidate pair whose local candidate + * equals the mapped address of the response, and whose remote candidate +- * equals the destination address to which the request was sent. ++ * equals the destination address to which the request was sent. + */ + + /* Add pair to valid list, if it's not there, otherwise just update +@@ -3039,6 +3746,9 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED; + new_check->nominated = check->nominated; + new_check->err_code = PJ_SUCCESS; ++#if PJ_HAS_TCP ++ new_check->reconnect_count = 0; ++#endif + } else { + new_check = &ice->valid_list.checks[i]; + ice->valid_list.checks[i].nominated = check->nominated; +@@ -3053,12 +3763,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, + sort_checklist(ice, &ice->valid_list); + + /* 7.1.2.2.2. Updating Pair States +- * ++ * + * The agent sets the state of the pair that generated the check to + * Succeeded. The success of this check might also cause the state of + * other checks to change as well. + */ +- check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, ++ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, + PJ_SUCCESS); + + /* Perform 7.1.2.2.2. Updating Pair States. +@@ -3100,11 +3810,11 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); +- ++ + /* Reject any requests except Binding request */ + if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) { +- pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, +- NULL, token, PJ_TRUE, ++ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, ++ NULL, token, PJ_TRUE, + src_addr, src_addr_len); + return PJ_SUCCESS; + } +@@ -3170,13 +3880,13 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + { + if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { + /* Switch role to controlled */ +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Changing role because of ICE-CONTROLLING attribute")); + pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED); + } else { + /* Generate 487 response */ +- pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, +- NULL, token, PJ_TRUE, ++ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, ++ NULL, token, PJ_TRUE, + src_addr, src_addr_len); + pj_grp_lock_release(ice->grp_lock); + return PJ_SUCCESS; +@@ -3187,21 +3897,21 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + { + if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { + /* Generate 487 response */ +- pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, +- NULL, token, PJ_TRUE, ++ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, ++ NULL, token, PJ_TRUE, + src_addr, src_addr_len); + pj_grp_lock_release(ice->grp_lock); + return PJ_SUCCESS; + } else { + /* Switch role to controlled */ +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Changing role because of ICE-CONTROLLED attribute")); + pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLING); + } + } + +- /* +- * First send response to this request ++ /* ++ * First send response to this request + */ + status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata); + if (status != PJ_SUCCESS) { +@@ -3217,7 +3927,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + for (i = 0; i < ice->clist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (c->lcand->comp_id == sd->comp_id && +- c->lcand->transport_id == transport_id) ++ c->lcand->transport_id == transport_id) + { + lcand = c->lcand; + break; +@@ -3232,7 +3942,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + */ + for (i = 0; i < ice->rcand_cnt; ++i) { + pj_sockaddr synth_addr; +- ++ + if (ice->rcand[i].addr.addr.sa_family != pj_AF_INET()) + continue; + +@@ -3252,7 +3962,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + + + /* Add XOR-MAPPED-ADDRESS attribute */ +- status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, ++ status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_XOR_MAPPED_ADDR, + PJ_TRUE, source_addr, + source_addr_len); +@@ -3263,11 +3973,14 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, + msg_data->has_req_data = PJ_FALSE; + + /* Send the response */ +- status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, ++ status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, pj_stun_session_tp_type(sess) == PJ_STUN_TP_UDP, + src_addr, src_addr_len, tdata); + ++ if (status == PJ_EBUSY) { ++ PJ_LOG(5, (ice->obj_name, "on_stun_rx_request, PJ_EBUSY")); ++ } + +- /* ++ /* + * Handling early check. + * + * It's possible that we receive this request before we receive SDP +@@ -3326,7 +4039,7 @@ static void handle_incoming_check(pj_ice_sess *ice, + + comp = find_comp(ice, rcheck->comp_id); + +- /* Find remote candidate based on the source transport address of ++ /* Find remote candidate based on the source transport address of + * the request. + */ + for (i=0; ircand_cnt; ++i) { +@@ -3344,7 +4057,7 @@ static void handle_incoming_check(pj_ice_sess *ice, + void *p; + + if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Unable to add new peer reflexive candidate: too many " + "candidates already (%d)", PJ_ICE_MAX_CAND)); + return; +@@ -3361,7 +4074,7 @@ static void handle_incoming_check(pj_ice_sess *ice, + rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 36, + "f%p", p); + +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Added new remote candidate from the request: %s:%d", + pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 2), + pj_sockaddr_get_port(&rcand->addr))); +@@ -3391,12 +4104,12 @@ static void handle_incoming_check(pj_ice_sess *ice, + /* Just get candidate with the highest priority and same transport ID + * for the specified component ID in the checklist. + */ +- for (i=0; iclist.count; ++i) { +- pj_ice_sess_check *c = &ice->clist.checks[i]; +- if (c->lcand->comp_id == rcheck->comp_id && +- c->lcand->transport_id == rcheck->transport_id) ++ for (i=0; ilcand_cnt; ++i) { ++ pj_ice_sess_cand *lcand_tmp = &ice->lcand[i]; ++ if (lcand_tmp->comp_id == rcheck->comp_id && ++ lcand_tmp->transport_id == rcheck->transport_id) + { +- lcand = c->lcand; ++ lcand = lcand_tmp; + break; + } + } +@@ -3404,17 +4117,17 @@ static void handle_incoming_check(pj_ice_sess *ice, + /* Should not happen, but just in case remote is sending a + * Binding request for a component which it doesn't have. + */ +- LOG4((ice->obj_name, ++ LOG4((ice->obj_name, + "Received Binding request but no local candidate is found!")); + return; + } + #endif + +- /* +- * Create candidate pair for this request. ++ /* ++ * Create candidate pair for this request. + */ + +- /* ++ /* + * 7.2.1.4. Triggered Checks + * + * Now that we have local and remote candidate, check if we already +@@ -3435,14 +4148,14 @@ static void handle_incoming_check(pj_ice_sess *ice, + * generate an immediate retransmit of the Binding Request for the + * check in progress. This is to facilitate rapid completion of + * ICE when both agents are behind NAT. +- * ++ * + * - If the state of that pair is Failed or Succeeded, no triggered + * check is sent. + */ + if (i != ice->clist.count) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + +- /* If USE-CANDIDATE is present, set nominated flag ++ /* If USE-CANDIDATE is present, set nominated flag + * Note: DO NOT overwrite nominated flag if one is already set. + */ + c->nominated = ((rcheck->use_candidate) || c->nominated); +@@ -3483,14 +4196,14 @@ static void handle_incoming_check(pj_ice_sess *ice, + unsigned j; + + /* If this check is nominated, scan the valid_list for the +- * same check and update the nominated flag. A controlled ++ * same check and update the nominated flag. A controlled + * agent might have finished the check earlier. + */ + if (rcheck->use_candidate) { + for (j=0; jvalid_list.count; ++j) { + pj_ice_sess_check *vc = &ice->valid_list.checks[j]; +- if (vc->lcand->transport_id == c->lcand->transport_id && +- vc->rcand == c->rcand) ++ if (vc->lcand->transport_id == c->lcand->transport_id && ++ vc->rcand == c->rcand) + { + /* Set nominated flag */ + vc->nominated = PJ_TRUE; +@@ -3498,8 +4211,8 @@ static void handle_incoming_check(pj_ice_sess *ice, + /* Update valid check and nominated check for the component */ + update_comp_check(ice, vc->lcand->comp_id, vc); + +- LOG5((ice->obj_name, "Valid check %s is nominated", +- dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), ++ LOG5((ice->obj_name, "Valid check %s is nominated", ++ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->valid_list, vc))); + } + } +@@ -3599,7 +4312,7 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess, + "for component %d", sd->comp_id)); + } else { + LOG4((sd->ice->obj_name, "Received unexpected %s indication " +- "for component %d", pj_stun_get_method_name(msg->hdr.type), ++ "for component %d", pj_stun_get_method_name(msg->hdr.type), + sd->comp_id)); + } + +@@ -3621,7 +4334,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, + pj_sockaddr addr; + + PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); +- ++ + /* It is possible that comp_cnt is less than comp_id, when remote + * doesn't support all the components that we have. + */ +@@ -3658,9 +4371,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, + + PJ_RACE_ME(5); + +- status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, +- data, data_len, +- &addr, ++ status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, ++ data, data_len, ++ &addr, + pj_sockaddr_get_len(&addr)); + + on_return: +@@ -3713,7 +4426,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, + * packets. We don't need to verify the STUN packet too rigorously, that + * will be done by the user. + */ +- status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, ++ status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, + PJ_STUN_IS_DATAGRAM | + PJ_STUN_NO_FINGERPRINT_CHECK); + if (status == PJ_SUCCESS) { +@@ -3734,12 +4447,10 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, + + PJ_RACE_ME(5); + +- (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, ++ (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, + src_addr, src_addr_len); + status = PJ_SUCCESS; + } + + return status; + } +- +- +diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c +index 370ca6f14..b666696ac 100644 +--- a/pjnath/src/pjnath/ice_strans.c ++++ b/pjnath/src/pjnath/ice_strans.c +@@ -68,6 +68,7 @@ enum tp_type + # define RELAY_PREF ((1 << PJ_ICE_LOCAL_PREF_BITS) - 1) + #endif + ++#define MAX_RTP_SIZE 65536 + + /* The candidate type preference when STUN candidate is used */ + static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = +@@ -86,9 +87,18 @@ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = + #endif + }; + ++////////////////////////////////////////////////////////////////////////////// ++ ++static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) ++{ ++ return (pj_uint16_t) ((buf1[0] << 8) | (buf2[0] << 0)); ++} ++ ++////////////////////////////////////////////////////////////////////////////// + + /* ICE callbacks */ + static void on_valid_pair(pj_ice_sess *ice); ++static void on_ice_destroy(pj_ice_sess *ice); + static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); + static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + unsigned comp_id, +@@ -103,6 +113,18 @@ static void ice_rx_data(pj_ice_sess *ice, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + ++#if PJ_HAS_TCP ++static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, ++ unsigned check_id); ++ ++static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, ++ unsigned check_id); ++ ++static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, ++ unsigned check_id); ++static pj_status_t ice_close_remaining_tcp(pj_ice_sess *ice); ++#endif ++ + + /* STUN socket callbacks */ + /* Notification when incoming packet has been received. */ +@@ -182,6 +204,16 @@ typedef struct pj_ice_strans_comp + } pj_ice_strans_comp; + + ++static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand, ++ unsigned idx, ++ unsigned i, ++ unsigned *cand_cnt, ++ unsigned *max_cand_cnt, ++ pj_stun_sock_info stun_sock_info, ++ pj_ice_strans *ice_st, ++ pj_ice_strans_comp *comp, ++ pj_ice_cand_transport transport); ++ + /* Pending send buffer */ + typedef struct pending_send + { +@@ -232,6 +264,12 @@ struct pj_ice_strans + signalled end of candidate? */ + pj_bool_t loc_cand_end;/**< Trickle ICE: local has + signalled end of candidate? */ ++ pj_uint8_t rtp_pkt[MAX_RTP_SIZE]; ++ pj_uint8_t rx_buffer[MAX_RTP_SIZE]; ++ pj_uint16_t rx_buffer_size; ++ pj_uint16_t rx_wanted_size; ++ ++ pj_ssize_t last_data_len; /**< What the application is waiting. */ + }; + + +@@ -268,6 +306,7 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) + pj_bzero(cfg, sizeof(*cfg)); + + cfg->af = pj_AF_INET(); ++ cfg->protocol = PJ_ICE_TP_UDP; + pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); + pj_ice_strans_stun_cfg_default(&cfg->stun); + pj_ice_strans_turn_cfg_default(&cfg->turn); +@@ -285,6 +324,7 @@ PJ_DEF(void) pj_ice_strans_stun_cfg_default(pj_ice_strans_stun_cfg *cfg) + pj_bzero(cfg, sizeof(*cfg)); + + cfg->af = pj_AF_INET(); ++ cfg->conn_type = PJ_STUN_TP_UDP; + cfg->port = PJ_STUN_PORT; + cfg->max_host_cands = 64; + cfg->ignore_stun_error = PJ_FALSE; +@@ -428,6 +468,9 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, + cand->transport_id = tp_id; + cand->comp_id = (pj_uint8_t) comp->comp_id; + new_cand = PJ_TRUE; ++ cand->transport = turn_cfg->conn_type == PJ_TURN_TP_UDP ? ++ PJ_CAND_UDP : ++ PJ_CAND_TCP_PASSIVE; + } + + /* Allocate and initialize TURN socket data */ +@@ -435,6 +478,10 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, + data->comp = comp; + data->transport_id = cand->transport_id; + ++ if (turn_cfg->conn_type == PJ_TURN_TP_TCP) { ++ turn_cfg->alloc_param.peer_conn_type = PJ_TURN_TP_TCP; ++ } ++ + /* Create the TURN transport */ + status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, turn_cfg->af, + turn_cfg->conn_type, +@@ -476,7 +523,7 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st, + return PJ_SUCCESS; + } + +-static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, ++static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, + pj_ice_sess_cand *rcand) + { + if (lcand == NULL && rcand == NULL){ +@@ -485,7 +532,7 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, + if (lcand == NULL || rcand == NULL){ + return PJ_FALSE; + } +- ++ + if (lcand->type != rcand->type + || lcand->status != rcand->status + || lcand->comp_id != rcand->comp_id +@@ -493,15 +540,145 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, + // local pref is no longer a constant, so it may be different + //|| lcand->local_pref != rcand->local_pref + || lcand->prio != rcand->prio ++ || lcand->transport != rcand->transport + || pj_sockaddr_cmp(&lcand->addr, &rcand->addr) != 0 + || pj_sockaddr_cmp(&lcand->base_addr, &rcand->base_addr) != 0) + { + return PJ_FALSE; + } +- ++ + return PJ_TRUE; + } + ++static pj_status_t add_nat_assisted_cand(pj_ice_strans *ice_st, ++ pj_ice_strans_comp *comp, ++ unsigned idx, ++ unsigned max_cand_cnt) ++{ ++ /* PJNATH library handles host and srflx connections through STUN ++ * sockets, even if there is no actual STUN server configured (for host ++ * only candidates). Since NAT-assisted candidates are srflx candidates, ++ * they will be handled through STUN sockets as well. ++ * NAT-assisted candidates are provided as a STUN configuration (as an ++ * entry in the stun_tp list). The position (index) of the config in the ++ * list is used to calculate the "local preference" of the priority, thus ++ * it will determine the priority of the NAT-assisted candidates relative ++ * to other srflx candidates. ++ */ ++ ++ pj_ice_sess_cand *cand; ++ pj_ice_strans_stun_cfg *nat_cfg = &ice_st->cfg.stun_tp[idx]; ++ pj_stun_sock_cfg *sock_cfg = &nat_cfg->cfg; ++ unsigned comp_idx = comp->comp_id - 1; ++ pj_stun_sock_cb sock_cb; ++ sock_user_data *data; ++ pj_status_t status; ++ ++ PJ_ASSERT_RETURN(max_cand_cnt > 0, PJ_ETOOSMALL); ++ PJ_ASSERT_RETURN(nat_cfg->cfg.user_mapping_cnt > comp_idx, PJ_ETOOSMALL); ++ ++ pj_sockaddr *laddr = &nat_cfg->cfg.user_mapping[comp_idx].local_addr; ++ pj_sockaddr *maddr = &nat_cfg->cfg.user_mapping[comp_idx].mapped_addr; ++ ++ pj_bzero(&sock_cb, sizeof(sock_cb)); ++ sock_cb.on_rx_data = &stun_on_rx_data; ++ sock_cb.on_status = &stun_on_status; ++ sock_cb.on_data_sent = &stun_on_data_sent; ++ ++ /* Override component specific QoS settings, if any */ ++ if (ice_st->cfg.comp[comp_idx].qos_type) { ++ sock_cfg->qos_type = ice_st->cfg.comp[comp_idx].qos_type; ++ } ++ if (ice_st->cfg.comp[comp_idx].qos_params.flags) { ++ pj_memcpy(&sock_cfg->qos_params, ++ &ice_st->cfg.comp[comp_idx].qos_params, ++ sizeof(sock_cfg->qos_params)); ++ } ++ ++ /* Override component specific socket buffer size settings, if any */ ++ if (ice_st->cfg.comp[comp_idx].so_rcvbuf_size > 0) { ++ sock_cfg->so_rcvbuf_size = ice_st->cfg.comp[comp_idx].so_rcvbuf_size; ++ } ++ if (ice_st->cfg.comp[comp_idx].so_sndbuf_size > 0) { ++ sock_cfg->so_sndbuf_size = ice_st->cfg.comp[comp_idx].so_sndbuf_size; ++ } ++ ++ /* Setup srflx candidate*/ ++ cand = &comp->cand_list[comp->cand_cnt]; ++ cand->type = PJ_ICE_CAND_TYPE_SRFLX; ++ /* User candidates are assumed ready */ ++ cand->status = PJ_SUCCESS; ++ cand->local_pref = (pj_uint16_t)(SRFLX_PREF - idx); ++ cand->transport_id = CREATE_TP_ID(TP_STUN, idx); ++ cand->comp_id = (pj_uint8_t) comp->comp_id; ++ cand->transport = nat_cfg->cfg.user_mapping[comp_idx].tp_type; ++ ++ /* Set the user mappings if availlabe. */ ++ pj_sockaddr_cp(&sock_cfg->bound_addr, laddr); ++ ++ { ++ char localStr[PJ_INET6_ADDRSTRLEN+8]; ++ char mappedStr[PJ_INET6_ADDRSTRLEN+8]; ++ PJ_LOG(5,(ice_st->obj_name, "Setting user mapping %s -> %s [%s (%i)] for comp %u at config index %i", ++ pj_sockaddr_print(laddr, localStr, sizeof(localStr), 3), ++ pj_sockaddr_print(maddr, mappedStr, sizeof(mappedStr), 3), ++ nat_cfg->conn_type == PJ_STUN_TP_UDP?"UDP":"TCP", ++ nat_cfg->conn_type, ++ comp->comp_id, idx)); ++ } ++ ++ /* Allocate and initialize STUN socket data */ ++ data = PJ_POOL_ZALLOC_T(ice_st->pool, sock_user_data); ++ data->comp = comp; ++ data->transport_id = cand->transport_id; ++ ++ /* Create the STUN transport */ ++ status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, ++ nat_cfg->af, nat_cfg->conn_type, ++ &sock_cb, sock_cfg, data, ++ &comp->stun[idx].sock); ++ if (status != PJ_SUCCESS) ++ return status; ++ ++ /* Update and commit NAT-assisted candidate. */ ++ pj_sockaddr_cp(&cand->addr, maddr); ++ pj_sockaddr_cp(&cand->base_addr, laddr); ++ pj_sockaddr_cp(&cand->rel_addr, &cand->base_addr); ++ pj_ice_calc_foundation(ice_st->pool, &cand->foundation, ++ cand->type, &cand->base_addr); ++ comp->cand_cnt++; ++ max_cand_cnt--; ++ ++ // Check if we already have a matching host candidate for ++ // this srflx candidate ++ ++ /* Find the base for this candidate */ ++ unsigned j=0; ++ for (; jcand_cnt; j++) { ++ pj_ice_sess_cand *host = &comp->cand_list[j]; ++ ++ if (host->type != PJ_ICE_CAND_TYPE_HOST) ++ continue; ++ ++ if (pj_sockaddr_cmp(&cand->base_addr, &host->addr) == 0) { ++ /* Found a matching host cadidate */ ++ break; ++ } ++ } ++ ++ /* Add local address as a host candidate if not already present. */ ++ if (j == comp->cand_cnt && nat_cfg->max_host_cands) { ++ pj_stun_sock_info stun_sock_info; ++ pj_memset(&stun_sock_info, 0, sizeof(stun_sock_info)); ++ stun_sock_info.alias_cnt = 1; ++ pj_sockaddr_cp(&stun_sock_info.aliases[0], laddr); ++ unsigned cand_cnt = 0; ++ status = add_local_candidate(cand, idx, 0, &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, cand->transport); ++ } ++ ++ return status; ++} + + static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + pj_ice_strans_comp *comp, +@@ -553,6 +730,9 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + cand->local_pref = (pj_uint16_t)(SRFLX_PREF - idx); + cand->transport_id = CREATE_TP_ID(TP_STUN, idx); + cand->comp_id = (pj_uint8_t) comp->comp_id; ++ cand->transport = stun_cfg->conn_type == PJ_STUN_TP_UDP ? ++ PJ_CAND_UDP : ++ PJ_CAND_TCP_PASSIVE; + + /* Allocate and initialize STUN socket data */ + data = PJ_POOL_ZALLOC_T(ice_st->pool, sock_user_data); +@@ -561,7 +741,7 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + + /* Create the STUN transport */ + status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, +- stun_cfg->af, &stun_sock_cb, ++ stun_cfg->af, stun_cfg->conn_type, &stun_sock_cb, + sock_cfg, data, &comp->stun[idx].sock); + if (status != PJ_SUCCESS) + return status; +@@ -646,106 +826,154 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st, + return status; + } + +- for (i = 0; i < stun_sock_info.alias_cnt && +- cand_cnt < stun_cfg->max_host_cands; ++i) +- { +- unsigned j; +- pj_bool_t cand_duplicate = PJ_FALSE; +- char addrinfo[PJ_INET6_ADDRSTRLEN+10]; +- const pj_sockaddr *addr = &stun_sock_info.aliases[i]; +- +- if (max_cand_cnt==0) { +- PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); +- break; +- } +- +- /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ +- if (stun_cfg->loop_addr==PJ_FALSE) { +- if (stun_cfg->af == pj_AF_INET() && +- (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) +- { +- continue; +- } +- else if (stun_cfg->af == pj_AF_INET6()) { +- pj_in6_addr in6addr = {{{0}}}; +- in6addr.s6_addr[15] = 1; +- if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, +- sizeof(in6addr))==0) +- { +- continue; +- } +- } ++ for (i = 0; i < stun_sock_info.alias_cnt && ++ cand_cnt < stun_cfg->max_host_cands && ++ status == PJ_SUCCESS; ++i) ++ { ++ status = !PJ_SUCCESS; ++ if (stun_sock_info.conn_type == PJ_STUN_TP_UDP) { ++ status = add_local_candidate(cand, idx, i, ++ &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, ++ PJ_CAND_UDP); ++ } else { ++ status = add_local_candidate(cand, idx, i, ++ &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, ++ PJ_CAND_TCP_PASSIVE); ++ /** RFC 6544, Section 4.1: ++ * First, agents SHOULD obtain host candidates as described in ++ * Section 5.1. Then, each agent SHOULD "obtain" (allocate a ++ * placeholder for) an active host candidate for each component of ++ * each TCP-capable media stream on each interface that the host ++ * has. The agent does not yet have to actually allocate a port for ++ * these candidates, but they are used for the creation of the check ++ * lists. ++ */ ++ status = add_local_candidate(cand, idx, i, ++ &cand_cnt, &max_cand_cnt, ++ stun_sock_info, ice_st, comp, ++ PJ_CAND_TCP_ACTIVE); + } ++ } ++ } + +- /* Ignore IPv6 link-local address, unless it is the default +- * address (first alias). +- */ +- if (stun_cfg->af == pj_AF_INET6() && i != 0) { +- const pj_in6_addr *a = &addr->ipv6.sin6_addr; +- if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) +- continue; +- } ++ return status; ++} + +- cand = &comp->cand_list[comp->cand_cnt]; +- +- cand->type = PJ_ICE_CAND_TYPE_HOST; +- cand->status = PJ_SUCCESS; +- cand->local_pref = (pj_uint16_t)(HOST_PREF - cand_cnt); +- cand->transport_id = CREATE_TP_ID(TP_STUN, idx); +- cand->comp_id = (pj_uint8_t) comp->comp_id; +- pj_sockaddr_cp(&cand->addr, addr); +- pj_sockaddr_cp(&cand->base_addr, addr); +- pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); +- +- /* Check if not already in list */ +- for (j=0; jcand_cnt; j++) { +- if (ice_cand_equals(cand, &comp->cand_list[j])) { +- cand_duplicate = PJ_TRUE; +- break; +- } +- } ++static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand, ++ unsigned idx, ++ unsigned i, ++ unsigned *cand_cnt, ++ unsigned *max_cand_cnt, ++ pj_stun_sock_info stun_sock_info, ++ pj_ice_strans *ice_st, ++ pj_ice_strans_comp *comp, ++ pj_ice_cand_transport transport) ++{ ++ unsigned j; ++ pj_bool_t cand_duplicate = PJ_FALSE; ++ char addrinfo[PJ_INET6_ADDRSTRLEN+10]; ++ const pj_sockaddr *addr = &stun_sock_info.aliases[i]; ++ pj_ice_strans_stun_cfg *stun_cfg = &ice_st->cfg.stun_tp[idx]; + +- if (cand_duplicate) { +- PJ_LOG(4, (ice_st->obj_name, +- "Comp %d: host candidate %s (tpid=%d) is a duplicate", +- comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo, +- sizeof(addrinfo), 3), cand->transport_id)); + +- pj_bzero(&cand->addr, sizeof(cand->addr)); +- pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); +- continue; +- } else { +- comp->cand_cnt+=1; +- cand_cnt++; +- max_cand_cnt--; +- } +- +- pj_ice_calc_foundation(ice_st->pool, &cand->foundation, +- cand->type, &cand->base_addr); ++ if (*max_cand_cnt==0) { ++ PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); ++ return !PJ_SUCCESS; ++ } + +- /* Set default candidate with the preferred default +- * address family +- */ +- if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && +- addr->addr.sa_family == comp->ice_st->cfg.af && +- comp->cand_list[comp->default_cand].base_addr.addr.sa_family != +- ice_st->cfg.af) ++ /* Ignore loopback addresses if cfg->stun.loop_addr is unset */ ++ if (stun_cfg->loop_addr==PJ_FALSE) { ++ if (stun_cfg->af == pj_AF_INET() && ++ (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) ++ { ++ return PJ_SUCCESS; ++ } ++ else if (stun_cfg->af == pj_AF_INET6()) { ++ pj_in6_addr in6addr = {0}; ++ in6addr.s6_addr[15] = 1; ++ if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr, ++ sizeof(in6addr))==0) + { +- comp->default_cand = (unsigned)(cand - comp->cand_list); ++ return PJ_SUCCESS; + } ++ } ++ } + +- PJ_LOG(4,(ice_st->obj_name, +- "Comp %d/%d: host candidate %s (tpid=%d) added", +- comp->comp_id, comp->cand_cnt-1, +- pj_sockaddr_print(&cand->addr, addrinfo, +- sizeof(addrinfo), 3), +- cand->transport_id)); ++ /* Ignore IPv6 link-local address, unless it is the default ++ * address (first alias). ++ */ ++ if (stun_cfg->af == pj_AF_INET6() && i != 0) { ++ const pj_in6_addr *a = &addr->ipv6.sin6_addr; ++ if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) ++ return PJ_SUCCESS; ++ } ++ ++ cand = &comp->cand_list[comp->cand_cnt]; ++ ++ cand->type = PJ_ICE_CAND_TYPE_HOST; ++ cand->status = PJ_SUCCESS; ++ cand->local_pref = (pj_uint16_t)(HOST_PREF - *cand_cnt); ++ cand->transport_id = CREATE_TP_ID(TP_STUN, idx); ++ cand->comp_id = (pj_uint8_t) comp->comp_id; ++ cand->transport = transport; ++ ++ pj_sockaddr_cp(&cand->addr, addr); ++ pj_sockaddr_cp(&cand->base_addr, addr); ++ pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); ++ ++ /* Check if not already in list */ ++ for (j=0; jcand_cnt; j++) { ++ if (ice_cand_equals(cand, &comp->cand_list[j])) { ++ cand_duplicate = PJ_TRUE; ++ return !PJ_SUCCESS; + } + } + +- return status; +-} ++ if (cand_duplicate) { ++ PJ_LOG(4, (ice_st->obj_name, ++ "Comp %d: host candidate %s (tpid=%d) is a duplicate", ++ comp->comp_id, ++ pj_sockaddr_print(&cand->addr, ++ addrinfo, sizeof(addrinfo), 3), ++ cand->transport_id)); ++ ++ pj_bzero(&cand->addr, sizeof(cand->addr)); ++ pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); ++ return PJ_SUCCESS; ++ } else { ++ comp->cand_cnt+=1; ++ (*cand_cnt)++; ++ (*max_cand_cnt)--; ++ } ++ pj_ice_calc_foundation(ice_st->pool, &cand->foundation, ++ cand->type, &cand->base_addr); ++ ++ /* Set default candidate with the preferred default ++ * address family ++ */ ++ if (comp->ice_st->cfg.af != pj_AF_UNSPEC() && ++ addr->addr.sa_family == comp->ice_st->cfg.af && ++ comp->cand_list[comp->default_cand].base_addr.addr.sa_family != ++ ice_st->cfg.af) ++ { ++ comp->default_cand = (unsigned)(cand - comp->cand_list); ++ } + ++ if (transport == PJ_CAND_TCP_ACTIVE) { ++ // Use the port 9 (DISCARD Protocol) for TCP active candidates. ++ pj_sockaddr_set_port(&cand->addr, 9); ++ } ++ ++ PJ_LOG(4,(ice_st->obj_name, ++ "Comp %d/%d: host candidate %s (tpid=%d) added", ++ comp->comp_id, comp->cand_cnt-1, ++ pj_sockaddr_print(&cand->addr, addrinfo, ++ sizeof(addrinfo), 3), ++ cand->transport_id)); ++ return PJ_SUCCESS; ++} + + /* + * Create the component. +@@ -776,18 +1004,31 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) + /* Create STUN transport if configured */ + for (i=0; icfg.stun_tp_cnt; ++i) { + unsigned max_cand_cnt = PJ_ICE_ST_MAX_CAND - comp->cand_cnt - +- ice_st->cfg.turn_tp_cnt; ++ ice_st->cfg.turn_tp_cnt; + + status = PJ_ETOOSMALL; + +- if ((max_cand_cnt > 0) && (max_cand_cnt <= PJ_ICE_ST_MAX_CAND)) +- status = add_stun_and_host(ice_st, comp, i, max_cand_cnt); +- +- if (status != PJ_SUCCESS) { +- PJ_PERROR(3,(ice_st->obj_name, status, +- "Failed creating STUN transport #%d for comp %d", +- i, comp->comp_id)); +- //return status; ++ if ((max_cand_cnt > 0) && (max_cand_cnt <= PJ_ICE_ST_MAX_CAND)) { ++ // Set custom mapping (nat) if provided by the user. ++ if (ice_st->cfg.stun_tp[i].cfg.user_mapping_cnt > 0) { ++ status = add_nat_assisted_cand(ice_st, comp, i, max_cand_cnt); ++ if (status != PJ_SUCCESS) ++ PJ_PERROR(3,(ice_st->obj_name, status, ++ "Failed to add NAT-assisted candidate at config #%d for comp %d", ++ i, comp->comp_id)); ++ } else { ++ status = add_stun_and_host(ice_st, comp, i, max_cand_cnt); ++ if (status != PJ_SUCCESS) ++ PJ_PERROR(3,(ice_st->obj_name, status, ++ "Failed creating STUN transport #%d for comp %d", ++ i, comp->comp_id)); ++ } ++ } else { ++ // All STUN config slots have been used. ++ if (status != PJ_SUCCESS) ++ PJ_PERROR(3,(ice_st->obj_name, status, ++ "Max STUN config (%d) has been reached for comp %d", ++ PJ_ICE_ST_MAX_CAND, comp->comp_id)); + } + } + +@@ -828,10 +1069,10 @@ static pj_status_t alloc_send_buf(pj_ice_strans *ice_st, unsigned buf_size) + { + if (buf_size > ice_st->buf_size) { + unsigned i; +- ++ + if (ice_st->is_pending) { + /* The current buffer is insufficient, but still currently used.*/ +- return PJ_EBUSY; ++ return PJ_EPENDING; + } + + pj_pool_safe_release(&ice_st->buf_pool); +@@ -851,7 +1092,7 @@ static pj_status_t alloc_send_buf(pj_ice_strans *ice_st, unsigned buf_size) + } + ice_st->buf_idx = ice_st->empty_idx = 0; + } +- ++ + return PJ_SUCCESS; + } + +@@ -919,7 +1160,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, + /* To maintain backward compatibility, check if old/deprecated setting is set + * and the new setting is not, copy the value to the new setting. + */ +- if (cfg->stun_tp_cnt == 0 && ++ if (cfg->stun_tp_cnt == 0 && + (cfg->stun.server.slen || cfg->stun.max_host_cands)) + { + ice_st->cfg.stun_tp_cnt = 1; +@@ -1135,7 +1376,7 @@ static void sess_init_update(pj_ice_strans *ice_st) + pj_ice_get_cand_type_name(cand->type))); + return; + } +- ++ + if (status == PJ_EUNKNOWN) { + status = cand->status; + } else { +@@ -1144,7 +1385,7 @@ static void sess_init_update(pj_ice_strans *ice_st) + status = PJ_SUCCESS; + } + } +- ++ + if (status != PJ_SUCCESS) + break; + } +@@ -1288,6 +1529,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, + ice_cb.on_ice_complete = &on_ice_complete; + ice_cb.on_rx_data = &ice_rx_data; + ice_cb.on_tx_pkt = &ice_tx_pkt; ++#if PJ_HAS_TCP ++ ice_cb.wait_tcp_connection = &ice_wait_tcp_connection; ++ ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection; ++ ice_cb.close_tcp_connection = &ice_close_tcp_connection; ++ ice_cb.on_ice_destroy = &on_ice_destroy; ++#endif + + /* Release the pool of previous ICE session to avoid memory bloat, + * as otherwise it will only be released after ICE strans is destroyed +@@ -1372,7 +1619,8 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, + &cand->foundation, &cand->addr, + &cand->base_addr, &cand->rel_addr, + pj_sockaddr_get_len(&cand->addr), +- (unsigned*)&ice_cand_id); ++ (unsigned*)&ice_cand_id, ++ cand->transport); + if (status != PJ_SUCCESS) + goto on_error; + } +@@ -1735,7 +1983,7 @@ pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st, + PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st) + { + PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); +- ++ + /* Protect with group lock, since this may cause race condition with + * pj_ice_strans_sendto2(). + * See ticket #1877. +@@ -1771,7 +2019,7 @@ static pj_status_t use_buffer( pj_ice_strans *ice_st, + status = alloc_send_buf(ice_st, (unsigned)data_len); + if (status != PJ_SUCCESS) + return status; +- ++ + if (ice_st->is_pending && ice_st->empty_idx == ice_st->buf_idx) { + /* We don't use buffer or there's no more empty buffer. */ + return PJ_EBUSY; +@@ -1786,12 +2034,12 @@ static pj_status_t use_buffer( pj_ice_strans *ice_st, + pj_sockaddr_cp(&ice_st->send_buf[idx].dst_addr, dst_addr); + ice_st->send_buf[idx].dst_addr_len = dst_addr_len; + *buffer = ice_st->send_buf[idx].buffer; +- ++ + if (ice_st->is_pending) { + /* We'll continue later since there's still a pending send. */ + return PJ_EPENDING; + } +- ++ + ice_st->is_pending = PJ_TRUE; + ice_st->buf_idx = idx; + +@@ -1844,6 +2092,8 @@ static pj_status_t send_data(pj_ice_strans *ice_st, + } + } + ++ def_cand = &comp->cand_list[comp->default_cand]; ++ pj_bool_t add_header = def_cand->transport != PJ_CAND_UDP; + /* If ICE is available, send data with ICE. If ICE nego is not completed + * yet, ICE will try to send using any valid candidate pair. For any + * failure, it will fallback to sending with the default candidate +@@ -1854,16 +2104,35 @@ static pj_status_t send_data(pj_ice_strans *ice_st, + */ + if (ice_st->ice && ice_st->state <= PJ_ICE_STRANS_STATE_RUNNING) { + status = pj_ice_sess_send_data(ice_st->ice, comp_id, buf, data_len); +- if (status == PJ_SUCCESS || status == PJ_EPENDING) { +- pj_grp_lock_release(ice_st->grp_lock); +- goto on_return; +- } +- } ++ pj_grp_lock_release(ice_st->grp_lock); ++ goto on_return; ++ } + + pj_grp_lock_release(ice_st->grp_lock); + +- def_cand = &comp->cand_list[comp->default_cand]; +- ++ /* TCP, add header */ ++ if (add_header) { ++ /* ++ * RFC6544 ICE requires an agent to demultiplex STUN and ++ * application-layer traffic, since they appear on the same port. This ++ * demultiplexing is described in [RFC5245] and is done using the magic ++ * cookie and other fields of the message. Stream-oriented transports ++ * introduce another wrinkle, since they require a way to frame the ++ * connection so that the application and STUN packets can be extracted ++ * in order to differentiate STUN packets from application-layer ++ * traffic. For this reason, TCP media streams utilizing ICE use the ++ * basic framing provided in RFC 4571 [RFC4571], even if the application ++ * layer protocol is not RTP. ++ */ ++ pj_uint8_t header_1 = data_len % 256; ++ pj_uint8_t header_0 = data_len >> 8; ++ pj_memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[2], (unsigned char *)data, data_len); ++ buf = &ice_st->rtp_pkt; ++ data_len += 2; ++ } ++ + if (def_cand->status == PJ_SUCCESS) { + unsigned tp_idx = GET_TP_IDX(def_cand->transport_id); + +@@ -1925,6 +2194,10 @@ static pj_status_t send_data(pj_ice_strans *ice_st, + status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, buf, + (unsigned)data_len, 0, dest_addr, + dest_addr_len); ++ /* Do not count the header */ ++ if (add_header) { ++ data_len -= sizeof(pj_uint16_t); ++ } + goto on_return; + } + +@@ -1933,8 +2206,14 @@ static pj_status_t send_data(pj_ice_strans *ice_st, + + on_return: + /* We continue later in on_data_sent() callback. */ +- if (status == PJ_EPENDING) +- return status; ++ if (status == PJ_EPENDING) { ++ ice_st->last_data_len = data_len; ++ if (add_header) { ++ // Don't forget the header ++ ice_st->last_data_len += sizeof(pj_uint16_t); ++ } ++ return status; ++ } + + if (call_cb) { + on_data_sent(ice_st, (status == PJ_SUCCESS? (pj_ssize_t)data_len: -status)); +@@ -1966,7 +2245,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, + dst_addr_len, PJ_TRUE, PJ_FALSE); + if (status == PJ_EPENDING) + status = PJ_SUCCESS; +- ++ + return status; + } + #endif +@@ -2026,7 +2305,7 @@ static void on_valid_pair(pj_ice_sess *ice) + pj_sockaddr_print(&check->lcand->addr, lip, sizeof(lip), 3); + pj_sockaddr_print(&check->rcand->addr, rip, sizeof(rip), 3); + +- if (tp_typ == TP_TURN) { ++ if (tp_typ == TP_TURN && check->lcand->transport == PJ_CAND_UDP) { + /* Activate channel binding for the remote address + * for more efficient data transfer using TURN. + */ +@@ -2068,6 +2347,15 @@ static void on_valid_pair(pj_ice_sess *ice) + pj_grp_lock_dec_ref(ice_st->grp_lock); + } + ++static void on_ice_destroy(pj_ice_sess *ice) ++{ ++ pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; ++ ++ if (ice_st->cb.on_destroy) { ++ (*ice_st->cb.on_destroy)(ice_st); ++ } ++} ++ + /* + * Callback called by ICE session when ICE processing is complete, either + * successfully or with failure. +@@ -2107,25 +2395,43 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) + pj_ice_strans_comp *comp = ice_st->comp[i]; + + check = pj_ice_strans_get_valid_pair(ice_st, i+1); ++ ++ // We nominated a connection, we can close the other ones. ++ ice_close_remaining_tcp(ice_st->ice); + if (check) { + char lip[PJ_INET6_ADDRSTRLEN+10]; + char rip[PJ_INET6_ADDRSTRLEN+10]; + unsigned tp_idx = GET_TP_IDX(check->lcand->transport_id); + unsigned tp_typ = GET_TP_TYPE(check->lcand->transport_id); + +- pj_sockaddr_print(&check->lcand->addr, lip, +- sizeof(lip), 3); ++ pj_sockaddr_print(&check->lcand->addr, lip, ++ sizeof(lip), 3); + pj_sockaddr_print(&check->rcand->addr, rip, +- sizeof(rip), 3); +- +- if (tp_typ == TP_TURN) { ++ sizeof(rip), 3); ++#if PJ_HAS_TCP ++ int idx = -1; ++ for (int i=0; icfg.stun_tp_cnt; ++i) { ++ if (ice_st->cfg.stun_tp[i].af == ++ check->rcand->addr.addr.sa_family) ++ { ++ idx = i; ++ break; ++ } ++ } ++ if (idx == -1) { ++ PJ_LOG(4, (ice_st->obj_name, ++ "Comp %d: No STUN sock found.", ++ comp->comp_id)); ++ } ++#endif ++ if (tp_typ == TP_TURN && check->lcand->transport == PJ_CAND_UDP) { + /* Activate channel binding for the remote address +- * for more efficient data transfer using TURN. +- */ ++ * for more efficient data transfer using TURN. ++ */ + status = pj_turn_sock_bind_channel( +- comp->turn[tp_idx].sock, +- &check->rcand->addr, +- sizeof(check->rcand->addr)); ++ comp->turn[tp_idx].sock, ++ &check->rcand->addr, ++ sizeof(check->rcand->addr)); + + /* Disable logging for Send/Data indications */ + PJ_LOG(5,(ice_st->obj_name, +@@ -2211,6 +2517,29 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + pj_sockaddr_get_port(dst_addr), + tp_typ)); + ++ /* TCP, add header */ ++ if (comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP) { ++ /* ++ * RFC6544 ICE requires an agent to demultiplex STUN and ++ * application-layer traffic, since they appear on the same port. This ++ * demultiplexing is described in [RFC5245] and is done using the magic ++ * cookie and other fields of the message. Stream-oriented transports ++ * introduce another wrinkle, since they require a way to frame the ++ * connection so that the application and STUN packets can be extracted ++ * in order to differentiate STUN packets from application-layer ++ * traffic. For this reason, TCP media streams utilizing ICE use the ++ * basic framing provided in RFC 4571 [RFC4571], even if the application ++ * layer protocol is not RTP. ++ */ ++ pj_uint8_t header_1 = size % 256; ++ pj_uint8_t header_0 = size >> 8; ++ pj_memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t)); ++ pj_memcpy(&ice_st->rtp_pkt[2], (unsigned char *)pkt, size); ++ buf = &ice_st->rtp_pkt; ++ size += 2; ++ } ++ + if (tp_typ == TP_TURN) { + if (comp->turn[tp_idx].sock) { + status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, +@@ -2233,7 +2562,7 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + if (status != PJ_SUCCESS) { + goto on_return; + } +- ++ + pj_sockaddr_cp(&comp->dst_addr, dst_addr); + comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr); + } +@@ -2244,9 +2573,13 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, + dest_addr_len = dst_addr_len; + } + +- status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, +- buf, (unsigned)size, 0, +- dest_addr, dest_addr_len); ++ if (comp->stun[tp_idx].sock) { ++ status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, ++ buf, (unsigned)size, 0, ++ dest_addr, dest_addr_len); ++ } else { ++ status = PJ_EINVALIDOP; ++ } + } else { + pj_assert(!"Invalid transport ID"); + status = PJ_EINVALIDOP; +@@ -2292,7 +2625,7 @@ static void check_pending_send(pj_ice_strans *ice_st) + + if (ice_st->num_buf > 0) + ice_st->buf_idx = (ice_st->buf_idx + 1) % ice_st->num_buf; +- ++ + if (ice_st->num_buf > 0 && ice_st->buf_idx != ice_st->empty_idx) { + /* There's some pending send. Send it one by one. */ + pending_send *ps = &ice_st->send_buf[ice_st->buf_idx]; +@@ -2306,6 +2639,253 @@ static void check_pending_send(pj_ice_strans *ice_st) + } + } + ++static void on_peer_connection(pj_stun_session* sess, ++ pj_status_t status, ++ pj_sockaddr_t* remote_addr) ++{ ++ ++ pj_stun_sock *stun_sock; ++ sock_user_data *data; ++ pj_ice_strans_comp *comp; ++ pj_ice_strans *ice_st; ++ ++ stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); ++ /* We have disassociated ourselves from the STUN session */ ++ if (!stun_sock) ++ return; ++ ++ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); ++ /* We have disassociated ourselves from the STUN socket */ ++ if (!data) ++ return; ++ ++ comp = data->comp; ++ ice_st = comp->ice_st; ++ ++ /* Incorrect ICE */ ++ if (!ice_st || !ice_st->ice) ++ return; ++ ++ pj_grp_lock_add_ref(ice_st->grp_lock); ++ ice_sess_on_peer_connection(ice_st->ice, ++ data->transport_id, status, remote_addr); ++ pj_grp_lock_dec_ref(ice_st->grp_lock); ++} ++ ++static void on_peer_reset_connection(pj_stun_session* sess, ++ pj_sockaddr_t* remote_addr) ++{ ++ pj_stun_sock *stun_sock; ++ sock_user_data *data; ++ pj_ice_strans_comp *comp; ++ pj_ice_strans *ice_st; ++ ++ stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); ++ /* We have disassociated ourselves from the STUN session */ ++ if (!stun_sock) ++ return; ++ ++ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); ++ /* We have disassociated ourselves from the STUN socket */ ++ if (!data) ++ return; ++ ++ comp = data->comp; ++ ice_st = comp->ice_st; ++ ++ /* Incorrect ICE */ ++ if (!ice_st || !ice_st->ice) ++ return; ++ ++ pj_grp_lock_add_ref(ice_st->grp_lock); ++ ++ ice_sess_on_peer_reset_connection(ice_st->ice, ++ data->transport_id, remote_addr); ++ pj_grp_lock_dec_ref(ice_st->grp_lock); ++} ++ ++static void on_peer_packet(pj_stun_session* sess, pj_sockaddr_t* remote_addr) ++{ ++ ++ if (!sess || !remote_addr) ++ return; ++ ++ pj_stun_sock *stun_sock; ++ sock_user_data *data; ++ pj_ice_strans_comp *comp; ++ pj_ice_strans *ice_st; ++ ++ stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess); ++ /* We have disassociated ourselves from the STUN session */ ++ if (!stun_sock) ++ return; ++ ++ data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock); ++ /* We have disassociated ourselves from the STUN socket */ ++ if (!data) ++ return; ++ ++ comp = data->comp; ++ if (!comp) ++ return; ++ ++ ice_st = comp->ice_st; ++ /* Incorrect ICE */ ++ if (!ice_st || !ice_st->ice) ++ return; ++ ++ pj_grp_lock_add_ref(ice_st->grp_lock); ++ ice_sess_on_peer_packet(ice_st->ice, data->transport_id, remote_addr); ++ pj_grp_lock_dec_ref(ice_st->grp_lock); ++} ++ ++#if PJ_HAS_TCP ++static pj_status_t ice_wait_tcp_connection(pj_ice_sess *ice, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &ice->clist.checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ int idx = -1; ++ for (int i=0; icfg.stun_tp_cnt; ++i) ++ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { ++ idx = i; ++ break; ++ } ++ ++ if (idx == -1) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ if (st_comp->stun[idx].sock) { ++ pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); ++ if (!sess) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ pj_stun_session_callback(sess)->on_peer_connection = ++ &on_peer_connection; ++ pj_stun_session_callback(sess)->on_peer_reset_connection = ++ &on_peer_reset_connection; ++ pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; ++ ++ return pj_stun_sock_connect_active(st_comp->stun[idx].sock, ++ &rcand->addr, ++ rcand->addr.addr.sa_family); ++ } ++ ++ return PJ_EINVAL; ++} ++ ++static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &ice->clist.checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ int idx = -1; ++ for (int i=0; icfg.stun_tp_cnt; ++i) ++ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { ++ idx = i; ++ break; ++ } ++ ++ if (idx == -1) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN sock found.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ ++ if (st_comp->stun[idx].sock) { ++ pj_stun_session *sess = pj_stun_sock_get_session(st_comp->stun[idx].sock); ++ if (!sess) { ++ PJ_LOG(4, (ice_st->obj_name, "Comp %d: No STUN session.", ++ st_comp->comp_id)); ++ return PJ_EINVAL; ++ } ++ pj_stun_session_callback(sess)->on_peer_connection = ++ &on_peer_connection; ++ pj_stun_session_callback(sess)->on_peer_reset_connection = ++ &on_peer_reset_connection; ++ pj_stun_session_callback(sess)->on_peer_packet = &on_peer_packet; ++ return pj_stun_sock_reconnect_active(st_comp->stun[idx].sock, ++ &rcand->addr, ++ rcand->addr.addr.sa_family); ++ } ++ ++ return PJ_EINVAL; ++} ++ ++static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice, ++ unsigned check_id) ++{ ++ pj_ice_sess_check *check = &ice->clist.checks[check_id]; ++ const pj_ice_sess_cand *lcand = check->lcand; ++ const pj_ice_sess_cand *rcand = check->rcand; ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[lcand->comp_id - 1]; ++ ++ int idx = -1; ++ for (int i=0; icfg.stun_tp_cnt; ++i) ++ if (ice_st->cfg.stun_tp[i].af == rcand->addr.addr.sa_family) { ++ idx = i; ++ break; ++ } ++ ++ if (idx != -1 && st_comp->stun[idx].sock) { ++ const pj_ice_sess_cand *rcand = check->rcand; ++ return pj_stun_sock_close(st_comp->stun[idx].sock, &rcand->addr); ++ } ++ ++ return PJ_EINVAL; ++} ++ ++static pj_status_t ice_close_remaining_tcp(pj_ice_sess *ice) ++{ ++ for (int i = 0; i < ice->comp_cnt; i++) { ++ pj_ice_strans *ice_st = (pj_ice_strans *)ice->user_data; ++ pj_ice_strans_comp *st_comp = ice_st->comp[i]; ++ ++ const pj_ice_sess_check *valid_check = pj_ice_strans_get_valid_pair(ice_st, i + 1); ++ ++ if (!valid_check) { ++ continue; ++ } ++ ++ if (valid_check->lcand->type != PJ_ICE_CAND_TYPE_RELAYED ++ && valid_check->rcand->type != PJ_ICE_CAND_TYPE_RELAYED) { ++ // If we're not a turn session we can close it. ++ for (int j = 0; j < ice_st->cfg.turn_tp_cnt; ++j) { ++ if (st_comp->turn[j].sock) { ++ pj_turn_sock_destroy(st_comp->turn[j].sock); ++ st_comp->turn[j].sock = NULL; ++ } ++ } ++ } ++ for (int j=0; j< ice_st->cfg.stun_tp_cnt; ++j) { ++ if (st_comp->stun[j].sock) { ++ pj_stun_sock_close_all_except(st_comp->stun[j].sock, &valid_check->rcand->addr); ++ } ++ if (ice_st->cfg.stun_tp[j].af != valid_check->rcand->addr.addr.sa_family) { ++ // If the valid candidate got the other address family we can close. ++ pj_stun_sock_destroy(st_comp->stun[j].sock); ++ } ++ } ++ } ++ ++ return PJ_SUCCESS; ++} ++ ++#endif ++ + /* Notifification when asynchronous send operation via STUN/TURN + * has completed. + */ +@@ -2314,7 +2894,8 @@ static pj_bool_t on_data_sent(pj_ice_strans *ice_st, pj_ssize_t sent) + if (ice_st->destroy_req || !ice_st->is_pending) + return PJ_TRUE; + +- if (ice_st->call_send_cb && ice_st->cb.on_data_sent) { ++ if (ice_st->call_send_cb && ice_st->cb.on_data_sent ++ && sent == ice_st->last_data_len /* Only app data should be announced */) { + (*ice_st->cb.on_data_sent)(ice_st, sent); + } + +@@ -2472,7 +3053,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + { + /* We get an IPv4 mapped address for our IPv6 + * host address. +- */ ++ */ + comp->ipv4_mapped = PJ_TRUE; + + /* Find other host candidates with the same (IPv6) +@@ -2484,7 +3065,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + + if (comp->cand_list[i].type != PJ_ICE_CAND_TYPE_HOST) + continue; +- ++ + a1 = &comp->cand_list[i].addr; + a2 = &cand->base_addr; + if (pj_memcmp(pj_sockaddr_get_addr(a1), +@@ -2501,7 +3082,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + pj_sockaddr_cp(&cand->base_addr, &info.mapped_addr); + pj_sockaddr_cp(&cand->rel_addr, &info.mapped_addr); + } +- ++ + /* Eliminate the srflx candidate if the address is + * equal to other (host) candidates. + */ +@@ -2551,7 +3132,8 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + &cand->base_addr, + &cand->rel_addr, + pj_sockaddr_get_len(&cand->addr), +- NULL); ++ NULL, ++ cand->transport); + } + } + +@@ -2576,7 +3158,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + if (op == PJ_STUN_SOCK_MAPPED_ADDR_CHANGE && + ice_st->cb.on_ice_complete) + { +- (*ice_st->cb.on_ice_complete)(ice_st, ++ (*ice_st->cb.on_ice_complete)(ice_st, + PJ_ICE_STRANS_OP_ADDR_CHANGE, + status); + } +@@ -2632,6 +3214,10 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + } + } + break; ++ case PJ_STUN_SESS_DESTROYED: ++ case PJ_STUN_TCP_CONNECT_ERROR: ++ default: ++ break; + } + + return pj_grp_lock_dec_ref(ice_st->grp_lock)? PJ_FALSE : PJ_TRUE; +@@ -2671,16 +3257,105 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock, + + } else { + +- /* Hand over the packet to ICE */ +- status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, +- data->transport_id, pkt, pkt_len, +- peer_addr, addr_len); +- +- if (status != PJ_SUCCESS) { +- ice_st_perror(comp->ice_st, +- "Error processing packet from TURN relay", +- status); +- } ++ /* Hand over the packet to ICE */ ++ if (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && pkt_len > 0) { ++ unsigned parsed = 0; ++ pj_status_t status; ++ ++ do { ++ pj_uint16_t leftover = pkt_len - parsed; ++ pj_uint8_t *current_packet = ((pj_uint8_t *)(pkt)) + parsed; ++ ++ /** ++ * RFC6544, the packet is wrapped into a packet following the ++ * RFC4571 ++ */ ++ pj_bool_t store_remaining = PJ_TRUE; ++ if (comp->ice_st->rx_buffer_size || ++ comp->ice_st->rx_wanted_size) ++ { ++ /* a single packet left to process */ ++ if (comp->ice_st->rx_buffer_size == 1 && comp->ice_st->rx_wanted_size == 0) { ++ /* get last frame's lenght from its header */ ++ leftover = GETVAL16H(comp->ice_st->rx_buffer, ++ current_packet); ++ /* adjust counters accordingly */ ++ comp->ice_st->rx_buffer_size = 0; ++ current_packet++; ++ parsed++; ++ ++ if (leftover + parsed <= pkt_len) { ++ /* we didn't get what we were promissed in the ++ * header. furthermore, this was the last frame and ++ * therefore we're done. ++ */ ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ comp->ice_st->rx_wanted_size = leftover; ++ } ++ } else if (leftover + comp->ice_st->rx_buffer_size >= ++ comp->ice_st->rx_wanted_size) ++ { ++ /* We have enough leftover bytes in buffer to build a new ++ * packet and parse it ++ */ ++ store_remaining = PJ_FALSE; ++ ++ pj_uint16_t eaten_bytes = comp->ice_st->rx_wanted_size - ++ comp->ice_st->rx_buffer_size; ++ pj_memcpy(comp->ice_st->rx_buffer + ++ comp->ice_st->rx_buffer_size, ++ current_packet, eaten_bytes); ++ ++ leftover = comp->ice_st->rx_wanted_size; ++ current_packet = comp->ice_st->rx_buffer; ++ parsed += eaten_bytes; ++ ++ comp->ice_st->rx_buffer_size = 0; ++ comp->ice_st->rx_wanted_size = 0; ++ } ++ } else if (leftover > 1) { ++ leftover = GETVAL16H(current_packet, current_packet+1); ++ current_packet += 2; ++ parsed += 2; ++ if (leftover + parsed <= pkt_len) { ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ comp->ice_st->rx_wanted_size = leftover; ++ } ++ } ++ ++ if (store_remaining) { ++ leftover = pkt_len - parsed; ++ pj_memcpy(comp->ice_st->rx_buffer + ++ comp->ice_st->rx_buffer_size, ++ current_packet, leftover); ++ comp->ice_st->rx_buffer_size += leftover; ++ status = PJ_SUCCESS; ++ break; ++ } ++ ++ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, ++ data->transport_id, ++ current_packet, leftover, ++ peer_addr, addr_len); ++ if (status != PJ_SUCCESS) { ++ ice_st_perror(comp->ice_st, ++ "Error processing packet from TURN relay", ++ status); ++ } ++ } while (parsed < pkt_len); ++ } else { ++ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, ++ data->transport_id, pkt, pkt_len, ++ peer_addr, addr_len); ++ if (status != PJ_SUCCESS) ++ ice_st_perror(comp->ice_st, ++ "Error processing packet from TURN relay", ++ status); ++ } + } + + pj_grp_lock_dec_ref(comp->ice_st->grp_lock); +@@ -2816,10 +3491,11 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, + cand->local_pref, + &cand->foundation, + &cand->addr, +- &cand->base_addr, ++ &cand->base_addr, + &cand->rel_addr, + pj_sockaddr_get_len(&cand->addr), +- NULL); ++ NULL, ++ cand->transport); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(comp->ice_st->obj_name, status, + "Comp %d/%d: failed to add TURN (tpid=%d) to ICE", +@@ -2953,4 +3629,3 @@ on_return: + + pj_log_pop_indent(); + } +- +diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c +index cf94c4e44..cb35770cd 100644 +--- a/pjnath/src/pjnath/nat_detect.c ++++ b/pjnath/src/pjnath/nat_detect.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -109,8 +109,8 @@ typedef struct nat_detect_session + } nat_detect_session; + + +-static void on_read_complete(pj_ioqueue_key_t *key, +- pj_ioqueue_op_key_t *op_key, ++static void on_read_complete(pj_ioqueue_key_t *key, ++ pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read); + static void on_request_complete(pj_stun_session *sess, + pj_status_t status, +@@ -201,7 +201,7 @@ static pj_status_t get_local_interface(const pj_sockaddr *server, + } + + pj_sockaddr_cp(local_addr, &tmp); +- ++ + pj_sock_close(sock); + return PJ_SUCCESS; + } +@@ -241,7 +241,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, + /* + * Init NAT detection session. + */ +- pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, ++ pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, + PJNATH_POOL_INC_NATCK, NULL); + if (!pool) + return PJ_ENOMEM; +@@ -317,7 +317,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, + pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); + ioqueue_cb.on_read_complete = &on_read_complete; + +- status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue, ++ status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue, + sess->sock, sess->grp_lock, sess, + &ioqueue_cb, &sess->key); + if (status != PJ_SUCCESS) +@@ -330,7 +330,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type2(const pj_sockaddr *server, + sess_cb.on_request_complete = &on_request_complete; + sess_cb.on_send_msg = &on_send_msg; + status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, +- PJ_FALSE, sess->grp_lock, &sess->stun_sess); ++ PJ_FALSE, sess->grp_lock, &sess->stun_sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) + goto on_error; + +@@ -359,7 +359,7 @@ on_error: + + static void sess_destroy(nat_detect_session *sess) + { +- if (sess->stun_sess) { ++ if (sess->stun_sess) { + pj_stun_session_destroy(sess->stun_sess); + sess->stun_sess = NULL; + } +@@ -422,8 +422,8 @@ static void end_session(nat_detect_session *sess, + /* + * Callback upon receiving packet from network. + */ +-static void on_read_complete(pj_ioqueue_key_t *key, +- pj_ioqueue_op_key_t *op_key, ++static void on_read_complete(pj_ioqueue_key_t *key, ++ pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) + { + nat_detect_session *sess; +@@ -440,19 +440,19 @@ static void on_read_complete(pj_ioqueue_key_t *key, + + if (bytes_read < 0) { + if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && +- -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && +- -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) ++ -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && ++ -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) + { + /* Permanent error */ +- end_session(sess, (pj_status_t)-bytes_read, ++ end_session(sess, (pj_status_t)-bytes_read, + PJ_STUN_NAT_TYPE_ERR_UNKNOWN); + goto on_return; + } + + } else if (bytes_read > 0) { + pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read, +- PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, +- NULL, NULL, ++ PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, ++ NULL, NULL, + &sess->src_addr, sess->src_addr_len); + } + +@@ -460,7 +460,7 @@ static void on_read_complete(pj_ioqueue_key_t *key, + sess->rx_pkt_len = sizeof(sess->rx_pkt); + sess->src_addr_len = sizeof(sess->src_addr); + status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len, +- PJ_IOQUEUE_ALWAYS_ASYNC, ++ PJ_IOQUEUE_ALWAYS_ASYNC, + &sess->src_addr, &sess->src_addr_len); + + if (status != PJ_EPENDING) { +@@ -595,11 +595,11 @@ static void on_request_complete(pj_stun_session *stun_sess, + /* Send Test 1B only when Test 2 completes. Must not send Test 1B + * before Test 2 completes to avoid creating mapping on the NAT. + */ +- if (!sess->result[ST_TEST_1B].executed && ++ if (!sess->result[ST_TEST_1B].executed && + sess->result[ST_TEST_2].complete && + sess->result[ST_TEST_2].status != PJ_SUCCESS && + sess->result[ST_TEST_1].complete && +- sess->result[ST_TEST_1].status == PJ_SUCCESS) ++ sess->result[ST_TEST_1].status == PJ_SUCCESS) + { + cmp = pj_sockaddr_cmp(&sess->local_addr, &sess->result[ST_TEST_1].ma); + if (cmp != 0) +@@ -661,7 +661,7 @@ static void on_request_complete(pj_stun_session *stun_sess, + switch (sess->result[ST_TEST_1].status) { + case PJNATH_ESTUNTIMEDOUT: + /* +- * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. ++ * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. + */ + end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); + break; +@@ -694,7 +694,7 @@ static void on_request_complete(pj_stun_session *stun_sess, + /* + * We've got other error with Test 2. + */ +- end_session(sess, sess->result[ST_TEST_2].status, ++ end_session(sess, sess->result[ST_TEST_2].status, + PJ_STUN_NAT_TYPE_ERR_UNKNOWN); + break; + } +@@ -774,14 +774,14 @@ static void on_request_complete(pj_stun_session *stun_sess, + * It could be that port 3489 is blocked, while the + * NAT itself looks to be a Restricted one. + */ +- end_session(sess, PJ_SUCCESS, ++ end_session(sess, PJ_SUCCESS, + PJ_STUN_NAT_TYPE_RESTRICTED); + break; + default: + /* Can't distinguish between Symmetric and Port + * Restricted, so set the type to Unknown + */ +- end_session(sess, PJ_SUCCESS, ++ end_session(sess, PJ_SUCCESS, + PJ_STUN_NAT_TYPE_ERR_UNKNOWN); + break; + } +@@ -799,7 +799,7 @@ static void on_request_complete(pj_stun_session *stun_sess, + /* + * We've got other error with Test 2. + */ +- end_session(sess, sess->result[ST_TEST_2].status, ++ end_session(sess, sess->result[ST_TEST_2].status, + PJ_STUN_NAT_TYPE_ERR_UNKNOWN); + break; + } +@@ -809,7 +809,7 @@ static void on_request_complete(pj_stun_session *stun_sess, + /* + * We've got other error with Test 1. + */ +- end_session(sess, sess->result[ST_TEST_1].status, ++ end_session(sess, sess->result[ST_TEST_1].status, + PJ_STUN_NAT_TYPE_ERR_UNKNOWN); + break; + } +@@ -841,15 +841,15 @@ static pj_status_t send_test(nat_detect_session *sess, + tsx_id[2] = test_id; + + /* Create BIND request */ +- status = pj_stun_session_create_req(sess->stun_sess, ++ status = pj_stun_session_create_req(sess->stun_sess, + PJ_STUN_BINDING_REQUEST, magic, +- (pj_uint8_t*)tsx_id, ++ (pj_uint8_t*)tsx_id, + &sess->result[test_id].tdata); + if (status != PJ_SUCCESS) + goto on_error; + + /* Add CHANGE-REQUEST attribute */ +- status = pj_stun_msg_add_uint_attr(sess->pool, ++ status = pj_stun_msg_add_uint_attr(sess->pool, + sess->result[test_id].tdata->msg, + PJ_STUN_ATTR_CHANGE_REQUEST, + change_flag); +@@ -868,15 +868,16 @@ static pj_status_t send_test(nat_detect_session *sess, + sess->cur_server = &sess->server; + } + +- PJ_LOG(5,(sess->pool->obj_name, +- "Performing %s to %s:%d", ++ PJ_LOG(5,(sess->pool->obj_name, ++ "Performing %s to %s:%d", + test_names[test_id], + pj_sockaddr_print(sess->cur_server, addr, sizeof(addr), 2), + pj_sockaddr_get_port(sess->cur_server))); + + /* Send the request */ + status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, +- PJ_TRUE, sess->cur_server, ++ (pj_stun_session_tp_type(sess->stun_sess) == PJ_STUN_TP_UDP), ++ sess->cur_server, + pj_sockaddr_get_len(sess->cur_server), + sess->result[test_id].tdata); + if (status != PJ_SUCCESS) +diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c +index 4a3e165f5..e117fef39 100644 +--- a/pjnath/src/pjnath/stun_session.c ++++ b/pjnath/src/pjnath/stun_session.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -48,6 +48,8 @@ struct pj_stun_session + + pj_stun_tx_data pending_request_list; + pj_stun_tx_data cached_response_list; ++ ++ pj_stun_tp_type conn_type; + }; + + #define SNAME(s_) ((s_)->pool->obj_name) +@@ -66,7 +68,7 @@ struct pj_stun_session + + + static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, +- pj_status_t status, ++ pj_status_t status, + const pj_stun_msg *response, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); +@@ -77,7 +79,7 @@ static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx); + static void stun_sess_on_destroy(void *comp); + static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force); + +-static pj_stun_tsx_cb tsx_cb = ++static pj_stun_tsx_cb tsx_cb = + { + &stun_tsx_on_complete, + &stun_tsx_on_send_msg, +@@ -109,7 +111,7 @@ static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess, + while (tdata != &sess->pending_request_list) { + pj_assert(sizeof(tdata->msg_key)==sizeof(msg->hdr.tsx_id)); + if (tdata->msg_magic == msg->hdr.magic && +- pj_memcmp(tdata->msg_key, msg->hdr.tsx_id, ++ pj_memcmp(tdata->msg_key, msg->hdr.tsx_id, + sizeof(msg->hdr.tsx_id))==0) + { + return tdata; +@@ -127,7 +129,7 @@ static pj_status_t create_tdata(pj_stun_session *sess, + pj_stun_tx_data *tdata; + + /* Create pool and initialize basic tdata attributes */ +- pool = pj_pool_create(sess->cfg->pf, "tdata%p", ++ pool = pj_pool_create(sess->cfg->pf, "tdata%p", + TDATA_POOL_SIZE, TDATA_POOL_INC, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + +@@ -150,7 +152,7 @@ static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx) + pj_stun_client_tsx_stop(tsx); + if (tdata) { + pj_stun_session *sess = tdata->sess; +- ++ + pj_grp_lock_acquire(sess->grp_lock); + tsx_erase(sess, tdata); + destroy_tdata(tdata, PJ_TRUE); +@@ -268,16 +270,16 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, + pj_str_t realm, username, nonce, auth_key; + + /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute +- * to the request. The server SHOULD include a SOFTWARE attribute in all ++ * to the request. The server SHOULD include a SOFTWARE attribute in all + * responses. + * + * If magic value is not PJ_STUN_MAGIC, only apply the attribute for + * responses. + */ +- if (sess->srv_name.slen && ++ if (sess->srv_name.slen && + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL && + (PJ_STUN_IS_RESPONSE(msg->hdr.type) || +- (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) ++ (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) + { + pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, + &sess->srv_name); +@@ -309,9 +311,9 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, + } + + /* Add NONCE when desired */ +- if (nonce.slen && ++ if (nonce.slen && + (PJ_STUN_IS_REQUEST(msg->hdr.type) || +- PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) ++ PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) + { + status = pj_stun_msg_add_string_attr(pool, msg, + PJ_STUN_ATTR_NONCE, +@@ -328,7 +330,7 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, + + /* Add FINGERPRINT attribute if necessary */ + if (sess->use_fingerprint) { +- status = pj_stun_msg_add_uint_attr(pool, msg, ++ status = pj_stun_msg_add_uint_attr(pool, msg, + PJ_STUN_ATTR_FINGERPRINT, 0); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + } +@@ -352,7 +354,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, + + if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM) + return PJ_SUCCESS; +- ++ + if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { + sess->auth_retry = 0; + return PJ_SUCCESS; +@@ -367,7 +369,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, + return PJNATH_EINSTUNMSG; + } + +- if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || ++ if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || + ea->err_code == PJ_STUN_SC_STALE_NONCE) + { + const pj_stun_nonce_attr *anonce; +@@ -433,7 +435,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, + continue; + } + +- tdata->msg->attr[tdata->msg->attr_count++] = ++ tdata->msg->attr[tdata->msg->attr_count++] = + pj_stun_attr_clone(tdata->pool, asrc); + } + +@@ -445,8 +447,8 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, + PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication")); + + /* Retry the request */ +- status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE, +- request->retransmit, src_addr, ++ status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE, ++ request->retransmit, src_addr, + src_addr_len, tdata); + + } else { +@@ -457,7 +459,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, + } + + static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, +- pj_status_t status, ++ pj_status_t status, + const pj_stun_msg *response, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +@@ -482,12 +484,12 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, + src_addr_len, ¬ify_user); + + if (notify_user && sess->cb.on_request_complete) { +- (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, ++ (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, + response, src_addr, src_addr_len); + } + + /* Destroy the transmit data. This will remove the transaction +- * from the pending list too. ++ * from the pending list too. + */ + if (status == PJNATH_ESTUNTIMEDOUT) + destroy_tdata(tdata, PJ_TRUE); +@@ -514,15 +516,15 @@ static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, + + /* Lock the session and prevent user from destroying us in the callback */ + pj_grp_lock_acquire(sess->grp_lock); +- ++ + if (sess->is_destroying) { + /* Stray timer */ + pj_grp_lock_release(sess->grp_lock); + return PJ_EINVALIDOP; + } + +- status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, +- pkt_size, tdata->dst_addr, ++ status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, ++ pkt_size, tdata->dst_addr, + tdata->addr_len); + if (pj_grp_lock_release(sess->grp_lock)) + return PJ_EGONE; +@@ -537,7 +539,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, + const pj_stun_session_cb *cb, + pj_bool_t fingerprint, + pj_grp_lock_t *grp_lock, +- pj_stun_session **p_sess) ++ pj_stun_session **p_sess, ++ pj_stun_tp_type conn_type) + { + pj_pool_t *pool; + pj_stun_session *sess; +@@ -548,7 +551,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, + if (name==NULL) + name = "stuse%p"; + +- pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS, ++ pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS, + PJNATH_POOL_INC_STUN_SESS, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + +@@ -558,6 +561,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, + pj_memcpy(&sess->cb, cb, sizeof(*cb)); + sess->use_fingerprint = fingerprint; + sess->log_flag = 0xFFFF; ++ sess->conn_type = conn_type; + + if (grp_lock) { + sess->grp_lock = grp_lock; +@@ -727,7 +731,7 @@ static pj_status_t get_auth(pj_stun_session *sess, + tdata->auth_info.username = sess->cred.data.static_cred.username; + tdata->auth_info.nonce = sess->cred.data.static_cred.nonce; + +- pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, ++ pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, + &tdata->auth_info.realm, + &tdata->auth_info.username, + sess->cred.data.static_cred.data_type, +@@ -739,16 +743,16 @@ static pj_status_t get_auth(pj_stun_session *sess, + pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; + pj_status_t rc; + +- rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, ++ rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, + tdata->pool, +- &tdata->auth_info.realm, ++ &tdata->auth_info.realm, + &tdata->auth_info.username, +- &tdata->auth_info.nonce, ++ &tdata->auth_info.nonce, + &data_type, &password); + if (rc != PJ_SUCCESS) + return rc; + +- pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, ++ pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, + &tdata->auth_info.realm, &tdata->auth_info.username, + data_type, &password); + +@@ -782,7 +786,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, + goto on_error; + + /* Create STUN message */ +- status = pj_stun_msg_create(tdata->pool, method, magic, ++ status = pj_stun_msg_create(tdata->pool, method, magic, + tsx_id, &tdata->msg); + if (status != PJ_SUCCESS) + goto on_error; +@@ -793,7 +797,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, + pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id, + sizeof(tdata->msg->hdr.tsx_id)); + +- ++ + /* Get authentication information for the request */ + if (sess->auth_type == PJ_STUN_AUTH_NONE) { + /* No authentication */ +@@ -856,7 +860,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, + + /* Create STUN message */ + msg_type |= PJ_STUN_INDICATION_BIT; +- status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, ++ status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, + NULL, &tdata->msg); + if (status != PJ_SUCCESS) { + pj_pool_safe_release(&tdata->pool); +@@ -895,7 +899,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, + } + + /* Create STUN response message */ +- status = pj_stun_msg_create_response(tdata->pool, rdata->msg, ++ status = pj_stun_msg_create_response(tdata->pool, rdata->msg, + err_code, err_msg, &tdata->msg); + if (status != PJ_SUCCESS) { + pj_pool_safe_release(&tdata->pool); +@@ -906,7 +910,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, + /* copy the request's transaction ID as the transaction key. */ + pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id)); + tdata->msg_magic = rdata->msg->hdr.magic; +- pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, ++ pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, + sizeof(rdata->msg->hdr.tsx_id)); + + /* copy the credential found in the request */ +@@ -925,8 +929,8 @@ static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, + unsigned pkt_size, const pj_sockaddr_t *addr) + { + char dst_name[PJ_INET6_ADDRSTRLEN+10]; +- +- if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && ++ ++ if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) || + (PJ_STUN_IS_RESPONSE(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) || +@@ -938,13 +942,13 @@ static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, + + pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3); + +- PJ_LOG(5,(SNAME(sess), ++ PJ_LOG(5,(SNAME(sess), + "TX %d bytes STUN message to %s:\n" + "--- begin STUN message ---\n" + "%s" + "--- end of STUN message ---\n", +- pkt_size, dst_name, +- pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), ++ pkt_size, dst_name, ++ pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), + NULL))); + + } +@@ -979,7 +983,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + tdata->retransmit = retransmit; + + /* Apply options */ +- status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, ++ status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, + tdata->msg); + if (status != PJ_SUCCESS) { + pj_stun_msg_destroy_tdata(sess, tdata); +@@ -988,8 +992,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + } + + /* Encode message */ +- status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, +- tdata->max_len, 0, ++ status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, ++ tdata->max_len, 0, + &tdata->auth_info.auth_key, + &tdata->pkt_size); + if (status != PJ_SUCCESS) { +@@ -1019,11 +1023,11 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + + /* Send the request! */ + status = pj_stun_client_tsx_send_msg(tdata->client_tsx, retransmit, +- tdata->pkt, ++ tdata->pkt, + (unsigned)tdata->pkt_size); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + pj_stun_msg_destroy_tdata(sess, tdata); +- LOG_ERR_(sess, "Error sending STUN request", status); ++ LOG_ERR_(sess, "Error sending STUN request (pj_stun_client_tsx_send_msg", status); + goto on_return; + } + +@@ -1032,9 +1036,9 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + + } else { + /* Otherwise for non-request message, send directly to transport. */ +- if (cache_res && ++ if (cache_res && + (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) || +- PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) ++ PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) + { + /* Requested to keep the response in the cache */ + pj_time_val timeout; +@@ -1053,7 +1057,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + * is still valid when cache timeout callback is called. + */ + pj_grp_lock_add_ref(sess->grp_lock); +- ++ + pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer)); + pj_timer_entry_init(&tdata->res_timer, PJ_FALSE, tdata, + &on_cache_timeout); +@@ -1075,12 +1079,12 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + } + + /* Send to transport directly. */ +- status = sess->cb.on_send_msg(sess, token, tdata->pkt, ++ status = sess->cb.on_send_msg(sess, token, tdata->pkt, + tdata->pkt_size, server, addr_len); + + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + pj_stun_msg_destroy_tdata(sess, tdata); +- LOG_ERR_(sess, "Error sending STUN request", status); ++ LOG_ERR_(sess, "Error sending STUN request (pj_stun_session_send_msg)", status); + goto on_return; + } + +@@ -1103,13 +1107,13 @@ on_return: + /* + * Create and send STUN response message. + */ +-PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, ++PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, + const pj_stun_rx_data *rdata, +- unsigned code, ++ unsigned code, + const char *errmsg, + void *token, +- pj_bool_t cache, +- const pj_sockaddr_t *dst_addr, ++ pj_bool_t cache, ++ const pj_sockaddr_t *dst_addr, + unsigned addr_len) + { + pj_status_t status; +@@ -1122,8 +1126,8 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, + return PJ_EINVALIDOP; + } + +- status = pj_stun_session_create_res(sess, rdata, code, +- (errmsg?pj_cstr(&reason,errmsg):NULL), ++ status = pj_stun_session_create_res(sess, rdata, code, ++ (errmsg?pj_cstr(&reason,errmsg):NULL), + &tdata); + if (status != PJ_SUCCESS) { + pj_grp_lock_release(sess->grp_lock); +@@ -1139,7 +1143,7 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, + + + /* +- * Cancel outgoing STUN transaction. ++ * Cancel outgoing STUN transaction. + */ + PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, + pj_stun_tx_data *tdata, +@@ -1158,7 +1162,7 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, + } + + if (notify) { +- (sess->cb.on_request_complete)(sess, notify_status, tdata->token, ++ (sess->cb.on_request_complete)(sess, notify_status, tdata->token, + tdata, NULL, NULL, 0); + } + +@@ -1220,7 +1224,7 @@ static pj_status_t send_response(pj_stun_session *sess, void *token, + out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len); + + /* Encode */ +- status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, ++ status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, + &auth_info->auth_key, &out_len); + if (status != PJ_SUCCESS) { + LOG_ERR_(sess, "Error encoding message", status); +@@ -1231,7 +1235,7 @@ static pj_status_t send_response(pj_stun_session *sess, void *token, + dump_tx_msg(sess, response, (unsigned)out_len, addr); + + /* Send packet */ +- status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len, ++ status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len, + addr, addr_len); + + return status; +@@ -1250,18 +1254,18 @@ static pj_status_t authenticate_req(pj_stun_session *sess, + pj_stun_msg *response; + pj_status_t status; + +- if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || ++ if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || + sess->auth_type == PJ_STUN_AUTH_NONE) + { + return PJ_SUCCESS; + } + +- status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, ++ status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, + &sess->cred, tmp_pool, &rdata->info, + &response); + if (status != PJ_SUCCESS && response != NULL) { + PJ_PERROR(5,(SNAME(sess), status, "Message authentication failed")); +- send_response(sess, token, tmp_pool, response, &rdata->info, ++ send_response(sess, token, tmp_pool, response, &rdata->info, + PJ_FALSE, src_addr, src_addr_len); + } + +@@ -1284,7 +1288,7 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, + /* Lookup pending client transaction */ + tdata = tsx_lookup(sess, msg); + if (tdata == NULL) { +- PJ_LOG(5,(SNAME(sess), ++ PJ_LOG(5,(SNAME(sess), + "Transaction not found, response silently discarded")); + return PJ_SUCCESS; + } +@@ -1295,11 +1299,11 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, + /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE + * is specified in the option. + */ +- if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && +- tdata->auth_info.auth_key.slen != 0 && ++ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && ++ tdata->auth_info.auth_key.slen != 0 && + pj_stun_auth_valid_for_msg(msg)) + { +- status = pj_stun_authenticate_response(pkt, pkt_len, msg, ++ status = pj_stun_authenticate_response(pkt, pkt_len, msg, + &tdata->auth_info.auth_key); + if (status != PJ_SUCCESS) { + PJ_PERROR(5,(SNAME(sess), status, +@@ -1308,11 +1312,11 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, + } + } + +- /* Pass the response to the transaction. ++ /* Pass the response to the transaction. + * If the message is accepted, transaction callback will be called, + * and this will call the session callback too. + */ +- status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg, ++ status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg, + src_addr, src_addr_len); + if (status != PJ_SUCCESS) { + return status; +@@ -1336,7 +1340,7 @@ static pj_status_t check_cached_response(pj_stun_session *sess, + while (t != &sess->cached_response_list) { + if (t->msg_magic == msg->hdr.magic && + t->msg->hdr.type == msg->hdr.type && +- pj_memcmp(t->msg_key, msg->hdr.tsx_id, ++ pj_memcmp(t->msg_key, msg->hdr.tsx_id, + sizeof(msg->hdr.tsx_id))==0) + { + break; +@@ -1347,10 +1351,10 @@ static pj_status_t check_cached_response(pj_stun_session *sess, + if (t != &sess->cached_response_list) { + /* Found response in the cache */ + +- PJ_LOG(5,(SNAME(sess), ++ PJ_LOG(5,(SNAME(sess), + "Request retransmission, sending cached response")); + +- send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info, ++ send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info, + PJ_TRUE, src_addr, src_addr_len); + return PJ_SUCCESS; + } +@@ -1383,8 +1387,8 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, + * is specified in the option. + */ + if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) { +- status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt, +- in_pkt_len,&rdata, tmp_pool, src_addr, ++ status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt, ++ in_pkt_len,&rdata, tmp_pool, src_addr, + src_addr_len); + if (status != PJ_SUCCESS) { + return status; +@@ -1400,11 +1404,11 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, + pj_stun_msg *response; + + err_text = pj_str("Callback is not set to handle request"); +- status = pj_stun_msg_create_response(tmp_pool, msg, +- PJ_STUN_SC_BAD_REQUEST, ++ status = pj_stun_msg_create_response(tmp_pool, msg, ++ PJ_STUN_SC_BAD_REQUEST, + &err_text, &response); + if (status == PJ_SUCCESS && response) { +- status = send_response(sess, token, tmp_pool, response, ++ status = send_response(sess, token, tmp_pool, response, + NULL, PJ_FALSE, src_addr, src_addr_len); + } + } +@@ -1440,8 +1444,8 @@ static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg, + unsigned pkt_size, const pj_sockaddr_t *addr) + { + char src_info[PJ_INET6_ADDRSTRLEN+10]; +- +- if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && ++ ++ if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) || + (PJ_STUN_IS_RESPONSE(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) || +@@ -1459,7 +1463,7 @@ static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg, + "%s" + "--- end of STUN message ---\n", + pkt_size, src_info, +- pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), ++ pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), + NULL))); + + } +@@ -1494,7 +1498,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + + /* Try to parse the message */ + status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet, +- pkt_size, options, ++ pkt_size, options, + &msg, parsed_len, &response); + if (status != PJ_SUCCESS) { + LOG_ERR_(sess, "STUN msg_decode() error", status); +@@ -1508,7 +1512,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + dump_rx_msg(sess, msg, (unsigned)pkt_size, src_addr); + + /* For requests, check if we have cached response */ +- status = check_cached_response(sess, sess->rx_pool, msg, ++ status = check_cached_response(sess, sess->rx_pool, msg, + src_addr, src_addr_len); + if (status == PJ_SUCCESS) { + goto on_return; +@@ -1518,23 +1522,23 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { +- status = on_incoming_response(sess, options, +- (const pj_uint8_t*) packet, +- (unsigned)pkt_size, msg, ++ status = on_incoming_response(sess, options, ++ (const pj_uint8_t*) packet, ++ (unsigned)pkt_size, msg, + src_addr, src_addr_len); + + } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { + +- status = on_incoming_request(sess, options, token, sess->rx_pool, +- (const pj_uint8_t*) packet, +- (unsigned)pkt_size, ++ status = on_incoming_request(sess, options, token, sess->rx_pool, ++ (const pj_uint8_t*) packet, ++ (unsigned)pkt_size, + msg, src_addr, src_addr_len); + + } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { + +- status = on_incoming_indication(sess, token, sess->rx_pool, +- (const pj_uint8_t*) packet, +- (unsigned)pkt_size, msg, src_addr, ++ status = on_incoming_indication(sess, token, sess->rx_pool, ++ (const pj_uint8_t*) packet, ++ (unsigned)pkt_size, msg, src_addr, + src_addr_len); + + } else { +@@ -1551,3 +1555,12 @@ on_return: + return status; + } + ++PJ_DECL(pj_stun_session_cb *) pj_stun_session_callback(pj_stun_session *sess) ++{ ++ return sess ? &sess->cb : NULL; ++} ++ ++PJ_DECL(pj_stun_tp_type) pj_stun_session_tp_type(pj_stun_session *sess) ++{ ++ return sess ? sess->conn_type : PJ_STUN_TP_UDP; ++} +diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c +index 28f760384..93b368777 100644 +--- a/pjnath/src/pjnath/stun_sock.c ++++ b/pjnath/src/pjnath/stun_sock.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -39,6 +39,35 @@ + + enum { MAX_BIND_RETRY = 100 }; + ++#if PJ_HAS_TCP ++// The head of a RTP packet is stored in a 16 bits header, so the max size of a ++// packet is 65536 ++#define MAX_RTP_SIZE 65536 ++#endif ++ ++typedef struct outgoing_sock { ++ pj_sock_t fd; ++ pj_activesock_t *sock; ++ pj_sockaddr addr; ++ int addr_len; ++} outgoing_sock; ++ ++typedef struct incoming_sock { ++ pj_sock_t fd; ++ pj_activesock_t *sock; ++ pj_sockaddr addr; ++ int addr_len; ++} incoming_sock; ++ ++typedef struct rx_buf { ++ pj_activesock_t *asock; ++ pj_uint8_t rx_buffer[MAX_RTP_SIZE]; ++ pj_uint16_t rx_buffer_size; ++ pj_uint16_t rx_wanted_size; ++ struct rx_buf *next; ++ struct rx_buf *prev; ++} rx_buf; ++ + struct pj_stun_sock + { + char *obj_name; /* Log identification */ +@@ -46,6 +75,8 @@ struct pj_stun_sock + void *user_data; /* Application user data */ + pj_bool_t is_destroying; /* Destroy already called */ + int af; /* Address family */ ++ pj_stun_tp_type conn_type; ++ pj_stun_sock_cfg cfg; + pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/ + pj_stun_sock_cb cb; /* Application callbacks */ + +@@ -58,6 +89,14 @@ struct pj_stun_sock + pj_dns_srv_async_query *q; /* Pending DNS query */ + pj_sock_t sock_fd; /* Socket descriptor */ + pj_activesock_t *active_sock; /* Active socket object */ ++#if PJ_HAS_TCP ++ pj_bool_t no_new_socket; ++ int outgoing_nb; ++ outgoing_sock outgoing_socks[PJ_ICE_MAX_CHECKS]; ++ int incoming_nb; ++ incoming_sock incoming_socks[PJ_ICE_MAX_CHECKS]; ++ rx_buf *rx_buffers; ++#endif + pj_ioqueue_op_key_t send_key; /* Default send key for app */ + pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ + pj_status_t last_err; /* Last error status */ +@@ -67,8 +106,17 @@ struct pj_stun_sock + pj_grp_lock_t *grp_lock; /* Session group lock */ + }; + +-/* +- * Prototypes for static functions ++////////////////////////////////////////////////////////////////////////////// ++ ++static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2) ++{ ++ return (pj_uint16_t) ((buf1[0] << 8) | (buf2[0] << 0)); ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++ ++/* ++ * Prototypes for static functions + */ + + /* Destructor for group lock */ +@@ -82,7 +130,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); + +-/* This callback is called by the STUN session when outgoing transaction ++/* This callback is called by the STUN session when outgoing transaction + * is complete + */ + static void sess_on_request_complete(pj_stun_session *sess, +@@ -119,6 +167,24 @@ static void start_ka_timer(pj_stun_sock *stun_sock); + /* Keep-alive timer callback */ + static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); + ++ ++static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, ++ pj_status_t status); ++ ++static pj_bool_t on_stun_sock_accept(pj_activesock_t *asock, ++ pj_sock_t newsock, ++ const pj_sockaddr_t *src_addr, ++ int src_addr_len); ++ ++static pj_bool_t on_connect_complete(pj_activesock_t *asock, ++ pj_status_t status); ++ ++/* Notify application that session has failed */ ++static pj_bool_t sess_fail(pj_stun_sock *stun_sock, ++ pj_stun_sock_op op, ++ pj_status_t status); ++ ++ + #define INTERNAL_MSG_TOKEN (void*)(pj_ssize_t)1 + + +@@ -150,6 +216,7 @@ PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg) + cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; + cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; + cfg->qos_ignore_error = PJ_TRUE; ++ cfg->user_mapping_cnt = 0; + } + + +@@ -160,116 +227,67 @@ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg) + } + + /* +- * Create the STUN transport using the specified configuration. ++ * Initialize. + */ +-PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, +- const char *name, +- int af, +- const pj_stun_sock_cb *cb, +- const pj_stun_sock_cfg *cfg, +- void *user_data, +- pj_stun_sock **p_stun_sock) ++PJ_DEF(pj_status_t) pj_stun_sock_alloc(pj_stun_sock *stun_sock) + { +- pj_pool_t *pool; +- pj_stun_sock *stun_sock; +- pj_stun_sock_cfg default_cfg; ++ pj_status_t status; + pj_sockaddr bound_addr; +- unsigned i; + pj_uint16_t max_bind_retry; +- pj_status_t status; +- +- PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); +- PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); +- PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); +- PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); +- +- status = pj_stun_config_check_valid(stun_cfg); +- if (status != PJ_SUCCESS) +- return status; ++ int sock_type; + +- if (name == NULL) +- name = "stuntp%p"; +- +- if (cfg == NULL) { +- pj_stun_sock_cfg_default(&default_cfg); +- cfg = &default_cfg; +- } +- +- +- /* Create structure */ +- pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); +- stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); +- stun_sock->pool = pool; +- stun_sock->obj_name = pool->obj_name; +- stun_sock->user_data = user_data; +- stun_sock->af = af; +- stun_sock->sock_fd = PJ_INVALID_SOCKET; +- pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); +- pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); +- +- stun_sock->ka_interval = cfg->ka_interval; +- if (stun_sock->ka_interval == 0) +- stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; +- +- if (cfg->grp_lock) { +- stun_sock->grp_lock = cfg->grp_lock; +- } else { +- status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); +- if (status != PJ_SUCCESS) { +- pj_pool_release(pool); +- return status; +- } +- } ++ pj_grp_lock_acquire(stun_sock->grp_lock); + +- pj_grp_lock_add_ref(stun_sock->grp_lock); +- pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, +- &stun_sock_destructor); ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP) ++ sock_type = pj_SOCK_DGRAM(); ++ else ++ sock_type = pj_SOCK_STREAM(); + + /* Create socket and bind socket */ +- status = pj_sock_socket(af, pj_SOCK_DGRAM() | pj_SOCK_CLOEXEC(), 0, &stun_sock->sock_fd); ++ status = pj_sock_socket(stun_sock->af, sock_type, 0, &stun_sock->sock_fd); + if (status != PJ_SUCCESS) + goto on_error; + + /* Apply QoS, if specified */ +- status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, +- &cfg->qos_params, 2, stun_sock->obj_name, ++ status = pj_sock_apply_qos2(stun_sock->sock_fd, stun_sock->cfg.qos_type, ++ &stun_sock->cfg.qos_params, 2, stun_sock->obj_name, + NULL); +- if (status != PJ_SUCCESS && !cfg->qos_ignore_error) ++ if (status != PJ_SUCCESS && !stun_sock->cfg.qos_ignore_error) + goto on_error; + + /* Apply socket buffer size */ +- if (cfg->so_rcvbuf_size > 0) { +- unsigned sobuf_size = cfg->so_rcvbuf_size; ++ if (stun_sock->cfg.so_rcvbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_rcvbuf_size; + status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), + PJ_TRUE, &sobuf_size); + if (status != PJ_SUCCESS) { + PJ_PERROR(3, (stun_sock->obj_name, status, + "Failed setting SO_RCVBUF")); + } else { +- if (sobuf_size < cfg->so_rcvbuf_size) { +- PJ_LOG(4, (stun_sock->obj_name, ++ if (sobuf_size < stun_sock->cfg.so_rcvbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, + "Warning! Cannot set SO_RCVBUF as configured, " + "now=%d, configured=%d", +- sobuf_size, cfg->so_rcvbuf_size)); ++ sobuf_size, stun_sock->cfg.so_rcvbuf_size)); + } else { + PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", + sobuf_size)); + } + } + } +- if (cfg->so_sndbuf_size > 0) { +- unsigned sobuf_size = cfg->so_sndbuf_size; ++ if (stun_sock->cfg.so_sndbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_sndbuf_size; + status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), + PJ_TRUE, &sobuf_size); + if (status != PJ_SUCCESS) { + PJ_PERROR(3, (stun_sock->obj_name, status, + "Failed setting SO_SNDBUF")); + } else { +- if (sobuf_size < cfg->so_sndbuf_size) { +- PJ_LOG(4, (stun_sock->obj_name, ++ if (sobuf_size < stun_sock->cfg.so_sndbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, + "Warning! Cannot set SO_SNDBUF as configured, " + "now=%d, configured=%d", +- sobuf_size, cfg->so_sndbuf_size)); ++ sobuf_size, stun_sock->cfg.so_sndbuf_size)); + } else { + PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", + sobuf_size)); +@@ -279,16 +297,16 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + + /* Bind socket */ + max_bind_retry = MAX_BIND_RETRY; +- if (cfg->port_range && cfg->port_range < max_bind_retry) +- max_bind_retry = cfg->port_range; +- pj_sockaddr_init(af, &bound_addr, NULL, 0); +- if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || +- cfg->bound_addr.addr.sa_family == pj_AF_INET6()) ++ if (stun_sock->cfg.port_range && stun_sock->cfg.port_range < max_bind_retry) ++ max_bind_retry = stun_sock->cfg.port_range; ++ pj_sockaddr_init(stun_sock->af, &bound_addr, NULL, 0); ++ if (stun_sock->cfg.bound_addr.addr.sa_family == pj_AF_INET() || ++ stun_sock->cfg.bound_addr.addr.sa_family == pj_AF_INET6()) + { +- pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); ++ pj_sockaddr_cp(&bound_addr, &stun_sock->cfg.bound_addr); + } + status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, +- cfg->port_range, max_bind_retry); ++ stun_sock->cfg.port_range, max_bind_retry); + if (status != PJ_SUCCESS) + goto on_error; + +@@ -298,13 +316,13 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + pj_sockaddr bound_addr; + int addr_len = sizeof(bound_addr); + +- status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, ++ status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, + &addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); +- pj_sockaddr_print(&bound_addr, stun_sock->info, ++ pj_sockaddr_print(&bound_addr, stun_sock->info, + PJ_INET6_ADDRSTRLEN, 3); + } + #endif +@@ -315,35 +333,153 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + pj_activesock_cb activesock_cb; + + pj_activesock_cfg_default(&activesock_cfg); +- activesock_cfg.grp_lock = stun_sock->grp_lock; +- activesock_cfg.async_cnt = cfg->async_cnt; ++ activesock_cfg.grp_lock = stun_sock->grp_lock; ++ activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; + activesock_cfg.concurrency = 0; + + /* Create the active socket */ + pj_bzero(&activesock_cb, sizeof(activesock_cb)); ++ activesock_cb.on_data_sent = &on_data_sent; + activesock_cb.on_data_recvfrom = &on_data_recvfrom; +- activesock_cb.on_data_sent = &on_data_sent; +- status = pj_activesock_create(pool, stun_sock->sock_fd, +- pj_SOCK_DGRAM(), +- &activesock_cfg, stun_cfg->ioqueue, +- &activesock_cb, stun_sock, +- &stun_sock->active_sock); +- if (status != PJ_SUCCESS) ++ ++#if PJ_HAS_TCP ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { ++ activesock_cb.on_accept_complete = &on_stun_sock_accept; ++ // Will be ready to accept incoming connections from the external world ++ status = pj_sock_listen(stun_sock->sock_fd, PJ_SOMAXCONN); ++ if (status != PJ_SUCCESS) { ++ goto on_error; ++ } ++ } else { ++ activesock_cb.on_connect_complete = &on_stun_sock_ready; ++ } ++#else ++ activesock_cb.on_connect_complete = &on_stun_sock_ready; ++#endif ++ ++ status = pj_activesock_create(stun_sock->pool, stun_sock->sock_fd, ++ sock_type, &activesock_cfg, ++ stun_sock->stun_cfg.ioqueue, ++ &activesock_cb, stun_sock, ++ &stun_sock->active_sock); ++ if (status != PJ_SUCCESS) { + goto on_error; ++ } + +- /* Start asynchronous read operations */ +- status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, +- cfg->max_pkt_size, 0); +- if (status != PJ_SUCCESS) ++#if PJ_HAS_TCP ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { ++ status = pj_activesock_start_accept(stun_sock->active_sock, ++ stun_sock->pool); ++ } else { ++ status = PJ_SUCCESS; ++ } ++ if (status == PJ_SUCCESS) { ++ on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS); ++ } else if (status != PJ_EPENDING) { ++ char addrinfo[PJ_INET6_ADDRSTRLEN + 10]; ++ pj_perror(3, stun_sock->pool->obj_name, status, ++ "Failed to connect to %s", ++ pj_sockaddr_print(&bound_addr, addrinfo, ++ sizeof(addrinfo), 3)); + goto on_error; ++ } ++#else ++ on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS); ++#endif ++ } + +- /* Init send keys */ +- pj_ioqueue_op_key_init(&stun_sock->send_key, +- sizeof(stun_sock->send_key)); +- pj_ioqueue_op_key_init(&stun_sock->int_send_key, +- sizeof(stun_sock->int_send_key)); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++on_error: ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++} ++ ++/* ++ * Create the STUN transport using the specified configuration. ++ */ ++PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, ++ const char *name, ++ int af, ++ pj_stun_tp_type conn_type, ++ const pj_stun_sock_cb *cb, ++ const pj_stun_sock_cfg *cfg, ++ void *user_data, ++ pj_stun_sock **p_stun_sock) ++{ ++ pj_pool_t *pool; ++ pj_stun_sock *stun_sock; ++ pj_stun_sock_cfg default_cfg; ++ pj_status_t status; ++ ++ PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); ++ PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); ++ PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); ++ PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); ++ PJ_ASSERT_RETURN(conn_type != PJ_STUN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); ++ ++ status = pj_stun_config_check_valid(stun_cfg); ++ if (status != PJ_SUCCESS) ++ return status; ++ ++ if (name == NULL) { ++ switch (conn_type) { ++ case PJ_STUN_TP_UDP: ++ name = "udpstun%p"; ++ break; ++ case PJ_STUN_TP_TCP: ++ name = "tcpstun%p"; ++ break; ++ default: ++ PJ_ASSERT_RETURN(!"Invalid STUN conn_type", PJ_EINVAL); ++ name = "tcpstun%p"; ++ break; ++ } ++ } ++ ++ if (cfg == NULL) { ++ pj_stun_sock_cfg_default(&default_cfg); ++ cfg = &default_cfg; ++ } ++ ++ /* Create structure */ ++ pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); ++ stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); ++ stun_sock->pool = pool; ++ stun_sock->obj_name = pool->obj_name; ++ stun_sock->user_data = user_data; ++ stun_sock->af = af; ++ stun_sock->conn_type = conn_type; ++ stun_sock->sock_fd = PJ_INVALID_SOCKET; ++#if PJ_HAS_TCP ++ stun_sock->no_new_socket = PJ_FALSE; ++ stun_sock->outgoing_nb = -1; ++ stun_sock->incoming_nb = -1; ++#endif ++ pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); ++ pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); ++ /* Copy socket settings; QoS parameters etc */ ++ pj_memcpy(&stun_sock->cfg, cfg, sizeof(*cfg)); ++ ++ stun_sock->ka_interval = cfg->ka_interval; ++ if (stun_sock->ka_interval == 0) ++ stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; ++ ++ if (cfg->grp_lock) { ++ stun_sock->grp_lock = cfg->grp_lock; ++ } else { ++ status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); ++ if (status != PJ_SUCCESS) { ++ pj_pool_release(pool); ++ return status; ++ } + } + ++ pj_grp_lock_add_ref(stun_sock->grp_lock); ++ pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, ++ &stun_sock_destructor); ++ + /* Create STUN session */ + { + pj_stun_session_cb sess_cb; +@@ -351,13 +487,16 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &sess_on_request_complete; + sess_cb.on_send_msg = &sess_on_send_msg; +- status = pj_stun_session_create(&stun_sock->stun_cfg, ++ status = pj_stun_session_create(&stun_sock->stun_cfg, + stun_sock->obj_name, +- &sess_cb, PJ_FALSE, ++ &sess_cb, PJ_FALSE, + stun_sock->grp_lock, +- &stun_sock->stun_sess); +- if (status != PJ_SUCCESS) +- goto on_error; ++ &stun_sock->stun_sess, ++ conn_type); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ return status; ++ } + } + + /* Associate us with the STUN session */ +@@ -368,25 +507,370 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, + * STUN messages we sent with STUN messages that the application sends. + * The last 16bit value in the array is a counter. + */ ++ unsigned i; + for (i=0; itsx_id); ++i) { + stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); + } + stun_sock->tsx_id[5] = 0; + +- + /* Init timer entry */ + stun_sock->ka_timer.cb = &ka_timer_cb; + stun_sock->ka_timer.user_data = stun_sock; + ++ pj_stun_sock_alloc(stun_sock); ++ + /* Done */ + *p_stun_sock = stun_sock; + return PJ_SUCCESS; ++} + +-on_error: +- pj_stun_sock_destroy(stun_sock); +- return status; ++/* ++ * Notification when outgoing TCP socket has been connected. ++ */ ++static pj_bool_t on_stun_sock_ready(pj_activesock_t *asock, pj_status_t status) ++{ ++ pj_stun_sock *stun_sock; ++ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); ++ if (!stun_sock) ++ return PJ_FALSE; ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ ++ /* TURN session may have already been destroyed here. ++ * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). ++ */ ++ if (!stun_sock->stun_sess) { ++ sess_fail(stun_sock, PJ_STUN_SESS_DESTROYED, status); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++ } ++ ++ if (status != PJ_SUCCESS) { ++ sess_fail(stun_sock, PJ_STUN_TCP_CONNECT_ERROR, status); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++ } ++ ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) ++ PJ_LOG(5,(stun_sock->obj_name, "TCP connected")); ++ ++ /* Start asynchronous read operations */ ++ pj_status_t result; ++ result = pj_activesock_start_recvfrom(asock, stun_sock->pool, ++ stun_sock->cfg.max_pkt_size, 0); ++ if (result != PJ_SUCCESS) ++ return PJ_FALSE; ++ ++ /* Associate us with the STUN session */ ++ pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); ++ ++ /* Initialize random numbers to be used as STUN transaction ID for ++ * outgoing Binding request. We use the 80bit number to distinguish ++ * STUN messages we sent with STUN messages that the application sends. ++ * The last 16bit value in the array is a counter. ++ */ ++ unsigned i; ++ for (i=0; itsx_id); ++i) { ++ stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); ++ } ++ stun_sock->tsx_id[5] = 0; ++ ++ /* Init timer entry */ ++ stun_sock->ka_timer.cb = &ka_timer_cb; ++ stun_sock->ka_timer.user_data = stun_sock; ++ ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Init send keys */ ++ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); ++ pj_ioqueue_op_key_init(&stun_sock->int_send_key, ++ sizeof(stun_sock->int_send_key)); ++ ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_TRUE; + } + ++static pj_bool_t parse_rx_packet(pj_activesock_t *asock, ++ void *data, ++ pj_size_t size, ++ const pj_sockaddr_t *rx_addr, ++ unsigned sock_addr_len) ++{ ++ ++ pj_stun_sock *stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); ++ if (!stun_sock) ++ return PJ_FALSE; ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ pj_uint16_t parsed = 0; ++ pj_status_t result = PJ_TRUE; ++ pj_status_t status; ++ ++#if PJ_HAS_TCP ++ // Search current rx_buf ++ rx_buf* buf = NULL; ++ rx_buf* stun_sock_buf = stun_sock->rx_buffers; ++ while (stun_sock_buf) { ++ if (stun_sock_buf->asock == asock) { ++ buf = stun_sock_buf; ++ break; ++ } ++ stun_sock_buf = stun_sock_buf->next; ++ } ++ if (!buf) { ++ // Create rx_buf, this buf will be released when the pool is released ++ buf = (rx_buf*)pj_pool_calloc(stun_sock->pool, 1, sizeof(rx_buf)); ++ if (!buf) { ++ PJ_LOG(5, (stun_sock->obj_name, "Cannot allocate memory for rx_buf")); ++ status = pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++ } ++ buf->asock = asock; ++ buf->next = stun_sock->rx_buffers; ++ if (stun_sock->rx_buffers) ++ stun_sock->rx_buffers->prev = buf; ++ stun_sock->rx_buffers = buf; ++ } ++#endif ++ ++ do { ++ pj_uint16_t leftover = size - parsed; ++ pj_uint8_t *current_packet = ((pj_uint8_t *)(data)) + parsed; ++ ++#if PJ_HAS_TCP ++ if (stun_sock->conn_type != PJ_STUN_TP_UDP) { ++ /* RFC6544, the packet is wrapped into a packet following the RFC4571 */ ++ pj_bool_t store_remaining = PJ_TRUE; ++ if (buf->rx_buffer_size != 0 || buf->rx_wanted_size != 0) { ++ if (buf->rx_buffer_size == 1 && buf->rx_wanted_size == 0) { ++ // In this case, we want to know the header size ++ leftover = GETVAL16H(buf->rx_buffer, current_packet); ++ ++ buf->rx_buffer_size = 0; ++ current_packet++; ++ parsed++; ++ ++ if (leftover + parsed <= size) { ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ buf->rx_wanted_size = leftover; ++ } ++ ++ } else if (leftover + buf->rx_buffer_size >= buf->rx_wanted_size) { ++ // We have enough data Build new packet to parse ++ store_remaining = PJ_FALSE; ++ pj_uint16_t eaten_bytes = buf->rx_wanted_size - buf->rx_buffer_size; ++ pj_memcpy(buf->rx_buffer + buf->rx_buffer_size, ++ current_packet, eaten_bytes); ++ ++ leftover = buf->rx_wanted_size; ++ current_packet = buf->rx_buffer; ++ parsed += eaten_bytes; ++ ++ buf->rx_buffer_size = 0; ++ buf->rx_wanted_size = 0; ++ } ++ } else if (leftover > 1) { ++ leftover = GETVAL16H(current_packet, current_packet+1); ++ current_packet += 2; ++ parsed += 2; ++ if (leftover + parsed <= size) { ++ store_remaining = PJ_FALSE; ++ parsed += leftover; ++ } else { ++ buf->rx_wanted_size = leftover; ++ } ++ } ++ if (store_remaining) { ++ leftover = size - parsed; ++ pj_memcpy(buf->rx_buffer + buf->rx_buffer_size, ++ current_packet, leftover); ++ buf->rx_buffer_size += leftover; ++ break; ++ } ++ } else { ++#endif ++ parsed = size; ++#if PJ_HAS_TCP ++ } ++#endif ++ /* Check that this is STUN message */ ++ status = pj_stun_msg_check((const pj_uint8_t *)current_packet, leftover, ++ PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); ++ if (status != PJ_SUCCESS) { ++ /* Not STUN -- give it to application */ ++ goto process_app_data; ++ } ++ ++ /* Treat packet as STUN header and copy the STUN message type. ++ * We don't want to access the type directly from the header ++ * since it may not be properly aligned. ++ */ ++ pj_stun_msg_hdr *hdr = (pj_stun_msg_hdr *)current_packet; ++ pj_uint16_t type; ++ pj_memcpy(&type, &hdr->type, 2); ++ type = pj_ntohs(type); ++ ++ /* If the packet is a STUN Binding response and part of the ++ * transaction ID matches our internal ID, then this is ++ * our internal STUN message (Binding request or keep alive). ++ * Give it to our STUN session. ++ */ ++ if (!PJ_STUN_IS_RESPONSE(type) || ++ PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || ++ pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) ++ { ++ /* Not STUN Binding response, or STUN transaction ID mismatch. ++ * This is not our message too -- give it to application. ++ */ ++ goto process_app_data; ++ } ++ ++ /* This is our STUN Binding response. Give it to the STUN session */ ++ status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, current_packet, ++ leftover, PJ_STUN_IS_DATAGRAM, NULL, ++ NULL, rx_addr, sock_addr_len); ++ ++ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; ++ continue; ++ ++process_app_data: ++ if (stun_sock->cb.on_rx_data) ++ (*stun_sock->cb.on_rx_data)(stun_sock, current_packet, ++ (unsigned)leftover, rx_addr, sock_addr_len); ++ ++ result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE; ++ } while (parsed < size && result); ++ ++ status = pj_grp_lock_release(stun_sock->grp_lock); ++ return result; ++} ++ ++static pj_bool_t on_data_read(pj_activesock_t *asock, ++ void *data, ++ pj_size_t size, ++ pj_status_t status, ++ pj_size_t *remainder) ++{ ++ ++ pj_stun_sock *stun_sock; ++ ++ if (!(stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock))) ++ return PJ_FALSE; ++ ++ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); ++ /* Log socket error or disconnection */ ++ if (status != PJ_SUCCESS) { ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP ++ || (status != PJ_EEOF && status != 120104 && status != 130054)) ++ { ++ PJ_PERROR(2, (stun_sock->obj_name, status, "read() error")); ++ } else if (status == 120104 ++ || status == 130054 /* RESET BY PEER */) ++ { ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) ++ if (stun_sock->outgoing_socks[i].sock == asock ++ && cb ++ && (cb->on_peer_reset_connection)) ++ { ++ (cb->on_peer_reset_connection)(stun_sock->stun_sess, ++ &stun_sock->outgoing_socks[i].addr); ++ } ++ } ++ return PJ_FALSE; ++ } ++#if PJ_HAS_TCP ++ pj_sockaddr_t *rx_addr = NULL; ++ unsigned sock_addr_len = 0; ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) ++ if (stun_sock->outgoing_socks[i].sock == asock) { ++ rx_addr = &stun_sock->outgoing_socks[i].addr; ++ sock_addr_len = pj_sockaddr_get_len(rx_addr); ++ if (cb && (cb->on_peer_packet)) ++ (cb->on_peer_packet)(stun_sock->stun_sess, ++ &stun_sock->outgoing_socks[i].addr); ++ } ++ ++ if (rx_addr == NULL && stun_sock->incoming_nb != -1) { ++ // It's an incoming message ++ for (int i = 0; i <= stun_sock->incoming_nb; ++i) ++ if (stun_sock->incoming_socks[i].sock == asock) { ++ rx_addr = &stun_sock->incoming_socks[i].addr; ++ sock_addr_len = stun_sock->incoming_socks[i].addr_len; ++ } ++ } ++ return parse_rx_packet(asock, data, size, rx_addr, sock_addr_len); ++#else ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return PJ_FALSE; ++#endif ++} ++ ++#if PJ_HAS_TCP ++/* ++ * Notification when incoming TCP socket has been connected. ++ * NOTE: cf https://www.pjsip.org/docs/latest/pjlib/docs/html//structpj__activesock__cb.htm if status needed ++ */ ++static pj_bool_t on_stun_sock_accept(pj_activesock_t *active_sock, ++ pj_sock_t sock, ++ const pj_sockaddr_t *src_addr, ++ int src_addr_len) ++{ ++ pj_status_t status; ++ pj_stun_sock *stun_sock; ++ int sock_type = pj_SOCK_STREAM(); ++ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(active_sock); ++ ++ if (stun_sock->no_new_socket) ++ return PJ_FALSE; ++ ++ stun_sock->incoming_nb += 1; ++ int nb_check = stun_sock->incoming_nb; ++ pj_sock_t *fd = &stun_sock->incoming_socks[nb_check].fd; ++ pj_activesock_t **asock = &stun_sock->incoming_socks[nb_check].sock; ++ ++ pj_sockaddr_cp(&stun_sock->incoming_socks[nb_check].addr, src_addr); ++ stun_sock->incoming_socks[nb_check].addr_len = src_addr_len; ++ *fd = sock; ++ ++ pj_activesock_cfg activesock_cfg; ++ pj_activesock_cb activesock_cb; ++ ++ pj_activesock_cfg_default(&activesock_cfg); ++ activesock_cfg.grp_lock = stun_sock->grp_lock; ++ activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; ++ activesock_cfg.concurrency = 0; ++ ++ /* Create the active socket */ ++ pj_bzero(&activesock_cb, sizeof(activesock_cb)); ++ activesock_cb.on_data_read = &on_data_read; ++ activesock_cb.on_data_sent = &on_data_sent; ++ ++ status = pj_activesock_create(stun_sock->pool, *fd, sock_type, ++ &activesock_cfg, stun_sock->stun_cfg.ioqueue, ++ &activesock_cb, stun_sock, asock); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Start asynchronous read operations */ ++ pj_status_t result; ++ result = pj_activesock_start_read(*asock, stun_sock->pool, ++ stun_sock->cfg.max_pkt_size, 0); ++ if (result != PJ_SUCCESS) ++ return PJ_FALSE; ++ ++ return PJ_TRUE; ++} ++#endif ++ + /* Start socket. */ + PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, + const pj_str_t *domain, +@@ -401,7 +885,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, + + /* Check whether the domain contains IP address */ + stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af; +- status = pj_inet_pton(stun_sock->af, domain, ++ status = pj_inet_pton(stun_sock->af, domain, + pj_sockaddr_get_addr(&stun_sock->srv_addr)); + if (status != PJ_SUCCESS) { + stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0; +@@ -423,9 +907,9 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, + opt = PJ_DNS_SRV_FALLBACK_A; + + stun_sock->last_err = PJ_SUCCESS; +- status = pj_dns_srv_resolve(domain, &res_name, default_port, ++ status = pj_dns_srv_resolve(domain, &res_name, default_port, + stun_sock->pool, resolver, opt, +- stun_sock, &dns_srv_resolver_cb, ++ stun_sock, &dns_srv_resolver_cb, + &stun_sock->q); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(stun_sock->obj_name, status, +@@ -525,6 +1009,26 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock) + stun_sock->sock_fd = PJ_INVALID_SOCKET; + } + ++ for (int i = 0; i <= stun_sock->incoming_nb ; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL) { ++ stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; ++ pj_activesock_close(stun_sock->incoming_socks[i].sock); ++ } else if (stun_sock->incoming_socks[i].fd != PJ_INVALID_SOCKET) { ++ pj_sock_close(stun_sock->incoming_socks[i].fd); ++ stun_sock->incoming_socks[i].fd = PJ_INVALID_SOCKET; ++ } ++ } ++ ++ for (int i = 0; i <= stun_sock->outgoing_nb ; ++i) { ++ if (stun_sock->outgoing_socks[i].sock != NULL) { ++ stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; ++ pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ } else if (stun_sock->outgoing_socks[i].fd != PJ_INVALID_SOCKET) { ++ pj_sock_close(stun_sock->outgoing_socks[i].fd); ++ stun_sock->outgoing_socks[i].fd = PJ_INVALID_SOCKET; ++ } ++ } ++ + if (stun_sock->stun_sess) { + pj_stun_session_destroy(stun_sock->stun_sess); + } +@@ -558,13 +1062,13 @@ PJ_DEF(pj_grp_lock_t *) pj_stun_sock_get_grp_lock(pj_stun_sock *stun_sock) + } + + /* Notify application that session has failed */ +-static pj_bool_t sess_fail(pj_stun_sock *stun_sock, ++static pj_bool_t sess_fail(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status) + { + pj_bool_t ret; + +- PJ_PERROR(4,(stun_sock->obj_name, status, ++ PJ_PERROR(4,(stun_sock->obj_name, status, + "Session failed because %s failed", + pj_stun_sock_op_name(op))); + +@@ -601,10 +1105,10 @@ static void dns_srv_resolver_cb(void *user_data, + pj_sockaddr_init(stun_sock->af, &stun_sock->srv_addr, NULL, + rec->entry[0].port); + if (stun_sock->af == pj_AF_INET6()) { +- stun_sock->srv_addr.ipv6.sin6_addr = ++ stun_sock->srv_addr.ipv6.sin6_addr = + rec->entry[0].server.addr[0].ip.v6; + } else { +- stun_sock->srv_addr.ipv4.sin_addr = ++ stun_sock->srv_addr.ipv4.sin_addr = + rec->entry[0].server.addr[0].ip.v4; + } + +@@ -625,18 +1129,18 @@ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) + ++stun_sock->tsx_id[5]; + status = pj_stun_session_create_req(stun_sock->stun_sess, + PJ_STUN_BINDING_REQUEST, +- PJ_STUN_MAGIC, +- (const pj_uint8_t*)stun_sock->tsx_id, ++ PJ_STUN_MAGIC, ++ (const pj_uint8_t*)stun_sock->tsx_id, + &tdata); + if (status != PJ_SUCCESS) + goto on_error; +- ++ + /* Send request */ + status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, +- PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, ++ PJ_FALSE, (stun_sock->conn_type == PJ_STUN_TP_UDP), &stun_sock->srv_addr, + pj_sockaddr_get_len(&stun_sock->srv_addr), + tdata); +- if (status != PJ_SUCCESS) ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) + goto on_error; + + return PJ_SUCCESS; +@@ -657,10 +1161,12 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, + + pj_grp_lock_acquire(stun_sock->grp_lock); + ++ info->conn_type = stun_sock->conn_type; ++ + /* Copy STUN server address and mapped address */ + pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, + sizeof(pj_sockaddr)); +- pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, ++ pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, + sizeof(pj_sockaddr)); + + /* Retrieve bound address */ +@@ -673,7 +1179,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, + } + + /* If socket is bound to a specific interface, then only put that +- * interface in the alias list. Otherwise query all the interfaces ++ * interface in the alias list. Otherwise query all the interfaces + * in the host. + */ + if (pj_sockaddr_has_addr(&info->bound_addr)) { +@@ -693,20 +1199,20 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } +- ++ + pj_sockaddr_set_port(&def_addr, port); +- ++ + /* Enum all IP interfaces in the host */ + pj_enum_ip_option_default(&enum_opt); + enum_opt.af = stun_sock->af; + enum_opt.omit_deprecated_ipv6 = PJ_TRUE; + info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); +- status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, ++ status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, + info->aliases); + if (status == PJ_ENOTSUP) { + /* Try again without omitting deprecated IPv6 addresses */ + enum_opt.omit_deprecated_ipv6 = PJ_FALSE; +- status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, ++ status = pj_enum_ip_interface2(&enum_opt, &info->alias_cnt, + info->aliases); + } + +@@ -754,7 +1260,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, + pj_status_t status; + + PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL); +- ++ + pj_grp_lock_acquire(stun_sock->grp_lock); + + if (!stun_sock->active_sock) { +@@ -769,13 +1275,276 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, + send_key = &stun_sock->send_key; + + size = pkt_len; +- status = pj_activesock_sendto(stun_sock->active_sock, send_key, +- pkt, &size, flag, dst_addr, addr_len); ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP) { ++ status = pj_activesock_sendto(stun_sock->active_sock, send_key, ++ pkt, &size, flag, dst_addr, addr_len); ++ } else { ++#if PJ_HAS_TCP ++ pj_bool_t is_outgoing = PJ_FALSE; ++ pj_bool_t is_incoming = PJ_FALSE; ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (stun_sock->outgoing_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, dst_addr) == 0) { ++ is_outgoing = PJ_TRUE; ++ status = pj_activesock_send(stun_sock->outgoing_socks[i].sock, ++ send_key, pkt, &size, flag); ++ break; ++ } ++ } ++ if (is_outgoing == PJ_FALSE) { ++ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, ++ dst_addr) == 0) { ++ status = pj_activesock_send(stun_sock->incoming_socks[i].sock, ++ send_key, pkt, &size, flag); ++ is_incoming = PJ_TRUE; ++ break; ++ } ++ } ++ } ++ if (is_outgoing == PJ_FALSE && is_incoming == PJ_FALSE) { ++ status = pj_activesock_send(stun_sock->active_sock, send_key, pkt, ++ &size, flag); ++ } ++ ++#endif ++ } + + pj_grp_lock_release(stun_sock->grp_lock); + return status; + } + ++#if PJ_HAS_TCP ++ ++PJ_DECL(pj_status_t) pj_stun_sock_connect(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af, ++ int nb_check) ++{ ++ ++ pj_grp_lock_acquire(stun_sock->grp_lock); ++ int sock_type = pj_SOCK_STREAM(); ++ ++ outgoing_sock* os = &stun_sock->outgoing_socks[nb_check]; ++ pj_sock_t *fd = &os->fd; ++ pj_activesock_t **asock = &os->sock; ++ ++ pj_sockaddr_t *addr = &os->addr; ++ os->addr_len = pj_sockaddr_get_len(remote_addr); ++ ++ ++ pj_status_t status = pj_sock_socket(af, sock_type, 0, fd); ++ if (status != PJ_SUCCESS) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Apply QoS, if specified */ ++ status = pj_sock_apply_qos2(*fd, stun_sock->cfg.qos_type, ++ &stun_sock->cfg.qos_params, 2, stun_sock->obj_name, NULL); ++ if (status != PJ_SUCCESS && !stun_sock->cfg.qos_ignore_error) { ++ pj_stun_sock_destroy(stun_sock); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ /* Apply socket buffer size */ ++ if (stun_sock->cfg.so_rcvbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_rcvbuf_size; ++ status = pj_sock_setsockopt_sobuf(*fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); ++ if (status != PJ_SUCCESS) { ++ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); ++ } else { ++ if (sobuf_size < stun_sock->cfg.so_rcvbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, ++ "Warning! Cannot set SO_RCVBUF as configured, " ++ "now=%d, configured=%d", ++ sobuf_size, stun_sock->cfg.so_rcvbuf_size)); ++ } else { ++ PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); ++ } ++ } ++ } ++ ++ if (stun_sock->cfg.so_sndbuf_size > 0) { ++ unsigned sobuf_size = stun_sock->cfg.so_sndbuf_size; ++ status = pj_sock_setsockopt_sobuf(*fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); ++ if (status != PJ_SUCCESS) { ++ pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); ++ } else { ++ if (sobuf_size < stun_sock->cfg.so_sndbuf_size) { ++ PJ_LOG(4, (stun_sock->obj_name, ++ "Warning! Cannot set SO_SNDBUF as configured, " ++ "now=%d, configured=%d", ++ sobuf_size, stun_sock->cfg.so_sndbuf_size)); ++ } else { ++ PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); ++ } ++ } ++ } ++ ++ /* Init active socket configuration */ ++ { ++ pj_activesock_cfg activesock_cfg; ++ pj_activesock_cb activesock_cb; ++ ++ pj_activesock_cfg_default(&activesock_cfg); ++ activesock_cfg.grp_lock = stun_sock->grp_lock; ++ activesock_cfg.async_cnt = stun_sock->cfg.async_cnt; ++ activesock_cfg.concurrency = 0; ++ ++ /* Create the active socket */ ++ pj_bzero(&activesock_cb, sizeof(activesock_cb)); ++ activesock_cb.on_data_read = &on_data_read; ++ activesock_cb.on_data_sent = &on_data_sent; ++ activesock_cb.on_connect_complete = &on_connect_complete; ++ ++ status = pj_activesock_create(stun_sock->pool, *fd, ++ sock_type, &activesock_cfg, ++ stun_sock->stun_cfg.ioqueue, &activesock_cb, ++ stun_sock, asock); ++ if (status != PJ_SUCCESS) { ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ ++ pj_sockaddr_init(stun_sock->af, addr, NULL, 0); ++ pj_sockaddr_cp(addr, remote_addr); ++ ++ status = pj_activesock_start_connect( ++ *asock, stun_sock->pool, addr, ++ os->addr_len); ++ if (status == PJ_SUCCESS) { ++ on_connect_complete(*asock, status); ++ } else if (status != PJ_EPENDING) { ++ char addrinfo[PJ_INET6_ADDRSTRLEN+8]; ++ pj_perror(3, stun_sock->pool->obj_name, status, "Failed to connect to %s", ++ pj_sockaddr_print(addr, addrinfo, sizeof(addrinfo), 3)); ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++ } ++ } ++ ++ pj_grp_lock_release(stun_sock->grp_lock); ++ return status; ++} ++ ++PJ_DECL(pj_status_t) pj_stun_sock_connect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af) ++{ ++ ++ if (stun_sock->incoming_nb != -1) { ++ // Check if not incoming, if so, already connected (mainly for PRFLX candidates) ++ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr)==0) { ++ pj_stun_session_cb *cb = ++ pj_stun_session_callback(stun_sock->stun_sess); ++ (cb->on_peer_connection)(stun_sock->stun_sess, PJ_SUCCESS, ++ (pj_sockaddr_t *)remote_addr); ++ return PJ_SUCCESS; ++ } ++ } ++ } ++ ++ /* Create socket and bind socket */ ++ stun_sock->outgoing_nb += 1; ++ int nb_check = stun_sock->outgoing_nb; ++ return pj_stun_sock_connect(stun_sock, remote_addr, af, nb_check); ++ ++} ++ ++PJ_DECL(pj_status_t) pj_stun_sock_reconnect_active(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr, ++ int af) ++{ ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (stun_sock->outgoing_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { ++ pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ return pj_stun_sock_connect(stun_sock, remote_addr, af, i); ++ } ++ } ++ return PJ_EINVAL; ++} ++ ++ ++PJ_DECL(pj_status_t) pj_stun_sock_close(pj_stun_sock *stun_sock, ++ const pj_sockaddr_t *remote_addr) ++{ ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (stun_sock->outgoing_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, remote_addr) == 0) { ++ return pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ } ++ } ++ ++ for (int i = 0; i <= stun_sock->incoming_nb; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) == 0) { ++ return pj_activesock_close(stun_sock->incoming_socks[i].sock); ++ } ++ } ++ return PJ_EINVAL; ++} ++ ++ ++PJ_DECL(pj_status_t) pj_stun_sock_close_all_except(pj_stun_sock *stun_sock, const pj_sockaddr_t *remote_addr) ++{ ++ stun_sock->no_new_socket = PJ_TRUE; ++ for (int i = 0; i <= stun_sock->outgoing_nb; ++i) { ++ if (stun_sock->outgoing_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->outgoing_socks[i].addr, remote_addr) != 0) { ++ pj_activesock_close(stun_sock->outgoing_socks[i].sock); ++ } ++ } ++ ++ for (int i = 0; i <= stun_sock->incoming_nb; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL ++ && pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) != 0) { ++ pj_activesock_close(stun_sock->incoming_socks[i].sock); ++ } ++ } ++ return PJ_SUCCESS; ++} ++ ++static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) ++{ ++ pj_stun_sock *stun_sock; ++ stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock); ++ ++ pj_sockaddr remote_addr; ++ pj_bool_t addr_found = PJ_FALSE; ++ ++ // Get remote connected address ++ for (int i = 0 ; i <= stun_sock->outgoing_nb ; ++i) { ++ if (stun_sock->outgoing_socks[i].sock == asock) { ++ pj_sockaddr_cp(&remote_addr, &stun_sock->outgoing_socks[i].addr); ++ addr_found = PJ_TRUE; ++ break; ++ } ++ } ++ if (!addr_found) ++ return PJ_FALSE; ++ ++ pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess); ++ if (!cb->on_peer_connection) ++ return PJ_FALSE; ++ ++ ++ if (status == PJ_SUCCESS) { ++ status = pj_activesock_start_read(asock, stun_sock->pool, ++ stun_sock->cfg.max_pkt_size, 0); ++ } ++ (cb->on_peer_connection)(stun_sock->stun_sess, status, &remote_addr); ++ return status != PJ_SUCCESS; ++} ++ ++#endif ++ + /* This callback is called by the STUN session to send packet */ + static pj_status_t sess_on_send_msg(pj_stun_session *sess, + void *token, +@@ -786,6 +1555,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, + { + pj_stun_sock *stun_sock; + pj_ssize_t size; ++ pj_status_t status; + + stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); + if (!stun_sock || !stun_sock->active_sock) { +@@ -799,12 +1569,33 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess, + PJ_UNUSED_ARG(token); + + size = pkt_size; +- return pj_activesock_sendto(stun_sock->active_sock, +- &stun_sock->int_send_key, +- pkt, &size, 0, dst_addr, addr_len); ++ if (stun_sock->conn_type == PJ_STUN_TP_UDP) { ++ status = pj_activesock_sendto(stun_sock->active_sock, ++ &stun_sock->int_send_key,pkt, &size, 0, ++ dst_addr, addr_len); ++ } ++#if PJ_HAS_TCP ++ else { ++ for (int i = 0 ; i <= stun_sock->incoming_nb; ++i) { ++ if (stun_sock->incoming_socks[i].sock != NULL ++ && !pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, dst_addr)) { ++ status = pj_activesock_send(stun_sock->incoming_socks[i].sock, ++ &stun_sock->int_send_key, ++ pkt, &size, 0); ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) ++ PJ_PERROR(4,(stun_sock->obj_name, status, ++ "Error sending answer on incoming_sock(s)")); ++ } ++ } ++ /* last attempt */ ++ status = pj_activesock_send(stun_sock->active_sock, ++ &stun_sock->int_send_key, pkt, &size, 0); ++ } ++#endif ++ return status; + } + +-/* This callback is called by the STUN session when outgoing transaction ++/* This callback is called by the STUN session when outgoing transaction + * is complete + */ + static void sess_on_request_complete(pj_stun_session *sess, +@@ -860,16 +1651,16 @@ static void sess_on_request_complete(pj_stun_session *sess, + } + + /* Determine if mapped address has changed, and save the new mapped +- * address and call callback if so ++ * address and call callback if so + */ + mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || +- pj_sockaddr_cmp(&stun_sock->mapped_addr, ++ pj_sockaddr_cmp(&stun_sock->mapped_addr, + &mapped_attr->sockaddr) != 0; + if (mapped_changed) { + /* Print mapped adress */ + { + char addrinfo[PJ_INET6_ADDRSTRLEN+10]; +- PJ_LOG(4,(stun_sock->obj_name, ++ PJ_LOG(4,(stun_sock->obj_name, + "STUN mapped address found/changed: %s", + pj_sockaddr_print(&mapped_attr->sockaddr, + addrinfo, sizeof(addrinfo), 3))); +@@ -941,8 +1732,6 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, + pj_status_t status) + { + pj_stun_sock *stun_sock; +- pj_stun_msg_hdr *hdr; +- pj_uint16_t type; + + stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); + if (!stun_sock) +@@ -954,58 +1743,7 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, + return PJ_TRUE; + } + +- pj_grp_lock_acquire(stun_sock->grp_lock); +- +- /* Check that this is STUN message */ +- status = pj_stun_msg_check((const pj_uint8_t*)data, size, +- PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); +- if (status != PJ_SUCCESS) { +- /* Not STUN -- give it to application */ +- goto process_app_data; +- } +- +- /* Treat packet as STUN header and copy the STUN message type. +- * We don't want to access the type directly from the header +- * since it may not be properly aligned. +- */ +- hdr = (pj_stun_msg_hdr*) data; +- pj_memcpy(&type, &hdr->type, 2); +- type = pj_ntohs(type); +- +- /* If the packet is a STUN Binding response and part of the +- * transaction ID matches our internal ID, then this is +- * our internal STUN message (Binding request or keep alive). +- * Give it to our STUN session. +- */ +- if (!PJ_STUN_IS_RESPONSE(type) || +- PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || +- pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) +- { +- /* Not STUN Binding response, or STUN transaction ID mismatch. +- * This is not our message too -- give it to application. +- */ +- goto process_app_data; +- } +- +- /* This is our STUN Binding response. Give it to the STUN session */ +- status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, +- PJ_STUN_IS_DATAGRAM, NULL, NULL, +- src_addr, addr_len); +- +- status = pj_grp_lock_release(stun_sock->grp_lock); +- +- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; +- +-process_app_data: +- if (stun_sock->cb.on_rx_data) { +- (*stun_sock->cb.on_rx_data)(stun_sock, data, (unsigned)size, +- src_addr, addr_len); +- status = pj_grp_lock_release(stun_sock->grp_lock); +- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; +- } +- +- status = pj_grp_lock_release(stun_sock->grp_lock); +- return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; ++ return parse_rx_packet(asock, data, size, src_addr, addr_len); + } + + /* Callback from active socket about send status */ +@@ -1031,7 +1769,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock, + pj_grp_lock_acquire(stun_sock->grp_lock); + + /* If app gives NULL send_key in sendto() function, then give +- * NULL in the callback too ++ * NULL in the callback too + */ + if (send_key == &stun_sock->send_key) + send_key = NULL; +@@ -1046,3 +1784,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock, + return PJ_TRUE; + } + ++pj_stun_session* pj_stun_sock_get_session(pj_stun_sock *stun_sock) ++{ ++ return stun_sock ? stun_sock->stun_sess : NULL; ++} +diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c +index 71c407b06..a367e6704 100644 +--- a/pjnath/src/pjnath/stun_transaction.c ++++ b/pjnath/src/pjnath/stun_transaction.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -61,9 +61,9 @@ struct pj_stun_client_tsx + #endif + + +-static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, ++static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, + pj_timer_entry *timer); +-static void destroy_timer_callback(pj_timer_heap_t *timer_heap, ++static void destroy_timer_callback(pj_timer_heap_t *timer_heap, + pj_timer_entry *timer); + + /* +@@ -355,7 +355,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, + + + /* Retransmit timer callback */ +-static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, ++static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, + pj_timer_entry *timer) + { + pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; +@@ -411,6 +411,9 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, + PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, + pj_bool_t mod_count) + { ++ if (!tsx) ++ return PJ_EINVAL; ++ + if (tsx->destroy_timer.id != 0 || tsx->is_destroying) + return PJ_SUCCESS; + +@@ -423,7 +426,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, + } + + /* Timer callback to destroy transaction */ +-static void destroy_timer_callback(pj_timer_heap_t *timer_heap, ++static void destroy_timer_callback(pj_timer_heap_t *timer_heap, + pj_timer_entry *timer) + { + pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; +@@ -449,23 +452,23 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, + pj_status_t status; + + /* Must be STUN response message */ +- if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) && ++ if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) && + !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { +- PJ_LOG(4,(tsx->obj_name, ++ PJ_LOG(4,(tsx->obj_name, + "STUN rx_msg() error: not response message")); + return PJNATH_EINSTUNMSGTYPE; + } + + +- /* We have a response with matching transaction ID. ++ /* We have a response with matching transaction ID. + * We can cancel retransmit timer now. + */ + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, + TIMER_INACTIVE); + + /* Find STUN error code attribute */ +- err_attr = (pj_stun_errcode_attr*) ++ err_attr = (pj_stun_errcode_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0); + + if (err_attr && err_attr->err_code <= 200) { +@@ -473,7 +476,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, + * Any response between 100 and 299 MUST result in the cessation + * of request retransmissions, but otherwise is discarded. + */ +- PJ_LOG(4,(tsx->obj_name, ++ PJ_LOG(4,(tsx->obj_name, + "STUN rx_msg() error: received provisional %d code (%.*s)", + err_attr->err_code, + (int)err_attr->reason.slen, +diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c +index e85e971f8..279e1bb82 100644 +--- a/pjnath/src/pjnath/turn_session.c ++++ b/pjnath/src/pjnath/turn_session.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -35,7 +35,7 @@ + #define PJ_TURN_CHANNEL_HTABLE_SIZE 8 + #define PJ_TURN_PERM_HTABLE_SIZE 8 + +-static const char *state_names[] = ++static const char *state_names[] = + { + "Null", + "Resolving", +@@ -95,8 +95,8 @@ struct perm_t + /* The permission expiration */ + pj_time_val expiry; + +- /* Arbitrary/random pointer value (token) to map this perm with the +- * request to create it. It is used to invalidate this perm when the ++ /* Arbitrary/random pointer value (token) to map this perm with the ++ * request to create it. It is used to invalidate this perm when the + * request fails. + */ + void *req_token; +@@ -219,7 +219,7 @@ PJ_DEF(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm) + /* + * Duplicate pj_turn_alloc_param. + */ +-PJ_DEF(void) pj_turn_alloc_param_copy( pj_pool_t *pool, ++PJ_DEF(void) pj_turn_alloc_param_copy( pj_pool_t *pool, + pj_turn_alloc_param *dst, + const pj_turn_alloc_param *src) + { +@@ -311,7 +311,7 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg, + stun_cb.on_request_complete = &stun_on_request_complete; + stun_cb.on_rx_indication = &stun_on_rx_indication; + status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb, +- PJ_FALSE, sess->grp_lock, &sess->stun); ++ PJ_FALSE, sess->grp_lock, &sess->stun, conn_type); + if (status != PJ_SUCCESS) { + do_destroy(sess); + return status; +@@ -509,9 +509,9 @@ PJ_DEF(pj_status_t) pj_turn_session_get_info( pj_turn_session *sess, + else + pj_bzero(&info->server, sizeof(info->server)); + +- pj_memcpy(&info->mapped_addr, &sess->mapped_addr, ++ pj_memcpy(&info->mapped_addr, &sess->mapped_addr, + sizeof(sess->mapped_addr)); +- pj_memcpy(&info->relay_addr, &sess->relay_addr, ++ pj_memcpy(&info->relay_addr, &sess->relay_addr, + sizeof(sess->relay_addr)); + + return PJ_SUCCESS; +@@ -594,7 +594,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, + + /* See if "domain" contains just IP address */ + tmp_addr.addr.sa_family = sess->af; +- status = pj_inet_pton(sess->af, domain, ++ status = pj_inet_pton(sess->af, domain, + pj_sockaddr_get_addr(&tmp_addr)); + is_ip_addr = (status == PJ_SUCCESS); + +@@ -647,8 +647,8 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, + /* Add reference before async DNS resolution */ + pj_grp_lock_add_ref(sess->grp_lock); + +- status = pj_dns_srv_resolve(domain, &res_name, default_port, +- sess->pool, resolver, opt, sess, ++ status = pj_dns_srv_resolve(domain, &res_name, default_port, ++ sess->pool, resolver, opt, sess, + &dns_srv_resolver_cb, NULL); + if (status != PJ_SUCCESS) { + set_state(sess, PJ_TURN_STATE_NULL); +@@ -664,9 +664,9 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, + unsigned i, cnt; + + /* Default port must be specified */ +- PJ_ASSERT_ON_FAIL(default_port>0 && default_port<65536, ++ PJ_ASSERT_ON_FAIL(default_port>0 && default_port<65536, + {status=PJ_EINVAL; goto on_return;}); +- ++ + sess->default_port = (pj_uint16_t)default_port; + + cnt = PJ_TURN_MAX_DNS_SRV_CNT; +@@ -689,7 +689,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, + + sess->srv_addr_cnt = (pj_uint16_t)cnt; + sess->srv_addr_list = (pj_sockaddr*) +- pj_pool_calloc(sess->pool, cnt, ++ pj_pool_calloc(sess->pool, cnt, + sizeof(pj_sockaddr)); + for (i=0; isrv_addr_list[i]; +@@ -738,8 +738,8 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, + pj_status_t status; + + PJ_ASSERT_RETURN(sess, PJ_EINVAL); +- PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && +- sess->state<=PJ_TURN_STATE_RESOLVED, ++ PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && ++ sess->state<=PJ_TURN_STATE_RESOLVED, + PJ_EINVALIDOP); + PJ_ASSERT_RETURN(!param || param->peer_conn_type == PJ_TURN_TP_UDP || + param->peer_conn_type == PJ_TURN_TP_TCP, +@@ -753,7 +753,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, + + pj_grp_lock_acquire(sess->grp_lock); + +- if (param && param != &sess->alloc_param) ++ if (param && param != &sess->alloc_param) + pj_turn_alloc_param_copy(sess->pool, &sess->alloc_param, param); + + if (sess->state < PJ_TURN_STATE_RESOLVED) { +@@ -769,7 +769,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, + + /* Ready to allocate */ + pj_assert(sess->state == PJ_TURN_STATE_RESOLVED); +- ++ + /* Create a bare request */ + status = pj_stun_session_create_req(sess->stun, PJ_STUN_ALLOCATE_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); +@@ -820,9 +820,9 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, + /* Send request */ + set_state(sess, PJ_TURN_STATE_ALLOCATING); + retransmit = (sess->conn_type == PJ_TURN_TP_UDP); +- status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, ++ status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, + retransmit, sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr), ++ pj_sockaddr_get_len(sess->srv_addr), + tdata); + if (status != PJ_SUCCESS) { + /* Set state back to RESOLVED. We don't want to destroy session now, +@@ -856,7 +856,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess, + pj_grp_lock_acquire(sess->grp_lock); + + /* Create a bare CreatePermission request */ +- status = pj_stun_session_create_req(sess->stun, ++ status = pj_stun_session_create_req(sess->stun, + PJ_STUN_CREATE_PERM_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); + if (status != PJ_SUCCESS) { +@@ -905,10 +905,10 @@ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess, + } + + /* Send the request */ +- status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, ++ status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, + (sess->conn_type==PJ_TURN_TP_UDP), + sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr), ++ pj_sockaddr_get_len(sess->srv_addr), + tdata); + if (status != PJ_SUCCESS) { + /* tdata is already destroyed */ +@@ -964,10 +964,10 @@ static void send_refresh(pj_turn_session *sess, int lifetime) + set_state(sess, PJ_TURN_STATE_DEALLOCATING); + } + +- status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, ++ status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, + (sess->conn_type==PJ_TURN_TP_UDP), + sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr), ++ pj_sockaddr_get_len(sess->srv_addr), + tdata); + if (status != PJ_SUCCESS) + goto on_error; +@@ -985,17 +985,29 @@ on_error: + /** + * Relay data to the specified peer through the session. + */ ++ + PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_sockaddr_t *addr, + unsigned addr_len) ++{ ++ unsigned sent; ++ return pj_turn_session_sendto2(sess, pkt, pkt_len, addr, addr_len, &sent); ++} ++ ++PJ_DEF(pj_status_t) pj_turn_session_sendto2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *addr, ++ unsigned addr_len, ++ unsigned *sent) + { + struct ch_t *ch; + struct perm_t *perm; + pj_status_t status; + +- PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len, ++ PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len, + PJ_EINVAL); + + /* Return error if we're not ready */ +@@ -1012,11 +1024,11 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + /* Permission doesn't exist, install it first */ + char ipstr[PJ_INET6_ADDRSTRLEN+2]; + +- PJ_LOG(4,(sess->obj_name, ++ PJ_LOG(4,(sess->obj_name, + "sendto(): IP %s has no permission, requesting it first..", + pj_sockaddr_print(addr, ipstr, sizeof(ipstr), 2))); + +- status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr, ++ status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr, + 0); + if (status != PJ_SUCCESS) { + pj_grp_lock_release(sess->grp_lock); +@@ -1026,19 +1038,19 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + + /* If peer connection is TCP (RFC 6062), send it directly */ + if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { +- status = sess->cb.on_send_pkt(sess, pkt, pkt_len, addr, addr_len); ++ status = sess->cb.on_send_pkt2(sess, pkt, pkt_len, addr, addr_len, sent, pkt_len); + goto on_return; + } + + /* See if the peer is bound to a channel number */ +- ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), ++ ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), + PJ_FALSE, PJ_FALSE); + if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) { + unsigned total_len; + + /* Peer is assigned a channel number, we can use ChannelData */ + pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt; +- ++ + pj_assert(sizeof(*cd)==4); + + /* Calculate total length, including paddings */ +@@ -1052,11 +1064,10 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + cd->length = pj_htons((pj_uint16_t)pkt_len); + pj_memcpy(cd+1, pkt, pkt_len); + +- pj_assert(sess->srv_addr != NULL); +- +- status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len, +- sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr)); ++ status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, total_len, ++ sess->srv_addr, ++ pj_sockaddr_get_len(sess->srv_addr), ++ sent, pkt_len); + + } else { + /* Use Send Indication. */ +@@ -1070,7 +1081,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + + /* Create blank SEND-INDICATION */ + status = pj_stun_msg_init(&send_ind, PJ_STUN_SEND_INDICATION, +- PJ_STUN_MAGIC, ++ PJ_STUN_MAGIC, + (const pj_uint8_t*)sess->send_ind_tsx_id); + if (status != PJ_SUCCESS) + goto on_return; +@@ -1087,17 +1098,18 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, + pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&data_attr); + + /* Encode the message */ +- status = pj_stun_msg_encode(&send_ind, sess->tx_pkt, ++ status = pj_stun_msg_encode(&send_ind, sess->tx_pkt, + sizeof(sess->tx_pkt), 0, + NULL, &send_ind_len); + if (status != PJ_SUCCESS) + goto on_return; + + /* Send the Send Indication */ +- status = sess->cb.on_send_pkt(sess, sess->tx_pkt, +- (unsigned)send_ind_len, +- sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr)); ++ status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, ++ (unsigned)send_ind_len, ++ sess->srv_addr, ++ pj_sockaddr_get_len(sess->srv_addr), ++ sent, pkt_len); + } + + on_return: +@@ -1124,7 +1136,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, + pj_grp_lock_acquire(sess->grp_lock); + + /* Create blank ChannelBind request */ +- status = pj_stun_session_create_req(sess->stun, ++ status = pj_stun_session_create_req(sess->stun, + PJ_STUN_CHANNEL_BIND_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); + if (status != PJ_SUCCESS) +@@ -1139,7 +1151,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, + /* Channel is already bound. This is a refresh request. */ + ch_num = ch->num; + } else { +- PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX, ++ PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX, + {status=PJ_ETOOMANY; goto on_return;}); + ch->num = ch_num = sess->next_ch++; + } +@@ -1154,10 +1166,10 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, + PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, + peer_adr, addr_len); + +- /* Send the request, associate peer data structure with tdata ++ /* Send the request, associate peer data structure with tdata + * for future reference when we receive the ChannelBind response. + */ +- status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE, ++ status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE, + (sess->conn_type==PJ_TURN_TP_UDP), + sess->srv_addr, + pj_sockaddr_get_len(sess->srv_addr), +@@ -1190,7 +1202,7 @@ PJ_DEF(pj_status_t) pj_turn_session_connection_bind( + pj_grp_lock_acquire(sess->grp_lock); + + /* Create blank ConnectionBind request */ +- status = pj_stun_session_create_req(sess->stun, ++ status = pj_stun_session_create_req(sess->stun, + PJ_STUN_CONNECTION_BIND_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); + if (status != PJ_SUCCESS) +@@ -1206,7 +1218,7 @@ PJ_DEF(pj_status_t) pj_turn_session_connection_bind( + pj_sockaddr_cp(&conn_bind->peer_addr, peer_addr); + conn_bind->peer_addr_len = addr_len; + +- /* Send the request, associate connection data structure with tdata ++ /* Send the request, associate connection data structure with tdata + * for future reference when we receive the ConnectionBind response. + */ + status = pj_stun_session_send_msg(sess->stun, conn_bind, PJ_FALSE, +@@ -1259,7 +1271,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, + { + pj_turn_session_on_rx_pkt_param prm; + pj_status_t status; +- ++ + pj_bzero(&prm, sizeof(prm)); + prm.pkt = pkt; + prm.pkt_len = pkt_len; +@@ -1349,7 +1361,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt2( + + /* Notify application */ + if (sess->cb.on_rx_data) { +- (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)prm->pkt)+sizeof(cd), ++ (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)prm->pkt)+sizeof(cd), + cd.length, &ch->addr, + pj_sockaddr_get_len(&ch->addr)); + } +@@ -1394,7 +1406,7 @@ static pj_status_t stun_on_send_msg(pj_stun_session *stun, + * Handle failed ALLOCATE or REFRESH request. This may switch to alternate + * server if we have one. + */ +-static void on_session_fail( pj_turn_session *sess, ++static void on_session_fail( pj_turn_session *sess, + enum pj_stun_method_e method, + pj_status_t status, + const pj_str_t *reason) +@@ -1415,12 +1427,12 @@ static void on_session_fail( pj_turn_session *sess, + pj_stun_get_method_name(method), + (int)reason->slen, reason->ptr)); + +- /* If this is ALLOCATE response and we don't have more server ++ /* If this is ALLOCATE response and we don't have more server + * addresses to try, notify application and destroy the TURN + * session. + */ + if (method==PJ_STUN_ALLOCATE_METHOD && +- sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1]) ++ sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1]) + { + + set_state(sess, PJ_TURN_STATE_DEALLOCATED); +@@ -1451,7 +1463,7 @@ static void on_session_fail( pj_turn_session *sess, + /* + * Handle successful response to ALLOCATE or REFRESH request. + */ +-static void on_allocate_success(pj_turn_session *sess, ++static void on_allocate_success(pj_turn_session *sess, + enum pj_stun_method_e method, + const pj_stun_msg *msg) + { +@@ -1528,10 +1540,10 @@ static void on_allocate_success(pj_turn_session *sess, + "RELAY-ADDRESS attribute")); + return; + } +- ++ + /* Save relayed address */ + if (raddr_attr) { +- /* If we already have relay address, check if the relay address ++ /* If we already have relay address, check if the relay address + * in the response matches our relay address. + */ + if (pj_sockaddr_has_addr(&sess->relay_addr)) { +@@ -1543,7 +1555,7 @@ static void on_allocate_success(pj_turn_session *sess, + } + } else { + /* Otherwise save the relayed address */ +- pj_memcpy(&sess->relay_addr, &raddr_attr->sockaddr, ++ pj_memcpy(&sess->relay_addr, &raddr_attr->sockaddr, + sizeof(pj_sockaddr)); + } + } +@@ -1591,7 +1603,7 @@ static void stun_on_request_complete(pj_stun_session *stun, + unsigned src_addr_len) + { + pj_turn_session *sess; +- enum pj_stun_method_e method = (enum pj_stun_method_e) ++ enum pj_stun_method_e method = (enum pj_stun_method_e) + PJ_STUN_GET_METHOD(tdata->msg->hdr.type); + + PJ_UNUSED_ARG(src_addr); +@@ -1612,8 +1624,8 @@ static void stun_on_request_complete(pj_stun_session *stun, + } + + /* Handle ALLOCATE response */ +- if (status==PJ_SUCCESS && +- PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) ++ if (status==PJ_SUCCESS && ++ PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + { + + /* Successful Allocate response */ +@@ -1641,8 +1653,8 @@ static void stun_on_request_complete(pj_stun_session *stun, + + } else if (method == PJ_STUN_REFRESH_METHOD) { + /* Handle Refresh response */ +- if (status==PJ_SUCCESS && +- PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) ++ if (status==PJ_SUCCESS && ++ PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + { + /* Success, schedule next refresh. */ + on_allocate_success(sess, method, response); +@@ -1670,8 +1682,8 @@ static void stun_on_request_complete(pj_stun_session *stun, + + } else if (method == PJ_STUN_CHANNEL_BIND_METHOD) { + /* Handle ChannelBind response */ +- if (status==PJ_SUCCESS && +- PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) ++ if (status==PJ_SUCCESS && ++ PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + { + /* Successful ChannelBind response */ + struct ch_t *ch = (struct ch_t*)token; +@@ -1720,8 +1732,8 @@ static void stun_on_request_complete(pj_stun_session *stun, + + } else if (method == PJ_STUN_CREATE_PERM_METHOD) { + /* Handle CreatePermission response */ +- if (status==PJ_SUCCESS && +- PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) ++ if (status==PJ_SUCCESS && ++ PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + { + /* No special handling when the request is successful. */ + } else { +@@ -1740,7 +1752,7 @@ static void stun_on_request_complete(pj_stun_session *stun, + const pj_stun_errcode_attr *eattr; + + eattr = (const pj_stun_errcode_attr*) +- pj_stun_msg_find_attr(response, ++ pj_stun_msg_find_attr(response, + PJ_STUN_ATTR_ERROR_CODE, 0); + if (eattr) { + err_code = eattr->err_code; +@@ -1761,9 +1773,9 @@ static void stun_on_request_complete(pj_stun_session *stun, + it = pj_hash_next(sess->perm_table, it); + + if (perm->req_token == token) { +- PJ_LOG(1,(sess->obj_name, ++ PJ_LOG(1,(sess->obj_name, + "CreatePermission failed for IP %s: %d/%.*s", +- pj_sockaddr_print(&perm->addr, ipstr, ++ pj_sockaddr_print(&perm->addr, ipstr, + sizeof(ipstr), 2), + err_code, (int)reason.slen, reason.ptr)); + +@@ -1784,7 +1796,7 @@ static void stun_on_request_complete(pj_stun_session *stun, + struct conn_bind_t *conn_bind = (struct conn_bind_t*)token; + + if (status != PJ_SUCCESS || +- !PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) ++ !PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) + { + pj_str_t reason = {0}; + if (status == PJ_SUCCESS) { +@@ -1898,7 +1910,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *stun, + + /* Must have both XOR-PEER-ADDRESS and CONNECTION-ID attributes */ + if (!peer_attr || !connection_id_attr) { +- PJ_LOG(4,(sess->obj_name, ++ PJ_LOG(4,(sess->obj_name, + "Received ConnectionAttempt indication with missing " + "attributes")); + return PJ_EINVALIDOP; +@@ -1940,14 +1952,14 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *stun, + + /* Must have both XOR-PEER-ADDRESS and DATA attributes */ + if (!peer_attr || !data_attr) { +- PJ_LOG(4,(sess->obj_name, ++ PJ_LOG(4,(sess->obj_name, + "Received Data indication with missing attributes")); + return PJ_EINVALIDOP; + } + + /* Notify application */ + if (sess->cb.on_rx_data) { +- (*sess->cb.on_rx_data)(sess, data_attr->data, data_attr->length, ++ (*sess->cb.on_rx_data)(sess, data_attr->data, data_attr->length, + &peer_attr->sockaddr, + pj_sockaddr_get_len(&peer_attr->sockaddr)); + } +@@ -1985,15 +1997,15 @@ static void dns_srv_resolver_cb(void *user_data, + + /* Allocate server entries */ + sess->srv_addr_list = (pj_sockaddr*) +- pj_pool_calloc(sess->pool, tot_cnt, ++ pj_pool_calloc(sess->pool, tot_cnt, + sizeof(pj_sockaddr)); + + /* Copy results to server entries */ + for (i=0, cnt=0; icount && cntentry[i].server.addr_count && +- cntentry[i].server.addr_count && ++ cntentry[i].server.addr[j].af == sess->af) { + pj_sockaddr *addr = &sess->srv_addr_list[cnt]; +@@ -2041,7 +2053,7 @@ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, + pj_uint32_t hval = 0; + struct ch_t *ch; + +- ch = (struct ch_t*) ++ ch = (struct ch_t*) + pj_hash_get(sess->ch_table, addr, addr_len, &hval); + if (ch == NULL && update) { + ch = PJ_POOL_ZALLOC_T(sess->pool, struct ch_t); +@@ -2062,7 +2074,7 @@ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, + /* Register by channel number */ + pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound); + +- if (pj_hash_get(sess->ch_table, &ch->num, ++ if (pj_hash_get(sess->ch_table, &ch->num, + sizeof(ch->num), &hval2)==0) { + pj_hash_set(sess->pool, sess->ch_table, &ch->num, + sizeof(ch->num), hval2, ch); +@@ -2089,7 +2101,7 @@ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, + static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess, + pj_uint16_t chnum) + { +- return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum, ++ return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum, + sizeof(chnum), NULL); + } + +@@ -2114,7 +2126,7 @@ static struct perm_t *lookup_perm(pj_turn_session *sess, + } + + /* lookup and create if it doesn't exist and wanted */ +- perm = (struct perm_t*) ++ perm = (struct perm_t*) + pj_hash_get(sess->perm_table, addr, addr_len, &hval); + if (perm == NULL && update) { + perm = PJ_POOL_ZALLOC_T(sess->pool, struct perm_t); +@@ -2147,7 +2159,7 @@ static void invalidate_perm(pj_turn_session *sess, + /* + * Scan permission's hash table to refresh the permission. + */ +-static unsigned refresh_permissions(pj_turn_session *sess, ++static unsigned refresh_permissions(pj_turn_session *sess, + const pj_time_val *now) + { + pj_stun_tx_data *tdata = NULL; +@@ -2169,7 +2181,7 @@ static unsigned refresh_permissions(pj_turn_session *sess, + if (tdata == NULL) { + /* Create a bare CreatePermission request */ + status = pj_stun_session_create_req( +- sess->stun, ++ sess->stun, + PJ_STUN_CREATE_PERM_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); + if (status != PJ_SUCCESS) { +@@ -2185,7 +2197,7 @@ static unsigned refresh_permissions(pj_turn_session *sess, + } + + status = pj_stun_msg_add_sockaddr_attr( +- tdata->pool, ++ tdata->pool, + tdata->msg, + PJ_STUN_ATTR_XOR_PEER_ADDR, + PJ_TRUE, +@@ -2211,10 +2223,10 @@ static unsigned refresh_permissions(pj_turn_session *sess, + } + + if (tdata) { +- status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, ++ status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, + (sess->conn_type==PJ_TURN_TP_UDP), + sess->srv_addr, +- pj_sockaddr_get_len(sess->srv_addr), ++ pj_sockaddr_get_len(sess->srv_addr), + tdata); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(sess->obj_name, status, +@@ -2241,7 +2253,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) + + eid = (enum timer_id_t) e->id; + e->id = TIMER_NONE; +- ++ + if (eid == TIMER_KEEP_ALIVE) { + pj_time_val now; + pj_hash_iterator_t itbuf, *it; +@@ -2270,11 +2282,11 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) + /* Scan hash table to refresh bound channels */ + it = pj_hash_first(sess->ch_table, &itbuf); + while (it) { +- struct ch_t *ch = (struct ch_t*) ++ struct ch_t *ch = (struct ch_t*) + pj_hash_this(sess->ch_table, it); + if (ch->bound && PJ_TIME_VAL_LTE(ch->expiry, now)) { + +- /* Send ChannelBind to refresh channel binding and ++ /* Send ChannelBind to refresh channel binding and + * permission. + */ + pj_turn_session_bind_channel(sess, &ch->addr, +@@ -2297,7 +2309,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) + pj_status_t rc; + + /* Create blank SEND-INDICATION */ +- rc = pj_stun_session_create_ind(sess->stun, ++ rc = pj_stun_session_create_ind(sess->stun, + PJ_STUN_SEND_INDICATION, &tdata); + if (rc == PJ_SUCCESS) { + /* Add DATA attribute with zero length */ +@@ -2305,7 +2317,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) + PJ_STUN_ATTR_DATA, NULL, 0); + + /* Send the indication */ +- pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, ++ pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, + PJ_FALSE, sess->srv_addr, + pj_sockaddr_get_len(sess->srv_addr), + tdata); +diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c +index e273e6e28..ada864c85 100644 +--- a/pjnath/src/pjnath/turn_sock.c ++++ b/pjnath/src/pjnath/turn_sock.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -99,6 +99,10 @@ struct pj_turn_sock + /* Data connection, when peer_conn_type==PJ_TURN_TP_TCP (RFC 6062) */ + unsigned data_conn_cnt; + tcp_data_conn_t data_conn[PJ_TURN_MAX_TCP_CONN_CNT]; ++ ++ /* The following variables are used by the on_data_sent callback */ ++ unsigned current_pkt_len; ++ unsigned current_body_len; + }; + + +@@ -115,6 +119,13 @@ static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess, + unsigned pkt_len, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len); ++static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *dst_addr, ++ unsigned dst_addr_len, ++ unsigned *sent, ++ unsigned body_len); + static void turn_on_channel_bound(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len, +@@ -124,7 +135,7 @@ static void turn_on_rx_data(pj_turn_session *sess, + unsigned pkt_len, + const pj_sockaddr_t *peer_addr, + unsigned addr_len); +-static void turn_on_state(pj_turn_session *sess, ++static void turn_on_state(pj_turn_session *sess, + pj_turn_state_t old_state, + pj_turn_state_t new_state); + static void turn_on_connection_attempt(pj_turn_session *sess, +@@ -252,7 +263,7 @@ PJ_DEF(void) pj_turn_sock_tls_cfg_wipe_keys(pj_turn_sock_tls_cfg *tls_cfg) + wipe_buf(&tls_cfg->password); + wipe_buf(&tls_cfg->ca_buf); + wipe_buf(&tls_cfg->cert_buf); +- wipe_buf(&tls_cfg->privkey_buf); ++ wipe_buf(&tls_cfg->privkey_buf); + } + #endif + +@@ -349,6 +360,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_send_pkt = &turn_on_send_pkt; + sess_cb.on_stun_send_pkt = &turn_on_stun_send_pkt; ++ sess_cb.on_send_pkt2 = &turn_on_send_pkt2; + sess_cb.on_channel_bound = &turn_on_channel_bound; + sess_cb.on_rx_data = &turn_on_rx_data; + sess_cb.on_state = &turn_on_state; +@@ -545,7 +557,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock) + } + + /* +- * Set STUN message logging for this TURN session. ++ * Set STUN message logging for this TURN session. + */ + PJ_DEF(void) pj_turn_sock_set_log( pj_turn_sock *turn_sock, + unsigned flags) +@@ -579,7 +591,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, + + pj_grp_lock_acquire(turn_sock->grp_lock); + +- /* Copy alloc param. We will call session_alloc() only after the ++ /* Copy alloc param. We will call session_alloc() only after the + * server address has been resolved. + */ + if (param) { +@@ -643,7 +655,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_set_perm( pj_turn_sock *turn_sock, + + /* + * Send packet. +- */ ++ */ + PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, + const pj_uint8_t *pkt, + unsigned pkt_len, +@@ -659,10 +671,26 @@ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, + * to store our actual data length to be sent here. + */ + turn_sock->body_len = pkt_len; +- return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len, ++ return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len, + addr, addr_len); + } + ++PJ_DEF(pj_status_t) pj_turn_sock_sendto2( pj_turn_sock *turn_sock, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *addr, ++ unsigned addr_len, ++ unsigned *sent) ++{ ++ PJ_ASSERT_RETURN(turn_sock && addr && addr_len, PJ_EINVAL); ++ ++ if (turn_sock->sess == NULL) ++ return PJ_EINVALIDOP; ++ ++ return pj_turn_session_sendto2(turn_sock->sess, pkt, pkt_len, ++ addr, addr_len, sent); ++} ++ + /* + * Bind a peer address to a channel number. + */ +@@ -759,10 +787,10 @@ static pj_bool_t on_connect_complete(pj_turn_sock *turn_sock, + } + + /* Kick start pending read operation */ +- if (turn_sock->conn_type != PJ_TURN_TP_TLS) +- status = pj_activesock_start_read(turn_sock->active_sock, ++ if (turn_sock->conn_type != PJ_TURN_TP_TLS) ++ status = pj_activesock_start_read(turn_sock->active_sock, + turn_sock->pool, +- turn_sock->setting.max_pkt_size, ++ turn_sock->setting.max_pkt_size, + 0); + #if PJ_HAS_SSL_SOCK + else +@@ -854,7 +882,7 @@ static unsigned has_packet(pj_turn_sock *turn_sock, const void *buf, pj_size_t b + pj_memcpy(&cd, buf, sizeof(pj_turn_channel_data)); + cd.length = pj_ntohs(cd.length); + +- if (bufsize >= cd.length+sizeof(cd)) ++ if (bufsize >= cd.length+sizeof(cd)) + return (cd.length+sizeof(cd)+3) & (~3); + else + return 0; +@@ -880,18 +908,18 @@ static pj_bool_t on_data_read(pj_turn_sock *turn_sock, + */ + unsigned pkt_len; + +- //PJ_LOG(5,(turn_sock->pool->obj_name, ++ //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Incoming data, %lu bytes total buffer", size)); + + while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { + pj_size_t parsed_len; + //const pj_uint8_t *pkt = (const pj_uint8_t*)data; + +- //PJ_LOG(5,(turn_sock->pool->obj_name, +- // "Packet start: %02X %02X %02X %02X", ++ //PJ_LOG(5,(turn_sock->pool->obj_name, ++ // "Packet start: %02X %02X %02X %02X", + // pkt[0], pkt[1], pkt[2], pkt[3])); + +- //PJ_LOG(5,(turn_sock->pool->obj_name, ++ //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Processing %lu bytes packet of %lu bytes total buffer", + // pkt_len, size)); + +@@ -912,7 +940,7 @@ static pj_bool_t on_data_read(pj_turn_sock *turn_sock, + } + size = *remainder; + +- //PJ_LOG(5,(turn_sock->pool->obj_name, ++ //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Buffer size now %lu bytes", size)); + } + } else if (status != PJ_SUCCESS) { +@@ -956,12 +984,7 @@ static pj_bool_t on_data_sent(pj_turn_sock *turn_sock, + } + + if (turn_sock->cb.on_data_sent) { +- pj_ssize_t header_len, sent_size; +- +- /* Remove the length of packet header from sent size. */ +- header_len = turn_sock->pkt_len - turn_sock->body_len; +- sent_size = (sent > header_len)? (sent - header_len) : 0; +- (*turn_sock->cb.on_data_sent)(turn_sock, sent_size); ++ (*turn_sock->cb.on_data_sent)(turn_sock, sent); + } + + return PJ_TRUE; +@@ -1028,7 +1051,7 @@ static pj_status_t send_pkt(pj_turn_session *sess, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len) + { +- pj_turn_sock *turn_sock = (pj_turn_sock*) ++ pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_session_get_user_data(sess); + pj_ssize_t len = pkt_len; + pj_status_t status = PJ_SUCCESS; +@@ -1108,6 +1131,74 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, + dst_addr, dst_addr_len); + } + ++static pj_status_t turn_on_send_pkt2(pj_turn_session *sess, ++ const pj_uint8_t *pkt, ++ unsigned pkt_len, ++ const pj_sockaddr_t *dst_addr, ++ unsigned dst_addr_len, ++ unsigned *sent, ++ unsigned body_len) ++{ ++ *sent = pkt_len; ++ pj_turn_sock *turn_sock = (pj_turn_sock*)pj_turn_session_get_user_data(sess); ++ pj_status_t status = PJ_SUCCESS; ++ ++ pj_ssize_t len = pkt_len; ++ turn_sock->current_body_len = body_len; ++ turn_sock->current_pkt_len = pkt_len; ++ ++ if (turn_sock == NULL || turn_sock->is_destroying) { ++ /* We've been destroyed */ ++ // https://trac.pjsip.org/repos/ticket/1316 ++ //pj_assert(!"We should shutdown gracefully"); ++ return PJ_EINVALIDOP; ++ } ++ ++ if (turn_sock->conn_type == PJ_TURN_TP_UDP) { ++ status = pj_activesock_sendto(turn_sock->active_sock, ++ &turn_sock->send_key, pkt, &len, 0, ++ dst_addr, dst_addr_len); ++ } else if (turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) { ++ pj_turn_session_info info; ++ pj_turn_session_get_info(turn_sock->sess, &info); ++ if (pj_sockaddr_cmp(&info.server, dst_addr) == 0) { ++ /* Destination address is TURN server */ ++ status = pj_activesock_send(turn_sock->active_sock, ++ &turn_sock->send_key, pkt, &len, 0); ++ } else { ++ /* Destination address is peer, lookup data connection */ ++ unsigned i; ++ ++ status = PJ_ENOTFOUND; ++ for (i=0; i < PJ_TURN_MAX_TCP_CONN_CNT; ++i) { ++ tcp_data_conn_t *conn = &turn_sock->data_conn[i]; ++ if (conn->state < DATACONN_STATE_CONN_BINDING) ++ continue; ++ if (pj_sockaddr_cmp(&conn->peer_addr, dst_addr) == 0) { ++ status = pj_activesock_send(conn->asock, ++ &conn->send_key, ++ pkt, &len, 0); ++ break; ++ } ++ } ++ } ++ } else { ++ status = pj_activesock_send(turn_sock->active_sock, ++ &turn_sock->send_key, pkt, &len, 0); ++ } ++ ++ if (status != PJ_SUCCESS && status != PJ_EPENDING) { ++ show_err(turn_sock, "socket send()", status); ++ } ++ ++ // Remove header from sent size. ++ // The application only wants to know if the packet is actually sent. ++ unsigned header_len = pkt_len - body_len; ++ *sent = (len > header_len)? (len - header_len) : 0; ++ ++ return status; ++ } ++ + static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, +@@ -1143,7 +1234,7 @@ static void turn_on_rx_data(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len) + { +- pj_turn_sock *turn_sock = (pj_turn_sock*) ++ pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_session_get_user_data(sess); + if (turn_sock == NULL || turn_sock->is_destroying) { + /* We've been destroyed */ +@@ -1156,7 +1247,7 @@ static void turn_on_rx_data(pj_turn_session *sess, + } + + if (turn_sock->cb.on_rx_data) { +- (*turn_sock->cb.on_rx_data)(turn_sock, pkt, pkt_len, ++ (*turn_sock->cb.on_rx_data)(turn_sock, pkt, pkt_len, + peer_addr, addr_len); + } + } +@@ -1165,11 +1256,11 @@ static void turn_on_rx_data(pj_turn_session *sess, + /* + * Callback from TURN session when state has changed + */ +-static void turn_on_state(pj_turn_session *sess, ++static void turn_on_state(pj_turn_session *sess, + pj_turn_state_t old_state, + pj_turn_state_t new_state) + { +- pj_turn_sock *turn_sock = (pj_turn_sock*) ++ pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_session_get_user_data(sess); + pj_status_t status = PJ_SUCCESS; + +@@ -1210,8 +1301,8 @@ static void turn_on_state(pj_turn_session *sess, + * we're switching to alternate TURN server when either TCP + * connection or ALLOCATE request failed. + */ +- if ((turn_sock->conn_type != PJ_TURN_TP_TLS) && +- (turn_sock->active_sock)) ++ if ((turn_sock->conn_type != PJ_TURN_TP_TLS) && ++ (turn_sock->active_sock)) + { + pj_activesock_close(turn_sock->active_sock); + turn_sock->active_sock = NULL; +@@ -1241,7 +1332,7 @@ static void turn_on_state(pj_turn_session *sess, + max_bind_retry = turn_sock->setting.port_range; + } + pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0); +- if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || ++ if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || + cfg_bind_addr->addr.sa_family == pj_AF_INET6()) + { + pj_sockaddr_cp(&bound_addr, cfg_bind_addr); +@@ -1271,7 +1362,7 @@ static void turn_on_state(pj_turn_session *sess, + &turn_sock->setting.qos_params, + (turn_sock->setting.qos_ignore_error?2:1), + turn_sock->pool->obj_name, NULL); +- if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) ++ if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) + { + pj_sock_close(sock); + turn_sock_destroy(turn_sock, status); +@@ -1331,7 +1422,7 @@ static void turn_on_state(pj_turn_session *sess, + sock_type, &asock_cfg, + turn_sock->cfg.ioqueue, &asock_cb, + turn_sock, +- &turn_sock->active_sock); ++ &turn_sock->active_sock); + if (status != PJ_SUCCESS) + pj_sock_close(sock); + } +@@ -1436,8 +1527,8 @@ static void turn_on_state(pj_turn_session *sess, + } + + PJ_LOG(5,(turn_sock->pool->obj_name, +- "Connecting to %s", +- pj_sockaddr_print(&info.server, addrtxt, ++ "Connecting to %s", ++ pj_sockaddr_print(&info.server, addrtxt, + sizeof(addrtxt), 3))); + + /* Initiate non-blocking connect */ +@@ -1447,12 +1538,12 @@ static void turn_on_state(pj_turn_session *sess, + #if PJ_HAS_TCP + else if (turn_sock->conn_type == PJ_TURN_TP_TCP) { + status=pj_activesock_start_connect( +- turn_sock->active_sock, ++ turn_sock->active_sock, + turn_sock->pool, +- &info.server, ++ &info.server, + pj_sockaddr_get_len(&info.server)); +- } +-#endif ++ } ++#endif + #if PJ_HAS_SSL_SOCK + else if (turn_sock->conn_type == PJ_TURN_TP_TLS) { + pj_ssl_start_connect_param connect_param; +@@ -1478,7 +1569,7 @@ static void turn_on_state(pj_turn_session *sess, + return; + } + +- /* Done for now. Subsequent work will be done in ++ /* Done for now. Subsequent work will be done in + * on_connect_complete() callback. + */ + } +@@ -1486,9 +1577,6 @@ static void turn_on_state(pj_turn_session *sess, + if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) { + pj_time_val delay = {0, 0}; + +- turn_sock->sess = NULL; +- pj_turn_session_set_user_data(sess, NULL); +- + pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap, + &turn_sock->timer, 0); + pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap, +@@ -1525,8 +1613,12 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock, + + if (size == 0 && status != PJ_SUCCESS) { + /* Connection gone, release data connection */ +- dataconn_cleanup(conn); +- --turn_sock->data_conn_cnt; ++ if (conn->state == DATACONN_STATE_CONN_BINDING) { ++ // TODO cancel request (and do not cleanup there) ++ } else if (conn->state == DATACONN_STATE_READY) { ++ dataconn_cleanup(conn); ++ --turn_sock->data_conn_cnt; ++ } + pj_grp_lock_release(turn_sock->grp_lock); + return PJ_FALSE; + } +@@ -1592,6 +1684,14 @@ static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock, + + pj_grp_lock_acquire(turn_sock->grp_lock); + ++ if (pj_turn_sock_get_user_data(turn_sock) == NULL) { ++ // It's possible for a TURN socket to be destroyed by ice_close_remaining_tcp ++ // after the on_connect_complete event has been put into an ioqueue, but ++ // before the callback is actually called, so we need to check for this. ++ PJ_LOG(4,(turn_sock->obj_name, "Socket is being destroyed, can't be used to establish a data connection")); ++ status = PJ_ECANCELLED; ++ } ++ + if (status == PJ_SUCCESS) { + status = pj_activesock_start_read(asock, turn_sock->pool, + turn_sock->setting.max_pkt_size, 0); +@@ -1621,7 +1721,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len) + { +- pj_turn_sock *turn_sock = (pj_turn_sock*) ++ pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_session_get_user_data(sess); + pj_pool_t *pool; + tcp_data_conn_t *new_conn; +@@ -1796,7 +1896,7 @@ on_return: + pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)); + + if (!new_conn->asock && sock != PJ_INVALID_SOCKET) +- pj_sock_close(sock); ++ pj_sock_close(sock); + + dataconn_cleanup(new_conn); + --turn_sock->data_conn_cnt; +@@ -1816,7 +1916,7 @@ static void turn_on_connection_bind_status(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len) + { +- pj_turn_sock *turn_sock = (pj_turn_sock*) ++ pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_session_get_user_data(sess); + tcp_data_conn_t *conn = NULL; + unsigned i; +@@ -1860,7 +1960,7 @@ static void turn_on_connect_complete(pj_turn_session *sess, + const pj_sockaddr_t *peer_addr, + unsigned addr_len) + { +- pj_turn_sock *turn_sock = (pj_turn_sock*) ++ pj_turn_sock *turn_sock = (pj_turn_sock*) + pj_turn_session_get_user_data(sess); + pj_pool_t *pool; + tcp_data_conn_t *new_conn; +@@ -2030,7 +2130,7 @@ on_return: + pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)); + + if (!new_conn->asock && sock != PJ_INVALID_SOCKET) +- pj_sock_close(sock); ++ pj_sock_close(sock); + + dataconn_cleanup(new_conn); + --turn_sock->data_conn_cnt; +@@ -2043,3 +2143,20 @@ on_return: + } + pj_grp_lock_release(turn_sock->grp_lock); + } ++ ++pj_bool_t pj_turn_sock_has_dataconn(pj_turn_sock *turn_sock, ++ const pj_sockaddr_t *peer) ++{ ++ if (!turn_sock) return PJ_FALSE; ++ ++ for (int i = 0; i < turn_sock->data_conn_cnt; ++i) { ++ tcp_data_conn_t* dataconn = &turn_sock->data_conn[i]; ++ if (dataconn) { ++ pj_sockaddr_t* conn_peer = &dataconn->peer_addr; ++ if (pj_sockaddr_cmp(conn_peer, peer) == 0) ++ return PJ_TRUE; ++ } ++ } ++ ++ return PJ_FALSE; ++} +diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c +index 686a81e70..b18a7be32 100644 +--- a/pjnath/src/pjturn-client/client_main.c ++++ b/pjnath/src/pjturn-client/client_main.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -78,7 +78,7 @@ static void turn_on_rx_data(pj_turn_sock *relay, + unsigned addr_len); + static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, + pj_turn_state_t new_state); +-static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, ++static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status); + static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock, +@@ -130,7 +130,7 @@ static int init() + /* Create global ioqueue */ + CHECK( pj_ioqueue_create(g.pool, 16, &g.stun_config.ioqueue) ); + +- /* ++ /* + * Create peers + */ + for (i=0; i<(int)PJ_ARRAY_SIZE(g.peer); ++i) { +@@ -153,8 +153,8 @@ static int init() + #endif + + name[strlen(name)-1] = '0'+i; +- status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), +- &stun_sock_cb, &ss_cfg, ++ status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), ++ PJ_STUN_TP_UDP, &stun_sock_cb, &ss_cfg, + &g.peer[i], &g.peer[i].stun_sock); + if (status != PJ_SUCCESS) { + my_perror("pj_stun_sock_create()", status); +@@ -168,7 +168,7 @@ static int init() + server = pj_str(o.srv_addr); + port = (pj_uint16_t)(o.srv_port?atoi(o.srv_port):PJ_STUN_PORT); + } +- status = pj_stun_sock_start(g.peer[i].stun_sock, &server, ++ status = pj_stun_sock_start(g.peer[i].stun_sock, &server, + port, NULL); + if (status != PJ_SUCCESS) { + my_perror("pj_stun_sock_start()", status); +@@ -257,8 +257,8 @@ static pj_status_t create_relay(void) + if (o.nameserver) { + pj_str_t ns = pj_str(o.nameserver); + +- status = pj_dns_resolver_create(&g.cp.factory, "resolver", 0, +- g.stun_config.timer_heap, ++ status = pj_dns_resolver_create(&g.cp.factory, "resolver", 0, ++ g.stun_config.timer_heap, + g.stun_config.ioqueue, &g.resolver); + if (status != PJ_SUCCESS) { + PJ_LOG(1,(THIS_FILE, "Error creating resolver (err=%d)", status)); +@@ -275,7 +275,7 @@ static pj_status_t create_relay(void) + pj_bzero(&rel_cb, sizeof(rel_cb)); + rel_cb.on_rx_data = &turn_on_rx_data; + rel_cb.on_state = &turn_on_state; +- CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(), ++ CHECK( pj_turn_sock_create(&g.stun_config, pj_AF_INET(), + (o.use_tcp? PJ_TURN_TP_TCP : PJ_TURN_TP_UDP), + &rel_cb, 0, + NULL, &g.relay) ); +@@ -332,7 +332,7 @@ static void turn_on_rx_data(pj_turn_sock *relay, + static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, + pj_turn_state_t new_state) + { +- PJ_LOG(3,(THIS_FILE, "State %s --> %s", pj_turn_state_name(old_state), ++ PJ_LOG(3,(THIS_FILE, "State %s --> %s", pj_turn_state_name(old_state), + pj_turn_state_name(new_state))); + + if (new_state == PJ_TURN_STATE_READY) { +@@ -345,7 +345,7 @@ static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, + } + } + +-static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, ++static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status) + { +@@ -458,7 +458,7 @@ static void console_main(void) + + if (fgets(input, sizeof(input), stdin) == NULL) + break; +- ++ + switch (input[0]) { + case 'a': + create_relay(); +@@ -477,9 +477,9 @@ static void console_main(void) + peer = &g.peer[1]; + + pj_ansi_strxcpy(input, "Hello from client", sizeof(input)); +- status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input, +- strlen(input)+1, +- &peer->mapped_addr, ++ status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input, ++ strlen(input)+1, ++ &peer->mapped_addr, + pj_sockaddr_get_len(&peer->mapped_addr)); + if (status != PJ_SUCCESS && status != PJ_EPENDING) + my_perror("turn_udp_sendto() failed", status); +@@ -622,10 +622,10 @@ int main(int argc, char *argv[]) + + if ((status=init()) != 0) + goto on_return; +- ++ + //if ((status=create_relay()) != 0) + // goto on_return; +- ++ + console_main(); + + on_return: +diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c +index 640c87a3d..dc6b3ee0c 100644 +--- a/pjnath/src/pjturn-srv/allocation.c ++++ b/pjnath/src/pjturn-srv/allocation.c +@@ -338,7 +338,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, + sess_cb.on_rx_request = &stun_on_rx_request; + sess_cb.on_rx_indication = &stun_on_rx_indication; + status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, +- &sess_cb, PJ_FALSE, NULL, &alloc->sess); ++ &sess_cb, PJ_FALSE, NULL, &alloc->sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + goto on_error; + } +diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c +index 48b62ce13..997c84a9f 100644 +--- a/pjnath/src/pjturn-srv/server.c ++++ b/pjnath/src/pjturn-srv/server.c +@@ -155,7 +155,7 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, + + status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name, + &sess_cb, PJ_FALSE, NULL, +- &srv->core.stun_sess); ++ &srv->core.stun_sess, PJ_STUN_TP_UDP); + if (status != PJ_SUCCESS) { + goto on_error; + } +diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c +index ed8f010a6..c10f86f87 100644 +--- a/pjsip-apps/src/samples/icedemo.c ++++ b/pjsip-apps/src/samples/icedemo.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify +@@ -13,7 +13,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -43,6 +43,7 @@ static struct app_t + pj_str_t stun_srv; + pj_str_t turn_srv; + pj_bool_t turn_tcp; ++ pj_bool_t ice_tcp; + pj_str_t turn_username; + pj_str_t turn_password; + pj_bool_t turn_fingerprint; +@@ -92,7 +93,7 @@ static void err_exit(const char *title, pj_status_t status) + + if (icedemo.icest) + pj_ice_strans_destroy(icedemo.icest); +- ++ + pj_thread_sleep(500); + + icedemo.thread_quit_flag = PJ_TRUE; +@@ -150,13 +151,13 @@ static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) + pj_assert(timeout.sec >= 0 && timeout.msec >= 0); + if (timeout.msec >= 1000) timeout.msec = 999; + +- /* compare the value with the timeout to wait from timer, and use the +- * minimum value. ++ /* compare the value with the timeout to wait from timer, and use the ++ * minimum value. + */ + if (PJ_TIME_VAL_GT(timeout, max_timeout)) + timeout = max_timeout; + +- /* Poll ioqueue. ++ /* Poll ioqueue. + * Repeat polling the ioqueue while we have immediate events, because + * timer heap may process more than one events, so if we only process + * one network events at a time (such as when IOCP backend is used), +@@ -212,7 +213,7 @@ static int icedemo_worker_thread(void *unused) + * as STUN connectivity checks or TURN signaling). + */ + static void cb_on_rx_data(pj_ice_strans *ice_st, +- unsigned comp_id, ++ unsigned comp_id, + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +@@ -237,11 +238,11 @@ static void cb_on_rx_data(pj_ice_strans *ice_st, + * This is the callback that is registered to the ICE stream transport to + * receive notification about ICE state progression. + */ +-static void cb_on_ice_complete(pj_ice_strans *ice_st, ++static void cb_on_ice_complete(pj_ice_strans *ice_st, + pj_ice_strans_op op, + pj_status_t status) + { +- const char *opname = ++ const char *opname = + (op==PJ_ICE_STRANS_OP_INIT? "initialization" : + (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op")); + +@@ -269,7 +270,7 @@ static void log_func(int level, const char *data, int len) + + /* + * This is the main application initialization function. It is called +- * once (and only once) during application initialization sequence by ++ * once (and only once) during application initialization sequence by + * main(). + */ + static pj_status_t icedemo_init(void) +@@ -295,18 +296,18 @@ static pj_status_t icedemo_init(void) + icedemo.ice_cfg.stun_cfg.pf = &icedemo.cp.factory; + + /* Create application memory pool */ +- icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", ++ icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", + 512, 512, NULL); + + /* Create timer heap for timer stuff */ +- CHECK( pj_timer_heap_create(icedemo.pool, 100, ++ CHECK( pj_timer_heap_create(icedemo.pool, 100, + &icedemo.ice_cfg.stun_cfg.timer_heap) ); + + /* and create ioqueue for network I/O stuff */ +- CHECK( pj_ioqueue_create(icedemo.pool, 16, ++ CHECK( pj_ioqueue_create(icedemo.pool, 16, + &icedemo.ice_cfg.stun_cfg.ioqueue) ); + +- /* something must poll the timer heap and ioqueue, ++ /* something must poll the timer heap and ioqueue, + * unless we're on Symbian where the timer heap and ioqueue run + * on themselves. + */ +@@ -317,14 +318,14 @@ static pj_status_t icedemo_init(void) + + /* Create DNS resolver if nameserver is set */ + if (icedemo.opt.ns.slen) { +- CHECK( pj_dns_resolver_create(&icedemo.cp.factory, +- "resolver", +- 0, ++ CHECK( pj_dns_resolver_create(&icedemo.cp.factory, ++ "resolver", ++ 0, + icedemo.ice_cfg.stun_cfg.timer_heap, +- icedemo.ice_cfg.stun_cfg.ioqueue, ++ icedemo.ice_cfg.stun_cfg.ioqueue, + &icedemo.ice_cfg.resolver) ); + +- CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, ++ CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, + &icedemo.opt.ns, NULL) ); + } + +@@ -340,6 +341,12 @@ static pj_status_t icedemo_init(void) + else + icedemo.ice_cfg.opt.aggressive = PJ_TRUE; + ++ /* Connection type to STUN server */ ++ if (icedemo.opt.ice_tcp) ++ icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_TCP; ++ else ++ icedemo.ice_cfg.stun.conn_type = PJ_STUN_TP_UDP; ++ + /* Configure STUN/srflx candidate resolution */ + if (icedemo.opt.stun_srv.slen) { + char *pos; +@@ -394,6 +401,10 @@ static pj_status_t icedemo_init(void) + icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; + } + ++ if (icedemo.opt.ice_tcp) { ++ icedemo.ice_cfg.protocol = PJ_ICE_TP_TCP; ++ } ++ + /* -= That's it for now, initialization is complete =- */ + return PJ_SUCCESS; + } +@@ -462,8 +473,8 @@ static void icedemo_destroy_instance(void) + */ + static void icedemo_init_session(unsigned rolechar) + { +- pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? +- PJ_ICE_SESS_ROLE_CONTROLLING : ++ pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? ++ PJ_ICE_SESS_ROLE_CONTROLLING : + PJ_ICE_SESS_ROLE_CONTROLLED); + pj_status_t status; + +@@ -529,18 +540,36 @@ static int print_cand(char buffer[], unsigned maxlen, + char *p = buffer; + int printed; + +- PRINT("a=candidate:%.*s %u UDP %u %s %u typ ", ++ PRINT("a=candidate:%.*s %u %s %u %s %u typ ", + (int)cand->foundation.slen, + cand->foundation.ptr, + (unsigned)cand->comp_id, ++ cand->transport == PJ_CAND_UDP? "UDP" : "TCP", + cand->prio, +- pj_sockaddr_print(&cand->addr, ipaddr, ++ pj_sockaddr_print(&cand->addr, ipaddr, + sizeof(ipaddr), 0), + (unsigned)pj_sockaddr_get_port(&cand->addr)); + + PRINT("%s\n", + pj_ice_get_cand_type_name(cand->type)); + ++ if (cand->transport != PJ_CAND_UDP) { ++ PRINT(" tcptype"); ++ switch (cand->transport) { ++ case PJ_CAND_TCP_ACTIVE: ++ PRINT(" active"); ++ break; ++ case PJ_CAND_TCP_PASSIVE: ++ PRINT(" passive"); ++ break; ++ case PJ_CAND_TCP_SO: ++ default: ++ PRINT(" so"); ++ break; ++ } ++ } ++ PRINT("\n"); ++ + if (p == buffer+maxlen) + return -PJ_ETOOSMALL; + +@@ -549,7 +578,7 @@ static int print_cand(char buffer[], unsigned maxlen, + return (int)(p-buffer); + } + +-/* ++/* + * Encode ICE information in SDP. + */ + static int encode_session(char buffer[], unsigned maxlen) +@@ -607,6 +636,26 @@ static int encode_session(char buffer[], unsigned maxlen) + sizeof(ipaddr), 0)); + } + ++ if (cand[0].transport != PJ_CAND_UDP) { ++ /** RFC 6544, Section 4.5: ++ * If the default candidate is TCP-based, the agent MUST include the ++ * a=setup and a=connection attributes from RFC 4145 [RFC4145], ++ * following the procedures defined there as if ICE were not in use. ++ */ ++ PRINT("a=setup:"); ++ switch (cand[0].transport) { ++ case PJ_CAND_TCP_ACTIVE: ++ PRINT("active\n"); ++ break; ++ case PJ_CAND_TCP_PASSIVE: ++ PRINT("passive\n"); ++ break; ++ default: ++ return PJ_EINVALIDOP; ++ } ++ PRINT("a=connection:new\n"); ++ } ++ + /* Enumerate all candidates for this component */ + cand_cnt = PJ_ARRAY_SIZE(cand); + status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, +@@ -663,7 +712,7 @@ static void icedemo_show_ice(void) + return; + } + +- printf("Negotiated comp_cnt: %d\n", ++ printf("Negotiated comp_cnt: %d\n", + pj_ice_strans_get_running_comp_cnt(icedemo.icest)); + printf("Role : %s\n", + pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ? +@@ -703,12 +752,12 @@ static void icedemo_show_ice(void) + + + /* +- * Input and parse SDP from the remote (containing remote's ICE information) ++ * Input and parse SDP from the remote (containing remote's ICE information) + * and save it to global variables. + */ + static void icedemo_input_remote(void) + { +- char linebuf[80]; ++ char linebuf[120]; + unsigned media_cnt = 0; + unsigned comp0_port = 0; + char comp0_addr[80]; +@@ -764,14 +813,14 @@ static void icedemo_input_remote(void) + } + + comp0_port = atoi(portstr); +- ++ + } + break; + case 'c': + { + int cnt; + char c[32], net[32], ip[80]; +- ++ + cnt = sscanf(line+2, "%s %s %s", c, net, ip); + if (cnt != 3) { + PJ_LOG(1,(THIS_FILE, "Error parsing connection line")); +@@ -822,28 +871,34 @@ static void icedemo_input_remote(void) + } else if (strcmp(attr, "candidate")==0) { + char *sdpcand = attr+strlen(attr)+1; + int af, cnt; +- char foundation[32], transport[12], ipaddr[80], type[32]; ++ char foundation[32], transport[12], ipaddr[80], type[32], tcp_type[32]; + pj_str_t tmpaddr; + int comp_id, prio, port; + pj_ice_sess_cand *cand; + pj_status_t status; ++ pj_bool_t is_tcp = PJ_FALSE; + +- cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s", ++ cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s tcptype %s\n", + foundation, + &comp_id, + transport, + &prio, + ipaddr, + &port, +- type); +- if (cnt != 7) { ++ type, ++ tcp_type); ++ if (cnt != 7 && cnt != 8) { + PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line")); + goto on_error; + } + ++ if (strcmp(transport, "TCP") == 0) { ++ is_tcp = PJ_TRUE; ++ } ++ + cand = &icedemo.rem.cand[icedemo.rem.cand_cnt]; + pj_bzero(cand, sizeof(*cand)); +- ++ + if (strcmp(type, "host")==0) + cand->type = PJ_ICE_CAND_TYPE_HOST; + else if (strcmp(type, "srflx")==0) +@@ -851,15 +906,32 @@ static void icedemo_input_remote(void) + else if (strcmp(type, "relay")==0) + cand->type = PJ_ICE_CAND_TYPE_RELAYED; + else { +- PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", ++ PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", + type)); + goto on_error; + } + ++ if (is_tcp) { ++ if (strcmp(tcp_type, "active") == 0) ++ cand->transport = PJ_CAND_TCP_ACTIVE; ++ else if (strcmp(tcp_type, "passive") == 0) ++ cand->transport = PJ_CAND_TCP_PASSIVE; ++ else if (strcmp(tcp_type, "so") == 0) ++ cand->transport = PJ_CAND_TCP_SO; ++ else { ++ PJ_LOG(1, (THIS_FILE, ++ "Error: invalid transport type '%s'", ++ tcp_type)); ++ goto on_error; ++ } ++ } else { ++ cand->transport = PJ_CAND_UDP; ++ } ++ + cand->comp_id = (pj_uint8_t)comp_id; + pj_strdup2(icedemo.pool, &cand->foundation, foundation); + cand->prio = prio; +- ++ + if (strchr(ipaddr, ':')) + af = pj_AF_INET6(); + else +@@ -919,7 +991,7 @@ static void icedemo_input_remote(void) + pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port); + } + +- PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", ++ PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", + icedemo.rem.cand_cnt)); + return; + +@@ -953,7 +1025,7 @@ static void icedemo_start_nego(void) + + PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation..")); + +- status = pj_ice_strans_start_ice(icedemo.icest, ++ status = pj_ice_strans_start_ice(icedemo.icest, + pj_cstr(&rufrag, icedemo.rem.ufrag), + pj_cstr(&rpwd, icedemo.rem.pwd), + icedemo.rem.cand_cnt, +diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c +index c5885f305..e1e7c6f2f 100644 +--- a/pjsip/src/pjsip/sip_transport.c ++++ b/pjsip/src/pjsip/sip_transport.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -44,7 +44,7 @@ + static const char *addr_string(const pj_sockaddr_t *addr) + { + static char str[PJ_INET6_ADDRSTRLEN]; +- pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, ++ pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, + pj_sockaddr_get_addr(addr), + str, sizeof(str)); + return str; +@@ -110,9 +110,9 @@ enum timer_id { + static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata); + + /* This module has sole purpose to print transmit data to contigous buffer +- * before actually transmitted to the wire. ++ * before actually transmitted to the wire. + */ +-static pjsip_module mod_msg_print = ++static pjsip_module mod_msg_print = + { + NULL, NULL, /* prev and next */ + { "mod-msg-print", 13}, /* Name. */ +@@ -140,7 +140,7 @@ typedef struct transport + /* + * Transport manager. + */ +-struct pjsip_tpmgr ++struct pjsip_tpmgr + { + pj_hash_table_t *table; + pj_lock_t *lock; +@@ -204,76 +204,76 @@ static struct transport_names_t + const char *description; /* Longer description */ + unsigned flag; /* Flags */ + char name_buf[16]; /* For user's transport */ +-} transport_names[16] = ++} transport_names[16] = + { +- { +- PJSIP_TRANSPORT_UNSPECIFIED, +- 0, +- {"Unspecified", 11}, +- "Unspecified", ++ { ++ PJSIP_TRANSPORT_UNSPECIFIED, ++ 0, ++ {"Unspecified", 11}, ++ "Unspecified", + 0 + }, +- { +- PJSIP_TRANSPORT_UDP, +- 5060, +- {"UDP", 3}, +- "UDP transport", ++ { ++ PJSIP_TRANSPORT_UDP, ++ 5060, ++ {"UDP", 3}, ++ "UDP transport", + PJSIP_TRANSPORT_DATAGRAM + }, +- { +- PJSIP_TRANSPORT_TCP, +- 5060, +- {"TCP", 3}, +- "TCP transport", ++ { ++ PJSIP_TRANSPORT_TCP, ++ 5060, ++ {"TCP", 3}, ++ "TCP transport", + PJSIP_TRANSPORT_RELIABLE + }, +- { +- PJSIP_TRANSPORT_TLS, +- 5061, +- {"TLS", 3}, +- "TLS transport", ++ { ++ PJSIP_TRANSPORT_TLS, ++ 5061, ++ {"TLS", 3}, ++ "TLS transport", + PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE + }, +- { ++ { + PJSIP_TRANSPORT_DTLS, +- 5061, +- {"DTLS", 4}, +- "DTLS transport", ++ 5061, ++ {"DTLS", 4}, ++ "DTLS transport", + PJSIP_TRANSPORT_SECURE + }, +- { +- PJSIP_TRANSPORT_SCTP, +- 5060, +- {"SCTP", 4}, +- "SCTP transport", ++ { ++ PJSIP_TRANSPORT_SCTP, ++ 5060, ++ {"SCTP", 4}, ++ "SCTP transport", + PJSIP_TRANSPORT_RELIABLE + }, +- { +- PJSIP_TRANSPORT_LOOP, +- 15060, +- {"LOOP", 4}, +- "Loopback transport", ++ { ++ PJSIP_TRANSPORT_LOOP, ++ 15060, ++ {"LOOP", 4}, ++ "Loopback transport", + PJSIP_TRANSPORT_RELIABLE +- }, +- { +- PJSIP_TRANSPORT_LOOP_DGRAM, +- 15060, +- {"LOOP-DGRAM", 10}, +- "Loopback datagram transport", ++ }, ++ { ++ PJSIP_TRANSPORT_LOOP_DGRAM, ++ 15060, ++ {"LOOP-DGRAM", 10}, ++ "Loopback datagram transport", + PJSIP_TRANSPORT_DATAGRAM + }, +- { +- PJSIP_TRANSPORT_UDP6, +- 5060, +- {"UDP", 3}, +- "UDP IPv6 transport", ++ { ++ PJSIP_TRANSPORT_UDP6, ++ 5060, ++ {"UDP", 3}, ++ "UDP IPv6 transport", + PJSIP_TRANSPORT_DATAGRAM + }, +- { +- PJSIP_TRANSPORT_TCP6, +- 5060, +- {"TCP", 3}, +- "TCP IPv6 transport", ++ { ++ PJSIP_TRANSPORT_TCP6, ++ 5060, ++ {"TCP", 3}, ++ "TCP IPv6 transport", + PJSIP_TRANSPORT_RELIABLE + }, + { +@@ -322,12 +322,12 @@ PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag, + pjsip_transport_type_e parent = 0; + + PJ_ASSERT_RETURN(tp_flag && tp_name && def_port, PJ_EINVAL); +- PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < +- PJ_ARRAY_SIZE(transport_names[0].name_buf), ++ PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < ++ PJ_ARRAY_SIZE(transport_names[0].name_buf), + PJ_ENAMETOOLONG); + + for (i=1; iendpt, tdata->pool ); + return status; + } +- ++ + //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock); + status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock); + if (status != PJ_SUCCESS) { +@@ -574,7 +574,7 @@ static void tx_data_destroy(pjsip_tx_data *tdata) + PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) + { + pj_atomic_value_t ref_cnt; +- ++ + PJ_ASSERT_RETURN(tdata && tdata->ref_cnt, PJ_EINVAL); + + ref_cnt = pj_atomic_dec_and_get(tdata->ref_cnt); +@@ -607,7 +607,7 @@ PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata) + PJ_USE_EXCEPTION; + + PJ_TRY { +- tdata->buf.start = (char*) ++ tdata->buf.start = (char*) + pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN); + } + PJ_CATCH_ANY { +@@ -623,7 +623,7 @@ PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata) + if (!pjsip_tx_data_is_valid(tdata)) { + pj_ssize_t size; + +- size = pjsip_msg_print( tdata->msg, tdata->buf.start, ++ size = pjsip_msg_print( tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + if (size < 0) { + return PJSIP_EMSGTOOLONG; +@@ -652,7 +652,7 @@ static char *get_msg_info(pj_pool_t *pool, const char *obj_name, + PJ_ASSERT_RETURN(cseq != NULL, "INVALID MSG"); + + if (msg->type == PJSIP_REQUEST_MSG) { +- len = pj_ansi_snprintf(info_buf, sizeof(info_buf), ++ len = pj_ansi_snprintf(info_buf, sizeof(info_buf), + "Request msg %.*s/cseq=%d (%s)", + (int)msg->line.req.method.name.slen, + msg->line.req.method.name.ptr, +@@ -918,7 +918,7 @@ static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata) + /* + * Send a SIP message using the specified transport. + */ +-PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, ++PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + pjsip_tx_data *tdata, + const pj_sockaddr_t *addr, + int addr_len, +@@ -932,7 +932,7 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + /* Is it currently being sent? */ + if (tdata->is_pending) { + pj_assert(!"Invalid operation step!"); +- PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", ++ PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", + pjsip_tx_data_get_info(tdata))); + return PJSIP_EPENDINGTX; + } +@@ -953,7 +953,7 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + sizeof(tdata->tp_info.dst_name)); + tdata->tp_info.dst_port = pj_sockaddr_get_port(addr); + +- /* Distribute to modules. ++ /* Distribute to modules. + * When the message reach mod_msg_print, the contents of the message will + * be "printed" to contiguous buffer. + */ +@@ -976,7 +976,7 @@ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + tdata->is_pending = 1; + + /* Send to transport. */ +- status = (*tr->send_msg)(tr, tdata, addr, addr_len, (void*)tdata, ++ status = (*tr->send_msg)(tr, tdata, addr, addr_len, (void*)tdata, + &transport_send_callback); + + if (status != PJ_EPENDING) { +@@ -1035,7 +1035,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, + { + pjsip_transport *tr; + pj_status_t status; +- ++ + /* Acquire the transport */ + status = pjsip_tpmgr_acquire_transport(mgr, tp_type, addr, addr_len, + sel, &tr); +@@ -1064,13 +1064,13 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, + tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, data_len+1); + tdata->buf.end = tdata->buf.start + data_len + 1; + } +- ++ + /* Copy data, if any! (application may send zero len packet) */ + if (data_len) { + pj_memcpy(tdata->buf.start, raw_data, data_len); + } + tdata->buf.cur = tdata->buf.start + data_len; +- ++ + /* Save callback data. */ + tdata->token = token; + tdata->cb = cb; +@@ -1081,7 +1081,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, + /* Send to transport */ + status = tr->send_msg(tr, tdata, addr, addr_len, + tdata, &send_raw_callback); +- ++ + if (status != PJ_EPENDING) { + /* callback will not be called, so destroy tdata now. */ + pjsip_tx_data_dec_ref(tdata); +@@ -1115,7 +1115,7 @@ static void transport_idle_callback(pj_timer_heap_t *timer_heap, + if (pj_atomic_get(tp->ref_cnt) == 0) { + tp->is_destroying = PJ_TRUE; + PJ_LOG(4, (THIS_FILE, "Transport %s is being destroyed " +- "due to timeout in %s timer", tp->obj_name, ++ "due to timeout in %s timer", tp->obj_name, + (entry_id == IDLE_TIMER_ID)?"idle":"initial")); + if (entry_id == INITIAL_IDLE_TIMER_ID) { + if (tp->last_recv_len > 0 && tp->tpmgr->tp_drop_data_cb) { +@@ -1225,7 +1225,7 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) + !tp->is_destroying && pj_atomic_get(tp->ref_cnt) == 0) + { + pj_time_val delay; +- ++ + int timer_id = IDLE_TIMER_ID; + + /* If transport is in graceful shutdown, then this is the +@@ -1240,7 +1240,7 @@ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) + } else { + delay.sec = PJSIP_TRANSPORT_SERVER_IDLE_TIME; + if (tp->last_recv_ts.u64 == 0 && tp->initial_timeout) { +- PJ_LOG(4, (THIS_FILE, ++ PJ_LOG(4, (THIS_FILE, + "Starting transport %s initial timer", + tp->obj_name)); + timer_id = INITIAL_IDLE_TIMER_ID; +@@ -1309,7 +1309,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, + if (!tp_add){ + pj_lock_release(mgr->lock); + return PJ_ENOMEM; +- } ++ } + pj_list_init(tp_add); + pj_list_push_back(&mgr->tp_entry_freelist, tp_add); + } +@@ -1319,7 +1319,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, + pj_list_erase(tp_add); + + if (tp_ref) { +- /* There'a already a transport list from the hash table. Add the ++ /* There'a already a transport list from the hash table. Add the + * new transport to the list. + */ + pj_list_push_back(tp_ref, tp_add); +@@ -1442,7 +1442,7 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr, + + + /* +- * Start graceful shutdown procedure for this transport. ++ * Start graceful shutdown procedure for this transport. + */ + PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp) + { +@@ -1451,7 +1451,7 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp) + + + /* +- * Start shutdown procedure for this transport. ++ * Start shutdown procedure for this transport. + */ + PJ_DEF(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp, + pj_bool_t force) +@@ -1551,6 +1551,12 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, + /* Check that no same factory has been registered. */ + status = PJ_SUCCESS; + for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { ++ if (p->type == tpf->type && ++ !pj_sockaddr_cmp(&tpf->local_addr, &p->local_addr)) ++ { ++ status = PJSIP_ETYPEEXISTS; ++ break; ++ } + if (p == tpf) { + status = PJ_EEXISTS; + break; +@@ -1702,7 +1708,7 @@ static pj_status_t get_net_interface(pjsip_transport_type_e tp_type, + status = pj_getipinterface(af, dst, &itf_addr, PJ_TRUE, NULL); + } + +- if (status != PJ_SUCCESS) { ++ if (status != PJ_SUCCESS) { + status = pj_getipinterface(af, dst, &itf_addr, PJ_FALSE, NULL); + if (status != PJ_SUCCESS) { + /* If it fails, e.g: on WM6(http://support.microsoft.com/kb/129065), +@@ -1727,7 +1733,7 @@ static pj_status_t get_net_interface(pjsip_transport_type_e tp_type, + + /* + * Find out the appropriate local address info (IP address and port) to +- * advertise in Contact header based on the remote address to be ++ * advertise in Contact header based on the remote address to be + * contacted. The local address info would be the address name of the + * transport or listener which will be used to send the request. + * +@@ -2089,9 +2095,9 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + + tr->last_recv_len = rdata->pkt_info.len; + pj_get_timestamp(&tr->last_recv_ts); +- +- /* Must NULL terminate buffer. This is the requirement of the +- * parser etc. ++ ++ /* Must NULL terminate buffer. This is the requirement of the ++ * parser etc. + */ + current_pkt[remaining_len] = '\0'; + +@@ -2134,7 +2140,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + /* Initialize default fragment size. */ + msg_fragment_size = remaining_len; + +- /* Clear and init msg_info in rdata. ++ /* Clear and init msg_info in rdata. + * Endpoint might inspect the values there when we call the callback + * to report some errors. + */ +@@ -2164,7 +2170,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + } + } + +- msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, ++ msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, + &msg_fragment_size); + if (msg_status != PJ_SUCCESS) { + pj_status_t dd_status = msg_status; +@@ -2207,10 +2213,10 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + } + + if (msg_status == PJSIP_EPARTIALMSG) { +- if (rdata->tp_info.transport->idle_timer.id == ++ if (rdata->tp_info.transport->idle_timer.id == + INITIAL_IDLE_TIMER_ID) + { +- /* We are not getting the first valid SIP message ++ /* We are not getting the first valid SIP message + * as expected, close the transport. + */ + PJ_LOG(4, (THIS_FILE, "Unexpected data was received "\ +@@ -2238,7 +2244,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + current_pkt[msg_fragment_size] = '\0'; + + /* Parse the message. */ +- rdata->msg_info.msg = msg = ++ rdata->msg_info.msg = msg = + pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata); + + /* Restore null termination */ +@@ -2299,7 +2305,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + dd.len = msg_fragment_size; + dd.status = PJSIP_EINVALIDMSG; + (*mgr->tp_drop_data_cb)(&dd); +- ++ + if (dd.len > 0 && dd.len < msg_fragment_size) + msg_fragment_size = dd.len; + } +@@ -2309,11 +2315,11 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + + /* Perform basic header checking. */ + if (rdata->msg_info.cid == NULL || +- rdata->msg_info.cid->id.slen == 0 || +- rdata->msg_info.from == NULL || +- rdata->msg_info.to == NULL || +- rdata->msg_info.via == NULL || +- rdata->msg_info.cseq == NULL) ++ rdata->msg_info.cid->id.slen == 0 || ++ rdata->msg_info.from == NULL || ++ rdata->msg_info.to == NULL || ++ rdata->msg_info.via == NULL || ++ rdata->msg_info.cseq == NULL) + { + mgr->on_rx_msg(mgr->endpt, PJSIP_EMISSINGHDR, rdata); + +@@ -2325,7 +2331,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + dd.data = current_pkt; + dd.len = msg_fragment_size; + dd.status = PJSIP_EMISSINGHDR; +- (*mgr->tp_drop_data_cb)(&dd); ++ (*mgr->tp_drop_data_cb)(&dd); + } + goto finish_process_fragment; + } +@@ -2333,8 +2339,8 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + /* For request: */ + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { + /* always add received parameter to the via. */ +- pj_strdup2(rdata->tp_info.pool, +- &rdata->msg_info.via->recvd_param, ++ pj_strdup2(rdata->tp_info.pool, ++ &rdata->msg_info.via->recvd_param, + rdata->pkt_info.src_name); + + /* RFC 3581: +@@ -2358,7 +2364,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + dd.data = current_pkt; + dd.len = msg_fragment_size; + dd.status = PJSIP_EINVALIDSTATUS; +- (*mgr->tp_drop_data_cb)(&dd); ++ (*mgr->tp_drop_data_cb)(&dd); + } + goto finish_process_fragment; + } +@@ -2463,10 +2469,13 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, + * for the destination. + */ + if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT && +- sel->u.transport) ++ sel->u.transport) + { + pjsip_transport *seltp = sel->u.transport; + ++ pjsip_transport_type_e type_no_ipv6 = type % PJSIP_TRANSPORT_IPV6; ++ pjsip_transport_type_e key_type_no_ipv6 = seltp->key.type % ++ PJSIP_TRANSPORT_IPV6; + /* See if the transport is (not) suitable */ + if (seltp->key.type != type) { + pj_lock_release(mgr->lock); +@@ -2765,7 +2774,7 @@ PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr) + PJ_LOG(3, (THIS_FILE, " Dumping listeners:")); + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { +- PJ_LOG(3, (THIS_FILE, " %s %s:%.*s:%d", ++ PJ_LOG(3, (THIS_FILE, " %s %s:%.*s:%d", + factory->obj_name, + factory->type_name, + (int)factory->addr_name.host.slen, +@@ -2933,7 +2942,7 @@ PJ_DEF(pj_status_t) pjsip_transport_add_state_listener ( + } + + /** +- * Remove a listener from the specified transport for transport state ++ * Remove a listener from the specified transport for transport state + * notification. + */ + PJ_DEF(pj_status_t) pjsip_transport_remove_state_listener ( +diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c +index a29b5cdd6..74f6f527c 100644 +--- a/pjsip/src/pjsua-lib/pjsua_core.c ++++ b/pjsip/src/pjsua-lib/pjsua_core.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * +@@ -14,7 +14,7 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include + #include +@@ -40,7 +40,7 @@ PJ_DEF(struct pjsua_data*) pjsua_get_var(void) + + + /* Display error */ +-PJ_DEF(void) pjsua_perror( const char *sender, const char *title, ++PJ_DEF(void) pjsua_perror( const char *sender, const char *title, + pj_status_t status) + { + char errmsg[PJ_ERR_MSG_SIZE]; +@@ -58,7 +58,7 @@ static void init_data() + + for (i=0; imsg_logging = PJ_TRUE; + cfg->level = 5; + cfg->console_level = 4; +- cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | ++ cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE | + PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC | + PJ_LOG_HAS_INDENT; +@@ -285,13 +285,13 @@ PJ_DEF(void) pjsua_srtp_opt_dup( pj_pool_t *pool, pjsua_srtp_opt *dst, + pj_bool_t check_str) + { + pjsua_srtp_opt backup_dst; +- ++ + if (check_str) pj_memcpy(&backup_dst, dst, sizeof(*dst)); + pj_memcpy(dst, src, sizeof(*src)); + + if (pool) { + unsigned i; +- ++ + for (i = 0; i < src->crypto_count; i++) { + if (!check_str || + pj_stricmp(&backup_dst.crypto[i].key, &src->crypto[i].key)) +@@ -396,7 +396,7 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) + { + const pj_sys_info *si = pj_get_sys_info(); + pj_str_t dev_model = {"iPhone5", 7}; +- ++ + pj_bzero(cfg, sizeof(*cfg)); + + cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE; +@@ -462,15 +462,15 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) + "--end msg--", + rdata->msg_info.len, + pjsip_rx_data_get_info(rdata), +- rdata->tp_info.transport->type_name, +- pj_addr_str_print(&input_str, +- rdata->pkt_info.src_port, ++ rdata->tp_info.transport->type_name, ++ pj_addr_str_print(&input_str, ++ rdata->pkt_info.src_port, + addr, +- sizeof(addr), ++ sizeof(addr), + 1), + (int)rdata->msg_info.len, + rdata->msg_info.msg_buf)); +- ++ + /* Always return false, otherwise messages will not get processed! */ + return PJ_FALSE; + } +@@ -480,7 +480,7 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) + { + char addr[PJ_INET6_ADDRSTRLEN+10]; + pj_str_t input_str = pj_str(tdata->tp_info.dst_name); +- ++ + /* Important note: + * tp_info field is only valid after outgoing messages has passed + * transport layer. So don't try to access tp_info when the module +@@ -492,10 +492,10 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) + (int)(tdata->buf.cur - tdata->buf.start), + pjsip_tx_data_get_info(tdata), + tdata->tp_info.transport->type_name, +- pj_addr_str_print(&input_str, +- tdata->tp_info.dst_port, ++ pj_addr_str_print(&input_str, ++ tdata->tp_info.dst_port, + addr, +- sizeof(addr), ++ sizeof(addr), + 1), + (int)(tdata->buf.cur - tdata->buf.start), + tdata->buf.start)); +@@ -506,7 +506,7 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) + } + + /* The module instance. */ +-static pjsip_module pjsua_msg_logger = ++static pjsip_module pjsua_msg_logger = + { + NULL, NULL, /* prev, next. */ + { "mod-pjsua-log", 13 }, /* Name. */ +@@ -546,14 +546,14 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) + + /* Don't want to handle if shutdown is in progress */ + if (pjsua_var.thread_quit_flag) { +- pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, ++ pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, + PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL, + NULL, NULL); + return PJ_TRUE; + } + + /* Create basic response. */ +- status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL, ++ status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL, + &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status); +@@ -563,28 +563,28 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) + /* Add Allow header */ + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL); + if (cap_hdr) { +- pjsip_msg_add_hdr(tdata->msg, ++ pjsip_msg_add_hdr(tdata->msg, + (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add Accept header */ + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL); + if (cap_hdr) { +- pjsip_msg_add_hdr(tdata->msg, ++ pjsip_msg_add_hdr(tdata->msg, + (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add Supported header */ + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL); + if (cap_hdr) { +- pjsip_msg_add_hdr(tdata->msg, ++ pjsip_msg_add_hdr(tdata->msg, + (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add Allow-Events header from the evsub module */ + cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL); + if (cap_hdr) { +- pjsip_msg_add_hdr(tdata->msg, ++ pjsip_msg_add_hdr(tdata->msg, + (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + +@@ -628,7 +628,7 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) + + + /* The module instance. */ +-static pjsip_module pjsua_options_handler = ++static pjsip_module pjsua_options_handler = + { + NULL, NULL, /* prev, next. */ + { "mod-pjsua-options", 17 }, /* Name. */ +@@ -768,9 +768,9 @@ PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg) + if (pjsua_var.log_cfg.log_filename.slen) { + unsigned flags = PJ_O_WRONLY | PJ_O_CLOEXEC; + flags |= pjsua_var.log_cfg.log_file_flags; +- status = pj_file_open(pjsua_var.pool, ++ status = pj_file_open(pjsua_var.pool, + pjsua_var.log_cfg.log_filename.ptr, +- flags, ++ flags, + &pjsua_var.log_file); + + if (status != PJ_SUCCESS) { +@@ -974,9 +974,9 @@ PJ_DEF(pj_status_t) pjsua_create(void) + pj_shutdown(); + return status; + } +- ++ + /* Create mutex */ +- status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua", ++ status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua", + &pjsua_var.mutex); + if (status != PJ_SUCCESS) { + pj_log_pop_indent(); +@@ -988,8 +988,8 @@ PJ_DEF(pj_status_t) pjsua_create(void) + /* Must create SIP endpoint to initialize SIP parser. The parser + * is needed for example when application needs to call pjsua_verify_url(). + */ +- status = pjsip_endpt_create(&pjsua_var.cp.factory, +- pj_gethostname()->ptr, ++ status = pjsip_endpt_create(&pjsua_var.cp.factory, ++ pj_gethostname()->ptr, + &pjsua_var.endpt); + if (status != PJ_SUCCESS) { + pj_log_pop_indent(); +@@ -1004,7 +1004,7 @@ PJ_DEF(pj_status_t) pjsua_create(void) + pj_list_init(&pjsua_var.event_list); + + /* Create timer mutex */ +- status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua_timer", ++ status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua_timer", + &pjsua_var.timer_mutex); + if (status != PJ_SUCCESS) { + pj_log_pop_indent(); +@@ -1028,7 +1028,7 @@ static void upnp_cb(pj_status_t status) + #endif + + /* +- * Initialize pjsua with the specified settings. All the settings are ++ * Initialize pjsua with the specified settings. All the settings are + * optional, and the default values will be used when the config is not + * specified. + */ +@@ -1081,7 +1081,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + unsigned ii; + + /* Create DNS resolver */ +- status = pjsip_endpt_create_resolver(pjsua_var.endpt, ++ status = pjsip_endpt_create_resolver(pjsua_var.endpt, + &pjsua_var.resolver); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error creating resolver", status); +@@ -1089,7 +1089,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + } + + /* Configure nameserver for the DNS resolver */ +- status = pj_dns_resolver_set_ns(pjsua_var.resolver, ++ status = pj_dns_resolver_set_ns(pjsua_var.resolver, + ua_cfg->nameserver_count, + ua_cfg->nameserver, NULL); + if (status != PJ_SUCCESS) { +@@ -1111,7 +1111,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + ua_cfg->nameserver[ii].ptr)); + } + #else +- PJ_LOG(2,(THIS_FILE, ++ PJ_LOG(2,(THIS_FILE, + "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)")); + #endif + } +@@ -1146,7 +1146,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + + /* Initialize and register PJSUA application module. */ + { +- const pjsip_module mod_initializer = ++ const pjsip_module mod_initializer = + { + NULL, NULL, /* prev, next. */ + { "mod-pjsua", 9 }, /* Name. */ +@@ -1201,7 +1201,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + + pj_list_push_back(&pjsua_var.outbound_proxy, r); + } +- ++ + + /* Initialize PJSUA call subsystem: */ + status = pjsua_call_subsys_init(ua_cfg); +@@ -1211,11 +1211,11 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + /* Convert deprecated STUN settings */ + if (pjsua_var.ua_cfg.stun_srv_cnt==0) { + if (pjsua_var.ua_cfg.stun_domain.slen) { +- pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = ++ pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = + pjsua_var.ua_cfg.stun_domain; + } + if (pjsua_var.ua_cfg.stun_host.slen) { +- pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = ++ pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] = + pjsua_var.ua_cfg.stun_host; + } + } +@@ -1311,7 +1311,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, + + for (ii=0; iiinfo.ptr)); + + pjsua_set_state(PJSUA_STATE_INIT); +@@ -1435,7 +1435,7 @@ static void stun_resolve_dec_ref(pjsua_stun_resolve *sess) + * is allowed to destroy the session, otherwise it may cause deadlock. + */ + if ((ref_cnt > 0) || +- (sess->blocking && (sess->waiter != pj_thread_this()))) ++ (sess->blocking && (sess->waiter != pj_thread_this()))) + { + return; + } +@@ -1465,7 +1465,7 @@ static void stun_resolve_complete(pjsua_stun_resolve *sess) + if (result.status == PJ_SUCCESS) { + char addr[PJ_INET6_ADDRSTRLEN+10]; + pj_sockaddr_print(&result.addr, addr, sizeof(addr), 3); +- PJ_LOG(4,(THIS_FILE, ++ PJ_LOG(4,(THIS_FILE, + "STUN resolution success, using %.*s, address is %s", + (int)sess->srv[sess->idx].slen, + sess->srv[sess->idx].ptr, +@@ -1488,7 +1488,7 @@ on_return: + * to report it's state. We use this as part of testing the + * STUN server. + */ +-static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, ++static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status) + { +@@ -1553,7 +1553,7 @@ static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock, + + } else + return PJ_TRUE; +- ++ + } + + /* This is an internal function to resolve and test current +@@ -1574,7 +1574,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) + pj_str_t hostpart; + pj_uint16_t port; + pj_stun_sock_cb stun_sock_cb; +- ++ + pj_assert(sess->idx < sess->count); + + if (pjsua_var.ua_cfg.stun_try_ipv6 && +@@ -1584,7 +1584,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) + /* Skip IPv4 STUN resolution if NAT64 is not disabled. */ + PJ_LOG(4,(THIS_FILE, "Skipping IPv4 resolution of STUN server " + "%s (%d of %d)", target, +- sess->idx+1, sess->count)); ++ sess->idx+1, sess->count)); + continue; + } + +@@ -1599,7 +1599,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) + PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target)); + continue; + } +- ++ + /* Use default port if not specified */ + if (port == 0) + port = PJ_STUN_PORT; +@@ -1615,12 +1615,12 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) + stun_sock_cb.on_status = &test_stun_on_status; + sess->async_wait = PJ_FALSE; + status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve", +- sess->af, &stun_sock_cb, ++ sess->af, PJ_STUN_TP_UDP, &stun_sock_cb, + NULL, sess, &sess->stun_sock); + if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(status, errmsg, sizeof(errmsg)); +- PJ_LOG(4,(THIS_FILE, ++ PJ_LOG(4,(THIS_FILE, + "Error creating STUN socket for %s: %s", + target, errmsg)); + +@@ -1632,7 +1632,7 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess) + if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(status, errmsg, sizeof(errmsg)); +- PJ_LOG(4,(THIS_FILE, ++ PJ_LOG(4,(THIS_FILE, + "Error starting STUN socket for %s: %s", + target, errmsg)); + +@@ -1672,7 +1672,7 @@ PJ_DEF(pj_status_t) pjsua_update_stun_servers(unsigned count, pj_str_t srv[], + pj_status_t status; + + PJ_ASSERT_RETURN(count && srv, PJ_EINVAL); +- ++ + PJSUA_LOCK(); + + pjsua_var.ua_cfg.stun_srv_cnt = count; +@@ -1683,7 +1683,7 @@ PJ_DEF(pj_status_t) pjsua_update_stun_servers(unsigned count, pj_str_t srv[], + pjsua_var.stun_status = PJ_EUNKNOWN; + + PJSUA_UNLOCK(); +- ++ + status = resolve_stun_server(wait, PJ_FALSE, 0); + if (wait == PJ_FALSE && status == PJ_EPENDING) + status = PJ_SUCCESS; +@@ -1743,7 +1743,7 @@ PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count, + */ + max_wait_ms = count * pjsua_var.stun_cfg.rto_msec * (1 << 7); + pj_get_timestamp(&start); +- ++ + while ((sess->status == PJ_EPENDING) && (!sess->destroy_flag)) { + /* If there is no worker thread or + * the function is called from the only worker thread, +@@ -1794,7 +1794,7 @@ PJ_DEF(pj_status_t) pjsua_cancel_stun_resolution( void *token, + result.status = PJ_ECANCELLED; + + sess->cb(&result); +- } ++ } + ++cancelled_count; + } + +@@ -1820,7 +1820,7 @@ static void internal_stun_resolve_cb(const pj_stun_resolve_result *result) + pjsua_detect_nat_type(); + } + } +- ++ + if (pjsua_var.ua_cfg.cb.on_stun_resolution_complete) + (*pjsua_var.ua_cfg.cb.on_stun_resolution_complete)(result); + } +@@ -1879,7 +1879,7 @@ pj_status_t resolve_stun_server(pj_bool_t wait, pj_bool_t retry_if_cur_error, + pjsua_var.stun_cfg.rto_msec * (1 << 7); + pj_get_timestamp(&start); + +- while (pjsua_var.stun_status == PJ_EPENDING) { ++ while (pjsua_var.stun_status == PJ_EPENDING) { + /* If there is no worker thread or + * the function is called from the only worker thread, + * we have to handle the events here. +@@ -1904,7 +1904,7 @@ pj_status_t resolve_stun_server(pj_bool_t wait, pj_bool_t retry_if_cur_error, + pjsua_var.stun_status != PJ_SUCCESS && + pjsua_var.ua_cfg.stun_ignore_failure) + { +- PJ_LOG(2,(THIS_FILE, ++ PJ_LOG(2,(THIS_FILE, + "Ignoring STUN resolution failure (by setting)")); + //pjsua_var.stun_status = PJ_SUCCESS; + return PJ_SUCCESS; +@@ -1932,7 +1932,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) + + /* Signal threads to quit: */ + pjsua_stop_worker_threads(); +- ++ + if (pjsua_var.endpt) { + unsigned max_wait; + +@@ -1973,7 +1973,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) + if (pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec > max_wait) + max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec; + } +- ++ + /* No waiting if RX is disabled */ + if (flags & PJSUA_DESTROY_NO_RX_MSG) { + max_wait = 0; +@@ -2027,7 +2027,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) + if (pjsua_var.acc[i].cfg.unreg_timeout > max_wait) + max_wait = pjsua_var.acc[i].cfg.unreg_timeout; + } +- ++ + /* No waiting if RX is disabled */ + if (flags & PJSUA_DESTROY_NO_RX_MSG) { + max_wait = 0; +@@ -2051,14 +2051,14 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) + /* Note variable 'i' is used below */ + + /* Wait for some time to allow unregistration and ICE/TURN +- * transports shutdown to complete: ++ * transports shutdown to complete: + */ + if (i < 20 && (flags & PJSUA_DESTROY_NO_RX_MSG) == 0) { + busy_sleep(1000 - i*50); + } + + PJ_LOG(4,(THIS_FILE, "Destroying...")); +- ++ + /* Terminate any pending STUN resolution */ + if (!pj_list_empty(&pjsua_var.stun_res)) { + pjsua_stun_resolve *sess = pjsua_var.stun_res.next; +@@ -2073,7 +2073,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) + for (i = 0; i < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata); i++) { + if (pjsua_var.tpdata[i].data.ptr) { + pjsip_transport_type_e tp_type; +- ++ + tp_type = pjsua_var.tpdata[i].type & ~PJSIP_TRANSPORT_IPV6; + if ((flags & PJSUA_DESTROY_NO_TX_MSG) && + tp_type == PJSIP_TRANSPORT_UDP && +@@ -2131,7 +2131,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) + pj_mutex_destroy(pjsua_var.mutex); + pjsua_var.mutex = NULL; + } +- ++ + if (pjsua_var.timer_mutex) { + pj_mutex_destroy(pjsua_var.timer_mutex); + pjsua_var.timer_mutex = NULL; +@@ -2202,7 +2202,7 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) + /** + * Application is recommended to call this function after all initialization + * is done, so that the library can do additional checking set up +- * additional ++ * additional + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +@@ -2259,7 +2259,7 @@ PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout) + return -status; + + return count; +- ++ + #endif + } + +@@ -2271,7 +2271,7 @@ PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size, + pj_size_t increment) + { + /* Pool factory is thread safe, no need to lock */ +- return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment, ++ return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment, + NULL); + } + +@@ -2314,7 +2314,7 @@ static const char *addr_string(const pj_sockaddr_t *addr) + { + static char str[128]; + str[0] = '\0'; +- pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, ++ pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, + pj_sockaddr_get_addr(addr), + str, sizeof(str)); + return str; +@@ -2378,11 +2378,11 @@ static pj_status_t create_sip_udp_sock(int af, + + /* Initialize bound address */ + if (cfg->bound_addr.slen) { +- status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr, ++ status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr, + (pj_uint16_t)port); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, +- "Unable to resolve transport bound address", ++ pjsua_perror(THIS_FILE, ++ "Unable to resolve transport bound address", + status); + return status; + } +@@ -2398,8 +2398,8 @@ static pj_status_t create_sip_udp_sock(int af, + } + + /* Apply QoS, if specified */ +- status = pj_sock_apply_qos2(sock, cfg->qos_type, +- &cfg->qos_params, ++ status = pj_sock_apply_qos2(sock, cfg->qos_type, ++ &cfg->qos_params, + 2, THIS_FILE, "SIP UDP socket"); + + /* Apply sockopt, if specified */ +@@ -2446,7 +2446,7 @@ static pj_status_t create_sip_udp_sock(int af, + status = PJ_SUCCESS; + if (pj_sockaddr_has_addr(p_pub_addr)) { + /* +- * Public address is already specified, no need to resolve the ++ * Public address is already specified, no need to resolve the + * address, only set the port. + */ + if (pj_sockaddr_get_port(p_pub_addr) == 0) +@@ -2466,14 +2466,14 @@ static pj_status_t create_sip_udp_sock(int af, + stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2; + stun_opt.af = pjsua_var.stun_srv.addr.sa_family; + stun_opt.srv1 = stun_opt.srv2 = stun_srv; +- stun_opt.port1 = stun_opt.port2 = ++ stun_opt.port1 = stun_opt.port2 = + pj_sockaddr_get_port(&pjsua_var.stun_srv); + status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt, + 1, &sock, &p_pub_addr->ipv4); + if (status != PJ_SUCCESS) { + /* Failed getting mapped address via STUN */ + pjsua_perror(THIS_FILE, "Error contacting STUN server", status); +- ++ + /* Return error if configured to not ignore STUN failure */ + if (!pjsua_var.ua_cfg.stun_ignore_failure) { + pj_sock_close(sock); +@@ -2573,20 +2573,20 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + } + + /* Initialize the public address from the config, if any */ +- pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr, ++ pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr, + NULL, (pj_uint16_t)cfg->port); + if (cfg->public_addr.slen) { + status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type), + &pub_addr, &cfg->public_addr); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, +- "Unable to resolve transport public address", ++ pjsua_perror(THIS_FILE, ++ "Unable to resolve transport public address", + status); + goto on_return; + } + } + +- /* Create the socket and possibly resolve the address with STUN ++ /* Create the socket and possibly resolve the address with STUN + * (only when public address is not specified). + */ + status = create_sip_udp_sock(pjsip_transport_type_get_af(type), +@@ -2602,7 +2602,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock, + &addr_name, 1, &tp); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", ++ pjsua_perror(THIS_FILE, "Error creating SIP UDP transport", + status); + pj_sock_close(sock); + goto on_return; +@@ -2642,12 +2642,12 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + pj_sockaddr_set_port(&tcp_cfg.bind_addr, (pj_uint16_t)cfg->port); + + if (cfg->bound_addr.slen) { +- status = pj_sockaddr_set_str_addr(tcp_cfg.af, ++ status = pj_sockaddr_set_str_addr(tcp_cfg.af, + &tcp_cfg.bind_addr, + &cfg->bound_addr); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, +- "Unable to resolve transport bound address", ++ pjsua_perror(THIS_FILE, ++ "Unable to resolve transport bound address", + status); + goto on_return; + } +@@ -2659,7 +2659,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + + /* Copy the QoS settings */ + tcp_cfg.qos_type = cfg->qos_type; +- pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params, ++ pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params, + sizeof(cfg->qos_params)); + + /* Copy the sockopt */ +@@ -2670,7 +2670,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp); + + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, "Error creating SIP TCP listener", ++ pjsua_perror(THIS_FILE, "Error creating SIP TCP listener", + status); + goto on_return; + } +@@ -2711,8 +2711,8 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + status = pj_sockaddr_set_str_addr(af, &local_addr, + &cfg->bound_addr); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, +- "Unable to resolve transport bound address", ++ pjsua_perror(THIS_FILE, ++ "Unable to resolve transport bound address", + status); + goto on_return; + } +@@ -2726,7 +2726,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, + status = pjsip_tls_transport_start2(pjsua_var.endpt, &cfg->tls_setting, + &local_addr, &a_name, 1, &tls); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", ++ pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", + status); + goto on_return; + } +@@ -2847,8 +2847,8 @@ PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[], + + PJSUA_LOCK(); + +- for (i=0, count=0; i=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), ++ PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_EINVAL); + + /* Make sure that transport exists */ +@@ -2892,7 +2892,7 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id, + PJSUA_UNLOCK(); + return PJ_EINVALIDOP; + } +- ++ + info->id = id; + info->type = (pjsip_transport_type_e) tp->key.type; + info->type_name = pj_str(tp->type_name); +@@ -2915,7 +2915,7 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id, + PJSUA_UNLOCK(); + return PJ_EINVALIDOP; + } +- ++ + info->id = id; + info->type = t->type; + info->type_name = pj_str(factory->type_name); +@@ -2948,7 +2948,7 @@ PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id, + pj_bool_t enabled) + { + /* Make sure id is in range. */ +- PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), ++ PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_EINVAL); + + /* Make sure that transport exists */ +@@ -2974,7 +2974,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, + pjsip_transport_type_e tp_type; + + /* Make sure id is in range. */ +- PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), ++ PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_EINVAL); + + /* Make sure that transport exists */ +@@ -2989,7 +2989,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, + */ + PJ_LOG(1, (THIS_FILE, "pjsua_transport_close(force=PJ_TRUE) is " + "deprecated.")); +- ++ + /* To minimize the effect to users, we shouldn't hard-deprecate this + * and let it continue as if force is false. + */ +@@ -3019,7 +3019,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id, + case PJSIP_TRANSPORT_TLS: + case PJSIP_TRANSPORT_TCP: + /* This will close the TCP listener, but existing TCP/TLS +- * connections (if any) will still linger ++ * connections (if any) will still linger + */ + status = (*pjsua_var.tpdata[id].data.factory->destroy) + (pjsua_var.tpdata[id].data.factory); +@@ -3048,31 +3048,31 @@ PJ_DEF(pj_status_t) pjsua_transport_lis_start(pjsua_transport_id id, + pjsip_transport_type_e tp_type; + + /* Make sure id is in range. */ +- PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), ++ PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), + PJ_EINVAL); + + /* Make sure that transport exists */ + PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL); + + tp_type = pjsua_var.tpdata[id].type & ~PJSIP_TRANSPORT_IPV6; +- ++ + if ((tp_type == PJSIP_TRANSPORT_TLS) || (tp_type == PJSIP_TRANSPORT_TCP)) { + pj_sockaddr bind_addr; + pjsip_host_port addr_name; + pjsip_tpfactory *factory = pjsua_var.tpdata[id].data.factory; +- ++ + int af = pjsip_transport_type_get_af(factory->type); + + if (cfg->port) + pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)cfg->port); + + if (cfg->bound_addr.slen) { +- status = pj_sockaddr_set_str_addr(af, ++ status = pj_sockaddr_set_str_addr(af, + &bind_addr, + &cfg->bound_addr); + if (status != PJ_SUCCESS) { +- pjsua_perror(THIS_FILE, +- "Unable to resolve transport bound address", ++ pjsua_perror(THIS_FILE, ++ "Unable to resolve transport bound address", + status); + return status; + } +@@ -3090,9 +3090,9 @@ PJ_DEF(pj_status_t) pjsua_transport_lis_start(pjsua_transport_id id, + #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 + else { + status = pjsip_tls_transport_lis_start(factory, &bind_addr, +- &addr_name); ++ &addr_name); + } +-#endif ++#endif + } else if (tp_type == PJSIP_TRANSPORT_UDP) { + status = PJ_SUCCESS; + } else { +@@ -3113,13 +3113,13 @@ void pjsua_process_msg_data(pjsip_tx_data *tdata, + const pjsip_hdr *hdr; + + /* Always add User-Agent */ +- if (pjsua_var.ua_cfg.user_agent.slen && +- tdata->msg->type == PJSIP_REQUEST_MSG) ++ if (pjsua_var.ua_cfg.user_agent.slen && ++ tdata->msg->type == PJSIP_REQUEST_MSG) + { + const pj_str_t STR_USER_AGENT = { "User-Agent", 10 }; + pjsip_hdr *h; +- h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, +- &STR_USER_AGENT, ++ h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, ++ &STR_USER_AGENT, + &pjsua_var.ua_cfg.user_agent); + pjsip_msg_add_hdr(tdata->msg, h); + } +@@ -3141,7 +3141,7 @@ void pjsua_process_msg_data(pjsip_tx_data *tdata, + + if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) { + pjsip_media_type ctype; +- pjsip_msg_body *body; ++ pjsip_msg_body *body; + + pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype); + body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype, +@@ -3218,9 +3218,9 @@ void pjsua_parse_media_type( pj_pool_t *pool, + + pos = pj_strchr(&tmp, '/'); + if (pos) { +- media_type->type.ptr = tmp.ptr; ++ media_type->type.ptr = tmp.ptr; + media_type->type.slen = (pos-tmp.ptr); +- media_type->subtype.ptr = pos+1; ++ media_type->subtype.ptr = pos+1; + media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1; + } else { + media_type->type = tmp; +@@ -3242,7 +3242,7 @@ void pjsua_init_tpselector(pjsua_acc_id acc_id, + pjsua_transport_data *tpdata; + unsigned flag; + +- PJ_ASSERT_RETURN(acc->cfg.transport_id >= 0 && ++ PJ_ASSERT_RETURN(acc->cfg.transport_id >= 0 && + acc->cfg.transport_id < + (int)PJ_ARRAY_SIZE(pjsua_var.tpdata), ); + tpdata = &pjsua_var.tpdata[acc->cfg.transport_id]; +@@ -3273,7 +3273,7 @@ PJ_DEF(void) pjsua_ip_change_param_default(pjsua_ip_change_param *param) + + + /* Callback upon NAT detection completion */ +-static void nat_detect_cb(void *user_data, ++static void nat_detect_cb(void *user_data, + const pj_stun_nat_detect_result *res) + { + PJ_UNUSED_ARG(user_data); +@@ -3312,8 +3312,8 @@ PJ_DEF(pj_status_t) pjsua_detect_nat_type() + return PJNATH_ESTUNINSERVER; + } + +- status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv, +- &pjsua_var.stun_cfg, ++ status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv, ++ &pjsua_var.stun_cfg, + NULL, &nat_detect_cb); + + if (status != PJ_SUCCESS) { +@@ -3391,7 +3391,7 @@ PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url) + } + + /* +- * Schedule a timer entry. ++ * Schedule a timer entry. + */ + #if PJ_TIMER_DEBUG + PJ_DEF(pj_status_t) pjsua_schedule_timer_dbg( pj_timer_entry *entry, +@@ -3430,7 +3430,7 @@ static void timer_cb( pj_timer_heap_t *th, + } + + /* +- * Schedule a timer callback. ++ * Schedule a timer callback. + */ + #if PJ_TIMER_DEBUG + PJ_DEF(pj_status_t) pjsua_schedule_timer2_dbg( void (*cb)(void *user_data), +@@ -3488,7 +3488,7 @@ PJ_DEF(void) pjsua_cancel_timer(pj_timer_entry *entry) + pjsip_endpt_cancel_timer(pjsua_var.endpt, entry); + } + +-/** ++/** + * Normalize route URI (check for ";lr" and append one if it doesn't + * exist and pjsua_config.force_lr is set. + */ +@@ -3507,16 +3507,16 @@ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri) + + uri_obj = pjsip_parse_uri(tmp_pool, tmp_uri.ptr, tmp_uri.slen, 0); + if (!uri_obj) { +- PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s", ++ PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s", + (int)uri->slen, uri->ptr)); + pj_pool_release(tmp_pool); + return PJSIP_EINVALIDURI; + } + +- if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) && ++ if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) && + !PJSIP_URI_SCHEME_IS_SIPS(uri_obj)) + { +- PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s", ++ PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s", + (int)uri->slen, uri->ptr)); + pj_pool_release(tmp_pool); + return PJSIP_EINVALIDSCHEME; +@@ -3535,10 +3535,10 @@ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri) + + /* Print the URI */ + tmp_uri.ptr = (char*) pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE); +- tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj, ++ tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj, + tmp_uri.ptr, PJSIP_MAX_URL_SIZE); + if (tmp_uri.slen < 1) { +- PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s", ++ PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s", + (int)uri->slen, uri->ptr)); + pj_pool_release(tmp_pool); + return PJSIP_EURITOOLONG; +@@ -3623,7 +3623,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) + pjsip_tsx_layer_dump(detail); + pjsip_ua_dump(detail); + +-// Dumping complete call states may require a 'large' buffer ++// Dumping complete call states may require a 'large' buffer + // (about 3KB per call session, including RTCP XR). + #if 0 + /* Dump all invite sessions: */ +@@ -3638,7 +3638,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) + + for (i=0; itryToRun(startUri); ++ return true; + } + + void diff --git a/net-voip/jami-client-qt/files/unbundle-qwindowkit.patch b/net-voip/jami-client-qt/files/unbundle-qwindowkit.patch new file mode 100644 index 0000000..9a0d74d --- /dev/null +++ b/net-voip/jami-client-qt/files/unbundle-qwindowkit.patch @@ -0,0 +1,19 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 29bfde9a..f0fbe76f 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -100,13 +101,7 @@ if(NOT WIN32) + endif() + + # qwindowkit (frameless window) +-add_fetch_content( +- TARGET qwindowkit +- URL https://github.com/stdware/qwindowkit.git +- BRANCH 758b00cb6c2d924be3a1ea137ec366dc33a5132d +- PATCHES ${QWINDOWKIT_PATCHES} +- OPTIONS ${QWINDOWKIT_OPTIONS} +-) ++find_package(QWindowKit) + list(APPEND CLIENT_INCLUDE_DIRS ${QWindowKit_BINARY_DIR}/include) + list(APPEND CLIENT_LIBS QWindowKit::Quick) + diff --git a/net-voip/jami-client-qt/files/unbundle-qwindowkit2.patch b/net-voip/jami-client-qt/files/unbundle-qwindowkit2.patch new file mode 100644 index 0000000..d847dce --- /dev/null +++ b/net-voip/jami-client-qt/files/unbundle-qwindowkit2.patch @@ -0,0 +1,20 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c046b597..89c64b95 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -100,14 +100,7 @@ if(NOT WIN32) + endif() + + # qwindowkit (frameless window) +-add_fetch_content( +- TARGET qwindowkit +- URL https://github.com/stdware/qwindowkit.git +- BRANCH 758b00cb6c2d924be3a1ea137ec366dc33a5132d +- PATCHES ${QWINDOWKIT_PATCHES} +- OPTIONS ${QWINDOWKIT_OPTIONS} +-) +-list(APPEND CLIENT_INCLUDE_DIRS ${QWindowKit_BINARY_DIR}/include) ++find_package(QWindowKit) + list(APPEND CLIENT_LIBS QWindowKit::Quick) + + # If ENABLE_CRASHREPORTS is enabled, we will use crashpad_cmake for now. diff --git a/net-voip/jami-client-qt/jami-client-qt-20251003.0.ebuild b/net-voip/jami-client-qt/jami-client-qt-20251003.0.ebuild new file mode 100644 index 0000000..033749b --- /dev/null +++ b/net-voip/jami-client-qt/jami-client-qt-20251003.0.ebuild @@ -0,0 +1,86 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake flag-o-matic + +DESCRIPTION="Jami clent QT" +HOMEPAGE="https://git.jami.net/savoirfairelinux/jami-client-qt" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://git.jami.net/savoirfairelinux/jami-client-qt" + EGIT_COMMIT="2e71d00c0500ffe2241e9171f7423a52a0efa96e" + KEYWORDS="" +else + EGIT_REPO_URI="https://git.jami.net/savoirfairelinux/jami-client-qt" + KEYWORDS="~amd64 ~x86" + EGIT_COMMIT="stable/${PV}" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="doc" + +DEPEND=" + net-voip/jami-daemon + net-libs/libnma + x11-libs/libnotify + media-gfx/qrencode + dev-libs/libayatana-appindicator + dev-qt/qtbase + dev-qt/qtdeclarative + dev-qt/qtgraphicaleffects + dev-qt/qtmultimedia[qml(+)] + dev-qt/qtnetworkauth + dev-qt/qtsvg + dev-qt/qttools + dev-qt/qtwebengine[qml(+)] + dev-qt/qt5compat + dev-qt/qtpositioning + dev-qt/qtwebsockets[qml(+)] + dev-qt/qwindowkit + media-libs/zxing-cpp + media-libs/zint + app-text/htmltidy + app-text/hunspell +" + +BDEPEND="doc? ( app-text/doxygen )" + +RDEPEND=" + ${DEPEND} +" + +src_prepare() { + eapply "${FILESDIR}"/drop-qt-version-check.patch + eapply "${FILESDIR}"/qt-6.6.patch + eapply "${FILESDIR}"/fix-link.patch + eapply "${FILESDIR}"/missing-cmake-include.patch + eapply "${FILESDIR}"/unbundle-qwindowkit.patch + cmake_src_prepare +} + +src_configure() { + append-cxxflags -I/usr/include/jami + append-ldflags -ljami + + local mycmakeargs=( + #-DCMAKE_INSTALL_PREFIX=/usr + #-DLIBJAMI_INCLUDE_DIR=/usr/lib64 + -DCMAKE_BUILD_TYPE=None + -DENABLE_LIBWRAP=ON + -DJAMICORE_AS_SUBDIR=OFF + -DWITH_DAEMON_SUBMODULE=OFF + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + -Wno-dev + ) + cmake_src_configure +} + +src_install() { + cmake_src_install +} diff --git a/net-voip/jami-client-qt/jami-client-qt-9999.ebuild b/net-voip/jami-client-qt/jami-client-qt-9999.ebuild new file mode 100644 index 0000000..3da036a --- /dev/null +++ b/net-voip/jami-client-qt/jami-client-qt-9999.ebuild @@ -0,0 +1,84 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit cmake flag-o-matic + +DESCRIPTION="Jami clent QT" +HOMEPAGE="https://git.jami.net/savoirfairelinux/jami-client-qt" + +if [[ "${PV}" == 9999* ]]; then + inherit git-r3 + EGIT_REPO_URI="https://github.com/savoirfairelinux/${PN}" + EGIT_COMMIT="2e71d00c0500ffe2241e9171f7423a52a0efa96e" +else + SRC_URI="https://github.com/savoirfairelinux/${PN}/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz" + KEYWORDS="~amd64 ~x86" +fi + +LICENSE="GPL-3" + +SLOT="0" + +IUSE="doc" + +DEPEND=" + net-voip/jami-daemon + net-libs/libnma + x11-libs/libnotify + media-gfx/qrencode + dev-libs/libayatana-appindicator + dev-qt/qtbase + dev-qt/qtdeclarative + dev-qt/qtgraphicaleffects + dev-qt/qtmultimedia[qml(+)] + dev-qt/qtnetworkauth + dev-qt/qtsvg + dev-qt/qttools + dev-qt/qtwebengine[qml(+)] + dev-qt/qt5compat + dev-qt/qtpositioning + dev-qt/qtwebsockets[qml(+)] + dev-qt/qwindowkit + media-libs/zxing-cpp + media-libs/zint + app-text/htmltidy + app-text/hunspell +" + +BDEPEND="doc? ( app-text/doxygen )" + +RDEPEND=" + ${DEPEND} +" + +src_prepare() { + eapply "${FILESDIR}"/drop-qt-version-check.patch + eapply "${FILESDIR}"/qt-6.6.patch + eapply "${FILESDIR}"/fix-link.patch + eapply "${FILESDIR}"/missing-cmake-include.patch + eapply "${FILESDIR}"/unbundle-qwindowkit.patch + cmake_src_prepare +} + +src_configure() { + append-cxxflags -I/usr/include/jami + append-ldflags -ljami + + local mycmakeargs=( + #-DCMAKE_INSTALL_PREFIX=/usr + #-DLIBJAMI_INCLUDE_DIR=/usr/lib64 + -DCMAKE_BUILD_TYPE=None + -DENABLE_LIBWRAP=ON + -DJAMICORE_AS_SUBDIR=OFF + -DWITH_DAEMON_SUBMODULE=OFF + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + -Wno-dev + ) + cmake_src_configure +} + +src_install() { + cmake_src_install +} diff --git a/net-voip/jami-daemon/Manifest b/net-voip/jami-daemon/Manifest new file mode 100644 index 0000000..e5c68d7 --- /dev/null +++ b/net-voip/jami-daemon/Manifest @@ -0,0 +1,7 @@ +AUX cmake.patch 572 BLAKE2B 7b35c9a654bdbcacb7c8742d6a82601ce5807c7efa08f9b05c81d5af6a439f4e7acb3bc2154dcb824701bd36cb52769001788646eeb7d0e38aa613057d95fdb0 SHA512 2092cd36a856bdb4a7a1645c637de371fe6a366cb40c2de255fbdb9388e52950400ad47bd6b6b7fa7282316561b04530883c26d99221d80b1597db6e4d39e536 +AUX ffmpeg-7.patch 2958 BLAKE2B 1eb36112b4611b606f0e6d7d5bb17920c4bfa064aed6720baebf77696675eb768e5b2e67650ab8adceaa53e89439009949920140915a85ead78c631d3161a3e6 SHA512 ddfc55f32664c505a3816c152a2de124000b34e9b7da650ee82fbdaab179976fe28a17a2add30c493494a4f7da384a456fc8a5541bdbfb41c7d590ca5190ceb8 +AUX ffmpeg-8.patch 4547 BLAKE2B edc96dc3b3837168bc6e56d8978d70ed5a3cc3a0de8fbfb42dc15d46d6056be6d586b67a123486a2dfc18154adade39f85c65637dc10ca2c9145b3768fc96d9f SHA512 6c87677936a5dcc6134ebd8a60a542b90573bddb5896731cde4f681cf06bff2fdcbc779b4f691a90a55d528c28621328862664a62a693e02805a6f0a3141c084 +AUX fmt-12.patch 1811 BLAKE2B 8654397ac8d140ec339df9029ae5e610a6061dda0944d3cd3890cc7ad9d47d65a6ae9d6744bf07f9b7ad223241fe58d372ecd24b6f5f2fd866689673467eaf81 SHA512 b645545120608e70f70ee664d43de03fa07ebc5b2af9a6515a40220aabfe057d4f69034b15781a21c5e8ff6f2fbb353b7dea69d840cb299f9dbf58eeaf082a40 +DIST jami-daemon 40700 BLAKE2B 7da599023a4639ade1808aaf525fc25cc7fdd74a3d9661b679fef4ef30c251b63d53bfed2d59d675e5f803471b68a41182122e29d5487f421f5f9109c1cd7a40 SHA512 690f8bf980899039aa89777673cc879fb4998acf063f8a6db4d62a7a3813ed693c2fe1f36171f27df2358701ffa0396e57a936cf0f305ddc99e2339dfbca37ac +EBUILD jami-daemon-20250929.ebuild 2608 BLAKE2B eee77be68b41ba2b6341df6ca2887812d6129f7ff8d4ac9cf7f2000f80e95dd8f8e59822154a5290506c96a433afb2cdb00ef36f9a9e1e4df16302c515c73765 SHA512 cd5d0f9bae1d02d0afffdfb94fa83902507eeffb29c94f0459a32df808cf49ff95802a722ef86e81a2fcea440c608af9ae2ee0c6aced13fab2f301135de81a6c +EBUILD jami-daemon-9999.ebuild 2608 BLAKE2B eee77be68b41ba2b6341df6ca2887812d6129f7ff8d4ac9cf7f2000f80e95dd8f8e59822154a5290506c96a433afb2cdb00ef36f9a9e1e4df16302c515c73765 SHA512 cd5d0f9bae1d02d0afffdfb94fa83902507eeffb29c94f0459a32df808cf49ff95802a722ef86e81a2fcea440c608af9ae2ee0c6aced13fab2f301135de81a6c diff --git a/net-voip/jami-daemon/files/cmake.patch b/net-voip/jami-daemon/files/cmake.patch new file mode 100644 index 0000000..9f57c86 --- /dev/null +++ b/net-voip/jami-daemon/files/cmake.patch @@ -0,0 +1,15 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index a4c01b3ab..c0b0a7973 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -849,7 +849,9 @@ else() + ${DBUS_FILES} + ${DBUS_ADAPTOR_FILES} + ) +- target_link_libraries(jamid ${PROJECT_NAME} PkgConfig::DBusCpp) ++ find_package(OpenSSL REQUIRED) ++ find_package(ZLIB REQUIRED) ++ target_link_libraries(jamid ${PROJECT_NAME} PkgConfig::DBusCpp OpenSSL::SSL OpenSSL::Crypto ZLIB::ZLIB) + install (TARGETS jamid DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}) + endif() + diff --git a/net-voip/jami-daemon/files/ffmpeg-7.patch b/net-voip/jami-daemon/files/ffmpeg-7.patch new file mode 100644 index 0000000..cac9024 --- /dev/null +++ b/net-voip/jami-daemon/files/ffmpeg-7.patch @@ -0,0 +1,63 @@ +diff --git a/src/media/audio/sound/dtmfgenerator.cpp b/src/media/audio/sound/dtmfgenerator.cpp +index c05435f66..5b9eca18a 100644 +--- a/src/media/audio/sound/dtmfgenerator.cpp ++++ b/src/media/audio/sound/dtmfgenerator.cpp +@@ -115,7 +115,7 @@ DTMFGenerator::fillToneBuffer(int index) + ptr->nb_samples = sampleRate_; + ptr->format = tone_.getFormat().sampleFormat; + ptr->sample_rate = sampleRate_; +- ptr->channel_layout = AV_CH_LAYOUT_MONO; ++ ptr->ch_layout.u.mask = AV_CH_LAYOUT_MONO; + av_channel_layout_from_mask(&ptr->ch_layout, AV_CH_LAYOUT_MONO); + av_frame_get_buffer(ptr.get(), 0); + tone_.genSin(ptr.get(), 0, ptr->nb_samples, tones_[index].higher, tones_[index].lower); +diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp +index ceaa036c6..0cbeae5be 100644 +--- a/src/media/media_decoder.cpp ++++ b/src/media/media_decoder.cpp +@@ -375,11 +375,11 @@ MediaDemuxer::Status + MediaDemuxer::decode() + { + if (inputParams_.format == "x11grab" || inputParams_.format == "dxgigrab") { +- auto ret = inputCtx_->iformat->read_header(inputCtx_); +- if (ret == AVERROR_EXTERNAL) { +- JAMI_ERR("Unable to read frame: %s\n", libav_utils::getError(ret).c_str()); +- return Status::ReadError; +- } ++ // auto ret = inputCtx_->iformat->read_header(inputCtx_); ++ // if (ret == AVERROR_EXTERNAL) { ++ // JAMI_ERR("Unable to read frame: %s\n", libav_utils::getError(ret).c_str()); ++ // return Status::ReadError; ++ // } + auto codecpar = inputCtx_->streams[0]->codecpar; + if (baseHeight_ != codecpar->height || baseWidth_ != codecpar->width) { + baseHeight_ = codecpar->height; +diff --git a/src/media/media_io_handle.h b/src/media/media_io_handle.h +index 7a64b85a7..a668e73c0 100644 +--- a/src/media/media_io_handle.h ++++ b/src/media/media_io_handle.h +@@ -32,7 +32,7 @@ struct AVIOContext; + #endif + + typedef int (*io_readcallback)(void* opaque, uint8_t* buf, int buf_size); +-typedef int (*io_writecallback)(void* opaque, uint8_t* buf, int buf_size); ++typedef int (*io_writecallback)(void* opaque, const uint8_t* buf, int buf_size); + typedef int64_t (*io_seekcallback)(void* opaque, int64_t offset, int whence); + + namespace jami { +diff --git a/src/media/socket_pair.cpp b/src/media/socket_pair.cpp +index 0589affd5..f0005f6b0 100644 +--- a/src/media/socket_pair.cpp ++++ b/src/media/socket_pair.cpp +@@ -382,8 +382,8 @@ SocketPair::createIOContext(const uint16_t mtu) + [](void* sp, uint8_t* buf, int len) { + return static_cast(sp)->readCallback(buf, len); + }, +- [](void* sp, uint8_t* buf, int len) { +- return static_cast(sp)->writeCallback(buf, len); ++ [](void* sp, const uint8_t* buf, int len) { ++ return static_cast(sp)->writeCallback((uint8_t*)buf, len); + }, + 0, + reinterpret_cast(this)); + diff --git a/net-voip/jami-daemon/files/ffmpeg-8.patch b/net-voip/jami-daemon/files/ffmpeg-8.patch new file mode 100644 index 0000000..d4cd512 --- /dev/null +++ b/net-voip/jami-daemon/files/ffmpeg-8.patch @@ -0,0 +1,110 @@ +diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp +index f999ffe42..eae622d48 100644 +--- a/src/media/media_encoder.cpp ++++ b/src/media/media_encoder.cpp +@@ -72,11 +72,7 @@ MediaEncoder::~MediaEncoder() + } + for (auto encoderCtx : encoders_) { + if (encoderCtx) { +-#ifndef _MSC_VER + avcodec_free_context(&encoderCtx); +-#else +- avcodec_close(encoderCtx); +-#endif + } + } + avformat_free_context(outputCtx_); +@@ -354,7 +350,7 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr + throw MediaEncoderException( + ("Unable to compute buffer size: " + libav_utils::getError(scaledFrameBufferSize_)) + .c_str()); +- else if (scaledFrameBufferSize_ <= AV_INPUT_BUFFER_MIN_SIZE) ++ else if (scaledFrameBufferSize_ <= 16384) + throw MediaEncoderException("buffer too small"); + + scaledFrameBuffer_.resize(scaledFrameBufferSize_); +@@ -448,10 +444,10 @@ MediaEncoder::encode(const std::shared_ptr& input, + + if (is_keyframe) { + avframe->pict_type = AV_PICTURE_TYPE_I; +- avframe->key_frame = 1; ++ avframe->flags |= AV_FRAME_FLAG_KEY; + } else { + avframe->pict_type = AV_PICTURE_TYPE_NONE; +- avframe->key_frame = 0; ++ avframe->flags &= ~AV_FRAME_FLAG_KEY; + } + + return encode(avframe, currentStreamIdx_); +@@ -690,7 +686,7 @@ MediaEncoder::extractProfileLevelID(const std::string& parameters, AVCodecContex + // From RFC3984: + // If no profile-level-id is present, the Baseline Profile without + // additional constraints at Level 1 MUST be implied. +- ctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; ++ ctx->profile = AV_PROFILE_H264_CONSTRAINED_BASELINE; + ctx->level = 0x0d; + // ctx->level = 0x0d; // => 13 aka 1.3 + if (parameters.empty()) +@@ -716,17 +712,17 @@ MediaEncoder::extractProfileLevelID(const std::string& parameters, AVCodecContex + const unsigned char profile_iop = ((result >> 8) & 0xff); // xx80xx -> 80 + ctx->level = result & 0xff; // xxxx0d -> 0d + switch (profile_idc) { +- case FF_PROFILE_H264_BASELINE: ++ case AV_PROFILE_H264_BASELINE: + // check constraint_set_1_flag + if ((profile_iop & 0x40) >> 6) +- ctx->profile |= FF_PROFILE_H264_CONSTRAINED; ++ ctx->profile |= AV_PROFILE_H264_CONSTRAINED; + break; +- case FF_PROFILE_H264_HIGH_10: +- case FF_PROFILE_H264_HIGH_422: +- case FF_PROFILE_H264_HIGH_444_PREDICTIVE: ++ case AV_PROFILE_H264_HIGH_10: ++ case AV_PROFILE_H264_HIGH_422: ++ case AV_PROFILE_H264_HIGH_444_PREDICTIVE: + // check constraint_set_3_flag + if ((profile_iop & 0x10) >> 4) +- ctx->profile |= FF_PROFILE_H264_INTRA; ++ ctx->profile |= AV_PROFILE_H264_INTRA; + break; + } + JAMI_DBG("Using profile %s (%x) and level %d", +@@ -837,7 +833,7 @@ MediaEncoder::initCodec(AVMediaType mediaType, AVCodecID avcodecId, uint64_t br) + encoderCtx->flags2 |= AV_CODEC_FLAG2_LOCAL_HEADER; + initH264(encoderCtx, br); + } else if (avcodecId == AV_CODEC_ID_HEVC) { +- encoderCtx->profile = FF_PROFILE_HEVC_MAIN; ++ encoderCtx->profile = AV_PROFILE_HEVC_MAIN; + forcePresetX2645(encoderCtx); + initH265(encoderCtx, br); + av_opt_set_int(encoderCtx, "b_ref_mode", 0, AV_OPT_SEARCH_CHILDREN); +@@ -1108,7 +1104,6 @@ MediaEncoder::stopEncoder() + } + } + AVCodecContext* encoderCtx = getCurrentVideoAVCtx(); +- avcodec_close(encoderCtx); + avcodec_free_context(&encoderCtx); + av_free(encoderCtx); + } +@@ -1212,7 +1207,7 @@ MediaEncoder::testH265Accel() + framerate.den = 1; + encoderCtx->time_base = av_inv_q(framerate); + encoderCtx->pix_fmt = accel->getFormat(); +- encoderCtx->profile = FF_PROFILE_HEVC_MAIN; ++ encoderCtx->profile = AV_PROFILE_HEVC_MAIN; + encoderCtx->opaque = accel.get(); + + auto br = SystemCodecInfo::DEFAULT_VIDEO_BITRATE; +@@ -1349,11 +1344,7 @@ MediaEncoder::resetStreams(int width, int height) + if (outputCtx_) { + for (auto encoderCtx : encoders_) { + if (encoderCtx) { +-#ifndef _MSC_VER + avcodec_free_context(&encoderCtx); +-#else +- avcodec_close(encoderCtx); +-#endif + } + } + encoders_.clear(); + diff --git a/net-voip/jami-daemon/files/fmt-12.patch b/net-voip/jami-daemon/files/fmt-12.patch new file mode 100644 index 0000000..929b8ca --- /dev/null +++ b/net-voip/jami-daemon/files/fmt-12.patch @@ -0,0 +1,32 @@ +diff -ru dhtnet.orig/src/upnp/upnp_context.cpp dhtnet/src/upnp/upnp_context.cpp +--- dhtnet.orig/src/upnp/upnp_context.cpp 2025-10-04 15:51:40.889482196 +0200 ++++ dhtnet/src/upnp/upnp_context.cpp 2025-10-04 16:09:31.538008243 +0200 +@@ -734,9 +734,12 @@ + } + if (toRenewLaterCount > 0) { + nextRenewalTime += MAPPING_RENEWAL_THROTTLING_DELAY; +- if (logger_) logger_->debug("{} mapping(s) didn't need to be renewed (next renewal scheduled for {:%Y-%m-%d %H:%M:%S})", ++ if (logger_) { ++ std::time_t t = sys_clock::to_time_t(nextRenewalTime); ++ logger_->debug("{} mapping(s) didn't need to be renewed (next renewal scheduled for {:%Y-%m-%d %H:%M:%S})", + toRenewLaterCount, +- fmt::localtime(sys_clock::to_time_t(nextRenewalTime))); ++ *std::localtime(&t)); ++ } + mappingRenewalTimer_.expires_at(nextRenewalTime); + mappingRenewalTimer_.async_wait([this](asio::error_code const& ec) { + if (ec != asio::error::operation_aborted) +@@ -783,8 +786,11 @@ + if (nextRenewalTime == mappingRenewalTimer_.expiry()) + return; + +- if (logger_) logger_->debug("Scheduling next port mapping renewal for {:%Y-%m-%d %H:%M:%S}", +- fmt::localtime(sys_clock::to_time_t(nextRenewalTime))); ++ if (logger_) { ++ std::time_t t = sys_clock::to_time_t(nextRenewalTime); ++ logger_->debug("Scheduling next port mapping renewal for {:%Y-%m-%d %H:%M:%S}", ++ *std::localtime(&t)); ++ } + mappingRenewalTimer_.expires_at(nextRenewalTime); + mappingRenewalTimer_.async_wait([this](asio::error_code const& ec) { + if (ec != asio::error::operation_aborted) diff --git a/net-voip/jami-daemon/jami-daemon-20250929.ebuild b/net-voip/jami-daemon/jami-daemon-20250929.ebuild new file mode 100644 index 0000000..97f52b5 --- /dev/null +++ b/net-voip/jami-daemon/jami-daemon-20250929.ebuild @@ -0,0 +1,111 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit meson + +if [[ ${PV} == *9999* ]]; then + inherit git-r3 + + EGIT_REPO_URI="https://git.jami.net/savoirfairelinux/jami-daemon" + SRC_URI="" + + IUSE="+alsa +dbus doc graph +gsm +hwaccel ipv6 jack -libav +libilbc +nat-pmp +opus pipewire portaudio pulseaudio +sdes +speex +speexdsp +upnp +vaapi vdpau +video +vpx +x264" + KEYWORDS="" +else + SRC_URI="https://git.jami.net/savoirfairelinux/jami-daemon" + EGIT_COMMIT="afe2446133eb3c9279e42b0d1dcfdd9a3c76a35f" + + IUSE="+alsa +dbus doc graph +gsm +hwaccel ipv6 jack -libav +libilbc +nat-pmp +opus pipewire portaudio pulseaudio +sdes +speex +speexdsp +upnp +vaapi vdpau +video +vpx +x264" + KEYWORDS="~amd64" +fi + +DESCRIPTION="Jami (formerly Ring) daemon" +HOMEPAGE="https://jami.net/" + +LICENSE="GPL-3" + +SLOT="0" + +RDEPEND=" + >=dev-cpp/yaml-cpp-0.5.3 + >=dev-libs/boost-1.61.0 + >=dev-libs/crypto++-5.6.5 + >=dev-libs/jsoncpp-1.7.2 + >=media-video/ffmpeg-3.4[gsm?,libilbc?,opus?,speex?,v4l,vaapi?,vdpau?,vpx?,x264?,zlib] + libilbc? ( media-libs/libilbc ) + speex? ( >=media-libs/speex-1.2.0 ) + speexdsp? ( >=media-libs/speexdsp-1.2_rc3 ) + >=net-libs/gnutls-3.4.14 + >=net-libs/opendht-1.10.1 + >=sys-libs/zlib-1.2.8 + media-libs/libva + dev-libs/libsecp256k1 + net-libs/restinio + net-libs/dhtnet + net-libs/http-parser + dev-libs/libgit2 + dev-cpp/sdbus-c++[tools(+)] + <=media-libs/webrtc-audio-processing-1.0.0 + dev-libs/msgpack + alsa? ( media-libs/alsa-lib ) + jack? ( virtual/jack ) + portaudio? ( >=media-libs/portaudio-19_pre20140130 ) + pulseaudio? ( media-libs/libpulse ) + dbus? ( dev-libs/dbus-c++ ) + sdes? ( >=dev-libs/libpcre-8.40 ) + video? ( virtual/libudev ) + nat-pmp? ( net-libs/libnatpmp ) + pipewire? ( media-video/pipewire ) +" + +DEPEND="${RDEPEND} + doc? ( + graph? ( app-doc/doxygen[dot] ) + !graph? ( app-doc/doxygen ) + )" + +REQUIRED_USE="dbus? ( sdes ) + graph? ( doc ) + hwaccel? ( video ) + vaapi? ( hwaccel ) + ?? ( dbus )" + +src_prepare() { + default + eapply "${FILESDIR}"/cmake.patch + eapply "${FILESDIR}"/ffmpeg-7.patch + eapply "${FILESDIR}"/ffmpeg-8.patch + #cmake_src_prepare + #eautoreconf +} + +#src_configure() { +# mkdir build +# cd build +# cmake .. -DBUILD_CONTRIB=OFF -DJAMI_DBUS=ON +#} + +src_configure() { + #local mycmakeargs=( + # -DJAMI_DBUS=ON + # -DBUILD_CONTRIB=OFF + # -DJAMI_VIDEO=ON + # -DENABLE_COVERAGE=OFF + # -DBUILD_TESTING=OFF + # -DBUILD_EXTRA_TOOLS=ON + #) + #cmake_src_configure + local emesonargs=( + -Dinterfaces=library,dbus + -Dopensl=disabled + -Dportaudio=disabled + ) + meson_src_configure +} + +src_install() { + #cmake_src_install + meson_src_install +} diff --git a/net-voip/jami-daemon/jami-daemon-9999.ebuild b/net-voip/jami-daemon/jami-daemon-9999.ebuild new file mode 100644 index 0000000..97f52b5 --- /dev/null +++ b/net-voip/jami-daemon/jami-daemon-9999.ebuild @@ -0,0 +1,111 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +inherit meson + +if [[ ${PV} == *9999* ]]; then + inherit git-r3 + + EGIT_REPO_URI="https://git.jami.net/savoirfairelinux/jami-daemon" + SRC_URI="" + + IUSE="+alsa +dbus doc graph +gsm +hwaccel ipv6 jack -libav +libilbc +nat-pmp +opus pipewire portaudio pulseaudio +sdes +speex +speexdsp +upnp +vaapi vdpau +video +vpx +x264" + KEYWORDS="" +else + SRC_URI="https://git.jami.net/savoirfairelinux/jami-daemon" + EGIT_COMMIT="afe2446133eb3c9279e42b0d1dcfdd9a3c76a35f" + + IUSE="+alsa +dbus doc graph +gsm +hwaccel ipv6 jack -libav +libilbc +nat-pmp +opus pipewire portaudio pulseaudio +sdes +speex +speexdsp +upnp +vaapi vdpau +video +vpx +x264" + KEYWORDS="~amd64" +fi + +DESCRIPTION="Jami (formerly Ring) daemon" +HOMEPAGE="https://jami.net/" + +LICENSE="GPL-3" + +SLOT="0" + +RDEPEND=" + >=dev-cpp/yaml-cpp-0.5.3 + >=dev-libs/boost-1.61.0 + >=dev-libs/crypto++-5.6.5 + >=dev-libs/jsoncpp-1.7.2 + >=media-video/ffmpeg-3.4[gsm?,libilbc?,opus?,speex?,v4l,vaapi?,vdpau?,vpx?,x264?,zlib] + libilbc? ( media-libs/libilbc ) + speex? ( >=media-libs/speex-1.2.0 ) + speexdsp? ( >=media-libs/speexdsp-1.2_rc3 ) + >=net-libs/gnutls-3.4.14 + >=net-libs/opendht-1.10.1 + >=sys-libs/zlib-1.2.8 + media-libs/libva + dev-libs/libsecp256k1 + net-libs/restinio + net-libs/dhtnet + net-libs/http-parser + dev-libs/libgit2 + dev-cpp/sdbus-c++[tools(+)] + <=media-libs/webrtc-audio-processing-1.0.0 + dev-libs/msgpack + alsa? ( media-libs/alsa-lib ) + jack? ( virtual/jack ) + portaudio? ( >=media-libs/portaudio-19_pre20140130 ) + pulseaudio? ( media-libs/libpulse ) + dbus? ( dev-libs/dbus-c++ ) + sdes? ( >=dev-libs/libpcre-8.40 ) + video? ( virtual/libudev ) + nat-pmp? ( net-libs/libnatpmp ) + pipewire? ( media-video/pipewire ) +" + +DEPEND="${RDEPEND} + doc? ( + graph? ( app-doc/doxygen[dot] ) + !graph? ( app-doc/doxygen ) + )" + +REQUIRED_USE="dbus? ( sdes ) + graph? ( doc ) + hwaccel? ( video ) + vaapi? ( hwaccel ) + ?? ( dbus )" + +src_prepare() { + default + eapply "${FILESDIR}"/cmake.patch + eapply "${FILESDIR}"/ffmpeg-7.patch + eapply "${FILESDIR}"/ffmpeg-8.patch + #cmake_src_prepare + #eautoreconf +} + +#src_configure() { +# mkdir build +# cd build +# cmake .. -DBUILD_CONTRIB=OFF -DJAMI_DBUS=ON +#} + +src_configure() { + #local mycmakeargs=( + # -DJAMI_DBUS=ON + # -DBUILD_CONTRIB=OFF + # -DJAMI_VIDEO=ON + # -DENABLE_COVERAGE=OFF + # -DBUILD_TESTING=OFF + # -DBUILD_EXTRA_TOOLS=ON + #) + #cmake_src_configure + local emesonargs=( + -Dinterfaces=library,dbus + -Dopensl=disabled + -Dportaudio=disabled + ) + meson_src_configure +} + +src_install() { + #cmake_src_install + meson_src_install +}