diff --git a/contrib/src/pjproject/0001-android.patch b/contrib/src/pjproject/0001-android.patch
index 8142a78668e730ca9251b6268ea310cac5730cbb..c127d58e9c82c903e4bbaba635eb17cec543efca 100644
--- a/contrib/src/pjproject/0001-android.patch
+++ b/contrib/src/pjproject/0001-android.patch
@@ -1,43 +1,3 @@
-From 34ca3353abf3e6bdaab4abb7d5b996421500f3e0 Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Fri, 28 Feb 2020 12:33:24 -0500
-Subject: [PATCH] android
-
----
- aconfigure                        |  3 ---
- aconfigure.ac                     |  3 ---
- pjlib/include/pj/config_site.h    | 11 +++++++++++
- pjlib/src/pj/os_timestamp_posix.c |  2 +-
- 4 files changed, 12 insertions(+), 7 deletions(-)
-
-diff --git a/aconfigure b/aconfigure
-index 7faf6e24c..de04d77a6 100755
---- a/aconfigure
-+++ b/aconfigure
-@@ -5996,9 +5996,6 @@ case $target in
- 	esac
- 	# UUID
- 	case $target in
--	  *android*)
--	  	ac_os_objs="$ac_os_objs guid_android.o"
--	  	;;
- 	  *)
- 		if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then
- 			ac_os_objs="$ac_os_objs guid_uuid.o"
-diff --git a/aconfigure.ac b/aconfigure.ac
-index 35d577081..a6a261cb4 100644
---- a/aconfigure.ac
-+++ b/aconfigure.ac
-@@ -514,9 +514,6 @@ case $target in
- 	esac
- 	# UUID
- 	case $target in
--	  *android*)
--	  	ac_os_objs="$ac_os_objs guid_android.o"
--	  	;;
- 	  *)
- 		if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then
- 			ac_os_objs="$ac_os_objs guid_uuid.o"
 diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h
 index 7b8ea2561..333dfc2f2 100644
 --- a/pjlib/include/pj/config_site.h
@@ -58,19 +18,6 @@ index 7b8ea2561..333dfc2f2 100644
 +#define PJMEDIA_HAS_G722_CODEC                  1
 +#define PJMEDIA_VID_DEV_INFO_FMT_CNT            128
 \ No newline at end of file
-diff --git a/pjlib/src/pj/os_timestamp_posix.c b/pjlib/src/pj/os_timestamp_posix.c
-index 0aed8c470..1ec156b0d 100644
---- a/pjlib/src/pj/os_timestamp_posix.c
-+++ b/pjlib/src/pj/os_timestamp_posix.c
-@@ -203,7 +203,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 <errno.h>
- #include <time.h>
 -- 
 2.25.1
 
diff --git a/contrib/src/pjproject/0001-rfc6544.patch b/contrib/src/pjproject/0001-rfc6544.patch
deleted file mode 100644
index 4bce2171817dbc368480bf1ab2fcce701e6ff088..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0001-rfc6544.patch
+++ /dev/null
@@ -1,4477 +0,0 @@
-Copyright (C) 2018-2021 Savoir-faire Linux Inc.
-
-ice: rfc6544 support
-
-This patch is an implementation proposal of the RFC 6544 into PJNATH.
-This allow PJNATH to support TCP ICE candidates and open a direct TCP
-connection between peers.
-
-+ BUG (semi-fixed with this patch): If an active_sock is busy due to
-a pending packet and receives a new packet to send, the final sent
-packet will be a mix between the pending packet and the new one.
-To avoid this, pj_ice_strans_sendto2 is now introduced.
-
-Written by
-Sébastien Blin <sebastien.blin@savoirfairelinux.com>
-on behalf of Savoir-faire Linux.
-
-Rebased for pjsip 2.10 by Peymane Marandi
-<paymon@savoirfairelinux.com>
-on behalf of Savoir-faire Linux.
-
----
- pjnath/include/pjnath/config.h          |    9 +
- pjnath/include/pjnath/ice_session.h     |  216 ++++-
- pjnath/include/pjnath/ice_strans.h      |   21 +
- pjnath/include/pjnath/stun_session.h    |   75 +-
- pjnath/include/pjnath/stun_sock.h       |   67 +-
- pjnath/include/pjnath/turn_sock.h       |   11 +
- pjnath/src/pjnath-test/concur_test.c    |    5 +-
- pjnath/src/pjnath-test/sess_auth.c      |   14 +-
- pjnath/src/pjnath-test/stun_sock_test.c |    7 +-
- pjnath/src/pjnath/ice_session.c         |  632 +++++++++++--
- pjnath/src/pjnath/ice_strans.c          |  749 +++++++++++++---
- pjnath/src/pjnath/nat_detect.c          |    7 +-
- pjnath/src/pjnath/stun_session.c        |   19 +-
- pjnath/src/pjnath/stun_sock.c           | 1081 +++++++++++++++++++----
- pjnath/src/pjnath/stun_transaction.c    |    3 +
- pjnath/src/pjnath/turn_session.c        |    3 +-
- pjnath/src/pjnath/turn_sock.c           |   26 +-
- pjnath/src/pjturn-client/client_main.c  |    2 +-
- pjnath/src/pjturn-srv/allocation.c      |    3 +-
- pjnath/src/pjturn-srv/server.c          |    2 +-
- pjsip-apps/src/samples/icedemo.c        |  116 ++-
- pjsip/src/pjsua-lib/pjsua_core.c        |    2 +-
- 22 files changed, 2606 insertions(+), 464 deletions(-)
-
-diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
-index 8a656c225..3e7c6ae3a 100644
---- a/pjnath/include/pjnath/config.h
-+++ b/pjnath/include/pjnath/config.h
-@@ -383,6 +383,15 @@
- #   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
- 
- /**
-  * For controlling agent if it uses regular nomination, specify the delay to
-diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
-index 53d7fd2d4..5362c72e9 100644
---- a/pjnath/include/pjnath/ice_session.h
-+++ b/pjnath/include/pjnath/ice_session.h
-@@ -163,6 +163,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;
-@@ -170,9 +216,6 @@ 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 
-@@ -205,32 +248,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;
--    } data;
--
--} pj_ice_msg_data;
--
--
- /**
-  * This structure describes an ICE candidate.
-  * ICE candidate is a transport address that is to be tested by ICE
-@@ -239,7 +256,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.
-@@ -318,7 +335,37 @@ 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;
-+    } data;
-+
-+} pj_ice_msg_data;
- 
- 
- /**
-@@ -333,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
-@@ -340,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
-@@ -544,6 +613,41 @@ typedef struct pj_ice_sess_cb
- 			      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 clist			The ICE connection list
-+     * @param check_id		The wanted check.
-+     */
-+    pj_status_t (*wait_tcp_connection)(pj_ice_sess *ice,
-+                                       pj_ice_sess_checklist *clist,
-+                                       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 clist			The ICE connection list
-+     * @param check_id		The wanted check.
-+     */
-+    pj_status_t (*reconnect_tcp_connection)(pj_ice_sess *ice,
-+                                            pj_ice_sess_checklist *clist,
-+                                            unsigned check_id);
-+
-+    /**
-+     * Close TCP socket
-+     *
-+     * @param ice			The ICE session.
-+     * @param clist			The ICE connection list
-+     * @param check_id		The wanted check.
-+     */
-+    pj_status_t (*close_tcp_connection)(pj_ice_sess *ice,
-+                                        pj_ice_sess_checklist *clist,
-+                                        unsigned check_id);
-+
- } pj_ice_sess_cb;
- 
- 
-@@ -673,6 +777,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         controlling_agent_passive_timeout;
-+
- } pj_ice_sess_options;
- 
- 
-@@ -705,6 +816,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.	    */
-@@ -928,6 +1040,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.
-  */
-@@ -941,7 +1054,8 @@ 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
-@@ -1088,6 +1202,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 dbad30766..1c20729e8 100644
---- a/pjnath/include/pjnath/ice_strans.h
-+++ b/pjnath/include/pjnath/ice_strans.h
-@@ -301,6 +301,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;
- 
- 
-@@ -316,6 +323,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
-@@ -395,6 +409,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 bee630ab4..3f2ecf739 100644
---- a/pjnath/include/pjnath/stun_session.h
-+++ b/pjnath/include/pjnath/stun_session.h
-@@ -174,6 +174,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
-@@ -307,6 +330,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;
- 
- 
-@@ -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
-@@ -752,6 +809,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 fe99292de..51f60cc03 100644
---- a/pjnath/include/pjnath/stun_sock.h
-+++ b/pjnath/include/pjnath/stun_sock.h
-@@ -24,10 +24,14 @@
-  * @file stun_sock.h
-  * @brief STUN aware socket transport
-  */
-+#include <pj/activesock.h>
- #include <pjnath/stun_config.h>
-+#include <pjnath/stun_session.h>
- #include <pjlib-util/resolver.h>
-+#include <pjlib-util/srv_resolver.h>
- #include <pj/ioqueue.h>
- #include <pj/lock.h>
-+#include <pj/pool.h>
- #include <pj/sock.h>
- #include <pj/sock_qos.h>
- 
-@@ -87,7 +91,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;
-@@ -197,6 +211,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.
-@@ -208,6 +227,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;
- 
- 
-@@ -343,6 +367,9 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg);
-  *			the operation of this transport.
-  * @param af		Address family of socket. Currently pj_AF_INET()
-  *			and pj_AF_INET6() are supported. 
-+ * @param conn_type     Connection type to the STUN server. Both TCP and UDP are
-+ *			supported.
-+ *
-  * @param name		Optional name to be given to this transport to
-  *			assist debugging.
-  * @param cb		Callback to receive events/data from the transport.
-@@ -357,6 +384,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,
-@@ -485,6 +513,43 @@ 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);
-+
-+#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_sock.h b/pjnath/include/pjnath/turn_sock.h
-index e4d306174..35388809f 100644
---- a/pjnath/include/pjnath/turn_sock.h
-+++ b/pjnath/include/pjnath/turn_sock.h
-@@ -623,6 +623,17 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock,
- 					       const pj_sockaddr_t *peer,
- 					       unsigned addr_len);
- 
-+/**
-+ *  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 c3013d2ab..7777c637c 100644
---- a/pjnath/src/pjnath-test/concur_test.c
-+++ b/pjnath/src/pjnath-test/concur_test.c
-@@ -184,8 +184,9 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess)
- 	char name[10];
- 	sprintf(name, "stun%02d", i);
- 	status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(),
--	                             &stun_cb, NULL, test_sess,
--	                             &stun_sock[i]);
-+				     PJ_STUN_TP_UDP,
-+				     &stun_cb, NULL, test_sess,
-+				     &stun_sock[i]);
- 	if (status != PJ_SUCCESS) {
- 	    PJ_PERROR(1,(THIS_FILE, status, "Error creating stun socket"));
- 	    return -10;
-diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c
-index 055eaad61..1d07bf299 100644
---- a/pjnath/src/pjnath-test/sess_auth.c
-+++ b/pjnath/src/pjnath-test/sess_auth.c
-@@ -248,7 +248,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;
-@@ -489,7 +490,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;
-@@ -575,8 +577,12 @@ static int run_client_test(const char *title,
-     }
- 
-     /* Send the request */
--    status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr,
--				      pj_sockaddr_get_len(&server->addr), tdata);
-+    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();
- 	return -270;
-diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c
-index 18a1bbd13..577659c87 100644
---- a/pjnath/src/pjnath-test/stun_sock_test.c
-+++ b/pjnath/src/pjnath-test/stun_sock_test.c
-@@ -254,8 +254,9 @@ 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, 
--				 client, &client->sock);
-+    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);
- 	pj_pool_release(pool);
-@@ -584,7 +585,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);
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index 3fecc3def..8917f647c 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -18,6 +18,7 @@
-  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
-  */
- #include <pjnath/ice_session.h>
-+#include <pjnath/stun_session.h>
- #include <pj/addr_resolv.h>
- #include <pj/array.h>
- #include <pj/assert.h>
-@@ -44,7 +45,10 @@ static const char *cand_type_names[] =
- static const char *check_state_name[] = 
- {
-     "Frozen",
-+    "Needs Retry",
-+    "Needs First Packet",
-     "Waiting",
-+    "Pending",
-     "In Progress",
-     "Succeeded",
-     "Failed"
-@@ -69,13 +73,15 @@ enum timer_type
- {
-     TIMER_NONE,			/**< Timer not active			*/
-     TIMER_COMPLETION_CALLBACK,	/**< Call on_ice_complete() callback    */
-+	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
- 
- };
- 
-@@ -123,6 +129,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;
- 
- 
-@@ -133,6 +141,7 @@ 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);
-@@ -293,7 +302,8 @@ static pj_status_t init_comp(pj_ice_sess *ice,
-     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;
- 
-@@ -326,6 +336,7 @@ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt)
-     opt->controlled_agent_want_nom_timeout = 
- 	ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT;
-     opt->trickle = PJ_ICE_SESS_TRICKLE_DISABLED;
-+	opt->controlling_agent_passive_timeout = ICE_CONTROLLING_PASSIVE_TIMEOUT;
- }
- 
- /*
-@@ -362,6 +373,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);
-@@ -508,6 +520,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; i<ice->comp_cnt; ++i) {
- 	if (ice->comp[i].stun_sess) {
- 	    pj_stun_session_destroy(ice->comp[i].stun_sess);
-@@ -733,7 +748,8 @@ 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)
- {
-     pj_ice_sess_cand *lcand;
-     pj_status_t status = PJ_SUCCESS;
-@@ -779,6 +795,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);
-@@ -1020,6 +1037,9 @@ static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check,
- 			    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],
-@@ -1171,6 +1191,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
-@@ -1240,10 +1269,14 @@ 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, 
--	      "Controlled agent timed-out in waiting for the controlling "
--	      "agent to send nominated check. Setting state to fail now.."));
-+	      "Controlled agent timed-out while waiting for nomination controlling agent"));
- 	on_ice_complete(ice, PJNATH_EICENOMTIMEOUT);
- 	break;
-     case TIMER_COMPLETION_CALLBACK:
-@@ -1273,6 +1306,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;
-@@ -1481,15 +1517,27 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice)
-      * See if all checks in the checklist have completed. If we do,
-      * then mark ICE processing as failed.
-      */
--    if (!ice->is_trickling) {
--	for (i=0; i<ice->clist.count; ++i) {
--	    pj_ice_sess_check *c = &ice->clist.checks[i];
--	    if (c->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) {
--		break;
--	    }
-+#if PJ_HAS_TCP	
-+	pj_bool_t hasTCP = PJ_FALSE;
-+#endif
-+    for (i=0; i<ice->clist.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 (!ice->is_trickling &&c->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) {
-+	    break;
- 	}
--	no_pending_check = (i == ice->clist.count);
-     }
-+	if (!ice->is_trickling) {
-+		no_pending_check = (i == ice->clist.count);
-+	}
- 
-     if (no_pending_check) {
- 	/* All checks have completed, but we don't have nominated pair.
-@@ -1506,8 +1554,8 @@ 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 {
-@@ -1541,11 +1589,48 @@ static pj_bool_t check_ice_complete(pj_ice_sess *ice)
- 	    /* Unreached */
- 
- 	} 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).
--	     */
--	    on_ice_complete(ice, PJNATH_EICEFAILED);
-+#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.controlling_agent_passive_timeout >= 0) 
-+			{
-+				pj_time_val delay;
-+
-+				delay.sec = 0;
-+				delay.msec = ice->opt.controlling_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.controlling_agent_passive_timeout));
-+			}
-+			return PJ_FALSE;
-+		} else {
-+#endif
-+			/* 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).
-+			*/
-+			on_ice_complete(ice, PJNATH_EICEFAILED);
-+#if PJ_HAS_TCP
-+		}
-+#endif
- 	    return PJ_TRUE;
- 
- 	} else {
-@@ -1737,6 +1822,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; i<ice->clist.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, &ice->clist, 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)
-@@ -1905,6 +2028,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
-@@ -1925,7 +2071,6 @@ static pj_status_t add_rcand_and_update_checklist(
- 	    }
- #endif
- 
--
- 	    /* Add the pair */
- 	    chk = &clist->checks[clist->count];
- 	    chk->lcand = lcand;
-@@ -2121,6 +2266,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;
- 
-@@ -2209,6 +2355,36 @@ 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, 
- 				 pj_ice_sess_checklist *clist,
-@@ -2219,19 +2395,17 @@ 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);
- 
-+    pj_log_push_indent();
-     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, 
-@@ -2282,32 +2456,71 @@ static pj_status_t perform_check(pj_ice_sess *ice,
- 				    &ice->tie_breaker);
- 
-     } else {
-+	if (nominate) {
-+	    check->nominated = PJ_TRUE;
-+	}
- 	pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, 
- 				    PJ_STUN_ATTR_ICE_CONTROLLED,
- 				    &ice->tie_breaker);
-     }
- 
-+#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, clist, 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, clist, check_id);
-+            if (ice->timer_connect.id != TIMER_NONE) {
-+                pj_assert(!"Not expected any timer active");
-+            } else {
-+                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);
-+            }
-+            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
- 
--    /* 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_TRUE, &rcand->addr, 
--				      pj_sockaddr_get_len(&rcand->addr),
--				      check->tdata);
--    if (status != 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", status);
--	pj_log_pop_indent();
--	return status;
-+	pjnath_perror(ice->obj_name, "Error sending STUN request (perform check)", status);
-     }
--
--    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, 
--	            PJ_SUCCESS);
-     pj_log_pop_indent();
--    return PJ_SUCCESS;
-+    return status;
- }
- 
- 
-@@ -2344,55 +2557,108 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
-     pj_log_push_indent();
- 
-     /* Send STUN Binding request for check with highest priority on
--     * Waiting state.
-+     * Retry state.
-      */
--    for (i=0; i<clist->count; ++i) {
--	pj_ice_sess_check *check = &clist->checks[i];
- 
--	if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
--	    status = perform_check(ice, clist, i, ice->is_nominating);
--	    if (status != PJ_SUCCESS) {
--		check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
--				status);
--		on_check_complete(ice, check);
-+    if (start_count == 0) {
-+	for (i = 0; i < clist->count; ++i) {
-+	    pj_ice_sess_check *check = &clist->checks[i];
-+	    // Reconnect closed TURN sockets
-+	    if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) {
-+		status = perform_check(ice, clist, i, ice->is_nominating);
-+		if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-+		    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
-+				    status);
-+		    on_check_complete(ice, check);
-+		}
-+		++start_count;
-+		break;
- 	    }
-+	}
-+    }
- 
--	    ++start_count;
--	    break;
-+    if (start_count == 0) {
-+	// 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 *check = &clist->checks[i];
-+	    if (check->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) {
-+		    status = perform_check(ice, clist, i, ice->is_nominating);
-+		    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-+			check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
-+					status);
-+			on_check_complete(ice, check);
-+		    }
-+		}
-+		++start_count;
-+		break;
-+	    }
-+	}
-+    }
-+
-+    /* Send STUN Binding request for check with highest priority on
-+     * Waiting state.
-+     */
-+
-+    if (start_count == 0) {
-+	for (i = 0; i < clist->count; ++i) {
-+	    pj_ice_sess_check *check = &clist->checks[i];
-+
-+	    if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
-+		status = perform_check(ice, clist, i, ice->is_nominating);
-+		if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-+		    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
-+				    status);
-+		    on_check_complete(ice, check);
-+		}
-+		++start_count;
-+		break;
-+	    }
- 	}
-     }
- 
-     /* If we don't have anything in Waiting state, perform check to
-      * highest priority pair that is in Frozen state.
-      */
--    if (start_count==0) {
--	for (i=0; i<clist->count; ++i) {
-+    if (start_count == 0) {
-+	for (i = 0; i < clist->count; ++i) {
- 	    pj_ice_sess_check *check = &clist->checks[i];
- 
- 	    if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
- 		status = perform_check(ice, clist, i, ice->is_nominating);
--		if (status != PJ_SUCCESS) {
--		    check_set_state(ice, check,
--		    		    PJ_ICE_SESS_CHECK_STATE_FAILED, status);
-+		if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-+		    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
- 		    on_check_complete(ice, check);
- 		}
-+		++start_count;
-+		break;
-+	    }
-+	}
-+    }
- 
-+    if (start_count == 0) {
-+	// If all sockets are pending, do nothing
-+	for (i = 0; i < clist->count; ++i) {
-+	    pj_ice_sess_check *check = &clist->checks[i];
-+	    if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) {
- 		++start_count;
- 		break;
- 	    }
- 	}
-     }
- 
--    /* Schedule next check for next candidate pair, unless there is no
--     * suitable candidate pair (all pairs have been checked or empty
--     * checklist).
--     */
-+    // Cannot start check because there's no suitable candidate pair.
-     if (start_count!=0) {
- 	pj_time_val timeout = {0, PJ_ICE_TA_VAL};
- 
- 	pj_time_val_normalize(&timeout);
- 	pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE,
--	                                  ice->grp_lock);
-+					  ice->grp_lock);
-     }
- 
-     pj_grp_lock_release(ice->grp_lock);
-@@ -2636,6 +2902,222 @@ 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,
-+							  &current_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,
-+					   &current_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 can try to reconnect a bit after and this until the check
-+	 * reached its timeout.
-+	 */
-+	check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY;
-+	check_set_state(ice, check,PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY,
-+			status);
-+	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.
-+		 */
-+		check_set_state(ice, check,PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY,
-+				status);
-+    } 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) {
-+	// 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,
-@@ -2941,7 +3423,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);
- 	if (status != PJ_SUCCESS) {
- 	    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, 
- 			    status);
-@@ -3003,11 +3487,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
-     /* Perform 7.1.2.2.2.  Updating Pair States.
-      * This may terminate ICE processing.
-      */
--    if (on_check_complete(ice, check)) {
--	/* ICE complete! */
--	pj_grp_lock_release(ice->grp_lock);
--	return;
--    }
-+	on_check_complete(ice, check);
- 
-     pj_grp_lock_release(ice->grp_lock);
- }
-@@ -3202,9 +3682,13 @@ 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.
-@@ -3323,12 +3807,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; i<ice->clist.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; i<ice->lcand_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;
- 	}
-     }
-diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index 72978a574..0fdd2c695 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -69,6 +69,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] =
-@@ -87,6 +88,14 @@ 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);
-@@ -104,6 +113,20 @@ 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,
-+					   pj_ice_sess_checklist *clist,
-+					   unsigned check_id);
-+
-+static pj_status_t ice_reconnect_tcp_connection(pj_ice_sess *ice,
-+						pj_ice_sess_checklist *clist,
-+						unsigned check_id);
-+
-+static pj_status_t ice_close_tcp_connection(pj_ice_sess *ice,
-+					    pj_ice_sess_checklist *clist,
-+					    unsigned check_id);
-+#endif
-+
- 
- /* STUN socket callbacks */
- /* Notification when incoming packet has been received. */
-@@ -183,6 +206,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
- {
-@@ -231,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. */
- };
- 
- 
-@@ -267,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);
-@@ -284,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;
-@@ -427,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 */
-@@ -434,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,
-@@ -475,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){
-@@ -484,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
-@@ -492,16 +540,16 @@ 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_stun_and_host(pj_ice_strans *ice_st,
- 				     pj_ice_strans_comp *comp,
- 				     unsigned idx,
-@@ -552,6 +600,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);
-@@ -560,8 +611,9 @@ 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,
--				 sock_cfg, data, &comp->stun[idx].sock);
-+				 stun_cfg->af, stun_cfg->conn_type,
-+				 &stun_sock_cb, sock_cfg, data,
-+				 &comp->stun[idx].sock);
-     if (status != PJ_SUCCESS)
- 	return status;
- 
-@@ -646,105 +698,153 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
- 	}
- 
- 	for (i = 0; i < stun_sock_info.alias_cnt &&
--		    cand_cnt < stun_cfg->max_host_cands; ++i)
-+		    cand_cnt < stun_cfg->max_host_cands &&
-+		    status == PJ_SUCCESS; ++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];
-+	    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);
-+            }
-+        }
-+    }
- 
--	    if (max_cand_cnt==0) {
--		PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
--		break;
--	    }
-+    return status;
-+}
- 
--	    /* 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;
--		    }
--		}
--	    }
-+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];
- 
--	    /* 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;
--	    }
- 
--	    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; j<comp->cand_cnt; j++) {
--		if (ice_cand_equals(cand, &comp->cand_list[j])) {
--		    cand_duplicate = PJ_TRUE;
--		    break;
--		}
--	    }
-+    if (*max_cand_cnt==0) {
-+	PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
-+	return !PJ_SUCCESS;
-+    }
- 
--	    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));
-+    /* 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)
-+            {
-+                return PJ_SUCCESS;
-+            }
-+        }
-+    }
- 
--		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);
-+    /* 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;
-+    }
- 
--	    /* 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);
--	    }
-+    cand = &comp->cand_list[comp->cand_cnt];
- 
--	    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));
--	}
-+    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; j<comp->cand_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.
-@@ -827,10 +927,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);
-@@ -850,7 +950,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;
- }
- 
-@@ -917,7 +1017,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;
-@@ -1125,7 +1225,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 {
-@@ -1134,7 +1234,7 @@ static void sess_init_update(pj_ice_strans *ice_st)
- 	    	    status = PJ_SUCCESS;
- 	    }
- 	}
--	
-+
- 	if (status != PJ_SUCCESS)
- 	    break;
-     }
-@@ -1278,6 +1378,11 @@ 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;
-+#endif
- 
-     /* Create! */
-     status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role,
-@@ -1353,7 +1458,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;
- 	}
-@@ -1718,7 +1824,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.
-@@ -1752,10 +1858,10 @@ static pj_status_t use_buffer( pj_ice_strans *ice_st,
-     status = alloc_send_buf(ice_st, 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;
-+    	return PJ_EPENDING;
-     }
- 
-     idx = ice_st->empty_idx;
-@@ -1767,12 +1873,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;
- 
-@@ -1825,6 +1931,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
-@@ -1843,8 +1951,29 @@ static pj_status_t send_data(pj_ice_strans *ice_st,
- 
-     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);
- 
-@@ -1906,6 +2035,11 @@ 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;
- 	}
- 
-@@ -1914,8 +2048,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)
-+    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? data_len: -status));
-@@ -1947,7 +2087,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
-@@ -2098,7 +2238,22 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
- 				      sizeof(lip), 3);
- 		    pj_sockaddr_print(&check->rcand->addr, rip,
- 				      sizeof(rip), 3);
--
-+#if PJ_HAS_TCP
-+		    int idx = -1;
-+		    for (int i=0; i<ice_st->cfg.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) {
- 			/* Activate channel binding for the remote address
- 			 * for more efficient data transfer using TURN.
-@@ -2192,6 +2347,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,
-@@ -2214,7 +2392,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);
-     	    }
-@@ -2225,9 +2403,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;
-@@ -2273,7 +2455,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];
-@@ -2287,6 +2469,219 @@ 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,
-+                                           pj_ice_sess_checklist *clist,
-+                                           unsigned check_id)
-+{
-+    pj_ice_sess_check      *check   = &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; i<ice_st->cfg.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,
-+                                                pj_ice_sess_checklist *clist,
-+                                                unsigned check_id)
-+{
-+    pj_ice_sess_check      *check   = &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; i<ice_st->cfg.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,
-+                                            pj_ice_sess_checklist *clist,
-+                                            unsigned check_id)
-+{
-+    pj_ice_sess_check      *check   = &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; i<ice_st->cfg.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;
-+}
-+#endif
-+
- /* Notifification when asynchronous send operation via STUN/TURN
-  * has completed.
-  */
-@@ -2295,7 +2690,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);
-     }
- 
-@@ -2453,7 +2849,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)
-@@ -2465,7 +2861,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),
-@@ -2482,7 +2878,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.
- 		 */
-@@ -2529,7 +2925,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);
- 		    }
- 		}
- 
-@@ -2554,7 +2951,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);
- 		}
-@@ -2610,6 +3007,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;
-@@ -2650,14 +3051,103 @@ 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 (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && pkt_len > 0) {
-+	    unsigned parsed = 0;
-+	    pj_status_t status;
- 
--	if (status != PJ_SUCCESS) {
--	    ice_st_perror(comp->ice_st,
--			  "Error processing packet from TURN relay",
--			  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);
- 	}
-     }
- 
-@@ -2797,7 +3287,8 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
- 					  &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",
-diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c
-index db0de10bc..808342bec 100644
---- a/pjnath/src/pjnath/nat_detect.c
-+++ b/pjnath/src/pjnath/nat_detect.c
-@@ -329,7 +329,8 @@ 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;
- 
-@@ -875,7 +876,9 @@ static pj_status_t send_test(nat_detect_session *sess,
- 
-     /* 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 f2b4f7058..20a9b1320 100644
---- a/pjnath/src/pjnath/stun_session.c
-+++ b/pjnath/src/pjnath/stun_session.c
-@@ -49,6 +49,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)
-@@ -524,7 +526,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;
-@@ -545,6 +548,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;
-@@ -1010,7 +1014,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
- 					     (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;
- 	}
- 
-@@ -1067,7 +1071,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
- 
- 	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;
- 	}
- 
-@@ -1538,3 +1542,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 5fe825cf5..6d2602552 100644
---- a/pjnath/src/pjnath/stun_sock.c
-+++ b/pjnath/src/pjnath/stun_sock.c
-@@ -40,6 +40,36 @@
- 
- 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
-+
-+// TODO (sblin) The incoming socks are a bit HACKY for now.
-+// Need a better approach
-+typedef struct outgoing_sock {
-+    pj_sock_t       fd;
-+    pj_activesock_t *sock;
-+    pj_sockaddr_t   *addr;
-+} 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	    */
-@@ -47,6 +77,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    */
- 
-@@ -59,6 +91,13 @@ 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
-+    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	    */
-@@ -68,6 +107,15 @@ struct pj_stun_sock
-     pj_grp_lock_t	*grp_lock;	/* Session group lock	    */
- };
- 
-+//////////////////////////////////////////////////////////////////////////////
-+
-+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 
-  */
-@@ -120,6 +168,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
- 
- 
-@@ -160,12 +226,183 @@ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg)
-     return cfg->max_pkt_size > 1 && cfg->async_cnt >= 1;
- }
- 
-+/*
-+ * Initialize.
-+ */
-+PJ_DEF(pj_status_t) pj_stun_sock_alloc(pj_stun_sock *stun_sock)
-+{
-+    pj_status_t status;
-+    pj_sockaddr bound_addr;
-+    pj_uint16_t max_bind_retry;
-+    int         sock_type;
-+
-+    pj_grp_lock_acquire(stun_sock->grp_lock);
-+
-+    if (stun_sock->conn_type == PJ_STUN_TP_UDP)
-+        sock_type = pj_SOCK_DGRAM();
-+    else
-+        sock_type = pj_SOCK_STREAM();
-+
-+    stun_sock->ka_interval = stun_sock->cfg.ka_interval;
-+    if (stun_sock->ka_interval == 0)
-+        stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
-+    /* Create socket and bind socket */
-+    status = pj_sock_socket(stun_sock->af, sock_type, 0, &stun_sock->sock_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(stun_sock->sock_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(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 < 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(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 < 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));
-+            }
-+        }
-+    }
-+
-+    /* Bind socket */
-+    max_bind_retry = MAX_BIND_RETRY;
-+    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, &stun_sock->cfg.bound_addr);
-+    }
-+
-+    status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr,
-+                                 stun_sock->cfg.port_range, max_bind_retry);
-+    if (status != PJ_SUCCESS) {
-+        pj_stun_sock_destroy(stun_sock);
-+        pj_grp_lock_release(stun_sock->grp_lock);
-+        return status;
-+    }
-+
-+    /* 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_sent     = &on_data_sent;
-+        activesock_cb.on_data_recvfrom = &on_data_recvfrom;
-+
-+#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) {
-+                pj_stun_sock_destroy(stun_sock);
-+                pj_grp_lock_release(stun_sock->grp_lock);
-+                return status;
-+            }
-+        } 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) {
-+            pj_stun_sock_destroy(stun_sock);
-+            pj_grp_lock_release(stun_sock->grp_lock);
-+            return status;
-+        }
-+
-+#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));
-+            pj_stun_sock_destroy(stun_sock);
-+            pj_grp_lock_release(stun_sock->grp_lock);
-+            return status;
-+        }
-+#else
-+        on_stun_sock_ready(stun_sock->active_sock, PJ_SUCCESS);
-+#endif
-+    }
-+
-+    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,
-@@ -174,22 +411,32 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
-     pj_pool_t *pool;
-     pj_stun_sock *stun_sock;
-     pj_stun_sock_cfg default_cfg;
--    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);
-+    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)
--	name = "stuntp%p";
-+    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);
-@@ -204,9 +451,16 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
-     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->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)
-@@ -226,140 +480,68 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
-     pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock,
- 			    &stun_sock_destructor);
- 
--    /* Create socket and bind socket */
--    status = pj_sock_socket(af, pj_SOCK_DGRAM(), 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,
--				NULL);
--    if (status != PJ_SUCCESS && !cfg->qos_ignore_error)
--	goto on_error;
-+    /* Create STUN session */
-+    {
-+	pj_stun_session_cb sess_cb;
- 
--    /* Apply socket buffer size */
--    if (cfg->so_rcvbuf_size > 0) {
--	unsigned sobuf_size = 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, 
--			   "Warning! Cannot set SO_RCVBUF as configured, "
--			   "now=%d, configured=%d",
--			   sobuf_size, 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;
--	status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(),
--					  PJ_TRUE, &sobuf_size);
-+	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,
-+					stun_sock->obj_name,
-+					&sess_cb, PJ_FALSE,
-+					stun_sock->grp_lock,
-+					&stun_sock->stun_sess,
-+					conn_type);
- 	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, 
--			   "Warning! Cannot set SO_SNDBUF as configured, "
--			   "now=%d, configured=%d",
--			   sobuf_size, cfg->so_sndbuf_size));
--	    } else {
--		PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d",
--			   sobuf_size));
--	    }
-+	    pj_stun_sock_destroy(stun_sock);
-+	    return status;
- 	}
-     }
- 
--    /* 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())
--    {
--	pj_sockaddr_cp(&bound_addr, &cfg->bound_addr);
--    }
--    status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr,
--				 cfg->port_range, max_bind_retry);
--    if (status != PJ_SUCCESS)
--	goto on_error;
-+    pj_stun_sock_alloc(stun_sock);
- 
--    /* Create more useful information string about this transport */
--#if 0
--    {
--	pj_sockaddr bound_addr;
--	int addr_len = sizeof(bound_addr);
-+    /* Done */
-+    *p_stun_sock = stun_sock;
-+    return PJ_SUCCESS;
-+}
-+
-+/*
-+ * 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;
- 
--	status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, 
--				     &addr_len);
--	if (status != PJ_SUCCESS)
--	    goto on_error;
-+    pj_grp_lock_acquire(stun_sock->grp_lock);
- 
--	stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10);
--	pj_sockaddr_print(&bound_addr, stun_sock->info, 
--			  PJ_INET6_ADDRSTRLEN, 3);
-+    /* 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;
-     }
--#endif
- 
--    /* 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 = cfg->async_cnt;
--	activesock_cfg.concurrency = 0;
--
--	/* Create the active socket */
--	pj_bzero(&activesock_cb, sizeof(activesock_cb));
--	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)
--	    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)
--	    goto on_error;
--
--	/* 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));
-+    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;
-     }
- 
--    /* Create STUN session */
--    {
--	pj_stun_session_cb sess_cb;
-+    if (stun_sock->conn_type != PJ_STUN_TP_UDP)
-+	PJ_LOG(5,(stun_sock->obj_name, "TCP connected"));
- 
--	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, 
--					stun_sock->obj_name,
--					&sess_cb, PJ_FALSE, 
--					stun_sock->grp_lock,
--					&stun_sock->stun_sess);
--	if (status != PJ_SUCCESS)
--	    goto on_error;
--    }
-+    /* 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);
-@@ -369,25 +551,305 @@ 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; i<PJ_ARRAY_SIZE(stun_sock->tsx_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;
- 
--    /* Done */
--    *p_stun_sock = stun_sock;
--    return PJ_SUCCESS;
-+    if (status != PJ_SUCCESS) {
-+	pj_stun_sock_destroy(stun_sock);
-+	pj_grp_lock_release(stun_sock->grp_lock);
-+	return status;
-+    }
- 
--on_error:
--    pj_stun_sock_destroy(stun_sock);
--    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);
-+
-+    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,
-@@ -526,6 +988,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);
-     }
-@@ -634,10 +1116,12 @@ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock)
-     
-     /* 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;
-@@ -658,6 +1142,8 @@ 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));
-@@ -770,13 +1256,246 @@ 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();
-+
-+    pj_sock_t *fd = &stun_sock->outgoing_socks[nb_check].fd;
-+    pj_activesock_t **asock = &stun_sock->outgoing_socks[nb_check].sock;
-+    pj_sockaddr_t **addr = &stun_sock->outgoing_socks[nb_check].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;
-+        }
-+
-+        *addr = (pj_sockaddr_t*)remote_addr;
-+
-+        status = pj_activesock_start_connect(
-+                                             *asock, stun_sock->pool, *addr,
-+                                             pj_sockaddr_get_len(*addr));
-+        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;
-+}
-+
-+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_t* remote_addr = NULL;
-+    // Get remote connected address
-+    for (int i = 0 ; i <= stun_sock->outgoing_nb ; ++i) {
-+        if (stun_sock->outgoing_socks[i].sock == asock) {
-+            remote_addr = stun_sock->outgoing_socks[i].addr;
-+        }
-+    }
-+    if (!remote_addr) return PJ_FALSE;
-+
-+    pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess);
-+    if (!cb->on_peer_connection) {
-+        return PJ_FALSE;
-+    }
-+
-+    (cb->on_peer_connection)(stun_sock->stun_sess, status, remote_addr);
-+    if (status == PJ_SUCCESS) {
-+        status = pj_activesock_start_read(asock, stun_sock->pool,
-+                                        stun_sock->cfg.max_pkt_size, 0);
-+    }
-+    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,
-@@ -787,6 +1506,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) {
-@@ -800,9 +1520,30 @@ 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 
-@@ -942,8 +1683,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)
-@@ -955,58 +1694,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 */
-@@ -1047,3 +1735,8 @@ 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 e4d67db0f..569b4826d 100644
---- a/pjnath/src/pjnath/stun_transaction.c
-+++ b/pjnath/src/pjnath/stun_transaction.c
-@@ -396,6 +396,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) {
- 	return PJ_SUCCESS;
-     }
-diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
-index 879ac6ffd..3a7df799a 100644
---- a/pjnath/src/pjnath/turn_session.c
-+++ b/pjnath/src/pjnath/turn_session.c
-@@ -311,7 +311,8 @@ 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;
-diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
-index dc6304d9f..6de6ab1da 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -894,12 +894,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;
-@@ -1553,7 +1548,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess,
- 		      return);
- 
-     PJ_LOG(5,(turn_sock->pool->obj_name, "Connection attempt from peer %s",
--	      pj_sockaddr_print(&peer_addr, addrtxt, sizeof(addrtxt), 3)));
-+	      pj_sockaddr_print(peer_addr, addrtxt, sizeof(addrtxt), 3)));
- 
-     if (turn_sock == NULL) {
- 	/* We've been destroyed */
-@@ -1766,3 +1761,20 @@ static void turn_on_connection_bind_status(pj_turn_session *sess,
- 					      peer_addr, addr_len);
-     }
- }
-+
-+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 6f9f1ff1a..e56c510c8 100644
---- a/pjnath/src/pjturn-client/client_main.c
-+++ b/pjnath/src/pjturn-client/client_main.c
-@@ -155,7 +155,7 @@ static int init()
- 
- 	name[strlen(name)-1] = '0'+i;
- 	status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), 
--				     &stun_sock_cb, &ss_cfg,
-+				     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);
-diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
-index 6c9c9ce11..99d1545c7 100644
---- a/pjnath/src/pjturn-srv/allocation.c
-+++ b/pjnath/src/pjturn-srv/allocation.c
-@@ -338,7 +338,8 @@ 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 94dda29a3..95ad1793d 100644
---- a/pjnath/src/pjturn-srv/server.c
-+++ b/pjnath/src/pjturn-srv/server.c
-@@ -156,7 +156,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 07ccc31f0..3b93b9417 100644
---- a/pjsip-apps/src/samples/icedemo.c
-+++ b/pjsip-apps/src/samples/icedemo.c
-@@ -44,6 +44,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;
-@@ -341,6 +342,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;
-@@ -384,7 +391,7 @@ static pj_status_t icedemo_init(void)
- 	icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password;
- 
- 	/* Connection type to TURN server */
--	if (icedemo.opt.turn_tcp)
-+	if (icedemo.opt.ice_tcp)
- 	    icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP;
- 	else
- 	    icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;
-@@ -395,6 +402,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;
- }
-@@ -530,10 +541,27 @@ static int print_cand(char buffer[], unsigned maxlen,
-     char *p = buffer;
-     int printed;
- 
--    PRINT("a=candidate:%.*s %u UDP %u %s %u typ ",
-+    /**   Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544)
-+     *    candidate-attribute   = "candidate" ":" foundation SP component-id SP
-+     *                             "TCP" SP
-+     *                             priority SP
-+     *                             connection-address SP
-+     *                             port SP
-+     *                             cand-type
-+     *                             [SP rel-addr]
-+     *                             [SP rel-port]
-+     *                             SP tcp-type-ext
-+     *                             *(SP extension-att-name SP
-+     *                                  extension-att-value)
-+     *
-+     *     tcp-type-ext          = "tcptype" SP tcp-type
-+     *     tcp-type              = "active" / "passive" / "so"
-+     */
-+    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, 
- 			    sizeof(ipaddr), 0),
-@@ -542,6 +570,23 @@ static int print_cand(char buffer[], unsigned maxlen,
-     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;
- 
-@@ -608,6 +653,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,
-@@ -709,7 +774,7 @@ static void icedemo_show_ice(void)
-  */
- static void icedemo_input_remote(void)
- {
--    char linebuf[80];
-+    char linebuf[120];
-     unsigned media_cnt = 0;
-     unsigned comp0_port = 0;
-     char     comp0_addr[80];
-@@ -819,27 +884,43 @@ static void icedemo_input_remote(void)
- 		    pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port);
- 
- 		} else if (strcmp(attr, "candidate")==0) {
-+		    /**   Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544)
-+		     *    candidate-attribute   = "candidate" ":" foundation SP component-id
-+		     * SP "TCP" SP priority SP connection-address SP port SP cand-type [SP
-+		     * rel-addr] [SP rel-port] SP tcp-type-ext
-+		     *                             *(SP extension-att-name SP
-+		     *                                  extension-att-value)
-+		     *
-+		     *     tcp-type-ext          = "tcptype" SP tcp-type
-+		     *     tcp-type              = "active" / "passive" / "so"
-+		     */
- 		    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));
- 		    
-@@ -855,6 +936,23 @@ static void icedemo_input_remote(void)
- 			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;
-@@ -879,6 +977,10 @@ static void icedemo_input_remote(void)
- 
- 		    if (cand->comp_id > icedemo.rem.comp_cnt)
- 			icedemo.rem.comp_cnt = cand->comp_id;
-+		} else if (strcmp(attr, "setup") == 0) {
-+		    // TODO
-+		} else if (strcmp(attr, "connection") == 0) {
-+		    // TODO
- 		}
- 	    }
- 	    break;
-diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
-index 7ae44d7d8..2010864c3 100644
---- a/pjsip/src/pjsua-lib/pjsua_core.c
-+++ b/pjsip/src/pjsua-lib/pjsua_core.c
-@@ -1553,7 +1553,7 @@ 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];
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0002-rfc2466.patch b/contrib/src/pjproject/0002-rfc2466.patch
deleted file mode 100644
index dc59a8ae841ce865413ec60dc480e52293c87d9c..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0002-rfc2466.patch
+++ /dev/null
@@ -1,207 +0,0 @@
- pjnath/src/pjnath/ice_session.c | 176 ++++++++++++++++++++++++++++++++
- 1 file changed, 176 insertions(+)
-
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index f35cfba5c..cc7e7d564 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -30,6 +30,21 @@
- #include <pj/rand.h>
- #include <pj/string.h>
- 
-+#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 <arpa/inet.h>
-+#include <asm/types.h>
-+#include <linux/netlink.h>
-+#include <linux/rtnetlink.h>
-+#include <sys/socket.h>
-+#include <unistd.h>
-+#endif
-+
- /* String names for candidate types */
- static const char *cand_type_names[] =
- {
-@@ -734,6 +749,144 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
- #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;
-+	}
-+
-+	if (nlmsg_ptr->nlmsg_type == NLMSG_DONE)
-+	    break;
-+
-+	for(; NLMSG_OK(nlmsg_ptr, nlmsg_len);
-+	    nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len))
-+	{
-+	    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;
-+		    }
-+		}
-+	    }
-+	}
-+    }
-+
-+    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
-@@ -751,6 +904,29 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
- 					 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; i<size*PJ_INET6_ADDRSTRLEN; i+=PJ_INET6_ADDRSTRLEN) {
-+	    if (!strncmp(tmpAddrStr, &deprecatedAddrs[i], PJ_INET6_ADDRSTRLEN)) {
-+		free(deprecatedAddrs);
-+		/* This address is considered as deprecated ignore it. */
-+		return PJ_SUCCESS;
-+	    }
-+	}
-+	free(deprecatedAddrs);
-+    }
-+
-     pj_ice_sess_cand *lcand;
-     pj_status_t status = PJ_SUCCESS;
-     char address[PJ_INET6_ADDRSTRLEN];
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0003-add-tcp-keep-alive.patch b/contrib/src/pjproject/0003-add-tcp-keep-alive.patch
deleted file mode 100644
index 486b9f664e865f81f23efdfab1fbecfc49861976..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0003-add-tcp-keep-alive.patch
+++ /dev/null
@@ -1,337 +0,0 @@
-From 0d46f77fe2cd2e3197f3b7b745d822b6c1cb9790 Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Thu, 27 Feb 2020 12:57:17 -0500
-Subject: [PATCH 3/9] add tcp keep alive
-
----
- pjlib/include/pj/sock.h             | 29 +++++++++++++++++++
- pjlib/src/pj/sock_bsd.c             | 45 +++++++++++++++++++++++++++++
- pjlib/src/pj/sock_common.c          | 20 +++++++++++++
- pjlib/src/pj/sock_uwp.cpp           | 24 +++++++++++++++
- pjlib/src/pj/symbols.c              |  3 ++
- pjnath/include/pjnath/ice_session.h |  5 ++++
- pjnath/include/pjnath/ice_strans.h  |  7 +++++
- pjnath/src/pjnath/ice_session.c     |  6 ++++
- pjnath/src/pjnath/ice_strans.c      | 11 +++++++
- 9 files changed, 150 insertions(+)
-
-diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
-index 292b364e7..7542045b1 100644
---- a/pjlib/include/pj/sock.h
-+++ b/pjlib/include/pj/sock.h
-@@ -314,6 +314,11 @@ 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;
-+
- /** Set the protocol-defined priority for all packets to be sent on socket.
-  */
- extern const pj_uint16_t PJ_SO_PRIORITY;
-@@ -344,9 +349,21 @@ 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_KEEPCNT constant */
-+#   define pj_TCP_KEEPCNT() PJ_TCP_KEEPCNT(void);
-+
-     /** Get #PJ_SO_REUSEADDR constant */
-     PJ_DECL(pj_uint16_t) pj_SO_REUSEADDR(void);
- 
-@@ -380,9 +397,21 @@ 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_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/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
-index ce23101c4..f9e8ffa82 100644
---- a/pjlib/src/pj/sock_bsd.c
-+++ b/pjlib/src/pj/sock_bsd.c
-@@ -28,6 +28,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.
-@@ -144,7 +153,19 @@ 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_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;
-@@ -517,6 +538,20 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af,
- 	if (rc==SOCKET_ERROR) {
- 	    // Ignored..
- 	}
-+    } else if(type == pj_SOCK_STREAM()) {
-+#ifndef SIO_KEEPALIVE_VALS
-+# define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4)
-+#endif
-+	DWORD dwBytesReturned = 0;
-+	struct tcp_keepalive {
-+	    ULONG onoff;
-+	    ULONG keepalivetime;
-+	    ULONG keepaliveinterval;
-+	} vals = { TRUE, 30000, 30000 };
-+	WSAIoctl(*sock, SIO_KEEPALIVE_VALS,
-+		 &vals, sizeof(vals),
-+		 NULL, 0, &dwBytesReturned,
-+		 NULL, NULL);
-     }
- #endif
- 
-@@ -548,6 +583,16 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af,
- 	if (type == pj_SOCK_STREAM()) {
- 	    pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(),
- 			       &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 = 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 96cd6b540..671d63f4a 100644
---- a/pjlib/src/pj/sock_common.c
-+++ b/pjlib/src/pj/sock_common.c
-@@ -1346,11 +1346,31 @@ 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_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 40250bf8d..148571aa8 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,12 @@ const pj_uint16_t PJ_SOL_TCP	= IPPROTO_TCP;
- const pj_uint16_t PJ_SOL_TCP	= 6;
- #endif /* SOL_TCP */
- 
-+#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 ab83af956..966a9fc43 100644
---- a/pjlib/src/pj/symbols.c
-+++ b/pjlib/src/pj/symbols.c
-@@ -259,6 +259,9 @@ 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_SOL_TCP)
- PJ_EXPORT_SYMBOL(PJ_SOL_UDP)
- PJ_EXPORT_SYMBOL(PJ_SOL_IPV6)
-diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
-index 5362c72e9..97069ed9e 100644
---- a/pjnath/include/pjnath/ice_session.h
-+++ b/pjnath/include/pjnath/ice_session.h
-@@ -648,6 +648,11 @@ typedef struct pj_ice_sess_cb
-                                         pj_ice_sess_checklist *clist,
-                                         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;
- 
- 
-diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h
-index 1c20729e8..1fed15f6f 100644
---- a/pjnath/include/pjnath/ice_strans.h
-+++ b/pjnath/include/pjnath/ice_strans.h
-@@ -219,6 +219,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;
- 
- 
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index cc7e7d564..0b1c5fd56 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -1534,6 +1534,12 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now)
- 					  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);
-diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index 0fdd2c695..0bd8c6138 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -99,6 +99,7 @@ static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2)
- 
- /* 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,
-@@ -1382,6 +1383,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
-     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
- 
-     /* Create! */
-@@ -2189,6 +2191,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.
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0003-win-vs2017-props.patch b/contrib/src/pjproject/0003-win-vs2017-props.patch
deleted file mode 100644
index c2a6d4439774ed558b0446de030325decec70090..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0003-win-vs2017-props.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From e9c6d327b059866dbf02a3ffd1004bbc677b87db Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Thu, 27 Feb 2020 14:43:27 -0500
-Subject: [PATCH 3/3] win vs2017 props
-
----
- build/vs/pjproject-vs14-common-config.props   | 8 ++++----
- build/vs/pjproject-vs14-common-defaults.props | 2 +-
- 2 files changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/build/vs/pjproject-vs14-common-config.props b/build/vs/pjproject-vs14-common-config.props
-index de8848f..6aa0276 100644
---- a/build/vs/pjproject-vs14-common-config.props
-+++ b/build/vs/pjproject-vs14-common-config.props
-@@ -18,22 +18,22 @@
-   <PropertyGroup>    
-     <API_Family Condition="'$(API_Family)'==''">WinDesktop</API_Family>
-     <PreprocessorDef></PreprocessorDef>
--    <DefaultToolset>v140</DefaultToolset>
-+    <DefaultToolset>v141</DefaultToolset>
-   </PropertyGroup>  
-   <Choose>
-     <When Condition="'$(API_Family)'=='WinDesktop'">
-       <PropertyGroup>
--        <BuildToolset>v140</BuildToolset>
-+        <BuildToolset>v141</BuildToolset>
-         <PreprocessorDef Condition="'$(Platform)'=='Win32'">WIN32;PJ_WIN32=1;PJ_M_I386=1;</PreprocessorDef>
-         <PreprocessorDef Condition="'$(Platform)'=='x64'">WIN64;PJ_WIN64=1;PJ_M_X86_64=1;</PreprocessorDef>
-       </PropertyGroup>
-     </When>
-     <When Condition="'$(API_Family)'=='UWP'">
-       <PropertyGroup>
--        <BuildToolset>v140</BuildToolset>
-+        <BuildToolset>v141</BuildToolset>
-         <PreprocessorDef>PJ_WIN32_UWP;UNICODE;_UNICODE;</PreprocessorDef>
-         <PreprocessorDef Condition="'$(Platform)'=='ARM'">$(PreprocessorDef);PJ_M_ARMV7=1;</PreprocessorDef>
--        <PlatformVersion>10.0.10586.0</PlatformVersion>
-+	<PlatformVersion>10.0.16299.0</PlatformVersion>
-         <MinPlatformVersion>10.0.10240.0</MinPlatformVersion>
-         <AppTypeRev>10.0</AppTypeRev>
-         
-diff --git a/build/vs/pjproject-vs14-common-defaults.props b/build/vs/pjproject-vs14-common-defaults.props
-index 526f6c9..974447f 100644
---- a/build/vs/pjproject-vs14-common-defaults.props
-+++ b/build/vs/pjproject-vs14-common-defaults.props
-@@ -3,7 +3,7 @@
-   <ImportGroup Label="PropertySheets">
-   </ImportGroup>
-   <PropertyGroup Label="UserMacros">
--    <VSVer>14</VSVer>
-+    <VSVer>15</VSVer>
-   </PropertyGroup>
-   <PropertyGroup>
-     <_ProjectFileVersion>14.0.22823.1</_ProjectFileVersion>
--- 
-2.25.4
diff --git a/contrib/src/pjproject/0004-multiple_listeners.patch b/contrib/src/pjproject/0004-multiple_listeners.patch
deleted file mode 100644
index 406f28c570aed7f2423f4ec4c8897cd741793b66..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0004-multiple_listeners.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 395a08ba6ebacf0e4039a73128fa147510f36f97 Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Thu, 20 Feb 2020 12:26:02 -0500
-Subject: [PATCH] multiple_listeners
-
----
- pjsip/src/pjsip/sip_transport.c | 22 ++++++++++++++++++----
- 1 file changed, 18 insertions(+), 4 deletions(-)
-
-diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
-index 7fac20f41..d5bd8fdda 100644
---- a/pjsip/src/pjsip/sip_transport.c
-+++ b/pjsip/src/pjsip/sip_transport.c
-@@ -1488,10 +1488,16 @@ 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 == tpf) {
--	    status = PJ_EEXISTS;
--	    break;
--	}
-+        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;
-+        }
-     }
- 
-     if (status != PJ_SUCCESS) {
-@@ -2467,6 +2473,14 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
- 
- 	} else {
- 
-+            /* Make sure we don't use another factory than the one given if
-+               secure flag is set */
-+            if (flag & PJSIP_TRANSPORT_SECURE) {
-+                TRACE_((THIS_FILE, "Can't create new TLS transport with no "
-+                        "provided suitable TLS listener."));
-+                return PJSIP_ETPNOTSUITABLE;
-+            }
-+
- 	    /* Find factory with type matches the destination type */
- 	    factory = mgr->factory_list.next;
- 	    while (factory != &mgr->factory_list) {
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0005-fix_ebusy_turn.patch b/contrib/src/pjproject/0005-fix_ebusy_turn.patch
deleted file mode 100644
index 0151aad5a2d57c86dd09c383ac71c19e9fbb0ec1..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0005-fix_ebusy_turn.patch
+++ /dev/null
@@ -1,335 +0,0 @@
-From a9a8cc6b62dc08e34ed4f7893a822809dd051572 Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Wed, 19 Feb 2020 15:17:38 -0500
-Subject: [PATCH] fix_ebusy_turn
-
----
- pjnath/include/pjnath/turn_session.h | 65 +++++++++++++++++++
- pjnath/include/pjnath/turn_sock.h    | 27 ++++++++
- pjnath/src/pjnath/turn_session.c     | 24 +++++--
- pjnath/src/pjnath/turn_sock.c        | 96 ++++++++++++++++++++++++++++
- 4 files changed, 207 insertions(+), 5 deletions(-)
-
-diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h
-index 956f57bf0..f546d7e18 100644
---- a/pjnath/include/pjnath/turn_session.h
-+++ b/pjnath/include/pjnath/turn_session.h
-@@ -254,6 +254,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
-@@ -798,6 +827,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 35388809f..05b5cafbe 100644
---- a/pjnath/include/pjnath/turn_sock.h
-+++ b/pjnath/include/pjnath/turn_sock.h
-@@ -623,6 +623,33 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(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
-  *
-diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
-index 3a7df799a..374b70b59 100644
---- a/pjnath/src/pjnath/turn_session.c
-+++ b/pjnath/src/pjnath/turn_session.c
-@@ -975,11 +975,23 @@ 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;
-@@ -1016,7 +1028,7 @@ 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;
-     }
- 
-@@ -1044,9 +1056,10 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
- 
- 	pj_assert(sess->srv_addr != NULL);
- 
--	status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len,
-+	status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, total_len,
- 				      sess->srv_addr,
--				      pj_sockaddr_get_len(sess->srv_addr));
-+				      pj_sockaddr_get_len(sess->srv_addr),
-+					  sent, pkt_len);
- 
-     } else {
- 	/* Use Send Indication. */
-@@ -1084,10 +1097,11 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
- 	    goto on_return;
- 
- 	/* Send the Send Indication */
--	status = sess->cb.on_send_pkt(sess, sess->tx_pkt, 
-+	status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, 
- 				      (unsigned)send_ind_len,
- 				      sess->srv_addr,
--				      pj_sockaddr_get_len(sess->srv_addr));
-+				      pj_sockaddr_get_len(sess->srv_addr),
-+					  sent, pkt_len);
-     }
- 
- on_return:
-diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
-index 6de6ab1da..111b4f133 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -100,6 +100,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;
- };
- 
- 
-@@ -116,6 +120,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,
-@@ -345,6 +356,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;
-@@ -653,6 +665,22 @@ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock,
- 				  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.
-  */
-@@ -1041,6 +1069,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,
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch b/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch
deleted file mode 100644
index f03e9f174f23be44906054ba83322f3566303e81..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From d8cbda261c7cebec8251742b1e383b69614951c5 Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Thu, 20 Feb 2020 13:37:23 -0500
-Subject: [PATCH] ignore_ipv6_on_transport_check
-
----
- pjsip/src/pjsip/sip_transport.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
-index d5bd8fdda..e168f14bc 100644
---- a/pjsip/src/pjsip/sip_transport.c
-+++ b/pjsip/src/pjsip/sip_transport.c
-@@ -2269,7 +2269,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
- 	pjsip_transport *seltp = sel->u.transport;
- 
- 	/* See if the transport is (not) suitable */
--	if (seltp->key.type != type) {
-+	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;
-+	if (type_no_ipv6 != key_type_no_ipv6) {
- 	    pj_lock_release(mgr->lock);
- 	    TRACE_((THIS_FILE, "Transport type in tpsel not matched"));
- 	    return PJSIP_ETPNOTSUITABLE;
--- 
-2.30.2
diff --git a/contrib/src/pjproject/0007-upnp-srflx-nat-assisted-cand.patch b/contrib/src/pjproject/0007-upnp-srflx-nat-assisted-cand.patch
deleted file mode 100644
index c3faa6b382c44093c0313c3a35342700fbd6d1fb..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0007-upnp-srflx-nat-assisted-cand.patch
+++ /dev/null
@@ -1,256 +0,0 @@
- pjnath/include/pjnath/config.h    |   2 +-
- pjnath/include/pjnath/stun_sock.h |  22 ++++
- pjnath/src/pjnath/ice_strans.c    | 171 +++++++++++++++++++++++++++---
- pjnath/src/pjnath/stun_sock.c     |   1 +
- 4 files changed, 181 insertions(+), 15 deletions(-)
-
-diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
-index 3e7c6ae3a..89b421c51 100644
---- a/pjnath/include/pjnath/config.h
-+++ b/pjnath/include/pjnath/config.h
-@@ -279,7 +279,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
- 
- 
-diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h
-index 51f60cc03..ba74cc6a5 100644
---- a/pjnath/include/pjnath/stun_sock.h
-+++ b/pjnath/include/pjnath/stun_sock.h
-@@ -276,6 +276,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
-diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index 0bd8c6138..d54eb4bf1 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -551,6 +551,136 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand,
-     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 (; j<comp->cand_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,
- 				     unsigned idx,
-@@ -875,20 +1005,33 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
- 
-     /* Create STUN transport if configured */
-     for (i=0; i<ice_st->cfg.stun_tp_cnt; ++i) {
--	unsigned max_cand_cnt = PJ_ICE_ST_MAX_CAND - comp->cand_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;
--	}
-+        unsigned max_cand_cnt = PJ_ICE_ST_MAX_CAND - comp->cand_cnt -
-+            ice_st->cfg.turn_tp_cnt;
-+
-+        status = PJ_ETOOSMALL;
-+
-+        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));
-+        }
-     }
- 
-     /* Create TURN relay if configured. */
-diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c
-index 6d2602552..b7643ca79 100644
---- a/pjnath/src/pjnath/stun_sock.c
-+++ b/pjnath/src/pjnath/stun_sock.c
-@@ -217,6 +217,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;
- }
- 
- 
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch b/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
deleted file mode 100644
index 5674ff09a10174046c7ca54cf6cd930356df2fc9..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-From 52f0c422e41ace1f2b682adbbaf67c4ddb0d17ed Mon Sep 17 00:00:00 2001
-From: jrun <darwinskernel@gmail.com>
-Date: Thu, 20 Feb 2020 12:39:23 -0500
-Subject: [PATCH] fix_ioqueue_ipv6_sendto
-
----
- pjlib/src/pj/ioqueue_common_abs.c |  5 ++++-
- pjlib/src/pj/ioqueue_common_abs.h | 17 ++++++++---------
- 2 files changed, 12 insertions(+), 10 deletions(-)
-
-diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c
-index 0b0abedea..87afabc31 100644
---- a/pjlib/src/pj/ioqueue_common_abs.c
-+++ b/pjlib/src/pj/ioqueue_common_abs.c
-@@ -1053,7 +1053,10 @@ retry_on_restart:
-     /*
-      * Check that address storage can hold the address parameter.
-      */
--    PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), 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/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h
-index d5e36b4d6..b1e9b2083 100644
---- a/pjlib/src/pj/ioqueue_common_abs.h
-+++ b/pjlib/src/pj/ioqueue_common_abs.h
-@@ -1,5 +1,5 @@
- /* $Id */
--/* 
-+/*
-  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
-  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
-  *
-@@ -15,14 +15,14 @@
-  *
-  * 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
-  */
- 
- /* ioqueue_common_abs.h
-  *
-- * This file contains private declarations for abstracting various 
-- * event polling/dispatching mechanisms (e.g. select, poll, epoll) 
-- * to the ioqueue. 
-+ * This file contains private declarations for abstracting various
-+ * event polling/dispatching mechanisms (e.g. select, poll, epoll)
-+ * to the ioqueue.
-  */
- 
- #include <pj/list.h>
-@@ -63,7 +63,7 @@ struct write_operation
-     pj_size_t		    size;
-     pj_ssize_t              written;
-     unsigned                flags;
--    pj_sockaddr_in	    rmt_addr;
-+    pj_sockaddr		    rmt_addr;
-     int			    rmt_addrlen;
- };
- 
-@@ -93,7 +93,7 @@ union operation_key
- 	unsigned	    ref_count;	\
- 	pj_bool_t	    closing;	\
- 	pj_time_val	    free_time;	\
--	
-+
- #else
- #   define UNREG_FIELDS
- #endif
-@@ -135,6 +135,5 @@ static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
-                                 pj_ioqueue_key_t *key,
-                                 enum ioqueue_event_type event_type );
- static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
--                                     pj_ioqueue_key_t *key, 
-+                                     pj_ioqueue_key_t *key,
-                                      enum ioqueue_event_type event_type);
--
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0010-fix-tcp-death-detection.patch b/contrib/src/pjproject/0010-fix-tcp-death-detection.patch
deleted file mode 100644
index 347aacdd7bc39ae260f793b7f8463d94d2bd33f3..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0010-fix-tcp-death-detection.patch
+++ /dev/null
@@ -1,115 +0,0 @@
- 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 7542045b1..3a3154db4 100644
---- a/pjlib/include/pj/sock.h
-+++ b/pjlib/include/pj/sock.h
-@@ -318,6 +318,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.
-  */
-@@ -361,6 +362,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);
- 
-@@ -406,6 +410,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 f9e8ffa82..a27fce775 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 671d63f4a..3cdfc8503 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 148571aa8..f1d92d093 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)
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0011-fix-turn-shutdown-crash.patch b/contrib/src/pjproject/0011-fix-turn-shutdown-crash.patch
deleted file mode 100644
index d0401ff50b720166b7c6662a5e691d4b9bf701f5..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0011-fix-turn-shutdown-crash.patch
+++ /dev/null
@@ -1,20 +0,0 @@
- pjnath/src/pjnath/turn_sock.c | 3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
-index 111b4f133..8e576e031 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -1492,9 +1492,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,
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0012-ignore-down-interfaces.patch b/contrib/src/pjproject/0012-ignore-down-interfaces.patch
deleted file mode 100644
index 8dd5f1ed74b45ab9b6f52081b984acbe5a76c832..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0012-ignore-down-interfaces.patch
+++ /dev/null
@@ -1,46 +0,0 @@
- pjlib/src/pj/ip_helper_generic.c | 15 +++++++++++++++
- 1 file changed, 15 insertions(+)
-
-diff --git a/pjlib/src/pj/ip_helper_generic.c b/pjlib/src/pj/ip_helper_generic.c
-index d7bbf725e..2a392d16b 100644
---- a/pjlib/src/pj/ip_helper_generic.c
-+++ b/pjlib/src/pj/ip_helper_generic.c
-@@ -97,6 +97,11 @@ static pj_status_t if_enum_by_af(int af,
- 	    continue; /* Skip when interface is down */
- 	}
- 
-+	if ((it->ifa_flags & IFF_RUNNING)==0) {
-+	    TRACE_((THIS_FILE, "  interface is not running"));
-+	    continue; /* Skip when interface is not running */
-+	}
-+
- #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
- 	if (it->ifa_flags & IFF_LOOPBACK) {
- 	    TRACE_((THIS_FILE, "  loopback interface"));
-@@ -208,6 +213,11 @@ static pj_status_t if_enum_by_af(int af,
- 	    continue; /* Skip when interface is down */
- 	}
- 
-+	if ((iff.ifr_flags & IFF_RUNNING)==0) {
-+	    TRACE_((THIS_FILE, "  interface is not running"));
-+	    continue; /* Skip when interface is not running */
-+	}
-+
- #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
- 	if (iff.ifr_flags & IFF_LOOPBACK) {
- 	    TRACE_((THIS_FILE, "  loopback interface"));
-@@ -286,6 +296,11 @@ static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[])
- 	    continue; /* Skip when interface is down */
- 	}
- 
-+        if ((ifreq.ifr_flags & IFF_RUNNING)==0) {
-+	    TRACE_((THIS_FILE, "  interface is not running"));
-+	    continue; /* Skip when interface is not running */
-+	}
-+
- #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
- 	if (ifreq.ifr_flags & IFF_LOOPBACK) {
- 	    TRACE_((THIS_FILE, "  loopback interface"));
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0013-ignore-addresses-for-RFC7335.patch b/contrib/src/pjproject/0013-ignore-addresses-for-RFC7335.patch
deleted file mode 100644
index e0ce47e89537589f9c52820d88a53ac0377a03a2..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0013-ignore-addresses-for-RFC7335.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-diff --git a/pjlib/src/pj/ip_helper_generic.c b/pjlib/src/pj/ip_helper_generic.c
-index 2a392d16b..c1e49c3d3 100644
---- a/pjlib/src/pj/ip_helper_generic.c
-+++ b/pjlib/src/pj/ip_helper_generic.c
-@@ -121,6 +121,16 @@ static pj_status_t if_enum_by_af(int af,
- 	    continue; /* Skip when interface is down */
- 	}
- 
-+	/* Ignore 192.0.0.0/29 address. cf https://datatracker.ietf.org/doc/html/rfc7335#section-4
-+	 */
-+	if (af==pj_AF_INET() &&
-+	    (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 4) == 201326592 /* 0b1100000000000000000000000000 which is 192.0.0.0 >> 4 */)
-+	{
-+	    TRACE_((THIS_FILE, "  address %s ignored (192.0.0.0/29 class)",
-+		    get_addr(ad), ad->sa_family));
-+	    continue;
-+	}
-+
- 	/* Ignore 0.0.0.0/8 address. This is a special address
- 	 * which doesn't seem to have practical use.
- 	 */
-@@ -225,6 +235,16 @@ static pj_status_t if_enum_by_af(int af,
- 	}
- #endif
- 
-+	/* Ignore 192.0.0.0/29 address. cf https://datatracker.ietf.org/doc/html/rfc7335#section-4
-+	 */
-+	if (af==pj_AF_INET() &&
-+	    (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 4) == 201326592 /* 0b1100000000000000000000000000 which is 192.0.0.0 >> 4 */)
-+	{
-+	    TRACE_((THIS_FILE, "  address %s ignored (192.0.0.0/29 class)",
-+		    get_addr(ad), ad->sa_family));
-+	    continue;
-+	}
-+
- 	/* Ignore 0.0.0.0/8 address. This is a special address
- 	 * which doesn't seem to have practical use.
- 	 */
-@@ -324,6 +344,16 @@ static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[])
- 	    continue;	/* Not address family that we want, continue */
- 	}
- 
-+	/* Ignore 192.0.0.0/29 address. cf https://datatracker.ietf.org/doc/html/rfc7335#section-4
-+	 */
-+	if (af==pj_AF_INET() &&
-+	    (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 4) == 201326592 /* 0b1100000000000000000000000000 which is 192.0.0.0 >> 4 */)
-+	{
-+	    TRACE_((THIS_FILE, "  address %s ignored (192.0.0.0/29 class)",
-+		    get_addr(ad), ad->sa_family));
-+	    continue;
-+	}
-+
- 	/* Ignore 0.0.0.0/8 address. This is a special address
- 	 * which doesn't seem to have practical use.
- 	 */
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0014-fix-socket-leak.patch b/contrib/src/pjproject/0014-fix-socket-leak.patch
deleted file mode 100644
index 423c4db9b3e5e3d646caf0885fb0eec41197a29a..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0014-fix-socket-leak.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From c1093eeb18a4090fd7eac6e8e70a6da1777f0907 Mon Sep 17 00:00:00 2001
-From: Olivier Dion <olivier.dion@savoirfairelinux.com>
-Date: Mon, 26 Jul 2021 09:52:50 -0400
-Subject: [PATCH]  pjnath/turn_sock: Fix leak of socket
-
-Opened socket is not closed on binding and quality of service error.  The socket
-is owned by the active sock only if everything went well.
----
- pjnath/src/pjnath/turn_sock.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
-index 8e576e031..7a88494c6 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -1286,6 +1286,7 @@ static void turn_on_state(pj_turn_session *sess,
- 					 turn_sock->setting.port_range,
- 					 max_bind_retry);
- 	    if (status != PJ_SUCCESS) {
-+		pj_sock_close(sock);
- 		pj_turn_sock_destroy(turn_sock);
- 		return;
- 	    }
-@@ -1296,6 +1297,7 @@ static void turn_on_state(pj_turn_session *sess,
- 				    turn_sock->pool->obj_name, NULL);
- 	    if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) 
- 	    {
-+		pj_sock_close(sock);
- 		pj_turn_sock_destroy(turn_sock);
- 		return;
- 	    }
-@@ -1353,6 +1355,11 @@ static void turn_on_state(pj_turn_session *sess,
- 					  turn_sock->cfg.ioqueue, &asock_cb,
- 					  turn_sock,
- 					  &turn_sock->active_sock);
-+
-+	    /* Active socket failed to take ownership of `sock` */
-+	    if (status != PJ_SUCCESS) {
-+		    pj_sock_close(sock);
-+	    }
- 	}
- #if PJ_HAS_SSL_SOCK
- 	else {
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0015-fix-socktype-and-duplicate-checking.patch b/contrib/src/pjproject/0015-fix-socktype-and-duplicate-checking.patch
deleted file mode 100644
index 8384046e5578179e99c5cd7eaad8b20f512daa7b..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0015-fix-socktype-and-duplicate-checking.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 2feee8db77ed47e7b574367295d4f03f9aea67f8 Mon Sep 17 00:00:00 2001
-From: sauwming <ming@teluu.com>
-Date: Fri, 6 Aug 2021 09:45:54 +0800
-Subject: [PATCH] Fix socktype and duplicate checking in pj_getaddrinfo()
-
----
- pjlib/src/pj/addr_resolv_sock.c | 11 ++++++++---
- 1 file changed, 8 insertions(+), 3 deletions(-)
-
-diff --git a/pjlib/src/pj/addr_resolv_sock.c b/pjlib/src/pj/addr_resolv_sock.c
-index f68c4b177..0954f2ee0 100644
---- a/pjlib/src/pj/addr_resolv_sock.c
-+++ b/pjlib/src/pj/addr_resolv_sock.c
-@@ -197,15 +197,20 @@ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename,
- 	if (af!=PJ_AF_UNSPEC && res->ai_family != af)
- 	    continue;
- 
--	if (res->ai_socktype != pj_SOCK_DGRAM()
--                    && res->ai_socktype != pj_SOCK_STREAM()) {
-+	if (res->ai_socktype != pj_SOCK_DGRAM() &&
-+            res->ai_socktype != pj_SOCK_STREAM() &&
-+            /* It is possible that the result's sock type
-+             * is unspecified.
-+             */
-+            res->ai_socktype != 0)
-+        {
- 	        continue;
- 	}
- 
- 	/* Add current address in the resulting list if there
- 	 * is no duplicates only. */
- 	for (j = 0; j < i; j++) {
--	    if (!pj_memcmp(&ai[j].ai_addr, res->ai_addr, res->ai_addrlen)) {
-+	    if (!pj_sockaddr_cmp(&ai[j].ai_addr, res->ai_addr)) {
- 		duplicate_found = PJ_TRUE;
- 		break;
- 	    }
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0016-use-larger-Ta-interval.patch b/contrib/src/pjproject/0016-use-larger-Ta-interval.patch
deleted file mode 100644
index 9de4dfd4172414a67d2adcbdce53fb04ca72b9ac..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0016-use-larger-Ta-interval.patch
+++ /dev/null
@@ -1,132 +0,0 @@
- pjnath/include/pjnath/config.h  |  2 +-
- pjnath/src/pjnath/ice_session.c | 31 ++++++++++++++++++++-----------
- 2 files changed, 21 insertions(+), 12 deletions(-)
-
-diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
-index 89b421c51..fe58be9c9 100644
---- a/pjnath/include/pjnath/config.h
-+++ b/pjnath/include/pjnath/config.h
-@@ -340,7 +340,7 @@
-  * Default: 20
-  */
- #ifndef PJ_ICE_TA_VAL
--#   define PJ_ICE_TA_VAL			    20
-+#   define PJ_ICE_TA_VAL			    50
- #endif
- 
- 
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index 0b1c5fd56..503cbfd91 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -2705,9 +2705,8 @@ static pj_status_t perform_check(pj_ice_sess *ice,
-     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, 
- 					pj_timer_entry *te)
-@@ -2735,8 +2734,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
-     /* Set checklist state to Running */
-     clist_set_state(ice, clist, PJ_ICE_SESS_CHECKLIST_ST_RUNNING);
- 
--    LOG5((ice->obj_name, "Starting checklist periodic check"));
--    pj_log_push_indent();
-+    pj_ice_sess_check *check = NULL;
- 
-     /* Send STUN Binding request for check with highest priority on
-      * Retry state.
-@@ -2744,9 +2742,11 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
- 
-     if (start_count == 0) {
- 	for (i = 0; i < clist->count; ++i) {
--	    pj_ice_sess_check *check = &clist->checks[i];
-+	    check = &clist->checks[i];
- 	    // Reconnect closed TURN sockets
- 	    if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) {
-+		LOG5((ice->obj_name, "re-Starting periodic check for check %i (needs retry)", i));
-+	    pj_log_push_indent();
- 		status = perform_check(ice, clist, i, ice->is_nominating);
- 		if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- 		    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
-@@ -2763,13 +2763,15 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
- 	// 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 *check = &clist->checks[i];
-+	    check = &clist->checks[i];
- 	    if (check->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));
-+		    pj_log_push_indent();
- 		    status = perform_check(ice, clist, i, ice->is_nominating);
- 		    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- 			check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
-@@ -2789,9 +2791,12 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
- 
-     if (start_count == 0) {
- 	for (i = 0; i < clist->count; ++i) {
--	    pj_ice_sess_check *check = &clist->checks[i];
-+	    check = &clist->checks[i];
- 
- 	    if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
-+		LOG5((ice->obj_name, "Starting periodic check for check %i (was waiting)", i));
-+		pj_log_push_indent();
-+
- 		status = perform_check(ice, clist, i, ice->is_nominating);
- 		if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- 		    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
-@@ -2809,9 +2814,11 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
-      */
-     if (start_count == 0) {
- 	for (i = 0; i < clist->count; ++i) {
--	    pj_ice_sess_check *check = &clist->checks[i];
-+	    check = &clist->checks[i];
- 
- 	    if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
-+	    LOG5((ice->obj_name, "Starting periodic check for check %i (was frozen)", i));
-+	    pj_log_push_indent();
- 		status = perform_check(ice, clist, i, ice->is_nominating);
- 		if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- 		    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
-@@ -2826,7 +2833,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
-     if (start_count == 0) {
- 	// If all sockets are pending, do nothing
- 	for (i = 0; i < clist->count; ++i) {
--	    pj_ice_sess_check *check = &clist->checks[i];
-+	    check = &clist->checks[i];
- 	    if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) {
- 		++start_count;
- 		break;
-@@ -2834,8 +2841,11 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
- 	}
-     }
- 
--    // Cannot start check because there's no suitable candidate pair.
-+    // The checks are performed at the rate of 1 check per Ta
-+    // interval. If a new check was started, we need to re-schedule
-+    // for the next one (if any).
-     if (start_count!=0) {
-+	pj_assert(check != NULL);
- 	pj_time_val timeout = {0, PJ_ICE_TA_VAL};
- 
- 	pj_time_val_normalize(&timeout);
-@@ -2848,7 +2858,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)
- {
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0017-auto-register-thread.patch b/contrib/src/pjproject/0017-auto-register-thread.patch
deleted file mode 100644
index 92acb3b34f5958eb4f4332708bf46629bb7432d2..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0017-auto-register-thread.patch
+++ /dev/null
@@ -1,84 +0,0 @@
-From 2e9dcff92232e7fe6bd779a5947d32d8c94cd807 Mon Sep 17 00:00:00 2001
-From: Olivier Dion <olivier.dion@polymtl.ca>
-Date: Wed, 28 Jul 2021 17:27:29 -0400
-Subject: [PATCH] pj/os_core: Register thread if not registered
-
----
- pjlib/src/pj/os_core_unix.c  | 35 ++++++++++++++++++++++++++++++++---
- pjlib/src/pj/os_core_win32.c |  7 ++++---
- 2 files changed, 36 insertions(+), 6 deletions(-)
-
-diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
-index c9a7b6fc1..7df3c6cc8 100644
---- a/pjlib/src/pj/os_core_unix.c
-+++ b/pjlib/src/pj/os_core_unix.c
-@@ -686,6 +686,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()
-  */
-@@ -695,9 +707,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);
-     }
- 
-     /*
-diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
-index 09d714490..10715b60f 100644
---- a/pjlib/src/pj/os_core_win32.c
-+++ b/pjlib/src/pj/os_core_win32.c
-@@ -583,9 +583,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);
-     }
- 
-     /*
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0018-fix-ioqueue-lock-acquire.patch b/contrib/src/pjproject/0018-fix-ioqueue-lock-acquire.patch
deleted file mode 100644
index 3ffc1cf1fe5c5b5bc422c0622cc25d9b8465e0e0..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0018-fix-ioqueue-lock-acquire.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 2bf169548d9d55ca022a3203bb64c3c90ed01891 Mon Sep 17 00:00:00 2001
-From: Olivier Dion <olivier.dion@savoirfairelinux.com>
-Date: Thu, 19 Aug 2021 10:15:13 -0400
-Subject: [PATCH] pj/ioqueue_epoll: Acquire ioqueue's lock before destroying
-
-`ioqueue_destroy(ioqueue)` will call `pj_lock_release(ioqueue->lock)`, which will
-result in failure of the assertion `mutex->owner == pj_thread_this()` when
-`PJ_DEBUG` is defined.
-
-Thus, acquire the lock before destroying the ioqueue.
----
- pjlib/src/pj/ioqueue_epoll.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c
-index dbf479d25..560160ab7 100644
---- a/pjlib/src/pj/ioqueue_epoll.c
-+++ b/pjlib/src/pj/ioqueue_epoll.c
-@@ -231,6 +231,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- 
-     ioqueue->epfd = os_epoll_create(max_fd);
-     if (ioqueue->epfd < 0) {
-+	pj_lock_acquire(ioqueue->lock);
- 	ioqueue_destroy(ioqueue);
- 	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-     }
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0019-resort-check-list-after-adding-prflx.patch b/contrib/src/pjproject/0019-resort-check-list-after-adding-prflx.patch
deleted file mode 100644
index 97bb6f0abde1019086593d596062d60f4c5b4f02..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0019-resort-check-list-after-adding-prflx.patch
+++ /dev/null
@@ -1,22 +0,0 @@
- pjnath/src/pjnath/ice_session.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index 503cbfd91..f060aaa9c 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -4138,7 +4138,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
- 	LOG4((ice->obj_name, "New triggered check added: %d", 
- 	     ice->clist.count));
- 	pj_log_push_indent();
-+
- 	perform_check(ice, &ice->clist, ice->clist.count++, nominate);
-+	/* Re-sort the list because of the newly added pair. */
-+	sort_checklist(ice, &ice->clist);
-+
- 	pj_log_pop_indent();
- 
-     } else {
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/0020-avoid-immediate-nominating-triggered-check.patch b/contrib/src/pjproject/0020-avoid-immediate-nominating-triggered-check.patch
deleted file mode 100644
index f8145095e42d6859b578cde9a4c0e501250462a6..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/0020-avoid-immediate-nominating-triggered-check.patch
+++ /dev/null
@@ -1,95 +0,0 @@
- pjnath/src/pjnath/ice_session.c | 57 ++++++++++++++++++++++++---------
- 1 file changed, 42 insertions(+), 15 deletions(-)
-
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index f060aaa9c..f04b7314e 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -4057,14 +4057,22 @@ static void handle_incoming_check(pj_ice_sess *ice,
- 	if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN ||
- 	    c->state == PJ_ICE_SESS_CHECK_STATE_WAITING)
- 	{
--	    /* See if we shall nominate this check */
--	    pj_bool_t nominate = (c->nominated || ice->is_nominating);
--
--	    LOG5((ice->obj_name, "Performing triggered check for check %d",i));
--	    pj_log_push_indent();
--	    perform_check(ice, &ice->clist, i, nominate);
--	    pj_log_pop_indent();
-+	    /* If we are nominating in regular nomination, don't nominate this
-+	     * triggered check immediately, just wait for its scheduled check.
-+	     */
-+	    if (ice->is_nominating && !ice->opt.aggressive) {
-+		LOG5((ice->obj_name, "Triggered check for check %d not "
-+		      "performed because nomination is in progress", i));
-+	    } else {
-+		/* See if we shall nominate this check */
-+		pj_bool_t nominate = (c->nominated || ice->is_nominating);
- 
-+		LOG5((ice->obj_name, "Performing triggered check for "
-+		      "check %d",i));
-+		pj_log_push_indent();
-+		perform_check(ice, &ice->clist, i, nominate);
-+		pj_log_pop_indent();
-+	    }
- 	} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) {
- 	    /* Should retransmit immediately
- 	     */
-@@ -4124,7 +4132,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
-     else if (ice->clist.count < PJ_ICE_MAX_CHECKS) {
- 
- 	pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count];
--	pj_bool_t nominate;
-+	unsigned check_id = ice->clist.count;
- 
- 	c->lcand = lcand;
- 	c->rcand = rcand;
-@@ -4132,19 +4140,38 @@ static void handle_incoming_check(pj_ice_sess *ice,
- 	c->state = PJ_ICE_SESS_CHECK_STATE_WAITING;
- 	c->nominated = rcheck->use_candidate;
- 	c->err_code = PJ_SUCCESS;
-+	++ice->clist.count;
- 
--	nominate = (c->nominated || ice->is_nominating);
-+	LOG4((ice->obj_name, "New triggered check added: %d", check_id));
- 
--	LOG4((ice->obj_name, "New triggered check added: %d", 
--	     ice->clist.count));
--	pj_log_push_indent();
-+	/* If we are nominating in regular nomination, don't nominate this
-+	 * newly found pair.
-+	 */
-+	if (ice->is_nominating && !ice->opt.aggressive) {
-+	    LOG5((ice->obj_name, "Triggered check for check %d not "
-+		  "performed because nomination is in progress", check_id));
-+
-+	    /* Just in case the periodic check has been stopped (due to no more
-+	     * pair to check), let's restart it for this pair.
-+	     */
-+	    if (!pj_timer_entry_running(&ice->clist.timer)) {
-+		pj_time_val delay = {0, 0};
-+		pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
-+						  &ice->clist.timer, &delay,
-+						  PJ_TRUE, ice->grp_lock);
-+	    }
-+	} else {
-+	    pj_bool_t nominate;
-+	    nominate = (c->nominated || ice->is_nominating);
-+
-+	    pj_log_push_indent();
-+	    perform_check(ice, &ice->clist, check_id, nominate);
-+	    pj_log_pop_indent();
-+	}
- 
--	perform_check(ice, &ice->clist, ice->clist.count++, nominate);
- 	/* Re-sort the list because of the newly added pair. */
- 	sort_checklist(ice, &ice->clist);
- 
--	pj_log_pop_indent();
--
-     } else {
- 	LOG4((ice->obj_name, "Error: unable to perform triggered check: "
- 	     "TOO MANY CHECKS IN CHECKLIST!"));
--- 
-2.25.1
-
diff --git a/contrib/src/pjproject/SHA512SUMS b/contrib/src/pjproject/SHA512SUMS
index 34aefbccd076c35eb5b0eec7eaf011dbe7bf4e49..e8cf85887a1ca727b585fcc1500ff71fe7d969c2 100644
--- a/contrib/src/pjproject/SHA512SUMS
+++ b/contrib/src/pjproject/SHA512SUMS
@@ -1 +1 @@
-021fe3f904b15e149cf647fe2ebdf530b5a8f11ba226396b6df1c8b85c406a532e6d9c310617434b520de787ec0cc4b07d9d54507d51b80627a5c23e884516c2  pjproject-2.11.tar.gz
+221164832b1922d72f783cfc0ec246de3dcf1121763205c324f2e0654dbb1dbff56f7ab92c7850843034c080e062153ffef838f01433390c72f0a80a33398baf  pjproject-f94b3d72b428d7c59b319e35095de711ed1d8534.tar.gz
diff --git a/contrib/src/pjproject/package.json b/contrib/src/pjproject/package.json
index dbbb0c6e3853632d92e7bcf2b2391bd6600a0c37..4822684e7945ebc3d2094ef3868a418ec517e544 100644
--- a/contrib/src/pjproject/package.json
+++ b/contrib/src/pjproject/package.json
@@ -1,32 +1,12 @@
 {
     "name": "pjproject",
-    "version": "2.11",
-    "url": "https://github.com/pjsip/pjproject/archive/__VERSION__.tar.gz",
+    "version": "f94b3d72b428d7c59b319e35095de711ed1d8534",
+    "url": "https://github.com/savoirfairelinux/pjproject/archive/__VERSION__.tar.gz",
     "deps": ["gnutls"],
     "patches": [
-        "0001-rfc6544.patch",
-        "0002-rfc2466.patch",
-        "0003-add-tcp-keep-alive.patch",
-        "0004-multiple_listeners.patch",
-        "0005-fix_ebusy_turn.patch",
-        "0006-ignore_ipv6_on_transport_check.patch",
-        "0007-upnp-srflx-nat-assisted-cand.patch",
-        "0008-fix_ioqueue_ipv6_sendto.patch",
         "0009-add-config-site.patch",
-        "0010-fix-tcp-death-detection.patch",
-        "0011-fix-turn-shutdown-crash.patch",
-        "0012-ignore-down-interfaces.patch",
-        "0013-ignore-addresses-for-RFC7335.patch",
-        "0014-fix-socket-leak.patch",
-        "0015-fix-socktype-and-duplicate-checking.patch",
-        "0016-use-larger-Ta-interval.patch",
-        "0017-auto-register-thread.patch",
-        "0018-fix-ioqueue-lock-acquire.patch",
-        "0019-resort-check-list-after-adding-prflx.patch",
-        "0020-avoid-immediate-nominating-triggered-check.patch",
         "0001-win-config.patch",
-        "0002-win-vs-gnutls.patch",
-        "0003-win-vs2017-props.patch"
+        "0002-win-vs-gnutls.patch"
     ],
     "project_paths": [
         "pjlib-util/build/pjlib_util.vcxproj",
diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak
index bc71723bcc894c89dfcf107f3e3f6e36fec8f463..e1c1fc87922d0c55d786ed314b5f38e0012cd36f 100644
--- a/contrib/src/pjproject/rules.mak
+++ b/contrib/src/pjproject/rules.mak
@@ -1,6 +1,6 @@
 # PJPROJECT
-PJPROJECT_VERSION := 2.11
-PJPROJECT_URL := https://github.com/pjsip/pjproject/archive/$(PJPROJECT_VERSION).tar.gz
+PJPROJECT_VERSION := f94b3d72b428d7c59b319e35095de711ed1d8534
+PJPROJECT_URL := https://github.com/savoirfairelinux/pjproject/archive/${PJPROJECT_VERSION}.tar.gz
 
 PJPROJECT_OPTIONS := --disable-sound        \
                      --enable-video         \
@@ -43,29 +43,9 @@ $(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.gz:
 
 .sum-pjproject: pjproject-$(PJPROJECT_VERSION).tar.gz
 
-# NOTE!  Remove 0018-fix-ioqueue-lock-acquire.patch when bumping pjsip to version 2.12
 pjproject: pjproject-$(PJPROJECT_VERSION).tar.gz .sum-pjproject
 	$(UNPACK)
-	$(APPLY) $(SRC)/pjproject/0001-rfc6544.patch
-	$(APPLY) $(SRC)/pjproject/0002-rfc2466.patch
-	$(APPLY) $(SRC)/pjproject/0003-add-tcp-keep-alive.patch
-	$(APPLY) $(SRC)/pjproject/0004-multiple_listeners.patch
-	$(APPLY) $(SRC)/pjproject/0005-fix_ebusy_turn.patch
-	$(APPLY) $(SRC)/pjproject/0006-ignore_ipv6_on_transport_check.patch
-	$(APPLY) $(SRC)/pjproject/0007-upnp-srflx-nat-assisted-cand.patch
-	$(APPLY) $(SRC)/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
 	$(APPLY) $(SRC)/pjproject/0009-add-config-site.patch
-	$(APPLY) $(SRC)/pjproject/0010-fix-tcp-death-detection.patch
-	$(APPLY) $(SRC)/pjproject/0011-fix-turn-shutdown-crash.patch
-	$(APPLY) $(SRC)/pjproject/0012-ignore-down-interfaces.patch # TODO remove with 2.12 (https://github.com/pjsip/pjproject/commit/997a468a5e90f9a88a36f543bef85e0fb0af0dfc)
-	$(APPLY) $(SRC)/pjproject/0013-ignore-addresses-for-RFC7335.patch # TODO remove with 2.12 (https://github.com/pjsip/pjproject/commit/d245ffaf91120fab7bb70e3f46206faeb5b01269)
-	$(APPLY) $(SRC)/pjproject/0014-fix-socket-leak.patch
-	$(APPLY) $(SRC)/pjproject/0015-fix-socktype-and-duplicate-checking.patch # TODO remove with 2.12 (https://github.com/pjsip/pjproject/commits/2feee8db77ed47e7b574367295d4f03f9aea67f8)
-	$(APPLY) $(SRC)/pjproject/0016-use-larger-Ta-interval.patch
-	$(APPLY) $(SRC)/pjproject/0017-auto-register-thread.patch
-	$(APPLY) $(SRC)/pjproject/0018-fix-ioqueue-lock-acquire.patch
-	$(APPLY) $(SRC)/pjproject/0019-resort-check-list-after-adding-prflx.patch # TODO remove with 2.12 (https://github.com/pjsip/pjproject/pull/2806)
-	$(APPLY) $(SRC)/pjproject/0020-avoid-immediate-nominating-triggered-check.patch # TODO remove with 2.12 (https://github.com/pjsip/pjproject/issues/2812)
 ifdef HAVE_ANDROID
 	$(APPLY) $(SRC)/pjproject/0001-android.patch
 endif
diff --git a/contrib/src/pjproject/sip_td_timeout.patch b/contrib/src/pjproject/sip_td_timeout.patch
deleted file mode 100644
index 4005d8a63900602188bcbf6ad189b1dc5629ff61..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/sip_td_timeout.patch
+++ /dev/null
@@ -1,15 +0,0 @@
- pjsip/include/pjsip/sip_config.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
-index 904df24e..0831e4ef 100644
---- a/pjsip/include/pjsip/sip_config.h
-+++ b/pjsip/include/pjsip/sip_config.h
-@@ -1054,7 +1054,7 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
-
- /** Transaction completed timer for INVITE */
- #if !defined(PJSIP_TD_TIMEOUT)
--#  define PJSIP_TD_TIMEOUT	32000
-+#  define PJSIP_TD_TIMEOUT	15000
- #endif
-