Index: pjproject/branches/projects/ice-turn07/pjlib/include/pj/sock.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjlib/include/pj/sock.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjlib/include/pj/sock.h	(revision 1926)
@@ -777,7 +777,20 @@
  * @param dst	    Destination socket address.
  * @param src	    Source socket address.
+ *
+ * @see @pj_sockaddr_cp()
  */
 PJ_DECL(void) pj_sockaddr_copy_addr(pj_sockaddr *dst,
 				    const pj_sockaddr *src);
+/**
+ * Copy socket address. This will copy the whole structure depending
+ * on the address family of the source socket address.
+ *
+ * @param dst	    Destination socket address.
+ * @param src	    Source socket address.
+ *
+ * @see @pj_sockaddr_copy_addr()
+ */
+PJ_DECL(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src);
+
 /**
  * Get the IP address of an IPv4 socket address.
Index: pjproject/branches/projects/ice-turn07/pjlib/src/pj/sock_common.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjlib/src/pj/sock_common.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjlib/src/pj/sock_common.c	(revision 1926)
@@ -391,4 +391,12 @@
 
 /*
+ * Copy socket address.
+ */
+PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src)
+{
+    pj_memcpy(dst, src, pj_sockaddr_get_len(src));
+}
+
+/*
  * Set port number of pj_sockaddr_in
  */
Index: pjproject/branches/projects/ice-turn07/pjmedia/include/pjmedia/transport_ice.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjmedia/include/pjmedia/transport_ice.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjmedia/include/pjmedia/transport_ice.h	(revision 1926)
@@ -63,5 +63,5 @@
  *			for logging purposes.
  * @param comp_cnt	Number of components to be created.
- * @param stun_cfg	Pointer to STUN configuration settings.
+ * @param cfg		Pointer to configuration settings.
  * @param cb		Optional callbacks.
  * @param p_tp		Pointer to receive the media transport instance.
@@ -72,5 +72,5 @@
 					const char *name,
 					unsigned comp_cnt,
-					pj_stun_config *stun_cfg,
+					const pj_ice_strans_cfg *cfg,
 					const pjmedia_ice_cb *cb,
 					pjmedia_transport **p_tp);
@@ -105,8 +105,4 @@
  *			  no other host candidates will be added for this
  *			  socket.
- * @param stun_srv	Address of the STUN server, or NULL if STUN server
- *			reflexive mapping is not to be used.
- * @param turn_srv	Address of the TURN server, or NULL if TURN relay
- *			is not to be used.
  *
  * @return		PJ_SUCCESS when the initialization process has started
@@ -115,7 +111,5 @@
 PJ_DECL(pj_status_t) pjmedia_ice_start_init(pjmedia_transport *tp,
 					    unsigned options,
-					    const pj_sockaddr_in *start_addr,
-					    const pj_sockaddr_in *stun_srv,
-					    const pj_sockaddr_in *turn_srv);
+					    const pj_sockaddr_in *start_addr);
 
 /**
Index: pjproject/branches/projects/ice-turn07/pjmedia/src/pjmedia/transport_ice.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjmedia/src/pjmedia/transport_ice.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjmedia/src/pjmedia/transport_ice.c	(revision 1926)
@@ -129,5 +129,5 @@
 				       const char *name,
 				       unsigned comp_cnt,
-				       pj_stun_config *stun_cfg,
+				       const pj_ice_strans_cfg *cfg,
 				       const pjmedia_ice_cb *cb,
 	    			       pjmedia_transport **p_tp)
@@ -146,6 +146,6 @@
 
     /* Create ICE */
-    status = pj_ice_strans_create(stun_cfg, name, comp_cnt, NULL, 
-			      &ice_st_cb, &ice_st);
+    status = pj_ice_strans_create(cfg, name, comp_cnt, NULL, 
+				  &ice_st_cb, &ice_st);
     if (status != PJ_SUCCESS)
 	return status;
@@ -177,14 +177,8 @@
 PJ_DEF(pj_status_t) pjmedia_ice_start_init( pjmedia_transport *tp,
 					    unsigned options,
-					    const pj_sockaddr_in *start_addr,
-					    const pj_sockaddr_in *stun_srv,
-					    const pj_sockaddr_in *turn_srv)
+					    const pj_sockaddr_in *start_addr)
 {
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
     pj_status_t status;
-
-    status = pj_ice_strans_set_stun_srv(tp_ice->ice_st, stun_srv, turn_srv);
-    if (status != PJ_SUCCESS)
-	return status;
 
     status = pj_ice_strans_create_comp(tp_ice->ice_st, 1, options, start_addr);
@@ -350,5 +344,5 @@
 	    PJ_TODO(RELATED_ADDR_FOR_RELAYED_ADDR);
 	    len = pj_ansi_snprintf(buffer+len, MAXLEN-len,
-			     "srflx raddr %s rport %d",
+			     "relay raddr %s rport %d",
 			     pj_inet_ntoa(cand->base_addr.ipv4.sin_addr),
 			     (int)pj_ntohs(cand->base_addr.ipv4.sin_port));
Index: pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/ice_session.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/ice_session.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/ice_session.h	(revision 1926)
@@ -431,4 +431,5 @@
      * @param ice	    The ICE session.
      * @param comp_id	    ICE component ID.
+     * @param cand_id	    ICE candidate ID.
      * @param pkt	    The STUN packet.
      * @param size	    The size of the packet.
@@ -437,4 +438,5 @@
      */
     pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, 
+			     unsigned cand_id,
 			     const void *pkt, pj_size_t size,
 			     const pj_sockaddr_t *dst_addr,
@@ -798,4 +800,12 @@
  * @param ice		The ICE session.
  * @param comp_id	Component ID.
+ * @param cand_id	The candidate ID where this packet was received
+ *			from. This parameter will be returned back to
+ *			application in \a on_tx_pkt() callback, and
+ *			application may use it to determine whether to
+ *			send outgoing packet using local socket or with
+ *			the TURN relay. The ICE session will not use
+ *			this information to determine the local candidate
+ *			for this packet.
  * @param pkt		Incoming packet.
  * @param pkt_size	Size of incoming packet.
@@ -807,4 +817,5 @@
 PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
 					   unsigned comp_id,
+					   unsigned cand_id,
 					   void *pkt,
 					   pj_size_t pkt_size,
Index: pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/ice_strans.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/ice_strans.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/ice_strans.h	(revision 1926)
@@ -26,4 +26,5 @@
  */
 #include <pjnath/ice_session.h>
+#include <pjnath/turn_sock.h>
 #include <pjlib-util/resolver.h>
 #include <pj/ioqueue.h>
@@ -92,9 +93,4 @@
  *	  has completed, either successfully or with failure.
  *
- * After the ICE stream transport is created, application may set up the
- * STUN servers to be used to obtain STUN server reflexive and relayed
- * candidate, by calling #pj_ice_strans_set_stun_domain() or 
- * #pj_ice_strans_set_stun_srv().
- *
  * Application then creates each component by calling 
  * #pj_ice_strans_create_comp(); this would create an actual socket
@@ -336,4 +332,6 @@
     pj_uint8_t		 ka_tsx_id[12];	/**< ID for keep STUN alives	*/
 
+    pj_turn_sock	*turn_relay;	/**< TURN relay object.		*/
+
     pj_sockaddr		 local_addr;	/**< Local/base address.	*/
 
@@ -356,4 +354,52 @@
 
 /**
+ * This structure describes ICE stream transport configuration.
+ */
+typedef struct pj_ice_strans_cfg
+{
+    /**
+     * STUN config. This setting is mandatory.
+     */
+    pj_stun_config	stun_cfg;
+
+    /**
+     * STUN server address, if STUN is enabled.
+     *
+     * Default is to have no TURN server.
+     */
+    pj_sockaddr		stun_srv;
+
+    /**
+     * TURN server address, if TURN is enabled.
+     *
+     * Default is to have no TURN server.
+     */
+    pj_sockaddr		turn_srv;
+
+    /**
+     * Type of connection to the TURN server.
+     *
+     * Default is PJ_TURN_TP_UDP.
+     */
+    pj_turn_tp_type	turn_conn_type;
+
+    /**
+     * Credential to be used for the TURN session.
+     *
+     * Default is to have no credential.
+     */
+    pj_stun_auth_cred	turn_cred;
+
+    /**
+     * Optional TURN Allocate parameter.
+     *
+     * Default is all empty.
+     */
+    pj_turn_alloc_param	turn_alloc_param;
+
+} pj_ice_strans_cfg;
+
+
+/**
  * This structure represents the ICE stream transport.
  */
@@ -364,5 +410,5 @@
     pj_pool_t		    *pool;	/**< Pool used by this object.	*/
     void		    *user_data;	/**< Application data.		*/
-    pj_stun_config	     stun_cfg;	/**< STUN settings.		*/
+    pj_ice_strans_cfg	     cfg;	/**< Configuration.		*/
     pj_ice_strans_cb	     cb;	/**< Application callback.	*/
 
@@ -371,9 +417,4 @@
     unsigned		     comp_cnt;	/**< Number of components.	*/
     pj_ice_strans_comp	   **comp;	/**< Components array.		*/
-
-    pj_dns_resolver	    *resolver;	/**< The resolver instance.	*/
-    pj_bool_t		     has_rjob;	/**< Has pending resolve?	*/
-    pj_sockaddr_in	     stun_srv;	/**< STUN server address.	*/
-    pj_sockaddr_in	     turn_srv;	/**< TURN server address.	*/
 
     pj_timer_entry	     ka_timer;	/**< STUN keep-alive timer.	*/
@@ -388,5 +429,5 @@
  * function.
  *
- * @param stun_cfg	The STUN settings.
+ * @param cfg		Configuration.
  * @param name		Optional name for logging identification.
  * @param comp_cnt	Number of components.
@@ -400,5 +441,5 @@
  *			successfully.
  */
-PJ_DECL(pj_status_t) pj_ice_strans_create(pj_stun_config *stun_cfg,
+PJ_DECL(pj_status_t) pj_ice_strans_create(const pj_ice_strans_cfg *cfg,
 					  const char *name,
 					  unsigned comp_cnt,
@@ -417,48 +458,4 @@
  */
 PJ_DECL(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st);
-
-
-/**
- * Set the domain to be used when resolving the STUN servers. If application
- * wants to utillize STUN, then STUN server must be specified, either by
- * calling this function or by calling #pj_ice_strans_set_stun_srv().
- *
- * If application calls this function, then the STUN/TURN servers will
- * be resolved by querying DNS SRV records for the specified domain.
- *
- * @param ice_st	The ICE stream transport.
- * @param resolver	The resolver instance that will be used to
- *			resolve the STUN/TURN servers.
- * @param domain	The target domain.
- *
- * @return		PJ_SUCCESS if DNS SRV resolution job can be
- *			started. The resolution process itself will
- *			complete asynchronously.
- */
-PJ_DECL(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st,
-						   pj_dns_resolver *resolver,
-						   const pj_str_t *domain);
-
-/**
- * Set the STUN and TURN server addresses. If application
- * wants to utillize STUN, then STUN server must be specified, either by
- * calling this function or by calling #pj_ice_strans_set_stun_domain().
- *
- * With this function, the STUN and TURN server addresses will be 
- * assigned immediately, that is no DNS resolution will need to be 
- * performed.
- *
- * @param ice_st	The ICE stream transport.
- * @param stun_srv	The STUN server address, or NULL if STUN
- *			reflexive candidate is not to be used.
- * @param turn_srv	The TURN server address, or NULL if STUN
- *			relay candidate is not to be used.
- *
- * @return		PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) 
-pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
-			    const pj_sockaddr_in *stun_srv,
-			    const pj_sockaddr_in *turn_srv);
 
 /**
Index: pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/stun_config.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/stun_config.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/stun_config.h	(revision 1926)
@@ -26,4 +26,6 @@
 
 #include <pjnath/stun_msg.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
 #include <pj/string.h>
 
@@ -104,4 +106,15 @@
 
 /**
+ * Check that STUN config is valid.
+ */
+PJ_INLINE(pj_status_t) pj_stun_config_check_valid(const pj_stun_config *cfg)
+{
+    PJ_ASSERT_RETURN(cfg->ioqueue && cfg->pf && cfg->timer_heap &&
+		     cfg->rto_msec && cfg->res_cache_msec, PJ_EINVAL);
+    return PJ_SUCCESS;
+}
+
+
+/**
  * @}
  */
Index: pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/turn_session.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/turn_session.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/turn_session.h	(revision 1926)
@@ -177,5 +177,5 @@
      */
     void (*on_rx_data)(pj_turn_session *sess,
-		       const pj_uint8_t *pkt,
+		       void *pkt,
 		       unsigned pkt_len,
 		       const pj_sockaddr_t *peer_addr,
@@ -258,5 +258,5 @@
  * Create TURN client session.
  */
-PJ_DECL(pj_status_t) pj_turn_session_create(pj_stun_config *cfg,
+PJ_DECL(pj_status_t) pj_turn_session_create(const pj_stun_config *cfg,
 					    const char *name,
 					    int af,
@@ -341,5 +341,5 @@
  */
 PJ_DECL(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
-					       const pj_uint8_t *pkt,
+					       void *pkt,
 					       unsigned pkt_len,
 					       pj_bool_t is_datagram);
Index: pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/turn_sock.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/turn_sock.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/include/pjnath/turn_sock.h	(revision 1926)
@@ -17,6 +17,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
-#ifndef __PJNATH_turn_sock_H__
-#define __PJNATH_turn_sock_H__
+#ifndef __PJNATH_TURN_SOCK_H__
+#define __PJNATH_TURN_SOCK_H__
 
 /**
@@ -54,5 +54,5 @@
      */
     void (*on_rx_data)(pj_turn_sock *turn_sock,
-		       const pj_uint8_t *pkt,
+		       void *pkt,
 		       unsigned pkt_len,
 		       const pj_sockaddr_t *peer_addr,
@@ -105,4 +105,17 @@
 
 /**
+ * Lock the TURN socket. Application may need to call this function to
+ * synchronize access to other objects to avoid deadlock.
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock);
+
+
+/**
+ * Unlock the TURN socket.
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock);
+
+
+/**
  * Initialize.
  */
@@ -139,4 +152,4 @@
 
 
-#endif	/* __PJNATH_turn_sock_H__ */
+#endif	/* __PJNATH_TURN_SOCK_H__ */
 
Index: pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/ice_session.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/ice_session.c	(revision 1926)
@@ -102,4 +102,25 @@
 
 
+/* This is the data that will be attached as token to outgoing
+ * STUN messages.
+ */
+struct msg_data
+{
+    pj_bool_t			     is_request;
+
+    union data {
+	struct request_data {
+	    pj_ice_sess		    *ice;
+	    pj_ice_sess_checklist   *clist;
+	    unsigned		     ckid;
+	} req;
+
+	struct response_data {
+	    unsigned		     cand_id;
+	} res;
+    } data;
+};
+
+
 /* Forward declarations */
 static void destroy_ice(pj_ice_sess *ice,
@@ -1346,17 +1367,4 @@
 }
 
-
-/* This is the data that will be attached as user data to outgoing
- * STUN requests, and it will be given back when we receive completion
- * status of the request.
- */
-struct req_data
-{
-    pj_ice_sess		    *ice;
-    pj_ice_sess_checklist   *clist;
-    unsigned		     ckid;
-};
-
-
 /* Perform check on the specified candidate pair */
 static pj_status_t perform_check(pj_ice_sess *ice, 
@@ -1365,5 +1373,5 @@
 {
     pj_ice_sess_comp *comp;
-    struct req_data *rd;
+    struct msg_data *msg_data;
     pj_ice_sess_check *check;
     const pj_ice_sess_cand *lcand;
@@ -1393,8 +1401,9 @@
      * completes and on_stun_request_complete() callback is called.
      */
-    rd = PJ_POOL_ZALLOC_T(check->tdata->pool, struct req_data);
-    rd->ice = ice;
-    rd->clist = clist;
-    rd->ckid = check_id;
+    msg_data = PJ_POOL_ZALLOC_T(check->tdata->pool, struct msg_data);
+    msg_data->is_request = PJ_TRUE;
+    msg_data->data.req.ice = ice;
+    msg_data->data.req.clist = clist;
+    msg_data->data.req.ckid = check_id;
 
     /* Add PRIORITY */
@@ -1428,5 +1437,5 @@
 
     /* Initiate STUN transaction to send the request */
-    status = pj_stun_session_send_msg(comp->stun_sess, (void*)rd, PJ_FALSE, 
+    status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, 
 				      PJ_TRUE, &rcand->addr, 
 				      sizeof(pj_sockaddr_in), check->tdata);
@@ -1656,10 +1665,19 @@
     stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
     pj_ice_sess *ice = sd->ice;
-
-    PJ_UNUSED_ARG(token);
-
-    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, 
-				pkt, pkt_size, 
-				dst_addr, addr_len);
+    struct msg_data *msg_data = (struct msg_data*) token;
+    unsigned cand_id;
+    
+    if (msg_data->is_request) {
+	pj_ice_sess_checklist *clist = msg_data->data.req.clist;
+	pj_ice_sess_cand *lcand = clist->checks[msg_data->data.req.ckid].lcand;
+
+	cand_id = lcand - ice->lcand;
+	
+    } else {
+	cand_id = msg_data->data.res.cand_id;
+    }
+
+    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, cand_id,
+				pkt, pkt_size, dst_addr, addr_len);
 }
 
@@ -1674,5 +1692,5 @@
 				     unsigned src_addr_len)
 {
-    struct req_data *rd = (struct req_data*) token;
+    struct msg_data *msg_data = (struct msg_data*) token;
     pj_ice_sess *ice;
     pj_ice_sess_check *check, *new_check;
@@ -1685,7 +1703,10 @@
     PJ_UNUSED_ARG(src_addr_len);
 
-    ice = rd->ice;
-    check = &rd->clist->checks[rd->ckid];
-    clist = rd->clist;
+    pj_assert(msg_data->is_request);
+
+    ice = msg_data->data.req.ice;
+    clist = msg_data->data.req.clist;
+    check = &clist->checks[msg_data->data.req.ckid];
+    
 
     /* Mark STUN transaction as complete */
@@ -1740,5 +1761,5 @@
 	    LOG4((ice->obj_name, "Resending check because of role conflict"));
 	    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
-	    perform_check(ice, clist, rd->ckid);
+	    perform_check(ice, clist, msg_data->data.req.ckid);
 	    pj_mutex_unlock(ice->mutex);
 	    return;
@@ -1919,5 +1940,7 @@
 {
     stun_data *sd;
+    unsigned *param_cand_id;
     const pj_stun_msg *msg = rdata->msg;
+    struct msg_data *msg_data;
     pj_ice_sess *ice;
     pj_stun_priority_attr *prio_attr;
@@ -1930,5 +1953,14 @@
     PJ_UNUSED_ARG(pkt);
     PJ_UNUSED_ARG(pkt_len);
-    PJ_UNUSED_ARG(token);
+    
+    /*
+     * Note about candidate ID parameter:
+     *  This parameter is given by us by user, and it cannot be used to
+     *  distinguish local and server reflexive candidate. Just about the
+     *  only thing that we can do with it is to return it back to user
+     *  in the on_tx_pkt(). The user needs this information to determine
+     *  whether to send packet using local socket or the relay.
+     */
+    param_cand_id = (unsigned*)token;
 
     /* Reject any requests except Binding request */
@@ -2035,9 +2067,16 @@
     }
 
+    /* Add XOR-MAPPED-ADDRESS attribute */
     status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, 
 					   PJ_STUN_ATTR_XOR_MAPPED_ADDR,
 					   PJ_TRUE, src_addr, src_addr_len);
 
-    status = pj_stun_session_send_msg(sess, NULL, PJ_TRUE, PJ_TRUE,
+    /* Create a msg_data to be associated with this response */
+    msg_data = PJ_POOL_ZALLOC_T(tdata->pool, struct msg_data);
+    msg_data->is_request = PJ_FALSE;
+    msg_data->data.res.cand_id = *param_cand_id;
+
+    /* Send the response */
+    status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE,
 				      src_addr, src_addr_len, tdata);
 
@@ -2091,5 +2130,4 @@
     pj_ice_sess_cand *rcand;
     unsigned i;
-    pj_bool_t is_relayed;
 
     comp = find_comp(ice, rcheck->comp_id);
@@ -2171,9 +2209,4 @@
      * Create candidate pair for this request. 
      */
-    /* First check if the source address is the source address of the 
-     * STUN relay, to determine if local candidate is relayed candidate.
-     */
-    PJ_TODO(DETERMINE_IF_REQUEST_COMES_FROM_RELAYED_CANDIDATE);
-    is_relayed = PJ_FALSE;
 
     /* 
@@ -2310,4 +2343,5 @@
     pj_status_t status = PJ_SUCCESS;
     pj_ice_sess_comp *comp;
+    unsigned cand_id;
 
     PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL);
@@ -2333,5 +2367,7 @@
     }
 
-    status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len, 
+    cand_id = comp->valid_check->lcand - ice->lcand;
+
+    status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand_id, data, data_len, 
 				  &comp->valid_check->rcand->addr, 
 				  sizeof(pj_sockaddr_in));
@@ -2345,4 +2381,5 @@
 PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
 					  unsigned comp_id,
+					  unsigned cand_id,
 					  void *pkt,
 					  pj_size_t pkt_size,
@@ -2368,5 +2405,5 @@
     if (stun_status == PJ_SUCCESS) {
 	status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size,
-					   PJ_STUN_IS_DATAGRAM, NULL,
+					   PJ_STUN_IS_DATAGRAM, &cand_id,
 					   NULL, src_addr, src_addr_len);
 	if (status != PJ_SUCCESS) {
Index: pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/ice_strans.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/ice_strans.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/ice_strans.c	(revision 1926)
@@ -41,4 +41,5 @@
 static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
 			      unsigned comp_id,
+			      unsigned cand_id,
 			      const void *pkt, pj_size_t size,
 			      const pj_sockaddr_t *dst_addr,
@@ -74,4 +75,14 @@
 				     unsigned src_addr_len);
 
+/* TURN callbacks */
+static void turn_on_rx_data(pj_turn_sock *turn_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *peer_addr,
+			    unsigned addr_len);
+static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
+			  pj_turn_state_t new_state);
+
+
 /* Keep-alive timer */
 static void start_ka_timer(pj_ice_strans *ice_st);
@@ -81,8 +92,26 @@
 #define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc)
 
+/* Validate configuration */
+static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg)
+{
+    pj_status_t status;
+
+    status = pj_stun_config_check_valid(&cfg->stun_cfg);
+    if (!status)
+	return status;
+
+    /* If TURN is specified then TURN credential must be specified */
+    PJ_ASSERT_RETURN(!pj_sockaddr_has_addr(&cfg->turn_srv) ||
+		      cfg->turn_cred.type != PJ_STUN_AUTH_NONE,
+		     PJ_EINVAL);
+
+    return PJ_SUCCESS;
+}
+
+
 /* 
  * Create ICE stream transport 
  */
-PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg,
+PJ_DEF(pj_status_t) pj_ice_strans_create( const pj_ice_strans_cfg *cfg,
 					  const char *name,
 					  unsigned comp_cnt,
@@ -93,15 +122,21 @@
     pj_pool_t *pool;
     pj_ice_strans *ice_st;
-
-    PJ_ASSERT_RETURN(stun_cfg && comp_cnt && cb && p_ice_st, PJ_EINVAL);
-    PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL);
+    pj_status_t status;
+
+    status = pj_ice_strans_cfg_check_valid(cfg);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st, PJ_EINVAL);
 
     if (name == NULL)
 	name = "icstr%p";
 
-    pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_STRANS, 
+    pool = pj_pool_create(cfg->stun_cfg.pf, name, PJNATH_POOL_LEN_ICE_STRANS, 
 			  PJNATH_POOL_INC_ICE_STRANS, NULL);
     ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans);
     ice_st->pool = pool;
+    pj_memcpy(&ice_st->cfg, cfg, sizeof(*cfg));
+    pj_stun_auth_cred_dup(pool, &ice_st->cfg.turn_cred, &cfg->turn_cred);
     pj_memcpy(ice_st->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
     ice_st->user_data = user_data;
@@ -112,5 +147,4 @@
 
     pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
-    pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg));
 
 
@@ -179,30 +213,4 @@
     PJ_UNUSED_ARG(domain);
     return -1;
-}
-
-/*
- * Set STUN server address.
- */
-PJ_DEF(pj_status_t) pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
-						const pj_sockaddr_in *stun_srv,
-						const pj_sockaddr_in *turn_srv)
-{
-    PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
-    /* Must not have pending resolver job */
-    PJ_ASSERT_RETURN(ice_st->has_rjob==PJ_FALSE, PJ_EINVALIDOP);
-
-    if (stun_srv) {
-	pj_memcpy(&ice_st->stun_srv, stun_srv, sizeof(pj_sockaddr_in));
-    } else {
-	pj_bzero(&ice_st->stun_srv, sizeof(pj_sockaddr_in));
-    }
-
-    if (turn_srv) {
-	pj_memcpy(&ice_st->turn_srv, turn_srv, sizeof(pj_sockaddr_in));
-    } else {
-	pj_bzero(&ice_st->turn_srv, sizeof(pj_sockaddr_in));
-    }
-
-    return PJ_SUCCESS;
 }
 
@@ -328,5 +336,6 @@
     pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
     ioqueue_cb.on_read_complete = &on_read_complete;
-    status = pj_ioqueue_register_sock(ice_st->pool, ice_st->stun_cfg.ioqueue, 
+    status = pj_ioqueue_register_sock(ice_st->pool, 
+				      ice_st->cfg.stun_cfg.ioqueue, 
 				      comp->sock, comp, &ioqueue_cb, 
 				      &comp->key);
@@ -388,5 +397,7 @@
 	     * as default candidate.
 	     */
-	    if (ifs[i].ipv4.sin_addr.s_addr == comp->local_addr.ipv4.sin_addr.s_addr) {
+	    if (ifs[i].ipv4.sin_addr.s_addr == 
+		    comp->local_addr.ipv4.sin_addr.s_addr) 
+	    {
 		set_default = PJ_TRUE;
 		local_pref = 65535;
@@ -464,22 +475,31 @@
 	 *    the binding alive.
 	 * 
-	 * 2) this could be a packet (STUN or not STUN) sent from the STUN
-	 *    relay server. In this case, still there are few options to do
-	 *    for this packet: a) process this locally if this packet is
-	 *    related to TURN session management (e.g. Allocate response),
-	 *    b) forward this packet to ICE if this is related to ICE
+	 * 2) this could be a STUN request or response sent as part of ICE
 	 *    discovery process.
 	 *
-	 * 3) this could be a STUN request or response sent as part of ICE
-	 *    discovery process.
-	 *
-	 * 4) this could be application's packet, e.g. when ICE processing
+	 * 3) this could be application's packet, e.g. when ICE processing
 	 *    is done and agents start sending RTP/RTCP packets to each
 	 *    other, or when ICE processing is not done and this ICE stream
 	 *    transport decides to allow sending data.
 	 *
-	 * So far we don't have good solution for this.
-	 * The process below is just a workaround.
 	 */
+	unsigned cand_id;
+
+	/* Find candidate ID for this packet */
+	for (cand_id=0; cand_id<comp->cand_cnt; ++cand_id) {
+	    if (comp->cand_list[cand_id].type != PJ_ICE_CAND_TYPE_RELAYED)
+		break;
+	}
+	if (cand_id == comp->cand_cnt) {
+	    //pj_assert(!"We should have at least one host/srflx candidate");
+	    //cand_id = 0;
+	    PJ_LOG(2,(ice_st->obj_name, 
+		      "Received pkt on comp %d which doesn't have host/srflx "
+		      "candidate",
+		      comp->comp_id));
+	    goto next_packet;
+	}
+
+	/* Is this a STUN message? */
 	status = pj_stun_msg_check(comp->pkt, bytes_read, 
 				   PJ_STUN_IS_DATAGRAM);
@@ -496,6 +516,4 @@
 						   comp->src_addr_len);
 	    } else if (ice_st->ice) {
-		PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY);
-
 		TRACE_PKT((comp->ice_st->obj_name, 
 			  "Component %d RX packet from %s:%d",
@@ -505,5 +523,5 @@
 
 		status = pj_ice_sess_on_rx_pkt(ice_st->ice, comp->comp_id, 
-					       comp->pkt, bytes_read,
+					       cand_id, comp->pkt, bytes_read,
 					       &comp->src_addr, 
 					       comp->src_addr_len);
@@ -525,4 +543,5 @@
 
     /* Read next packet */
+next_packet:
     for (retry=0; retry<RETRY;) {
 	pkt_size = sizeof(comp->pkt);
@@ -552,4 +571,9 @@
 static void destroy_component(pj_ice_strans_comp *comp)
 {
+    if (comp->turn_relay) {
+	pj_turn_sock_destroy(comp->turn_relay);
+	comp->turn_relay = NULL;
+    }
+
     if (comp->stun_sess) {
 	pj_stun_session_destroy(comp->stun_sess);
@@ -611,5 +635,6 @@
 		  pj_ntohs(comp->local_addr.ipv4.sin_port)));
 	status = pj_stun_session_send_msg(comp->stun_sess, &comp->cand_list[j],
-					  PJ_FALSE, PJ_TRUE, &ice_st->stun_srv,
+					  PJ_FALSE, PJ_TRUE, 
+					  &ice_st->cfg.stun_srv,
 					  sizeof(pj_sockaddr_in), tdata);
 	if (status != PJ_SUCCESS) {
@@ -638,5 +663,5 @@
     ice_st->ka_timer.user_data = ice_st;
     
-    if (pj_timer_heap_schedule(ice_st->stun_cfg.timer_heap, 
+    if (pj_timer_heap_schedule(ice_st->cfg.stun_cfg.timer_heap, 
 			       &ice_st->ka_timer, &delay)==PJ_SUCCESS)
     {
@@ -653,5 +678,5 @@
 	return;
 
-    pj_timer_heap_cancel(ice_st->stun_cfg.timer_heap, &ice_st->ka_timer);
+    pj_timer_heap_cancel(ice_st->cfg.stun_cfg.timer_heap, &ice_st->ka_timer);
     ice_st->ka_timer.id = PJ_FALSE;
 }
@@ -671,10 +696,6 @@
     PJ_ASSERT_RETURN(ice_st && comp, PJ_EINVAL);
     
-    /* Bail out if STUN server is still being resolved */
-    if (ice_st->has_rjob)
-	return PJ_EBUSY;
-
     /* Just return (successfully) if STUN server is not configured */
-    if (ice_st->stun_srv.sin_family == 0)
+    if (ice_st->cfg.stun_srv.addr.sa_family == 0)
 	return PJ_SUCCESS;
 
@@ -684,5 +705,5 @@
     sess_cb.on_request_complete = &stun_on_request_complete;
     sess_cb.on_send_msg = &stun_on_send_msg;
-    status = pj_stun_session_create(&ice_st->stun_cfg, ice_st->obj_name,
+    status = pj_stun_session_create(&ice_st->cfg.stun_cfg, ice_st->obj_name,
 				    &sess_cb, PJ_FALSE, &comp->stun_sess);
     if (status != PJ_SUCCESS)
@@ -711,5 +732,5 @@
     /* Add new alias to this component */
     cand->type = PJ_ICE_CAND_TYPE_SRFLX;
-    cand->status = PJ_EPENDING;
+    cand->status = PJ_SUCCESS;
     cand->ice_cand_id = -1;
     cand->local_pref = 65535;
@@ -721,5 +742,5 @@
     /* Send STUN binding request */
     status = pj_stun_session_send_msg(comp->stun_sess, (void*)cand, PJ_FALSE, 
-				      PJ_TRUE, &ice_st->stun_srv, 
+				      PJ_TRUE, &ice_st->cfg.stun_srv, 
 				      sizeof(pj_sockaddr_in), tdata);
     if (status != PJ_SUCCESS) {
@@ -753,7 +774,4 @@
     PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EBUSY);
     
-    /* Can't add new component while resolver is running */
-    PJ_ASSERT_RETURN(ice_st->has_rjob == PJ_FALSE, PJ_EBUSY);
-
 
     /* Create component */
@@ -762,5 +780,8 @@
 	return status;
 
-    if ((options & PJ_ICE_ST_OPT_DISABLE_STUN) == 0) {
+    /* Start STUN mapped address resolution */
+    if ((options & PJ_ICE_ST_OPT_DISABLE_STUN) == 0 &&
+	pj_sockaddr_has_addr(&ice_st->cfg.stun_srv)) 
+    {
 	status = get_stun_mapped_addr(ice_st, comp);
 	if (status != PJ_SUCCESS) {
@@ -769,4 +790,50 @@
 	}
     }
+
+    /* Create TURN relay if wanted. */
+    if ((options & PJ_ICE_ST_OPT_DISABLE_RELAY) == 0 &&
+	pj_sockaddr_has_addr(&ice_st->cfg.turn_srv)) 
+    {
+	pj_turn_sock_cb turn_sock_cb;
+	char ipaddr[PJ_INET6_ADDRSTRLEN+8];
+	pj_str_t s;
+
+	pj_assert(comp->cand_cnt < PJ_ICE_ST_MAX_CAND);
+
+	/* Init TURN socket */
+	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;
+
+	status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, pj_AF_INET(),
+				     ice_st->cfg.turn_conn_type,
+				     &turn_sock_cb, 0, comp,
+				     &comp->turn_relay);
+	if (status != PJ_SUCCESS) {
+	    destroy_component(comp);
+	    return status;
+	}
+
+	pj_sockaddr_print(&ice_st->cfg.turn_srv, ipaddr, sizeof(ipaddr), 0);
+
+	++comp->pending_cnt;
+
+	/* Start allocation */
+	status = pj_turn_sock_init(comp->turn_relay, pj_cstr(&s, ipaddr), 
+				   pj_sockaddr_get_port(&ice_st->cfg.turn_srv),
+				   NULL, &ice_st->cfg.turn_cred, 
+				   &ice_st->cfg.turn_alloc_param);
+	if (status != PJ_SUCCESS) {
+	    if (comp->turn_relay) {
+		pj_turn_sock_destroy(comp->turn_relay);
+	    }
+	    comp->turn_relay = NULL;
+	    --comp->pending_cnt;
+	    destroy_component(comp);
+	    return status;
+	}
+
+    }
+
 
     /* Store this component */
@@ -849,5 +916,5 @@
 
     /* Create! */
-    status = pj_ice_sess_create(&ice_st->stun_cfg, ice_st->obj_name, role,
+    status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role,
 			        ice_st->comp_cnt, &ice_cb, 
 			        local_ufrag, local_passwd, &ice_st->ice);
@@ -877,4 +944,5 @@
 	for (j=0; j<comp->cand_cnt; ++j) {
 	    pj_ice_strans_cand *cand = &comp->cand_list[j];
+	    pj_sockaddr_t *local_addr, *relay_addr;
 
 	    /* Skip if candidate is not ready */
@@ -886,4 +954,11 @@
 	    }
 
+	    if (cand->type == PJ_ICE_CAND_TYPE_RELAYED) {
+		local_addr = &cand->addr;
+		relay_addr = &cand->addr;
+	    } else {
+		local_addr = &comp->local_addr;
+		relay_addr = NULL;
+	    }
 	    status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id, 
 					  cand->type, cand->local_pref, 
@@ -978,5 +1053,5 @@
 
 /*
- * Send packet using non-ICE means (e.g. when ICE was not negotiated).
+ * Application wants to send outgoing packet.
  */
 PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
@@ -998,5 +1073,12 @@
     /* If ICE is available, send data with ICE */
     if (ice_st->ice) {
-	return pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
+	if (comp->turn_relay) {
+	    pj_turn_sock_lock(comp->turn_relay);
+	}
+	status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
+	if (comp->turn_relay) {
+	    pj_turn_sock_unlock(comp->turn_relay);
+	}
+	return status;
     }
 
@@ -1029,4 +1111,5 @@
 static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
 			      unsigned comp_id, 
+			      unsigned cand_id,
 			      const void *pkt, pj_size_t size,
 			      const pj_sockaddr_t *dst_addr,
@@ -1034,23 +1117,32 @@
 {
     pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
-    pj_ice_strans_comp *comp = NULL;
-    pj_ssize_t pkt_size;
+    pj_ice_strans_comp *comp;
+    pj_ice_strans_cand *cand;
     pj_status_t status;
 
-    PJ_TODO(TX_TO_RELAY);
-
     PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
+
     comp = ice_st->comp[comp_id-1];
+    cand = &comp->cand_list[cand_id];
 
     TRACE_PKT((comp->ice_st->obj_name, 
-	      "Component %d TX packet to %s:%d",
-	      comp_id,
+	      "Component %d candidate %d TX packet to %s:%d",
+	      comp_id, cand_id,
 	      pj_inet_ntoa(((pj_sockaddr_in*)dst_addr)->sin_addr),
 	      (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port)));
 
-    pkt_size = size;
-    status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
-			       pkt, &pkt_size, 0,
-			       dst_addr, dst_addr_len);
+    if (cand->type == PJ_ICE_CAND_TYPE_RELAYED) {
+	if (comp->turn_relay) {
+	    status = pj_turn_sock_sendto(comp->turn_relay, pkt, size,
+					 dst_addr, dst_addr_len);
+	} else {
+	    status = PJ_EINVALIDOP;
+	}
+    } else {
+	pj_ssize_t pkt_size = size;
+	status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
+				   pkt, &pkt_size, 0,
+				   dst_addr, dst_addr_len);
+    }
     
     return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
@@ -1207,2 +1299,102 @@
 }
 
+
+/* Callback when TURN client has received a packet */
+static void turn_on_rx_data(pj_turn_sock *turn_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *peer_addr,
+			    unsigned addr_len)
+{
+    pj_ice_strans_comp *comp;
+    unsigned cand_id;
+    pj_status_t status;
+
+    comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock);
+    if (comp == NULL) {
+	return;
+    }
+
+    /* Find candidate ID for this packet */
+    for (cand_id=0; cand_id<comp->cand_cnt; ++cand_id) {
+	if (comp->cand_list[cand_id].type == PJ_ICE_CAND_TYPE_RELAYED)
+	    break;
+    }
+    if (cand_id == comp->cand_cnt) {
+	pj_assert(!"Missing relay candidate");
+	return;
+    }
+
+    /* Hand over the packet to ICE */
+    status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, 
+				   cand_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);
+    }
+}
+
+
+/* Callback when TURN client state has changed */
+static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
+			  pj_turn_state_t new_state)
+{
+    pj_ice_strans_comp *comp;
+
+    comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock);
+    if (comp == NULL) {
+	/* Not interested in further state notification once the relay is
+	 * disconnecting.
+	 */
+	return;
+    }
+
+    PJ_LOG(5,(comp->ice_st->obj_name, "TURN client state changed %s --> %s",
+	      pj_turn_state_name(old_state), pj_turn_state_name(new_state)));
+
+    if (old_state < PJ_TURN_STATE_READY &&
+	new_state >= PJ_TURN_STATE_READY)
+    {
+	pj_assert(comp->pending_cnt > 0);
+	comp->pending_cnt--;
+    }
+
+    if (new_state == PJ_TURN_STATE_READY) {
+	pj_turn_session_info rel_info;
+	char ipaddr[PJ_INET6_ADDRSTRLEN+8];
+	pj_ice_strans_cand *cand;
+
+	/* Get allocation info */
+	pj_turn_sock_get_info(turn_sock, &rel_info);
+
+	/* Add a relay candidate to this component */
+	pj_assert(comp->cand_cnt < PJ_ICE_ST_MAX_CAND);
+	cand = &comp->cand_list[comp->cand_cnt++];
+
+	/* Add new candidate to this component */
+	cand->type = PJ_ICE_CAND_TYPE_RELAYED;
+	cand->status = PJ_SUCCESS;
+	cand->ice_cand_id = -1;
+	cand->local_pref = 65535;
+	pj_sockaddr_cp(&cand->addr, &rel_info.relay_addr);
+	pj_ice_calc_foundation(comp->ice_st->pool, &cand->foundation, 
+			       PJ_ICE_CAND_TYPE_RELAYED, 
+			       &rel_info.relay_addr);
+
+	PJ_LOG(4,(comp->ice_st->obj_name, 
+		  "Component %d cand %d: relay address: %s",
+		  comp->comp_id, cand - comp->cand_list,
+		  pj_sockaddr_print(&rel_info.relay_addr, ipaddr, 
+				     sizeof(ipaddr), 3)));
+
+    } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) {
+	/* Unregister ourself from the TURN relay */
+	pj_turn_sock_set_user_data(turn_sock, NULL);
+	comp->turn_relay = NULL;
+
+	PJ_LOG(4,(comp->ice_st->obj_name, "Relay destroyed"));
+    }
+}
+
Index: pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/turn_session.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/turn_session.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/turn_session.c	(revision 1926)
@@ -67,4 +67,5 @@
     pj_turn_session_cb	 cb;
     void		*user_data;
+    pj_stun_config	 stun_cfg;
 
     pj_lock_t		*lock;
@@ -177,5 +178,5 @@
  * Create TURN client session.
  */
-PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg,
+PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
 					    const char *name,
 					    int af,
@@ -212,4 +213,7 @@
     sess->next_ch = PJ_TURN_CHANNEL_MIN;
 
+    /* Copy STUN session */
+    pj_memcpy(&sess->stun_cfg, cfg, sizeof(pj_stun_config));
+
     /* Copy callback */
     pj_memcpy(&sess->cb, cb, sizeof(*cb));
@@ -234,6 +238,6 @@
     stun_cb.on_request_complete = &stun_on_request_complete;
     stun_cb.on_rx_indication = &stun_on_rx_indication;
-    status = pj_stun_session_create(cfg, sess->obj_name, &stun_cb, PJ_FALSE,
-				    &sess->stun);
+    status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb,
+				    PJ_FALSE, &sess->stun);
     if (status != PJ_SUCCESS) {
 	do_destroy(sess);
@@ -850,5 +854,5 @@
  */
 PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
-					      const pj_uint8_t *pkt,
+					      void *pkt,
 					      unsigned pkt_len,
 					      pj_bool_t is_datagram)
@@ -865,5 +869,5 @@
 
     /* Quickly check if this is STUN message */
-    is_stun = ((pkt[0] & 0xC0) == 0);
+    is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0);
 
     if (is_stun) {
@@ -871,5 +875,5 @@
 	unsigned options;
 
-	options = PJ_STUN_CHECK_PACKET;
+	options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
 	if (is_datagram)
 	    options |= PJ_STUN_IS_DATAGRAM;
@@ -906,6 +910,6 @@
 
 	/* Notify application */
-	(*sess->cb.on_rx_data)(sess, pkt+sizeof(cd), cd.length,
-			       &peer->addr,
+	(*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd), 
+			       cd.length, &peer->addr,
 			       pj_sockaddr_get_len(&peer->addr));
 
Index: pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/turn_sock.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/turn_sock.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/src/pjnath/turn_sock.c	(revision 1926)
@@ -72,5 +72,5 @@
 				  unsigned ch_num);
 static void turn_on_rx_data(pj_turn_session *sess,
-			    const pj_uint8_t *pkt,
+			    void *pkt,
 			    unsigned pkt_len,
 			    const pj_sockaddr_t *peer_addr,
@@ -310,4 +310,22 @@
 }
 
+/**
+ * Lock the TURN socket. Application may need to call this function to
+ * synchronize access to other objects to avoid deadlock.
+ */
+PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock)
+{
+    return pj_lock_acquire(turn_sock->lock);
+}
+
+/**
+ * Unlock the TURN socket.
+ */
+PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock)
+{
+    return pj_lock_release(turn_sock->lock);
+}
+
+
 /*
  * Initialize.
@@ -525,5 +543,5 @@
  */
 static void turn_on_rx_data(pj_turn_session *sess,
-			    const pj_uint8_t *pkt,
+			    void *pkt,
 			    unsigned pkt_len,
 			    const pj_sockaddr_t *peer_addr,
Index: pjproject/branches/projects/ice-turn07/pjnath/src/pjturn-srv/auth.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjnath/src/pjturn-srv/auth.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjnath/src/pjturn-srv/auth.c	(revision 1926)
@@ -36,6 +36,5 @@
     { "100", "100" },
     { "700", "700" },
-    { "701", "701" },
-    { "702", "702" }
+    { "701", "701" }
 };
 
Index: pjproject/branches/projects/ice-turn07/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1926)
@@ -23,4 +23,14 @@
 #define NO_LIMIT	(int)0x7FFFFFFF
 
+#if 1
+#define TURN_SERVER	"turn.pjsip.org"
+#define TURN_PORT	34780
+#define TURN_TCP	0
+#define TURN_REALM	"pjsip.org"
+#define TURN_USER	"700"
+#define TURN_PASSWD	"700"
+#endif
+
+
 //#define STEREO_DEMO
 
@@ -55,4 +65,6 @@
     pj_pool_t		   *pool;
     /* Compatibility with older pjsua */
+
+    pj_bool_t		    use_turn;
 
     unsigned		    codec_cnt;
@@ -172,4 +184,8 @@
     puts  ("Media Options:");
     puts  ("  --use-ice           Enable ICE (default:no)");
+    puts  ("  --use-turn          Enable experimantal TURN (default:no)");
+    puts  ("  --ice-no-host       Disable ICE host candidates");
+    puts  ("  --ice-no-srflx      Disable ICE srflx candidates");
+    puts  ("  --ice-no-rtcp       Disable RTCP in ICE");
     puts  ("  --add-codec=name    Manually add codec (default is to enable all)");
     puts  ("  --dis-codec=name    Disable codec (can be specified multiple times)");
@@ -392,5 +408,6 @@
 	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
 	   OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
-	   OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
+	   OPT_USE_ICE, OPT_USE_TURN, OPT_ICE_NO_HOST, OPT_ICE_NO_SRFLX, 
+	   OPT_ICE_NO_RTCP, OPT_USE_SRTP, OPT_SRTP_SECURE,
 	   OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 
 	   OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
@@ -453,4 +470,8 @@
 	{ "rtp-port",	1, 0, OPT_RTP_PORT},
 	{ "use-ice",    0, 0, OPT_USE_ICE},
+	{ "use-turn",   0, 0, OPT_USE_TURN},
+	{ "ice-no-host",0, 0, OPT_ICE_NO_HOST},
+	{ "ice-no-srflx",0,0, OPT_ICE_NO_SRFLX},
+	{ "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 	{ "use-srtp",   1, 0, OPT_USE_SRTP},
@@ -826,4 +847,20 @@
 	    break;
 
+	case OPT_USE_TURN:
+	    cfg->use_turn = PJ_TRUE;
+	    break;
+
+	case OPT_ICE_NO_HOST:
+	    cfg->media_cfg.ice_options |= PJ_ICE_ST_OPT_DONT_ADD_CAND;
+	    break;
+
+	case OPT_ICE_NO_SRFLX:
+	    cfg->media_cfg.ice_options |= PJ_ICE_ST_OPT_DISABLE_STUN;
+	    break;
+
+	case OPT_ICE_NO_RTCP:
+	    cfg->media_cfg.ice_no_rtcp = PJ_TRUE;
+	    break;
+
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 	case OPT_USE_SRTP:
@@ -3467,4 +3504,18 @@
     app_config.cfg.cb.on_nat_detect = &on_nat_detect;
 
+    /* Init TURN settings */
+#ifdef TURN_SERVER
+    if (app_config.use_turn) {
+	app_config.cfg.turn_host = pj_str(TURN_SERVER);
+	app_config.cfg.turn_port = TURN_PORT;
+	app_config.cfg.turn_tcp = 0;
+	app_config.cfg.turn_cred.type = PJ_STUN_AUTH_CRED_STATIC;
+	app_config.cfg.turn_cred.data.static_cred.realm = pj_str(TURN_REALM);
+	app_config.cfg.turn_cred.data.static_cred.username = pj_str(TURN_USER);
+	app_config.cfg.turn_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+	app_config.cfg.turn_cred.data.static_cred.data = pj_str(TURN_PASSWD);
+    }
+#endif
+
     /* Initialize pjsua */
     status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
Index: pjproject/branches/projects/ice-turn07/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/branches/projects/ice-turn07/pjsip/include/pjsua-lib/pjsua.h	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjsip/include/pjsua-lib/pjsua.h	(revision 1926)
@@ -1044,7 +1044,22 @@
 
     /**
-     * Specify STUN relay server to be used.
-     */
-    pj_str_t	    stun_relay_host;
+     * Specify TURN server to be used.
+     */
+    pj_str_t	    turn_host;
+
+    /**
+     * Specify TURN server port number.
+     */
+    pj_uint16_t	    turn_port;
+
+    /**
+     * Specify if TCP connection to TURN server should be used.
+     */
+    pj_bool_t	    turn_tcp;
+
+    /**
+     * Specify STUN credential for the TURN connection.
+     */
+    pj_stun_auth_cred turn_cred;
 
     /**
@@ -3887,7 +3902,12 @@
 
     /**
-     * Enable ICE media relay.
-     */
-    pj_bool_t		enable_relay;
+     * ICE options.
+     */
+    unsigned		ice_options;
+
+    /**
+     * Disable RTCP in ICE.
+     */
+    pj_bool_t		ice_no_rtcp;
 };
 
Index: pjproject/branches/projects/ice-turn07/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjsip/src/pjsua-lib/pjsua_core.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjsip/src/pjsua-lib/pjsua_core.c	(revision 1926)
@@ -114,5 +114,6 @@
     pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
     pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
-    pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
+    pj_strdup_with_null(pool, &dst->turn_host, &src->turn_host);
+    pj_stun_auth_cred_dup(pool, &dst->turn_cred, &src->turn_cred);
 }
 
Index: pjproject/branches/projects/ice-turn07/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- pjproject/branches/projects/ice-turn07/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1925)
+++ pjproject/branches/projects/ice-turn07/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1926)
@@ -625,4 +625,5 @@
     unsigned i;
     pj_sockaddr_in addr;
+    pj_ice_strans_cfg ice_cfg;
     pj_status_t status;
 
@@ -635,4 +636,37 @@
 
     pj_sockaddr_in_init(&addr, 0, (pj_uint16_t)cfg->port);
+
+    /* Init ICE config */
+    pj_bzero(&ice_cfg, sizeof(ice_cfg));
+
+    /* Duplicate STUN config */
+    pj_memcpy(&ice_cfg.stun_cfg, &pjsua_var.stun_cfg, sizeof(pj_stun_config));
+
+    /* Set STUN server, if any */
+    if (pj_sockaddr_has_addr(&pjsua_var.stun_srv))
+	pj_sockaddr_cp(&ice_cfg.stun_srv, &pjsua_var.stun_srv);
+
+    if (pjsua_var.ua_cfg.turn_host.slen) {
+	/* Set TURN server.
+	 * TODO: DNS SRV
+	 */
+	status = pj_sockaddr_in_init(&ice_cfg.turn_srv.ipv4, 
+				     &pjsua_var.ua_cfg.turn_host,
+				     pjsua_var.ua_cfg.turn_port);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Error resolving TURN server", status);
+	    return status;
+	}
+
+	/* Copy TURN credential */
+	pj_memcpy(&ice_cfg.turn_cred, &pjsua_var.ua_cfg.turn_cred,
+		  sizeof(pjsua_var.ua_cfg.turn_cred));
+
+	/* TURN connection type. */
+	if (pjsua_var.ua_cfg.turn_tcp)
+	    ice_cfg.turn_conn_type = PJ_TURN_TP_TCP;
+	else
+	    ice_cfg.turn_conn_type = PJ_TURN_TP_UDP;
+    }
 
     /* Create each media transport */
@@ -642,9 +676,16 @@
 	int next_port;
 	char name[32];
-#if PJMEDIA_ADVERTISE_RTCP
-	enum { COMP_CNT=2 };
+	unsigned options, comp_cnt;
+
+#if PJMEDIA_ADVERTISE_RTCP==0
+	comp_cnt = 1;
 #else
-	enum { COMP_CNT=1 };
+	if (pjsua_var.media_cfg.ice_no_rtcp)
+	    comp_cnt = 1;
+	else
+	    comp_cnt = 2;
 #endif
+
+	options = pjsua_var.media_cfg.ice_options;
 
 	pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
@@ -653,6 +694,6 @@
 	pj_ansi_snprintf(name, sizeof(name), "icetp%02d", i);
 			 
-	status = pjmedia_ice_create(pjsua_var.med_endpt, name, COMP_CNT,
-				    &pjsua_var.stun_cfg, &ice_cb,
+	status = pjmedia_ice_create(pjsua_var.med_endpt, name, comp_cnt,
+				    &ice_cfg, &ice_cb,
 				    &pjsua_var.calls[i].med_tp);
 	if (status != PJ_SUCCESS) {
@@ -670,6 +711,6 @@
 				        pjsua_var.media_cfg.rx_drop_pct);
 
-	status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr,
-				        &pjsua_var.stun_srv.ipv4, NULL);
+	status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, options, 
+					&addr);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error starting ICE transport",
