diff --git a/contrib/src/pjproject/0001-android.patch b/contrib/src/pjproject/0001-android.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0e063cb97a1588ffb9b0a5101a2ec8542146b205
--- /dev/null
+++ b/contrib/src/pjproject/0001-android.patch
@@ -0,0 +1,75 @@
+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 7fcb12ec4..066a1d0f4 100755
+--- a/aconfigure
++++ b/aconfigure
+@@ -5954,9 +5954,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 0367679e2..0280f6894 100644
+--- a/aconfigure.ac
++++ b/aconfigure.ac
+@@ -489,9 +489,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 47dd40ad5..f8cf75f80 100644
+--- a/pjlib/include/pj/config_site.h
++++ b/pjlib/include/pj/config_site.h
+@@ -21,3 +21,14 @@
+ #define PJ_ICE_MAX_TURN                         4
+ #define PJ_ICE_COMP_BITS                        2
+ #define PJ_ICE_MAX_CHECKS                       1024
++
++/*
++ * ANDROID settings.
++ */
++#undef  PJ_ANDROID
++#define PJ_ANDROID                              0
++#undef  PJ_JNI_HAS_JNI_ONLOAD
++#define PJ_JNI_HAS_JNI_ONLOAD                   0
++#undef  PJMEDIA_HAS_G722_CODEC
++#define PJMEDIA_HAS_G722_CODEC                  1
++#define PJMEDIA_VID_DEV_INFO_FMT_CNT            128
+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.24.1
+
diff --git a/contrib/src/pjproject/0001-rfc6544.patch b/contrib/src/pjproject/0001-rfc6544.patch
new file mode 100644
index 0000000000000000000000000000000000000000..182478456618d4502fe52b6f55af0c68a1091527
--- /dev/null
+++ b/contrib/src/pjproject/0001-rfc6544.patch
@@ -0,0 +1,3929 @@
+From bbf656dd46216c2551895abfb487b51ff786d1ae Mon Sep 17 00:00:00 2001
+From: jrun <darwinskernel@gmail.com>
+Date: Wed, 26 Feb 2020 13:27:51 -0500
+Subject: [PATCH] rfc6544
+
+---
+ pjnath/include/pjnath/ice_session.h     |  173 +++-
+ 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         |  513 ++++++++++-
+ pjnath/src/pjnath/ice_strans.c          |  727 ++++++++++++---
+ pjnath/src/pjnath/nat_detect.c          |    7 +-
+ pjnath/src/pjnath/stun_session.c        |   15 +-
+ pjnath/src/pjnath/stun_sock.c           | 1076 +++++++++++++++++++----
+ pjnath/src/pjnath/stun_transaction.c    |    3 +
+ pjnath/src/pjnath/turn_session.c        |    3 +-
+ pjnath/src/pjnath/turn_sock.c           |   17 +
+ 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 +-
+ 21 files changed, 2481 insertions(+), 378 deletions(-)
+
+diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
+index 8971220f0..39c197c29 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;
+@@ -309,6 +355,11 @@ typedef struct pj_ice_sess_cand
+      */
+     pj_sockaddr		 rel_addr;
+ 
++    /**
++     * Transport used (TCP or UDP)
++     */
++    pj_ice_cand_transport transport;
++
+ } pj_ice_sess_cand;
+ 
+ 
+@@ -324,6 +375,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
+@@ -331,6 +398,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
+@@ -520,6 +593,52 @@ 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);
++
++    /**
++     * Select incoming TURN dataconn
++     *
++     * @param ice			The ICE session.
++     * @param clist			The ICE connection list
++     * @param check_id		The wanted check.
++     */
++    pj_status_t (*select_turn_dataconn)(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;
+ 
+ 
+@@ -636,6 +755,7 @@ struct pj_ice_sess
+     pj_bool_t            valid_pair_found;          /**< First pair found   */
+     pj_status_t		 ice_status;		    /**< Error status.	    */
+     pj_timer_entry	 timer;			    /**< ICE timer.	    */
++    pj_timer_entry	 timer_connect;		    /**< ICE timer tcp timeout*/
+     pj_ice_sess_cb	 cb;			    /**< Callback.	    */
+ 
+     pj_stun_config	 stun_cfg;		    /**< STUN settings.	    */
+@@ -855,6 +975,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.
+  */
+@@ -868,7 +989,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
+@@ -980,6 +1102,55 @@ 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);
++
++/**
++ * Select dataconn from TURN
++ *
++ * @param ice          The ICE session.
++ * @param clist        The ice check list
++ * @param check_id     The wanted check
++ */
++PJ_DECL(void) ice_select_incoming_turn(pj_ice_sess *ice,
++				       pj_ice_sess_checklist *clist,
++				       unsigned check_id);
+ 
+ 
+ /**
+diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h
+index d0af76679..9eb74b35f 100644
+--- a/pjnath/include/pjnath/ice_strans.h
++++ b/pjnath/include/pjnath/ice_strans.h
+@@ -274,6 +274,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;
+ 
+ 
+@@ -289,6 +296,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
+@@ -368,6 +382,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 fff4df885..bfc9c1415 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 fff4fad26..e7f8b84eb 100644
+--- a/pjnath/src/pjnath-test/stun_sock_test.c
++++ b/pjnath/src/pjnath-test/stun_sock_test.c
+@@ -255,8 +255,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);
+@@ -585,7 +586,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 2a4125bc5..cd9e32e25 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"
+@@ -75,7 +79,8 @@ enum timer_type
+ 				     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 +128,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 +140,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);
+@@ -291,7 +299,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;
+ 
+@@ -717,7 +726,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;
+@@ -740,6 +750,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->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id);
+     pj_sockaddr_cp(&lcand->addr, addr);
+@@ -1081,6 +1092,17 @@ 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) {
++	    pj_array_erase(clist->checks, sizeof(clist->checks[0]),
++			   clist->count, i);
++	    --clist->count;
++	    --i;
++	}
+     }
+ 
+     /* Next remove a pair if its local and remote candidates are identical
+@@ -1183,6 +1205,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;
+@@ -1619,6 +1644,43 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
+     return PJ_FALSE;
+ }
+ 
++static void on_tcp_connect_timeout(pj_ice_sess* ice)
++{
++    pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,&ice->timer,
++				   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.id == TIMER_NONE) {
++	/* Reschedule */
++	pj_time_val delay = {
++	    .sec  = 0,
++	    .msec = 1500
++	};
++	pj_time_val_normalize(&delay);
++	pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
++				    &ice->timer, &delay,
++				    TIMER_CONNECTION_TIMEOUT,
++				    ice->grp_lock);
++    }
++}
+ 
+ /* Create checklist by pairing local candidates with remote candidates */
+ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
+@@ -1705,6 +1767,30 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
+ 		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;
++	    }
++
+ 
+ 	    chk->lcand = lcand;
+ 	    chk->rcand = rcand;
+@@ -1749,6 +1835,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;
+ 
+@@ -1761,6 +1848,36 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
+     return PJ_SUCCESS;
+ }
+ 
++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,
+@@ -1771,19 +1888,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, 
+@@ -1831,32 +1946,92 @@ 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_UDP:
++        status = send_connectivity_check(ice, clist, check_id,
++                                         nominate, msg_data);
++        break;
++    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, TIMER_NONE);
++            status = (*ice->cb.wait_tcp_connection)(ice, clist, check_id);
++            if (ice->timer.id != TIMER_NONE) {
++                pj_assert(!"Not expected any timer active");
++            } else {
++                pj_time_val delay = {
++                    .sec  = 0,
++                    .msec = 1500,
++                };
++                pj_time_val_normalize(&delay);
++                pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
++                                                  &ice->timer, &delay,
++                                                  TIMER_CONNECTION_TIMEOUT,
++                                                  ice->grp_lock);
++            }
++            break;
++        }
++        break;
++    case PJ_CAND_TCP_PASSIVE:
++    case PJ_CAND_TCP_SO:
++    default:
++        if (lcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
++            pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
++                                           &ice->timer, TIMER_NONE);
++            status = (*ice->cb.select_turn_dataconn)(ice, clist, check_id);
++            if (ice->timer.id == TIMER_NONE) {
++                pj_time_val delay = {
++                    .sec  = 0,
++                    .msec = 1500
++                };
++                pj_time_val_normalize(&delay);
++                pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
++                                                  &ice->timer, &delay,
++                                                  TIMER_CONNECTION_TIMEOUT,
++                                                  ice->grp_lock);
++            }
++        } else {
++            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;
+     }
+-
+-    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, 
+-	            PJ_SUCCESS);
+     pj_log_pop_indent();
+-    return PJ_SUCCESS;
++    return status;
+ }
+ 
+ 
+@@ -1892,40 +2067,96 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
+     LOG5((ice->obj_name, "Starting checklist periodic check"));
+     pj_log_push_indent();
+ 
++    /* Send STUN Binding request for check with highest priority on
++     * Retry state.
++     */
++
++    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;
++	    }
++	}
++    }
++
++    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.
+      */
+-    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];
++
++	    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;
+ 	    }
+-
+-	    ++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;
+ 	    }
+@@ -1933,14 +2164,14 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
+     }
+ 
+     /* Cannot start check because there's no suitable candidate pair.
+-     */
++    */
+     if (start_count!=0) {
+ 	/* Schedule for next timer */
+ 	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);
+@@ -2181,6 +2412,192 @@ 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;
++
++    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) {
++	    return;
++	}
++    }
++
++    if (status != PJ_SUCCESS) {
++	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
++	on_check_complete(ice, check);
++	return;
++    }
++
++    // TCP is correctly connected. Craft the message to send
++    const pj_ice_sess_cand *lcand = check->lcand;
++    const pj_ice_sess_cand *rcand = check->rcand;
++    if (check->tdata == NULL) {
++	LOG5((ice->obj_name, "Error sending STUN request, empty data"));
++	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;
++
++    pj_ice_sess_comp *comp = find_comp(ice, lcand->comp_id);
++    pj_status_t status_send_msg;
++    // Note that USERNAME and MESSAGE-INTEGRITY will be added by the
++    // STUN session.
++
++    // Initiate STUN transaction to send the request
++    status_send_msg = 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 (status_send_msg == PJ_EBUSY /* EBUSY */) {
++        check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET;
++	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET,
++			status_send_msg);
++	return;
++    }
++    if (status_send_msg == 120033 /* BROKEN PIPE */) {
++	check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY;
++	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY,
++			status_send_msg);
++	return;
++    }
++
++    if ((status_send_msg == 120104 || status_send_msg == 130054)/* CONNECTION RESET BY PEER */
++	&& rcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
++	/**
++	 * 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_send_msg);
++    } else if (status_send_msg != PJ_SUCCESS) {
++	check->tdata = NULL;
++	pjnath_perror(ice->obj_name, "Error sending STUN request", status_send_msg);
++	pj_log_pop_indent();
++	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
++	on_check_complete(ice, check);
++    } else if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
++	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status);
++    } else {
++	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, status);
++    }
++}
++
++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 reseted
++    if (!remote_addr)
++	return;
++
++    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)
++	    return;
++    }
++
++    const pj_ice_sess_cand *rcand = check->rcand;
++    if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
++	check->state = PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY;
++	check_set_state(ice, check,
++			PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, 120104);
++    }
++}
++
++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_ice_sess_check *check =
++	get_current_check_at_state(ice, remote_addr,
++				   PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET,
++				   NULL);
++    if (!check) {
++	return;
++    }
++
++    const pj_ice_sess_cand *rcand = check->rcand;
++    if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
++	check->state = PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS;
++	check_set_state(ice, check,
++			PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS);
++    }
++}
++
++
++void ice_select_incoming_turn(pj_ice_sess *ice,
++			      pj_ice_sess_checklist *clist,
++			      unsigned check_id)
++{
++    check_set_state(ice, &clist->checks[check_id],
++		    PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, PJ_SUCCESS);
++    update_comp_check(ice, clist->checks[check_id].lcand->comp_id,
++		      &clist->checks[check_id]);
++    on_check_complete(ice, &clist->checks[check_id]);
++}
+ 
+ /* This callback is called when outgoing STUN request completed */
+ static void on_stun_request_complete(pj_stun_session *stun_sess,
+@@ -2411,7 +2828,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);
+@@ -2673,7 +3092,9 @@ 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);
+ 
+ 
+@@ -2795,11 +3216,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
+      * 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) 
++	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 3cb350c2a..1cb6a6d14 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  0
+ #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_ice_complete(pj_ice_sess *ice, pj_status_t status);
+@@ -103,6 +112,24 @@ 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_select_turn_dataconn(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. */
+@@ -182,6 +209,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
+ {
+@@ -225,6 +262,12 @@ struct pj_ice_strans
+     pj_bool_t		     destroy_req;/**< Destroy has been called?	*/
+     pj_bool_t		     cb_called;	/**< Init error callback called?*/
+     pj_bool_t		     call_send_cb;/**< Need to call send cb?	*/
++
++    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;
++
+ };
+ 
+ 
+@@ -261,6 +304,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);
+@@ -278,6 +322,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;
+@@ -421,6 +466,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 */
+@@ -428,6 +476,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,
+@@ -481,6 +533,7 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand,
+         || lcand->transport_id != rcand->transport_id
+         || 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)
+     {
+@@ -490,7 +543,6 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand,
+     return PJ_TRUE;
+ }
+ 
+-
+ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
+ 				     pj_ice_strans_comp *comp,
+ 				     unsigned idx,
+@@ -541,6 +593,9 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
+     cand->local_pref = SRFLX_PREF;
+     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);
+@@ -549,8 +604,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;
+ 
+@@ -635,105 +691,154 @@ 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];
+-
+-	    if (max_cand_cnt==0) {
+-		PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
+-		break;
+-	    }
+-
+-	    /* Ignore loopback addresses if cfg->stun.loop_addr is unset */
+-	    if (stun_cfg->loop_addr==PJ_FALSE) {
+-		if (stun_cfg->af == pj_AF_INET() && 
+-		    (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127)
+-		{
+-		    continue;
+-		}
+-		else if (stun_cfg->af == pj_AF_INET6()) {
+-		    pj_in6_addr in6addr = {{0}};
+-		    in6addr.s6_addr[15] = 1;
+-		    if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr,
+-				  sizeof(in6addr))==0)
+-		    {
+-			continue;
+-		    }
+-		}
+-	    }
+-
+-	    /* 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 = HOST_PREF;
+-	    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 (cand_duplicate) {
+-		PJ_LOG(4, (ice_st->obj_name,
+-		       "Comp %d: host candidate %s (tpid=%d) is a duplicate",
+-		       comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo,
+-		       sizeof(addrinfo), 3), cand->transport_id));
+-
+-		pj_bzero(&cand->addr, sizeof(cand->addr));
+-		pj_bzero(&cand->base_addr, sizeof(cand->base_addr));
+-		continue;
+-	    } else {
+-		comp->cand_cnt+=1;
+-		cand_cnt++;
+-		max_cand_cnt--;
+-	    }
+-            
+-	    pj_ice_calc_foundation(ice_st->pool, &cand->foundation,
+-				   cand->type, &cand->base_addr);
+-
+-	    /* 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);
+-	    }
+-
+-	    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));
+-	}
++	    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);
++            }
++        }
+     }
+ 
+     return status;
+ }
+ 
++static pj_bool_t add_local_candidate(pj_ice_sess_cand *cand,
++                                     unsigned idx,
++                                     unsigned i,
++                                     unsigned *cand_cnt,
++                                     unsigned *max_cand_cnt,
++                                     pj_stun_sock_info stun_sock_info,
++                                     pj_ice_strans *ice_st,
++                                     pj_ice_strans_comp *comp,
++                                     pj_ice_cand_transport transport)
++{
++    unsigned j;
++    pj_bool_t cand_duplicate = PJ_FALSE;
++    char addrinfo[PJ_INET6_ADDRSTRLEN+10];
++    const pj_sockaddr *addr = &stun_sock_info.aliases[i];
++    pj_ice_strans_stun_cfg *stun_cfg = &ice_st->cfg.stun_tp[idx];
++
++
++    if (*max_cand_cnt==0) {
++	PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
++	return !PJ_SUCCESS;
++    }
++
++    /* 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;
++            }
++        }
++    }
++
++    /* Ignore IPv6 link-local address, unless it is the default
++     * address (first alias).
++     */
++    if (stun_cfg->af == pj_AF_INET6() && i != 0) {
++	const pj_in6_addr *a = &addr->ipv6.sin6_addr;
++	if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80)
++            return PJ_SUCCESS;
++    }
++
++    cand = &comp->cand_list[comp->cand_cnt];
++
++    cand->type         = PJ_ICE_CAND_TYPE_HOST;
++    cand->status       = PJ_SUCCESS;
++    cand->local_pref   = HOST_PREF;
++    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;
++        }
++    }
++
++    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.
+@@ -1207,6 +1312,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
+     ice_cb.on_ice_complete = &on_ice_complete;
+     ice_cb.on_rx_data = &ice_rx_data;
+     ice_cb.on_tx_pkt = &ice_tx_pkt;
++#if PJ_HAS_TCP
++    ice_cb.wait_tcp_connection      = &ice_wait_tcp_connection;
++    ice_cb.select_turn_dataconn     = &ice_select_turn_dataconn;
++    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,
+@@ -1282,7 +1393,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;
+ 	}
+@@ -1668,7 +1780,31 @@ 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];
+-    
++
++    pj_bool_t add_header = def_cand->transport != PJ_CAND_UDP;
++    /* 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);
+ 
+@@ -1730,6 +1866,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;
+ 	}
+ 
+@@ -1842,7 +1983,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.
+@@ -1936,6 +2092,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,
+@@ -1969,9 +2148,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;
+@@ -2031,6 +2214,237 @@ 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;
++
++    ice_st->is_pending = PJ_FALSE;
++    ice_sess_on_peer_connection(ice_st->ice,
++                                data->transport_id, status, remote_addr);
++}
++
++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;
++
++    ice_sess_on_peer_reset_connection(ice_st->ice,
++                                      data->transport_id, remote_addr);
++}
++
++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;
++
++    ice_sess_on_peer_packet(ice_st->ice, data->transport_id, remote_addr);
++}
++
++#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_select_turn_dataconn(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];
++
++    for (int i=0; i<ice_st->cfg.turn_tp_cnt; ++i) {
++        pj_turn_session_info info;
++        pj_turn_sock_get_info(st_comp->turn[i].sock, &info);
++        if (st_comp->turn[i].sock
++            && pj_turn_sock_has_dataconn(st_comp->turn[i].sock, &rcand->addr)) {
++            ice_select_incoming_turn(ice, clist, check_id);
++            return PJ_SUCCESS; // Already connected via TURN
++        }
++    }
++
++    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;
++        ice_st->is_pending = PJ_FALSE;
++        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.
+  */
+@@ -2318,6 +2732,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;
+@@ -2358,14 +2776,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 (status != PJ_SUCCESS) {
+-	    ice_st_perror(comp->ice_st,
+-			  "Error processing packet from TURN relay",
+-			  status);
++	if (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && pkt_len > 0) {
++	    unsigned parsed = 0;
++	    pj_status_t status;
++
++	    do {
++		pj_uint16_t leftover = pkt_len - parsed;
++		pj_uint8_t *current_packet = ((pj_uint8_t *)(pkt)) + parsed;
++
++		/**
++		 * RFC6544, the packet is wrapped into a packet following the
++		 * RFC4571
++		 */
++		pj_bool_t store_remaining = PJ_TRUE;
++		if (comp->ice_st->rx_buffer_size ||
++		    comp->ice_st->rx_wanted_size)
++		{
++		    /* a single packet left to process */
++		    if (comp->ice_st->rx_buffer_size == 1) {
++			/* 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);
+ 	}
+     }
+ 
+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..ed17b904f 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;
+@@ -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..c4519f82e 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,125 +480,6 @@ 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;
+-
+-    /* 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);
+-	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));
+-	    }
+-	}
+-    }
+-
+-    /* 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;
+-
+-    /* Create more useful information string about this transport */
+-#if 0
+-    {
+-	pj_sockaddr bound_addr;
+-	int addr_len = sizeof(bound_addr);
+-
+-	status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, 
+-				     &addr_len);
+-	if (status != PJ_SUCCESS)
+-	    goto on_error;
+-
+-	stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10);
+-	pj_sockaddr_print(&bound_addr, stun_sock->info, 
+-			  PJ_INET6_ADDRSTRLEN, 3);
+-    }
+-#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));
+-    }
+-
+     /* Create STUN session */
+     {
+ 	pj_stun_session_cb sess_cb;
+@@ -356,11 +491,58 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *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;
++					&stun_sock->stun_sess,
++					conn_type);
++	if (status != PJ_SUCCESS) {
++	    pj_stun_sock_destroy(stun_sock);
++	    return status;
++	}
+     }
+ 
++    pj_stun_sock_alloc(stun_sock);
++
++    /* 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;
++
++    pj_grp_lock_acquire(stun_sock->grp_lock);
++
++    /* TURN session may have already been destroyed here.
++     * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557).
++     */
++    if (!stun_sock->stun_sess) {
++	sess_fail(stun_sock, PJ_STUN_SESS_DESTROYED, status);
++	pj_grp_lock_release(stun_sock->grp_lock);
++	return PJ_FALSE;
++    }
++
++    if (status != PJ_SUCCESS) {
++	sess_fail(stun_sock, PJ_STUN_TCP_CONNECT_ERROR, status);
++	pj_grp_lock_release(stun_sock->grp_lock);
++	return PJ_FALSE;
++    }
++
++    if (stun_sock->conn_type != PJ_STUN_TP_UDP)
++	PJ_LOG(5,(stun_sock->obj_name, "TCP connected"));
++
++    /* Start asynchronous read operations */
++    pj_status_t result;
++    result = pj_activesock_start_recvfrom(asock, stun_sock->pool,
++					  stun_sock->cfg.max_pkt_size, 0);
++    if (result != PJ_SUCCESS)
++	return PJ_FALSE;
++
+     /* Associate us with the STUN session */
+     pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock);
+ 
+@@ -369,24 +551,303 @@ 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) {
++		    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,
+@@ -526,6 +987,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 +1115,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 +1141,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 +1255,241 @@ 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 (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 (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 (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 (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 (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 (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_status_t result = pj_activesock_start_read(asock, stun_sock->pool,
++                                                  stun_sock->cfg.max_pkt_size, 0);
++    if (result != PJ_SUCCESS) {
++        return PJ_FALSE;
++    };
++
++    pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess);
++    if (!cb->on_peer_connection) {
++        return PJ_FALSE;
++    }
++
++    // Get remote connected address
++    pj_sockaddr_t* remote_addr = NULL;
++    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;
++    (cb->on_peer_connection)(stun_sock->stun_sess, status, remote_addr);
++    return PJ_TRUE;
++}
++
++#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 +1500,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 +1514,29 @@ 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 (!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 +1676,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 +1687,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 +1728,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 a378b8672..88985af69 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..9b69a6a21 100644
+--- a/pjnath/src/pjnath/turn_sock.c
++++ b/pjnath/src/pjnath/turn_sock.c
+@@ -1766,3 +1766,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 474a8d07c..9257f07a4 100644
+--- a/pjsip/src/pjsua-lib/pjsua_core.c
++++ b/pjsip/src/pjsua-lib/pjsua_core.c
+@@ -1548,7 +1548,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.24.1
+
diff --git a/contrib/src/pjproject/0001-win-config.patch b/contrib/src/pjproject/0001-win-config.patch
new file mode 100644
index 0000000000000000000000000000000000000000..495d31fcfd873f29e3f032c98a2379d8d571dc14
--- /dev/null
+++ b/contrib/src/pjproject/0001-win-config.patch
@@ -0,0 +1,25 @@
+From 205d5003e099a62dcb7504dab20ed575081eb597 Mon Sep 17 00:00:00 2001
+From: jrun <darwinskernel@gmail.com>
+Date: Wed, 4 Mar 2020 09:34:42 -0500
+Subject: [PATCH 1/3] win config
+
+---
+ pjlib/include/pj/config_site.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h
+index 47dd40ad5..5d4e3d10d 100644
+--- a/pjlib/include/pj/config_site.h
++++ b/pjlib/include/pj/config_site.h
+@@ -21,3 +21,8 @@
+ #define PJ_ICE_MAX_TURN                         4
+ #define PJ_ICE_COMP_BITS                        2
+ #define PJ_ICE_MAX_CHECKS                       1024
++
++/*
++ * WINDOWS settings.
++ */
++#define THIRD_PARTY_MEDIA                       0
+-- 
+2.24.1
+
diff --git a/contrib/src/pjproject/0002-rfc2466.patch b/contrib/src/pjproject/0002-rfc2466.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1080e494fbdf444d6a23bf5bed1c6485df2dadb3
--- /dev/null
+++ b/contrib/src/pjproject/0002-rfc2466.patch
@@ -0,0 +1,207 @@
+From 753c3c04da7465622d6f0ce2efebdf428fba536f Mon Sep 17 00:00:00 2001
+From: jrun <darwinskernel@gmail.com>
+Date: Thu, 27 Feb 2020 11:34:40 -0500
+Subject: [PATCH 2/9] rfc2466
+
+---
+ pjnath/src/pjnath/ice_session.c | 170 ++++++++++++++++++++++++++++++++
+ 1 file changed, 170 insertions(+)
+
+diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
+index 960c55156..a50c21c94 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[] =
+ {
+@@ -712,6 +727,138 @@ 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(): ");
++	    return;
++	}
++
++	nlmsg_ptr = (struct nlmsghdr *) read_buffer;
++	nlmsg_len = rtn;
++
++	if (nlmsg_len < sizeof (struct nlmsghdr)) {
++	    perror ("Received an uncomplete netlink packet");
++	    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");
++				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");
++	}
++    }
++#endif
++}
+ 
+ /*
+  * Add ICE candidate
+@@ -729,6 +876,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.24.1
+
diff --git a/contrib/src/pjproject/win32_vs_gnutls.patch b/contrib/src/pjproject/0002-win-vs-gnutls.patch
similarity index 85%
rename from contrib/src/pjproject/win32_vs_gnutls.patch
rename to contrib/src/pjproject/0002-win-vs-gnutls.patch
index 01c12e5e082979f2ea00b839e537d3c1be45a000..c2fccd54d4cb5716d1a87c209a14ef8718aba576 100644
--- a/contrib/src/pjproject/win32_vs_gnutls.patch
+++ b/contrib/src/pjproject/0002-win-vs-gnutls.patch
@@ -1,3 +1,16 @@
+From c8dfc738005dbc44975855cca8ac626af0f4ca4d Mon Sep 17 00:00:00 2001
+From: jrun <darwinskernel@gmail.com>
+Date: Thu, 27 Feb 2020 14:40:07 -0500
+Subject: [PATCH 2/3] win vs gnutls
+
+---
+ pjlib/build/pjlib.vcxproj      | 12 +++++++++---
+ pjlib/include/pj/config_site.h |  3 +++
+ pjmedia/build/pjmedia.vcxproj  | 12 +++++++++---
+ 3 files changed, 21 insertions(+), 6 deletions(-)
+
+diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj
+index f2d6fb42c..dee805a7c 100644
 --- a/pjlib/build/pjlib.vcxproj
 +++ b/pjlib/build/pjlib.vcxproj
 @@ -309,14 +309,17 @@
@@ -35,34 +48,19 @@
    </ItemDefinitionGroup>
    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Static|Win32'">
      <ClCompile>
---- a/pjlib/include/pj/config.h
-+++ b/pjlib/include/pj/config.h
-@@ -570,6 +570,7 @@
-  * that this will increase the footprint of the libraries since it
-  * tracks the filename and line number of each functions.
-  */
-+#define PJ_OS_HAS_CHECK_STACK		0
- #ifndef PJ_OS_HAS_CHECK_STACK
- #	define PJ_OS_HAS_CHECK_STACK		0
- #endif
-@@ -919,7 +920,7 @@
-  * Default: 0 (for now)
+diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h
+index 5d4e3d10d..a37d6d88c 100644
+--- a/pjlib/include/pj/config_site.h
++++ b/pjlib/include/pj/config_site.h
+@@ -26,3 +26,6 @@
+  * WINDOWS settings.
   */
- #ifndef PJ_HAS_SSL_SOCK
--#  define PJ_HAS_SSL_SOCK	    0
-+#  define PJ_HAS_SSL_SOCK	    1
- #endif
- 
- 
-@@ -945,7 +946,7 @@
- #   if PJ_HAS_SSL_SOCK==0
- #	define PJ_SSL_SOCK_IMP		    PJ_SSL_SOCK_IMP_NONE
- #   else
--#	define PJ_SSL_SOCK_IMP		    PJ_SSL_SOCK_IMP_OPENSSL
-+#	define PJ_SSL_SOCK_IMP		    PJ_SSL_SOCK_IMP_GNUTLS
- #   endif
- #endif
- 
+ #define THIRD_PARTY_MEDIA                       0
++#define PJ_OS_HAS_CHECK_STACK                   0
++#define PJ_HAS_SSL_SOCK                         1
++#define PJ_SSL_SOCK_IMP                         PJ_SSL_SOCK_IMP_GNUTLS
+diff --git a/pjmedia/build/pjmedia.vcxproj b/pjmedia/build/pjmedia.vcxproj
+index e596939d0..0c7108b83 100644
 --- a/pjmedia/build/pjmedia.vcxproj
 +++ b/pjmedia/build/pjmedia.vcxproj
 @@ -307,14 +307,17 @@
@@ -101,5 +99,5 @@
    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
      <ClCompile>
 -- 
-2.19.0.windows.1
+2.24.1
 
diff --git a/contrib/src/pjproject/0003-add-tcp-keep-alive.patch b/contrib/src/pjproject/0003-add-tcp-keep-alive.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9d8b846a5bd372a0fd5aba0e4dc65e0c6ab5dd6b
--- /dev/null
+++ b/contrib/src/pjproject/0003-add-tcp-keep-alive.patch
@@ -0,0 +1,336 @@
+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      | 10 +++++++
+ 9 files changed, 149 insertions(+)
+
+diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
+index 4daf298c5..095a38335 100644
+--- a/pjlib/include/pj/sock.h
++++ b/pjlib/include/pj/sock.h
+@@ -313,6 +313,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;
+@@ -343,9 +348,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);
+ 
+@@ -379,9 +396,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 e416991de..0e9bfdbe9 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 693f3af62..6beb28590 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 876c3287a..34baebcee 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 39c197c29..d44e3e896 100644
+--- a/pjnath/include/pjnath/ice_session.h
++++ b/pjnath/include/pjnath/ice_session.h
+@@ -639,6 +639,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 9eb74b35f..1269442cd 100644
+--- a/pjnath/include/pjnath/ice_strans.h
++++ b/pjnath/include/pjnath/ice_strans.h
+@@ -192,6 +192,13 @@ typedef struct pj_ice_strans_cb
+ 			       pj_ice_strans_op op,
+ 			       pj_status_t status);
+ 
++    /**
++     * 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 a50c21c94..4ba44f38b 100644
+--- a/pjnath/src/pjnath/ice_session.c
++++ b/pjnath/src/pjnath/ice_session.c
+@@ -1435,6 +1435,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 == 120032) {
++	    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 8ba2172e5..4adc9bc70 100644
+--- a/pjnath/src/pjnath/ice_strans.c
++++ b/pjnath/src/pjnath/ice_strans.c
+@@ -98,6 +98,7 @@ static pj_uint16_t GETVAL16H(const pj_uint8_t *buf1, const pj_uint8_t *buf2)
+ //////////////////////////////////////////////////////////////////////////////
+ 
+ /* ICE callbacks */
++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,
+@@ -1322,6 +1323,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
+     ice_cb.select_turn_dataconn     = &ice_select_turn_dataconn;
+     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! */
+@@ -1938,6 +1940,14 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st,
+     		     dst_addr_len, PJ_TRUE, PJ_FALSE);
+ }
+ 
++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
+-- 
+2.24.1
+
diff --git a/contrib/src/pjproject/win_vs2017_props.patch b/contrib/src/pjproject/0003-win-vs2017-props.patch
similarity index 66%
rename from contrib/src/pjproject/win_vs2017_props.patch
rename to contrib/src/pjproject/0003-win-vs2017-props.patch
index 6ccc60359a9373ceeac868a1ad3a70496276ab99..6c306c3dddde534ae57c8f5669ce0ce1f72ec89c 100644
--- a/contrib/src/pjproject/win_vs2017_props.patch
+++ b/contrib/src/pjproject/0003-win-vs2017-props.patch
@@ -1,43 +1,64 @@
---- 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>
-         
---- 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.10.2.windows.1
-
+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 | 4 ++--
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/build/vs/pjproject-vs14-common-config.props b/build/vs/pjproject-vs14-common-config.props
+index de8848ff5..6aa0276ba 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 526f6c925..198282b90 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>
+@@ -35,4 +35,4 @@
+       <Value>$(VSVer)</Value>
+     </BuildMacro>
+   </ItemGroup>
+-</Project>
+\ No newline at end of file
++</Project>
+-- 
+2.24.1
+
diff --git a/contrib/src/pjproject/0004-multiple_listeners.patch b/contrib/src/pjproject/0004-multiple_listeners.patch
new file mode 100644
index 0000000000000000000000000000000000000000..58494dba77be407ebf68ff1430cdbabc54e0154c
--- /dev/null
+++ b/contrib/src/pjproject/0004-multiple_listeners.patch
@@ -0,0 +1,64 @@
+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 | 24 +++++++++++++++++++-----
+ 1 file changed, 19 insertions(+), 5 deletions(-)
+
+diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
+index 24b59153b..a336a8648 100644
+--- a/pjsip/src/pjsip/sip_transport.c
++++ b/pjsip/src/pjsip/sip_transport.c
+@@ -1471,10 +1471,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) {
+@@ -2327,10 +2333,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
+ 	    }
+ 	}
+ 
++        unsigned flag = pjsip_transport_get_flag_from_type(type);
+ 	if (tp_ref == NULL &&
+ 	    (!sel || sel->disable_connection_reuse == PJ_FALSE))
+ 	{
+-	    unsigned flag = pjsip_transport_get_flag_from_type(type);
+ 	    const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;
+ 
+ 
+@@ -2429,6 +2435,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.24.1
+
diff --git a/contrib/src/pjproject/0005-fix_ebusy_turn.patch b/contrib/src/pjproject/0005-fix_ebusy_turn.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9bcdf129bb35beb4b78d732517a84c10b512780a
--- /dev/null
+++ b/contrib/src/pjproject/0005-fix_ebusy_turn.patch
@@ -0,0 +1,582 @@
+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 5/9] fix_ebusy_turn
+
+---
+ pjnath/include/pjnath/turn_session.h | 126 ++++++++++++++++++++-------
+ pjnath/include/pjnath/turn_sock.h    |  27 ++++++
+ pjnath/src/pjnath/ice_strans.c       |   2 +-
+ pjnath/src/pjnath/turn_session.c     |  39 ++++++---
+ pjnath/src/pjnath/turn_sock.c        | 102 +++++++++++++++++++++-
+ 5 files changed, 248 insertions(+), 48 deletions(-)
+
+diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h
+index 956f57bf0..04add93db 100644
+--- a/pjnath/include/pjnath/turn_session.h
++++ b/pjnath/include/pjnath/turn_session.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,7 +15,7 @@
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  */
+ #ifndef __PJNATH_TURN_SESSION_H__
+ #define __PJNATH_TURN_SESSION_H__
+@@ -50,10 +50,10 @@ or to support new transport types to TURN, such as TLS.
+ These steps describes how to use the TURN session:
+ 
+  - <b>Creating the session</b>:\n
+-   use #pj_turn_session_create() to create the session. 
++   use #pj_turn_session_create() to create the session.
+ 
+  - <b>Configuring credential</b>:\n
+-   all TURN operations requires the use of authentication (it uses STUN 
++   all TURN operations requires the use of authentication (it uses STUN
+    long term autentication method). Use #pj_turn_session_set_credential()
+    to configure the TURN credential to be used by the session.
+ 
+@@ -62,8 +62,8 @@ These steps describes how to use the TURN session:
+    Allocate request (with pj_turn_session_alloc()). This function will
+    resolve the TURN server using DNS SRV resolution if the \a resolver
+    is set. The server resolution process will complete asynchronously,
+-   and application will be notified in \a on_state() callback of the 
+-   #pj_turn_session_cb structurewith the session state set to 
++   and application will be notified in \a on_state() callback of the
++   #pj_turn_session_cb structurewith the session state set to
+    PJ_TURN_STATE_RESOLVED.
+ 
+  - <b>Creating allocation</b>:\n
+@@ -75,40 +75,40 @@ These steps describes how to use the TURN session:
+ 
+  - <b>Getting the allocation result</b>:\n
+    if allocation is successful, the session state will progress to
+-   \a PJ_TURN_STATE_READY, otherwise the state will be 
++   \a PJ_TURN_STATE_READY, otherwise the state will be
+    \a PJ_TURN_STATE_DEALLOCATED or higher. Session state progression is
+-   reported in the \a on_state() callback of the #pj_turn_session_cb 
++   reported in the \a on_state() callback of the #pj_turn_session_cb
+    structure. On successful allocation, application may retrieve the
+    allocation info by calling #pj_turn_session_get_info().
+ 
+  - <b>Sending data through the relay</b>.\n
+-   Once allocation has been created, client may send data to any remote 
++   Once allocation has been created, client may send data to any remote
+    endpoints (called peers in TURN terminology) via the "relay port". It does
+-   so by calling #pj_turn_session_sendto(), giving the peer address 
++   so by calling #pj_turn_session_sendto(), giving the peer address
+    in the function argument. But note that at this point peers are not allowed
+    to send data towards the client (via the "relay port") before permission is
+    installed for that peer.
+ 
+  - <b>Creating permissions</b>.\n
+-   Permission needs to be created in the TURN server so that a peer can send 
++   Permission needs to be created in the TURN server so that a peer can send
+    data to the client via the relay port (a peer in this case is identified by
+    its IP address). Without this, when the TURN server receives data from the
+    peer in the "relay port", it will drop this data. Create the permission by
+    calling #pj_turn_session_set_perm(), specifying the peer IP address in the
+-   argument (the port part of the address is ignored). More than one IP 
++   argument (the port part of the address is ignored). More than one IP
+    addresses may be specified.
+ 
+  - <b>Receiving data from peers</b>.\n
+-   Once permission has been installed for the peer, any data received by the 
+-   TURN server (from that peer) in the "relay port" will be relayed back to 
++   Once permission has been installed for the peer, any data received by the
++   TURN server (from that peer) in the "relay port" will be relayed back to
+    client by the server, and application will be notified via \a on_rx_data
+    callback of the #pj_turn_session_cb.
+ 
+  - <b>Using ChannelData</b>.\n
+-   TURN provides optimized framing to the data by using ChannelData 
++   TURN provides optimized framing to the data by using ChannelData
+    packetization. The client activates this format for the specified peer by
+    calling #pj_turn_session_bind_channel(). Data sent or received to/for
+-   this peer will then use ChannelData format instead of Send or Data 
++   this peer will then use ChannelData format instead of Send or Data
+    Indications.
+ 
+  - <b>Refreshing the allocation, permissions, and channel bindings</b>.\n
+@@ -125,15 +125,15 @@ These steps describes how to use the TURN session:
+ 
+  */
+ 
+-/** 
++/**
+  * Opaque declaration for TURN client session.
+  */
+ typedef struct pj_turn_session pj_turn_session;
+ 
+ 
+-/** 
+- * TURN transport types, which will be used both to specify the connection 
+- * type for reaching TURN server and the type of allocation transport to be 
++/**
++ * TURN transport types, which will be used both to specify the connection
++ * type for reaching TURN server and the type of allocation transport to be
+  * requested to server (the REQUESTED-TRANSPORT attribute).
+  */
+ typedef enum pj_turn_tp_type
+@@ -246,7 +246,7 @@ typedef struct pj_turn_session_cb
+      * @param addr_len	Length of the destination address.
+      *
+      * @return		The callback should return the status of the
+-     *			send operation. 
++     *			send operation.
+      */
+     pj_status_t (*on_send_pkt)(pj_turn_session *sess,
+ 			       const pj_uint8_t *pkt,
+@@ -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
+@@ -268,7 +297,7 @@ typedef struct pj_turn_session_cb
+      * @param addr_len	Length of the destination address.
+      *
+      * @return		The callback should return the status of the
+-     *			send operation. 
++     *			send operation.
+      */
+     pj_status_t (*on_stun_send_pkt)(pj_turn_session *sess,
+ 			       	    const pj_uint8_t *pkt,
+@@ -277,7 +306,7 @@ typedef struct pj_turn_session_cb
+ 			       	    unsigned addr_len);
+ 
+     /**
+-     * Notification when peer address has been bound successfully to 
++     * Notification when peer address has been bound successfully to
+      * a channel number.
+      *
+      * This callback is optional since the nature of this callback is
+@@ -320,7 +349,7 @@ typedef struct pj_turn_session_cb
+      * @param old_state	The previous state of the session.
+      * @param new_state	The current state of the session.
+      */
+-    void (*on_state)(pj_turn_session *sess, 
++    void (*on_state)(pj_turn_session *sess,
+ 		     pj_turn_state_t old_state,
+ 		     pj_turn_state_t new_state);
+ 
+@@ -365,7 +394,7 @@ typedef struct pj_turn_session_cb
+ 
+ 
+ /**
+- * Allocation parameter, which can be given when application calls 
++ * Allocation parameter, which can be given when application calls
+  * pj_turn_session_alloc() to allocate relay address in the TURN server.
+  * Application should call pj_turn_alloc_param_default() to initialize
+  * this structure with the default values.
+@@ -505,7 +534,7 @@ PJ_DECL(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm);
+  * @param dst	Destination parameter.
+  * @param src	Source parameter.
+  */
+-PJ_DECL(void) pj_turn_alloc_param_copy(pj_pool_t *pool, 
++PJ_DECL(void) pj_turn_alloc_param_copy(pj_pool_t *pool,
+ 				       pj_turn_alloc_param *dst,
+ 				       const pj_turn_alloc_param *src);
+ 
+@@ -703,7 +732,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess,
+ 
+ 
+ /**
+- * Set credential to be used to authenticate against TURN server. 
++ * Set credential to be used to authenticate against TURN server.
+  * Application must call this function before sending Allocate request
+  * with pj_turn_session_alloc().
+  *
+@@ -724,7 +753,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess,
+  * used with pj_turn_session_set_credential() before calling this function.
+  *
+  * This function will complete asynchronously, and the application will be
+- * notified about the allocation result in \a on_state() callback. The 
++ * notified about the allocation result in \a on_state() callback. The
+  * TURN session state will move to PJ_TURN_STATE_READY if allocation is
+  * successful, and PJ_TURN_STATE_DEALLOCATING or greater state if allocation
+  * has failed.
+@@ -768,7 +797,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess,
+ 
+ 
+ /**
+- * Send a data to the specified peer address via the TURN relay. This 
++ * 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.
+@@ -777,7 +806,7 @@ PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess,
+  * 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 
++ * ultimately call \a on_send_pkt() callback to request the application
+  * to actually send the packet containing the data to the TURN server.
+  *
+  * @param sess		The TURN client session.
+@@ -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
+@@ -907,4 +972,3 @@ PJ_END_DECL
+ 
+ 
+ #endif	/* __PJNATH_TURN_SESSION_H__ */
+-
+diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h
+index 35388809f..645c414fe 100644
+--- a/pjnath/include/pjnath/turn_sock.h
++++ b/pjnath/include/pjnath/turn_sock.h
+@@ -603,6 +603,33 @@ PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock,
+ 					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_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);
++
+ /**
+  * 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/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
+index 4adc9bc70..643bbbac9 100644
+--- a/pjnath/src/pjnath/ice_strans.c
++++ b/pjnath/src/pjnath/ice_strans.c
+@@ -1937,7 +1937,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st,
+ {
+     ice_st->call_send_cb = PJ_TRUE;
+     return send_data(ice_st, comp_id, data, data_len, dst_addr,
+-    		     dst_addr_len, PJ_TRUE, PJ_FALSE);
++		     dst_addr_len, PJ_TRUE, PJ_FALSE);
+ }
+ 
+ static void on_ice_destroy(pj_ice_sess *ice)
+diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
+index 88985af69..12738c8e7 100644
+--- a/pjnath/src/pjnath/turn_session.c
++++ b/pjnath/src/pjnath/turn_session.c
+@@ -971,10 +971,21 @@ 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)
++                                            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;
+@@ -1011,7 +1022,8 @@ 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;
+     }
+ 
+@@ -1039,9 +1051,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,
+-				      sess->srv_addr,
+-				      pj_sockaddr_get_len(sess->srv_addr));
++        status = sess->cb.on_send_pkt2(sess, sess->tx_pkt, total_len,
++                                       sess->srv_addr,
++                                       pj_sockaddr_get_len(sess->srv_addr),
++                                       sent, pkt_len);
+ 
+     } else {
+ 	/* Use Send Indication. */
+@@ -1079,10 +1092,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, 
+-				      (unsigned)send_ind_len,
+-				      sess->srv_addr,
+-				      pj_sockaddr_get_len(sess->srv_addr));
++        status = sess->cb.on_send_pkt2(sess, sess->tx_pkt,
++                                       (unsigned)send_ind_len,
++                                       sess->srv_addr,
++                                       pj_sockaddr_get_len(sess->srv_addr),
++                                       sent, pkt_len);
+     }
+ 
+ on_return:
+@@ -1090,7 +1104,6 @@ on_return:
+     return status;
+ }
+ 
+-
+ /**
+  * Bind a peer address to a channel number.
+  */
+diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
+index 9b69a6a21..bacc80859 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;
+ };
+ 
+ 
+@@ -111,6 +115,13 @@ static pj_status_t turn_on_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 pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess,
+ 				    	 const pj_uint8_t *pkt,
+ 				    	 unsigned pkt_len,
+@@ -182,8 +193,8 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock,
+ 				       pj_status_t status,
+ 				       pj_size_t *remainder);
+ static pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock,
+-			      	       pj_ioqueue_op_key_t *send_key,
+-			      	       pj_ssize_t sent);
++                                       pj_ioqueue_op_key_t *send_key,
++                                       pj_ssize_t sent);
+ static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock,
+ 					      pj_status_t status);
+ static void dataconn_cleanup(tcp_data_conn_t *conn);
+@@ -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.
+  */
+@@ -1043,7 +1071,75 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
+ 				    unsigned dst_addr_len)
+ {
+     return send_pkt(sess, PJ_FALSE, pkt, pkt_len,
+-    		    dst_addr, dst_addr_len);
++                    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,
+-- 
+2.24.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
new file mode 100644
index 0000000000000000000000000000000000000000..0de4f7ecf10982cbd0342d15342055cf6d14270f
--- /dev/null
+++ b/contrib/src/pjproject/0006-ignore_ipv6_on_transport_check.patch
@@ -0,0 +1,28 @@
+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 6/9] 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 e19b90fee..62a21526c 100644
+--- a/pjsip/src/pjsip/sip_transport.c
++++ b/pjsip/src/pjsip/sip_transport.c
+@@ -2248,7 +2248,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.24.1
+
diff --git a/contrib/src/pjproject/0007-pj_ice_sess.patch b/contrib/src/pjproject/0007-pj_ice_sess.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6f6263582358d6cf31a2b3b202da62ba5c83b2bb
--- /dev/null
+++ b/contrib/src/pjproject/0007-pj_ice_sess.patch
@@ -0,0 +1,42 @@
+From 02c53ba6beb9e9faeb59059bf466e444dfdb14a6 Mon Sep 17 00:00:00 2001
+From: jrun <darwinskernel@gmail.com>
+Date: Thu, 20 Feb 2020 12:33:47 -0500
+Subject: [PATCH 7/9] pj_ice_sess
+
+---
+ pjnath/include/pjnath/ice_strans.h | 2 ++
+ pjnath/src/pjnath/ice_strans.c     | 5 +++++
+ 2 files changed, 7 insertions(+)
+
+diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h
+index 1269442cd..41f3b6297 100644
+--- a/pjnath/include/pjnath/ice_strans.h
++++ b/pjnath/include/pjnath/ice_strans.h
+@@ -1054,6 +1054,8 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st,
+ 					   const pj_sockaddr_t *dst_addr,
+ 					   int dst_addr_len);
+ 
++PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans   *ice_st);
++
+ 
+ /**
+  * @}
+diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
+index 643bbbac9..603b75121 100644
+--- a/pjnath/src/pjnath/ice_strans.c
++++ b/pjnath/src/pjnath/ice_strans.c
+@@ -1949,6 +1949,11 @@ static void on_ice_destroy(pj_ice_sess *ice)
+     }
+ }
+ 
++PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess( pj_ice_strans *ice_st )
++{
++    return ice_st->ice;
++}
++
+ /*
+  * Callback called by ICE session when ICE processing is complete, either
+  * successfully or with failure.
+-- 
+2.24.1
+
diff --git a/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch b/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b0071dbdeb4142b7a402e6858b4397bbd9255d1b
--- /dev/null
+++ b/contrib/src/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
@@ -0,0 +1,85 @@
+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 8/9] 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 aaefc80e8..e8b929e7a 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.24.1
+
diff --git a/contrib/src/pjproject/0009-add-config-site.patch b/contrib/src/pjproject/0009-add-config-site.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9891484d3cfdb51201efbfd6da964b8599c7a8e0
--- /dev/null
+++ b/contrib/src/pjproject/0009-add-config-site.patch
@@ -0,0 +1,42 @@
+From e657b352dba5b23c82fa29028e7ff4eb19a7dd50 Mon Sep 17 00:00:00 2001
+From: jrun <darwinskernel@gmail.com>
+Date: Thu, 27 Feb 2020 13:44:47 -0500
+Subject: [PATCH 9/9] add config site
+
+---
+ pjlib/include/pj/config_site.h | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+ create mode 100644 pjlib/include/pj/config_site.h
+
+diff --git a/pjlib/include/pj/config_site.h b/pjlib/include/pj/config_site.h
+new file mode 100644
+index 000000000..47dd40ad5
+--- /dev/null
++++ b/pjlib/include/pj/config_site.h
+@@ -0,0 +1,23 @@
++#include "config_site_sample.h"
++
++/*
++* PJLIB settings.
++*/
++#define PJ_HAS_IPV6                             1
++#define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION   1
++
++/*
++* PJSIP settings.
++*/
++#define PJSIP_MAX_PKT_LEN                       8000
++#define PJSIP_TRANSPORT_SERVER_IDLE_TIME        3
++
++/*
++* PJNAT settings.
++*/
++#define PJ_ICE_MAX_CAND                         256
++#define PJ_ICE_ST_MAX_CAND                      32
++#define PJ_ICE_MAX_STUN                         6
++#define PJ_ICE_MAX_TURN                         4
++#define PJ_ICE_COMP_BITS                        2
++#define PJ_ICE_MAX_CHECKS                       1024
+-- 
+2.24.1
+
diff --git a/contrib/src/pjproject/SHA512SUMS b/contrib/src/pjproject/SHA512SUMS
index 23d0102fe341144a8101b9e3d9d56e4a1a0522d3..54e6366f11adf05a3926d2bca543192731eb6a56 100644
--- a/contrib/src/pjproject/SHA512SUMS
+++ b/contrib/src/pjproject/SHA512SUMS
@@ -1 +1 @@
-a9433b47294434288d61524dea556687fb02137ed56a10e8e2ba85d7888a2ca2a5bea4ae4a9ad008a4c208c5ec53fe364b10ed14481700d6aa8b9b6137e5e9ee  pjproject-5dfa75be7d69047387f9b0436dd9492bbbf03fe4.tar.gz
\ No newline at end of file
+a67f083df175b536b4e6a7b7fe39e07d3ee805d6917ec64a50694542a7455c33a100889191044ab3fa679b6656774a6be045621aa53510b5f04cdde9ddd59893  pjproject-2.10.tar.gz
diff --git a/contrib/src/pjproject/add_dtls_transport.patch b/contrib/src/pjproject/add_dtls_transport.patch
deleted file mode 100644
index b7f9f296e794354bc299092b6d3cc7e5e2d5d3a4..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/add_dtls_transport.patch
+++ /dev/null
@@ -1,63 +0,0 @@
---- a/pjsip/src/pjsip/sip_transport.c
-+++ b/pjsip/src/pjsip/sip_transport.c
-@@ -183,6 +183,13 @@
- 	PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE
-     },
-     { 
-+    PJSIP_TRANSPORT_DTLS,
-+	5061, 
-+	{"DTLS", 4}, 
-+	"DTLS transport", 
-+	PJSIP_TRANSPORT_SECURE
-+    },
-+    { 
- 	PJSIP_TRANSPORT_SCTP, 
- 	5060, 
- 	{"SCTP", 4}, 
-@@ -224,6 +231,13 @@
- 	"TLS IPv6 transport",
- 	PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE
-     },
-+    {
-+	PJSIP_TRANSPORT_DTLS6,
-+	5061,
-+	{"DTLS", 4},
-+	"DTLS IPv6 transport",
-+	PJSIP_TRANSPORT_SECURE
-+    },
- };
- 
- static void tp_state_callback(pjsip_transport *tp,
-@@ -249,7 +263,7 @@
-  */
- PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag,
- 						   const char *tp_name,
--						   int def_port,
-+                           int def_port,
- 						   int *p_tp_type)
- {
-     unsigned i;
---- a/pjsip/include/pjsip/sip_types.h
-+++ b/pjsip/include/pjsip/sip_types.h
-@@ -73,6 +73,9 @@
-     /** TLS. */
-     PJSIP_TRANSPORT_TLS,
- 
-+    /** DTLS. */
-+    PJSIP_TRANSPORT_DTLS,
-+
-     /** SCTP. */
-     PJSIP_TRANSPORT_SCTP,
- 
-@@ -95,7 +98,10 @@
-     PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6,
- 
-     /** TLS over IPv6 */
--    PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6
-+    PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6,
-+
-+    /** DTLS over IPv6 */
-+    PJSIP_TRANSPORT_DTLS6 = PJSIP_TRANSPORT_DTLS + PJSIP_TRANSPORT_IPV6
- 
- } pjsip_transport_type_e;
- 
diff --git a/contrib/src/pjproject/android.patch b/contrib/src/pjproject/android.patch
deleted file mode 100644
index 01621b9886f7fb052ab32b806b7eb7f21adea4d1..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/android.patch
+++ /dev/null
@@ -1,49 +0,0 @@
---- a/pjlib/include/pj/config.h
-+++ b/pjlib/include/pj/config.h
-@@ -293,6 +293,21 @@
-  */
- #include <pj/config_site.h>
- 
-+#undef PJ_ANDROID
-+#define PJ_ANDROID            0
-+#define PJ_JNI_HAS_JNI_ONLOAD 0
-+#undef PJ_HAS_FLOATING_POINT
-+#define PJ_HAS_FLOATING_POINT       0
-+#define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0
-+#define PJMEDIA_AUDIO_DEV_HAS_WMME      0
-+#define PJMEDIA_AUDIO_DEV_HAS_OPENSL        0
-+#define PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI   0
-+#define PJMEDIA_HAS_L16_CODEC       0
-+#define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5
-+#define PJMEDIA_VID_DEV_INFO_FMT_CNT    128
-+#define PJSIP_MAX_TSX_COUNT         31
-+#define PJSIP_MAX_DIALOG_COUNT      31
-+
- /********************************************************************
-  * PJLIB Features.
-  */
---- a/aconfigure
-+++ b/aconfigure
-@@ -5809,6 +5809,3 @@
- 	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
---- a/aconfigure.ac
-+++ b/aconfigure.ac
-@@ -457,6 +457,3 @@
- 	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
---- a/pjlib/src/pj/os_timestamp_posix.c
-+++ b/pjlib/src/pj/os_timestamp_posix.c
-@@ -163,3 +163,3 @@
- 
--#elif defined(__ANDROID__)
-+#elif defined(PJ_ANDROID) && PJ_ANDROID
- 
\ No newline at end of file
diff --git a/contrib/src/pjproject/disable_local_resolution.patch b/contrib/src/pjproject/disable_local_resolution.patch
deleted file mode 100644
index 0f06cbaaa2e00063b9095156e34941a1ada64e9c..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/disable_local_resolution.patch
+++ /dev/null
@@ -1,17 +0,0 @@
- pjlib/include/pj/config.h | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
-index ad0abbb5..6bba5a55 100644
---- a/pjlib/include/pj/config.h
-+++ b/pjlib/include/pj/config.h
-@@ -1003,6 +1003,9 @@
- #endif
- 
- 
-+#ifndef PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION
-+#   define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION	    1
-+#endif
- 
- /** @} */
- 
diff --git a/contrib/src/pjproject/fix_assert_on_connection_attempt.patch b/contrib/src/pjproject/fix_assert_on_connection_attempt.patch
deleted file mode 100644
index 43ffa2bb35f381743292811494fecc57e4d7195a..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_assert_on_connection_attempt.patch
+++ /dev/null
@@ -1,16 +0,0 @@
- pjnath/src/pjnath/turn_sock.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
-index e6c60126..aa764019 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -1597,7 +1597,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess,
- 	if (turn_sock->data_conn[i].state == DATACONN_STATE_NULL)
- 	    break;
-     }
--    pj_assert(i < turn_sock->data_conn_cnt);
-+    pj_assert(i < PJ_TURN_MAX_TCP_CONN_CNT);
-     ++turn_sock->data_conn_cnt;
- 
-     /* Init new data connection */
diff --git a/contrib/src/pjproject/fix_ebusy_turn.patch b/contrib/src/pjproject/fix_ebusy_turn.patch
deleted file mode 100644
index cde32247c443ba80825b9b12dddcdd0bae8085c4..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_ebusy_turn.patch
+++ /dev/null
@@ -1,435 +0,0 @@
- pjnath/include/pjnath/turn_session.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- pjnath/include/pjnath/turn_sock.h    | 44 ++++++++++++++++++++++++++++++++++++++++++++
- pjnath/src/pjnath/ice_strans.c       | 14 ++++++++------
- pjnath/src/pjnath/turn_session.c     | 22 +++++++++++++++++-----
- pjnath/src/pjnath/turn_sock.c        | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
- 5 files changed, 228 insertions(+), 12 deletions(-)
-
-diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h
-index 9984c1f57..890d70c90 100644
---- a/pjnath/include/pjnath/turn_session.h
-+++ b/pjnath/include/pjnath/turn_session.h
-@@ -250,6 +250,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);
-+
-     /**
-      * Notification when peer address has been bound successfully to 
-      * a channel number.
-@@ -770,6 +799,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 99570ac25..f86682c35 100644
---- a/pjnath/include/pjnath/turn_sock.h
-+++ b/pjnath/include/pjnath/turn_sock.h
-@@ -86,6 +86,23 @@ typedef struct pj_turn_sock_cb
- 		       const pj_sockaddr_t *peer_addr,
- 		       unsigned addr_len);
- 
-+    /**
-+     * Notifification when asynchronous send operation has completed.
-+     *
-+     * @param turn_sock	The TURN transport.
-+     * @param sent	If value is positive non-zero it indicates the
-+     *			number of data sent. When the value is negative,
-+     *			it contains the error code which can be retrieved
-+     *			by negating the value (i.e. status=-sent).
-+     *
-+     * @return		Application should normally return PJ_TRUE to let
-+     *			the STUN transport continue its operation. However
-+     *			it must return PJ_FALSE if it has destroyed the
-+     *			STUN transport in this callback.
-+     */
-+    pj_bool_t (*on_data_sent)(pj_turn_sock *sock,
-+			       pj_ssize_t sent);
-+
-     /**
-      * Notification when TURN session state has changed. Application should
-      * implement this callback to monitor the progress of the TURN session.
-@@ -469,6 +486,33 @@ PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock,
- 					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_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);
-+
- /**
-  * 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/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index d048adfd6..11992ac44 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -391,6 +391,7 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st,
-     pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
-     turn_sock_cb.on_rx_data = &turn_on_rx_data;
-     turn_sock_cb.on_state = &turn_on_state;
-+    turn_sock_cb.on_data_sent = &turn_on_data_sent;
- 
-     /* Override with component specific QoS settings, if any */
-     if (ice_st->cfg.comp[comp_idx].qos_type)
-@@ -1654,9 +1655,9 @@ pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data,
-         comp->turn[tp_idx].log_off = PJ_TRUE;
-       }
- 
--      status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, final_pkt,
--                              final_len, dst_addr, dst_addr_len);
--	  ice_st->is_pending = ((status == PJ_EPENDING) && ice_st);
-+      status = pj_turn_sock_sendto2(comp->turn[tp_idx].sock, final_pkt,
-+                              final_len, dst_addr, dst_addr_len, size);
-+      ice_st->is_pending = ((status == PJ_EPENDING || *size != data_len) && ice_st);
-     } else {
- 		const pj_sockaddr_t *dest_addr;
- 		unsigned dest_addr_len;
-@@ -1860,9 +1861,10 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
- 
- 	if (tp_typ == TP_TURN) {
- 		if (comp->turn[tp_idx].sock) {
--			status = pj_turn_sock_sendto(comp->turn[tp_idx].sock,
--						final_pkt, final_len, dst_addr, dst_addr_len);
--			ice_st->is_pending = status == PJ_EPENDING;
-+			status = pj_turn_sock_sendto2(comp->turn[tp_idx].sock,
-+						final_pkt, final_len, dst_addr, dst_addr_len, &sent_size);
-+			ice_st->is_pending = (status == PJ_EPENDING || (unsigned)sent_size != final_len);
-+
- 		} else {
- 			status = PJ_EINVALIDOP;
- 		}
-diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
-index 1c0430bc7..773427c82 100644
---- a/pjnath/src/pjnath/turn_session.c
-+++ b/pjnath/src/pjnath/turn_session.c
-@@ -975,6 +975,18 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
- 					    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;
-@@ -1011,7 +1023,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;
-     }
- 
-@@ -1039,9 +1051,9 @@ 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. */
-@@ -1079,10 +1091,10 @@ 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 7033fd939..1a0890942 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -89,6 +89,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;
- };
- 
- 
-@@ -100,6 +104,13 @@ static pj_status_t turn_on_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,
-@@ -127,6 +138,9 @@ static pj_bool_t on_data_read(pj_activesock_t *asock,
- 			      pj_size_t size,
- 			      pj_status_t status,
- 			      pj_size_t *remainder);
-+static pj_bool_t on_data_sent(pj_activesock_t *asock,
-+			      pj_ioqueue_op_key_t *send_key,
-+			      pj_ssize_t sent);
- static pj_bool_t on_connect_complete(pj_activesock_t *asock,
- 				     pj_status_t status);
- 
-@@ -135,6 +149,9 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock,
- 				       pj_size_t size,
- 				       pj_status_t status,
- 				       pj_size_t *remainder);
-+static pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock,
-+			      pj_ioqueue_op_key_t *send_key,
-+			      pj_ssize_t sent);
- static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock,
- 					      pj_status_t status);
- static void dataconn_cleanup(tcp_data_conn_t *conn);
-@@ -236,6 +253,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg,
-     /* Init TURN session */
-     pj_bzero(&sess_cb, sizeof(sess_cb));
-     sess_cb.on_send_pkt = &turn_on_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;
-@@ -531,6 +549,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.
-  */
-@@ -710,6 +744,22 @@ on_return:
-     return ret;
- }
- 
-+pj_bool_t on_data_sent(pj_activesock_t *asock,
-+			      pj_ioqueue_op_key_t *send_key,
-+			      pj_ssize_t sent)
-+{
-+	pj_turn_sock *turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
-+
-+	unsigned header_len = turn_sock->current_pkt_len - turn_sock->current_body_len;
-+	unsigned sent_size = (sent > header_len)? (sent - header_len) : 0;
-+
-+	if (turn_sock->cb.on_data_sent) {
-+		(*turn_sock->cb.on_data_sent)(turn_sock, sent_size);
-+	}
-+
-+	return PJ_TRUE;
-+}
-+
- 
- /*
-  * Callback from TURN session to send outgoing packet.
-@@ -720,11 +770,28 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
- 				    const pj_sockaddr_t *dst_addr,
- 				    unsigned dst_addr_len)
- {
-+	unsigned sent = pkt_len;
-+	return turn_on_send_pkt2(sess, pkt, pkt_len, dst_addr, dst_addr_len, &sent, pkt_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_ssize_t len = pkt_len;
-     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
-@@ -769,6 +836,11 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
- 	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;
- }
- 
-@@ -960,6 +1032,7 @@ static void turn_on_state(pj_turn_session *sess,
- 
- 	pj_bzero(&asock_cb, sizeof(asock_cb));
- 	asock_cb.on_data_read = &on_data_read;
-+	asock_cb.on_data_sent = &on_data_sent;
- 	asock_cb.on_connect_complete = &on_connect_complete;
- 	status = pj_activesock_create(turn_sock->pool, sock,
- 				      sock_type, &asock_cfg,
-@@ -1090,6 +1163,25 @@ on_return:
-     return PJ_TRUE;
- }
- 
-+pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock,
-+			      pj_ioqueue_op_key_t *send_key,
-+			      pj_ssize_t sent)
-+{
-+    tcp_data_conn_t *conn = (tcp_data_conn_t*)
-+			    pj_activesock_get_user_data(asock);
-+    pj_turn_sock *turn_sock = conn->turn_sock;
-+
-+    unsigned header_len = turn_sock->current_pkt_len - turn_sock->current_body_len;
-+    unsigned sent_size = (sent > header_len)? (sent - header_len) : 0;
-+
-+    if (turn_sock->cb.on_data_sent) {
-+        (*turn_sock->cb.on_data_sent)(turn_sock, sent_size);
-+    }
-+
-+    return PJ_TRUE;
-+}
-+
-+
- static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock,
- 					      pj_status_t status)
- {
-@@ -1268,6 +1360,7 @@ static void turn_on_connection_attempt(pj_turn_session *sess,
- 
-     pj_bzero(&asock_cb, sizeof(asock_cb));
-     asock_cb.on_data_read = &dataconn_on_data_read;
-+    asock_cb.on_data_sent = &dataconn_on_data_sent;
-     asock_cb.on_connect_complete = &dataconn_on_connect_complete;
-     status = pj_activesock_create(pool, sock,
- 				  pj_SOCK_STREAM(), &asock_cfg,
diff --git a/contrib/src/pjproject/fix_first_packet_turn_tcp.patch b/contrib/src/pjproject/fix_first_packet_turn_tcp.patch
deleted file mode 100644
index dbedf6a1512025699c52962cc3421fd6f0f1835a..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_first_packet_turn_tcp.patch
+++ /dev/null
@@ -1,79 +0,0 @@
- pjnath/src/pjnath/turn_sock.c | 64 +++++++++++++++++++++++++++++++++-------------------------------
- 1 file changed, 33 insertions(+), 31 deletions(-)
-
-diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
-index 7033fd93..6a3a3e31 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -1052,38 +1052,40 @@ static pj_bool_t dataconn_on_data_read(pj_activesock_t *asock,
- 	return PJ_FALSE;
-     }
- 
--    if (conn->state == DATACONN_STATE_READY) {
--	/* Application data */
--	if (turn_sock->cb.on_rx_data) {
--	    (*turn_sock->cb.on_rx_data)(turn_sock, data, size,
--					&conn->peer_addr,
--					conn->peer_addr_len);
-+	*remainder = size;
-+	while (*remainder > 0) {
-+		if (conn->state == DATACONN_STATE_READY) {
-+			/* Application data */
-+			if (turn_sock->cb.on_rx_data) {
-+				(*turn_sock->cb.on_rx_data)(turn_sock, data, *remainder,
-+							&conn->peer_addr,
-+							conn->peer_addr_len);
-+			}
-+			*remainder = 0;
-+		} else if (conn->state == DATACONN_STATE_CONN_BINDING) {
-+			/* Waiting for ConnectionBind response */
-+			pj_bool_t is_stun;
-+			pj_turn_session_on_rx_pkt_param prm;
-+				
-+			/* Ignore if this is not a STUN message */
-+			is_stun = ((((pj_uint8_t*)data)[0] & 0xC0) == 0);
-+			if (!is_stun)
-+				goto on_return;
-+
-+			pj_bzero(&prm, sizeof(prm));
-+			prm.pkt = data;
-+			prm.pkt_len = *remainder;
-+			prm.src_addr = &conn->peer_addr;
-+			prm.src_addr_len = conn->peer_addr_len;
-+			pj_turn_session_on_rx_pkt2(conn->turn_sock->sess, &prm);
-+			/* Got remainder? */
-+			if (prm.parsed_len < *remainder && prm.parsed_len > 0) {
-+				pj_memmove(data, (pj_uint8_t*)data+prm.parsed_len, *remainder);
-+			}
-+			*remainder -= prm.parsed_len;
-+		} else
-+			goto on_return;
- 	}
--    } else if (conn->state == DATACONN_STATE_CONN_BINDING) {
--	/* Waiting for ConnectionBind response */
--	pj_bool_t is_stun;
--	pj_turn_session_on_rx_pkt_param prm;
--        
--	/* Ignore if this is not a STUN message */
--	is_stun = ((((pj_uint8_t*)data)[0] & 0xC0) == 0);
--	if (!is_stun)
--	    goto on_return;
--
--	pj_bzero(&prm, sizeof(prm));
--	prm.pkt = data;
--	prm.pkt_len = size;
--	prm.src_addr = &conn->peer_addr;
--	prm.src_addr_len = conn->peer_addr_len;
--	pj_turn_session_on_rx_pkt2(conn->turn_sock->sess, &prm);
--	/* Got remainder? */
--	if (prm.parsed_len < size) {
--	    *remainder = size - prm.parsed_len;
--	    if (prm.parsed_len) {
--		pj_memmove(data, (pj_uint8_t*)data+prm.parsed_len,
--			   *remainder);
--	    }
--	}
--    }
- 
- on_return:
-     pj_grp_lock_release(turn_sock->grp_lock);
diff --git a/contrib/src/pjproject/fix_ioqueue_ipv6_sendto.patch b/contrib/src/pjproject/fix_ioqueue_ipv6_sendto.patch
deleted file mode 100644
index bc53fd2cb4a82964befbcde117fc5a5e35af517d..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_ioqueue_ipv6_sendto.patch
+++ /dev/null
@@ -1,19 +0,0 @@
---- a/pjlib/src/pj/ioqueue_common_abs.c	2015-11-05 23:18:46.000000000 -0500
-+++ b/pjlib/src/pj/ioqueue_common_abs.c	2016-10-21 13:49:09.183662433 -0400
-@@ -1048,5 +1048,6 @@
-      * 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);
- 
-     /*
---- a/pjlib/src/pj/ioqueue_common_abs.h	2013-02-21 06:18:36.000000000 -0500
-+++ b/pjlib/src/pj/ioqueue_common_abs.h	2016-10-21 14:04:04.148928591 -0400
-@@ -64,5 +64,5 @@
-     pj_ssize_t              written;
-     unsigned                flags;
--    pj_sockaddr_in	    rmt_addr;
-+    pj_sockaddr	    rmt_addr;
-     int			    rmt_addrlen;
- };
diff --git a/contrib/src/pjproject/fix_turn_alloc_failure.patch b/contrib/src/pjproject/fix_turn_alloc_failure.patch
deleted file mode 100644
index 1f8438e35fde7f9ea5811ff4ce9938b3e96995ae..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_turn_alloc_failure.patch
+++ /dev/null
@@ -1,27 +0,0 @@
- pjnath/src/pjnath/ice_strans.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index ca15a74e8..040ab4595 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -396,6 +396,9 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st,
-     data->comp = comp;
-     data->transport_id = cand->transport_id;
- 
-+    /* Commit the relayed candidate. */
-+    comp->cand_cnt++;
-+
-     /* Create the TURN transport */
-     status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, turn_cfg->af,
- 				 turn_cfg->conn_type,
-@@ -420,9 +423,6 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st,
- 	return status;
-     }
- 
--    /* Commit the relayed candidate. */
--    comp->cand_cnt++;
--
-     PJ_LOG(4,(ice_st->obj_name,
- 		  "Comp %d/%d: TURN relay candidate (tpid=%d) "
- 		  "waiting for allocation",
diff --git a/contrib/src/pjproject/fix_turn_connection_failure.patch b/contrib/src/pjproject/fix_turn_connection_failure.patch
deleted file mode 100644
index f42a15bd72348b9dd1f710e76f378183afea4ae6..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_turn_connection_failure.patch
+++ /dev/null
@@ -1,16 +0,0 @@
- pjnath/src/pjnath/ice_strans.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index 72f12700..d4790049 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -2854,7 +2854,7 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
- 
- 	sess_init_update(comp->ice_st);
- 
--    } else if ((old_state == PJ_TURN_STATE_RESOLVING || old_state == PJ_TURN_STATE_ALLOCATING) &&
-+    } else if (old_state >= PJ_TURN_STATE_RESOLVING && old_state <= PJ_TURN_STATE_ALLOCATING &&
-                new_state >= PJ_TURN_STATE_DEALLOCATING)
-     {
-         pj_ice_sess_cand *cand = NULL;
diff --git a/contrib/src/pjproject/fix_turn_fallback.patch b/contrib/src/pjproject/fix_turn_fallback.patch
deleted file mode 100644
index 50229dd01ccf7d8bfcd016acfa967cab7b606a0c..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/fix_turn_fallback.patch
+++ /dev/null
@@ -1,53 +0,0 @@
---- a/pjnath/src/pjnath/turn_session.c	2016-09-19 18:21:09.073614574 -0400
-+++ b/pjnath/src/pjnath/turn_session.c	2016-09-19 18:21:30.648631620 -0400
-@@ -653,3 +653,3 @@
- 
--	cnt = PJ_TURN_MAX_DNS_SRV_CNT;
-+	cnt = 1;
- 	ai = (pj_addrinfo*)
---- a/pjnath/src/pjnath/ice_strans.c	2016-09-19 18:36:04.180104330 -0400
-+++ b/pjnath/src/pjnath/ice_strans.c	2016-09-19 18:37:10.614136809 -0400
-@@ -1304,2 +1304,5 @@
- 
-+		if (!comp->turn[n].sock)
-+			continue;
-+
- 	    /* Gather remote addresses for this component */
-@@ -1995,4 +1998,37 @@
-         sess_init_update(comp->ice_st);
-
-+    } else if ((old_state == PJ_TURN_STATE_RESOLVING || old_state == PJ_TURN_STATE_ALLOCATING) &&
-+               new_state >= PJ_TURN_STATE_DEALLOCATING)
-+    {
-+        pj_ice_sess_cand *cand = NULL;
-+        unsigned i;
-+
-+        /* DNS resolution has failed! */
-+        ++comp->turn[tp_idx].err_cnt;
-+
-+        /* Unregister ourself from the TURN relay */
-+        pj_turn_sock_set_user_data(turn_sock, NULL);
-+        comp->turn[tp_idx].sock = NULL;
-+
-+        /* Wait until initialization completes */
-+        pj_grp_lock_acquire(comp->ice_st->grp_lock);
-+
-+        /* Find relayed candidate in the component */
-+        for (i=0; i<comp->cand_cnt; ++i) {
-+            if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED &&
-+                comp->cand_list[i].transport_id == data->transport_id)
-+            {
-+                cand = &comp->cand_list[i];
-+                break;
-+            }
-+        }
-+        pj_assert(cand != NULL);
-+
-+        pj_grp_lock_release(comp->ice_st->grp_lock);
-+
-+        cand->status = old_state == PJ_TURN_STATE_RESOLVING ? PJ_ERESOLVE : PJ_EINVALIDOP;
-+
-+        sess_init_update(comp->ice_st);
-+
-     } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) {
-         pj_turn_session_info info;
diff --git a/contrib/src/pjproject/ice_config.patch b/contrib/src/pjproject/ice_config.patch
deleted file mode 100644
index 3eaa383819972f1f315ef243e946211c9e6a0604..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/ice_config.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-Description:
- Increase PJ_ICE_MAX_CAND to 256. Upstream's default, 16, is
- not enough to handle cases with numerous local/remote candidates
- like the case when multiple hosts in IPv6 and IPv4 exists.
- Tests show that 40 entries are needed with 5 local host IPs.
- .
- Increase PJ_ICE_MAX_CHECKS, which would be way too low for the
- number of candidates.
- .
- Increase PJ_ICE_MAX_STUN to 6:
-  - One active, one passive for IPv4
-  - One active, one passive for IPv6
-  - One active, one passive set by the user
- .
- Increase PJ_ICE_COMP_BITS so that we can have more components:
-  - 2 Audio components
-  - 2 Video components
-Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
-
- pjnath/include/pjnath/config.h | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
-index 5c2f04bd..d8b546c8 100644
---- a/pjnath/include/pjnath/config.h
-+++ b/pjnath/include/pjnath/config.h
-@@ -232,7 +232,7 @@
-  * Default: 16
-  */
- #ifndef PJ_ICE_MAX_CAND
--#   define PJ_ICE_MAX_CAND			    16
-+#   define PJ_ICE_MAX_CAND			    256
- #endif
- 
- 
-@@ -253,7 +253,7 @@
-  * Default: 4 (one active and one passive for IPv4 and IPv6)
-  */
- #ifndef PJ_ICE_MAX_STUN
--#   define PJ_ICE_MAX_STUN			    4
-+#   define PJ_ICE_MAX_STUN			    6
- #endif
- 
- 
-@@ -273,7 +273,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
- 
- 
-@@ -324,7 +324,7 @@
-  * Default: 32
-  */
- #ifndef PJ_ICE_MAX_CHECKS
--#   define PJ_ICE_MAX_CHECKS			    32
-+#   define PJ_ICE_MAX_CHECKS			    1024
- #endif
- 
\ No newline at end of file
diff --git a/contrib/src/pjproject/ignore_ipv6_on_transport_check.patch b/contrib/src/pjproject/ignore_ipv6_on_transport_check.patch
deleted file mode 100644
index ec472b5168a17c00303a6ebe00e0f19a7b8f6a4b..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/ignore_ipv6_on_transport_check.patch
+++ /dev/null
@@ -1,18 +0,0 @@
- pjsip/src/pjsip/sip_transport.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
-index 1d0e28d32..cd9e462a4 100644
---- a/pjsip/src/pjsip/sip_transport.c
-+++ b/pjsip/src/pjsip/sip_transport.c
-@@ -2151,7 +2151,9 @@ 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);
- 	    return PJSIP_ETPNOTSUITABLE;
- 	}
diff --git a/contrib/src/pjproject/ipv6.patch b/contrib/src/pjproject/ipv6.patch
deleted file mode 100644
index 8b42fbbda1bd8ab36f61182e0d4cf6d096b26418..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/ipv6.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/pjlib/include/pj/config.h
-+++ b/pjlib/include/pj/config.h
-@@ -549,7 +549,7 @@
-  * Default: 0 (disabled, for now)
-  */
- #ifndef PJ_HAS_IPV6
--#  define PJ_HAS_IPV6		    0
-+#  define PJ_HAS_IPV6		    1
- #endif
- 
-  /**
diff --git a/contrib/src/pjproject/keep_alive.patch b/contrib/src/pjproject/keep_alive.patch
deleted file mode 100644
index 05ca2152eab56abbb4e12b798b57dc6a3be056d9..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/keep_alive.patch
+++ /dev/null
@@ -1,149 +0,0 @@
- pjlib/src/pj/sock_bsd.c             | 31 +++++++++++++++++++++++++++++++
- pjnath/include/pjnath/ice_session.h |  4 ++++
- pjnath/include/pjnath/ice_strans.h  |  7 +++++++
- pjnath/src/pjnath/ice_session.c     |  6 ++++++
- pjnath/src/pjnath/ice_strans.c      | 11 +++++++++++
- 5 files changed, 59 insertions(+)
-
-diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
-index e416991d..9db16b93 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.
-@@ -517,6 +526,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 +571,14 @@ 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));
-+	    uint32_t val = 1;
-+	    setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(uint32_t));
-+	    uint32_t keepint = 30; // seconds
-+	    setsockopt(*sock, SOL_TCP, TCP_KEEPIDLE, &keepint, sizeof(uint32_t));
-+	    keepint = 30; // seconds
-+	    setsockopt(*sock, SOL_TCP, TCP_KEEPINTVL, &keepint, sizeof(uint32_t));
-+	    uint32_t cnt = 1;
-+	    setsockopt(*sock, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(uint32_t));
- 	}
- #if defined(PJ_SOCK_HAS_IPV6_V6ONLY) && PJ_SOCK_HAS_IPV6_V6ONLY != 0
- 	if (af == PJ_AF_INET6) {
-diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
-index 77e1278d..141d5b3e 100644
---- a/pjnath/include/pjnath/ice_session.h
-+++ b/pjnath/include/pjnath/ice_session.h
-@@ -630,6 +630,10 @@ typedef struct pj_ice_sess_cb
-     pj_status_t (*close_tcp_connection)(pj_ice_sess *ice,
-                                        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 afaddce2..de14be0b 100644
---- a/pjnath/include/pjnath/ice_strans.h
-+++ b/pjnath/include/pjnath/ice_strans.h
-@@ -182,6 +182,13 @@ typedef struct pj_ice_strans_cb
-     void (*on_data_sent)(pj_ice_strans *ice_st, unsigned comp_id,
-                          pj_ssize_t size);
- 
-+    /**
-+     * 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 7373cdf3..6e47ca9d 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -1413,6 +1413,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 == 120032) {
-+		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 e758ad84..5537c7a0 100644
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -90,6 +90,7 @@ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] =
- 
- 
- /* ICE callbacks */
-+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,
-@@ -1174,6 +1175,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
-     ice_cb.on_rx_data = &ice_rx_data;
-     ice_cb.on_tx_pkt = &ice_tx_pkt;
- #if PJ_HAS_TCP
-+    ice_cb.on_ice_destroy = &on_ice_destroy;
-     ice_cb.wait_tcp_connection = &ice_wait_tcp_connection;
-     ice_cb.select_turn_dataconn = &ice_select_turn_dataconn;
-     ice_cb.reconnect_tcp_connection = &ice_reconnect_tcp_connection;
-@@ -1700,6 +1702,15 @@ pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data,
-     return PJ_EINVALIDOP;
- }
- 
-+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.
diff --git a/contrib/src/pjproject/multiple_listeners.patch b/contrib/src/pjproject/multiple_listeners.patch
deleted file mode 100644
index ebdcc75f5d1c3d0e7723f782d92e561a61beda1a..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/multiple_listeners.patch
+++ /dev/null
@@ -1,59 +0,0 @@
- pjsip/src/pjsip/sip_transport.c | 26 ++++++++++++++++++--------
- 1 file changed, 18 insertions(+), 8 deletions(-)
-
-diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
-index 67e235a3..a270d0a9 100644
---- a/pjsip/src/pjsip/sip_transport.c
-+++ b/pjsip/src/pjsip/sip_transport.c
-@@ -1338,14 +1338,16 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
- 
-     pj_lock_acquire(mgr->lock);
- 
--    /* Check that no factory with the same type has been registered. */
-+    /* Check that no factory with the same type and bound address has been
-+     * registered. */
-     status = PJ_SUCCESS;
-     for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
--	if (p->type == tpf->type) {
--	    status = PJSIP_ETYPEEXISTS;
--	    break;
--	}
--	if (p == tpf) {
-+      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;
- 	}
-@@ -2166,10 +2168,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
- 		        pj_hash_get(mgr->table, &key, key_len, NULL);
- 	}
- 
-+	unsigned flag = pjsip_transport_get_flag_from_type(type);
- 	if (transport == NULL &&
- 	    (!sel || sel->disable_connection_reuse == PJ_FALSE))
- 	{
--	    unsigned flag = pjsip_transport_get_flag_from_type(type);
- 	    const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;
- 
- 
-@@ -2258,7 +2260,15 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
- 
- 	} else {
- 
--	    /* Find factory with type matches the destination type */
-+        /* 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) {
- 		if (factory->type == type)
diff --git a/contrib/src/pjproject/package.json b/contrib/src/pjproject/package.json
index 3144fc3bdca8cdb9a779390b3fe4cafce7e4e0ae..f32ec6ca809c8f2a698660bd3539d83402721acf 100644
--- a/contrib/src/pjproject/package.json
+++ b/contrib/src/pjproject/package.json
@@ -1,31 +1,21 @@
 {
     "name": "pjproject",
-    "version": "5dfa75be7d69047387f9b0436dd9492bbbf03fe4",
+    "version": "3e7b75cb2e482baee58c1991bd2fa4fb06774e0d",
     "url": "https://github.com/pjsip/pjproject/archive/__VERSION__.tar.gz",
     "deps": ["gnutls"],
     "patches": [
-        "fix_turn_alloc_failure.patch",
-        "ipv6.patch",
-        "multiple_listeners.patch",
-        "pj_ice_sess.patch",
-        "fix_turn_fallback.patch",
-        "fix_ioqueue_ipv6_sendto.patch",
-        "add_dtls_transport.patch",
-        "rfc6544.patch",
-        "ice_config.patch",
-        "fix_first_packet_turn_tcp.patch",
-        "fix_ebusy_turn.patch",
-        "ignore_ipv6_on_transport_check.patch",
-        "fix_turn_connection_failure.patch",
-        "disable_local_resolution.patch",
-        "fix_assert_on_connection_attempt.patch",
-        "keep_alive.patch",
-        "sip_td_timeout.patch"
-    ],
-    "win_patches": [
-        "win32_vs_gnutls.patch",
-        "win_config.patch",
-        "win_vs2017_props.patch"
+        "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-pj_ice_sess.patch",
+        "0008-fix_ioqueue_ipv6_sendto.patch",
+        "0009-add-config-site.patch",
+        "0001-win-config.patch",
+        "0002-win-vs-gnutls.patch",
+        "0003-win-vs2017-props.patch"
     ],
     "project_paths": [
         "pjlib-util/build/pjlib_util.vcxproj",
@@ -45,4 +35,4 @@
         "build": [],
         "post_build": []
     }
-}
\ No newline at end of file
+}
diff --git a/contrib/src/pjproject/pj_ice_sess.patch b/contrib/src/pjproject/pj_ice_sess.patch
deleted file mode 100644
index f8cf88c5423045dfd3dcd7669f5b93898ba455aa..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/pj_ice_sess.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-Description: Expose the ice session to allow adding candidates.
- .
- Ring has other ways than stun to determine its IP address.
- .
- See ice_transport.cpp for usage details.
-Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
-
---- a/pjnath/include/pjnath/ice_strans.h
-+++ b/pjnath/include/pjnath/ice_strans.h
-@@ -845,6 +845,8 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendt
-					  int dst_addr_len);
-
-
-+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st);
-+
- /**
-  * @}
-  */
---- a/pjnath/src/pjnath/ice_strans.c
-+++ b/pjnath/src/pjnath/ice_strans.c
-@@ -1243,6 +1243,11 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto
-	return PJ_EINVALIDOP;
- }
-
-+PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess( pj_ice_strans *ice_st )
-+{
-+	return ice_st->ice;
-+}
-+
diff --git a/contrib/src/pjproject/rfc2466.patch b/contrib/src/pjproject/rfc2466.patch
deleted file mode 100644
index cd5143a66b16993ec9248b4f53fbc5d92881cafc..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/rfc2466.patch
+++ /dev/null
@@ -1,197 +0,0 @@
-From 2c4a267fbd68f394630acbab0a0e358d0c905464 Mon Sep 17 00:00:00 2001
-From: Sebastien Blin <sebastien.blin@savoirfairelinux.com>
-Date: Wed, 18 Jul 2018 12:12:04 -0400
-Subject: [PATCH 1/1] RFC 2466: ignore deprecated candidates for new ICE
- candidates
-
----
- pjnath/src/pjnath/ice_session.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 162 insertions(+)
-
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index c51dba77..a5c7a5b0 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -29,6 +29,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[] =
- {
-@@ -701,6 +716,128 @@ 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__)
-+  /* TODO(sblin): find an alternative for these paltforms */
-+#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(): ");
-+            return;
-+        }
-+
-+        nlmsg_ptr = (struct nlmsghdr *) read_buffer;
-+        nlmsg_len = rtn;
-+
-+        if (nlmsg_len < sizeof (struct nlmsghdr)) {
-+            perror ("Received an uncomplete netlink packet");
-+            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");
-+                            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");
-+        }
-+    }
-+#endif
-+}
-+
- 
- /*
-  * Add ICE candidate
-@@ -717,6 +854,31 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
- 					 int addr_len,
- 					 unsigned *p_cand_id)
- {
-+
-+
-+  /**
-+   * 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) == 0) {
-+        free(deprecatedAddrs);
-+        // This address is considered as deprecated so, we can ignore this one.
-+        return PJ_SUCCESS;
-+      }
-+    }
-+    free(deprecatedAddrs);
-+  }
-+
-     pj_ice_sess_cand *lcand;
-     pj_status_t status = PJ_SUCCESS;
-     char address[PJ_INET6_ADDRSTRLEN];
diff --git a/contrib/src/pjproject/rfc6544.patch b/contrib/src/pjproject/rfc6544.patch
deleted file mode 100644
index 89b96600561cad459cb3d08d0649a5f25aaae871..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/rfc6544.patch
+++ /dev/null
@@ -1,5501 +0,0 @@
-Copyright (C) 2018-2019 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.
-
----
- pjnath/include/pjnath/config.h          |   13 +-
- pjnath/include/pjnath/ice_session.h     |  187 +++++++++-
- pjnath/include/pjnath/ice_strans.h      |   61 +++-
- pjnath/include/pjnath/stun_session.h    |   82 ++++-
- pjnath/include/pjnath/stun_sock.h       |   93 ++++-
- pjnath/include/pjnath/turn_sock.h       |   11 +
- pjnath/src/pjnath-test/concur_test.c    |    8 +-
- pjnath/src/pjnath-test/sess_auth.c      |   12 +-
- pjnath/src/pjnath-test/stun_sock_test.c |    7 +-
- pjnath/src/pjnath/ice_session.c         |  595 +++++++++++++++++++++++-------
- pjnath/src/pjnath/ice_strans.c          |  986 +++++++++++++++++++++++++++++++++++++-------------
- pjnath/src/pjnath/nat_detect.c          |   12 +-
- pjnath/src/pjnath/stun_session.c        |   12 +-
- pjnath/src/pjnath/stun_sock.c           | 1359 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
- pjnath/src/pjnath/stun_transaction.c    |    1 +
- pjnath/src/pjnath/turn_session.c        |    3 +-
- pjnath/src/pjnath/turn_sock.c           |   16 +
- pjnath/src/pjturn-client/client_main.c  |   11 +-
- pjnath/src/pjturn-srv/allocation.c      |    2 +-
- pjnath/src/pjturn-srv/server.c          |    2 +-
- pjsip-apps/src/samples/icedemo.c        |  671 +++++++++++++++++++---------------
- pjsip/src/pjsua-lib/pjsua_core.c        |    8 +-
- 22 files changed, 3049 insertions(+), 1103 deletions(-)
-
-diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
-index fc1e2755..6f17a663 100644
---- a/pjnath/include/pjnath/config.h
-+++ b/pjnath/include/pjnath/config.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>
-  *
-@@ -248,7 +248,7 @@
-  * Default: 8
-  */
- #ifndef PJ_ICE_ST_MAX_CAND
--#   define PJ_ICE_ST_MAX_CAND			    8
-+#   define PJ_ICE_ST_MAX_CAND			    32
- #endif
- 
- 
-@@ -256,21 +256,20 @@
-  * Maximum number of STUN transports for each ICE stream transport component.
-  * Valid values are 1 - 64.
-  *
-- * Default: 2
-+ * Default: 4 (one active and one passive for IPv4 and IPv6)
-  */
- #ifndef PJ_ICE_MAX_STUN
--#   define PJ_ICE_MAX_STUN			    2
-+#   define PJ_ICE_MAX_STUN			    4
- #endif
- 
--
- /**
-  * Maximum number of TURN transports for each ICE stream transport component.
-  * Valid values are 1 - 64.
-  *
-- * Default: 2
-+ * Default: 4 (one active and one passive for IPv4 and IPv6)
-  */
- #ifndef PJ_ICE_MAX_TURN
--#   define PJ_ICE_MAX_TURN			    3
-+#   define PJ_ICE_MAX_TURN			    4
- #endif
- 
- 
-diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
-index fa13a3b7..77e1278d 100644
---- a/pjnath/include/pjnath/ice_session.h
-+++ b/pjnath/include/pjnath/ice_session.h
-@@ -163,6 +163,51 @@ 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;
-@@ -309,6 +354,11 @@ typedef struct pj_ice_sess_cand
-      */
-     pj_sockaddr		 rel_addr;
- 
-+    /**
-+     * Transport used (TCP or UDP)
-+     */
-+    pj_ice_cand_transport transport;
-+
- } pj_ice_sess_cand;
- 
- 
-@@ -324,6 +374,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
-@@ -331,6 +397,13 @@ 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
-@@ -512,6 +585,52 @@ 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);
-+
-+    /**
-+     * Select incoming TURN dataconn
-+     *
-+     * @param ice			The ICE session.
-+     * @param clist			The ICE connection list
-+     * @param check_id		The wanted check.
-+     */
-+    pj_status_t (*select_turn_dataconn)(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;
- 
- 
-@@ -627,6 +746,7 @@ struct pj_ice_sess
-     pj_bool_t		 is_destroying;		    /**< Destroy is called  */
-     pj_status_t		 ice_status;		    /**< Error status.	    */
-     pj_timer_entry	 timer;			    /**< ICE timer.	    */
-+    pj_timer_entry	 timer_connect;			    /**< ICE timer tcp timeout.	    */
-     pj_ice_sess_cb	 cb;			    /**< Callback.	    */
- 
-     pj_stun_config	 stun_cfg;		    /**< STUN settings.	    */
-@@ -669,6 +789,7 @@ struct pj_ice_sess
-     	char txt[128];
- 	char errmsg[PJ_ERR_MSG_SIZE];
-     } tmp;
-+
- };
- 
- 
-@@ -826,8 +947,6 @@ PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice,
- PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice,
- 					   const pj_uint8_t prefs[4]);
- 
--
--
- /**
-  * Add a candidate to this ICE session. Application must add candidates for
-  * each components ID before it can start pairing the candidates and 
-@@ -846,20 +965,17 @@ 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.
-  */
--PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
--					  unsigned comp_id,
--					  unsigned transport_id,
--					  pj_ice_cand_type type,
--					  pj_uint16_t local_pref,
--					  const pj_str_t *foundation,
--					  const pj_sockaddr_t *addr,
--					  const pj_sockaddr_t *base_addr,
--					  const pj_sockaddr_t *rel_addr,
--					  int addr_len,
--					  unsigned *p_cand_id);
-+PJ_DECL(pj_status_t)
-+pj_ice_sess_add_cand(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id,
-+                     pj_ice_cand_type type, pj_uint16_t local_pref,
-+                     const pj_str_t *foundation, const pj_sockaddr_t *addr,
-+                     const pj_sockaddr_t *base_addr,
-+                     const pj_sockaddr_t *rel_addr, int addr_len,
-+                     unsigned *p_cand_id, pj_ice_cand_transport transport);
- 
- /**
-  * Find default candidate for the specified component ID, using this
-@@ -968,7 +1084,52 @@ 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);
-+
-+/**
-+ * Select dataconn from TURN
-+ *
-+ * @param ice          The ICE session.
-+ * @param clist        The ice check list
-+ * @param check_id     The wanted check
-+ */
-+PJ_DECL(void)
-+ice_select_incoming_turn(pj_ice_sess *ice, pj_ice_sess_checklist *clist, unsigned check_id);
- 
- /**
-  * @}
-diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h
-index cb677724..afaddce2 100644
---- a/pjnath/include/pjnath/ice_strans.h
-+++ b/pjnath/include/pjnath/ice_strans.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>
-  *
-@@ -171,6 +171,17 @@ typedef struct pj_ice_strans_cb
- 			       pj_ice_strans_op op,
- 			       pj_status_t status);
- 
-+    /**
-+     * This callback will be called when the ICE transport has
-+     * sent data asynchronously.
-+     *
-+     * @param ice_st	    The ICE stream transport.
-+     * @param comp_id	    The component ID.
-+     * @param size	        Size of the packet.
-+     */
-+    void (*on_data_sent)(pj_ice_strans *ice_st, unsigned comp_id,
-+                         pj_ssize_t size);
-+
- } pj_ice_strans_cb;
- 
- 
-@@ -253,6 +264,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;
- 
- 
-@@ -268,6 +286,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
-@@ -347,6 +372,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
-@@ -932,6 +964,33 @@ PJ_DECL(pj_status_t) pj_ice_strans_sendto(pj_ice_strans *ice_st,
- 					  const pj_sockaddr_t *dst_addr,
- 					  int dst_addr_len);
- 
-+/**
-+ * Send outgoing packet using this transport.
-+ * Application can send data (normally RTP or RTCP packets) at any time
-+ * by calling this function. This function takes a destination
-+ * address as one of the arguments, and this destination address should
-+ * be taken from the default transport address of the component (that is
-+ * the address in SDP c= and m= lines, or in a=rtcp attribute).
-+ * If ICE negotiation is in progress, this function will send the data
-+ * to the destination address. Otherwise if ICE negotiation has completed
-+ * successfully, this function will send the data to the nominated remote
-+ * address, as negotiated by ICE.
-+ *
-+ * @param ice_st	The ICE stream transport.
-+ * @param comp_id	Component ID.
-+ * @param data		The data or packet to be sent.
-+ * @param data_len	Size of data or packet, in bytes.
-+ * @param dst_addr	The destination address.
-+ * @param dst_addr_len	Length of destination address.
-+ * @param size	        Length of sent packet.
-+ *
-+ * @return		PJ_SUCCESS if data is sent successfully. PJ_EPENDING if
-+ * sending
-+ */
-+PJ_DECL(pj_status_t)
-+pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data,
-+                     pj_size_t data_len, const pj_sockaddr_t *dst_addr,
-+                     int dst_addr_len, pj_ssize_t *size);
- 
- PJ_DECL(pj_ice_sess *) pj_ice_strans_get_ice_sess(pj_ice_strans *ice_st);
- 
-diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
-index f8ea4d1d..3d42af5a 100644
---- a/pjnath/include/pjnath/stun_session.h
-+++ b/pjnath/include/pjnath/stun_session.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>
-  *
-@@ -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,33 @@ 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;
- 
- 
-@@ -388,15 +438,15 @@ 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.
-  */
--PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg,
--					    const char *name,
--					    const pj_stun_session_cb *cb,
--					    pj_bool_t fingerprint,
--					    pj_grp_lock_t *grp_lock,
--					    pj_stun_session **p_sess);
-+PJ_DECL(pj_status_t)
-+pj_stun_session_create(pj_stun_config *cfg, const char *name,
-+                       const pj_stun_session_cb *cb, pj_bool_t fingerprint,
-+                       pj_grp_lock_t *grp_lock,
-+                       pj_stun_session **p_sess, pj_stun_tp_type conn_type);
- 
- /**
-  * Destroy the STUN session and all objects created in the context of
-@@ -751,6 +801,24 @@ 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 fff4df88..05a61bb1 100644
---- a/pjnath/include/pjnath/stun_sock.h
-+++ b/pjnath/include/pjnath/stun_sock.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>
-  *
-@@ -24,17 +24,19 @@
-  * @file stun_sock.h
-  * @brief STUN aware socket transport
-  */
--#include <pjnath/stun_config.h>
--#include <pjlib-util/resolver.h>
-+#include <pj/activesock.h>
- #include <pj/ioqueue.h>
- #include <pj/lock.h>
-+#include <pj/pool.h>
- #include <pj/sock.h>
- #include <pj/sock_qos.h>
--
-+#include <pjlib-util/resolver.h>
-+#include <pjlib-util/srv_resolver.h>
-+#include <pjnath/stun_config.h>
-+#include <pjnath/stun_session.h>
- 
- PJ_BEGIN_DECL
- 
--
- /**
-  * @addtogroup PJNATH_STUN_SOCK
-  * @{
-@@ -87,9 +89,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;
- 
- 
-@@ -195,7 +205,12 @@ typedef struct pj_stun_sock_info
-      * mapped address has not been resolved. Application may query whether
-      * this field contains valid address with pj_sockaddr_has_addr().
-      */
--    pj_sockaddr	    mapped_addr;
-+    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
-@@ -208,6 +223,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;
- 
- 
-@@ -332,7 +352,6 @@ PJ_DECL(const char*) pj_stun_sock_op_name(pj_stun_sock_op op);
-  */
- PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg);
- 
--
- /**
-  * Create the STUN transport using the specified configuration. Once
-  * the STUN transport has been create, application should call
-@@ -342,7 +361,9 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg);
-  *			things the ioqueue and timer heap instance for
-  *			the operation of this transport.
-  * @param af		Address family of socket. Currently pj_AF_INET()
-- *			and pj_AF_INET6() are supported. 
-+ *			and pj_AF_INET6() are supported.
-+ * @param 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.
-@@ -354,14 +375,11 @@ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg);
-  * @return		PJ_SUCCESS if the operation has been successful,
-  *			or the appropriate error code on failure.
-  */
--PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg,
--					 const char *name,
--					 int af,
--					 const pj_stun_sock_cb *cb,
--					 const pj_stun_sock_cfg *cfg,
--					 void *user_data,
--					 pj_stun_sock **p_sock);
--
-+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,
-+                    pj_stun_sock **p_sock);
- 
- /**
-  * Start the STUN transport. This will start the DNS SRV resolution for
-@@ -470,6 +488,7 @@ PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock,
-  * @param flag		pj_ioqueue_sendto() flag.
-  * @param dst_addr	The remote address.
-  * @param addr_len	Length of the address.
-+ * @param size      size sent
-  *
-  * @return		PJ_SUCCESS if data has been sent immediately, or
-  *			PJ_EPENDING if data cannot be sent immediately. In
-@@ -483,7 +502,45 @@ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock,
- 					 unsigned pkt_len,
- 					 unsigned flag,
- 					 const pj_sockaddr_t *dst_addr,
--					 unsigned addr_len);
-+					 unsigned addr_len,
-+                     pj_ssize_t *size);
-+
-+#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 8650cfc5..99570ac2 100644
---- a/pjnath/include/pjnath/turn_sock.h
-+++ b/pjnath/include/pjnath/turn_sock.h
-@@ -490,6 +490,17 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock,
- 					       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 c3013d2a..ebe17392 100644
---- a/pjnath/src/pjnath-test/concur_test.c
-+++ b/pjnath/src/pjnath-test/concur_test.c
-@@ -183,10 +183,10 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess)
-     for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
- 	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]);
--	if (status != PJ_SUCCESS) {
-+        status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(),
-+                                     PJ_STUN_TP_UDP, &stun_cb, NULL, test_sess,
-+                                     &stun_sock[i]);
-+        if (status != PJ_SUCCESS) {
- 	    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 055eaad6..d1ad137a 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,10 @@ 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 fff4fad2..a54df74d 100644
---- a/pjnath/src/pjnath-test/stun_sock_test.c
-+++ b/pjnath/src/pjnath-test/stun_sock_test.c
-@@ -255,8 +255,8 @@ 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,9 +584,10 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6)
- 	char txt[100];
- 	PJ_LOG(3,(THIS_FILE, "     sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
-     }
-+    pj_ssize_t size;
-     status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
- 				 0, &info.srv_addr, 
--				 pj_sockaddr_get_len(&info.srv_addr));
-+				 pj_sockaddr_get_len(&info.srv_addr), &size);
-     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- 	app_perror("    error: server sending data", status);
- 	ret = -390;
-diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
-index a5c7a5b0..94d797ca 100644
---- a/pjnath/src/pjnath/ice_session.c
-+++ b/pjnath/src/pjnath/ice_session.c
-@@ -1,5 +1,5 @@
- /* $Id$ */
--/* 
-+/*
-  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
-  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
-  *
-@@ -59,7 +59,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"
-@@ -90,8 +93,8 @@ enum timer_type
- 				     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
- };
- 
- /* Candidate type preference */
-@@ -138,6 +141,7 @@ typedef struct timer_data
- {
-     pj_ice_sess		    *ice;
-     pj_ice_sess_checklist   *clist;
-+    unsigned                first_packet_counter; // TODO (remove), for now, needed for the NEEDS_FIRST_PACKET state
- } timer_data;
- 
- 
-@@ -148,6 +152,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);
-@@ -303,10 +308,9 @@ static pj_status_t init_comp(pj_ice_sess *ice,
-     sess_cb.on_send_msg = &on_stun_send_msg;
- 
-     /* Create STUN session for this candidate */
--    status = pj_stun_session_create(&ice->stun_cfg, NULL, 
--			            &sess_cb, PJ_TRUE,
--			            ice->grp_lock,
--				    &comp->stun_sess);
-+    status = pj_stun_session_create(&ice->stun_cfg, NULL, &sess_cb, PJ_TRUE,
-+                                      ice->grp_lock, &comp->stun_sess,
-+                                      PJ_STUN_TP_UDP);
-     if (status != PJ_SUCCESS)
- 	return status;
- 
-@@ -852,7 +856,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)
- {
- 
- 
-@@ -900,6 +905,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->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id);
-     pj_sockaddr_cp(&lcand->addr, addr);
-@@ -1121,6 +1127,8 @@ 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 == st) return; // nothing to do
-+    if (st == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET && check->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) return;
-     pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED);
- 
-     LOG5((ice->obj_name, "Check %s: state changed from %s to %s",
-@@ -1241,6 +1249,17 @@ 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) {
-+		pj_array_erase(clist->checks, sizeof(clist->checks[0]),
-+					   clist->count, i);
-+		--clist->count;
-+		--i;
-+	}
-     }
- 
-     /* Next remove a pair if its local and remote candidates are identical
-@@ -1343,6 +1362,8 @@ 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);
-     case TIMER_NONE:
- 	/* Nothing to do, just to get rid of gcc warning */
- 	break;
-@@ -1364,9 +1385,9 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now)
- 	pj_bool_t saved;
- 	pj_status_t status;
- 
--	/* Must have nominated check by now */
--	pj_assert(comp->nominated_check != NULL);
--	the_check = comp->nominated_check;
-+    /* Must have nominated check by now */
-+    pj_assert(comp->nominated_check != NULL);
-+    the_check = comp->nominated_check;
- 
- 	/* Create the Binding Indication */
- 	status = pj_stun_session_create_ind(comp->stun_sess, 
-@@ -1505,7 +1526,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
- 		check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
- 	    }
- 	}
--
- 	LOG5((ice->obj_name, "Check %d is successful%s",
- 	     GET_CHECK_ID(&ice->clist, check),
- 	     (check->nominated ? "  and nominated" : "")));
-@@ -1771,6 +1791,35 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
-     return PJ_FALSE;
- }
- 
-+static void on_tcp_connect_timeout(pj_ice_sess* ice) {
-+    pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE);
-+
-+    pj_bool_t first_found = PJ_FALSE, set_timer = PJ_FALSE;
-+
-+    for (int i = 0; i < ice->clist.count; ++i) {
-+        pj_ice_sess_check *check = &ice->clist.checks[i];
-+        if (check->state == PJ_ICE_SESS_CHECK_STATE_PENDING && !first_found) {
-+            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);
-+            first_found = PJ_TRUE;
-+        } else if(check->state == PJ_ICE_SESS_CHECK_STATE_PENDING) {
-+            set_timer = PJ_TRUE;
-+            break;
-+        }
-+    }
-+
-+    if (set_timer && ice->timer.id == TIMER_NONE) {
-+        // Reschedule
-+        pj_time_val delay = {0, 0};
-+        delay.msec = 1500;
-+        pj_time_val_normalize(&delay);
-+        pj_timer_heap_schedule_w_grp_lock(
-+            ice->stun_cfg.timer_heap, &ice->timer, &delay,
-+            TIMER_CONNECTION_TIMEOUT, ice->grp_lock);
-+    }
-+}
-+
- 
- /* Create checklist by pairing local candidates with remote candidates */
- PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
-@@ -1857,6 +1906,25 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
- 		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;
-+        }
- 
- 	    chk->lcand = lcand;
- 	    chk->rcand = rcand;
-@@ -1901,6 +1969,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;
- 
-@@ -1913,40 +1982,73 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
-     return PJ_SUCCESS;
- }
- 
--/* Perform check on the specified candidate pair. */
--static pj_status_t perform_check(pj_ice_sess *ice, 
--				 pj_ice_sess_checklist *clist,
--				 unsigned check_id,
--				 pj_bool_t nominate)
-+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_comp *comp;
--    pj_ice_msg_data *msg_data;
-     pj_ice_sess_check *check;
-+    check = &clist->checks[check_id];
-     const pj_ice_sess_cand *lcand;
-+    lcand = check->lcand;
-     const pj_ice_sess_cand *rcand;
--    pj_uint32_t prio;
-+    rcand = check->rcand;
-+    pj_ice_sess_comp *comp;
-+    comp = find_comp(ice, lcand->comp_id);
-     pj_status_t status;
-+    /* 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_stun_session_tp_type(comp->stun_sess) == PJ_STUN_TP_UDP,
-+        &rcand->addr, pj_sockaddr_get_len(&rcand->addr), check->tdata);
-+    if (status != PJ_SUCCESS) {
-+        check->tdata = NULL;
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
-+        on_check_complete(ice, check);
-+        pjnath_perror(ice->obj_name, "Error sending STUN request", status);
-+        pj_log_pop_indent();
-+        return status;
-+    }
-+
- 
-+    return PJ_SUCCESS;
-+}
-+
-+/* Perform check on the specified candidate pair. */
-+static pj_status_t perform_check(pj_ice_sess *ice,
-+                                 pj_ice_sess_checklist *clist,
-+                                 unsigned check_id,
-+                                 pj_bool_t nominate)
-+{
-+    pj_ice_sess_check *check;
-     check = &clist->checks[check_id];
-+    const pj_ice_sess_cand *lcand;
-     lcand = check->lcand;
--    rcand = check->rcand;
-+    pj_ice_sess_comp *comp;
-     comp = find_comp(ice, lcand->comp_id);
-+    pj_status_t status;
- 
--    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();
-+    LOG5((ice->obj_name,
-+         "Sending connectivity check for check %s",
-+         dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check)));
- 
-     /* Create request */
--    status = pj_stun_session_create_req(comp->stun_sess, 
--					PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC,
--					NULL, &check->tdata);
-+    status = pj_stun_session_create_req(comp->stun_sess,
-+                                       PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC,
-+                                       NULL, &check->tdata);
-     if (status != PJ_SUCCESS) {
--	pjnath_perror(ice->obj_name, "Error creating STUN request", status);
--	pj_log_pop_indent();
--	return status;
-+        pjnath_perror(ice->obj_name, "Error creating STUN request", status);
-+        pj_log_pop_indent();
-+        return status;
-     }
- 
-+    pj_ice_msg_data *msg_data;
-     /* Attach data to be retrieved later when STUN request transaction
-      * completes and on_stun_request_complete() callback is called.
-      */
-@@ -1958,57 +2060,102 @@ static pj_status_t perform_check(pj_ice_sess *ice,
-     msg_data->data.req.ckid = check_id;
- 
-     /* Add PRIORITY */
-+    pj_uint32_t prio;
- #if PJNATH_ICE_PRIO_STD
--    prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, 
--			  lcand->comp_id);
-+    prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535,
-+                          lcand->comp_id);
- #else
--    prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0, 
--			  lcand->comp_id);
-+    prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0,
-+                          lcand->comp_id);
- #endif
--    pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, 
--			      PJ_STUN_ATTR_PRIORITY, prio);
-+
-+
-+    pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg,
-+                              PJ_STUN_ATTR_PRIORITY, prio);
- 
-     /* Add USE-CANDIDATE and set this check to nominated.
-      * Also add ICE-CONTROLLING or ICE-CONTROLLED
-      */
-     if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
--	if (nominate) {
--	    pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
--				       PJ_STUN_ATTR_USE_CANDIDATE);
--	    check->nominated = PJ_TRUE;
--	}
-+        if (nominate) {
-+            pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
-+                                       PJ_STUN_ATTR_USE_CANDIDATE);
-+            check->nominated = PJ_TRUE;
-+        }
- 
--	pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, 
--				    PJ_STUN_ATTR_ICE_CONTROLLING,
--				    &ice->tie_breaker);
-+        pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg,
-+                                    PJ_STUN_ATTR_ICE_CONTROLLING,
-+                                    &ice->tie_breaker);
- 
-     } else {
--	pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, 
--				    PJ_STUN_ATTR_ICE_CONTROLLED,
--				    &ice->tie_breaker);
-+        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
-+    if (lcand->transport == PJ_CAND_UDP) {
-+        status = send_connectivity_check(ice, clist, check_id, nominate, msg_data);
-+    } else if (lcand->transport == PJ_CAND_TCP_ACTIVE) {
-+        if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) {
-+            status = (*ice->cb.reconnect_tcp_connection)(ice, clist, check_id);
-+        } else if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET) {
-+            status = send_connectivity_check(ice, clist, check_id, nominate, msg_data);
-+        } else {
-+            pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE);
-+            status = (*ice->cb.wait_tcp_connection)(ice, clist, check_id);
-+            if (ice->timer.id == TIMER_NONE) {
-+                pj_time_val delay = {0, 0};
-+                delay.msec = 1500;
-+                pj_time_val_normalize(&delay);
-+                pj_timer_heap_schedule_w_grp_lock(
-+                    ice->stun_cfg.timer_heap, &ice->timer, &delay,
-+                    TIMER_CONNECTION_TIMEOUT, ice->grp_lock);
-+            } else if (check->state == PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY) {
-+                pj_assert(!"Not expected any timer active");
-+            }
-+        }
-+    } else {
-+        // TCP PASSIVE
-+        if (lcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
-+            pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE);
-+            status = (*ice->cb.select_turn_dataconn)(ice, clist, check_id);
-+            if (ice->timer.id == TIMER_NONE) {
-+                pj_time_val delay = {0, 0};
-+                delay.msec = 1500;
-+                pj_time_val_normalize(&delay);
-+                pj_timer_heap_schedule_w_grp_lock(
-+                    ice->stun_cfg.timer_heap, &ice->timer, &delay,
-+                    TIMER_CONNECTION_TIMEOUT, ice->grp_lock);
-+            }
-+        } else {
-+            status = send_connectivity_check(ice, clist, check_id, nominate, msg_data);
-+        }
-     }
-+#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) {
--	check->tdata = NULL;
--	pjnath_perror(ice->obj_name, "Error sending STUN request", status);
--	pj_log_pop_indent();
--	return status;
-+    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_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS,
-+                            PJ_SUCCESS);
-+        }
-     }
- 
--    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, 
--	            PJ_SUCCESS);
-     pj_log_pop_indent();
--    return PJ_SUCCESS;
-+
-+    return status;
- }
- 
- 
-@@ -2044,44 +2191,101 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
-     LOG5((ice->obj_name, "Starting checklist periodic check"));
-     pj_log_push_indent();
- 
-+    /* Send STUN Binding request for check with highest priority on
-+     * retry state.
-+     */
-+
-+    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;
-+            }
-+        }
-+    }
-+
-+    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.
-      */
--    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_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);
--	    }
--
--	    ++start_count;
-+	    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) {
--	    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);
--		    on_check_complete(ice, check);
--		}
-+    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 && 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) {
-+        // If all sockets are pending, do nothing
-+        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_PENDING) {
-+                ++start_count;
-+                break;
-+            }
-+        }
-     }
- 
-     /* Cannot start check because there's no suitable candidate pair.
-@@ -2098,8 +2302,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
-     pj_grp_lock_release(ice->grp_lock);
-     pj_log_pop_indent();
-     return PJ_SUCCESS;
--}
--
-+    }
- 
- /* Start sending connectivity check with USE-CANDIDATE */
- static void start_nominated_check(pj_ice_sess *ice)
-@@ -2271,13 +2474,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
-     /* First, perform all pending triggered checks, simultaneously. */
-     rcheck = ice->early_check.next;
-     while (rcheck != &ice->early_check) {
--	LOG4((ice->obj_name, 
--	      "Performing delayed triggerred check for component %d",
--	      rcheck->comp_id));
--	pj_log_push_indent();
--	handle_incoming_check(ice, rcheck);
--	rcheck = rcheck->next;
--	pj_log_pop_indent();
-+      LOG4((ice->obj_name,
-+            "Performing delayed triggerred check for component %d",
-+            rcheck->comp_id));
-+      pj_log_push_indent();
-+      handle_incoming_check(ice, rcheck);
-+      rcheck = rcheck->next;
-+      pj_log_pop_indent();
-     }
-     pj_list_init(&ice->early_check);
- 
-@@ -2323,7 +2526,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess,
- 	/* Stray retransmit timer that could happen while
- 	 * we're being destroyed */
- 	pj_grp_lock_release(ice->grp_lock);
--	return PJ_EINVALIDOP;
-+        return PJ_EINVALIDOP;
-     }
- 
-     status = (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
-@@ -2333,6 +2536,154 @@ 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;
-+
-+  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) {
-+            return;
-+        }
-+  }
-+
-+    if (status != PJ_SUCCESS) {
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
-+        on_check_complete(ice, check);
-+        return;
-+    }
-+
-+    // TCP is correctly connected. Craft the message to send
-+    const pj_ice_sess_cand *lcand = check->lcand;
-+    const pj_ice_sess_cand *rcand = check->rcand;
-+    if (check->tdata == NULL) {
-+        LOG5((ice->obj_name, "Error sending STUN request, empty data"));
-+        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;
-+
-+    pj_ice_sess_comp *comp = find_comp(ice, lcand->comp_id);
-+    pj_status_t status_send_msg;
-+    // Note that USERNAME and MESSAGE-INTEGRITY will be added by the
-+    // STUN session.
-+
-+    // Initiate STUN transaction to send the request
-+    status_send_msg = 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 (status_send_msg == PJ_EBUSY /* EBUSY */) {
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status);
-+        return;
-+    }
-+    if (status_send_msg == 120033 /* BROKEN PIPE */) {
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_RETRY, status);
-+        return;
-+    }
-+
-+    if ((status_send_msg == 120104 || status_send_msg == 130054)/* CONNECTION RESET BY PEER */
-+        && rcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
-+        /**
-+         * 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_send_msg != PJ_SUCCESS) {
-+        check->tdata = NULL;
-+        pjnath_perror(ice->obj_name, "Error sending STUN request", status_send_msg);
-+        pj_log_pop_indent();
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
-+        on_check_complete(ice, check);
-+    } else if (rcand->type == PJ_ICE_CAND_TYPE_RELAYED) {
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, status);
-+    } else {
-+        check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, status);
-+    }
-+}
-+
-+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 reseted
-+    if (!remote_addr) return;
-+    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) 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_NEEDS_RETRY, 120104);
-+    }
-+}
-+
-+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_ice_sess_check *check = get_current_check_at_state(ice, remote_addr,
-+        PJ_ICE_SESS_CHECK_STATE_NEEDS_FIRST_PACKET, NULL);
-+    if (!check) 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);
-+    }
-+}
-+
-+
-+void
-+ice_select_incoming_turn(pj_ice_sess *ice, pj_ice_sess_checklist *clist, unsigned check_id)
-+{
-+    check_set_state(ice, &clist->checks[check_id], PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, PJ_SUCCESS);
-+    update_comp_check(ice, clist->checks[check_id].lcand->comp_id, &clist->checks[check_id]);
-+    on_check_complete(ice, &clist->checks[check_id]);
-+}
- 
- /* This callback is called when outgoing STUN request completed */
- static void on_stun_request_complete(pj_stun_session *stun_sess,
-@@ -2563,7 +2914,8 @@ 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);
-@@ -2678,8 +3030,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
-     pj_grp_lock_acquire(ice->grp_lock);
- 
-     if (ice->is_destroying) {
--	pj_grp_lock_release(ice->grp_lock);
--	return PJ_EINVALIDOP;
-+      pj_grp_lock_release(ice->grp_lock);
-+      return PJ_EINVALIDOP;
-     }
- 
-     /*
-@@ -2694,9 +3046,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
-     prio_attr = (pj_stun_priority_attr*)
- 	        pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0);
-     if (prio_attr == NULL) {
--	LOG5((ice->obj_name, "Received Binding request with no PRIORITY"));
--	pj_grp_lock_release(ice->grp_lock);
--	return PJ_SUCCESS;
-+      LOG5((ice->obj_name, "Received Binding request with no PRIORITY"));
-+      pj_grp_lock_release(ice->grp_lock);
-+      return PJ_SUCCESS;
-     }
- 
-     /* Get USE-CANDIDATE attribute */
-@@ -2741,7 +3093,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
- 				    NULL, token, PJ_TRUE, 
- 				    src_addr, src_addr_len);
- 	    pj_grp_lock_release(ice->grp_lock);
--	    return PJ_SUCCESS;
-+            return PJ_SUCCESS;
- 	}
- 
-     } else if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED &&
-@@ -2753,7 +3105,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
- 				    NULL, token, PJ_TRUE, 
- 				    src_addr, src_addr_len);
- 	    pj_grp_lock_release(ice->grp_lock);
--	    return PJ_SUCCESS;
-+            return PJ_SUCCESS;
- 	} else {
- 	    /* Switch role to controlled */
- 	    LOG4((ice->obj_name, 
-@@ -2768,7 +3120,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
-     status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata);
-     if (status != PJ_SUCCESS) {
- 	pj_grp_lock_release(ice->grp_lock);
--	return status;
-+        return status;
-     }
- 
-     if (((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6()) {
-@@ -2825,9 +3177,9 @@ 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,
--				      src_addr, src_addr_len, tdata);
--
-+    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);
- 
-     /* 
-      * Handling early check.
-@@ -2946,14 +3298,16 @@ 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) 
--	{
--	    lcand = c->lcand;
--	    break;
--	}
-+    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 = lcand_tmp;
-+            break;
-+        }
-     }
-     if (lcand == NULL) {
- 	/* Should not happen, but just in case remote is sending a
-@@ -2976,9 +3330,9 @@ static void handle_incoming_check(pj_ice_sess *ice,
-      * have this pair in our checklist.
-      */
-     for (i=0; i<ice->clist.count; ++i) {
--	pj_ice_sess_check *c = &ice->clist.checks[i];
--	if (c->lcand == lcand && c->rcand == rcand)
--	    break;
-+        pj_ice_sess_check *c = &ice->clist.checks[i];
-+        if (c->lcand == lcand && c->rcand == rcand)
-+            break;
-     }
- 
-     /* If the pair is already on the check list:
-@@ -3001,7 +3355,6 @@ static void handle_incoming_check(pj_ice_sess *ice,
- 	 * Note: DO NOT overwrite nominated flag if one is already set.
- 	 */
- 	c->nominated = ((rcheck->use_candidate) || c->nominated);
--
- 	if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN ||
- 	    c->state == PJ_ICE_SESS_CHECK_STATE_WAITING)
- 	{
-diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
-index c7c2a5e7..7f2b6470 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  0
- #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] =
-@@ -102,8 +103,23 @@ static void	   ice_rx_data(pj_ice_sess *ice,
- 			       void *pkt, pj_size_t size,
- 			       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_select_turn_dataconn(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. */
- static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
-@@ -115,6 +131,7 @@ static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
- static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock,
- 				   pj_ioqueue_op_key_t *send_key,
- 				   pj_ssize_t sent);
-+static pj_bool_t turn_on_data_sent(pj_turn_sock *turn_sock, pj_ssize_t sent);
- /* Notification when the status of the STUN transport has changed. */
- static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
- 				pj_stun_sock_op op,
-@@ -130,8 +147,6 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock,
- static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
- 			  pj_turn_state_t new_state);
- 
--
--
- /* Forward decls */
- static void ice_st_on_destroy(void *obj);
- static void destroy_ice_st(pj_ice_strans *ice_st);
-@@ -201,6 +216,14 @@ struct pj_ice_strans
- 
-     pj_bool_t		     destroy_req;/**< Destroy has been called?	*/
-     pj_bool_t		     cb_called;	/**< Init error callback called?*/
-+
-+	pj_bool_t			 is_pending;
-+
-+	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;
- };
- 
- 
-@@ -237,6 +260,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);
-@@ -252,6 +276,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_TURN_TP_UDP;
-     cfg->port = PJ_STUN_PORT;
-     cfg->max_host_cands = 64;
-     cfg->ignore_stun_error = PJ_FALSE;
-@@ -389,6 +414,7 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st,
- 	cand->local_pref = RELAY_PREF;
- 	cand->transport_id = tp_id;
- 	cand->comp_id = (pj_uint8_t) comp->comp_id;
-+	cand->transport = turn_cfg->conn_type == PJ_TURN_TP_UDP ? PJ_CAND_UDP : PJ_CAND_TCP_PASSIVE;
-     }
- 
-     /* Allocate and initialize TURN socket data */
-@@ -399,6 +425,10 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st,
-     /* Commit the relayed candidate. */
-     comp->cand_cnt++;
- 
-+    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,
-@@ -447,6 +477,7 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand,
-         || lcand->transport_id != rcand->transport_id
-         || 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)
-     {
-@@ -456,6 +487,115 @@ static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand,
-     return PJ_TRUE;
- }
- 
-+static pj_bool_t
-+add_local_candidate(pj_ice_sess_cand *cand, unsigned idx, unsigned i,
-+                    pj_stun_sock_info stun_sock_info, pj_ice_strans *ice_st,
-+                    pj_ice_strans_comp *comp, pj_ice_cand_transport transport)
-+{
-+  pj_ice_strans_stun_cfg *stun_cfg = &ice_st->cfg.stun_tp[idx];
-+  unsigned j;
-+  pj_bool_t cand_duplicate = PJ_FALSE;
-+  char addrinfo[PJ_INET6_ADDRSTRLEN+10];
-+  const pj_sockaddr *addr = &stun_sock_info.aliases[i];
-+
-+  /* Leave one candidate for relay */
-+  if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) {
-+      PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
-+      return PJ_FALSE;
-+  }
-+
-+  /* 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_TRUE;
-+      }
-+      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_TRUE;
-+          }
-+      }
-+  }
-+  pj_sockaddr_print(addr, addrinfo, sizeof(addrinfo), 3);
-+
-+  /* Ignore IPv6 link-local address */
-+  if (stun_cfg->af == pj_AF_INET6()) {
-+      const pj_in6_addr *a = &addr->ipv6.sin6_addr;
-+      if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80)
-+          return PJ_TRUE;
-+  }
-+
-+  cand = &comp->cand_list[comp->cand_cnt];
-+
-+  cand->type = PJ_ICE_CAND_TYPE_HOST;
-+  cand->status = PJ_SUCCESS;
-+  cand->local_pref = HOST_PREF;
-+  cand->transport_id = CREATE_TP_ID(TP_STUN, idx);
-+  cand->comp_id = (pj_uint8_t) comp->comp_id;
-+  cand->transport = transport;
-+
-+  char addstr[PJ_INET6_ADDRSTRLEN+10];
-+  pj_sockaddr_print(addr, addstr,
-+                            sizeof(addstr), 3);
-+  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_FALSE;
-+      }
-+  }
-+
-+  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_TRUE;
-+  } else {
-+      comp->cand_cnt+=1;
-+  }
-+
-+  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_TRUE;
-+}
-+
-+
- 
- static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
- 				     pj_ice_strans_comp *comp,
-@@ -504,6 +644,7 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
-     cand->local_pref = SRFLX_PREF;
-     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);
-@@ -511,11 +652,12 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
-     data->transport_id = cand->transport_id;
- 
-     /* 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);
--    if (status != PJ_SUCCESS)
--	return status;
-+    status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, stun_cfg->af,
-+                                 stun_cfg->conn_type, &stun_sock_cb, sock_cfg,
-+                                 data, &comp->stun[idx].sock);
-+    if (status != PJ_SUCCESS) {
-+      return status;
-+	}
- 
-     /* Start STUN Binding resolution and add srflx candidate only if server
-      * is set. When any error occur during STUN Binding resolution, let's
-@@ -581,116 +723,43 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
- 	break;
-     }
- 
-+
-     /* Add local addresses to host candidates, unless max_host_cands
-      * is set to zero.
-      */
-     if (stun_cfg->max_host_cands) {
--	pj_stun_sock_info stun_sock_info;
--	unsigned i, cand_cnt = 0;
--
--	/* Enumerate addresses */
--	status = pj_stun_sock_get_info(comp->stun[idx].sock, &stun_sock_info);
--	if (status != PJ_SUCCESS) {
--	    PJ_PERROR(4,(ice_st->obj_name, status,
--			 "Failed in querying STUN socket info"));
--	    return status;
--	}
--
--	for (i = 0; i < stun_sock_info.alias_cnt &&
--		    cand_cnt < stun_cfg->max_host_cands; ++i)
--	{
--	    unsigned j;
--	    pj_bool_t cand_duplicate = PJ_FALSE;
--	    char addrinfo[PJ_INET6_ADDRSTRLEN+10];
--	    const pj_sockaddr *addr = &stun_sock_info.aliases[i];
--
--	    /* Leave one candidate for relay */
--	    if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) {
--		PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
--		break;
--	    }
--
--	    /* Ignore loopback addresses if cfg->stun.loop_addr is unset */
--	    if (stun_cfg->loop_addr==PJ_FALSE) {
--		if (stun_cfg->af == pj_AF_INET() && 
--		    (pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127)
--		{
--		    continue;
--		}
--		else if (stun_cfg->af == pj_AF_INET6()) {
--		    pj_in6_addr in6addr = {{0}};
--		    in6addr.s6_addr[15] = 1;
--		    if (pj_memcmp(&in6addr, &addr->ipv6.sin6_addr,
--				  sizeof(in6addr))==0)
--		    {
--			continue;
--		    }
--		}
--	    }
--
--	    /* 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 = HOST_PREF;
--	    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 (cand_duplicate) {
--		PJ_LOG(4, (ice_st->obj_name,
--		       "Comp %d: host candidate %s (tpid=%d) is a duplicate",
--		       comp->comp_id, pj_sockaddr_print(&cand->addr, addrinfo,
--		       sizeof(addrinfo), 3), cand->transport_id));
--
--		pj_bzero(&cand->addr, sizeof(cand->addr));
--		pj_bzero(&cand->base_addr, sizeof(cand->base_addr));
--		continue;
--	    } else {
--		comp->cand_cnt+=1;
--		cand_cnt++;
--	    }
--            
--	    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);
--	    }
-+        pj_stun_sock_info stun_sock_info;
-+        unsigned i = 0;
-+        pj_bool_t add_tcp_active_cand;
-+        /* Enumerate addresses */
-+        status = pj_stun_sock_get_info(comp->stun[idx].sock, &stun_sock_info);
-+        if (status != PJ_SUCCESS) {
-+            PJ_PERROR(4,(ice_st->obj_name, status,
-+                "Failed in querying STUN socket info"));
-+            return status;
-+        }
- 
--	    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));
--	}
-+        add_tcp_active_cand = stun_sock_info.conn_type != PJ_STUN_TP_UDP;
-+        for (i = 0; i < stun_sock_info.alias_cnt && i < stun_cfg->max_host_cands; ++i) {
-+            if (!add_tcp_active_cand) {
-+                add_local_candidate(cand, idx, i, stun_sock_info, ice_st, comp,
-+                              	  PJ_CAND_UDP);
-+            } else {
-+                add_local_candidate(cand, idx, i, 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.
-+				 */
-+                add_local_candidate(cand, idx, i, stun_sock_info, ice_st, comp,
-+                                    PJ_CAND_TCP_ACTIVE);
-+            }
-+        }
-     }
- 
-     return status;
-@@ -803,6 +872,13 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
- 	return status;
-     }
- 
-+    ice_st->is_pending = PJ_FALSE;
-+    if (status != PJ_SUCCESS) {
-+      pj_pool_release(pool);
-+      pj_log_pop_indent();
-+      return status;
-+    }
-+
-     pj_grp_lock_add_ref(ice_st->grp_lock);
-     pj_grp_lock_add_handler(ice_st->grp_lock, pool, ice_st,
- 			    &ice_st_on_destroy);
-@@ -1097,6 +1173,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
-     ice_cb.on_ice_complete = &on_ice_complete;
-     ice_cb.on_rx_data = &ice_rx_data;
-     ice_cb.on_tx_pkt = &ice_tx_pkt;
-+#if PJ_HAS_TCP
-+    ice_cb.wait_tcp_connection = &ice_wait_tcp_connection;
-+    ice_cb.select_turn_dataconn = &ice_select_turn_dataconn;
-+    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,
-@@ -1172,7 +1254,7 @@ 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;
- 	}
-@@ -1468,110 +1550,154 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
- 					  const pj_sockaddr_t *dst_addr,
- 					  int dst_addr_len)
- {
--    pj_ice_strans_comp *comp;
--    pj_ice_sess_cand *def_cand;
--    pj_status_t status;
--
--    PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt &&
--		     dst_addr && dst_addr_len, PJ_EINVAL);
--
--    comp = ice_st->comp[comp_id-1];
--
--    /* Check that default candidate for the component exists */
--    if (comp->default_cand >= comp->cand_cnt)
--	return PJ_EINVALIDOP;
-+	pj_ssize_t size;
-+	pj_status_t res = pj_ice_strans_sendto2(ice_st, comp_id, data, data_len, dst_addr, dst_addr_len, &size);
-+	return (res==PJ_SUCCESS||res==PJ_EPENDING) ?
-+		PJ_SUCCESS : res;
-+}
- 
--    /* Protect with group lock, since this may cause race condition with
--     * pj_ice_strans_stop_ice().
--     * See ticket #1877.
--     */
--    pj_grp_lock_acquire(ice_st->grp_lock);
-+/*
-+ * Application wants to send outgoing packet.
-+ */
-+PJ_DEF(pj_status_t)
-+pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data,
-+                     pj_size_t data_len, const pj_sockaddr_t *dst_addr,
-+                     int dst_addr_len, pj_ssize_t* size) {
-+  pj_ice_strans_comp *comp;
-+  pj_ice_sess_cand *def_cand;
-+  pj_status_t status;
-+
-+  PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt &&
-+                       dst_addr && dst_addr_len,
-+                   PJ_EINVAL);
-+
-+  if (ice_st->is_pending) {
-+    return PJ_EBUSY;
-+  }
-+
-+  comp = ice_st->comp[comp_id - 1];
-+
-+  /* Check that default candidate for the component exists */
-+  if (comp->default_cand >= comp->cand_cnt)
-+    return PJ_EINVALIDOP;
-+
-+  /* Protect with group lock, since this may cause race condition with
-+   * pj_ice_strans_stop_ice().
-+   * See ticket #1877.
-+   */
-+  pj_grp_lock_acquire(ice_st->grp_lock);
-+
-+  /* If ICE is available, send data with ICE, otherwise send with the
-+   * default candidate selected during initialization.
-+   *
-+   * https://trac.pjsip.org/repos/ticket/1416:
-+   * Once ICE has failed, also send data with the default candidate.
-+   */
-+  if (ice_st->ice && ice_st->state == PJ_ICE_STRANS_STATE_RUNNING) {
-+    status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
- 
--    /* If ICE is available, send data with ICE, otherwise send with the
--     * default candidate selected during initialization.
--     *
--     * https://trac.pjsip.org/repos/ticket/1416:
--     * Once ICE has failed, also send data with the default candidate.
--     */
--    if (ice_st->ice && ice_st->state == PJ_ICE_STRANS_STATE_RUNNING) {
--	status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
--	
--	pj_grp_lock_release(ice_st->grp_lock);
--	
--	return status;
--    } 
--    
-     pj_grp_lock_release(ice_st->grp_lock);
- 
--    def_cand = &comp->cand_list[comp->default_cand];
--    
--    if (def_cand->status == PJ_SUCCESS) {
--	unsigned tp_idx = GET_TP_IDX(def_cand->transport_id);
-+    if (ice_st->is_pending) {
-+      return PJ_EPENDING;
-+    }
- 
--	if (def_cand->type == PJ_ICE_CAND_TYPE_RELAYED) {
-+    return status;
-+  }
- 
--	    enum {
--		msg_disable_ind = 0xFFFF &
--				  ~(PJ_STUN_SESS_LOG_TX_IND|
--				    PJ_STUN_SESS_LOG_RX_IND)
--	    };
-+  pj_grp_lock_release(ice_st->grp_lock);
- 
--	    /* https://trac.pjsip.org/repos/ticket/1316 */
--	    if (comp->turn[tp_idx].sock == NULL) {
--		/* TURN socket error */
--		return PJ_EINVALIDOP;
--	    }
-+  def_cand = &comp->cand_list[comp->default_cand];
- 
--	    if (!comp->turn[tp_idx].log_off) {
--		/* Disable logging for Send/Data indications */
--		PJ_LOG(5,(ice_st->obj_name,
--			  "Disabling STUN Indication logging for "
--			  "component %d", comp->comp_id));
--		pj_turn_sock_set_log(comp->turn[tp_idx].sock,
--				     msg_disable_ind);
--		comp->turn[tp_idx].log_off = PJ_TRUE;
--	    }
-+    pj_bool_t add_header = def_cand->transport != PJ_CAND_UDP;
-+	pj_uint8_t* final_pkt = data;
-+	unsigned final_len = data_len;
-+	
-+	if (add_header) {
-+		// 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 = data_len % 256;
-+		pj_uint8_t header_0 = data_len >> 8;
-+		final_len = 2 + data_len;
-+		memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t));
-+		memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t));
-+		memcpy(&ice_st->rtp_pkt[2], (unsigned char *)data, data_len);
-+		final_pkt = &ice_st->rtp_pkt;
-+	}
- 
--	    status = pj_turn_sock_sendto(comp->turn[tp_idx].sock,
--					 (const pj_uint8_t*)data,
--					 (unsigned)data_len,
--					 dst_addr, dst_addr_len);
--	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
--		    PJ_SUCCESS : status;
--	} else {
--    	    const pj_sockaddr_t *dest_addr;
--    	    unsigned dest_addr_len;
--
--    	    if (comp->ipv4_mapped) {
--    	    	if (comp->synth_addr_len == 0 ||
--    	    	    pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
--    	    	{
--    	    	    status = pj_sockaddr_synthesize(pj_AF_INET6(),
--    	    					    &comp->synth_addr,
--    	    					    dst_addr);
--    	    	    if (status != PJ_SUCCESS)
--    	            	return status;
--
--    	    	    pj_sockaddr_cp(&comp->dst_addr, dst_addr);
--    	    	    comp->synth_addr_len = pj_sockaddr_get_len(
--    	    	    			       &comp->synth_addr);
--    	    	}
--	    	dest_addr = &comp->synth_addr;
--    	    	dest_addr_len = comp->synth_addr_len;
--    	    } else {
--    		dest_addr = dst_addr;
--    		dest_addr_len = dst_addr_len;
--    	    }
-+  if (def_cand->status == PJ_SUCCESS) {
-+    unsigned tp_idx = GET_TP_IDX(def_cand->transport_id);
-+
-+    if (def_cand->type == PJ_ICE_CAND_TYPE_RELAYED) {
-+
-+      enum {
-+        msg_disable_ind =
-+            0xFFFF & ~(PJ_STUN_SESS_LOG_TX_IND | PJ_STUN_SESS_LOG_RX_IND)
-+      };
-+
-+      /* https://trac.pjsip.org/repos/ticket/1316 */
-+      if (comp->turn[tp_idx].sock == NULL) {
-+        /* TURN socket error */
-+        return PJ_EINVALIDOP;
-+      }
-+
-+      if (!comp->turn[tp_idx].log_off) {
-+        /* Disable logging for Send/Data indications */
-+        PJ_LOG(5, (ice_st->obj_name,
-+                   "Disabling STUN Indication logging for "
-+                   "component %d",
-+                   comp->comp_id));
-+        pj_turn_sock_set_log(comp->turn[tp_idx].sock, msg_disable_ind);
-+        comp->turn[tp_idx].log_off = PJ_TRUE;
-+      }
-+
-+      status = pj_turn_sock_sendto(comp->turn[tp_idx].sock, final_pkt,
-+                              final_len, dst_addr, dst_addr_len);
-+	  ice_st->is_pending = ((status == PJ_EPENDING) && ice_st);
-+    } else {
-+		const pj_sockaddr_t *dest_addr;
-+		unsigned dest_addr_len;
-+
-+		if (comp->ipv4_mapped) {
-+			if (comp->synth_addr_len == 0 ||
-+				pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0) {
-+				status = pj_sockaddr_synthesize(pj_AF_INET6(), &comp->synth_addr,
-+												dst_addr);
-+				if (status != PJ_SUCCESS)
-+					return status;
-+
-+				pj_sockaddr_cp(&comp->dst_addr, dst_addr);
-+				comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr);
-+			}
-+			dest_addr = &comp->synth_addr;
-+			dest_addr_len = comp->synth_addr_len;
-+		} else {
-+			dest_addr = dst_addr;
-+			dest_addr_len = dst_addr_len;
-+		}
- 
--	    status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, data,
--					 (unsigned)data_len, 0, dest_addr,
--					 dest_addr_len);
--	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
--		    PJ_SUCCESS : status;
--	}
-+		status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, final_pkt,
-+								final_len, 0, dest_addr, dest_addr_len, size);
-+		
-+		if (add_header) *size -= sizeof(pj_uint16_t); // Do not count the header
-+		ice_st->is_pending = ((status == PJ_EPENDING || *size != data_len) && ice_st);
-+    }
- 
--    } else
--	return PJ_EINVALIDOP;
-+	return status;
-+
-+  } else
-+    return PJ_EINVALIDOP;
- }
- 
- /*
-@@ -1623,7 +1749,15 @@ 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;
-+				}
-+			}
-+#endif
- 		    if (tp_typ == TP_TURN) {
- 			/* Activate channel binding for the remote address
- 			 * for more efficient data transfer using TURN.
-@@ -1691,24 +1825,55 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
- 
-     PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
- 
-+	if (ice_st->is_pending) {
-+		return PJ_EBUSY;
-+	}
-+
-     comp = ice_st->comp[comp_id-1];
- 
-     TRACE_PKT((comp->ice_st->obj_name,
- 	       "Component %d TX packet to %s:%d with transport %d",
- 	       comp_id,
--	       pj_sockaddr_print(dst_addr, daddr, sizeof(addr), 2),
-+	       pj_sockaddr_print(dst_addr, daddr, sizeof(daddr), 2),
- 	       pj_sockaddr_get_port(dst_addr),
- 	       tp_typ));
- 
--    if (tp_typ == TP_TURN) {
--	if (comp->turn[tp_idx].sock) {
--	    status = pj_turn_sock_sendto(comp->turn[tp_idx].sock,
--					 (const pj_uint8_t*)pkt,
--					 (unsigned)size,
--					 dst_addr, dst_addr_len);
--	} else {
--	    status = PJ_EINVALIDOP;
-+	pj_bool_t add_header = comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP;
-+	pj_uint8_t* final_pkt = pkt;
-+	unsigned final_len = size;
-+	pj_ssize_t sent_size;
-+	
-+	if (add_header) {
-+		// 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;
-+		final_len = 2 + size;
-+		memcpy(&ice_st->rtp_pkt, &(header_0), sizeof(pj_uint8_t));
-+		memcpy(&ice_st->rtp_pkt[1], &(header_1), sizeof(pj_uint8_t));
-+		memcpy(&ice_st->rtp_pkt[2], (unsigned char *)pkt, size);
-+		final_pkt = &ice_st->rtp_pkt;
- 	}
-+
-+	if (tp_typ == TP_TURN) {
-+		if (comp->turn[tp_idx].sock) {
-+			status = pj_turn_sock_sendto(comp->turn[tp_idx].sock,
-+						final_pkt, final_len, dst_addr, dst_addr_len);
-+			ice_st->is_pending = status == PJ_EPENDING;
-+		} else {
-+			status = PJ_EINVALIDOP;
-+		}
-     } else if (tp_typ == TP_STUN) {
-     	const pj_sockaddr_t *dest_addr;
-     	unsigned dest_addr_len;
-@@ -1732,12 +1897,16 @@ 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,
--				     pkt, (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, final_pkt,
-+								final_len, 0, dest_addr, dest_addr_len, &sent_size);
-+			ice_st->is_pending = (status == PJ_EPENDING || sent_size != final_len);
-+		} else {
-+          status = PJ_EINVALIDOP;
-+        }
-     } else {
- 	pj_assert(!"Invalid transport ID");
--	status = PJ_EINVALIDOP;
-+        status = PJ_EINVALIDOP;
-     }
- 
-     return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
-@@ -1763,22 +1932,241 @@ static void ice_rx_data(pj_ice_sess *ice,
-     }
- }
- 
-+static void on_peer_connection(pj_stun_session* sess, pj_status_t status, pj_sockaddr_t* remote_addr) {
-+
-+  sock_user_data *data;
-+  pj_ice_strans_comp *comp;
-+  pj_ice_strans *ice_st;
-+  pj_stun_sock* stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess);
-+  if (!stun_sock) {
-+    /* We have disassociated ourselves from the STUN session */
-+    return;
-+  }
-+  data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock);
-+  if (!data) {
-+    /* We have disassociated ourselves from the STUN socket */
-+    return;
-+  }
-+
-+  comp = data->comp;
-+  ice_st = comp->ice_st;
-+  if (!ice_st || !ice_st->ice) {
-+	  // Incorrect ICE
-+	  return;
-+  }
-+
-+  ice_st->is_pending = PJ_FALSE;
-+  ice_sess_on_peer_connection(ice_st->ice, data->transport_id, status, remote_addr);
-+}
-+
-+static void on_peer_reset_connection(pj_stun_session* sess, pj_sockaddr_t* remote_addr) {
-+
-+  sock_user_data *data;
-+  pj_ice_strans_comp *comp;
-+  pj_ice_strans *ice_st;
-+  pj_stun_sock* stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess);
-+  if (!stun_sock) {
-+    /* We have disassociated ourselves from the STUN session */
-+    return;
-+  }
-+  data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock);
-+  if (!data) {
-+    /* We have disassociated ourselves from the STUN socket */
-+    return;
-+  }
-+
-+  comp = data->comp;
-+  ice_st = comp->ice_st;
-+  if (!ice_st || !ice_st->ice) {
-+	  // Incorrect ICE
-+	  return;
-+  }
-+
-+  ice_sess_on_peer_reset_connection(ice_st->ice, data->transport_id, remote_addr);
-+}
-+
-+static void on_peer_packet(pj_stun_session* sess, pj_sockaddr_t* remote_addr) {
-+
-+  if (!sess || !remote_addr) return;
-+  sock_user_data *data;
-+  pj_ice_strans_comp *comp;
-+  pj_ice_strans *ice_st;
-+  pj_stun_sock* stun_sock = (pj_stun_sock *)pj_stun_session_get_user_data(sess);
-+  if (!stun_sock) {
-+    /* We have disassociated ourselves from the STUN session */
-+    return;
-+  }
-+  data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock);
-+  if (!data) {
-+    /* We have disassociated ourselves from the STUN socket */
-+    return;
-+  }
-+
-+  comp = data->comp;
-+  if (!comp) return;
-+  ice_st = comp->ice_st;
-+  if (!ice_st || !ice_st->ice) {
-+	  // Incorrect ICE
-+	  return;
-+  }
-+
-+  ice_sess_on_peer_packet(ice_st->ice, data->transport_id, remote_addr);
-+}
-+
-+#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;
-+	check = &clist->checks[check_id];
-+	const pj_ice_sess_cand *lcand;
-+	lcand = check->lcand;
-+	const pj_ice_sess_cand *rcand;
-+	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_select_turn_dataconn(pj_ice_sess *ice,
-+											pj_ice_sess_checklist *clist,
-+											unsigned check_id) {
-+	pj_ice_sess_check *check;
-+	check = &clist->checks[check_id];
-+	const pj_ice_sess_cand *lcand;
-+	lcand = check->lcand;
-+	const pj_ice_sess_cand *rcand;
-+	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];
-+
-+    for (int i=0; i<ice_st->cfg.turn_tp_cnt; ++i) {
-+		pj_turn_session_info info;
-+		pj_turn_sock_get_info(st_comp->turn[i].sock, &info);
-+		if (st_comp->turn[i].sock
-+			&& pj_turn_sock_has_dataconn(st_comp->turn[i].sock, &rcand->addr)) {
-+			ice_select_incoming_turn(ice, clist, check_id);
-+			return PJ_SUCCESS; // Already connected via TURN
-+		}
-+    }
-+
-+	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;
-+	check = &clist->checks[check_id];
-+	const pj_ice_sess_cand *lcand;
-+	lcand = check->lcand;
-+	const pj_ice_sess_cand *rcand;
-+	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;
-+	check = &clist->checks[check_id];
-+	const pj_ice_sess_cand *lcand;
-+	lcand = check->lcand;
-+	const pj_ice_sess_cand *rcand;
-+	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;
-+	    ice_st->is_pending = PJ_FALSE;
-+		return pj_stun_sock_close(st_comp->stun[idx].sock, &rcand->addr);
-+	}
-+
-+	return PJ_EINVAL;
-+}
-+#endif
-+
- /* Notification when incoming packet has been received from
-  * the STUN socket.
-  */
--static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
--				 void *pkt,
--				 unsigned pkt_len,
--				 const pj_sockaddr_t *src_addr,
--				 unsigned addr_len)
--{
--    sock_user_data *data;
--    pj_ice_strans_comp *comp;
--    pj_ice_strans *ice_st;
--    pj_status_t status;
--
--    data = (sock_user_data*) pj_stun_sock_get_user_data(stun_sock);
--    if (data == NULL) {
-+    static pj_bool_t stun_on_rx_data(
-+        pj_stun_sock * stun_sock, void *pkt, unsigned pkt_len,
-+        const pj_sockaddr_t *src_addr, unsigned addr_len) {
-+      sock_user_data *data;
-+      pj_ice_strans_comp *comp;
-+      pj_ice_strans *ice_st;
-+      pj_status_t status;
-+
-+      data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock);
-+      if (data == NULL) {
- 	/* We have disassociated ourselves from the STUN socket */
- 	return PJ_FALSE;
-     }
-@@ -1822,9 +2210,34 @@ static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock,
- 				   pj_ioqueue_op_key_t *send_key,
- 				   pj_ssize_t sent)
- {
--    PJ_UNUSED_ARG(stun_sock);
--    PJ_UNUSED_ARG(send_key);
--    PJ_UNUSED_ARG(sent);
-+    sock_user_data *data;
-+    pj_ice_strans_comp *comp;
-+    pj_ice_strans *ice_st;
-+
-+    data = (sock_user_data*) pj_stun_sock_get_user_data(stun_sock);
-+    comp = data->comp;
-+    ice_st = comp->ice_st;
-+    ice_st->is_pending = PJ_FALSE;
-+    if (ice_st->cb.on_data_sent) {
-+      (*ice_st->cb.on_data_sent)(ice_st, comp->comp_id, sent);
-+    }
-+    return PJ_TRUE;
-+}
-+
-+static pj_bool_t turn_on_data_sent(pj_turn_sock *turn_sock, pj_ssize_t sent)
-+{
-+    sock_user_data *data;
-+    pj_ice_strans_comp *comp;
-+    pj_ice_strans *ice_st;
-+
-+    data = (sock_user_data*) pj_turn_sock_get_user_data(turn_sock);
-+    comp = data->comp;
-+    ice_st = comp->ice_st;
-+    ice_st->is_pending = PJ_FALSE;
-+
-+    if (ice_st->cb.on_data_sent) {
-+      (*ice_st->cb.on_data_sent)(ice_st, comp->comp_id, sent);
-+    }
-     return PJ_TRUE;
- }
- 
-@@ -2029,6 +2442,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;
-@@ -2037,7 +2454,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
- /* Callback when TURN socket has received a packet */
- static void turn_on_rx_data(pj_turn_sock *turn_sock,
- 			    void *pkt,
--			    unsigned pkt_len,
-+			    unsigned size,
- 			    const pj_sockaddr_t *peer_addr,
- 			    unsigned addr_len)
- {
-@@ -2063,20 +2480,90 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock,
- 	 */
- 	if (comp->ice_st->cb.on_rx_data) {
- 	    (*comp->ice_st->cb.on_rx_data)(comp->ice_st, comp->comp_id, pkt,
--					   pkt_len, peer_addr, addr_len);
-+					   size, peer_addr, addr_len);
- 	}
- 
-     } else {
- 
- 	/* Hand over the packet to ICE */
--	status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id,
--				       data->transport_id, pkt, pkt_len,
--				       peer_addr, addr_len);
--
--	if (status != PJ_SUCCESS) {
--	    ice_st_perror(comp->ice_st,
--			  "Error processing packet from TURN relay",
--			  status);
-+	if (comp->ice_st->cfg.turn_tp->conn_type == PJ_TURN_TP_TCP && size > 0) {
-+		pj_uint16_t parsed = 0;
-+		pj_status_t status;
-+
-+		do {
-+			pj_uint16_t pkt_len = size - parsed;
-+			pj_uint8_t *current_packet = ((pj_uint8_t *)(pkt)) + parsed;
-+
-+			/* RFC6544, the packet is wrapped into a packet following the RFC4571 */
-+			// cf stun_sock.c:parse_rx_packet
-+			pj_bool_t store_remaining = PJ_TRUE;
-+			if (comp->ice_st->rx_buffer_size != 0 || comp->ice_st->rx_wanted_size != 0) {
-+				// We currently have a packet to complete
-+				if (comp->ice_st->rx_buffer_size == 1) {
-+					// We do not know the current size, parse it.
-+					pkt_len = (((pj_uint8_t *)comp->ice_st->rx_buffer)[0] << 8) +
-+							((pj_uint8_t *)current_packet)[0];
-+					comp->ice_st->rx_buffer_size = 0; // We have eaten the temp packet.
-+					current_packet = current_packet + 1;
-+					parsed += 1;
-+					if (pkt_len + parsed <= size) {
-+						store_remaining = PJ_FALSE;
-+						parsed += pkt_len;
-+					} else {
-+						comp->ice_st->rx_wanted_size = pkt_len;
-+					}
-+				} else if (pkt_len + comp->ice_st->rx_buffer_size >= comp->ice_st->rx_wanted_size) {
-+					// We have enough pkt Build new packet to parse
-+					store_remaining = PJ_FALSE;
-+					pj_uint16_t eaten_bytes = comp->ice_st->rx_wanted_size - comp->ice_st->rx_buffer_size;
-+					memcpy(comp->ice_st->rx_buffer + comp->ice_st->rx_buffer_size,
-+						current_packet, eaten_bytes);
-+					pkt_len = 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 (pkt_len > 1) {
-+				pkt_len = (((pj_uint8_t *)current_packet)[0] << 8) + ((pj_uint8_t *)current_packet)[1];
-+				current_packet = current_packet + 2;
-+				parsed += 2;
-+				if (pkt_len + parsed <= size) {
-+					store_remaining = PJ_FALSE;
-+					parsed += pkt_len;
-+				} else {
-+					comp->ice_st->rx_wanted_size = pkt_len;
-+					
-+				}
-+			}
-+			if (store_remaining) {
-+				pj_uint16_t stored_size = size - parsed;
-+				memcpy(comp->ice_st->rx_buffer + comp->ice_st->rx_buffer_size,
-+						current_packet, stored_size);
-+				comp->ice_st->rx_buffer_size += stored_size;
-+				status = PJ_SUCCESS;
-+				break;
-+			}
-+
-+			status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id,
-+						data->transport_id, current_packet, pkt_len,
-+						peer_addr, addr_len);
-+			
-+			if (status != PJ_SUCCESS) {
-+				ice_st_perror(comp->ice_st,
-+					"Error processing packet from TURN relay",
-+					status);
-+			}
-+		} while (parsed < size);
-+	} else {
-+		status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id,
-+					data->transport_id, pkt, size,
-+					peer_addr, addr_len);
-+		if (status != PJ_SUCCESS) {
-+			ice_st_perror(comp->ice_st,
-+				"Error processing packet from TURN relay",
-+				status);
-+		}
- 	}
-     }
- 
-@@ -2293,5 +2780,4 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
-     pj_grp_lock_dec_ref(comp->ice_st->grp_lock);
- 
-     pj_log_pop_indent();
--}
--
-+}
-\ No newline at end of file
-diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c
-index db0de10b..3013eeed 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;
- 
-@@ -874,10 +875,11 @@ static pj_status_t send_test(nat_detect_session *sess,
- 	      pj_sockaddr_get_port(sess->cur_server)));
- 
-     /* Send the request */
--    status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE,
--				      PJ_TRUE, sess->cur_server, 
--				      pj_sockaddr_get_len(sess->cur_server),
--				      sess->result[test_id].tdata);
-+    status = pj_stun_session_send_msg(
-+        sess->stun_sess, NULL, PJ_TRUE,
-+        (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)
- 	goto on_error;
- 
-diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
-index 7b53aba7..2b006d91 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)
-@@ -505,7 +507,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;
-@@ -526,6 +529,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;
-@@ -1511,3 +1515,9 @@ 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 7692e6c1..e7e876ce 100644
---- a/pjnath/src/pjnath/stun_sock.c
-+++ b/pjnath/src/pjnath/stun_sock.c
-@@ -1,5 +1,5 @@
- /* $Id$ */
--/* 
-+/*
-  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
-  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
-  *
-@@ -20,7 +20,6 @@
- #include <pjnath/stun_sock.h>
- #include <pjnath/errno.h>
- #include <pjnath/stun_transaction.h>
--#include <pjnath/stun_session.h>
- #include <pjlib-util/srv_resolver.h>
- #include <pj/activesock.h>
- #include <pj/addr_resolv.h>
-@@ -40,52 +39,101 @@
- 
- enum { MAX_BIND_RETRY = 100 };
- 
--struct pj_stun_sock
--{
--    char		*obj_name;	/* Log identification	    */
--    pj_pool_t		*pool;		/* Pool			    */
--    void		*user_data;	/* Application user data    */
--    pj_bool_t		 is_destroying; /* Destroy already called   */
--    int			 af;		/* Address family	    */
--    pj_stun_config	 stun_cfg;	/* STUN config (ioqueue etc)*/
--    pj_stun_sock_cb	 cb;		/* Application callbacks    */
--
--    int			 ka_interval;	/* Keep alive interval	    */
--    pj_timer_entry	 ka_timer;	/* Keep alive timer.	    */
--
--    pj_sockaddr		 srv_addr;	/* Resolved server addr	    */
--    pj_sockaddr		 mapped_addr;	/* Our public address	    */
--
--    pj_dns_srv_async_query *q;		/* Pending DNS query	    */
--    pj_sock_t		 sock_fd;	/* Socket descriptor	    */
--    pj_activesock_t	*active_sock;	/* Active socket object	    */
--    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_uint16_t		 tsx_id[6];	/* .. to match STUN msg	    */
--    pj_stun_session	*stun_sess;	/* STUN session		    */
--    pj_grp_lock_t	*grp_lock;	/* Session group lock	    */
--};
-+#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;
-+
-+typedef struct pj_stun_sock {
-+  char *obj_name;          /* Log identification         */
-+  pj_pool_t *pool;         /* Pool                       */
-+  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 setting;
-+  pj_stun_config cfg; /* STUN config (ioqueue etc)  */
-+  pj_stun_sock_cb cb; /* Application callbacks      */
-+
-+  int ka_interval;         /* Keep alive interval        */
-+  pj_timer_entry ka_timer; /* Keep alive timer.          */
-+
-+  pj_sockaddr srv_addr;    /* Resolved server addr       */
-+  pj_sockaddr mapped_addr; /* Our public address         */
-+
-+  pj_dns_srv_async_query *q;  /* Pending DNS query          */
-+  pj_sock_t main_sock_fd;     /* Socket descriptor          */
-+  pj_activesock_t *main_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_uint16_t tsx_id[6];      /* .. to match STUN msg       */
-+  pj_stun_session *stun_sess; /* STUN session               */
-+  pj_grp_lock_t *grp_lock;    /* Session group lock         */
-+} pj_stun_sock;
- 
- /* 
-  * Prototypes for static functions 
-  */
- 
-+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);
-+
-+pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status);
-+
- /* Destructor for group lock */
- static void stun_sock_destructor(void *obj);
- 
- /* This callback is called by the STUN session to send packet */
--static pj_status_t sess_on_send_msg(pj_stun_session *sess,
-+pj_status_t sess_on_send_msg(pj_stun_session *sess,
- 				    void *token,
- 				    const void *pkt,
- 				    pj_size_t pkt_size,
- 				    const pj_sockaddr_t *dst_addr,
- 				    unsigned addr_len);
- 
-+static pj_bool_t sess_fail(pj_stun_sock *stun_sock, pj_stun_sock_op op,
-+                           pj_status_t status);
-+
- /* This callback is called by the STUN session when outgoing transaction 
-  * is complete
-  */
--static void sess_on_request_complete(pj_stun_session *sess,
-+void sess_on_request_complete(pj_stun_session *sess,
- 				     pj_status_t status,
- 				     void *token,
- 				     pj_stun_tx_data *tdata,
-@@ -101,7 +149,7 @@ static void dns_srv_resolver_cb(void *user_data,
- static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock);
- 
- /* Callback from active socket when incoming packet is received */
--static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
-+pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
- 				  void *data,
- 				  pj_size_t size,
- 				  const pj_sockaddr_t *src_addr,
-@@ -109,15 +157,15 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
- 				  pj_status_t status);
- 
- /* Callback from active socket about send status */
--static pj_bool_t on_data_sent(pj_activesock_t *asock,
-+pj_bool_t on_data_sent(pj_activesock_t *asock,
- 			      pj_ioqueue_op_key_t *send_key,
- 			      pj_ssize_t sent);
- 
- /* Schedule keep-alive timer */
--static void start_ka_timer(pj_stun_sock *stun_sock);
-+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);
-+void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
- 
- #define INTERNAL_MSG_TOKEN  (void*)(pj_ssize_t)1
- 
-@@ -160,206 +208,316 @@ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg)
- }
- 
- /*
-- * Create the STUN transport using the specified configuration.
-+ * Initialize.
-  */
--PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
--					 const char *name,
--					 int af,
--					 const pj_stun_sock_cb *cb,
--					 const pj_stun_sock_cfg *cfg,
--					 void *user_data,
--					 pj_stun_sock **p_stun_sock)
--{
--    pj_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_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->setting.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->main_sock_fd);
-+  if (status != PJ_SUCCESS) {
-+    pj_stun_sock_destroy(stun_sock);
-+    pj_grp_lock_release(stun_sock->grp_lock);
-+    return 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);
-+  /* Apply QoS, if specified */
-+  status = pj_sock_apply_qos2(
-+      stun_sock->main_sock_fd, stun_sock->setting.qos_type,
-+      &stun_sock->setting.qos_params, 2, stun_sock->obj_name, NULL);
-+  if (status != PJ_SUCCESS && !stun_sock->setting.qos_ignore_error) {
-+    pj_stun_sock_destroy(stun_sock);
-+    pj_grp_lock_release(stun_sock->grp_lock);
-+    return status;
-+  }
- 
--    status = pj_stun_config_check_valid(stun_cfg);
--    if (status != PJ_SUCCESS)
--	return status;
-+  /* Apply socket buffer size */
-+  if (stun_sock->setting.so_rcvbuf_size > 0) {
-+    unsigned sobuf_size = stun_sock->setting.so_rcvbuf_size;
-+    status = pj_sock_setsockopt_sobuf(stun_sock->main_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->setting.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->setting.so_rcvbuf_size));
-+      } else {
-+        PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size));
-+      }
-+    }
-+  }
-+  if (stun_sock->setting.so_sndbuf_size > 0) {
-+    unsigned sobuf_size = stun_sock->setting.so_sndbuf_size;
-+    status = pj_sock_setsockopt_sobuf(stun_sock->main_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->setting.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->setting.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->setting.port_range &&
-+      stun_sock->setting.port_range < max_bind_retry)
-+    max_bind_retry = stun_sock->setting.port_range;
-+  pj_sockaddr_init(stun_sock->af, &bound_addr, NULL, 0);
-+  if (stun_sock->setting.bound_addr.addr.sa_family == pj_AF_INET() ||
-+      stun_sock->setting.bound_addr.addr.sa_family == pj_AF_INET6()) {
-+    pj_sockaddr_cp(&bound_addr, &stun_sock->setting.bound_addr);
-+  }
-+  status = pj_sock_bind_random(stun_sock->main_sock_fd, &bound_addr,
-+                               stun_sock->setting.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->setting.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->main_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
- 
--    if (name == NULL)
--	name = "stuntp%p";
-+    status =
-+        pj_activesock_create(stun_sock->pool, stun_sock->main_sock_fd,
-+                             sock_type, &activesock_cfg, stun_sock->cfg.ioqueue,
-+                             &activesock_cb, stun_sock, &stun_sock->main_sock);
- 
--    if (cfg == NULL) {
--	pj_stun_sock_cfg_default(&default_cfg);
--	cfg = &default_cfg;
-+    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->main_sock, stun_sock->pool);
-+    } else {
-+      status = PJ_SUCCESS;
-+    }
-+    if (status == PJ_SUCCESS) {
-+      on_stun_sock_ready(stun_sock->main_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->main_sock, PJ_SUCCESS);
-+#endif
-+  }
- 
--    /* Create structure */
--    pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL);
--    stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock);
--    stun_sock->pool = pool;
--    stun_sock->obj_name = pool->obj_name;
--    stun_sock->user_data = user_data;
--    stun_sock->af = af;
--    stun_sock->sock_fd = PJ_INVALID_SOCKET;
--    pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg));
--    pj_memcpy(&stun_sock->cb, cb, sizeof(*cb));
-+  pj_grp_lock_release(stun_sock->grp_lock);
-+  return status;
-+}
- 
--    stun_sock->ka_interval = cfg->ka_interval;
--    if (stun_sock->ka_interval == 0)
--	stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
-+/*
-+ * Create the STUN transport using the specified configuration.
-+ */
-+PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *cfg,
-+                                         const char *name,
-+                                         int af,
-+                                         pj_stun_tp_type conn_type,
-+                                         const pj_stun_sock_cb *cb,
-+                                         const pj_stun_sock_cfg *setting,
-+                                         void *user_data,
-+pj_stun_sock **p_stun_sock)
-+{
-+  pj_stun_sock *stun_sock;
-+  pj_stun_sock_cfg default_cfg;
-+  pj_pool_t *pool;
-+  pj_status_t status;
-+
-+  PJ_ASSERT_RETURN(cfg && cb && p_stun_sock, PJ_EINVAL);
-+  PJ_ASSERT_RETURN(af == pj_AF_INET() || af == pj_AF_INET6(), PJ_EAFNOTSUP);
-+  PJ_ASSERT_RETURN(!setting || pj_stun_sock_cfg_is_valid(setting), 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(cfg);
-+  if (status != PJ_SUCCESS)
-+    return status;
- 
--    if (cfg->grp_lock) {
--	stun_sock->grp_lock = cfg->grp_lock;
--    } else {
--	status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock);
--	if (status != PJ_SUCCESS) {
--	    pj_pool_release(pool);
--	    return status;
--	}
-+  if (!setting) {
-+    pj_stun_sock_cfg_default(&default_cfg);
-+    setting = &default_cfg;
-+  }
-+
-+  if (!name) {
-+    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;
-     }
-+  }
-+
-+  /* Create and init basic data structure */
-+  const int PJNATH_POOL_LEN_STUN_SOCK = 256;
-+  const int PJNATH_POOL_INC_STUN_SOCK = 512;
-+  pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SOCK,
-+                        PJNATH_POOL_INC_STUN_SOCK, NULL);
-+  stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock);
-+  stun_sock->pool = pool;
-+  stun_sock->obj_name = pool->obj_name;
-+  stun_sock->user_data = user_data;
-+  stun_sock->af = af;
-+  stun_sock->conn_type = conn_type;
-+  stun_sock->main_sock_fd = PJ_INVALID_SOCKET;
-+#if PJ_HAS_TCP
-+  stun_sock->outgoing_nb = -1;
-+  stun_sock->incoming_nb = -1;
-+#endif
- 
--    pj_grp_lock_add_ref(stun_sock->grp_lock);
--    pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock,
--			    &stun_sock_destructor);
-+  /* Copy STUN config (this contains ioqueue, timer heap, etc.) */
-+  pj_memcpy(&stun_sock->cfg, cfg, sizeof(*cfg));
- 
--    /* 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;
-+  /* Copy setting (QoS parameters etc */
-+  pj_memcpy(&stun_sock->setting, setting, sizeof(*setting));
- 
--    /* 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;
-+  /* Set callback */
-+  pj_memcpy(&stun_sock->cb, cb, sizeof(*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 (setting->grp_lock) {
-+    stun_sock->grp_lock = setting->grp_lock;
-+  } else {
-+    status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock);
-+    if (status != PJ_SUCCESS) {
-+      pj_pool_release(pool);
-+      return status;
-     }
--    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);
--	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_grp_lock_add_ref(stun_sock->grp_lock);
-+  pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock,
-+                          &stun_sock_destructor);
-+
-+  /* Create STUN session */
-+  {
-+    pj_stun_session_cb sess_cb;
-+
-+    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->cfg, stun_sock->obj_name,
-+                                    &sess_cb, PJ_FALSE, stun_sock->grp_lock,
-+                                    &stun_sock->stun_sess, conn_type);
-+    if (status != PJ_SUCCESS) {
-+      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;
-+}
- 
--	status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, 
--				     &addr_len);
--	if (status != PJ_SUCCESS)
--	    goto on_error;
- 
--	stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10);
--	pj_sockaddr_print(&bound_addr, stun_sock->info, 
--			  PJ_INET6_ADDRSTRLEN, 3);
-+/*
-+ * Notification when outgoing TCP socket has been connected.
-+ */
-+static pj_bool_t
-+on_stun_sock_ready(pj_activesock_t *asock, pj_status_t status)
-+{
-+  pj_stun_sock *stun_sock;
-+  stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock);
-+  if (!stun_sock)
-+    return PJ_FALSE;
-+
-+  pj_grp_lock_acquire(stun_sock->grp_lock);
-+
-+  /* TURN session may have already been destroyed here.
-+   * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557).
-+   */
-+  if (!stun_sock->stun_sess) {
-+    sess_fail(stun_sock, PJ_STUN_SESS_DESTROYED, status);
-+    pj_grp_lock_release(stun_sock->grp_lock);
-+    return PJ_FALSE;
-     }
--#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;
--
--	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;
-+    if (stun_sock->conn_type != PJ_STUN_TP_UDP) {
-+        PJ_LOG(5,(stun_sock->obj_name, "TCP connected"));
-     }
- 
-+    /* Start asynchronous read operations */
-+    pj_status_t result = pj_activesock_start_recvfrom(
-+        asock, stun_sock->pool, stun_sock->setting.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);
- 
-@@ -368,8 +526,9 @@ 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[i] = (pj_uint16_t) pj_rand();
-     }
-     stun_sock->tsx_id[5] = 0;
- 
-@@ -378,15 +537,286 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
-     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;
-+}
-+
-+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
-+    pj_uint8_t*       rx_buffer = NULL;
-+    pj_uint16_t*      rx_buffer_size = NULL;
-+    pj_uint16_t*      rx_wanted_size = NULL;
-+
-+
-+    // 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 pkt_len = 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) {
-+                // We currently have a packet to complete
-+                if (buf->rx_buffer_size == 1) {
-+                    // We do not know the current size, parse it.
-+                    pkt_len = (((pj_uint8_t *)buf->rx_buffer)[0] << 8) +
-+                              ((pj_uint8_t *)current_packet)[0];
-+                    buf->rx_buffer_size = 0; // We have eaten the temp packet.
-+                    current_packet = current_packet + 1;
-+                    parsed += 1;
-+                    if (pkt_len + parsed <= size) {
-+                      store_remaining = PJ_FALSE;
-+                      parsed += pkt_len;
-+                    } else {
-+                      buf->rx_wanted_size = pkt_len;
-+                    }
-+                } else if (pkt_len + 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;
-+                    memcpy(buf->rx_buffer + buf->rx_buffer_size,
-+                           current_packet, eaten_bytes);
-+                    pkt_len = buf->rx_wanted_size;
-+                    current_packet = buf->rx_buffer;
-+                    parsed += eaten_bytes;
-+                    buf->rx_buffer_size = 0;
-+                    buf->rx_wanted_size = 0;
-+                }
-+            } else if (pkt_len > 1) {
-+                pkt_len = (((pj_uint8_t *)current_packet)[0] << 8) + ((pj_uint8_t *)current_packet)[1];
-+                current_packet = current_packet + 2;
-+                parsed += 2;
-+                if (pkt_len + parsed <= size) {
-+                    store_remaining = PJ_FALSE;
-+                    parsed += pkt_len;
-+                } else {
-+                    buf->rx_wanted_size = pkt_len;
-+                }
-+            }
-+            if (store_remaining) {
-+                pj_uint16_t stored_size = size - parsed;
-+                memcpy(buf->rx_buffer + buf->rx_buffer_size,
-+                        current_packet, stored_size);
-+                buf->rx_buffer_size += stored_size;
-+                result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE;
-+                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, pkt_len,
-+                                   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,
-+                                           pkt_len, 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)pkt_len, rx_addr, sock_addr_len);
-+            result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE;
-+            continue;
-+        }
-+
-+        result &= status != PJ_EGONE ? PJ_TRUE : PJ_FALSE;
-+    } while (parsed < size && result);
-+
-+    status = pj_grp_lock_release(stun_sock->grp_lock);
-+    return result;
-+}
-+
-+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;
-+
-+    stun_sock = (pj_stun_sock *)pj_activesock_get_user_data(asock);
-+    if (!stun_sock) 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) {
-+                    if (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 *main_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(main_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->setting.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->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 = pj_activesock_start_read(*asock, stun_sock->pool,
-+        stun_sock->setting.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,
-@@ -504,15 +934,35 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
-     }
- 
-     stun_sock->is_destroying = PJ_TRUE;
--    pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
-+    pj_timer_heap_cancel_if_active(stun_sock->cfg.timer_heap,
-                                    &stun_sock->ka_timer, 0);
- 
--    if (stun_sock->active_sock != NULL) {
--	stun_sock->sock_fd = PJ_INVALID_SOCKET;
--	pj_activesock_close(stun_sock->active_sock);
--    } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
--	pj_sock_close(stun_sock->sock_fd);
--	stun_sock->sock_fd = PJ_INVALID_SOCKET;
-+    if (stun_sock->main_sock != NULL) {
-+        stun_sock->main_sock_fd = PJ_INVALID_SOCKET;
-+        pj_activesock_close(stun_sock->main_sock);
-+    } else if (stun_sock->main_sock_fd != PJ_INVALID_SOCKET) {
-+        pj_sock_close(stun_sock->main_sock_fd);
-+        stun_sock->main_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) {
-@@ -619,12 +1069,12 @@ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock)
- 					&tdata);
-     if (status != PJ_SUCCESS)
- 	goto on_error;
--    
-+
-     /* Send request */
--    status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN,
--				    PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr,
--				    pj_sockaddr_get_len(&stun_sock->srv_addr),
--				    tdata);
-+    status = pj_stun_session_send_msg(
-+        stun_sock->stun_sess, INTERNAL_MSG_TOKEN, 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 && status != PJ_EPENDING)
- 	goto on_error;
- 
-@@ -637,7 +1087,7 @@ on_error:
- 
- /* Get info */
- PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
--					   pj_stun_sock_info *info)
-+                                           pj_stun_sock_info *info)
- {
-     int addr_len;
-     pj_status_t status;
-@@ -646,73 +1096,73 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
- 
-     pj_grp_lock_acquire(stun_sock->grp_lock);
- 
-+    info->conn_type = stun_sock->conn_type;
-+
-     /* Copy STUN server address and mapped address */
-     pj_memcpy(&info->srv_addr, &stun_sock->srv_addr,
--	      sizeof(pj_sockaddr));
--    pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, 
--	      sizeof(pj_sockaddr));
-+              sizeof(pj_sockaddr));
-+    pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr,
-+              sizeof(pj_sockaddr));
- 
-     /* Retrieve bound address */
-     addr_len = sizeof(info->bound_addr);
--    status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr,
--				 &addr_len);
-+    status = pj_sock_getsockname(stun_sock->main_sock_fd, &info->bound_addr,
-+                                 &addr_len);
-     if (status != PJ_SUCCESS) {
--	pj_grp_lock_release(stun_sock->grp_lock);
--	return status;
-+        pj_grp_lock_release(stun_sock->grp_lock);
-+        return status;
-     }
- 
--    /* If socket is bound to a specific interface, then only put that
--     * interface in the alias list. Otherwise query all the interfaces 
--     * in the host.
-+    /* Query all the interfaces in the host, because STUN is compatible with TCP
-      */
-     if (pj_sockaddr_has_addr(&info->bound_addr)) {
--	info->alias_cnt = 1;
--	pj_sockaddr_cp(&info->aliases[0], &info->bound_addr);
-+        info->alias_cnt = 1;
-+        pj_sockaddr_cp(&info->aliases[0], &info->bound_addr);
-     } else {
--	pj_sockaddr def_addr;
--	pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); 
--	unsigned i;
--
--	/* Get the default address */
--	status = pj_gethostip(stun_sock->af, &def_addr);
--	if (status != PJ_SUCCESS) {
--	    PJ_PERROR(4,(stun_sock->obj_name, status,
--			 "Failed in getting default address for STUN info"));
--	    pj_grp_lock_release(stun_sock->grp_lock);
--	    return status;
--	}
--	
--	pj_sockaddr_set_port(&def_addr, port);
--	
--	/* Enum all IP interfaces in the host */
--	info->alias_cnt = PJ_ARRAY_SIZE(info->aliases);
--	status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, 
--				      info->aliases);
--	if (status != PJ_SUCCESS) {
--	    /* If enumeration fails, just return the default address */
--	    PJ_PERROR(4,(stun_sock->obj_name, status,
--			 "Failed in enumerating interfaces for STUN info, "
--			 "returning default address only"));
--	    info->alias_cnt = 1;
--	    pj_sockaddr_cp(&info->aliases[0], &def_addr);
--	}
--
--	/* Set the port number for each address.
--	 */
--	for (i=0; i<info->alias_cnt; ++i) {
--	    pj_sockaddr_set_port(&info->aliases[i], port);
--	}
--
--	/* Put the default IP in the first slot */
--	for (i=0; i<info->alias_cnt; ++i) {
--	    if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) {
--		if (i!=0) {
--		    pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]);
--		    pj_sockaddr_cp(&info->aliases[0], &def_addr);
--		}
--		break;
--	    }
--	}
-+        pj_sockaddr def_addr;
-+        pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); 
-+        unsigned i;
-+
-+        /* Get the default address */
-+        status = pj_gethostip(stun_sock->af, &def_addr);
-+        if (status != PJ_SUCCESS) {
-+            PJ_PERROR(4,(stun_sock->obj_name, status,
-+                "Failed in getting default address for STUN info"));
-+            pj_grp_lock_release(stun_sock->grp_lock);
-+            return status;
-+        }
-+        
-+        pj_sockaddr_set_port(&def_addr, port);
-+        
-+        /* Enum all IP interfaces in the host */
-+        info->alias_cnt = PJ_ARRAY_SIZE(info->aliases);
-+        status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, 
-+                        info->aliases);
-+        if (status != PJ_SUCCESS) {
-+            /* If enumeration fails, just return the default address */
-+            PJ_PERROR(4,(stun_sock->obj_name, status,
-+                "Failed in enumerating interfaces for STUN info, "
-+                "returning default address only"));
-+            info->alias_cnt = 1;
-+            pj_sockaddr_cp(&info->aliases[0], &def_addr);
-+        }
-+
-+        /* Set the port number for each address.
-+        */
-+        for (i=0; i<info->alias_cnt; ++i) {
-+            pj_sockaddr_set_port(&info->aliases[i], port);
-+        }
-+
-+        /* Put the default IP in the first slot */
-+        for (i=0; i<info->alias_cnt; ++i) {
-+            if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) {
-+                if (i!=0) {
-+                    pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]);
-+                    pj_sockaddr_cp(&info->aliases[0], &def_addr);
-+                }
-+                break;
-+            }
-+        }
-     }
- 
-     pj_grp_lock_release(stun_sock->grp_lock);
-@@ -726,36 +1176,253 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock,
- 					 unsigned pkt_len,
- 					 unsigned flag,
- 					 const pj_sockaddr_t *dst_addr,
--					 unsigned addr_len)
-+					 unsigned addr_len,
-+                     pj_ssize_t* size)
- {
--    pj_ssize_t size;
-     pj_status_t status;
- 
-     PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL);
--    
-+
-     pj_grp_lock_acquire(stun_sock->grp_lock);
- 
--    if (!stun_sock->active_sock) {
--	/* We have been shutdown, but this callback may still get called
--	 * by retransmit timer.
--	 */
--	pj_grp_lock_release(stun_sock->grp_lock);
--	return PJ_EINVALIDOP;
-+    if (!stun_sock->main_sock) {
-+        /* We have been shutdown, but this callback may still get called
-+         * by retransmit timer.
-+         */
-+        pj_grp_lock_release(stun_sock->grp_lock);
-+        return PJ_EINVALIDOP;
-     }
- 
-     if (send_key==NULL)
--	send_key = &stun_sock->send_key;
-+        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);
-+    *size = pkt_len;
-+    if (stun_sock->conn_type == PJ_STUN_TP_UDP) {
-+        status = pj_activesock_sendto(stun_sock->main_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 (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 (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->main_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->setting.qos_type,
-+                            &stun_sock->setting.qos_params, 2, stun_sock->obj_name, NULL);
-+  if (status != PJ_SUCCESS && !stun_sock->setting.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->setting.so_rcvbuf_size > 0) {
-+    unsigned sobuf_size = stun_sock->setting.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->setting.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->setting.so_rcvbuf_size));
-+      } else {
-+        PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size));
-+      }
-+    }
-+  }
-+
-+  if (stun_sock->setting.so_sndbuf_size > 0) {
-+    unsigned sobuf_size = stun_sock->setting.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->setting.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->setting.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->setting.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->cfg.ioqueue, &activesock_cb,
-+                                  stun_sock, asock);
-+
-+    if (status != PJ_SUCCESS) {
-+      pj_grp_lock_release(stun_sock->grp_lock);
-+      return status;
-+    }
-+
-+    *addr = 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 (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, 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 (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 (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 (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, remote_addr) == 0) {
-+            return pj_activesock_close(stun_sock->incoming_socks[i].sock);
-+        }
-+    }
-+    return PJ_EINVAL;
-+}
-+
-+pj_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_status_t result = pj_activesock_start_read(
-+      asock, stun_sock->pool, stun_sock->setting.max_pkt_size, 0);
-+  if (result != PJ_SUCCESS) {
-+    return PJ_FALSE;
-+  };
-+
-+  pj_stun_session_cb *cb = pj_stun_session_callback(stun_sock->stun_sess);
-+  if (!cb->on_peer_connection) {
-+    return PJ_FALSE;
-+  }
-+
-+  // Get remote connected address
-+  pj_sockaddr_t* remote_addr = NULL;
-+  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;
-+  (cb->on_peer_connection)(stun_sock->stun_sess, status, remote_addr);
-+  return PJ_TRUE;
-+}
-+
-+#endif
-+
- /* This callback is called by the STUN session to send packet */
--static pj_status_t sess_on_send_msg(pj_stun_session *sess,
-+pj_status_t sess_on_send_msg(pj_stun_session *sess,
- 				    void *token,
- 				    const void *pkt,
- 				    pj_size_t pkt_size,
-@@ -766,7 +1433,7 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess,
-     pj_ssize_t size;
- 
-     stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
--    if (!stun_sock || !stun_sock->active_sock) {
-+    if (!stun_sock || !stun_sock->main_sock) {
- 	/* We have been shutdown, but this callback may still get called
- 	 * by retransmit timer.
- 	 */
-@@ -777,15 +1444,32 @@ 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) {
-+        pj_status_t status = pj_activesock_sendto(stun_sock->main_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 (pj_sockaddr_cmp(&stun_sock->incoming_socks[i].addr, dst_addr) == 0) {
-+                pj_status_t status = pj_activesock_send(stun_sock->incoming_socks[i].sock,
-+                                            &stun_sock->int_send_key, pkt, &size, 0);
-+            }
-+        }
-+        pj_status_t status = pj_activesock_send(stun_sock->main_sock, &stun_sock->int_send_key,
-+                        pkt, &size, 0);
-+        return status;
-+    }
-+#else
-+    return PJ_EINVAL;
-+#endif
- }
- 
- /* This callback is called by the STUN session when outgoing transaction 
-  * is complete
-  */
--static void sess_on_request_complete(pj_stun_session *sess,
-+void sess_on_request_complete(pj_stun_session *sess,
- 				     pj_status_t status,
- 				     void *token,
- 				     pj_stun_tx_data *tdata,
-@@ -869,9 +1553,9 @@ on_return:
- }
- 
- /* Schedule keep-alive timer */
--static void start_ka_timer(pj_stun_sock *stun_sock)
-+void start_ka_timer(pj_stun_sock *stun_sock)
- {
--    pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
-+    pj_timer_heap_cancel_if_active(stun_sock->cfg.timer_heap,
-                                    &stun_sock->ka_timer, 0);
- 
-     pj_assert(stun_sock->ka_interval != 0);
-@@ -881,7 +1565,7 @@ static void start_ka_timer(pj_stun_sock *stun_sock)
- 	delay.sec = stun_sock->ka_interval;
- 	delay.msec = 0;
- 
--	pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap,
-+	pj_timer_heap_schedule_w_grp_lock(stun_sock->cfg.timer_heap,
- 	                                  &stun_sock->ka_timer,
- 	                                  &delay, PJ_TRUE,
- 	                                  stun_sock->grp_lock);
-@@ -889,7 +1573,7 @@ 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)
-+void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
- {
-     pj_stun_sock *stun_sock;
- 
-@@ -911,7 +1595,7 @@ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
- }
- 
- /* Callback from active socket when incoming packet is received */
--static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
-+pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
- 				  void *data,
- 				  pj_size_t size,
- 				  const pj_sockaddr_t *src_addr,
-@@ -919,8 +1603,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)
-@@ -932,62 +1614,11 @@ 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 */
--static pj_bool_t on_data_sent(pj_activesock_t *asock,
-+pj_bool_t on_data_sent(pj_activesock_t *asock,
- 			      pj_ioqueue_op_key_t *send_key,
- 			      pj_ssize_t sent)
- {
-@@ -1024,3 +1655,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
-     return PJ_TRUE;
- }
- 
-+pj_stun_session* pj_stun_sock_get_session(pj_stun_sock *stun_sock)
-+{
-+  return stun_sock ? stun_sock->stun_sess : NULL;
-+}
-diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c
-index 28f62300..0c15763c 100644
---- a/pjnath/src/pjnath/stun_transaction.c
-+++ b/pjnath/src/pjnath/stun_transaction.c
-@@ -394,6 +394,7 @@ 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 97e9435c..9f91dd34 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 a8d9ed00..7033fd93 100644
---- a/pjnath/src/pjnath/turn_sock.c
-+++ b/pjnath/src/pjnath/turn_sock.c
-@@ -1357,3 +1357,19 @@ 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;
-+}
-\ No newline at end of file
-diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c
-index 1a866722..5f013631 100644
---- a/pjnath/src/pjturn-client/client_main.c
-+++ b/pjnath/src/pjturn-client/client_main.c
-@@ -154,10 +154,10 @@ static int init()
- #endif
- 
- 	name[strlen(name)-1] = '0'+i;
--	status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), 
--				     &stun_sock_cb, &ss_cfg,
--				     &g.peer[i], &g.peer[i].stun_sock);
--	if (status != PJ_SUCCESS) {
-+        status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(),
-+                                     PJ_STUN_TP_UDP, &stun_sock_cb, &ss_cfg,
-+                                     &g.peer[i], &g.peer[i].stun_sock);
-+        if (status != PJ_SUCCESS) {
- 	    my_perror("pj_stun_sock_create()", status);
- 	    return status;
- 	}
-@@ -525,8 +525,9 @@ static void console_main(void)
- 	    }
- 	    peer = &g.peer[input[0]-'0'];
- 	    sprintf(input, "Hello from peer%d", input[0]-'0');
-+		pj_ssize_t size;
- 	    pj_stun_sock_sendto(peer->stun_sock, NULL, input, strlen(input)+1, 0,
--				&g.relay_addr, pj_sockaddr_get_len(&g.relay_addr));
-+				&g.relay_addr, pj_sockaddr_get_len(&g.relay_addr), &size);
- 	    break;
- 	case 'q':
- 	    g.quit = PJ_TRUE;
-diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
-index 6c9c9ce1..eea91f01 100644
---- a/pjnath/src/pjturn-srv/allocation.c
-+++ b/pjnath/src/pjturn-srv/allocation.c
-@@ -338,7 +338,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport,
-     sess_cb.on_rx_request = &stun_on_rx_request;
-     sess_cb.on_rx_indication = &stun_on_rx_indication;
-     status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
--				    &sess_cb, PJ_FALSE, NULL, &alloc->sess);
-+				    &sess_cb, PJ_FALSE, NULL, &alloc->sess, PJ_STUN_TP_UDP);
-     if (status != PJ_SUCCESS) {
- 	goto on_error;
-     }
-diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c
-index 94dda29a..95ad1793 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 9d811374..92826fd8 100644
---- a/pjsip-apps/src/samples/icedemo.c
-+++ b/pjsip-apps/src/samples/icedemo.c
-@@ -43,7 +43,7 @@ static struct app_t
- 	pj_bool_t   regular;
- 	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,25 +341,31 @@ static pj_status_t icedemo_init(void)
-     else
- 	icedemo.ice_cfg.opt.aggressive = PJ_TRUE;
- 
--    /* Configure STUN/srflx candidate resolution */
--    if (icedemo.opt.stun_srv.slen) {
--	char *pos;
--
--	/* Command line option may contain port number */
--	if ((pos=pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) {
--	    icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr;
--	    icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr);
--
--	    icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos+1);
--	} else {
--	    icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv;
--	    icedemo.ice_cfg.stun.port = PJ_STUN_PORT;
--	}
--
--	/* For this demo app, configure longer STUN keep-alive time
--	 * so that it does't clutter the screen output.
--	 */
--	icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL;
-+    /* 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;
-+
-+      /* Command line option may contain port number */
-+      if ((pos = pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) {
-+        icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr;
-+        icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr);
-+
-+        icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos + 1);
-+      } else {
-+        icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv;
-+        icedemo.ice_cfg.stun.port = PJ_STUN_PORT;
-+      }
-+
-+      /* For this demo app, configure longer STUN keep-alive time
-+       * so that it does't clutter the screen output.
-+       */
-+      icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL;
-     }
- 
-     /* Configure TURN candidate */
-@@ -384,7 +390,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,8 +401,12 @@ static pj_status_t icedemo_init(void)
- 	icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL;
-     }
- 
--    /* -= That's it for now, initialization is complete =- */
--    return PJ_SUCCESS;
-+	if (icedemo.opt.ice_tcp) {
-+		icedemo.ice_cfg.protocol = PJ_ICE_TP_TCP;
-+	}
-+
-+	/* -= That's it for now, initialization is complete =- */
-+	return PJ_SUCCESS;
- }
- 
- 
-@@ -524,26 +534,60 @@ static void icedemo_stop_session(void)
- 
- /* Utility to create a=candidate SDP attribute */
- static int print_cand(char buffer[], unsigned maxlen,
--		      const pj_ice_sess_cand *cand)
-+                      const pj_ice_sess_cand *cand)
- {
-     char ipaddr[PJ_INET6_ADDRSTRLEN];
-     char *p = buffer;
-     int printed;
- 
--    PRINT("a=candidate:%.*s %u UDP %u %s %u typ ",
--	  (int)cand->foundation.slen,
--	  cand->foundation.ptr,
--	  (unsigned)cand->comp_id,
--	  cand->prio,
--	  pj_sockaddr_print(&cand->addr, ipaddr, 
--			    sizeof(ipaddr), 0),
--	  (unsigned)pj_sockaddr_get_port(&cand->addr));
--
--    PRINT("%s\n",
--	  pj_ice_get_cand_type_name(cand->type));
-+    /**   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),
-+          (unsigned)pj_sockaddr_get_port(&cand->addr));
-+
-+    PRINT("%s",
-+          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;
-+        return -PJ_ETOOSMALL;
- 
-     *p = '\0';
- 
-@@ -608,6 +652,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,
-@@ -702,231 +766,247 @@ static void icedemo_show_ice(void)
-     }
- }
- 
--
- /*
-- * Input and parse SDP from the remote (containing remote's ICE information) 
-+ * Input and parse SDP from the remote (containing remote's ICE information)
-  * and save it to global variables.
-  */
--static void icedemo_input_remote(void)
--{
--    char linebuf[80];
--    unsigned media_cnt = 0;
--    unsigned comp0_port = 0;
--    char     comp0_addr[80];
--    pj_bool_t done = PJ_FALSE;
--
--    puts("Paste SDP from remote host, end with empty line");
--
--    reset_rem_info();
--
--    comp0_addr[0] = '\0';
--
--    while (!done) {
--	pj_size_t len;
--	char *line;
--
--	printf(">");
--	if (stdout) fflush(stdout);
--
--	if (fgets(linebuf, sizeof(linebuf), stdin)==NULL)
--	    break;
--
--	len = strlen(linebuf);
--	while (len && (linebuf[len-1] == '\r' || linebuf[len-1] == '\n'))
--	    linebuf[--len] = '\0';
--
--	line = linebuf;
--	while (len && pj_isspace(*line))
--	    ++line, --len;
--
--	if (len==0)
--	    break;
--
--	/* Ignore subsequent media descriptors */
--	if (media_cnt > 1)
--	    continue;
--
--	switch (line[0]) {
--	case 'm':
--	    {
--		int cnt;
--		char media[32], portstr[32];
--
--		++media_cnt;
--		if (media_cnt > 1) {
--		    puts("Media line ignored");
--		    break;
--		}
--
--		cnt = sscanf(line+2, "%s %s RTP/", media, portstr);
--		if (cnt != 2) {
--		    PJ_LOG(1,(THIS_FILE, "Error parsing media line"));
--		    goto on_error;
--		}
--
--		comp0_port = atoi(portstr);
--		
--	    }
--	    break;
--	case 'c':
--	    {
--		int cnt;
--		char c[32], net[32], ip[80];
--		
--		cnt = sscanf(line+2, "%s %s %s", c, net, ip);
--		if (cnt != 3) {
--		    PJ_LOG(1,(THIS_FILE, "Error parsing connection line"));
--		    goto on_error;
--		}
--
--		strcpy(comp0_addr, ip);
--	    }
--	    break;
--	case 'a':
--	    {
--		char *attr = strtok(line+2, ": \t\r\n");
--		if (strcmp(attr, "ice-ufrag")==0) {
--		    strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1);
--		} else if (strcmp(attr, "ice-pwd")==0) {
--		    strcpy(icedemo.rem.pwd, attr+strlen(attr)+1);
--		} else if (strcmp(attr, "rtcp")==0) {
--		    char *val = attr+strlen(attr)+1;
--		    int af, cnt;
--		    int port;
--		    char net[32], ip[64];
--		    pj_str_t tmp_addr;
--		    pj_status_t status;
--
--		    cnt = sscanf(val, "%d IN %s %s", &port, net, ip);
--		    if (cnt != 3) {
--			PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute"));
--			goto on_error;
--		    }
--
--		    if (strchr(ip, ':'))
--			af = pj_AF_INET6();
--		    else
--			af = pj_AF_INET();
--
--		    pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);
--		    tmp_addr = pj_str(ip);
--		    status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1],
--						      &tmp_addr);
--		    if (status != PJ_SUCCESS) {
--			PJ_LOG(1,(THIS_FILE, "Invalid IP address"));
--			goto on_error;
--		    }
--		    pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port);
--
--		} else if (strcmp(attr, "candidate")==0) {
--		    char *sdpcand = attr+strlen(attr)+1;
--		    int af, cnt;
--		    char foundation[32], transport[12], ipaddr[80], type[32];
--		    pj_str_t tmpaddr;
--		    int comp_id, prio, port;
--		    pj_ice_sess_cand *cand;
--		    pj_status_t status;
--
--		    cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s",
--				 foundation,
--				 &comp_id,
--				 transport,
--				 &prio,
--				 ipaddr,
--				 &port,
--				 type);
--		    if (cnt != 7) {
--			PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line"));
--			goto on_error;
--		    }
--
--		    cand = &icedemo.rem.cand[icedemo.rem.cand_cnt];
--		    pj_bzero(cand, sizeof(*cand));
--		    
--		    if (strcmp(type, "host")==0)
--			cand->type = PJ_ICE_CAND_TYPE_HOST;
--		    else if (strcmp(type, "srflx")==0)
--			cand->type = PJ_ICE_CAND_TYPE_SRFLX;
--		    else if (strcmp(type, "relay")==0)
--			cand->type = PJ_ICE_CAND_TYPE_RELAYED;
--		    else {
--			PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", 
--				   type));
--			goto on_error;
--		    }
--
--		    cand->comp_id = (pj_uint8_t)comp_id;
--		    pj_strdup2(icedemo.pool, &cand->foundation, foundation);
--		    cand->prio = prio;
--		    
--		    if (strchr(ipaddr, ':'))
--			af = pj_AF_INET6();
--		    else
--			af = pj_AF_INET();
--
--		    tmpaddr = pj_str(ipaddr);
--		    pj_sockaddr_init(af, &cand->addr, NULL, 0);
--		    status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr);
--		    if (status != PJ_SUCCESS) {
--			PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'",
--				  ipaddr));
--			goto on_error;
--		    }
--
--		    pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port);
--
--		    ++icedemo.rem.cand_cnt;
--
--		    if (cand->comp_id > icedemo.rem.comp_cnt)
--			icedemo.rem.comp_cnt = cand->comp_id;
--		}
--	    }
--	    break;
--	}
--    }
--
--    if (icedemo.rem.cand_cnt==0 ||
--	icedemo.rem.ufrag[0]==0 ||
--	icedemo.rem.pwd[0]==0 ||
--	icedemo.rem.comp_cnt == 0)
--    {
--	PJ_LOG(1, (THIS_FILE, "Error: not enough info"));
--	goto on_error;
-+static void icedemo_input_remote(void) {
-+  char linebuf[120];
-+  unsigned media_cnt = 0;
-+  unsigned comp0_port = 0;
-+  char comp0_addr[80];
-+  pj_bool_t done = PJ_FALSE;
-+
-+  puts("Paste SDP from remote host, end with empty line");
-+
-+  reset_rem_info();
-+
-+  comp0_addr[0] = '\0';
-+
-+  while (!done) {
-+    pj_size_t len;
-+    char *line;
-+
-+    printf(">");
-+    if (stdout)
-+      fflush(stdout);
-+
-+    if (fgets(linebuf, sizeof(linebuf), stdin) == NULL)
-+      break;
-+
-+    len = strlen(linebuf);
-+    while (len && (linebuf[len - 1] == '\r' || linebuf[len - 1] == '\n'))
-+      linebuf[--len] = '\0';
-+
-+    line = linebuf;
-+    while (len && pj_isspace(*line))
-+      ++line, --len;
-+
-+    if (len == 0)
-+      break;
-+
-+    /* Ignore subsequent media descriptors */
-+    if (media_cnt > 1)
-+      continue;
-+
-+    switch (line[0]) {
-+    case 'm': {
-+      int cnt;
-+      char media[32], portstr[32];
-+
-+      ++media_cnt;
-+      if (media_cnt > 1) {
-+        puts("Media line ignored");
-+        break;
-+      }
-+
-+      cnt = sscanf(line + 2, "%s %s RTP/", media, portstr);
-+      if (cnt != 2) {
-+        PJ_LOG(1, (THIS_FILE, "Error parsing media line"));
-+        goto on_error;
-+      }
-+
-+      comp0_port = atoi(portstr);
-+
-+    } break;
-+    case 'c': {
-+      int cnt;
-+      char c[32], net[32], ip[80];
-+
-+      cnt = sscanf(line + 2, "%s %s %s", c, net, ip);
-+      if (cnt != 3) {
-+        PJ_LOG(1, (THIS_FILE, "Error parsing connection line"));
-+        goto on_error;
-+      }
-+
-+      strcpy(comp0_addr, ip);
-+    } break;
-+    case 'a': {
-+      char *attr = strtok(line + 2, ": \t\r\n");
-+      if (strcmp(attr, "ice-ufrag") == 0) {
-+        strcpy(icedemo.rem.ufrag, attr + strlen(attr) + 1);
-+      } else if (strcmp(attr, "ice-pwd") == 0) {
-+        strcpy(icedemo.rem.pwd, attr + strlen(attr) + 1);
-+      } else if (strcmp(attr, "rtcp") == 0) {
-+        char *val = attr + strlen(attr) + 1;
-+        int af, cnt;
-+        int port;
-+        char net[32], ip[64];
-+        pj_str_t tmp_addr;
-+        pj_status_t status;
-+
-+        cnt = sscanf(val, "%d IN %s %s", &port, net, ip);
-+        if (cnt != 3) {
-+          PJ_LOG(1, (THIS_FILE, "Error parsing rtcp attribute"));
-+          goto on_error;
-+        }
-+
-+        if (strchr(ip, ':'))
-+          af = pj_AF_INET6();
-+        else
-+          af = pj_AF_INET();
-+
-+        pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);
-+        tmp_addr = pj_str(ip);
-+        status =
-+            pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1], &tmp_addr);
-+        if (status != PJ_SUCCESS) {
-+          PJ_LOG(1, (THIS_FILE, "Invalid IP address"));
-+          goto on_error;
-+        }
-+        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], 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 tcptype %s\n", foundation,
-+                   &comp_id, transport, &prio, ipaddr, &port, type, tcp_type);
-+        if (cnt != 7 && cnt != 8) {
-+          PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line", cnt));
-+          goto on_error;
-+        }
-+
-+        if (strcmp(transport, "TCP") == 0) {
-+          is_tcp = PJ_TRUE;
-+        }
-+
-+        cand = &icedemo.rem.cand[icedemo.rem.cand_cnt];
-+        pj_bzero(cand, sizeof(*cand));
-+
-+        if (strcmp(type, "host") == 0)
-+          cand->type = PJ_ICE_CAND_TYPE_HOST;
-+        else if (strcmp(type, "srflx") == 0)
-+          cand->type = PJ_ICE_CAND_TYPE_SRFLX;
-+        else if (strcmp(type, "relay") == 0)
-+          cand->type = PJ_ICE_CAND_TYPE_RELAYED;
-+        else {
-+          PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", type));
-+          goto on_error;
-+        }
-+
-+        if (is_tcp) {
-+          if (strcmp(tcp_type, "active") == 0)
-+            cand->transport = PJ_CAND_TCP_ACTIVE;
-+          else if (strcmp(tcp_type, "passive") == 0)
-+            cand->transport = PJ_CAND_TCP_PASSIVE;
-+          else if (strcmp(tcp_type, "so") == 0)
-+            cand->transport = PJ_CAND_TCP_SO;
-+          else {
-+            PJ_LOG(1,
-+                   (THIS_FILE, "Error: invalid transport type '%s'", tcp_type));
-+            goto on_error;
-+          }
-+        } else {
-+          cand->transport = PJ_CAND_UDP;
-+        }
-+
-+        cand->comp_id = (pj_uint8_t)comp_id;
-+        pj_strdup2(icedemo.pool, &cand->foundation, foundation);
-+        cand->prio = prio;
-+
-+        if (strchr(ipaddr, ':'))
-+          af = pj_AF_INET6();
-+        else
-+          af = pj_AF_INET();
-+
-+        tmpaddr = pj_str(ipaddr);
-+        pj_sockaddr_init(af, &cand->addr, NULL, 0);
-+        status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr);
-+        if (status != PJ_SUCCESS) {
-+          PJ_LOG(1, (THIS_FILE, "Error: invalid IP address '%s'", ipaddr));
-+          goto on_error;
-+        }
-+
-+        pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port);
-+
-+        ++icedemo.rem.cand_cnt;
-+
-+        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;
-     }
-+  }
-+
-+  if (icedemo.rem.cand_cnt == 0 || icedemo.rem.ufrag[0] == 0 ||
-+      icedemo.rem.pwd[0] == 0 || icedemo.rem.comp_cnt == 0) {
-+    PJ_LOG(1, (THIS_FILE, "Error: not enough info"));
-+    goto on_error;
-+  }
-+
-+  if (comp0_port == 0 || comp0_addr[0] == '\0') {
-+    PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found"));
-+    goto on_error;
-+  } else {
-+    int af;
-+    pj_str_t tmp_addr;
-+    pj_status_t status;
- 
--    if (comp0_port==0 || comp0_addr[0]=='\0') {
--	PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found"));
--	goto on_error;
--    } else {
--	int af;
--	pj_str_t tmp_addr;
--	pj_status_t status;
-+    if (strchr(comp0_addr, ':'))
-+      af = pj_AF_INET6();
-+    else
-+      af = pj_AF_INET();
- 
--	if (strchr(comp0_addr, ':'))
--	    af = pj_AF_INET6();
--	else
--	    af = pj_AF_INET();
--
--	pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0);
--	tmp_addr = pj_str(comp0_addr);
--	status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0],
--					  &tmp_addr);
--	if (status != PJ_SUCCESS) {
--	    PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line"));
--	    goto on_error;
--	}
--	pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port);
-+    pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0);
-+    tmp_addr = pj_str(comp0_addr);
-+    status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0], &tmp_addr);
-+    if (status != PJ_SUCCESS) {
-+      PJ_LOG(1, (THIS_FILE, "Invalid IP address in c= line"));
-+      goto on_error;
-     }
-+    pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port);
-+  }
- 
--    PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 
--	       icedemo.rem.cand_cnt));
--    return;
-+  PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added",
-+             icedemo.rem.cand_cnt));
-+  return;
- 
- on_error:
--    reset_rem_info();
-+  reset_rem_info();
- }
- 
--
- /*
-  * Start ICE negotiation! This function is invoked from the menu.
-  */
-@@ -1213,52 +1293,53 @@ int main(int argc, char *argv[])
-     icedemo.opt.max_host = -1;
- 
-     while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) {
--	switch (c) {
--	case 'c':
--	    icedemo.opt.comp_cnt = atoi(pj_optarg);
--	    if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) {
--		puts("Invalid component count value");
--		return 1;
--	    }
--	    break;
--	case 'n':
--	    icedemo.opt.ns = pj_str(pj_optarg);
--	    break;
--	case 'H':
--	    icedemo.opt.max_host = atoi(pj_optarg);
--	    break;
--	case 'h':
--	    icedemo_usage();
--	    return 0;
--	case 's':
--	    icedemo.opt.stun_srv = pj_str(pj_optarg);
--	    break;
--	case 't':
--	    icedemo.opt.turn_srv = pj_str(pj_optarg);
--	    break;
--	case 'T':
--	    icedemo.opt.turn_tcp = PJ_TRUE;
--	    break;
--	case 'u':
--	    icedemo.opt.turn_username = pj_str(pj_optarg);
--	    break;
--	case 'p':
--	    icedemo.opt.turn_password = pj_str(pj_optarg);
--	    break;
--	case 'F':
--	    icedemo.opt.turn_fingerprint = PJ_TRUE;
--	    break;
--	case 'R':
--	    icedemo.opt.regular = PJ_TRUE;
--	    break;
--	case 'L':
--	    icedemo.opt.log_file = pj_optarg;
--	    break;
--	default:
--	    printf("Argument \"%s\" is not valid. Use -h to see help",
--		   argv[pj_optind]);
--	    return 1;
--	}
-+      switch (c) {
-+      case 'c':
-+        icedemo.opt.comp_cnt = atoi(pj_optarg);
-+        if (icedemo.opt.comp_cnt < 1 ||
-+            icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) {
-+          puts("Invalid component count value");
-+          return 1;
-+        }
-+        break;
-+      case 'n':
-+        icedemo.opt.ns = pj_str(pj_optarg);
-+        break;
-+      case 'H':
-+        icedemo.opt.max_host = atoi(pj_optarg);
-+        break;
-+      case 'h':
-+        icedemo_usage();
-+        return 0;
-+      case 's':
-+        icedemo.opt.stun_srv = pj_str(pj_optarg);
-+        break;
-+      case 't':
-+        icedemo.opt.turn_srv = pj_str(pj_optarg);
-+        break;
-+      case 'T':
-+        icedemo.opt.ice_tcp = PJ_TRUE;
-+        break;
-+      case 'u':
-+        icedemo.opt.turn_username = pj_str(pj_optarg);
-+        break;
-+      case 'p':
-+        icedemo.opt.turn_password = pj_str(pj_optarg);
-+        break;
-+      case 'F':
-+        icedemo.opt.turn_fingerprint = PJ_TRUE;
-+        break;
-+      case 'R':
-+        icedemo.opt.regular = PJ_TRUE;
-+        break;
-+      case 'L':
-+        icedemo.opt.log_file = pj_optarg;
-+        break;
-+      default:
-+        printf("Argument \"%s\" is not valid. Use -h to see help",
-+               argv[pj_optind]);
-+        return 1;
-+      }
-     }
- 
-     status = icedemo_init();
-diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
-index ea1e72b8..39d05d1e 100644
---- a/pjsip/src/pjsua-lib/pjsua_core.c
-+++ b/pjsip/src/pjsua-lib/pjsua_core.c
-@@ -1529,10 +1529,10 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
- 	pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
- 	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,
--				     NULL, sess, &sess->stun_sock);
--	if (status != PJ_SUCCESS) {
-+        status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
-+                                     sess->af, PJ_STUN_TP_UDP, &stun_sock_cb,
-+                                     NULL, sess, &sess->stun_sock);
-+        if (status != PJ_SUCCESS) {
- 	    char errmsg[PJ_ERR_MSG_SIZE];
- 	    pj_strerror(status, errmsg, sizeof(errmsg));
- 	    PJ_LOG(4,(THIS_FILE, 
diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak
index 08e01c4dc48c422acdb9950c5eea0617df0400d9..85c3834315ded3d7d12b7b305d1b4f1e4315c5a6 100644
--- a/contrib/src/pjproject/rules.mak
+++ b/contrib/src/pjproject/rules.mak
@@ -1,10 +1,9 @@
 # PJPROJECT
-PJPROJECT_VERSION := 5dfa75be7d69047387f9b0436dd9492bbbf03fe4
+PJPROJECT_VERSION := 2.10
 PJPROJECT_URL := https://github.com/pjsip/pjproject/archive/$(PJPROJECT_VERSION).tar.gz
 
-PJPROJECT_OPTIONS := --disable-oss          \
-                     --disable-sound        \
-                     --disable-video        \
+PJPROJECT_OPTIONS := --disable-sound        \
+                     --enable-video         \
                      --enable-ext-sound     \
                      --disable-speex-aec    \
                      --disable-g711-codec   \
@@ -24,10 +23,6 @@ PJPROJECT_OPTIONS := --disable-oss          \
                      --disable-libwebrtc    \
                      --with-gnutls=$(PREFIX)
 
-ifdef HAVE_WIN64
-PJPROJECT_EXTRA_CFLAGS += -DPJ_WIN64=1
-endif
-
 PKGS += pjproject
 # FIXME: nominally 2.2.0 is enough, but it has to be patched for gnutls
 ifeq ($(call need_pkg,'libpjproject'),)
@@ -35,10 +30,12 @@ PKGS_FOUND += pjproject
 endif
 
 DEPS_pjproject += gnutls
-ifndef HAVE_WIN32
 ifndef HAVE_MACOSX
 DEPS_pjproject += uuid
 endif
+
+ifdef HAVE_LINUX
+PJPROJECT_OPTIONS += --enable-epoll
 endif
 
 $(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.gz:
@@ -48,31 +45,18 @@ $(TARBALLS)/pjproject-$(PJPROJECT_VERSION).tar.gz:
 
 pjproject: pjproject-$(PJPROJECT_VERSION).tar.gz .sum-pjproject
 	$(UNPACK)
-ifdef HAVE_WIN32
-	$(APPLY) $(SRC)/pjproject/pj_win.patch
-endif
+	$(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-pj_ice_sess.patch
+	$(APPLY) $(SRC)/pjproject/0008-fix_ioqueue_ipv6_sendto.patch
+	$(APPLY) $(SRC)/pjproject/0009-add-config-site.patch
 ifdef HAVE_ANDROID
-	$(APPLY) $(SRC)/pjproject/android.patch
+	$(APPLY) $(SRC)/pjproject/0001-android.patch
 endif
-	$(APPLY) $(SRC)/pjproject/fix_turn_alloc_failure.patch
-	$(APPLY) $(SRC)/pjproject/rfc2466.patch
-	$(APPLY) $(SRC)/pjproject/ipv6.patch
-	$(APPLY) $(SRC)/pjproject/multiple_listeners.patch
-	$(APPLY) $(SRC)/pjproject/pj_ice_sess.patch
-	$(APPLY) $(SRC)/pjproject/fix_turn_fallback.patch
-	$(APPLY) $(SRC)/pjproject/fix_ioqueue_ipv6_sendto.patch
-	$(APPLY) $(SRC)/pjproject/add_dtls_transport.patch
-	$(APPLY) $(SRC)/pjproject/rfc6544.patch
-	$(APPLY) $(SRC)/pjproject/ice_config.patch
-	$(APPLY) $(SRC)/pjproject/sip_config.patch
-	$(APPLY) $(SRC)/pjproject/fix_first_packet_turn_tcp.patch
-	$(APPLY) $(SRC)/pjproject/fix_ebusy_turn.patch
-	$(APPLY) $(SRC)/pjproject/ignore_ipv6_on_transport_check.patch
-	$(APPLY) $(SRC)/pjproject/fix_turn_connection_failure.patch
-	$(APPLY) $(SRC)/pjproject/disable_local_resolution.patch
-	$(APPLY) $(SRC)/pjproject/fix_assert_on_connection_attempt.patch
-	$(APPLY) $(SRC)/pjproject/keep_alive.patch
-	$(APPLY) $(SRC)/pjproject/sip_td_timeout.patch
 	$(UPDATE_AUTOCONFIG)
 	$(MOVE)
 
diff --git a/contrib/src/pjproject/sip_config.patch b/contrib/src/pjproject/sip_config.patch
deleted file mode 100644
index 693a43c084adc739e26bbd91f6a8683e7579cd69..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/sip_config.patch
+++ /dev/null
@@ -1,23 +0,0 @@
- pjsip/include/pjsip/sip_config.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
-index 904df24e..f36b2d6a 100644
---- a/pjsip/include/pjsip/sip_config.h
-+++ b/pjsip/include/pjsip/sip_config.h
-@@ -359,7 +359,7 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
-  * containing presence information can be quite large (>1500).
-  */
- #ifndef PJSIP_MAX_PKT_LEN
--#   define PJSIP_MAX_PKT_LEN		4000
-+#   define PJSIP_MAX_PKT_LEN		8000
- #endif
- 
- 
-@@ -658,7 +658,7 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
-  * Default: 600
-  */
- #ifndef PJSIP_TRANSPORT_SERVER_IDLE_TIME
--#   define PJSIP_TRANSPORT_SERVER_IDLE_TIME	600
-+#   define PJSIP_TRANSPORT_SERVER_IDLE_TIME	3
- #endif
diff --git a/contrib/src/pjproject/uwp_vs.patch b/contrib/src/pjproject/uwp_vs.patch
deleted file mode 100644
index 30c82e7d39ea325121f0459224f43aeac9d1f315..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/uwp_vs.patch
+++ /dev/null
@@ -1,17 +0,0 @@
---- a/pjlib/build/pjlib.vcxproj
-+++ b/pjlib/build/pjlib.vcxproj
-@@ -633,9 +633,11 @@
-     </ClCompile>
-     <ClCompile Include="..\src\pj\ip_helper_win32.c">
-       <ExcludedFromBuild Condition="'$(API_Family)'!='WinDesktop'">true</ExcludedFromBuild>
-+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
-     </ClCompile>
-     <ClCompile Include="..\src\pj\ip_helper_generic.c">
-       <ExcludedFromBuild Condition="'$(API_Family)'=='WinDesktop'">true</ExcludedFromBuild>
-+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
-     </ClCompile>
-     <ClCompile Include="..\src\pj\list.c" />
-     <ClCompile Include="..\src\pj\lock.c" />
--- 
-2.10.2.windows.1
-
diff --git a/contrib/src/pjproject/win_config.patch b/contrib/src/pjproject/win_config.patch
deleted file mode 100644
index 70c790c23537c854de0941ecfa792c4f3034db68..0000000000000000000000000000000000000000
--- a/contrib/src/pjproject/win_config.patch
+++ /dev/null
@@ -1,9 +0,0 @@
---- /dev/null
-+++ b/pjlib/include/pj/config_site.h
-@@ -0,0 +1,3 @@
-+#define THIRD_PARTY_MEDIA			0
-+#include "config_site_sample.h"
-+
--- 
-2.10.2.windows.1
-
diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp
index 6e7d9b83b5df513d99acd4265712dc44faf8f111..7f7361740572be2f436bcad311fb4afb571b6a74 100644
--- a/src/ice_transport.cpp
+++ b/src/ice_transport.cpp
@@ -185,7 +185,7 @@ public:
     bool handleEvents(unsigned max_msec);
 
     // Wait data on components
-    std::vector<pj_ssize_t> lastReadLen_;
+    pj_ssize_t lastReadLen_;
     std::condition_variable waitDataCv_ = {};
 
     onShutdownCb scb;
@@ -307,7 +307,6 @@ IceTransport::Impl::Impl(const char* name, int component_count, bool master,
     }
 
     peerChannels_.resize(component_count_ + 1);
-    lastReadLen_.resize(component_count_);
 
     // Add local hosts (IPv4, IPv6) as stun candidates
     add_stun_server(config_, pj_AF_INET6());
@@ -338,13 +337,10 @@ IceTransport::Impl::Impl(const char* name, int component_count, bool master,
             JAMI_WARN("null IceTransport");
     };
 
-    icecb.on_data_sent = [](pj_ice_strans* ice_st, unsigned comp_id,
-                                pj_ssize_t size) {
+    icecb.on_data_sent = [](pj_ice_strans* ice_st, pj_ssize_t size) {
         if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st))) {
-          if (comp_id > 0 && comp_id - 1 < tr->lastReadLen_.size()) {
-            tr->lastReadLen_[comp_id - 1] = size;
+            tr->lastReadLen_ = size;
             tr->waitDataCv_.notify_all();
-          }
         } else
             JAMI_WARN("null IceTransport");
     };
@@ -1313,15 +1309,15 @@ IceTransport::send(int comp_id, const unsigned char* buf, size_t len)
         return -1;
     }
     pj_ssize_t sent_size = 0;
-    auto status = pj_ice_strans_sendto2(pimpl_->icest_.get(), comp_id+1, buf, len, remote.pjPtr(), remote.getLength(), &sent_size);
+    auto status = pj_ice_strans_sendto2(pimpl_->icest_.get(), comp_id+1, buf, len, remote.pjPtr(), remote.getLength());
     if (status == PJ_EPENDING && isTCPEnabled()) {
         auto current_size = sent_size;
         // NOTE; because we are in TCP, the sent size will count the header (2
         // bytes length).
-        while (static_cast<std::size_t>(comp_id) < pimpl_->lastReadLen_.size() && current_size < static_cast<pj_ssize_t>(len)) {
+        while (current_size < static_cast<pj_ssize_t>(len)) {
           std::unique_lock<std::mutex> lk(pimpl_->iceMutex_);
           pimpl_->waitDataCv_.wait(lk);
-          current_size = pimpl_->lastReadLen_[comp_id];
+          current_size = pimpl_->lastReadLen_;
         }
     } else if (status != PJ_SUCCESS && status != PJ_EPENDING) {
         if (status == PJ_EBUSY) {