diff --git a/contrib/src/pjproject/rfc6062.patch b/contrib/src/pjproject/rfc6062.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0c4fc73406bd6bd19c447782032f767cc7322de8
--- /dev/null
+++ b/contrib/src/pjproject/rfc6062.patch
@@ -0,0 +1,877 @@
+Copyright (c) 2017 Savoir-faire Linux Inc.
+
+turn: rfc 6062 support
+
+Current implementation of TURN and STUN doesn't support TCP as a valid
+peer connection. This connection is defined by the rfc 6062.
+This patch is an implementation proposal of this rfc into PJNATH.
+Modifications are API backware compatible, not ABI.
+Users must rebuild their code.
+
+Written by
+Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
+on behalf of Savoir-faire Linux.
+
+----
+
+--- a/pjnath/include/pjnath/config.h	2017-09-27 16:48:31.000000000 -0400
++++ b/pjnath/include/pjnath/config.h	2017-09-28 08:23:37.000000000 -0400
+@@ -220,6 +220,14 @@
+ #   define PJ_TURN_KEEP_ALIVE_SEC		    15
+ #endif
+ 
++/**
++ * Maximal number of TCP data connection that a client can open/accept with
++ * peers.
++ */
++#ifndef PJ_TURN_MAX_TCP_CNX
++#   define PJ_TURN_MAX_TCP_CNX              32
++#endif
++
+ 
+ /* **************************************************************************
+  * ICE CONFIGURATION
+--- a/pjnath/include/pjnath/stun_msg.h	2017-01-17 22:50:32.000000000 -0500
++++ b/pjnath/include/pjnath/stun_msg.h	2017-09-28 08:23:37.000000000 -0400
+@@ -92,6 +92,21 @@
+      */
+     PJ_STUN_CHANNEL_BIND_METHOD		    = 9,
+ 
++    /*
++     * STUN/TURN Connect as defined by RFC 6062
++     */
++    PJ_STUN_CONNECT_METHOD                 = 10,
++
++    /*
++     * STUN/TURN ConnectionBind as defined by RFC 6062
++     */
++    PJ_STUN_CONNECTION_BIND_METHOD         = 11,
++
++    /*
++     * STUN/TURN ConnectionAttempt as defined by RFC 6062
++     */
++    PJ_STUN_CONNECTION_ATTEMPT_METHOD      = 12,
++
+     /**
+      * All known methods.
+      */
+@@ -261,6 +276,16 @@
+      */
+     PJ_STUN_DATA_INDICATION		    = 0x0017,
+ 
++	/**
++     * STUN/TURN ConnectBind Request
++     */
++    PJ_STUN_CONNECTION_BIND_REQUEST		    = 0x000b,
++
++    /**
++     * TURN ConnectionAttempt indication
++     */
++    PJ_STUN_CONNECTION_ATTEMPT_INDICATION  = 0x001c,
++
+ 
+     /**
+      * TURN CreatePermission request
+@@ -333,6 +358,7 @@
+     PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM	    */
+     PJ_STUN_ATTR_PRIORITY	    = 0x0024,/**< PRIORITY		    */
+     PJ_STUN_ATTR_USE_CANDIDATE	    = 0x0025,/**< USE-CANDIDATE		    */
++    PJ_STUN_ATTR_CONNECTION_ID = 0x002a,/**< CONNECTION-ID		    */
+     PJ_STUN_ATTR_ICMP		    = 0x0030,/**< ICMP (TURN)		    */
+ 
+     PJ_STUN_ATTR_END_MANDATORY_ATTR,
+--- a/pjnath/include/pjnath/stun_session.h	2013-10-01 01:00:57.000000000 -0400
++++ b/pjnath/include/pjnath/stun_session.h	2017-09-28 08:23:37.000000000 -0400
+@@ -751,6 +751,13 @@
+ 					pj_stun_tx_data *tdata);
+ 
+ 
++PJ_DEF(void) pj_stun_session_get_server_cred(
++	pj_stun_session *sess,
++	pj_pool_t *pool, pj_str_t *nonce, pj_str_t *realm);
++
++PJ_DEF(void) pj_stun_session_set_server_cred(
++	pj_stun_session *sess, const pj_str_t *nonce, pj_str_t *realm);
++
+ /**
+  * @}
+  */
+--- a/pjnath/include/pjnath/turn_session.h	2016-11-14 01:13:01.000000000 -0500
++++ b/pjnath/include/pjnath/turn_session.h	2017-09-28 08:26:16.000000000 -0400
+@@ -184,6 +184,12 @@
+     PJ_TURN_STATE_ALLOCATING,
+ 
+     /**
++     * TURN session has issued CONNECTION-BIND request and is waiting for response
++     * from the TURN server.
++     */
++    PJ_TURN_STATE_CONNECTION_BINDING,
++
++    /**
+      * TURN session has successfully allocated relay resoruce and now is
+      * ready to be used.
+      */
+@@ -298,6 +304,20 @@
+ 		     pj_turn_state_t old_state,
+ 		     pj_turn_state_t new_state);
+ 
++    /**
++     * Notification when TURN session get a ConnectionAttempt indication.
++     *
++     * @param sess			The TURN session.
++     * @param conn_id		The connection-id to use for connection binding.
++     * @param peer_addr	Peer address that tried to connect on the TURN server.
++     * @param addr_len		Length of the peer address.
++
++     */
++    void (*on_peer_connection)(pj_turn_session *sess,
++                               pj_uint32_t conn_id,
++                               const pj_sockaddr_t *peer_addr,
++                               unsigned addr_len);
++
+ } pj_turn_session_cb;
+ 
+ 
+@@ -339,6 +359,14 @@
+      */
+     int	    af;
+ 
++    /**
++     * Type of connection to from TURN server to peer.
++     *
++     * Supported values: PJ_TURN_TP_UDP (rfc 5766), PJ_TURN_TP_TLS (rfc 6062)
++     *
++     * Default is PJ_TURN_TP_UDP.
++     */
++    pj_turn_tp_type peer_conn_type;
+ 
+ } pj_turn_alloc_param;
+ 
+@@ -741,6 +769,15 @@
+ 					       pj_size_t pkt_len,
+ 					       pj_size_t *parsed_len);
+ 
++/**
++ * rfc6062
++ */
++PJ_DEF(void) pj_turn_session_get_server_cred(
++	pj_turn_session *sess,
++	pj_pool_t *pool, pj_str_t *nonce, pj_str_t *realm);
++
++PJ_DEF(void) pj_turn_session_set_server_cred(
++	pj_turn_session *sess, const pj_str_t *nonce, pj_str_t *realm);
+ 
+ /**
+  * @}
+--- a/pjnath/include/pjnath/turn_sock.h	2013-10-01 01:00:57.000000000 -0400
++++ b/pjnath/include/pjnath/turn_sock.h	2017-09-28 08:23:37.000000000 -0400
+@@ -98,6 +98,20 @@
+ 		     pj_turn_state_t old_state,
+ 		     pj_turn_state_t new_state);
+ 
++    /**
++     * Notification when TURN session get a ConnectionAttempt indication.
++     *
++     * @param turn_sock	The TURN client transport.
++     * @param conn_id		The connection-id to use for connection binding.
++     * @param peer_addr	Peer address that tried to connect on the TURN server.
++     * @param addr_len		Length of the peer address.
++
++     */
++    void (*on_peer_connection)(pj_turn_sock *turn_sock,
++                               pj_uint32_t conn_id,
++                               const pj_sockaddr_t *peer_addr,
++                               unsigned addr_len);
++
+ } pj_turn_sock_cb;
+ 
+ 
+@@ -446,6 +460,13 @@
+ 					       const pj_sockaddr_t *peer,
+ 					       unsigned addr_len);
+ 
++/**
++ * RFC 6062
++ */
++PJ_DECL(pj_status_t) pj_turn_connect_peer(pj_turn_sock *sock,
++										  pj_uint32_t conn_id,
++										  const pj_sockaddr_t *peer_addr,
++										  unsigned addr_len);
+ 
+ /**
+  * @}
+--- a/pjnath/src/pjnath/stun_msg.c	2017-01-17 22:50:32.000000000 -0500
++++ b/pjnath/src/pjnath/stun_msg.c	2017-09-28 08:23:37.000000000 -0400
+@@ -45,6 +45,9 @@
+     "Data",			/* 7 */
+     "CreatePermission",		/* 8 */
+     "ChannelBind",		/* 9 */
++    "Connect",           /* 10 */
++    "ConnectionBind",    /* 11 */
++    "ConnectionAttempt", /* 12 */
+ };
+ 
+ static struct
+@@ -476,11 +479,11 @@
+ 	NULL
+     },
+     {
+-	/* ID 0x002a is not assigned */
+-	NULL,
+-	NULL,
+-	NULL,
+-	NULL
++	/* PJ_STUN_ATTR_CONNECTION_ID, */
++	"CONNECTION-ID",
++	&decode_uint_attr,
++	&encode_uint_attr,
++	&clone_uint_attr
+     },
+     {
+ 	/* ID 0x002b is not assigned */
+--- a/pjnath/src/pjnath/stun_session.c	2016-01-05 09:34:22.000000000 -0500
++++ b/pjnath/src/pjnath/stun_session.c	2017-09-28 08:23:37.000000000 -0400
+@@ -1511,3 +1511,14 @@
+     return status;
+ }
+ 
++PJ_DEF(void) pj_stun_session_get_server_cred(pj_stun_session *sess, pj_pool_t *pool, pj_str_t *nonce, pj_str_t *realm)
++{
++	pj_strdup(pool, nonce,  &sess->next_nonce);
++	pj_strdup(pool, realm,  &sess->server_realm);
++}
++
++PJ_DEF(void) pj_stun_session_set_server_cred(pj_stun_session *sess, const pj_str_t *nonce, pj_str_t *realm)
++{
++	pj_strdup(sess->pool, &sess->next_nonce, nonce);
++	pj_strdup(sess->pool, &sess->server_realm, realm);
++}
+--- a/pjnath/src/pjnath/turn_session.c	2017-09-27 16:48:31.000000000 -0400
++++ b/pjnath/src/pjnath/turn_session.c	2017-09-28 08:23:37.000000000 -0400
+@@ -42,6 +42,7 @@
+     "Resolving",
+     "Resolved",
+     "Allocating",
++	"TcpBinding",
+     "Ready",
+     "Deallocating",
+     "Deallocated",
+@@ -208,6 +209,7 @@
+ PJ_DEF(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm)
+ {
+     pj_bzero(prm, sizeof(*prm));
++	prm->peer_conn_type = PJ_TURN_TP_UDP;
+ }
+ 
+ /*
+@@ -403,6 +405,11 @@
+ 	sess->pending_destroy = PJ_TRUE;
+ 	can_destroy = PJ_FALSE;
+ 	break;
++    case PJ_TURN_STATE_CONNECTION_BINDING:
++	/* We need to wait until connection binding complete */
++	sess->pending_destroy = PJ_TRUE;
++	can_destroy = PJ_FALSE;
++	break;
+     case PJ_TURN_STATE_READY:
+ 	/* Send REFRESH with LIFETIME=0 */
+ 	can_destroy = PJ_FALSE;
+@@ -719,6 +726,9 @@
+     PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && 
+ 		     sess->state<=PJ_TURN_STATE_RESOLVED, 
+ 		     PJ_EINVALIDOP);
++    PJ_ASSERT_RETURN(param->peer_conn_type == PJ_TURN_TP_UDP ||
++                     param->peer_conn_type == PJ_TURN_TP_TCP,
++                     PJ_EINVAL);
+ 
+     /* Verify address family in allocation param */
+     if (param && param->af) {
+@@ -756,7 +766,7 @@
+     /* MUST include REQUESTED-TRANSPORT attribute */
+     pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
+ 			      PJ_STUN_ATTR_REQ_TRANSPORT, 
+-			      PJ_STUN_SET_RT_PROTO(PJ_TURN_TP_UDP));
++			      PJ_STUN_SET_RT_PROTO(param->peer_conn_type));
+ 
+     /* Include BANDWIDTH if requested */
+     if (sess->alloc_param.bandwidth > 0) {
+@@ -994,6 +1004,13 @@
+ 	}
+     }
+ 
++	/* rfc6062: direct send if peer connection is TCP */
++	if (sess->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) {
++		status = sess->cb.on_send_pkt(sess, pkt, pkt_len,
++									  addr, addr_len);
++		goto on_return;
++	}
++
+     /* See if the peer is bound to a channel number */
+     ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), 
+ 			   PJ_FALSE, PJ_FALSE);
+@@ -1670,6 +1687,32 @@
+ 
+     sess = (pj_turn_session*)pj_stun_session_get_user_data(stun);
+ 
++    if (msg->hdr.type == PJ_STUN_CONNECTION_ATTEMPT_INDICATION) {
++        pj_stun_uint_attr *connection_id_attr;
++        /* Get CONNECTION-ID attribute */
++        connection_id_attr = (pj_stun_uint_attr*)
++            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CONNECTION_ID, 0);
++
++        /* Get XOR-PEER-ADDRESS attribute */
++        peer_attr = (pj_stun_xor_peer_addr_attr*)
++            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
++
++        /* Must have both XOR-PEER-ADDRESS and CONNECTION-ID attributes */
++        if (!peer_attr || !connection_id_attr) {
++            PJ_LOG(4,(sess->obj_name, 
++                      "Received ConnectionAttempt indication with missing attributes"));
++            return PJ_EINVALIDOP;
++        }
++
++        /* Notify application */
++        if (sess->cb.on_peer_connection) {
++            (*sess->cb.on_peer_connection)(sess, connection_id_attr->value,
++                                           &peer_attr->sockaddr,
++                                           pj_sockaddr_get_len(&peer_attr->sockaddr));
++        }
++        return PJ_SUCCESS;
++    }
++
+     /* Expecting Data Indication only */
+     if (msg->hdr.type != PJ_STUN_DATA_INDICATION) {
+ 	PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s indication",
+@@ -2089,3 +2132,16 @@
+     pj_grp_lock_release(sess->grp_lock);
+ }
+ 
++PJ_DEF(void) pj_turn_session_get_server_cred(pj_turn_session *sess,
++											 pj_pool_t *pool, pj_str_t *nonce,
++											 pj_str_t *realm)
++{
++	pj_stun_session_get_server_cred(sess->stun, pool, nonce, realm);
++}
++
++PJ_DEF(void) pj_turn_session_set_server_cred(pj_turn_session *sess,
++											 const pj_str_t *nonce,
++											 pj_str_t *realm)
++{
++	pj_stun_session_set_server_cred(sess->stun, nonce, realm);
++}
+--- a/pjnath/src/pjnath/turn_sock.c	2017-01-19 02:41:25.000000000 -0500
++++ b/pjnath/src/pjnath/turn_sock.c	2017-09-28 08:23:37.000000000 -0400
+@@ -35,9 +35,29 @@
+ 
+ enum { MAX_BIND_RETRY = 100 };
+ 
++enum { CONNECTION_USED = (1<<0), /* TCP connection slot is used or free */
++	   CONNECTION_READY = (1<<1) /* TCP connection bind and ready to use for data transfer */
++};
+ 
+ #define INIT	0x1FFFFFFF
+ 
++/**
++ * pj_turn_tcp_data_connection contains information on TCP connection open between
++ * the client and the turn server, conveying data from/to a specific peer.
++ * notes: part of RFC 6062 support
++ */
++typedef struct pj_turn_tcp_data_connection
++{
++    pj_uint32_t id;						/* identity of this connection as given by the TURN server */
++    pj_uint32_t flags;					/* 0 or CONNECTION_USED */
++	pj_sockaddr peer_addr;				/* mapped address of connected peer */
++	unsigned peer_addr_len;
++    pj_activesock_t	*active_tcp_sock;	/* socket between client and TURN server */
++	pj_ioqueue_op_key_t send_key;
++	pj_stun_session *stun_sess;			/* STUN session used to send ConnectBind msg */
++	pj_turn_sock *turn_sock;		    /* up link */
++} pj_turn_tcp_data_connection;
++
+ struct pj_turn_sock
+ {
+     pj_pool_t		*pool;
+@@ -59,6 +79,11 @@
+     pj_turn_tp_type	 conn_type;
+     pj_activesock_t	*active_sock;
+     pj_ioqueue_op_key_t	 send_key;
++
++    /* RFC 6062 */
++	pj_stun_auth_cred cred; /* saved from control connection */
++    pj_size_t tcp_cnx_count; /* number of elements in tcp_cnx */
++    pj_turn_tcp_data_connection	tcp_cnx[PJ_TURN_MAX_TCP_CNX]; /* peer dedicated data connections throught the TURN server */
+ };
+ 
+ 
+@@ -82,6 +107,11 @@
+ static void turn_on_state(pj_turn_session *sess, 
+ 			  pj_turn_state_t old_state,
+ 			  pj_turn_state_t new_state);
++static void turn_on_peer_connection(pj_turn_session *sess,
++                                    pj_uint32_t conn_id,
++                                    const pj_sockaddr_t *peer_addr,
++                                    unsigned addr_len);
++
+ 
+ static pj_bool_t on_data_read(pj_activesock_t *asock,
+ 			      void *data,
+@@ -97,6 +127,26 @@
+ static void destroy(pj_turn_sock *turn_sock);
+ static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e);
+ 
++static pj_bool_t on_peer_data_read(pj_activesock_t *asock,
++								   void *data,
++								   pj_size_t size,
++								   pj_status_t status,
++								   pj_size_t *remainder);
++static pj_bool_t on_peer_connect_complete(pj_activesock_t *asock,
++										  pj_status_t status);
++static pj_status_t on_tcp_stun_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 void on_tcp_stun_request_complete(pj_stun_session *sess,
++										 pj_status_t status,
++										 void *token,
++										 pj_stun_tx_data *tdata,
++										 const pj_stun_msg *response,
++										 const pj_sockaddr_t *src_addr,
++										 unsigned src_addr_len);
+ 
+ /* Init config */
+ PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg)
+@@ -193,6 +243,7 @@
+     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;
++    sess_cb.on_peer_connection = &turn_on_peer_connection;
+     status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type,
+                                     turn_sock->grp_lock, &sess_cb, 0,
+                                     turn_sock, &turn_sock->sess);
+@@ -238,6 +289,9 @@
+ 	pj_turn_session_shutdown(turn_sock->sess);
+     if (turn_sock->active_sock)
+ 	pj_activesock_close(turn_sock->active_sock);
++	for (int i=0; i < turn_sock->tcp_cnx_count; ++i) {
++		pj_activesock_close(turn_sock->tcp_cnx[i].active_tcp_sock);
++	}
+     pj_grp_lock_dec_ref(turn_sock->grp_lock);
+     pj_grp_lock_release(turn_sock->grp_lock);
+ }
+@@ -411,6 +465,9 @@
+ 
+     /* Set credental */
+     if (cred) {
++		// save credentials for peer/TCP connections
++		if (param->peer_conn_type == PJ_TURN_TP_TCP)
++			pj_memcpy(&turn_sock->cred, cred, sizeof(turn_sock->cred));
+ 	status = pj_turn_session_set_credential(turn_sock->sess, cred);
+ 	if (status != PJ_SUCCESS) {
+ 	    sess_fail(turn_sock, "Error setting credential", status);
+@@ -676,11 +733,35 @@
+ 	return PJ_EINVALIDOP;
+     }
+ 
+-    PJ_UNUSED_ARG(dst_addr);
+-    PJ_UNUSED_ARG(dst_addr_len);
++	/* With TCP peer connection filter by address
++	 * if packet is for the server or the peer
++	 */
++	pj_activesock_t *asock = NULL;
++	pj_turn_session_info info;
++	pj_turn_session_get_info(turn_sock->sess, &info);
++	if (pj_sockaddr_cmp(&info.server, dst_addr) &&
++		turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) {
++		for (int i=0; i < turn_sock->tcp_cnx_count; ++i) {
++			pj_turn_tcp_data_connection *tcp_cnx = &turn_sock->tcp_cnx[i];
++			if ((tcp_cnx->flags & CONNECTION_READY) ==  0)
++				continue;
++			if (!pj_sockaddr_cmp(&tcp_cnx->peer_addr, dst_addr)) {
++				asock = tcp_cnx->active_tcp_sock;
++				break;
++			}
++		}
++		if (!asock) {
++			status = PJ_ENOTFOUND;
++			show_err(turn_sock, "socket send()", status);
++			return status;
++		}
++	} else {
++		asock = turn_sock->active_sock;
++	}
+ 
+-    status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key,
++    status = pj_activesock_send(asock, &turn_sock->send_key,
+ 				pkt, &len, 0);
++
+     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ 	show_err(turn_sock, "socket send()", status);
+     }
+@@ -927,4 +1008,365 @@
+     }
+ }
+ 
++static void turn_on_peer_connection(pj_turn_session *sess,
++                                    pj_uint32_t conn_id,
++                                    const pj_sockaddr_t *peer_addr,
++                                    unsigned addr_len)
++{
++    pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess);
++    if (turn_sock == NULL || turn_sock->is_destroying) {
++        /* We've been destroyed */
++        return;
++    }
++
++    if (turn_sock->cb.on_peer_connection) {
++        (*turn_sock->cb.on_peer_connection)(turn_sock, conn_id,
++                                            peer_addr, addr_len);
++    }
++}
++
++PJ_DECL(pj_status_t) pj_turn_connect_peer(pj_turn_sock *turn_sock,
++										  pj_uint32_t conn_id,
++										  const pj_sockaddr_t *peer_addr,
++										  unsigned addr_len)
++{
++	pj_status_t status;
++	pj_turn_tcp_data_connection *new_tcp_cnx = NULL;
++
++	for (int i=0; i < turn_sock->tcp_cnx_count; ++i) {
++		pj_turn_tcp_data_connection *tcp_cnx = &turn_sock->tcp_cnx[i];
++		if ((tcp_cnx->flags & CONNECTION_USED) == 0) {
++			new_tcp_cnx = tcp_cnx;
++			continue;
++		}
++		if (tcp_cnx->id == conn_id)
++			// TODO: need log
++			return PJ_EINVAL; // TODO: need better error code
++	}
++
++	if (!new_tcp_cnx) {
++		if (turn_sock->tcp_cnx_count == PJ_TURN_MAX_TCP_CNX) {
++			// TODO: need log
++			return PJ_ETOOMANY;
++		}
++		new_tcp_cnx = &turn_sock->tcp_cnx[turn_sock->tcp_cnx_count++];
++	}
++
++	/* Initialize this TCP connection slot */
++	pj_bzero(new_tcp_cnx, sizeof(*new_tcp_cnx));
++	new_tcp_cnx->id = conn_id;
++	new_tcp_cnx->flags = CONNECTION_USED;
++	new_tcp_cnx->turn_sock = turn_sock;
++	pj_sockaddr_cp(&new_tcp_cnx->peer_addr, peer_addr);
++	new_tcp_cnx->peer_addr_len = addr_len;
++
++	pj_ioqueue_op_key_init(&new_tcp_cnx->send_key,
++						   sizeof(new_tcp_cnx->send_key));
++
++	/* Initiate a new TCP connection on TURN server
++	 * that will become the peer data connection */
++	pj_turn_session_info info;
++	int sock_type;
++	pj_sock_t sock;
++	pj_activesock_cfg asock_cfg;
++	pj_activesock_cb asock_cb;
++	pj_sockaddr bound_addr, *cfg_bind_addr;
++	pj_uint16_t max_bind_retry;
++
++	/* Get server address from session info */
++	pj_turn_session_get_info(turn_sock->sess, &info);
++
++	assert(turn_sock->conn_type == PJ_TURN_TP_TCP);
++	sock_type = pj_SOCK_STREAM();
++
++	/* Init socket */
++	status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock);
++	if (status != PJ_SUCCESS) {
++	    pj_turn_sock_destroy(turn_sock);
++	    return status;
++	}
++
++	/* Bind socket */
++	cfg_bind_addr = &turn_sock->setting.bound_addr;
++	max_bind_retry = MAX_BIND_RETRY;
++	if (turn_sock->setting.port_range &&
++	    turn_sock->setting.port_range < max_bind_retry)
++	{
++	    max_bind_retry = turn_sock->setting.port_range;
++	}
++	pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0);
++	if (cfg_bind_addr->addr.sa_family == pj_AF_INET() ||
++	    cfg_bind_addr->addr.sa_family == pj_AF_INET6())
++	{
++	    pj_sockaddr_cp(&bound_addr, cfg_bind_addr);
++	}
++	status = pj_sock_bind_random(sock, &bound_addr,
++								 turn_sock->setting.port_range,
++								 max_bind_retry);
++	if (status != PJ_SUCCESS) {
++	    pj_turn_sock_destroy(turn_sock);
++	    return status;
++	}
++
++	/* Apply socket buffer size */
++	if (turn_sock->setting.so_rcvbuf_size > 0) {
++	    unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size;
++	    status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(),
++										  PJ_TRUE, &sobuf_size);
++	    if (status != PJ_SUCCESS) {
++			pj_perror(3, turn_sock->obj_name, status,
++					  "Failed setting SO_RCVBUF");
++	    } else {
++			if (sobuf_size < turn_sock->setting.so_rcvbuf_size) {
++				PJ_LOG(4, (turn_sock->obj_name,
++						   "Warning! Cannot set SO_RCVBUF as configured,"
++						   " now=%d, configured=%d", sobuf_size,
++						   turn_sock->setting.so_rcvbuf_size));
++			} else {
++				PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d",
++						   sobuf_size));
++			}
++	    }
++	}
++	if (turn_sock->setting.so_sndbuf_size > 0) {
++	    unsigned sobuf_size = turn_sock->setting.so_sndbuf_size;
++	    status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(),
++										  PJ_TRUE, &sobuf_size);
++	    if (status != PJ_SUCCESS) {
++			pj_perror(3, turn_sock->obj_name, status,
++					  "Failed setting SO_SNDBUF");
++	    } else {
++			if (sobuf_size < turn_sock->setting.so_sndbuf_size) {
++				PJ_LOG(4, (turn_sock->obj_name,
++						   "Warning! Cannot set SO_SNDBUF as configured,"
++						   " now=%d, configured=%d", sobuf_size,
++						   turn_sock->setting.so_sndbuf_size));
++			} else {
++				PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d",
++						   sobuf_size));
++			}
++	    }
++	}
++
++	/* Create active socket */
++	pj_activesock_cfg_default(&asock_cfg);
++	asock_cfg.grp_lock = turn_sock->grp_lock;
++
++	pj_bzero(&asock_cb, sizeof(asock_cb));
++	asock_cb.on_data_read = &on_peer_data_read;
++	asock_cb.on_connect_complete = &on_peer_connect_complete;
++	status = pj_activesock_create(turn_sock->pool, sock,
++								  sock_type, &asock_cfg,
++								  turn_sock->cfg.ioqueue, &asock_cb,
++								  new_tcp_cnx,
++								  &new_tcp_cnx->active_tcp_sock);
++	if (status != PJ_SUCCESS) {
++	    pj_turn_sock_destroy(turn_sock);
++	    return status;
++	}
++
++	char addrtxt[PJ_INET6_ADDRSTRLEN+8];
++	PJ_LOG(5,(turn_sock->pool->obj_name,
++			  "Connecting to %s",
++			  pj_sockaddr_print(&info.server, addrtxt,
++								sizeof(addrtxt), 3)));
++
++	status = pj_activesock_start_connect(new_tcp_cnx->active_tcp_sock,
++										 turn_sock->pool,
++										 &info.server,
++										 pj_sockaddr_get_len(&info.server));
++	if (status == PJ_SUCCESS) {
++	    on_peer_connect_complete(new_tcp_cnx->active_tcp_sock, PJ_SUCCESS);
++	} else if (status != PJ_EPENDING) {
++		pj_perror(3, turn_sock->pool->obj_name, status,
++				  "Failed to connect to %s",
++				  pj_sockaddr_print(&info.server, addrtxt,
++									sizeof(addrtxt), 3));
++	    pj_turn_sock_destroy(turn_sock);
++	    return status;
++	}
++
++	return PJ_SUCCESS;
++}
++
++static pj_bool_t on_peer_data_read(pj_activesock_t *asock,
++								   void *data,
++								   pj_size_t size,
++								   pj_status_t status,
++								   pj_size_t *remainder)
++{
++	pj_turn_tcp_data_connection *tcp_cnx;
++	pj_turn_sock *turn_sock;
++
++	tcp_cnx = (pj_turn_tcp_data_connection*) pj_activesock_get_user_data(asock);
++	pj_assert(tcp_cnx && tcp_cnx->turn_sock);
++	turn_sock = tcp_cnx->turn_sock;
++
++	if (status != PJ_SUCCESS) {
++		// TODO: error handling
++		char addrtxt[PJ_INET6_ADDRSTRLEN + 8];
++		pj_perror(3, turn_sock->pool->obj_name, status,
++				  "Failed to read data from %s",
++				  pj_sockaddr_print(&tcp_cnx->peer_addr, addrtxt,
++									sizeof(addrtxt), 3));
++		return PJ_FALSE;
++	}
+ 
++	pj_grp_lock_acquire(turn_sock->grp_lock);
++
++	*remainder = size;
++	pj_uint8_t* pkt = data;
++	while (*remainder > 0) {
++		if ((tcp_cnx->flags & CONNECTION_READY) != 0) {
++			if (turn_sock->cb.on_rx_data)
++				turn_sock->cb.on_rx_data(turn_sock, pkt, *remainder,
++										 &tcp_cnx->peer_addr,
++										 tcp_cnx->peer_addr_len);
++			pj_grp_lock_release(turn_sock->grp_lock);
++			*remainder = 0;
++			return PJ_TRUE;
++		}
++
++		/* STUN session waiting for ConnectBind response */
++		pj_size_t parsed_len;
++		unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
++		status = pj_stun_session_on_rx_pkt(tcp_cnx->stun_sess, pkt, size,
++										   options, NULL, &parsed_len,
++										   &tcp_cnx->peer_addr,
++										   tcp_cnx->peer_addr_len);
++
++		if (status != PJ_SUCCESS) {
++			// TODO: error handling
++			pj_grp_lock_release(turn_sock->grp_lock);
++			return PJ_FALSE;
++		}
++
++		PJ_LOG(3, ("rfc6062",
++				   "parsed STUN msg (read %zu byte(s) over %zu), status=%u",
++				   parsed_len, size, status));
++
++		pkt += parsed_len;
++		*remainder -= parsed_len;
++	}
++
++	pj_grp_lock_release(turn_sock->grp_lock);
++
++	return PJ_TRUE;
++}
++
++static pj_bool_t on_peer_connect_complete(pj_activesock_t *asock,
++										  pj_status_t status)
++{
++	pj_turn_tcp_data_connection *tcp_cnx;
++	pj_turn_sock *turn_sock;
++
++	tcp_cnx = (pj_turn_tcp_data_connection*) pj_activesock_get_user_data(asock);
++	if (!tcp_cnx)
++		return PJ_FALSE;
++
++	turn_sock = tcp_cnx->turn_sock;
++	pj_assert(turn_sock);
++
++    pj_grp_lock_acquire(turn_sock->grp_lock);
++
++	PJ_LOG(3, ("rfc6062", "peer data connection %s", status == PJ_SUCCESS ? "ready" : "failed"));
++
++	// TODO: handle failures
++	if (status != PJ_SUCCESS) {
++		pj_grp_lock_release(turn_sock->grp_lock);
++		return PJ_FALSE;
++	}
++
++	/* start pending read operation */
++    status = pj_activesock_start_read(asock, turn_sock->pool,
++									  turn_sock->setting.max_pkt_size, 0);
++	if (status != PJ_SUCCESS) {
++		// TODO: error handling
++		pj_grp_lock_release(turn_sock->grp_lock);
++		return PJ_FALSE;
++	}
++
++	/* Create a temporary STUN session to send the ConnectBind request */
++	pj_stun_session_cb stun_cb;
++	pj_bzero(&stun_cb, sizeof(stun_cb));
++	stun_cb.on_send_msg = &on_tcp_stun_send_msg;
++	stun_cb.on_request_complete = &on_tcp_stun_request_complete;
++	status = pj_stun_session_create(&turn_sock->cfg, NULL,
++									&stun_cb, PJ_FALSE, NULL,
++									&tcp_cnx->stun_sess);
++	if (status != PJ_SUCCESS) {
++		pj_grp_lock_release(turn_sock->grp_lock);
++        return PJ_FALSE;
++	}
++
++	pj_stun_session_set_user_data(tcp_cnx->stun_sess, tcp_cnx);
++
++	/* Copy credentials from control connection */
++	pj_stun_session_set_credential(tcp_cnx->stun_sess, PJ_STUN_AUTH_LONG_TERM, &turn_sock->cred);
++	pj_str_t server_nonce, server_realm;
++	pj_turn_session_get_server_cred(turn_sock->sess, turn_sock->pool, &server_nonce, &server_realm);
++	pj_stun_session_set_server_cred(tcp_cnx->stun_sess, &server_nonce, &server_realm);
++
++	/* Send ConnectBind request */
++	pj_stun_tx_data *tdata;
++	status = pj_stun_session_create_req(tcp_cnx->stun_sess, PJ_STUN_CONNECTION_BIND_REQUEST,
++										PJ_STUN_MAGIC, NULL, &tdata);
++    if (status != PJ_SUCCESS) {
++		pj_stun_session_destroy(tcp_cnx->stun_sess);
++		pj_grp_lock_release(turn_sock->grp_lock);
++		return PJ_FALSE;
++    }
++
++    /* MUST include REQUESTED-TRANSPORT attribute */
++    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_CONNECTION_ID, tcp_cnx->id);
++
++	PJ_LOG(3, ("rfc6062", "bind TCP connection id=%x", tcp_cnx->id));
++	status = pj_stun_session_send_msg(tcp_cnx->stun_sess, tcp_cnx, PJ_FALSE, PJ_FALSE,
++									  &tcp_cnx->peer_addr, tcp_cnx->peer_addr_len,
++									  tdata);
++	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
++		pj_stun_session_destroy(tcp_cnx->stun_sess);
++		pj_grp_lock_release(turn_sock->grp_lock);
++		return status;
++	}
++
++	pj_grp_lock_release(turn_sock->grp_lock);
++	return PJ_TRUE;
++}
++
++static pj_status_t on_tcp_stun_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)
++{
++	pj_status_t status;
++	pj_turn_tcp_data_connection *tcp_cnx = (pj_turn_tcp_data_connection*) token;
++	pj_assert(tcp_cnx != NULL && tcp_cnx->turn_sock != NULL);
++
++	pj_grp_lock_acquire(tcp_cnx->turn_sock->grp_lock);
++	status = pj_activesock_send(tcp_cnx->active_tcp_sock, &tcp_cnx->send_key, pkt, &pkt_size, 0);
++	pj_grp_lock_release(tcp_cnx->turn_sock->grp_lock);
++
++	return status;
++}
++
++static void on_tcp_stun_request_complete(pj_stun_session *sess,
++										 pj_status_t status,
++										 void *token,
++										 pj_stun_tx_data *tdata,
++										 const pj_stun_msg *response,
++										 const pj_sockaddr_t *src_addr,
++										 unsigned src_addr_len)
++{
++	pj_turn_tcp_data_connection *tcp_cnx = (pj_turn_tcp_data_connection*) token;
++	pj_assert(tcp_cnx != NULL && tcp_cnx->turn_sock != NULL);
++
++	pj_grp_lock_acquire(tcp_cnx->turn_sock->grp_lock);
++	pj_stun_session_destroy(tcp_cnx->stun_sess);
++	tcp_cnx->stun_sess = NULL;
++	tcp_cnx->flags |= CONNECTION_READY;
++	PJ_LOG(3, ("rfc6062", "peer data connection bind %s", status == PJ_SUCCESS ? "succeed" : "failed"));
++	pj_grp_lock_release(tcp_cnx->turn_sock->grp_lock);
++}
diff --git a/contrib/src/pjproject/rules.mak b/contrib/src/pjproject/rules.mak
index 009699eac9e9e064212c8a82a34fc2f38ea48b0f..3710fb2b1ab1e970c7ad4ba4b3212c5c51b248d7 100644
--- a/contrib/src/pjproject/rules.mak
+++ b/contrib/src/pjproject/rules.mak
@@ -80,6 +80,7 @@ endif
 	$(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/rfc6062.patch
 	$(UPDATE_AUTOCONFIG)
 	$(MOVE)