From 93f85f3776709563b4e38537c83c7cc86aec59cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Fri, 26 Jun 2020 14:56:01 -0400
Subject: [PATCH] pjproject: fix TCP sockets death detection

TCP_KEEPALIVE are not enough to detect that TCP connections are dead or not.
Some routers doesn't seems to support TCP_KEEPALIVE and moreover, sometimes
the settings is just not enough as described in this article:
https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/

RFC 5482 describe a new option for sockets: TCP_USER_TIMEOUT which must be used
with keep alives to detect closed connection in a better way.

In this patch we set TCP_USER_TIMEOUT to 30 seconds (like the keep alive). This is
a TCP level socket option used to specify the max time in ms that transmitted data
can be unacknowledged before a force close.

Change-Id: I217616b9b9e08adfa5a044a451fbbc97faf1939e
Gitlab: #243
---
 .../0011-fix-tcp-death-detection.patch        | 112 ++++++++++++++++++
 contrib/src/pjproject/package.json            |   5 +-
 contrib/src/pjproject/rules.mak               |   1 +
 3 files changed, 116 insertions(+), 2 deletions(-)
 create mode 100644 contrib/src/pjproject/0011-fix-tcp-death-detection.patch

diff --git a/contrib/src/pjproject/0011-fix-tcp-death-detection.patch b/contrib/src/pjproject/0011-fix-tcp-death-detection.patch
new file mode 100644
index 0000000000..3899d2d936
--- /dev/null
+++ b/contrib/src/pjproject/0011-fix-tcp-death-detection.patch
@@ -0,0 +1,112 @@
+ pjlib/include/pj/sock.h    | 7 +++++++
+ pjlib/src/pj/sock_bsd.c    | 9 +++++++++
+ pjlib/src/pj/sock_common.c | 5 +++++
+ pjlib/src/pj/sock_uwp.cpp  | 6 ++++++
+ pjlib/src/pj/symbols.c     | 1 +
+ 5 files changed, 28 insertions(+)
+
+diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
+index 095a38335..e1349c52c 100644
+--- a/pjlib/include/pj/sock.h
++++ b/pjlib/include/pj/sock.h
+@@ -317,6 +317,7 @@ 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.
+  */
+@@ -360,6 +361,9 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP;
+     /** 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);
+ 
+@@ -405,6 +409,9 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP;
+     /** 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
+ 
+diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
+index 0e9bfdbe9..e8bd94bee 100644
+--- a/pjlib/src/pj/sock_bsd.c
++++ b/pjlib/src/pj/sock_bsd.c
+@@ -162,6 +162,11 @@ const pj_uint16_t PJ_TCP_KEEPIDLE = TCP_KEEPIDLE;
+ # 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
+@@ -592,7 +597,11 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af,
+ 			       &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;
++
+ 	}
+ #if defined(PJ_SOCK_HAS_IPV6_V6ONLY) && PJ_SOCK_HAS_IPV6_V6ONLY != 0
+ 	if (af == PJ_AF_INET6) {
+diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c
+index 6beb28590..1a0a38aee 100644
+--- a/pjlib/src/pj/sock_common.c
++++ b/pjlib/src/pj/sock_common.c
+@@ -1351,6 +1351,11 @@ 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;
+diff --git a/pjlib/src/pj/sock_uwp.cpp b/pjlib/src/pj/sock_uwp.cpp
+index 34baebcee..4ba61cfd1 100644
+--- a/pjlib/src/pj/sock_uwp.cpp
++++ b/pjlib/src/pj/sock_uwp.cpp
+@@ -97,6 +97,12 @@ 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
+diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c
+index 966a9fc43..c71e49da4 100644
+--- a/pjlib/src/pj/symbols.c
++++ b/pjlib/src/pj/symbols.c
+@@ -262,6 +262,7 @@ 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/contrib/src/pjproject/package.json b/contrib/src/pjproject/package.json
index f32ec6ca80..915d302d3f 100644
--- a/contrib/src/pjproject/package.json
+++ b/contrib/src/pjproject/package.json
@@ -15,8 +15,9 @@
         "0009-add-config-site.patch",
         "0001-win-config.patch",
         "0002-win-vs-gnutls.patch",
-        "0003-win-vs2017-props.patch"
-    ],
+        "0003-win-vs2017-props.patch",
+        "0011-fix-tcp-death-detection.patch"
+],
     "project_paths": [
         "pjlib-util/build/pjlib_util.vcxproj",
         "pjmedia/build/pjmedia.vcxproj",
diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak
index 1a302e40c5..97d489d654 100644
--- a/contrib/src/pjproject/rules.mak
+++ b/contrib/src/pjproject/rules.mak
@@ -55,6 +55,7 @@ pjproject: pjproject-$(PJPROJECT_VERSION).tar.gz .sum-pjproject
 	$(APPLY) $(SRC)/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
 	$(APPLY) $(SRC)/pjproject/0009-add-config-site.patch
 	$(APPLY) $(SRC)/pjproject/0010-fix-pkgconfig.patch
+	$(APPLY) $(SRC)/pjproject/0011-fix-tcp-death-detection.patch
 ifdef HAVE_ANDROID
 	$(APPLY) $(SRC)/pjproject/0001-android.patch
 endif
-- 
GitLab