Index: pjproject/trunk/pjnath/src/pjnath/ice.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1090)
+++ pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1091)
@@ -212,4 +212,814 @@
 
 
+static void resolver_cb(void *user_data,
+			pj_status_t status,
+			pj_dns_parsed_packet *response)
+{
+    pj_assert(!"Not implemented yet!");
+    PJ_UNUSED_ARG(user_data);
+    PJ_UNUSED_ARG(status);
+    PJ_UNUSED_ARG(response);
+}
+
+PJ_DEF(pj_status_t) pj_ice_set_srv(pj_ice *ice,
+				   pj_bool_t enable_relay,
+				   pj_dns_resolver *resolver,
+				   const pj_str_t *domain)
+{
+    char namebuf[128];
+    char *tp_name;
+    pj_str_t name;
+    pj_status_t status;
+
+
+    /* Not implemented yet! */
+    return PJ_ENOTSUP;
+
+
+    PJ_ASSERT_RETURN(ice && resolver && domain, PJ_EINVAL);
+
+    /* Must not have a running resolver. This is because we couldn't
+     * safely cancel the query (there is a race condition situation
+     * between the callback acquiring the mutex and this function
+     * acquiring the mutex)
+     */
+    PJ_ASSERT_RETURN(ice->resv_q==NULL, PJ_EBUSY);
+
+    pj_mutex_lock(ice->mutex);
+
+    /* Reset resolver and server addresses */
+    ice->relay_enabled = enable_relay;
+    ice->resv = resolver;
+    pj_bzero(&ice->stun_srv, sizeof(ice->stun_srv));
+
+    /* Build SRV record name */
+    if (ice->sock_type == PJ_SOCK_DGRAM) {
+	tp_name = "_udp";
+    } else if (ice->sock_type == PJ_SOCK_STREAM) {
+	tp_name = "_tcp";
+    } else {
+	pj_assert(!"Invalid sock_type");
+	pj_mutex_unlock(ice->mutex);
+	return PJ_EBUG;
+    }
+
+    if (enable_relay) {
+	name.ptr = namebuf;
+	name.slen = pj_ansi_snprintf(namebuf, sizeof(namebuf),
+				     "_stun-relay.%s.%.*s",
+				     tp_name,
+				     (int)domain->slen,
+				     domain->ptr);
+    } else {
+	name.ptr = namebuf;
+	name.slen = pj_ansi_snprintf(namebuf, sizeof(namebuf),
+				     "_stun.%s.%.*s",
+				     tp_name,
+				     (int)domain->slen,
+				     domain->ptr);
+    }
+
+    if (name.slen < 1 || name.slen >= sizeof(namebuf)) {
+	pj_mutex_unlock(ice->mutex);
+	return PJ_ENAMETOOLONG;
+    }
+
+    /* Start DNS query */
+    status = pj_dns_resolver_start_query(ice->resv, &name, 
+					 PJ_DNS_TYPE_SRV, 0, 
+					 &resolver_cb, 
+					 ice, &ice->resv_q);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(ice->mutex);
+	return status;
+    }
+
+    pj_mutex_unlock(ice->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_ice_set_srv_addr(pj_ice *ice,
+					pj_bool_t enable_relay,
+					const pj_sockaddr_t *srv_addr,
+					unsigned addr_len)
+{
+    PJ_ASSERT_RETURN(ice && srv_addr, PJ_EINVAL);
+    /* Must not have a running resolver. This is because we couldn't
+     * safely cancel the query (there is a race condition situation
+     * between the callback acquiring the mutex and this function
+     * acquiring the mutex)
+     */
+    PJ_ASSERT_RETURN(ice->resv_q==NULL, PJ_EBUSY);
+
+    pj_mutex_lock(ice->mutex);
+
+    ice->relay_enabled = enable_relay;
+    pj_memcpy(&ice->stun_srv, srv_addr, addr_len);
+
+    pj_mutex_unlock(ice->mutex);
+
+    return PJ_SUCCESS;
+
+}
+
+
+
+PJ_DEF(pj_status_t) pj_ice_add_comp(pj_ice *ice,
+				    unsigned comp_id,
+				    const pj_sockaddr_t *local_addr,
+				    unsigned addr_len)
+{
+    pj_status_t status;
+    pj_sock_t sock;
+
+    PJ_ASSERT_RETURN(ice && local_addr && addr_len, PJ_EINVAL);
+
+    status = pj_sock_socket(ice->af, ice->sock_type, 0, &sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pj_sock_bind(sock, local_addr, addr_len);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pj_ice_add_sock_comp(ice, comp_id, sock);
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(sock);
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+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,
+					  unsigned comp_id,
+					  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);
+
+    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);
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t stun_auth_get_auth(void *user_data,
+				      pj_pool_t *pool,
+				      pj_str_t *realm,
+				      pj_str_t *nonce)
+{
+    PJ_UNUSED_ARG(user_data);
+    PJ_UNUSED_ARG(pool);
+
+    realm->slen = 0;
+    nonce->slen = 0;
+
+    return PJ_SUCCESS;
+}
+
+
+/* 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,
+					  pj_pool_t *pool,
+					  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(realm);
+    PJ_UNUSED_ARG(pool);
+
+    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)
+	    return -1;
+	*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
+	 * equal to the username fragment generated by the agent in an offer
+	 * or answer for a session in-progress, and the MESSAGE-INTEGRITY 
+	 * is the output of a hash of the password and the STUN packet's 
+	 * contents. 
+	 */
+	PJ_TODO(CHECK_USERNAME_FOR_INCOMING_STUN_REQUEST);
+	*data_type = 0;
+	*data = ice->rx_pass;
+
+    }
+
+    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,
+					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;
+}
+
+
+PJ_DEF(pj_status_t) pj_ice_set_credentials(pj_ice *ice,
+					   const pj_str_t *local_ufrag,
+					   const pj_str_t *local_pass,
+					   const pj_str_t *remote_ufrag,
+					   const pj_str_t *remote_pass)
+{
+    char buf[128];
+    pj_str_t username;
+
+    username.ptr = buf;
+
+    PJ_ASSERT_RETURN(ice && local_ufrag && local_pass &&
+		     remote_ufrag && remote_pass, PJ_EINVAL);
+    PJ_ASSERT_RETURN(local_ufrag->slen + remote_ufrag->slen <
+		     sizeof(buf), PJ_ENAMETOOLONG);
+
+    pj_strcpy(&username, remote_ufrag);
+    pj_strcat2(&username, ":");
+    pj_strcat(&username, local_ufrag);
+
+    pj_strdup(ice->pool, &ice->tx_uname, &username);
+    pj_strdup(ice->pool, &ice->tx_pass, remote_pass);
+
+    pj_strcpy(&username, local_ufrag);
+    pj_strcat2(&username, ":");
+    pj_strcat(&username, remote_ufrag);
+
+    pj_strdup(ice->pool, &ice->rx_uname, &username);
+    pj_strdup(ice->pool, &ice->rx_pass, local_pass);
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t gather_host_cands(pj_ice *ice)
+{
+    unsigned i;
+    pj_status_t status;
+
+    for (i=0; i<ice->comp_cnt; ++i) {
+	pj_ice_comp *comp = &ice->comp[i];
+	pj_sockaddr addr;
+	int addr_len;
+
+	addr_len = sizeof(addr);
+	status = pj_sock_getsockname(comp->sock, &addr, &addr_len);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (addr.ipv4.sin_addr.s_addr == 0) {
+	    status = pj_gethostip(&addr.ipv4.sin_addr);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+
+	status = pj_ice_add_cand(ice, i, PJ_ICE_CAND_TYPE_HOST, 65535,
+				 &host_foundation, &addr, &addr, NULL,
+    				 sizeof(pj_sockaddr_in), NULL);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* 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,
+					unsigned flags)
+{
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(flags);
+
+    /* Gather host candidate */
+    status = gather_host_cands(ice);
+    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);
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_uint32_t CALC_CAND_PRIO(pj_ice_cand_type type,
+				  pj_uint32_t local_pref,
+				  pj_uint32_t comp_id)
+{
+    static pj_uint32_t type_pref[] =
+    {
+	PJ_ICE_HOST_PREF,
+	PJ_ICE_MAPPED_PREF,
+	PJ_ICE_PEER_MAPPED_PREF,
+	PJ_ICE_RELAYED_PREF
+    };
+
+    return ((1 << 24) * type_pref[type]) + 
+	   ((1 << 8) * local_pref) +
+	   (256 - comp_id);
+}
+
+
+PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice,
+				    unsigned comp_id,
+				    pj_ice_cand_type type,
+				    pj_uint16_t local_pref,
+				    const pj_str_t *foundation,
+				    const pj_sockaddr_t *addr,
+				    const pj_sockaddr_t *base_addr,
+				    const pj_sockaddr_t *srv_addr,
+				    int addr_len,
+				    unsigned *p_cand_id)
+{
+    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);
+
+    if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) {
+	status = PJ_ETOOMANY;
+	goto on_error;
+    }
+
+    lcand = &ice->lcand[ice->lcand_cnt];
+    lcand->comp_id = comp_id;
+    lcand->type = type;
+    pj_strdup(ice->pool, &lcand->foundation, foundation);
+    lcand->prio = CALC_CAND_PRIO(type, local_pref, lcand->comp_id);
+    pj_memcpy(&lcand->addr, addr, addr_len);
+    pj_memcpy(&lcand->base_addr, base_addr, addr_len);
+    if (srv_addr)
+	pj_memcpy(&lcand->srv_addr, srv_addr, addr_len);
+    else
+	pj_bzero(&lcand->srv_addr, sizeof(lcand->srv_addr));
+
+    if (p_cand_id)
+	*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;
+
+on_error:
+    pj_mutex_unlock(ice->mutex);
+    return status;
+}
+
+
+PJ_DEF(unsigned) pj_ice_get_cand_cnt(pj_ice *ice)
+{
+    return ice->lcand_cnt;
+}
+
+
+PJ_DEF(pj_status_t) pj_ice_enum_cands(pj_ice *ice,
+				      unsigned *p_count,
+				      unsigned cand_ids[])
+{
+    unsigned i, count;
+
+    PJ_ASSERT_RETURN(ice && p_count && *p_count && cand_ids, PJ_EINVAL);
+
+    pj_mutex_lock(ice->mutex);
+
+    count = (*p_count < ice->lcand_cnt) ? *p_count : ice->lcand_cnt;
+    for (i=0; i<count; ++i)
+	cand_ids[i] = i;
+
+    *p_count = count;
+    pj_mutex_unlock(ice->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+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,
+				    pj_ice_cand **p_cand)
+{
+    PJ_ASSERT_RETURN(ice && p_cand, PJ_EINVAL);
+    PJ_ASSERT_RETURN(cand_id <= ice->lcand_cnt, PJ_EINVAL);
+
+    *p_cand = &ice->lcand[cand_id];
+
+    return PJ_SUCCESS;
+}
+
+#ifndef MIN
+#   define MIN(a,b) (a < b ? a : b)
+#endif
+
+#ifndef MAX
+#   define MAX(a,b) (a > b ? a : b)
+#endif
+
+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);
+}
+
+static const char *dump_check(char *buffer, unsigned bufsize,
+			      const pj_ice_check *check)
+{
+    const pj_ice_cand *lcand = check->lcand;
+    const pj_ice_cand *rcand = check->rcand;
+    char laddr[CHECK_NAME_LEN];
+    int len;
+
+    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;
+    else if (len >= (int)bufsize)
+	len = bufsize - 1;
+
+    buffer[len] = '\0';
+    return buffer;
+}
+
+#if PJ_LOG_MAX_LEVEL >= 4
+static void dump_checklist(const char *title, const pj_ice *ice, 
+			   const pj_ice_checklist *clist)
+{
+    unsigned i;
+    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];
+	LOG((ice->obj_name, " %d: %s (prio=%u, state=%s)",
+	     i, dump_check(buffer, sizeof(buffer), c),
+	     c->prio, check_state_name[c->state]));
+    }
+}
+#else
+#define dump_checklist(ice, clist)
+#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), 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;
+}
+
+/* Sort checklist based on priority */
+static void sort_checklist(pj_ice_checklist *clist)
+{
+    unsigned i;
+
+    for (i=0; i<clist->count-1; ++i) {
+	unsigned j, highest = i;
+	for (j=i+1; j<clist->count; ++j) {
+	    if (clist->checks[j].prio > clist->checks[highest].prio) {
+		highest = j;
+	    }
+	}
+
+	if (highest != i) {
+	    pj_ice_check tmp;
+
+	    pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_check));
+	    pj_memcpy(&clist->checks[i], &clist->checks[highest], 
+		      sizeof(pj_ice_check));
+	    pj_memcpy(&clist->checks[highest], &tmp, sizeof(pj_ice_check));
+	}
+    }
+}
+
+/* Sort valid list based on priority */
+static void sort_valid_list(pj_ice *ice)
+{
+    unsigned i;
+
+    for (i=0; i<ice->valid_cnt-1; ++i) {
+	unsigned j, highest = i;
+	pj_ice_check *ci = &ice->clist.checks[ice->valid_list[i]];
+
+	for (j=i+1; j<ice->valid_cnt; ++j) {
+	    pj_ice_check *cj = &ice->clist.checks[ice->valid_list[j]];
+
+	    if (cj->prio > ci->prio) {
+		highest = j;
+	    }
+	}
+
+	if (highest != i) {
+	    unsigned tmp = ice->valid_list[i];
+	    ice->valid_list[i] = ice->valid_list[j];
+	    ice->valid_list[j] = tmp;
+	}
+    }
+}
+
+
+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), &clist->checks[j])));
+
+		pj_array_erase(clist->checks, sizeof(clist->checks[0]),
+			       clist->count, j);
+		--clist->count;
+
+	    } else {
+		++j;
+	    }
+	}
+    }
+}
+
 /* This function is called when ICE processing completes */
 static void on_ice_complete(pj_ice *ice, pj_status_t status)
@@ -235,8 +1045,8 @@
     if (check->nominated) {
 	for (i=0; i<ice->clist.count; ++i) {
-	    pj_ice_check *c;
+	    pj_ice_check *c = &ice->clist.checks[i];
 	    if (c->lcand->comp_id == check->lcand->comp_id &&
 		(c->state==PJ_ICE_CHECK_STATE_FROZEN ||
-		 c->state==PJ_ICE_CHECK_STATE_WAITING)
+		 c->state==PJ_ICE_CHECK_STATE_WAITING))
 	    {
 		check_set_state(ice, check, PJ_ICE_CHECK_STATE_FAILED,
@@ -281,5 +1091,5 @@
      */
     for (i=0; i<ice->valid_cnt; ++i) {
-	pj_ice_check *c = ice->clist.checks[ice->valid_list[i]];
+	pj_ice_check *c = &ice->clist.checks[ice->valid_list[i]];
 	if (c->lcand->comp_id == 1)
 	    break;
@@ -314,814 +1124,6 @@
 
 
-static void resolver_cb(void *user_data,
-			pj_status_t status,
-			pj_dns_parsed_packet *response)
-{
-    pj_assert(!"Not implemented yet!");
-    PJ_UNUSED_ARG(user_data);
-    PJ_UNUSED_ARG(status);
-    PJ_UNUSED_ARG(response);
-}
-
-PJ_DEF(pj_status_t) pj_ice_set_srv(pj_ice *ice,
-				   pj_bool_t enable_relay,
-				   pj_dns_resolver *resolver,
-				   const pj_str_t *domain)
-{
-    char namebuf[128];
-    char *tp_name;
-    pj_str_t name;
-    pj_status_t status;
-
-
-    /* Not implemented yet! */
-    return PJ_ENOTSUP;
-
-
-    PJ_ASSERT_RETURN(ice && resolver && domain, PJ_EINVAL);
-
-    /* Must not have a running resolver. This is because we couldn't
-     * safely cancel the query (there is a race condition situation
-     * between the callback acquiring the mutex and this function
-     * acquiring the mutex)
-     */
-    PJ_ASSERT_RETURN(ice->resv_q==NULL, PJ_EBUSY);
-
-    pj_mutex_lock(ice->mutex);
-
-    /* Reset resolver and server addresses */
-    ice->relay_enabled = enable_relay;
-    ice->resv = resolver;
-    pj_bzero(&ice->stun_srv, sizeof(ice->stun_srv));
-
-    /* Build SRV record name */
-    if (ice->sock_type == PJ_SOCK_DGRAM) {
-	tp_name = "_udp";
-    } else if (ice->sock_type == PJ_SOCK_STREAM) {
-	tp_name = "_tcp";
-    } else {
-	pj_assert(!"Invalid sock_type");
-	pj_mutex_unlock(ice->mutex);
-	return PJ_EBUG;
-    }
-
-    if (enable_relay) {
-	name.ptr = namebuf;
-	name.slen = pj_ansi_snprintf(namebuf, sizeof(namebuf),
-				     "_stun-relay.%s.%.*s",
-				     tp_name,
-				     (int)domain->slen,
-				     domain->ptr);
-    } else {
-	name.ptr = namebuf;
-	name.slen = pj_ansi_snprintf(namebuf, sizeof(namebuf),
-				     "_stun.%s.%.*s",
-				     tp_name,
-				     (int)domain->slen,
-				     domain->ptr);
-    }
-
-    if (name.slen < 1 || name.slen >= sizeof(namebuf)) {
-	pj_mutex_unlock(ice->mutex);
-	return PJ_ENAMETOOLONG;
-    }
-
-    /* Start DNS query */
-    status = pj_dns_resolver_start_query(ice->resv, &name, 
-					 PJ_DNS_TYPE_SRV, 0, 
-					 &resolver_cb, 
-					 ice, &ice->resv_q);
-    if (status != PJ_SUCCESS) {
-	pj_mutex_unlock(ice->mutex);
-	return status;
-    }
-
-    pj_mutex_unlock(ice->mutex);
-
-    return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_ice_set_srv_addr(pj_ice *ice,
-					pj_bool_t enable_relay,
-					const pj_sockaddr_t *srv_addr,
-					unsigned addr_len)
-{
-    PJ_ASSERT_RETURN(ice && srv_addr, PJ_EINVAL);
-    /* Must not have a running resolver. This is because we couldn't
-     * safely cancel the query (there is a race condition situation
-     * between the callback acquiring the mutex and this function
-     * acquiring the mutex)
-     */
-    PJ_ASSERT_RETURN(ice->resv_q==NULL, PJ_EBUSY);
-
-    pj_mutex_lock(ice->mutex);
-
-    ice->relay_enabled = enable_relay;
-    pj_memcpy(&ice->stun_srv, srv_addr, addr_len);
-
-    pj_mutex_unlock(ice->mutex);
-
-    return PJ_SUCCESS;
-
-}
-
-
-
-PJ_DEF(pj_status_t) pj_ice_add_comp(pj_ice *ice,
-				    unsigned comp_id,
-				    const pj_sockaddr_t *local_addr,
-				    unsigned addr_len)
-{
-    pj_status_t status;
-    pj_sock_t sock;
-
-    PJ_ASSERT_RETURN(ice && local_addr && addr_len, PJ_EINVAL);
-
-    status = pj_sock_socket(ice->af, ice->sock_type, 0, &sock);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    status = pj_sock_bind(sock, local_addr, addr_len);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    status = pj_ice_add_sock_comp(ice, comp_id, sock);
-    if (status != PJ_SUCCESS) {
-	pj_sock_close(sock);
-	return status;
-    }
-
-    return PJ_SUCCESS;
-}
-
-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,
-					  unsigned comp_id,
-					  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);
-
-    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);
-
-    return PJ_SUCCESS;
-}
-
-
-static pj_status_t stun_auth_get_auth(void *user_data,
-				      pj_pool_t *pool,
-				      pj_str_t *realm,
-				      pj_str_t *nonce)
-{
-    PJ_UNUSED_ARG(user_data);
-    PJ_UNUSED_ARG(pool);
-
-    realm->slen = 0;
-    nonce->slen = 0;
-
-    return PJ_SUCCESS;
-}
-
-
-/* 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,
-					  pj_pool_t *pool,
-					  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(realm);
-    PJ_UNUSED_ARG(pool);
-
-    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)
-	    return -1;
-	*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
-	 * equal to the username fragment generated by the agent in an offer
-	 * or answer for a session in-progress, and the MESSAGE-INTEGRITY 
-	 * is the output of a hash of the password and the STUN packet's 
-	 * contents. 
-	 */
-	PJ_TODO(CHECK_USERNAME_FOR_INCOMING_STUN_REQUEST);
-	*data_type = 0;
-	*data = ice->rx_pass;
-
-    }
-
-    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,
-					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;
-}
-
-
-PJ_DEF(pj_status_t) pj_ice_set_credentials(pj_ice *ice,
-					   const pj_str_t *local_ufrag,
-					   const pj_str_t *local_pass,
-					   const pj_str_t *remote_ufrag,
-					   const pj_str_t *remote_pass)
-{
-    char buf[128];
-    pj_str_t username;
-
-    username.ptr = buf;
-
-    PJ_ASSERT_RETURN(ice && local_ufrag && local_pass &&
-		     remote_ufrag && remote_pass, PJ_EINVAL);
-    PJ_ASSERT_RETURN(local_ufrag->slen + remote_ufrag->slen <
-		     sizeof(buf), PJ_ENAMETOOLONG);
-
-    pj_strcpy(&username, remote_ufrag);
-    pj_strcat2(&username, ":");
-    pj_strcat(&username, local_ufrag);
-
-    pj_strdup(ice->pool, &ice->tx_uname, &username);
-    pj_strdup(ice->pool, &ice->tx_pass, remote_pass);
-
-    pj_strcpy(&username, local_ufrag);
-    pj_strcat2(&username, ":");
-    pj_strcat(&username, remote_ufrag);
-
-    pj_strdup(ice->pool, &ice->rx_uname, &username);
-    pj_strdup(ice->pool, &ice->rx_pass, local_pass);
-
-    return PJ_SUCCESS;
-}
-
-
-static pj_status_t gather_host_cands(pj_ice *ice)
-{
-    unsigned i;
-    pj_status_t status;
-
-    for (i=0; i<ice->comp_cnt; ++i) {
-	pj_ice_comp *comp = &ice->comp[i];
-	pj_sockaddr addr;
-	int addr_len;
-
-	addr_len = sizeof(addr);
-	status = pj_sock_getsockname(comp->sock, &addr, &addr_len);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	if (addr.ipv4.sin_addr.s_addr == 0) {
-	    status = pj_gethostip(&addr.ipv4.sin_addr);
-	    if (status != PJ_SUCCESS)
-		return status;
-	}
-
-	status = pj_ice_add_cand(ice, i, PJ_ICE_CAND_TYPE_HOST, 65535,
-				 &host_foundation, &addr, &addr, NULL,
-    				 sizeof(pj_sockaddr_in), NULL);
-	if (status != PJ_SUCCESS)
-	    return status;
-    }
-
-    return PJ_SUCCESS;
-}
-
-/* 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,
-					unsigned flags)
-{
-    pj_status_t status;
-
-    PJ_UNUSED_ARG(flags);
-
-    /* Gather host candidate */
-    status = gather_host_cands(ice);
-    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);
-
-    return PJ_SUCCESS;
-}
-
-
-static pj_uint32_t CALC_CAND_PRIO(pj_ice_cand_type type,
-				  pj_uint32_t local_pref,
-				  pj_uint32_t comp_id)
-{
-    static pj_uint32_t type_pref[] =
-    {
-	PJ_ICE_HOST_PREF,
-	PJ_ICE_MAPPED_PREF,
-	PJ_ICE_PEER_MAPPED_PREF,
-	PJ_ICE_RELAYED_PREF
-    };
-
-    return ((1 << 24) * type_pref[type]) + 
-	   ((1 << 8) * local_pref) +
-	   (256 - comp_id);
-}
-
-
-PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice,
-				    unsigned comp_id,
-				    pj_ice_cand_type type,
-				    pj_uint16_t local_pref,
-				    const pj_str_t *foundation,
-				    const pj_sockaddr_t *addr,
-				    const pj_sockaddr_t *base_addr,
-				    const pj_sockaddr_t *srv_addr,
-				    int addr_len,
-				    unsigned *p_cand_id)
-{
-    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);
-
-    if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) {
-	status = PJ_ETOOMANY;
-	goto on_error;
-    }
-
-    lcand = &ice->lcand[ice->lcand_cnt];
-    lcand->comp_id = comp_id;
-    lcand->type = type;
-    pj_strdup(ice->pool, &lcand->foundation, foundation);
-    lcand->prio = CALC_CAND_PRIO(type, local_pref, lcand->comp_id);
-    pj_memcpy(&lcand->addr, addr, addr_len);
-    pj_memcpy(&lcand->base_addr, base_addr, addr_len);
-    if (srv_addr)
-	pj_memcpy(&lcand->srv_addr, srv_addr, addr_len);
-    else
-	pj_bzero(&lcand->srv_addr, sizeof(lcand->srv_addr));
-
-    if (p_cand_id)
-	*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;
-
-on_error:
-    pj_mutex_unlock(ice->mutex);
-    return status;
-}
-
-
-PJ_DEF(unsigned) pj_ice_get_cand_cnt(pj_ice *ice)
-{
-    return ice->lcand_cnt;
-}
-
-
-PJ_DEF(pj_status_t) pj_ice_enum_cands(pj_ice *ice,
-				      unsigned *p_count,
-				      unsigned cand_ids[])
-{
-    unsigned i, count;
-
-    PJ_ASSERT_RETURN(ice && p_count && *p_count && cand_ids, PJ_EINVAL);
-
-    pj_mutex_lock(ice->mutex);
-
-    count = (*p_count < ice->lcand_cnt) ? *p_count : ice->lcand_cnt;
-    for (i=0; i<count; ++i)
-	cand_ids[i] = i;
-
-    *p_count = count;
-    pj_mutex_unlock(ice->mutex);
-
-    return PJ_SUCCESS;
-}
-
-
-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,
-				    pj_ice_cand **p_cand)
-{
-    PJ_ASSERT_RETURN(ice && p_cand, PJ_EINVAL);
-    PJ_ASSERT_RETURN(cand_id <= ice->lcand_cnt, PJ_EINVAL);
-
-    *p_cand = &ice->lcand[cand_id];
-
-    return PJ_SUCCESS;
-}
-
-#ifndef MIN
-#   define MIN(a,b) (a < b ? a : b)
-#endif
-
-#ifndef MAX
-#   define MAX(a,b) (a > b ? a : b)
-#endif
-
-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);
-}
-
-static const char *dump_check(char *buffer, unsigned bufsize,
-			      const pj_ice *ice,
-			      const pj_ice_check *check)
-{
-    const pj_ice_cand *lcand = check->lcand;
-    const pj_ice_cand *rcand = check->rcand;
-    char laddr[CHECK_NAME_LEN];
-    int len;
-
-    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;
-    else if (len >= (int)bufsize)
-	len = bufsize - 1;
-
-    buffer[len] = '\0';
-    return buffer;
-}
-
-#if PJ_LOG_MAX_LEVEL >= 4
-static void dump_checklist(const char *title, const pj_ice *ice, 
-			   const pj_ice_checklist *clist)
-{
-    unsigned i;
-    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];
-	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]));
-    }
-}
-#else
-#define dump_checklist(ice, clist)
-#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;
-}
-
-/* Sort checklist based on priority */
-static void sort_checklist(pj_ice_checklist *clist)
-{
-    unsigned i;
-
-    for (i=0; i<clist->count-1; ++i) {
-	unsigned j, highest = i;
-	for (j=i+1; j<clist->count; ++j) {
-	    if (clist->checks[j].prio > clist->checks[highest].prio) {
-		highest = j;
-	    }
-	}
-
-	if (highest != i) {
-	    pj_ice_check tmp;
-
-	    pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_check));
-	    pj_memcpy(&clist->checks[i], &clist->checks[highest], 
-		      sizeof(pj_ice_check));
-	    pj_memcpy(&clist->checks[highest], &tmp, sizeof(pj_ice_check));
-	}
-    }
-}
-
-/* Sort valid list based on priority */
-static void sort_valid_list(pj_ice *ice)
-{
-    unsigned i;
-
-    for (i=0; i<ice->valid_cnt-1; ++i) {
-	unsigned j, highest = i;
-	pj_ice_check *ci = ice->clist.checks[ice->valid_list[i]];
-
-	for (j=i+1; j<ice->valid_cnt; ++j) {
-	    pj_ice_check *cj = ice->clist.checks[ice->valid_list[j]];
-
-	    if (cj->prio > ci->prio) {
-		highest = j;
-	    }
-	}
-
-	if (highest != i) {
-	    unsigned tmp = ice->valid_list[i];
-	    ice->valid_list[i] = ice->valid_list[j];
-	    ice->valid_list[j] = tmp;
-	}
-    }
-}
-
-
-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
@@ -1242,5 +1244,5 @@
     LOG((ice->obj_name, 
 	 "Sending connectivity check for check %d: %s", 
-	 check_id, dump_check(buffer, sizeof(buffer), ice, check)));
+	 check_id, dump_check(buffer, sizeof(buffer), check)));
 
     /* Create request */
@@ -1434,5 +1436,5 @@
     struct req_data *rd = (struct req_data*) tdata->user_data;
     pj_ice *ice;
-    pj_ice_check *check, *valid_check;
+    pj_ice_check *check;
     const pj_ice_cand *lcand;
     const pj_ice_cand *rcand;
@@ -1456,5 +1458,5 @@
 	 "Connectivity check %s for check %s",
 	 (status==PJ_SUCCESS ? "SUCCESS" : "FAILED"), 
-	 dump_check(buffer, sizeof(buffer), ice, check)));
+	 dump_check(buffer, sizeof(buffer), check)));
 
     if (status != PJ_SUCCESS) {
