Index: pjproject/trunk/pjnath/src/pjnath-test/ice.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/ice.c	(revision 1093)
+++ pjproject/trunk/pjnath/src/pjnath-test/ice.c	(revision 1094)
@@ -24,8 +24,12 @@
 struct ice_data
 {
+    const char	   *obj_name;
     pj_bool_t	    complete;
     pj_status_t	    err_code;
     unsigned	    rx_rtp_cnt;
     unsigned	    rx_rtcp_cnt;
+
+    char	    rx_rtp_data[32];
+    char	    rx_rtcp_data[32];
 };
 
@@ -38,4 +42,6 @@
     id->complete = PJ_TRUE;
     id->err_code = status;
+    PJ_LOG(3,(THIS_FILE, "    ICE %s complete %s", id->obj_name,
+	      (status==PJ_SUCCESS ? "successfully" : "with failure")));
 }
 
@@ -47,5 +53,10 @@
 {
     struct ice_data *id = (struct ice_data*) icemt->user_data;
+
     id->rx_rtp_cnt++;
+    pj_memcpy(id->rx_rtp_data, pkt, size);
+
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
 }
 
@@ -57,5 +68,10 @@
 {
     struct ice_data *id = (struct ice_data*) icemt->user_data;
+
     id->rx_rtcp_cnt++;
+    pj_memcpy(id->rx_rtcp_data, pkt, size);
+
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
 }
 
@@ -129,13 +145,31 @@
 
 
-/* Direct agent to agent communication */
-static int ice_direct_test()
+/* Perform ICE test with the following parameters:
+ *
+ * - title:	The title of the test
+ * - ocand_cnt,
+ *   ocand	Additional candidates to be added to offerer
+ * - acand_cnt,
+ *   acand	Additional candidates to be added to answerer
+ *
+ * The additional candidates are invalid candidates, meaning they
+ * won't be reachable by the agents. They are used to "confuse"
+ * ICE processing.
+ */
+static int perform_ice_test(const char *title,
+			    unsigned ocand_cnt,
+			    const pj_ice_cand ocand[],
+			    unsigned acand_cnt,
+			    const pj_ice_cand acand[])
 {
     pj_icemt *im1, *im2;
     pj_icemt_cb icemt_cb;
     struct ice_data *id1, *id2;
+    pj_timestamp t_start, t_end;
+    pj_ice_cand *rcand;
+    unsigned i;
     pj_status_t status;
 
-    PJ_LOG(3,(THIS_FILE, "...direct communication"));
+    PJ_LOG(3,(THIS_FILE, "...%s", title));
 
     pj_bzero(&icemt_cb, sizeof(icemt_cb));
@@ -145,5 +179,5 @@
 
     /* Create first ICE */
-    status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLING,
+    status = pj_icemt_create(&stun_cfg, "offerer", PJ_ICE_ROLE_CONTROLLING,
 			     &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im1);
     if (status != PJ_SUCCESS)
@@ -151,8 +185,19 @@
 
     id1 = PJ_POOL_ZALLOC_T(im1->pool, struct ice_data);
+    id1->obj_name = "offerer";
     im1->user_data = id1;
 
+    /* Add additional candidates */
+    for (i=0; i<ocand_cnt; ++i) {
+	status = pj_ice_add_cand(im1->ice, 1, ocand[i].type, 65535,
+				 &ocand[i].foundation, &ocand[i].addr,
+				 &ocand[i].base_addr, &ocand[i].srv_addr,
+				 sizeof(pj_sockaddr_in), NULL);
+	if (status != PJ_SUCCESS)
+	    return -22;
+    }
+
     /* Create second ICE */
-    status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLED,
+    status = pj_icemt_create(&stun_cfg, "answerer", PJ_ICE_ROLE_CONTROLLED,
 			     &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im2);
     if (status != PJ_SUCCESS)
@@ -160,10 +205,22 @@
 
     id2 = PJ_POOL_ZALLOC_T(im2->pool, struct ice_data);
+    id2->obj_name = "answerer";
     im2->user_data = id2;
 
+    /* Add additional candidates */
+    for (i=0; i<acand_cnt; ++i) {
+	status = pj_ice_add_cand(im1->ice, 1, acand[i].type, 65535,
+				 &acand[i].foundation, &acand[i].addr,
+				 &acand[i].base_addr, &acand[i].srv_addr,
+				 sizeof(pj_sockaddr_in), NULL);
+	if (status != PJ_SUCCESS)
+	    return -22;
+    }
+
+    /* Set credentials */
     {
-	pj_str_t u1 = pj_str("uname1");
+	pj_str_t u1 = pj_str("offerer");
 	pj_str_t p1 = pj_str("pass1");
-	pj_str_t u2 = pj_str("uname2");
+	pj_str_t u2 = pj_str("answerer");
 	pj_str_t p2 = pj_str("pass2");
 
@@ -182,4 +239,7 @@
 	return -35;
 
+    /* Mark start time */
+    pj_get_timestamp(&t_start);
+
     /* Both can start now */
     status = pj_ice_start_check(im1->ice);
@@ -187,16 +247,66 @@
 	return -40;
 
-#if 0
+#if 1
     status = pj_ice_start_check(im2->ice);
     if (status != PJ_SUCCESS)
-	return -40;
+	return -45;
 #endif
 
     /* Just wait until both completes, or timed out */
-    while (!id1->complete || !id2->complete)
+    while (!id1->complete || !id2->complete) {
+	pj_timestamp t_now;
+
 	handle_events(1);
 
+	pj_get_timestamp(&t_now);
+	if (pj_elapsed_msec(&t_start, &t_now) >= 10000) {
+	    PJ_LOG(3,(THIS_FILE, "....error: timed-out"));
+	    return -50;
+	}
+    }
+
+    /* Mark end-time */
+    pj_get_timestamp(&t_end);
+
+    /* Check status */
+    if (id1->err_code != PJ_SUCCESS)
+	return -53;
+    if (id2->err_code != PJ_SUCCESS)
+	return -56;
+
+    /* Verify that offerer gets answerer's transport address */
+    rcand = im1->ice->clist.checks[im1->ice->comp[0].nominated_check_id].rcand;
+    if (pj_memcmp(&rcand->addr, &im2->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
+	PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
+	return -60;
+    }
+
+    /* And the other way around */
+    rcand = im2->ice->clist.checks[im2->ice->comp[0].nominated_check_id].rcand;
+    if (pj_memcmp(&rcand->addr, &im1->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
+	PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
+	return -70;
+    }
+
+    /* Done */
+    PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec", 
+	      pj_elapsed_msec(&t_start, &t_end)));
+
+    /* Wait for some more time */
+    PJ_LOG(3,(THIS_FILE, ".....waiting.."));
+    for (;;) {
+	pj_timestamp t_now;
+
+	pj_get_timestamp(&t_now);
+	if (pj_elapsed_msec(&t_end, &t_now) > 10000)
+	    break;
+
+	handle_events(1);
+    }
+
+
+    pj_icemt_destroy(im1);
+    pj_icemt_destroy(im2);
     return 0;
-
 }
 
@@ -208,5 +318,8 @@
     pj_ioqueue_t *ioqueue;
     pj_timer_heap_t *timer_heap;
-    
+    pj_ice_cand ocand[PJ_ICE_MAX_CAND];
+    pj_ice_cand acand[PJ_ICE_MAX_CAND];
+    pj_str_t s;
+
     pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
     pj_ioqueue_create(pool, 12, &ioqueue);
@@ -217,11 +330,26 @@
     pj_log_set_level(5);
 
+    /* Basic create/destroy */
     rc = ice_basic_create_destroy_test();
     if (rc != 0)
 	goto on_return;
 
-    rc = ice_direct_test();
+    /* Direct communication */
+    rc = perform_ice_test("Direct connection", 0, NULL, 0, NULL);
     if (rc != 0)
 	goto on_return;
+
+    /* Direct communication with invalid address */
+    pj_bzero(ocand, sizeof(ocand));
+    pj_sockaddr_in_init(&ocand[0].addr.ipv4, pj_cstr(&s, "127.0.0.127"), 1234);
+    pj_sockaddr_in_init(&ocand[0].base_addr.ipv4, pj_cstr(&s, "127.0.0.128"), 1234);
+    ocand[0].comp_id = 1;
+    ocand[0].foundation = pj_str("H2");
+    ocand[0].type = PJ_ICE_CAND_TYPE_HOST;
+
+    rc = perform_ice_test("Direct connection with 1 invalid address", 1, ocand, 0, NULL);
+    if (rc != 0)
+	goto on_return;
+
 
 on_return:
