Index: pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5983)
+++ pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5987)
@@ -104,4 +104,10 @@
 };
 
+struct conn_bind_t
+{
+    pj_uint32_t	     id;		/* Connection ID.	*/
+    pj_sockaddr	     peer_addr;		/* Peer address.	*/
+    unsigned	     peer_addr_len;
+};
 
 /* The TURN client session structure */
@@ -209,4 +215,5 @@
 {
     pj_bzero(prm, sizeof(*prm));
+    prm->peer_conn_type = PJ_TURN_TP_UDP;
 }
 
@@ -724,4 +731,7 @@
 		     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 */
@@ -761,5 +771,5 @@
     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 */
@@ -999,4 +1009,10 @@
     }
 
+    /* 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);
+	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), 
@@ -1139,7 +1155,51 @@
 
 /**
- * Notify TURN client session upon receiving a packet from server.
- * The packet maybe a STUN packet or ChannelData packet.
- */
+ * Send ConnectionBind request.
+ */
+PJ_DEF(pj_status_t) pj_turn_session_connection_bind(
+					    pj_turn_session *sess,
+					    pj_pool_t *pool,
+					    pj_uint32_t conn_id,
+					    const pj_sockaddr_t *peer_addr,
+					    unsigned addr_len)
+{
+    pj_stun_tx_data *tdata;
+    struct conn_bind_t *conn_bind;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sess && pool && conn_id && peer_addr && addr_len,
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_READY, PJ_EINVALIDOP);
+
+    pj_grp_lock_acquire(sess->grp_lock);
+
+    /* Create blank ConnectionBind request */
+    status = pj_stun_session_create_req(sess->stun, 
+					PJ_STUN_CONNECTION_BIND_REQUEST,
+					PJ_STUN_MAGIC, NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Add CONNECTION_ID attribute */
+    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
+			      PJ_STUN_ATTR_CONNECTION_ID,
+			      conn_id);
+
+    conn_bind = PJ_POOL_ZALLOC_T(pool, struct conn_bind_t);
+    conn_bind->id = conn_id;
+    pj_sockaddr_cp(&conn_bind->peer_addr, peer_addr);
+    conn_bind->peer_addr_len = addr_len;
+
+    /* Send the request, associate connection data structure with tdata 
+     * for future reference when we receive the ConnectionBind response.
+     */
+    status = pj_stun_session_send_msg(sess->stun, conn_bind, PJ_FALSE,
+				      PJ_FALSE, peer_addr, addr_len, tdata);
+
+on_return:
+    pj_grp_lock_release(sess->grp_lock);
+    return status;
+}
+
 PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
 					      void *pkt,
@@ -1147,4 +1207,24 @@
 					      pj_size_t *parsed_len)
 {
+    pj_turn_session_on_rx_pkt_param prm;
+    pj_status_t status;
+    
+    pj_bzero(&prm, sizeof(prm));
+    prm.pkt = pkt;
+    prm.pkt_len = pkt_len;
+    status = pj_turn_session_on_rx_pkt2(sess, &prm);
+    if (status == PJ_SUCCESS && parsed_len)
+	*parsed_len = prm.parsed_len;
+    return status;
+}
+
+/**
+ * Notify TURN client session upon receiving a packet from server.
+ * The packet maybe a STUN packet or ChannelData packet.
+ */
+PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt2(
+				pj_turn_session *sess,
+				pj_turn_session_on_rx_pkt_param *prm)
+{
     pj_bool_t is_stun;
     pj_status_t status;
@@ -1161,17 +1241,20 @@
 
     /* Quickly check if this is STUN message */
-    is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0);
+    is_stun = ((((pj_uint8_t*)prm->pkt)[0] & 0xC0) == 0);
 
     if (is_stun) {
 	/* This looks like STUN, give it to the STUN session */
 	unsigned options;
+	const pj_sockaddr_t *src_addr = prm->src_addr?
+					prm->src_addr:sess->srv_addr;
+	unsigned src_addr_len = prm->src_addr_len? prm->src_addr_len:
+				pj_sockaddr_get_len(sess->srv_addr);
 
 	options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
 	if (is_datagram)
 	    options |= PJ_STUN_IS_DATAGRAM;
-	status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len,
-					 options, NULL, parsed_len,
-					 sess->srv_addr,
-					 pj_sockaddr_get_len(sess->srv_addr));
+	status=pj_stun_session_on_rx_pkt(sess->stun, prm->pkt, prm->pkt_len,
+					 options, NULL, &prm->parsed_len,
+					 src_addr, src_addr_len);
 
     } else {
@@ -1180,32 +1263,28 @@
 	struct ch_t *ch;
 
-	if (pkt_len < 4) {
-	    if (parsed_len) *parsed_len = 0;
+	if (prm->pkt_len < 4) {
+	    prm->parsed_len = 0;
 	    return PJ_ETOOSMALL;
 	}
 
 	/* Decode ChannelData packet */
-	pj_memcpy(&cd, pkt, sizeof(pj_turn_channel_data));
+	pj_memcpy(&cd, prm->pkt, sizeof(pj_turn_channel_data));
 	cd.ch_number = pj_ntohs(cd.ch_number);
 	cd.length = pj_ntohs(cd.length);
 
 	/* Check that size is sane */
-	if (pkt_len < cd.length+sizeof(cd)) {
-	    if (parsed_len) {
-		if (is_datagram) {
-		    /* Discard the datagram */
-		    *parsed_len = pkt_len;
-		} else {
-		    /* Insufficient fragment */
-		    *parsed_len = 0;
-		}
+	if (prm->pkt_len < cd.length+sizeof(cd)) {
+	    if (is_datagram) {
+		/* Discard the datagram */
+		prm->parsed_len = prm->pkt_len;
+	    } else {
+		/* Insufficient fragment */
+		prm->parsed_len = 0;
 	    }
 	    status = PJ_ETOOSMALL;
 	    goto on_return;
 	} else {
-	    if (parsed_len) {
-		/* Apply padding too */
-		*parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd);
-	    }
+	    /* Apply padding too */
+	    prm->parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd);
 	}
 
@@ -1219,5 +1298,5 @@
 	/* Notify application */
 	if (sess->cb.on_rx_data) {
-	    (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd), 
+	    (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)prm->pkt)+sizeof(cd), 
 				   cd.length, &ch->addr,
 				   pj_sockaddr_get_len(&ch->addr));
@@ -1643,4 +1722,34 @@
 	}
 
+    } else if (method == PJ_STUN_CONNECTION_BIND_METHOD) {
+	/* Handle ConnectionBind response */
+	struct conn_bind_t *conn_bind = (struct conn_bind_t*)token;
+
+	if (status != PJ_SUCCESS ||
+	    !PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) 
+	{
+	    pj_str_t reason = {0};
+	    if (status == PJ_SUCCESS) {
+		const pj_stun_errcode_attr *err_attr;
+		err_attr = (const pj_stun_errcode_attr*)
+			   pj_stun_msg_find_attr(response,
+						 PJ_STUN_ATTR_ERROR_CODE, 0);
+		if (err_attr) {
+		    status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
+		    reason = err_attr->reason;
+		} else {
+		    status = PJNATH_EINSTUNMSG;
+		}
+	    }
+	    pj_perror(1, sess->obj_name, status, "ConnectionBind failed: %.*s",
+		      (int)reason.slen, reason.ptr);
+	}
+
+	/* Notify app */
+	if (sess->cb.on_connection_bind_status) {
+	    (*sess->cb.on_connection_bind_status)
+			(sess, status, conn_bind->id,
+			&conn_bind->peer_addr, conn_bind->peer_addr_len);
+	}
     } else {
 	PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s response",
@@ -1675,5 +1784,36 @@
     sess = (pj_turn_session*)pj_stun_session_get_user_data(stun);
 
-    /* Expecting Data Indication only */
+    /* ConnectionAttempt Indication */
+    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_connection_attempt) {
+            (*sess->cb.on_connection_attempt)
+				(sess,
+				connection_id_attr->value,
+				&peer_attr->sockaddr,
+                                pj_sockaddr_get_len(&peer_attr->sockaddr));
+        }
+        return PJ_SUCCESS;
+    }
+
+    /* Next, expecting Data Indication only */
     if (msg->hdr.type != PJ_STUN_DATA_INDICATION) {
 	PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s indication",
