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"));
+    }
+}
+
