Index: pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1913)
+++ pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1988)
@@ -22,4 +22,5 @@
 #include <pj/assert.h>
 #include <pj/guid.h>
+#include <pj/hash.h>
 #include <pj/log.h>
 #include <pj/os.h>
@@ -100,4 +101,9 @@
     pj_ice_sess_checklist   *clist;
 } timer_data;
+
+
+/* This is the data that will be attached as token to outgoing
+ * STUN messages.
+ */
 
 
@@ -170,4 +176,19 @@
 
 
+PJ_DEF(const char*) pj_ice_sess_role_name(pj_ice_sess_role role)
+{
+    switch (role) {
+    case PJ_ICE_SESS_ROLE_UNKNOWN:
+	return "Unknown";
+    case PJ_ICE_SESS_ROLE_CONTROLLED:
+	return "Controlled";
+    case PJ_ICE_SESS_ROLE_CONTROLLING:
+	return "Controlling";
+    default:
+	return "??";
+    }
+}
+
+
 /* Get the prefix for the foundation */
 static int get_type_prefix(pj_ice_cand_type type)
@@ -184,5 +205,10 @@
 }
 
-/* Calculate foundation */
+/* Calculate foundation:
+ * Two candidates have the same foundation when they are "similar" - of
+ * the same type and obtained from the same host candidate and STUN
+ * server using the same protocol.  Otherwise, their foundation is
+ * different.
+ */
 PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool,
 				    pj_str_t *foundation,
@@ -191,8 +217,14 @@
 {
     char buf[64];
-
+    pj_uint32_t val;
+
+    if (base_addr->addr.sa_family == pj_AF_INET()) {
+	val = pj_ntohl(base_addr->ipv4.sin_addr.s_addr);
+    } else {
+	val = pj_hash_calc(0, pj_sockaddr_get_addr(base_addr),
+			   pj_sockaddr_get_addr_len(base_addr));
+    }
     pj_ansi_snprintf(buf, sizeof(buf), "%c%x",
-		     get_type_prefix(type),
-		     (int)pj_ntohl(base_addr->ipv4.sin_addr.s_addr));
+		     get_type_prefix(type), val);
     pj_strdup2(pool, foundation, buf);
 }
@@ -264,5 +296,5 @@
 
     if (name == NULL)
-	name = "ice%p";
+	name = "icess%p";
 
     pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, 
@@ -299,4 +331,10 @@
 	    return status;
 	}
+    }
+
+    /* Initialize transport datas */
+    for (i=0; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
+	ice->tp_data[i].transport_id = i;
+	ice->tp_data[i].has_req_data = PJ_FALSE;
     }
 
@@ -552,4 +590,5 @@
 PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
 					 unsigned comp_id,
+					 unsigned transport_id,
 					 pj_ice_cand_type type,
 					 pj_uint16_t local_pref,
@@ -577,5 +616,6 @@
 
     lcand = &ice->lcand[ice->lcand_cnt];
-    lcand->comp_id = comp_id;
+    lcand->comp_id = (pj_uint8_t)comp_id;
+    lcand->transport_id = (pj_uint8_t)transport_id;
     lcand->type = type;
     pj_strdup(ice->pool, &lcand->foundation, foundation);
@@ -583,9 +623,5 @@
     pj_memcpy(&lcand->addr, addr, addr_len);
     pj_memcpy(&lcand->base_addr, base_addr, addr_len);
-    if (rel_addr)
-	pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
-    else
-	pj_bzero(&lcand->rel_addr, sizeof(lcand->rel_addr));
-
+    pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
 
     pj_ansi_strcpy(ice->tmp.txt, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
@@ -1323,7 +1359,11 @@
 
     /* Disable our components which don't have matching component */
-    if (ice->comp_cnt==2 && highest_comp==1) {
-	ice->comp_cnt = 1;
-    }
+    for (i=highest_comp; i<ice->comp_cnt; ++i) {
+	if (ice->comp[i].stun_sess) {
+	    pj_stun_session_destroy(ice->comp[i].stun_sess);
+	    pj_bzero(&ice->comp[i], sizeof(ice->comp[i]));
+	}
+    }
+    ice->comp_cnt = highest_comp;
 
     /* Init timer entry in the checklist. Initially the timer ID is FALSE
@@ -1346,17 +1386,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 +1392,5 @@
 {
     pj_ice_sess_comp *comp;
-    struct req_data *rd;
+    pj_ice_msg_data *msg_data;
     pj_ice_sess_check *check;
     const pj_ice_sess_cand *lcand;
@@ -1393,8 +1420,10 @@
      * 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, pj_ice_msg_data);
+    msg_data->transport_id = lcand->transport_id;
+    msg_data->has_req_data = 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 +1457,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 +1685,8 @@
     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);
+    pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
+    
+    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
+				pkt, pkt_size, dst_addr, addr_len);
 }
 
@@ -1674,5 +1701,5 @@
 				     unsigned src_addr_len)
 {
-    struct req_data *rd = (struct req_data*) token;
+    pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
     pj_ice_sess *ice;
     pj_ice_sess_check *check, *new_check;
@@ -1685,7 +1712,10 @@
     PJ_UNUSED_ARG(src_addr_len);
 
-    ice = rd->ice;
-    check = &rd->clist->checks[rd->ckid];
-    clist = rd->clist;
+    pj_assert(msg_data->has_req_data);
+
+    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 +1770,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;
@@ -1847,4 +1877,5 @@
 	/* Add new peer reflexive candidate */
 	status = pj_ice_sess_add_cand(ice, check->lcand->comp_id, 
+				      msg_data->transport_id,
 				      PJ_ICE_CAND_TYPE_PRFLX,
 				      65535, &foundation,
@@ -1920,4 +1951,5 @@
     stun_data *sd;
     const pj_stun_msg *msg = rdata->msg;
+    pj_ice_msg_data *msg_data;
     pj_ice_sess *ice;
     pj_stun_priority_attr *prio_attr;
@@ -1930,10 +1962,9 @@
     PJ_UNUSED_ARG(pkt);
     PJ_UNUSED_ARG(pkt_len);
-    PJ_UNUSED_ARG(token);
-
+    
     /* Reject any requests except Binding request */
     if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
 	pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, 
-				NULL, NULL, PJ_TRUE, 
+				NULL, token, PJ_TRUE, 
 				src_addr, src_addr_len);
 	return PJ_SUCCESS;
@@ -2002,5 +2033,5 @@
 	    /* Generate 487 response */
 	    pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, 
-				    NULL, NULL, PJ_TRUE, 
+				    NULL, token, PJ_TRUE, 
 				    src_addr, src_addr_len);
 	    pj_mutex_unlock(ice->mutex);
@@ -2014,5 +2045,5 @@
 	    /* Generate 487 response */
 	    pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, 
-				    NULL, NULL, PJ_TRUE, 
+				    NULL, token, PJ_TRUE, 
 				    src_addr, src_addr_len);
 	    pj_mutex_unlock(ice->mutex);
@@ -2035,9 +2066,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, pj_ice_msg_data);
+    msg_data->transport_id = ((pj_ice_msg_data*)token)->transport_id;
+    msg_data->has_req_data = PJ_FALSE;
+
+    /* Send the response */
+    status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE,
 				      src_addr, src_addr_len, tdata);
 
@@ -2059,4 +2097,5 @@
     /* Init rcheck */
     rcheck->comp_id = sd->comp_id;
+    rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
     rcheck->src_addr_len = src_addr_len;
     pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len);
@@ -2091,5 +2130,4 @@
     pj_ice_sess_cand *rcand;
     unsigned i;
-    pj_bool_t is_relayed;
 
     comp = find_comp(ice, rcheck->comp_id);
@@ -2110,5 +2148,5 @@
     if (i == ice->rcand_cnt) {
 	rcand = &ice->rcand[ice->rcand_cnt++];
-	rcand->comp_id = rcheck->comp_id;
+	rcand->comp_id = (pj_uint8_t)rcheck->comp_id;
 	rcand->type = PJ_ICE_CAND_TYPE_PRFLX;
 	rcand->prio = rcheck->priority;
@@ -2148,10 +2186,12 @@
     }
 #else
-    /* Just get candidate with the highest priority for the specified 
-     * component ID in the checklist.
+    /* Just get candidate with the highest priority and same transport ID
+     * for the specified  component ID in the checklist.
      */
     for (i=0; i<ice->clist.count; ++i) {
 	pj_ice_sess_check *c = &ice->clist.checks[i];
-	if (c->lcand->comp_id == rcheck->comp_id) {
+	if (c->lcand->comp_id == rcheck->comp_id &&
+	    c->lcand->transport_id == rcheck->transport_id) 
+	{
 	    lcand = c->lcand;
 	    break;
@@ -2171,9 +2211,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 +2345,5 @@
     pj_status_t status = PJ_SUCCESS;
     pj_ice_sess_comp *comp;
+    pj_ice_sess_cand *cand;
 
     PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL);
@@ -2333,5 +2369,7 @@
     }
 
-    status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len, 
+    cand = comp->valid_check->lcand;
+    status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand->transport_id, 
+				  data, data_len, 
 				  &comp->valid_check->rcand->addr, 
 				  sizeof(pj_sockaddr_in));
@@ -2345,4 +2383,5 @@
 PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
 					  unsigned comp_id,
+					  unsigned transport_id,
 					  void *pkt,
 					  pj_size_t pkt_size,
@@ -2352,4 +2391,6 @@
     pj_status_t status = PJ_SUCCESS;
     pj_ice_sess_comp *comp;
+    pj_ice_msg_data *msg_data = NULL;
+    unsigned i;
     pj_status_t stun_status;
 
@@ -2364,9 +2405,22 @@
     }
 
+    /* Find transport */
+    for (i=0; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
+	if (ice->tp_data[i].transport_id == transport_id) {
+	    msg_data = &ice->tp_data[i];
+	    break;
+	}
+    }
+    if (msg_data == NULL) {
+	pj_assert(!"Invalid transport ID");
+	status = PJ_EINVAL;
+	goto on_return;
+    }
+
     stun_status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, 
     				    PJ_STUN_IS_DATAGRAM);
     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, msg_data,
 					   NULL, src_addr, src_addr_len);
 	if (status != PJ_SUCCESS) {
@@ -2376,5 +2430,5 @@
 	}
     } else {
-	(*ice->cb.on_rx_data)(ice, comp_id, pkt, pkt_size, 
+	(*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, 
 			      src_addr, src_addr_len);
     }
