Index: pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1114)
+++ pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1126)
@@ -41,4 +41,5 @@
 
 /* String names for pj_ice_sess_check_state */
+#if PJ_LOG_MAX_LEVEL >= 4
 static const char *check_state_name[] = 
 {
@@ -56,4 +57,5 @@
     "Completed"
 };
+#endif	/* PJ_LOG_MAX_LEVEL >= 4 */
 
 static const char *role_names[] = 
@@ -61,4 +63,13 @@
     "Controlled",
     "Controlling"
+};
+
+/* Default ICE session preferences, according to draft-ice */
+static pj_uint8_t cand_type_prefs[4] =
+{
+    126,    /**< PJ_ICE_HOST_PREF	*/
+    100,    /**< PJ_ICE_SRFLX_PREF.	*/
+    110,    /**< PJ_ICE_PRFLX_PREF	*/
+    0	    /**< PJ_ICE_RELAYED_PREF	*/
 };
 
@@ -72,6 +83,5 @@
 typedef struct stun_data
 {
-    pj_ice_sess	*ice;
-    unsigned	 lcand_id;
+    pj_ice_sess		*ice;
     pj_ice_sess_cand	*lcand;
 } stun_data;
@@ -79,6 +89,6 @@
 typedef struct timer_data
 {
-    pj_ice_sess		*ice;
-    pj_ice_sess_checklist	*clist;
+    pj_ice_sess		    *ice;
+    pj_ice_sess_checklist   *clist;
 } timer_data;
 
@@ -132,9 +142,4 @@
 					  int *data_type,
 					  pj_str_t *data);
-static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg,
-					void *user_data,
-					const pj_str_t *realm,
-					const pj_str_t *username,
-					const pj_str_t *nonce);
 
 
@@ -189,6 +194,4 @@
     pj_pool_t *pool;
     pj_ice_sess *ice;
-    char tmp[64];
-    pj_str_t s;
     unsigned i;
     pj_status_t status;
@@ -205,4 +208,5 @@
     ice->tie_breaker.u32.hi = pj_rand();
     ice->tie_breaker.u32.lo = pj_rand();
+    ice->prefs = cand_type_prefs;
 
     pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
@@ -227,16 +231,16 @@
 
     if (local_ufrag == NULL) {
-	pj_ansi_snprintf(tmp, sizeof(tmp), "%x%x", pj_rand(), pj_rand());
-	s = pj_str(tmp);
-	local_ufrag = &s;
-    }
-    pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag);
+	ice->rx_ufrag.ptr = pj_pool_alloc(ice->pool, 16);
+	pj_create_random_string(ice->rx_ufrag.ptr, 16);
+    } else {
+	pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag);
+    }
 
     if (local_passwd == NULL) {
-	pj_ansi_snprintf(tmp, sizeof(tmp), "%x%x", pj_rand(), pj_rand());
-	s = pj_str(tmp);
-	local_passwd = &s;
-    }
-    pj_strdup(ice->pool, &ice->rx_pass, local_passwd);
+	ice->rx_pass.ptr = pj_pool_alloc(ice->pool, 16);
+	pj_create_random_string(ice->rx_pass.ptr, 16);
+    } else {
+	pj_strdup(ice->pool, &ice->rx_pass, local_passwd);
+    }
 
 
@@ -313,4 +317,18 @@
     }
 
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Change type preference
+ */
+PJ_DEF(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice,
+					  const pj_uint8_t prefs[4])
+{
+    PJ_ASSERT_RETURN(ice && prefs, PJ_EINVAL);
+    ice->prefs = pj_pool_calloc(ice->pool, PJ_ARRAY_SIZE(prefs), 
+				sizeof(pj_uint8_t));
+    pj_memcpy(ice->prefs, prefs, sizeof(prefs));
     return PJ_SUCCESS;
 }
@@ -422,33 +440,10 @@
 
 
-static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg,
-					void *user_data,
-					const pj_str_t *realm,
-					const pj_str_t *username,
-					const pj_str_t *nonce)
-{
-    /* We don't use NONCE */
-    PJ_UNUSED_ARG(msg);
-    PJ_UNUSED_ARG(user_data);
-    PJ_UNUSED_ARG(realm);
-    PJ_UNUSED_ARG(username);
-    PJ_UNUSED_ARG(nonce);
-    return PJ_TRUE;
-}
-
-
-static pj_uint32_t CALC_CAND_PRIO(pj_ice_cand_type type,
+static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
+				  pj_ice_cand_type type,
 				  pj_uint32_t local_pref,
 				  pj_uint32_t comp_id)
 {
-    pj_uint32_t type_pref[] =
-    {
-	PJ_ICE_HOST_PREF,
-	PJ_ICE_SRFLX_PREF,
-	PJ_ICE_PRFLX_PREF,
-	PJ_ICE_RELAYED_PREF
-    };
-
-    return ((type_pref[type] & 0xFF) << 24) + 
+    return ((ice->prefs[type] & 0xFF) << 24) + 
 	   ((local_pref & 0xFFFF)    << 8) +
 	   (((256 - comp_id) & 0xFF) << 0);
@@ -493,5 +488,5 @@
     lcand->type = type;
     pj_strdup(ice->pool, &lcand->foundation, foundation);
-    lcand->prio = CALC_CAND_PRIO(type, local_pref, lcand->comp_id);
+    lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id);
     pj_memcpy(&lcand->addr, addr, addr_len);
     pj_memcpy(&lcand->base_addr, base_addr, addr_len);
@@ -521,5 +516,4 @@
     sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data);
     sd->ice = ice;
-    sd->lcand_id = GET_LCAND_ID(lcand);
     sd->lcand = lcand;
     pj_stun_session_set_user_data(lcand->stun_sess, sd);
@@ -531,5 +525,4 @@
     auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred;
     auth_cred.data.dyn_cred.get_password = &stun_auth_get_password;
-    auth_cred.data.dyn_cred.verify_nonce = &stun_auth_verify_nonce;
     auth_cred.data.dyn_cred.user_data = lcand->stun_sess;
     pj_stun_session_set_credential(lcand->stun_sess, &auth_cred);
@@ -563,4 +556,5 @@
 
 /* Find default candidate ID for the component */
+#if 0
 PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
 						  unsigned comp_id,
@@ -630,4 +624,5 @@
     return PJ_EBUG;
 }
+#endif	/* if 0 */
 
 
@@ -640,9 +635,14 @@
 #endif
 
-static pj_uint64_t CALC_CHECK_PRIO(const pj_ice_sess *ice, 
-				   const pj_ice_sess_cand *lcand,
-				   const pj_ice_sess_cand *rcand)
+static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, 
+				    const pj_ice_sess_cand *lcand,
+				    const pj_ice_sess_cand *rcand)
 {
     pj_uint32_t O, A;
+    pj_timestamp prio;
+
+    /* Original formula:
+     *   pair priority = 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0)
+     */
 
     if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
@@ -654,8 +654,24 @@
     }
 
+    /*
     return ((pj_uint64_t)1 << 32) * MIN(O, A) +
 	   (pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0);
-}
-
+    */
+
+    prio.u32.hi = MIN(O,A);
+    prio.u32.lo = (MAX(O, A) << 1) + (O>A ? 1 : 0);
+
+    return prio;
+}
+
+
+PJ_INLINE(int) CMP_CHECK_PRIO(const pj_ice_sess_check *c1,
+			      const pj_ice_sess_check *c2)
+{
+    return pj_cmp_timestamp(&c1->prio, &c2->prio);
+}
+
+
+#if PJ_LOG_MAX_LEVEL >= 4
 static const char *dump_check(char *buffer, unsigned bufsize,
 			      const pj_ice_sess_checklist *clist,
@@ -691,5 +707,4 @@
 }
 
-#if PJ_LOG_MAX_LEVEL >= 4
 static void dump_checklist(const char *title, const pj_ice_sess *ice, 
 			   const pj_ice_sess_checklist *clist)
@@ -747,5 +762,5 @@
 	unsigned j, highest = i;
 	for (j=i+1; j<clist->count; ++j) {
-	    if (clist->checks[j].prio > clist->checks[highest].prio) {
+	    if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) {
 		highest = j;
 	    }
@@ -828,6 +843,6 @@
 		ljaddr = &ljcand->addr;
 
-	    if (sockaddr_cmp(liaddr, ljaddr) == SOCKADDR_EQUAL &&
-		sockaddr_cmp(&ricand->addr, &rjcand->addr) == SOCKADDR_EQUAL)
+	    if (ricand == rjcand && 
+		sockaddr_cmp(liaddr, ljaddr) == SOCKADDR_EQUAL)
 	    {
 		/* Found duplicate, remove it */
@@ -835,6 +850,6 @@
 
 		LOG5((ice->obj_name, "Check %s pruned",
-		    dump_check(buf, sizeof(buf), &ice->clist, 
-			       &clist->checks[j])));
+		     dump_check(buf, sizeof(buf), &ice->clist, 
+			        &clist->checks[j])));
 
 		pj_array_erase(clist->checks, sizeof(clist->checks[0]),
@@ -929,5 +944,5 @@
 	    comp->valid_check = check;
 	} else {
-	    if (comp->valid_check->prio < check->prio)
+	    if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
 		comp->valid_check = check;
 	}
@@ -1155,6 +1170,8 @@
 					PJ_STUN_BINDING_REQUEST, 
 					&check->tdata);
-    if (status != PJ_SUCCESS)
+    if (status != PJ_SUCCESS) {
+	pjnath_perror(ice->obj_name, "Error creating STUN request", status);
 	return status;
+    }
 
     /* Attach data to be retrieved later when STUN request transaction
@@ -1168,5 +1185,5 @@
 
     /* Add PRIORITY */
-    prio = CALC_CAND_PRIO(PJ_ICE_CAND_TYPE_PRFLX, 65535, 
+    prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, 
 			  lcand->comp_id);
     pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, 
@@ -1190,4 +1207,5 @@
     if (status != PJ_SUCCESS) {
 	check->tdata = NULL;
+	pjnath_perror(ice->obj_name, "Error sending STUN request", status);
 	return status;
     }
@@ -1290,4 +1308,17 @@
 }
 
+
+/* Utility: find string in string array */
+const pj_str_t *find_str(const pj_str_t *strlist[], unsigned count,
+			 const pj_str_t *str)
+{
+    unsigned i;
+    for (i=0; i<count; ++i) {
+	if (pj_strcmp(strlist[i], str)==0)
+	    return strlist[i];
+    }
+    return NULL;
+}
+
 /* Start ICE check */
 PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
@@ -1295,17 +1326,43 @@
     pj_ice_sess_checklist *clist;
     const pj_ice_sess_cand *cand0;
-    unsigned i;
+    const pj_str_t *flist[PJ_ICE_MAX_CAND];
+    unsigned i, flist_cnt = 0;
 
     PJ_ASSERT_RETURN(ice, PJ_EINVAL);
-    /* Checklist must be created */
+
+    /* Checklist must have been created */
     PJ_ASSERT_RETURN(ice->clist.count > 0, PJ_EINVALIDOP);
 
     LOG4((ice->obj_name, "Starting ICE check.."));
 
+    /* The agent examines the check list for the first media stream (a
+     * media stream is the first media stream when it is described by
+     * the first m-line in the SDP offer and answer).  For that media
+     * stream, it:
+     * 
+     * -  Groups together all of the pairs with the same foundation,
+     * 
+     * -  For each group, sets the state of the pair with the lowest
+     *    component ID to Waiting.  If there is more than one such pair,
+     *    the one with the highest priority is used.
+     */
+
     clist = &ice->clist;
 
-    /* Pickup the first pair and set the state to Waiting */
-    clist->checks[0].state = PJ_ICE_SESS_CHECK_STATE_WAITING;
-    cand0 = clist->checks[0].lcand;
+    /* Pickup the first pair for component 1. */
+    for (i=0; i<clist->count; ++i) {
+	if (clist->checks[0].lcand->comp_id == 1)
+	    break;
+    }
+    if (i == clist->count) {
+	pj_assert(!"Unable to find checklist for component 1");
+	return PJNATH_EICEINCOMPID;
+    }
+
+    /* Set this check to WAITING */
+    check_set_state(ice, &clist->checks[i], 
+		    PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS);
+    cand0 = clist->checks[i].lcand;
+    flist[flist_cnt++] = &clist->checks[i].lcand->foundation;
 
     /* Find all of the other pairs in that check list with the same
@@ -1313,13 +1370,15 @@
      * states to Waiting as well.
      */
-    for (i=1; i<clist->count; ++i) {
+    for (++i; i<clist->count; ++i) {
 	const pj_ice_sess_cand *cand1;
 
 	cand1 = clist->checks[i].lcand;
 
-	if (cand0->comp_id == cand1->comp_id &&
-	    pj_strcmp(&cand0->foundation, &cand1->foundation)!=0)
+	if (cand1->comp_id==cand0->comp_id &&
+	    find_str(flist, flist_cnt, &cand1->foundation)==NULL)
 	{
-	    clist->checks[i].state = PJ_ICE_SESS_CHECK_STATE_WAITING;
+	    check_set_state(ice, &clist->checks[i], 
+			    PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS);
+	    flist[flist_cnt++] = &cand1->foundation;
 	}
     }
@@ -1342,5 +1401,7 @@
 {
     stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
-    return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->lcand->comp_id, sd->lcand_id,
+    pj_ice_sess *ice = sd->ice;
+    return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->lcand->comp_id, 
+				    GET_LCAND_ID(sd->lcand),
 				    pkt, pkt_size, 
 				    dst_addr, addr_len);
