Index: pjproject/trunk/pjnath/include/pjnath/errno.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 1088)
+++ pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 1089)
@@ -46,4 +46,5 @@
 #define PJNATH_ESTUNFINGERPRINT	    -1
 #define PJNATH_ESTUNNOTRESPOND	    -1
+#define PJNATH_ESTUNNOXORMAP	    -1
 
 /**
Index: pjproject/trunk/pjnath/include/pjnath/ice.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice.h	(revision 1088)
+++ pjproject/trunk/pjnath/include/pjnath/ice.h	(revision 1089)
@@ -28,4 +28,6 @@
 #include <pjlib-util/resolver.h>
 #include <pj/sock.h>
+#include <pj/timer.h>
+
 
 PJ_BEGIN_DECL
@@ -68,4 +70,5 @@
 #define PJ_ICE_MAX_COMP	    8
 #define PJ_ICE_MAX_CHECKS   32
+#define PJ_ICE_TA_VAL	    20
 
 /**
@@ -74,6 +77,8 @@
 typedef struct pj_ice_comp
 {
-    unsigned	    comp_id;
-    pj_sock_t	    sock;
+    unsigned	     comp_id;
+    pj_sock_t	     sock;
+    pj_stun_session *stun_sess;
+    pj_sockaddr	     local_addr;
 } pj_ice_comp;
 
@@ -105,16 +110,11 @@
 typedef struct pj_ice_check
 {
-    unsigned		cand_id;
-    pj_uint32_t		comp_id;
-    pj_str_t		foundation;
-
-    pj_uint64_t		check_prio;
-    pj_ice_check_state	check_state;
-
-    pj_ice_cand_type	rem_type;
-    pj_str_t		rem_foundation;
-    pj_uint32_t		rem_prio;
-    pj_sockaddr		rem_addr;
-    pj_sockaddr		rem_base_addr;
+    pj_ice_cand		*lcand;
+    pj_ice_cand		*rcand;
+
+    pj_uint64_t		 prio;
+    pj_ice_check_state	 state;
+    pj_bool_t		 nominated;
+    pj_status_t		 err_code;
 } pj_ice_check;
 
@@ -132,4 +132,5 @@
     unsigned		     count;
     pj_ice_check	     checks[PJ_ICE_MAX_CHECKS];
+    pj_timer_entry	     timer;
 } pj_ice_checklist;
 
@@ -176,6 +177,7 @@
     pj_ice_role		 role;
     pj_ice_state	 state;
-
     pj_ice_cb		 cb;
+
+    pj_stun_config	 stun_cfg;
 
     /* STUN credentials */
@@ -206,12 +208,8 @@
     pj_bool_t		 relay_enabled;
     pj_sockaddr		 stun_srv;
-
-    /* STUN sessions */
-    pj_stun_session	*tx_sess;
-    pj_stun_session	*rx_sess;
 };
 
 
-PJ_DECL(pj_status_t) pj_ice_create(pj_stun_config *cfg,
+PJ_DECL(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg,
 				   const char *name,
 				   pj_ice_role role,
@@ -257,9 +255,9 @@
 PJ_DECL(unsigned) pj_ice_get_cand_cnt(pj_ice *ice);
 PJ_DECL(pj_status_t) pj_ice_enum_cands(pj_ice *ice,
-				       unsigned sort_by,
 				       unsigned *p_count,
 				       unsigned cand_ids[]);
-PJ_DECL(unsigned) pj_ice_get_default_cand(pj_ice *ice,
-					  int *cand_id);
+PJ_DECL(pj_status_t) pj_ice_get_default_cand(pj_ice *ice,
+					     unsigned comp_id,
+					     int *cand_id);
 PJ_DECL(pj_status_t) pj_ice_get_cand(pj_ice *ice,
 				     unsigned cand_id,
@@ -267,5 +265,4 @@
 
 PJ_DECL(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
-					      pj_bool_t is_remote_offer,
 					      unsigned rem_cand_cnt,
 					      const pj_ice_cand rem_cand[]);
Index: pjproject/trunk/pjnath/include/pjnath/stun_auth.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_auth.h	(revision 1088)
+++ pjproject/trunk/pjnath/include/pjnath/stun_auth.h	(revision 1089)
@@ -156,7 +156,43 @@
 
 	    /**
+	     * Get the credential to be put in outgoing message.
+	     *
+	     * @param msg	The outgoing message where the credential is
+	     *			to be applied.
+	     * @param user_data	The user data as specified in the credential.
+	     * @param pool	Pool where the callback can allocate memory
+	     *			to fill in the credential.
+	     * @param realm	On return, the callback may specify the realm
+	     *			if long term credential is desired, otherwise
+	     *			this string must be set to empty.
+	     * @param username	On return, the callback must fill in with the
+	     *			username.
+	     * @param nonce	On return, the callback may optionally fill in
+	     *			this argument with NONCE value if desired,
+	     *			otherwise this argument must be set to empty.
+	     * @param data_type	On return, the callback must set this argument
+	     *			with the type of password in the data argument.
+	     * @param data	On return, the callback must set this with
+	     *			the password, encoded according to data_type
+	     *			argument.
+	     *
+	     * @return		The callback must return PJ_SUCCESS, otherwise
+	     *			the message transmission will be cancelled.
+	     */
+	    pj_status_t (*get_cred)(const pj_stun_msg *msg,
+				    void *user_data,
+				    pj_pool_t *pool,
+				    pj_str_t *realm,
+				    pj_str_t *username,
+				    pj_str_t *nonce,
+				    int *data_type,
+				    pj_str_t *data);
+
+	    /**
 	     * Get the password for the specified username. This function 
 	     * is also used to check whether the username is valid.
 	     *
+	     * @param msg	The STUN message where the password will be
+	     *			applied to.
 	     * @param user_data	The user data as specified in the credential.
 	     * @param realm	The realm as specified in the message.
@@ -176,5 +212,6 @@
 	     *			username is not valid.
 	     */
-	    pj_status_t	(*get_password)(void *user_data, 
+	    pj_status_t	(*get_password)(const pj_stun_msg *msg,
+					void *user_data, 
 				        const pj_str_t *realm,
 				        const pj_str_t *username,
@@ -188,4 +225,5 @@
 	     * PJ_FALSE, 438 (Stale Nonce) response will be created.
 	     *
+	     * @param msg	The STUN message where the nonce was received.
 	     * @param user_data	The user data as specified in the credential.
 	     * @param realm	The realm as specified in the message.
@@ -196,5 +234,6 @@
 	     *			NONCE can be accepted.
 	     */
-	    pj_bool_t	(*verify_nonce)(void *user_data,
+	    pj_bool_t	(*verify_nonce)(const pj_stun_msg *msg,
+					void *user_data,
 					const pj_str_t *realm,
 					const pj_str_t *username,
Index: pjproject/trunk/pjnath/src/pjnath/ice.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1088)
+++ pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1089)
@@ -20,4 +20,5 @@
 #include <pjnath/errno.h>
 #include <pj/addr_resolv.h>
+#include <pj/array.h>
 #include <pj/assert.h>
 #include <pj/log.h>
@@ -27,4 +28,15 @@
 
 
+/* String names for candidate types */
+static const char *cand_type_names[] =
+{
+    "Host",
+    "Server Reflexive",
+    "Peer Reflexive",
+    "Relayed"
+
+};
+
+/* String names for pj_ice_check_state */
 static const char *check_state_name[] = 
 {
@@ -36,9 +48,25 @@
 };
 
+static const char *clist_state_name[] =
+{
+    "Idle",
+    "Running",
+    "Completed"
+};
+
+const pj_str_t host_foundation = {"host", 4};
+const pj_str_t mapped_foundation = {"srfx", 4};
+const pj_str_t relayed_foundation = {"rlyd", 4};
+const pj_str_t peer_mapped_foundation = {"peer", 4};
+
+#define CHECK_NAME_LEN	128
+#define LOG(expr)	PJ_LOG(4,expr)
+
 static void destroy_ice(pj_ice *ice,
 			pj_status_t reason);
 static void ice_set_state(pj_ice *ice,
 			  pj_ice_state new_state);
-
+static pj_status_t start_periodic_check(pj_timer_heap_t *th, 
+					pj_timer_entry *te);
 static pj_status_t on_stun_send_msg(pj_stun_session *sess,
 				    const void *pkt,
@@ -52,5 +80,5 @@
 				      const pj_sockaddr_t *src_addr,
 				      unsigned src_addr_len);
-static void on_stun_request_complete(pj_stun_session *sess,
+static void on_stun_request_complete(pj_stun_session *stun_sess,
 				     pj_status_t status,
 				     pj_stun_tx_data *tdata,
@@ -67,5 +95,14 @@
 				      pj_str_t *realm,
 				      pj_str_t *nonce);
-static pj_status_t stun_auth_get_password(void *user_data, 
+static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
+				      void *user_data,
+				      pj_pool_t *pool,
+				      pj_str_t *realm,
+				      pj_str_t *username,
+				      pj_str_t *nonce,
+				      int *data_type,
+				      pj_str_t *data);
+static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
+					  void *user_data, 
 					  const pj_str_t *realm,
 					  const pj_str_t *username,
@@ -73,5 +110,6 @@
 					  int *data_type,
 					  pj_str_t *data);
-static pj_bool_t stun_auth_verify_nonce(void *user_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,
@@ -79,5 +117,5 @@
 
 
-PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *cfg,
+PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg,
 				  const char *name,
 				  pj_ice_role role,
@@ -89,9 +127,7 @@
     pj_pool_t *pool;
     pj_ice *ice;
-    pj_stun_session_cb sess_cb;
-    pj_stun_auth_cred auth_cred;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(cfg && cb && p_ice, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL);
     PJ_ASSERT_RETURN(sock_type==PJ_SOCK_DGRAM || sock_type==PJ_SOCK_STREAM,
 		     PJ_EINVAL);
@@ -100,5 +136,5 @@
 	name = "ice%p";
 
-    pool = pj_pool_create(cfg->pf, name, 4000, 4000, NULL);
+    pool = pj_pool_create(stun_cfg->pf, name, 4000, 4000, NULL);
     ice = PJ_POOL_ZALLOC_T(pool, pj_ice);
     ice->pool = pool;
@@ -118,47 +154,11 @@
 
     pj_memcpy(&ice->cb, cb, sizeof(*cb));
-
-    /* Init STUN callbacks */
-    pj_bzero(&sess_cb, sizeof(sess_cb));
-    sess_cb.on_request_complete = &on_stun_request_complete;
-    sess_cb.on_rx_indication = &on_stun_rx_indication;
-    sess_cb.on_rx_request = &on_stun_rx_request;
-    sess_cb.on_send_msg = &on_stun_send_msg;
-
-    /* Init STUN authentication credential */
-    pj_bzero(&auth_cred, sizeof(auth_cred));
-    auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
-    auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth;
-    auth_cred.data.dyn_cred.get_password = &stun_auth_get_password;
-    auth_cred.data.dyn_cred.verify_nonce = &stun_auth_verify_nonce;
-
-    /* Create STUN session for outgoing requests */
-    status = pj_stun_session_create(cfg, ice->obj_name, &sess_cb, PJ_FALSE,
-				    &ice->tx_sess);
-    if (status != PJ_SUCCESS) {
-	destroy_ice(ice, status);
-	return status;
-    }
-
-    pj_stun_session_set_user_data(ice->tx_sess, ice);
-    auth_cred.data.dyn_cred.user_data = ice->tx_sess;
-    pj_stun_session_set_credential(ice->tx_sess, &auth_cred);
-
-    /* Create STUN session for incoming requests */
-    status = pj_stun_session_create(cfg, ice->obj_name, &sess_cb, PJ_FALSE,
-				    &ice->rx_sess);
-    if (status != PJ_SUCCESS) {
-	destroy_ice(ice, status);
-	return status;
-    }
-
-    pj_stun_session_set_user_data(ice->rx_sess, ice);
-    auth_cred.data.dyn_cred.user_data = ice->rx_sess;
-    pj_stun_session_set_credential(ice->rx_sess, &auth_cred);
+    pj_memcpy(&ice->stun_cfg, stun_cfg, sizeof(*stun_cfg));
+
 
     /* Done */
     *p_ice = ice;
 
-    PJ_LOG(4,(ice->obj_name, "ICE session created"));
+    LOG((ice->obj_name, "ICE session created"));
 
     return PJ_SUCCESS;
@@ -170,5 +170,5 @@
 {
     if (reason == PJ_SUCCESS) {
-	PJ_LOG(4,(ice->obj_name, "Destroying ICE session"));
+	LOG((ice->obj_name, "Destroying ICE session"));
     }
 
@@ -318,4 +318,5 @@
 
 
+
 PJ_DEF(pj_status_t) pj_ice_add_comp(pj_ice *ice,
 				    unsigned comp_id,
@@ -345,4 +346,11 @@
 }
 
+typedef struct stun_data
+{
+    pj_ice	*ice;
+    unsigned	 comp_id;
+    pj_ice_comp	*comp;
+} stun_data;
+
 
 PJ_DEF(pj_status_t) pj_ice_add_sock_comp( pj_ice *ice,
@@ -350,11 +358,63 @@
 					  pj_sock_t sock)
 {
+    pj_stun_session_cb sess_cb;
+    pj_ice_comp *comp;
+    pj_stun_auth_cred auth_cred;
+    stun_data *sd;
+    int addr_len;
+    pj_status_t status;
+
     PJ_ASSERT_RETURN(ice && sock != PJ_INVALID_SOCKET, PJ_EINVAL);
     PJ_ASSERT_RETURN(ice->comp_cnt < PJ_ARRAY_SIZE(ice->comp), PJ_ETOOMANY);
 
     pj_mutex_lock(ice->mutex);
-    ice->comp[ice->comp_cnt].comp_id = comp_id;
-    ice->comp[ice->comp_cnt].sock = sock;
-    ++ice->comp_cnt;
+
+    comp = &ice->comp[ice->comp_cnt];
+    comp->comp_id = comp_id;
+    comp->sock = sock;
+
+    addr_len = sizeof(comp->local_addr);
+    status = pj_sock_getsockname(sock, &comp->local_addr, &addr_len);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(ice->mutex);
+	return status;
+    }
+
+
+    /* Init STUN callbacks */
+    pj_bzero(&sess_cb, sizeof(sess_cb));
+    sess_cb.on_request_complete = &on_stun_request_complete;
+    sess_cb.on_rx_indication = &on_stun_rx_indication;
+    sess_cb.on_rx_request = &on_stun_rx_request;
+    sess_cb.on_send_msg = &on_stun_send_msg;
+
+    /* Create STUN session for this component */
+    status = pj_stun_session_create(&ice->stun_cfg, ice->obj_name, 
+			            &sess_cb, PJ_FALSE,
+				    &comp->stun_sess);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(ice->mutex);
+	return status;
+    }
+
+    /* Associate data with this STUN session */
+    sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data);
+    sd->ice = ice;
+    sd->comp_id = comp_id;
+    sd->comp = comp;
+    pj_stun_session_set_user_data(comp->stun_sess, sd);
+
+    /* Init STUN authentication credential */
+    pj_bzero(&auth_cred, sizeof(auth_cred));
+    auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
+    auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth;
+    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 = comp->stun_sess;
+    pj_stun_session_set_credential(comp->stun_sess, &auth_cred);
+
+    /* Done */
+    ice->comp_cnt++;
     pj_mutex_unlock(ice->mutex);
 
@@ -378,5 +438,43 @@
 
 
-static pj_status_t stun_auth_get_password(void *user_data, 
+/* Get credential to be sent with outgoing message */
+static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
+				      void *user_data,
+				      pj_pool_t *pool,
+				      pj_str_t *realm,
+				      pj_str_t *username,
+				      pj_str_t *nonce,
+				      int *data_type,
+				      pj_str_t *data)
+{
+    pj_stun_session *sess = (pj_stun_session *)user_data;
+    stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
+    pj_ice *ice = sd->ice;
+
+    PJ_UNUSED_ARG(pool);
+    realm->slen = nonce->slen = 0;
+
+    if (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
+	PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
+    {
+	/* Outgoing responses need to have the same credential as
+	 * incoming requests.
+	 */
+	*username = ice->rx_uname;
+	*data_type = 0;
+	*data = ice->rx_pass;
+    }
+    else {
+	*username = ice->tx_uname;
+	*data_type = 0;
+	*data = ice->tx_pass;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Get password to be used to authenticate incoming message */
+static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
+					  void *user_data, 
 					  const pj_str_t *realm,
 					  const pj_str_t *username,
@@ -386,10 +484,14 @@
 {
     pj_stun_session *sess = (pj_stun_session *)user_data;
-    pj_ice *ice = (pj_ice*) pj_stun_session_get_user_data(sess);
+    stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
+    pj_ice *ice = sd->ice;
 
     PJ_UNUSED_ARG(realm);
     PJ_UNUSED_ARG(pool);
 
-    if (sess == ice->tx_sess) {
+    if (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
+	PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
+    {
+	/* Incoming response is authenticated with TX credential */
 	/* Verify username */
 	if (pj_strcmp(username, &ice->tx_uname) != 0)
@@ -397,5 +499,7 @@
 	*data_type = 0;
 	*data = ice->tx_pass;
+
     } else {
+	/* Incoming request is authenticated with RX credential */
 	/* The agent MUST accept a credential if the username consists
 	 * of two values separated by a colon, where the first value is
@@ -408,11 +512,13 @@
 	*data_type = 0;
 	*data = ice->rx_pass;
-    }
-
-    return PJ_SUCCESS;
-}
-
-
-static pj_bool_t stun_auth_verify_nonce(void *user_data,
+
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+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,
@@ -420,4 +526,5 @@
 {
     /* We don't use NONCE */
+    PJ_UNUSED_ARG(msg);
     PJ_UNUSED_ARG(user_data);
     PJ_UNUSED_ARG(realm);
@@ -484,5 +591,5 @@
 
 	status = pj_ice_add_cand(ice, i, PJ_ICE_CAND_TYPE_HOST, 65535,
-				 NULL, &addr, &addr, NULL,
+				 &host_foundation, &addr, &addr, NULL,
     				 sizeof(pj_sockaddr_in), NULL);
 	if (status != PJ_SUCCESS)
@@ -493,15 +600,19 @@
 }
 
-static pj_status_t gather_mapped_cands(pj_ice *ice)
-{
-    PJ_UNUSED_ARG(ice);
-    return PJ_ENOTSUP;
-}
-
-static pj_status_t gather_relayed_cands(pj_ice *ice)
-{
-    PJ_UNUSED_ARG(ice);
-    return PJ_ENOTSUP;
-}
+/* Eliminate redundant candidates. */
+static void eliminate_redundant_cand(unsigned *cnt,
+				     pj_ice_cand cand[])
+{
+
+    /* A candidate is redundant if its transport address equals another 
+     * candidate, and its base equals the base of that other candidate.
+     * Note that two candidates can have the same transport address yet
+     * have different bases, and these would not be considered redundant.
+     */
+    PJ_TODO(ELIMINATE_REDUNDANT_CANDIDATES);
+    PJ_UNUSED_ARG(cnt);
+    PJ_UNUSED_ARG(cand);
+}
+
 
 PJ_DEF(pj_status_t) pj_ice_start_gather(pj_ice *ice,
@@ -516,4 +627,7 @@
     if (status != PJ_SUCCESS)
 	return status;
+
+    /* Eliminate redundant host candidates. */
+    eliminate_redundant_cand(&ice->lcand_cnt, ice->lcand);
 
     PJ_TODO(GATHER_MAPPED_AND_RELAYED_CANDIDATES);
@@ -556,4 +670,9 @@
     pj_ice_cand *lcand;
     pj_status_t status = PJ_SUCCESS;
+    char tmp[128];
+
+    PJ_ASSERT_RETURN(ice && comp_id && type && local_pref &&
+		     foundation && addr && base_addr && addr_len,
+		     PJ_EINVAL);
 
     pj_mutex_lock(ice->mutex);
@@ -579,4 +698,19 @@
 	*p_cand_id = ice->lcand_cnt;
 
+    pj_ansi_strcpy(tmp, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
+    LOG((ice->obj_name, 
+	 "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, "
+	 "addr=%s:%d, base=%s:%d, prio=0x%x (%u)",
+	 ice->lcand_cnt, 
+	 lcand->comp_id, 
+	 cand_type_names[lcand->type],
+	 (int)lcand->foundation.slen,
+	 lcand->foundation.ptr,
+	 tmp, 
+	 (int)pj_ntohs(lcand->addr.ipv4.sin_port),
+	 pj_inet_ntoa(lcand->base_addr.ipv4.sin_addr),
+	 (int)pj_htons(lcand->base_addr.ipv4.sin_port),
+	 lcand->prio, lcand->prio));
+
     ++ice->lcand_cnt;
 
@@ -594,5 +728,4 @@
 
 PJ_DEF(pj_status_t) pj_ice_enum_cands(pj_ice *ice,
-				      unsigned sort_by,
 				      unsigned *p_count,
 				      unsigned cand_ids[])
@@ -600,5 +733,5 @@
     unsigned i, count;
 
-    PJ_UNUSED_ARG(sort_by);
+    PJ_ASSERT_RETURN(ice && p_count && *p_count && cand_ids, PJ_EINVAL);
 
     pj_mutex_lock(ice->mutex);
@@ -615,4 +748,29 @@
 
 
+PJ_DEF(pj_status_t) pj_ice_get_default_cand(pj_ice *ice,
+					    unsigned comp_id,
+					    int *cand_id)
+{
+    PJ_ASSERT_RETURN(ice && comp_id && cand_id, PJ_EINVAL);
+
+    pj_mutex_lock(ice->mutex);
+
+    /* First find in valid list if we have nominated pair */
+
+    /* If there's no nominated pair, find relayed candidate */
+
+    /* If there's no relayed candidate, find server reflexive candidate */
+
+    /* Otherwise return host candidate */
+
+    pj_assert(!"Not implemented yet");
+    PJ_TODO(IMPLEMENT_GET_DEFAULT_CAND);
+
+    pj_mutex_unlock(ice->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
 PJ_DEF(pj_status_t) pj_ice_get_cand(pj_ice *ice,
 				    unsigned cand_id,
@@ -635,6 +793,18 @@
 #endif
 
-static pj_uint64_t CALC_CHECK_PRIO(pj_uint32_t O, pj_uint32_t A)
-{
+static pj_uint64_t CALC_CHECK_PRIO(const pj_ice *ice, 
+				   const pj_ice_cand *lcand,
+				   const pj_ice_cand *rcand)
+{
+    pj_uint32_t O, A;
+
+    if (ice->role == PJ_ICE_ROLE_CONTROLLING) {
+	O = lcand->prio; 
+	A = rcand->prio;
+    } else {
+	O = rcand->prio;
+	A = lcand->prio;
+    }
+
     return ((pj_uint64_t)1 << 32) * MIN(O, A) +
 	   (pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0);
@@ -645,16 +815,22 @@
 			      const pj_ice_check *check)
 {
-    char local_addr[128];
+    const pj_ice_cand *lcand = check->lcand;
+    const pj_ice_cand *rcand = check->rcand;
+    char laddr[CHECK_NAME_LEN];
     int len;
 
-    pj_ansi_strcpy(local_addr, 
-		   pj_inet_ntoa(ice->lcand[check->cand_id].addr.ipv4.sin_addr));
-
-    len = pj_ansi_snprintf(buffer, bufsize,
-	      "%s:%d-->%s:%d",
-	      local_addr,
-	      (int)pj_ntohs(ice->lcand[check->cand_id].addr.ipv4.sin_port),
-	      pj_inet_ntoa(check->rem_addr.ipv4.sin_addr),
-	      (int)pj_ntohs(check->rem_addr.ipv4.sin_port));
+    pj_ansi_strcpy(laddr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
+
+    if (lcand->addr.addr.sa_family == PJ_AF_INET) {
+	len = pj_ansi_snprintf(buffer, bufsize,
+			       "%s:%d-->%s:%d",
+			       laddr, (int)pj_ntohs(lcand->addr.ipv4.sin_port),
+			       pj_inet_ntoa(rcand->addr.ipv4.sin_addr),
+			       (int)pj_ntohs(rcand->addr.ipv4.sin_port));
+    } else {
+	len = pj_ansi_snprintf(buffer, bufsize, "IPv6->IPv6");
+    }
+
+
     if (len < 0)
 	len = 0;
@@ -671,12 +847,12 @@
 {
     unsigned i;
-    char buffer[128];
-
-    PJ_LOG(4,(ice->obj_name, "%s", title));
+    char buffer[CHECK_NAME_LEN];
+
+    LOG((ice->obj_name, "%s", title));
     for (i=0; i<clist->count; ++i) {
 	const pj_ice_check *c = &clist->checks[i];
-	PJ_LOG(4,(ice->obj_name, " %d: %s (prio=%u, state=%s)",
-		  i, dump_check(buffer, sizeof(buffer), ice, c),
-		  c->check_prio, check_state_name[c->check_state]));
+	LOG((ice->obj_name, " %d: %s (prio=%u, state=%s)",
+	     i, dump_check(buffer, sizeof(buffer), ice, c),
+	     c->prio, check_state_name[c->state]));
     }
 }
@@ -685,4 +861,26 @@
 #endif
 
+static void check_set_state(pj_ice *ice, pj_ice_check *check,
+			    pj_ice_check_state st, 
+			    pj_status_t err_code)
+{
+    char buf[CHECK_NAME_LEN];
+    LOG((ice->obj_name, "Check %s: state changed from %s to %s",
+	 dump_check(buf, sizeof(buf), ice, check),
+	 check_state_name[check->state],
+	 check_state_name[st]));
+    check->state = st;
+    check->err_code = err_code;
+}
+
+static void clist_set_state(pj_ice *ice, pj_ice_checklist *clist,
+			    pj_ice_checklist_state st)
+{
+    LOG((ice->obj_name, "Checklist: state changed from %s to %s",
+	 clist_state_name[clist->state],
+	 clist_state_name[st]));
+    clist->state = st;
+}
+
 static void sort_checklist(pj_ice_checklist *clist)
 {
@@ -692,7 +890,5 @@
 	unsigned j, highest = i;
 	for (j=i+1; j<clist->count; ++j) {
-	    if (clist->checks[j].check_prio > 
-		clist->checks[highest].check_prio) 
-	    {
+	    if (clist->checks[j].prio > clist->checks[highest].prio) {
 		highest = j;
 	    }
@@ -710,14 +906,106 @@
 }
 
+
+enum 
+{ 
+    SOCKADDR_EQUAL = 0, 
+    SOCKADDR_NOT_EQUAL = 1 
+};
+
+/* Utility: compare sockaddr.
+ * Returns 0 if equal.
+ */
+static int sockaddr_cmp(const pj_sockaddr *a1, const pj_sockaddr *a2)
+{
+    if (a1->addr.sa_family != a2->addr.sa_family)
+	return SOCKADDR_NOT_EQUAL;
+
+    if (a1->addr.sa_family == PJ_AF_INET) {
+	return !(a1->ipv4.sin_addr.s_addr == a2->ipv4.sin_addr.s_addr &&
+		 a1->ipv4.sin_port == a2->ipv4.sin_port);
+    } else if (a1->addr.sa_family == PJ_AF_INET6) {
+	return pj_memcmp(&a1->ipv6, &a2->ipv6, sizeof(a1->ipv6));
+    } else {
+	pj_assert(!"Invalid address family!");
+	return SOCKADDR_NOT_EQUAL;
+    }
+}
+
+
+/* Prune checklist, this must have been done after the checklist
+ * is sorted.
+ */
+static void prune_checklist(pj_ice *ice, pj_ice_checklist *clist)
+{
+    unsigned i;
+
+    /* Since an agent cannot send requests directly from a reflexive
+     * candidate, but only from its base, the agent next goes through the
+     * sorted list of candidate pairs.  For each pair where the local
+     * candidate is server reflexive, the server reflexive candidate MUST be
+     * replaced by its base.  Once this has been done, the agent MUST prune
+     * the list.  This is done by removing a pair if its local and remote
+     * candidates are identical to the local and remote candidates of a pair
+     * higher up on the priority list.  The result is a sequence of ordered
+     * candidate pairs, called the check list for that media stream.    
+     */
+    for (i=0; i<clist->count; ++i) {
+	pj_ice_cand *licand = clist->checks[i].lcand;
+	pj_ice_cand *ricand = clist->checks[i].rcand;
+	const pj_sockaddr *liaddr;
+	unsigned j;
+
+	if (licand->type == PJ_ICE_CAND_TYPE_MAPPED)
+	    liaddr = &licand->base_addr;
+	else
+	    liaddr = &licand->addr;
+
+	for (j=i+1; j<clist->count;) {
+	    pj_ice_cand *ljcand = clist->checks[j].lcand;
+	    pj_ice_cand *rjcand = clist->checks[j].rcand;
+	    const pj_sockaddr *ljaddr;
+
+	    if (ljcand->type == PJ_ICE_CAND_TYPE_MAPPED)
+		ljaddr = &licand->base_addr;
+	    else
+		ljaddr = &licand->addr;
+
+	    if (sockaddr_cmp(liaddr, ljaddr) == SOCKADDR_EQUAL &&
+		sockaddr_cmp(&ricand->addr, &rjcand->addr) == SOCKADDR_EQUAL)
+	    {
+		/* Found duplicate, remove it */
+		char buf[CHECK_NAME_LEN];
+
+		LOG((ice->obj_name, "Check %s pruned",
+		    dump_check(buf, sizeof(buf), ice, &clist->checks[j])));
+
+		pj_array_erase(clist->checks, sizeof(clist->checks[0]),
+			       clist->count, j);
+		--clist->count;
+
+	    } else {
+		++j;
+	    }
+	}
+    }
+}
+
+typedef struct timer_data
+{
+    pj_ice		*ice;
+    pj_ice_checklist	*clist;
+} timer_data;
+
 PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
-					     pj_bool_t is_remote_offer,
-					     unsigned rem_cand_cnt,
-					     const pj_ice_cand rem_cand[])
+					     unsigned rcand_cnt,
+					     const pj_ice_cand rcand[])
 {
     pj_ice_checklist *clist;
+    timer_data *td;
     unsigned i, j;
 
-    PJ_ASSERT_RETURN(ice && rem_cand_cnt && rem_cand, PJ_EINVAL);
-    PJ_ASSERT_RETURN(rem_cand_cnt < PJ_ICE_MAX_CAND, PJ_ETOOMANY);
+    PJ_ASSERT_RETURN(ice && rcand_cnt && rcand, PJ_EINVAL);
+    PJ_ASSERT_RETURN(rcand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND, 
+		     PJ_ETOOMANY);
 
     pj_mutex_lock(ice->mutex);
@@ -725,8 +1013,8 @@
     /* Save remote candidates */
     ice->rcand_cnt = 0;
-    for (i=0; i<rem_cand_cnt; ++i) {
+    for (i=0; i<rcand_cnt; ++i) {
 	pj_ice_cand *cn = &ice->rcand[ice->rcand_cnt++];
-	pj_memcpy(cn, &rem_cand[i], sizeof(pj_ice_cand));
-	pj_strdup(ice->pool, &cn->foundation, &rem_cand[i].foundation);
+	pj_memcpy(cn, &rcand[i], sizeof(pj_ice_cand));
+	pj_strdup(ice->pool, &cn->foundation, &rcand[i].foundation);
     }
 
@@ -734,7 +1022,16 @@
     clist = &ice->cklist;
     for (i=0; i<ice->lcand_cnt; ++i) {
-	for (j=0; j<rem_cand_cnt; ++j) {
-
-	    pj_ice_check *c = &clist->checks[clist->count++];
+	for (j=0; j<ice->rcand_cnt; ++j) {
+
+	    pj_ice_cand *lcand = &ice->lcand[i];
+	    pj_ice_cand *rcand = &ice->rcand[j];
+	    pj_ice_check *chk = &clist->checks[clist->count];
+
+	    if (clist->count > PJ_ICE_MAX_CHECKS) {
+		pj_mutex_unlock(ice->mutex);
+		return PJ_ETOOMANY;
+	    } else {
+		clist->count++;
+	    }
 
 	    /* A local candidate is paired with a remote candidate if
@@ -742,29 +1039,16 @@
 	     * and have the same IP address version. 
 	     */
-	    if (ice->lcand[i].comp_id != rem_cand[j].comp_id ||
-		pj_strcmp(&ice->lcand[i].foundation,&rem_cand[j].foundation)==0)
+	    if (lcand->comp_id != rcand->comp_id ||
+		lcand->addr.addr.sa_family != rcand->addr.addr.sa_family)
 	    {
 		continue;
 	    }
 
-	    c->cand_id = i;
-	    c->comp_id = ice->lcand[i].comp_id;
-	    c->foundation = ice->lcand[i].foundation;
-
-	    if (is_remote_offer) {
-		c->check_prio = CALC_CHECK_PRIO(rem_cand[j].prio,
-						ice->lcand[i].prio);
-	    } else {
-		c->check_prio = CALC_CHECK_PRIO(ice->lcand[i].prio, 
-						rem_cand[j].prio);
-	    }
-
-	    c->rem_type = rem_cand[j].type;
-	    pj_strdup(ice->pool, &c->rem_foundation, &rem_cand[j].foundation);
-	    c->rem_prio = rem_cand[j].prio;
-	    pj_memcpy(&c->rem_addr, &rem_cand[j].addr, 
-		      sizeof(rem_cand[j].addr));
-	    pj_memcpy(&c->rem_base_addr, &rem_cand[j].base_addr, 
-		      sizeof(rem_cand[j].addr));
+
+	    chk->lcand = lcand;
+	    chk->rcand = rcand;
+	    chk->state = PJ_ICE_CHECK_STATE_FROZEN;
+
+	    chk->prio = CALC_CHECK_PRIO(ice, lcand, rcand);
 	}
     }
@@ -774,7 +1058,16 @@
 
     /* Prune the checklist */
-    for (i=0; i<clist->count; ++i) {
-	PJ_TODO(PRUNE_CHECKLIST);
-    }
+    prune_checklist(ice, clist);
+
+    /* Init timer entry in the checklist. Initially the timer ID is FALSE
+     * because timer is not running.
+     */
+    clist->timer.id = PJ_FALSE;
+    td = PJ_POOL_ZALLOC_T(ice->pool, timer_data);
+    td->ice = ice;
+    td->clist = clist;
+    clist->timer.user_data = (void*)td;
+    clist->timer.cb = &start_periodic_check;
+
 
     /* Log checklist */
@@ -799,6 +1092,9 @@
 {
     pj_stun_tx_data *tdata;
+    pj_ice_comp *comp;
     struct req_data *rd;
     pj_ice_check *check;
+    const pj_ice_cand *lcand;
+    const pj_ice_cand *rcand;
     pj_uint32_t prio;
     char buffer[128];
@@ -806,11 +1102,14 @@
 
     check = &clist->checks[check_id];
-
-    PJ_LOG(5,(ice->obj_name, 
-	      "Sending connectivity check for check %d: %s", 
-	      check_id, dump_check(buffer, sizeof(buffer), ice, check)));
+    lcand = check->lcand;
+    rcand = check->rcand;
+    comp = &ice->comp[lcand->comp_id];
+
+    LOG((ice->obj_name, 
+	 "Sending connectivity check for check %d: %s", 
+	 check_id, dump_check(buffer, sizeof(buffer), ice, check)));
 
     /* Create request */
-    status = pj_stun_session_create_req(ice->tx_sess, 
+    status = pj_stun_session_create_req(comp->stun_sess, 
 					PJ_STUN_BINDING_REQUEST, &tdata);
     if (status != PJ_SUCCESS)
@@ -827,5 +1126,6 @@
 
     /* Add PRIORITY */
-    prio = CALC_CAND_PRIO(check->rem_type, 65535, check->comp_id);
+    prio = CALC_CAND_PRIO(PJ_ICE_CAND_TYPE_PEER_MAPPED, 65535, 
+			  lcand->comp_id);
     pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_PRIORITY,
 			      prio);
@@ -837,40 +1137,61 @@
     }
 
+    /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the 
+     * STUN session.
+     */
+
     /* Initiate STUN transaction to send the request */
-    status = pj_stun_session_send_msg(ice->tx_sess, PJ_FALSE, 
-				      &check->rem_addr, 
+    status = pj_stun_session_send_msg(comp->stun_sess, PJ_FALSE, 
+				      &rcand->addr, 
 				      sizeof(pj_sockaddr_in), tdata);
     if (status != PJ_SUCCESS)
 	return status;
 
-    check->check_state = PJ_ICE_CHECK_STATE_IN_PROGRESS;
-    return PJ_SUCCESS;
-}
-
-/* Start periodic check for the specified checklist */
-static pj_status_t start_periodic_check(pj_ice *ice, pj_ice_checklist *clist)
-{
+    check_set_state(ice, check, PJ_ICE_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS);
+    return PJ_SUCCESS;
+}
+
+
+/* Start periodic check for the specified checklist.
+ * This callback is called by timer on every Ta (20msec by default)
+ */
+static pj_status_t start_periodic_check(pj_timer_heap_t *th, 
+					pj_timer_entry *te)
+{
+    timer_data *td;
+    pj_ice *ice;
+    pj_ice_checklist *clist;
     unsigned i, start_count=0;
     pj_status_t status;
 
-    /* Checklist state must be idle or completed */
-    pj_assert(clist->state == PJ_ICE_CHECKLIST_ST_IDLE ||
-	      clist->state == PJ_ICE_CHECKLIST_ST_COMPLETED);
+    td = (struct timer_data*) te->user_data;
+    ice = td->ice;
+    clist = td->clist;
+
+    pj_mutex_lock(ice->mutex);
+
+    /* Set timer ID to FALSE first */
+    te->id = PJ_FALSE;
 
     /* Set checklist state to Running */
-    clist->state = PJ_ICE_CHECKLIST_ST_RUNNING;
-
-    PJ_LOG(4,(ice->obj_name, "Starting checklist periodic check"));
-
-    /* Send STUN Binding request for checks in Waiting list */
+    clist_set_state(ice, clist, PJ_ICE_CHECKLIST_ST_RUNNING);
+
+    LOG((ice->obj_name, "Starting checklist periodic check"));
+
+    /* Send STUN Binding request for check with highest priority on
+     * Waiting state.
+     */
     for (i=0; i<clist->count; ++i) {
 	pj_ice_check *check = &clist->checks[i];
 
-	if (check->check_state == PJ_ICE_CHECK_STATE_WAITING) {
+	if (check->state == PJ_ICE_CHECK_STATE_WAITING) {
 	    status = perform_check(ice, clist, i);
-	    if (status != PJ_SUCCESS)
+	    if (status != PJ_SUCCESS) {
+		pj_mutex_unlock(ice->mutex);
 		return status;
+	    }
 
 	    ++start_count;
+	    break;
 	}
     }
@@ -883,8 +1204,10 @@
 	    pj_ice_check *check = &clist->checks[i];
 
-	    if (check->check_state == PJ_ICE_CHECK_STATE_FROZEN) {
+	    if (check->state == PJ_ICE_CHECK_STATE_FROZEN) {
 		status = perform_check(ice, clist, i);
-		if (status != PJ_SUCCESS)
+		if (status != PJ_SUCCESS) {
+		    pj_mutex_unlock(ice->mutex);
 		    return status;
+		}
 
 		++start_count;
@@ -898,8 +1221,16 @@
      */
     if (start_count==0) {
-	clist->state = PJ_ICE_CHECKLIST_ST_COMPLETED;
-	PJ_LOG(4,(ice->obj_name, "Checklist completed"));
-    }
-
+	clist_set_state(ice, clist, PJ_ICE_CHECKLIST_ST_COMPLETED);
+
+    } else {
+	/* Schedule for next timer */
+	pj_time_val timeout = {0, PJ_ICE_TA_VAL};
+
+	te->id = PJ_TRUE;
+	pj_time_val_normalize(&timeout);
+	pj_timer_heap_schedule(th, te, &timeout);
+    }
+
+    pj_mutex_unlock(ice->mutex);
     return PJ_SUCCESS;
 }
@@ -910,7 +1241,10 @@
 {
     pj_ice_checklist *clist;
+    const pj_ice_cand *cand0;
     unsigned i;
 
     PJ_ASSERT_RETURN(ice, PJ_EINVAL);
+
+    LOG((ice->obj_name, "Starting ICE check.."));
 
     clist = &ice->cklist;
@@ -920,5 +1254,6 @@
 
     /* Pickup the first pair and set the state to Waiting */
-    clist->checks[0].check_state = PJ_ICE_CHECK_STATE_WAITING;
+    clist->checks[0].state = PJ_ICE_CHECK_STATE_WAITING;
+    cand0 = clist->checks[0].lcand;
 
     /* Find all of the other pairs in that check list with the same
@@ -927,17 +1262,17 @@
      */
     for (i=1; i<clist->count; ++i) {
-	pj_ice_check *cki = &clist->checks[i];
-
-	if (cki->comp_id != clist->checks[0].comp_id)
-	    continue;
-
-	if (pj_strcmp(&cki->foundation, &clist->checks[0].foundation)==0)
-	    continue;
-
-	clist->checks[i].check_state = PJ_ICE_CHECK_STATE_WAITING;
+	const pj_ice_cand *cand1;
+
+	cand1 = clist->checks[i].lcand;
+
+	if (cand0->comp_id == cand1->comp_id &&
+	    pj_strcmp(&cand0->foundation, &cand1->foundation)!=0)
+	{
+	    clist->checks[i].state = PJ_ICE_CHECK_STATE_WAITING;
+	}
     }
 
     /* Start periodic check */
-    return start_periodic_check(ice, clist);
+    return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer);
 }
 
@@ -956,54 +1291,6 @@
 
 
-static pj_status_t on_stun_rx_request(pj_stun_session *sess,
-				      const pj_uint8_t *pkt,
-				      unsigned pkt_len,
-				      const pj_stun_msg *msg,
-				      const pj_sockaddr_t *src_addr,
-				      unsigned src_addr_len)
-{
-    pj_stun_tx_data *tdata;
-    pj_status_t status;
-
-    /* 7.2.1.2.  Learning Peer Reflexive Candidates */
-    PJ_TODO(LEARN_PEER_REFLEXIVE_CANDIDATES);
-
-    /* Reject any requests except Binding request */
-    if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
-	status = pj_stun_session_create_response(sess, msg, 
-						 PJ_STUN_SC_BAD_REQUEST,
-						 NULL, &tdata);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	status = pj_stun_session_send_msg(sess, PJ_TRUE, 
-					  src_addr, src_addr_len, tdata);
-	return status;
-    }
-
-    status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    status = pj_stun_msg_add_sockaddr_attr(tdata->pool, msg, 
-					   PJ_STUN_ATTR_XOR_MAPPED_ADDR,
-					   PJ_TRUE, src_addr, src_addr_len);
-
-    status = pj_stun_session_send_msg(sess, PJ_TRUE, 
-				      src_addr, src_addr_len, tdata);
-
-    /* 7.2.1.3.  Triggered Checks:
-     * Next, the agent constructs a pair whose local candidate is equal to
-     * the transport address on which the STUN request was received, and a
-     * remote candidate equal to the source transport address where the
-     * request came from (which may be peer-reflexive remote candidate that
-     * was just learned). 
-     */
-    
-    return status;
-}
-
 /* This callback is called when outgoing STUN request completed */
-static void on_stun_request_complete(pj_stun_session *sess,
+static void on_stun_request_complete(pj_stun_session *stun_sess,
 				     pj_status_t status,
 				     pj_stun_tx_data *tdata,
@@ -1013,6 +1300,12 @@
     pj_ice *ice;
     pj_ice_check *check, *valid_check;
+    const pj_ice_cand *lcand;
+    const pj_ice_cand *rcand;
     pj_ice_checklist *clist;
-    char buffer[128];
+    pj_stun_xor_mapped_addr_attr *xaddr;
+    char buffer[CHECK_NAME_LEN];
+    unsigned i;
+
+    PJ_UNUSED_ARG(stun_sess);
 
     ice = rd->ice;
@@ -1020,11 +1313,17 @@
     clist = rd->clist;
 
-    PJ_LOG(5,(ice->obj_name, 
-	      "Connectivity check %s for check %d: %s",
-	      (status==PJ_SUCCESS ? "SUCCESS" : "FAILED"), rd->ckid, 
-	      dump_check(buffer, sizeof(buffer), ice, check)));
+    pj_mutex_lock(ice->mutex);
+
+    lcand = check->lcand;
+    rcand = check->rcand;
+
+    LOG((ice->obj_name, 
+	 "Connectivity check %s for check %s",
+	 (status==PJ_SUCCESS ? "SUCCESS" : "FAILED"), 
+	 dump_check(buffer, sizeof(buffer), ice, check)));
 
     if (status != PJ_SUCCESS) {
-	check->check_state = PJ_ICE_CHECK_STATE_FAILED;
+	check_set_state(ice, check, PJ_ICE_CHECK_STATE_FAILED, status);
+	pj_mutex_unlock(ice->mutex);
 	return;
     }
@@ -1036,23 +1335,63 @@
      * Request was sent from.
      */
-    PJ_TODO(CHECK_ICE_RESPONSE_SOURCE_ADDRESS);
-
-    /* Get the STUN MAPPED-ADDRESS attribute. If the
-     * transport address does not match any of the local candidates that the
-     * agent knows about, the mapped address represents a new candidate - a
-     * peer reflexive candidate 
-     */
-    PJ_TODO(CHECK_ICE_RESPONSE_SOURCE_ADDRESS2);
+    PJ_TODO(ICE_CHECK_RESPONSE_SOURCE_ADDRESS);
+
+    /* Get the STUN MAPPED-ADDRESS attribute. */
+    xaddr = (pj_stun_xor_mapped_addr_attr*)
+	    pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0);
+    if (!xaddr) {
+	check_set_state(ice, check, PJ_ICE_CHECK_STATE_FAILED, 
+			PJNATH_ESTUNNOXORMAP);
+	pj_mutex_unlock(ice->mutex);
+	return;
+    }
+
+    /* If the transport address returned in XOR-MAPPED-ADDRESS does not match
+     * any of the local candidates that the agent knows about, the mapped 
+     * address represents a new candidate - a peer reflexive candidate.
+     */
+    for (i=0; i<ice->lcand_cnt; ++i) {
+	if (sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0) {
+	    /* Match */
+	    break;
+	}
+    }
+
+    if (i == ice->lcand_cnt) {
+	unsigned cand_id;
+
+	/* Add new peer reflexive candidate */
+	status = pj_ice_add_cand(ice, lcand->comp_id, 
+				 PJ_ICE_CAND_TYPE_PEER_MAPPED,
+				 65535, &peer_mapped_foundation,
+				 &xaddr->sockaddr, &lcand->base_addr, NULL,
+				 sizeof(pj_sockaddr_in), &cand_id);
+	if (status != PJ_SUCCESS) {
+	    check_set_state(ice, check, PJ_ICE_CHECK_STATE_FAILED, status);
+	    pj_mutex_unlock(ice->mutex);
+	    return;
+	}
+
+	/* Update local candidate */
+	lcand = &ice->lcand[cand_id];
+    }
 
     /* Sets the state of the pair that generated the check to succeeded. */
-    check->check_state = PJ_ICE_CHECK_STATE_SUCCEEDED;
+    check_set_state(ice, check, PJ_ICE_CHECK_STATE_SUCCEEDED, PJ_SUCCESS);
+
 
     /* This is a valid pair, so add this to the valid list */
     valid_check = &ice->valid_list.checks[ice->valid_list.count++];
-    pj_memcpy(valid_check, check, sizeof(*check));
+    valid_check->lcand = lcand;
+    valid_check->rcand = rcand;
+    valid_check->prio = CALC_CHECK_PRIO(ice, lcand, rcand);
+    valid_check->state = PJ_ICE_CHECK_STATE_SUCCEEDED;
+    valid_check->nominated = (pj_stun_msg_find_attr(tdata->msg, 
+						    PJ_STUN_ATTR_USE_CANDIDATE,
+						    0) != NULL);
+    valid_check->err_code = PJ_SUCCESS;
 
     /* Sort valid_list */
     sort_checklist(&ice->valid_list);
-
 
     /* If the pair had a component ID of 1, the agent MUST change the
@@ -1060,21 +1399,26 @@
      * same foundation, but different component IDs, to Waiting.
      */
-    if (check->comp_id == 1) {
+    if (lcand->comp_id == 1) {
 	unsigned i;
+	pj_bool_t unfrozen = PJ_FALSE;
+
 	for (i=0; i<clist->count; ++i)  {
 	    pj_ice_check *c = &clist->checks[i];
 
-	    if (c->check_state == PJ_ICE_CHECK_STATE_FROZEN &&
-		c->comp_id != check->comp_id &&
-		pj_strcmp(&c->foundation, &check->foundation)==0)
+	    if (c->state == PJ_ICE_CHECK_STATE_FROZEN &&
+		c->lcand->comp_id != lcand->comp_id &&
+		pj_strcmp(&c->lcand->foundation, &lcand->foundation)==0)
 	    {
 		/* Unfreeze and start check */
-		PJ_LOG(5,(ice->obj_name, "Unfreezing check %d", i));
-		c->check_state = PJ_ICE_CHECK_STATE_WAITING;
-		perform_check(ice, clist, i);
+		check_set_state(ice, c, PJ_ICE_CHECK_STATE_WAITING, 
+				PJ_SUCCESS);
+		unfrozen = PJ_TRUE;
 	    }
 	}
 
+	if (unfrozen && clist->timer.id == PJ_FALSE)
+	    start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer);
     } 
+
     /* If the pair had a component ID equal to the number of components
      * for the media stream (where this is the actual number of
@@ -1095,5 +1439,148 @@
     }
 
-}
+    pj_mutex_unlock(ice->mutex);
+}
+
+
+static pj_status_t on_stun_rx_request(pj_stun_session *sess,
+				      const pj_uint8_t *pkt,
+				      unsigned pkt_len,
+				      const pj_stun_msg *msg,
+				      const pj_sockaddr_t *src_addr,
+				      unsigned src_addr_len)
+{
+    stun_data *sd;
+    pj_ice *ice;
+    pj_stun_priority_attr *ap;
+    pj_ice_comp *comp;
+    pj_ice_cand *lcand;
+    pj_ice_cand *rcand;
+    unsigned i;
+    pj_stun_tx_data *tdata;
+    pj_bool_t is_relayed;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(pkt_len);
+
+    /* Only accepts Binding request */
+    if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
+	LOG((ice->obj_name, "Received non-Binding request, ignored"));
+	return PJ_SUCCESS;
+    }
+
+    sd = (stun_data*) pj_stun_session_get_user_data(sess);
+    ice = sd->ice;
+    comp = sd->comp;
+
+    pj_mutex_lock(ice->mutex);
+
+    /* Get PRIORITY attribute */
+    ap = (pj_stun_priority_attr*)
+	 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0);
+    if (ap == 0) {
+	LOG((ice->obj_name, "Received Binding request with no PRIORITY"));
+	pj_mutex_unlock(ice->mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* For simplicity, ignore incoming requests when we don't have remote
+     * candidates yet. The peer agent should retransmit the STUN request
+     * and we'll receive it again later.
+     */
+    if (ice->rcand_cnt == 0) {
+	pj_mutex_unlock(ice->mutex);
+	return PJ_SUCCESS;
+    }
+
+
+    /* Find remote candidate based on the source transport address of 
+     * the request.
+     */
+    for (i=0; i<ice->rcand_cnt; ++i) {
+	if (sockaddr_cmp((const pj_sockaddr*)src_addr, &ice->rcand[i].addr)==0)
+	    break;
+    }
+
+    /* If the source transport address of the request does not match any
+     * existing remote candidates, it represents a new peer reflexive remote
+     * candidate.
+     */
+    if (i == ice->rcand_cnt) {
+	rcand = &ice->rcand[ice->rcand_cnt++];
+	rcand->comp_id = comp->comp_id;
+	rcand->type = PJ_ICE_CAND_TYPE_PEER_MAPPED;
+	rcand->prio = ap->value;
+	pj_memcpy(&rcand->addr, src_addr, src_addr_len);
+
+	/* Foundation is random, unique from other foundation */
+	rcand->foundation.ptr = pj_pool_alloc(ice->pool, 32);
+	rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 32,
+						  "f%p", 
+						  rcand->foundation.ptr);
+    } else {
+	/* Remote candidate found */
+	rcand = &ice->rcand[i];
+    }
+
+    /* 
+     * 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;
+
+    /* Next find local candidate */
+    /* Just pick up 
+
+
+
+    /* 7.2.1.2.  Learning Peer Reflexive Candidates */
+    PJ_TODO(LEARN_PEER_REFLEXIVE_CANDIDATES);
+
+    /* Reject any requests except Binding request */
+    if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
+	status = pj_stun_session_create_response(sess, msg, 
+						 PJ_STUN_SC_BAD_REQUEST,
+						 NULL, &tdata);
+	if (status != PJ_SUCCESS) {
+	    pj_mutex_unlock(ice->mutex);
+	    return status;
+	}
+
+	status = pj_stun_session_send_msg(sess, PJ_TRUE, 
+					  src_addr, src_addr_len, tdata);
+
+	pj_mutex_unlock(ice->mutex);
+	return status;
+    }
+
+    status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(ice->mutex);
+	return status;
+    }
+
+    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, PJ_TRUE, 
+				      src_addr, src_addr_len, tdata);
+
+    /* 7.2.1.3.  Triggered Checks:
+     * Next, the agent constructs a pair whose local candidate is equal to
+     * the transport address on which the STUN request was received, and a
+     * remote candidate equal to the source transport address where the
+     * request came from (which may be peer-reflexive remote candidate that
+     * was just learned). 
+     */
+    
+    pj_mutex_unlock(ice->mutex);
+    return status;
+}
+
 
 static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
@@ -1104,5 +1591,13 @@
 					 unsigned src_addr_len)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(pkt_len);
+    PJ_UNUSED_ARG(msg);
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
+
     PJ_TODO(SUPPORT_RX_BIND_REQUEST_AS_INDICATION);
+
     return PJ_ENOTSUP;
 }
Index: pjproject/trunk/pjnath/src/pjnath/stun_auth.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_auth.c	(revision 1088)
+++ pjproject/trunk/pjnath/src/pjnath/stun_auth.c	(revision 1089)
@@ -186,5 +186,6 @@
 	int data_type = 0;
 	pj_status_t rc;
-	rc = cred->data.dyn_cred.get_password(cred->data.dyn_cred.user_data,
+	rc = cred->data.dyn_cred.get_password(msg, 
+					      cred->data.dyn_cred.user_data,
 					      (arealm?&arealm->value:NULL),
 					      &auser->value, pool,
@@ -270,5 +271,6 @@
 
 	if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
-	    ok=cred->data.dyn_cred.verify_nonce(cred->data.dyn_cred.user_data,
+	    ok=cred->data.dyn_cred.verify_nonce(msg, 
+						cred->data.dyn_cred.user_data,
 						(arealm?&arealm->value:NULL),
 						&auser->value,
Index: pjproject/trunk/pjnath/src/pjnath/stun_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 1088)
+++ pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 1089)
@@ -207,12 +207,29 @@
 }
 
-static pj_str_t *get_passwd(pj_stun_session *sess)
-{
-    if (sess->cred == NULL)
+static pj_str_t *get_passwd(pj_stun_session *sess, pj_pool_t *pool,
+			    const pj_stun_msg *msg)
+{
+    if (sess->cred == NULL) {
 	return NULL;
-    else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC)
+    } else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC) {
 	return &sess->cred->data.static_cred.data;
-    else
+    } else if (sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
+	pj_str_t realm, username, nonce;
+	pj_str_t *password;
+	void *user_data = sess->cred->data.dyn_cred.user_data;
+	int data_type = 0;
+	pj_status_t status;
+
+	realm.slen = username.slen = nonce.slen = 0;
+	password = PJ_POOL_ZALLOC_T(pool, pj_str_t);
+	status = (*sess->cred->data.dyn_cred.get_cred)(msg, user_data, pool,
+						       &realm, &username,
+						       &nonce, &data_type,
+						       password);
+	return password;
+
+    } else {
 	return NULL;
+    }
 }
 
@@ -222,4 +239,8 @@
 {
     pj_status_t status = 0;
+    pj_str_t realm, username, nonce, password;
+    int data_type = 0;
+
+    realm.slen = username.slen = nonce.slen = password.slen = 0;
 
     /* The server SHOULD include a SERVER attribute in all responses */
@@ -239,26 +260,49 @@
 	!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) 
     {
-	const pj_str_t *username;
-
-	/* Create and add USERNAME attribute */
-	username = &sess->cred->data.static_cred.username;
+	realm = sess->cred->data.static_cred.realm;
+	username = sess->cred->data.static_cred.username;
+	data_type = sess->cred->data.static_cred.data_type;
+	password = sess->cred->data.static_cred.data;
+	nonce = sess->cred->data.static_cred.nonce;
+
+    } else if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC &&
+	       !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) 
+    {
+	void *user_data = sess->cred->data.dyn_cred.user_data;
+
+	status = (*sess->cred->data.dyn_cred.get_cred)(msg, user_data, pool,
+						       &realm, &username,
+						       &nonce, &data_type,
+						       &password);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+
+    /* Create and add USERNAME attribute */
+    status = pj_stun_msg_add_string_attr(pool, msg,
+					 PJ_STUN_ATTR_USERNAME,
+					 &username);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+    /* Add REALM only when long term credential is used */
+    if (realm.slen) {
 	status = pj_stun_msg_add_string_attr(pool, msg,
-					     PJ_STUN_ATTR_USERNAME,
-					     username);
+					    PJ_STUN_ATTR_REALM,
+					    &realm);
 	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
-
-	/* Add REALM only when long term credential is used */
-	if (sess->cred->data.static_cred.realm.slen) {
-	    const pj_str_t *realm = &sess->cred->data.static_cred.realm;
-	    status = pj_stun_msg_add_string_attr(pool, msg,
-						PJ_STUN_ATTR_REALM,
-						realm);
-	}
-
-	/* Add MESSAGE-INTEGRITY attribute */
-	status = pj_stun_msg_add_msgint_attr(pool, msg);
-	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
-
-    } 
+    }
+
+    /* Add NONCE when desired */
+    if (nonce.slen) {
+	status = pj_stun_msg_add_string_attr(pool, msg,
+					    PJ_STUN_ATTR_NONCE,
+					    &nonce);
+    }
+
+    /* Add MESSAGE-INTEGRITY attribute */
+    status = pj_stun_msg_add_msgint_attr(pool, msg);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
 
     /* Add FINGERPRINT attribute if necessary */
@@ -543,5 +587,6 @@
     /* Encode message */
     status = pj_stun_msg_encode(tdata->msg, tdata->pkt, tdata->max_len,
-			        0, get_passwd(sess), &tdata->pkt_size);
+			        0, get_passwd(sess, tdata->pool, tdata->msg),
+				&tdata->pkt_size);
     if (status != PJ_SUCCESS) {
 	pj_stun_msg_destroy_tdata(sess, tdata);
@@ -640,16 +685,19 @@
     pj_status_t status;
 
+    /* Apply options */
+    if (!retransmission) {
+	status = apply_msg_options(sess, pool, response);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
     /* Alloc packet buffer */
     out_max_len = PJ_STUN_MAX_PKT_LEN;
     out_pkt = pj_pool_alloc(pool, out_max_len);
 
-    /* Apply options */
-    if (!retransmission) {
-	apply_msg_options(sess, pool, response);
-    }
-
     /* Encode */
     status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, 
-				get_passwd(sess), &out_len);
+				get_passwd(sess, pool, response),
+				&out_len);
     if (status != PJ_SUCCESS) {
 	LOG_ERR_(sess, "Error encoding message", status);
