Index: pjproject/trunk/pjlib-util/src/pjlib-util/resolver.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/resolver.c	(revision 4649)
+++ pjproject/trunk/pjlib-util/src/pjlib-util/resolver.c	(revision 5826)
@@ -20,4 +20,5 @@
 #include <pjlib-util/resolver.h>
 #include <pjlib-util/errno.h>
+#include <pj/compat/socket.h>
 #include <pj/assert.h>
 #include <pj/ctype.h>
@@ -80,5 +81,5 @@
 struct nameserver
 {
-    pj_sockaddr_in  addr;		/**< Server address.		    */
+    pj_sockaddr     addr;		/**< Server address.		    */
 
     enum ns_state   state;		/**< Nameserver state.		    */
@@ -169,5 +170,5 @@
     /* Internals */
     pj_pool_t		*pool;		/**< Internal pool.		    */
-    pj_mutex_t		*mutex;		/**< Mutex protection.		    */
+    pj_grp_lock_t	*grp_lock;	/**< Group lock protection.	    */
     pj_bool_t		 own_timer;	/**< Do we own timer?		    */
     pj_timer_heap_t	*timer;		/**< Timer instance.		    */
@@ -180,10 +181,21 @@
     pj_ioqueue_key_t	*udp_key;	/**< UDP socket ioqueue key.	    */
     unsigned char	 udp_rx_pkt[UDPSZ];/**< UDP receive buffer.	    */
-    unsigned char	 udp_tx_pkt[UDPSZ];/**< UDP receive buffer.	    */
-    pj_ssize_t		 udp_len;	/**< Length of received packet.	    */
+    unsigned char	 udp_tx_pkt[UDPSZ];/**< UDP transmit buffer.	    */
     pj_ioqueue_op_key_t	 udp_op_rx_key;	/**< UDP read operation key.	    */
     pj_ioqueue_op_key_t	 udp_op_tx_key;	/**< UDP write operation key.	    */
-    pj_sockaddr_in	 udp_src_addr;	/**< Source address of packet	    */
+    pj_sockaddr		 udp_src_addr;	/**< Source address of packet	    */
     int			 udp_addr_len;	/**< Source address length.	    */
+
+#if PJ_HAS_IPV6
+    /* IPv6 socket */
+    pj_sock_t		 udp6_sock;	/**< UDP socket.		    */
+    pj_ioqueue_key_t	*udp6_key;	/**< UDP socket ioqueue key.	    */
+    unsigned char	 udp6_rx_pkt[UDPSZ];/**< UDP receive buffer.	    */
+    //unsigned char	 udp6_tx_pkt[UDPSZ];/**< UDP transmit buffer.	    */
+    pj_ioqueue_op_key_t	 udp6_op_rx_key;/**< UDP read operation key.	    */
+    pj_ioqueue_op_key_t	 udp6_op_tx_key;/**< UDP write operation key.	    */
+    pj_sockaddr		 udp6_src_addr;	/**< Source address of packet	    */
+    int			 udp6_addr_len;	/**< Source address length.	    */
+#endif
 
     /* Settings */
@@ -225,4 +237,6 @@
 				      unsigned servers[]);
 
+/* Destructor */
+static void dns_resolver_on_destroy(void *member);
 
 /* Close UDP socket */
@@ -238,4 +252,15 @@
 	resv->udp_sock = PJ_INVALID_SOCKET;
     }
+
+#if PJ_HAS_IPV6
+    if (resv->udp6_key != NULL) {
+	pj_ioqueue_unregister(resv->udp6_key);
+	resv->udp6_key = NULL;
+	resv->udp6_sock = PJ_INVALID_SOCKET;
+    } else if (resv->udp6_sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(resv->udp6_sock);
+	resv->udp6_sock = PJ_INVALID_SOCKET;
+    }
+#endif
 }
 
@@ -245,4 +270,6 @@
 {
     pj_ioqueue_callback socket_cb;
+    pj_sockaddr bound_addr;
+    pj_ssize_t rx_pkt_size;
     pj_status_t status;
 
@@ -260,7 +287,7 @@
     pj_bzero(&socket_cb, sizeof(socket_cb));
     socket_cb.on_read_complete = &on_read_complete;
-    status = pj_ioqueue_register_sock(resv->pool, resv->ioqueue,
-				      resv->udp_sock, resv, &socket_cb,
-				      &resv->udp_key);
+    status = pj_ioqueue_register_sock2(resv->pool, resv->ioqueue,
+				       resv->udp_sock, resv->grp_lock,
+				       resv, &socket_cb, &resv->udp_key);
     if (status != PJ_SUCCESS)
 	return status;
@@ -270,12 +297,64 @@
 
     /* Start asynchronous read to the UDP socket */
-    resv->udp_len = sizeof(resv->udp_rx_pkt);
+    rx_pkt_size = sizeof(resv->udp_rx_pkt);
     resv->udp_addr_len = sizeof(resv->udp_src_addr);
     status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key,
-				 resv->udp_rx_pkt, &resv->udp_len,
+				 resv->udp_rx_pkt, &rx_pkt_size,
 				 PJ_IOQUEUE_ALWAYS_ASYNC,
 				 &resv->udp_src_addr, &resv->udp_addr_len);
     if (status != PJ_EPENDING)
 	return status;
+
+
+#if PJ_HAS_IPV6
+    /* Also setup IPv6 socket */
+
+    /* Create the UDP socket */
+    status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0,
+			    &resv->udp6_sock);
+    if (status != PJ_SUCCESS) {
+	/* Skip IPv6 socket on system without IPv6 (see ticket #1953) */
+	if (status == PJ_STATUS_FROM_OS(OSERR_EAFNOSUPPORT)) {
+	    PJ_LOG(3,(resv->name.ptr,
+		      "System does not support IPv6, resolver will "
+		      "ignore any IPv6 nameservers"));
+	    return PJ_SUCCESS;
+	}
+	return status;
+    }
+
+    /* Bind to any address/port */
+    pj_sockaddr_init(pj_AF_INET6(), &bound_addr, NULL, 0);
+    status = pj_sock_bind(resv->udp6_sock, &bound_addr,
+			  pj_sockaddr_get_len(&bound_addr));
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Register to ioqueue */
+    pj_bzero(&socket_cb, sizeof(socket_cb));
+    socket_cb.on_read_complete = &on_read_complete;
+    status = pj_ioqueue_register_sock2(resv->pool, resv->ioqueue,
+				       resv->udp6_sock, resv->grp_lock,
+				       resv, &socket_cb, &resv->udp6_key);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_ioqueue_op_key_init(&resv->udp6_op_rx_key,
+			   sizeof(resv->udp6_op_rx_key));
+    pj_ioqueue_op_key_init(&resv->udp6_op_tx_key,
+			   sizeof(resv->udp6_op_tx_key));
+
+    /* Start asynchronous read to the UDP socket */
+    rx_pkt_size = sizeof(resv->udp6_rx_pkt);
+    resv->udp6_addr_len = sizeof(resv->udp6_src_addr);
+    status = pj_ioqueue_recvfrom(resv->udp6_key, &resv->udp6_op_rx_key,
+				 resv->udp6_rx_pkt, &rx_pkt_size,
+				 PJ_IOQUEUE_ALWAYS_ASYNC,
+				 &resv->udp6_src_addr, &resv->udp6_addr_len);
+    if (status != PJ_EPENDING)
+	return status;
+#else
+    PJ_UNUSED_ARG(bound_addr);
+#endif
 
     return PJ_SUCCESS;
@@ -326,8 +405,12 @@
     pj_strdup2_with_null(pool, &resv->name, name);
     
-    /* Create the mutex */
-    status = pj_mutex_create_recursive(pool, name, &resv->mutex);
+    /* Create group lock */
+    status = pj_grp_lock_create_w_handler(pool, NULL, resv,
+					  &dns_resolver_on_destroy,
+					  &resv->grp_lock); 
     if (status != PJ_SUCCESS)
 	goto on_error;
+
+    pj_grp_lock_add_ref(resv->grp_lock);
 
     /* Timer, ioqueue, and settings */
@@ -341,4 +424,5 @@
     /* Create the timer heap if one is not specified */
     if (resv->timer == NULL) {
+	resv->own_timer = PJ_TRUE;
 	status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer);
 	if (status != PJ_SUCCESS)
@@ -348,4 +432,5 @@
     /* Create the ioqueue if one is not specified */
     if (resv->ioqueue == NULL) {
+	resv->own_ioqueue = PJ_TRUE;
 	status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue);
 	if (status != PJ_SUCCESS)
@@ -373,4 +458,11 @@
     pj_dns_resolver_destroy(resv, PJ_FALSE);
     return status;
+}
+
+
+void dns_resolver_on_destroy(void *member)
+{
+    pj_dns_resolver *resolver = (pj_dns_resolver*)member;
+    pj_pool_safe_release(&resolver->pool);
 }
 
@@ -432,14 +524,6 @@
     }
 
-    if (resolver->mutex) {
-	pj_mutex_destroy(resolver->mutex);
-	resolver->mutex = NULL;
-    }
-
-    if (resolver->pool) {
-	pj_pool_t *pool = resolver->pool;
-	resolver->pool = NULL;
-	pj_pool_release(pool);
-    }
+    pj_grp_lock_dec_ref(resolver->grp_lock);
+
     return PJ_SUCCESS;
 }
@@ -462,5 +546,5 @@
     PJ_ASSERT_RETURN(count < PJ_DNS_RESOLVER_MAX_NS, PJ_EINVAL);
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     if (count > PJ_DNS_RESOLVER_MAX_NS)
@@ -475,8 +559,11 @@
 	struct nameserver *ns = &resolver->ns[i];
 
-	status = pj_sockaddr_in_init(&ns->addr, &servers[i], 
-				     (pj_uint16_t)(ports ? ports[i] : PORT));
+	status = pj_sockaddr_init(pj_AF_INET(), &ns->addr, &servers[i], 
+				  (pj_uint16_t)(ports ? ports[i] : PORT));
+	if (status != PJ_SUCCESS)
+	    status = pj_sockaddr_init(pj_AF_INET6(), &ns->addr, &servers[i], 
+				      (pj_uint16_t)(ports ? ports[i] : PORT));
 	if (status != PJ_SUCCESS) {
-	    pj_mutex_unlock(resolver->mutex);
+	    pj_grp_lock_release(resolver->grp_lock);
 	    return PJLIB_UTIL_EDNSINNSADDR;
 	}
@@ -489,5 +576,5 @@
     resolver->ns_count = count;
 
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
     return PJ_SUCCESS;
 }
@@ -503,7 +590,7 @@
     PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL);
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
     pj_memcpy(&resolver->settings, st, sizeof(*st));
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
     return PJ_SUCCESS;
 }
@@ -518,7 +605,7 @@
     PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL);
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
     pj_memcpy(st, &resolver->settings, sizeof(*st));
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
     return PJ_SUCCESS;
 }
@@ -533,7 +620,7 @@
     PJ_ASSERT_ON_FAIL(resolver, return);
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
     pj_timer_heap_poll(resolver->timer, NULL);
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 
     pj_ioqueue_poll(resolver->ioqueue, timeout);
@@ -580,5 +667,5 @@
 {
     unsigned pkt_size;
-    unsigned i, server_cnt;
+    unsigned i, server_cnt, send_cnt;
     unsigned servers[PJ_DNS_RESOLVER_MAX_NS];
     pj_time_val now;
@@ -607,5 +694,8 @@
     delay.msec = resolver->settings.qretr_delay;
     pj_time_val_normalize(&delay);
-    status = pj_timer_heap_schedule(resolver->timer, &q->timer_entry, &delay);
+    status = pj_timer_heap_schedule_w_grp_lock(resolver->timer,
+					       &q->timer_entry,
+					       &delay, 1,
+					       resolver->grp_lock);
     if (status != PJ_SUCCESS) {
 	return status;
@@ -613,5 +703,12 @@
 
     /* Check if the socket is available for sending */
-    if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)) {
+    if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)
+#if PJ_HAS_IPV6
+	|| (resolver->udp6_key &&
+	    pj_ioqueue_is_pending(resolver->udp6_key,
+				  &resolver->udp6_op_tx_key))
+#endif
+	)
+    {
 	++q->transmit_cnt;
 	PJ_LOG(4,(resolver->name.ptr,
@@ -638,13 +735,33 @@
 
     /* Send the packet to name servers */
+    send_cnt = 0;
     for (i=0; i<server_cnt; ++i) {
+        char addr[PJ_INET6_ADDRSTRLEN];
 	pj_ssize_t sent  = (pj_ssize_t) pkt_size;
 	struct nameserver *ns = &resolver->ns[servers[i]];
 
-	status = pj_ioqueue_sendto(resolver->udp_key,
-				   &resolver->udp_op_tx_key,
-				   resolver->udp_tx_pkt, &sent, 0,
-				   &resolver->ns[servers[i]].addr,
-				   sizeof(pj_sockaddr_in));
+	if (ns->addr.addr.sa_family == pj_AF_INET()) {
+	    status = pj_ioqueue_sendto(resolver->udp_key,
+				       &resolver->udp_op_tx_key,
+				       resolver->udp_tx_pkt, &sent, 0,
+				       &ns->addr,
+				       pj_sockaddr_get_len(&ns->addr));
+	    if (status == PJ_SUCCESS || status == PJ_EPENDING)
+		send_cnt++;
+	}
+#if PJ_HAS_IPV6
+	else if (resolver->udp6_key) {
+	    status = pj_ioqueue_sendto(resolver->udp6_key,
+				       &resolver->udp6_op_tx_key,
+				       resolver->udp_tx_pkt, &sent, 0,
+				       &ns->addr,
+				       pj_sockaddr_get_len(&ns->addr));
+	    if (status == PJ_SUCCESS || status == PJ_EPENDING)
+		send_cnt++;
+	}
+#endif
+	else {
+	    continue;
+	}
 
 	PJ_PERROR(4,(resolver->name.ptr, status,
@@ -652,6 +769,6 @@
 		  (q->transmit_cnt==0? "Transmitting":"Re-transmitting"),
 		  (int)pkt_size, servers[i],
-		  pj_inet_ntoa(ns->addr.sin_addr), 
-		  (int)pj_ntohs(ns->addr.sin_port),
+		  pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
+		  pj_sockaddr_get_port(&ns->addr),
 		  pj_dns_get_type_name(q->key.qtype), 
 		  q->key.name));
@@ -661,4 +778,9 @@
 	    ns->sent_time = now;
 	}
+    }
+
+    if (send_cnt == 0) {
+        pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
+	return PJLIB_UTIL_EDNSNOWORKINGNS;
     }
 
@@ -747,5 +869,5 @@
     struct res_key key;
     struct cached_res *cache;
-    pj_dns_async_query *q;
+    pj_dns_async_query *q, *p_q = NULL;
     pj_uint32_t hval;
     pj_status_t status = PJ_SUCCESS;
@@ -761,12 +883,9 @@
     PJ_ASSERT_RETURN(type > 0 && type < 0xFFFF, PJ_EINVAL);
 
-    if (p_query)
-	*p_query = NULL;
-
     /* Build resource key for looking up hash tables */
     init_res_key(&key, type, name);
 
     /* Start working with the resolver */
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     /* Get current time. */
@@ -801,5 +920,5 @@
 	     */
 	    cache->ref_cnt++;
-	    pj_mutex_unlock(resolver->mutex);
+	    pj_grp_lock_release(resolver->grp_lock);
 
 	    /* This cached response is still valid. Just return this
@@ -811,5 +930,5 @@
 
 	    /* Done. No host resolution is necessary */
-	    pj_mutex_lock(resolver->mutex);
+	    pj_grp_lock_acquire(resolver->grp_lock);
 
 	    /* Decrement the ref counter. Also check if it is time to free
@@ -823,5 +942,11 @@
 	    status = PJ_SUCCESS;
 
-	    goto on_return;
+	    /*
+	     * We cannot write to *p_query after calling cb because what
+	     * p_query points to may have been freed by cb.
+             * Refer to ticket #1974.
+	     */
+	    pj_grp_lock_release(resolver->grp_lock);
+	    return status;
 	}
 
@@ -855,4 +980,5 @@
 	 * query completes.
 	 */
+	p_q = nq;
 	status = PJ_SUCCESS;
 	goto on_return;
@@ -882,9 +1008,11 @@
 		   0, q->hbufkey, q);
 
+    p_q = q;
+
+on_return:
     if (p_query)
-	*p_query = q;
-
-on_return:
-    pj_mutex_unlock(resolver->mutex);
+	*p_query = p_q;
+
+    pj_grp_lock_release(resolver->grp_lock);
     return status;
 }
@@ -901,5 +1029,10 @@
     PJ_ASSERT_RETURN(query, PJ_EINVAL);
 
-    pj_mutex_lock(query->resolver->mutex);
+    pj_grp_lock_acquire(query->resolver->grp_lock);
+
+    if (query->timer_entry.id == 1) {
+	pj_timer_heap_cancel_if_active(query->resolver->timer,
+				       &query->timer_entry, 0);
+    }
 
     cb = query->cb;
@@ -909,5 +1042,5 @@
 	(*cb)(query->user_data, PJ_ECANCELLED, NULL);
 
-    pj_mutex_unlock(query->resolver->mutex);
+    pj_grp_lock_release(query->resolver->grp_lock);
     return PJ_SUCCESS;
 }
@@ -1027,4 +1160,130 @@
 
 
+/* 
+ * DNS response containing A and/or AAAA packet. 
+ */
+PJ_DEF(pj_status_t) pj_dns_parse_addr_response(
+					    const pj_dns_parsed_packet *pkt,
+					    pj_dns_addr_record *rec)
+{
+    enum { MAX_SEARCH = 20 };
+    pj_str_t hostname, alias = {NULL, 0}, *resname;
+    pj_size_t bufstart = 0;
+    pj_size_t bufleft;
+    unsigned i, ansidx, cnt=0;
+
+    PJ_ASSERT_RETURN(pkt && rec, PJ_EINVAL);
+
+    /* Init the record */
+    pj_bzero(rec, sizeof(*rec));
+
+    bufleft = sizeof(rec->buf_);
+
+    /* Return error if there's error in the packet. */
+    if (PJ_DNS_GET_RCODE(pkt->hdr.flags))
+	return PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(pkt->hdr.flags));
+
+    /* Return error if there's no query section */
+    if (pkt->hdr.qdcount == 0)
+	return PJLIB_UTIL_EDNSINANSWER;
+
+    /* Return error if there's no answer */
+    if (pkt->hdr.anscount == 0)
+	return PJLIB_UTIL_EDNSNOANSWERREC;
+
+    /* Get the hostname from the query. */
+    hostname = pkt->q[0].name;
+
+    /* Copy hostname to the record */
+    if (hostname.slen > (int)bufleft) {
+	return PJ_ENAMETOOLONG;
+    }
+
+    pj_memcpy(&rec->buf_[bufstart], hostname.ptr, hostname.slen);
+    rec->name.ptr = &rec->buf_[bufstart];
+    rec->name.slen = hostname.slen;
+
+    bufstart += hostname.slen;
+    bufleft -= hostname.slen;
+
+    /* Find the first RR which name matches the hostname. */
+    for (ansidx=0; ansidx < pkt->hdr.anscount; ++ansidx) {
+	if (pj_stricmp(&pkt->ans[ansidx].name, &hostname)==0)
+	    break;
+    }
+
+    if (ansidx == pkt->hdr.anscount)
+	return PJLIB_UTIL_EDNSNOANSWERREC;
+
+    resname = &hostname;
+
+    /* Keep following CNAME records. */
+    while (pkt->ans[ansidx].type == PJ_DNS_TYPE_CNAME &&
+	   cnt++ < MAX_SEARCH)
+    {
+	resname = &pkt->ans[ansidx].rdata.cname.name;
+
+	if (!alias.slen)
+	    alias = *resname;
+
+	for (i=0; i < pkt->hdr.anscount; ++i) {
+	    if (pj_stricmp(resname, &pkt->ans[i].name)==0)
+		break;
+	}
+
+	if (i==pkt->hdr.anscount)
+	    return PJLIB_UTIL_EDNSNOANSWERREC;
+
+	ansidx = i;
+    }
+
+    if (cnt >= MAX_SEARCH)
+	return PJLIB_UTIL_EDNSINANSWER;
+
+    if (pkt->ans[ansidx].type != PJ_DNS_TYPE_A &&
+	pkt->ans[ansidx].type != PJ_DNS_TYPE_AAAA)
+    {
+	return PJLIB_UTIL_EDNSINANSWER;
+    }
+
+    /* Copy alias to the record, if present. */
+    if (alias.slen) {
+	if (alias.slen > (int)bufleft)
+	    return PJ_ENAMETOOLONG;
+
+	pj_memcpy(&rec->buf_[bufstart], alias.ptr, alias.slen);
+	rec->alias.ptr = &rec->buf_[bufstart];
+	rec->alias.slen = alias.slen;
+
+	bufstart += alias.slen;
+	bufleft -= alias.slen;
+    }
+
+    /* Get the IP addresses. */
+    cnt = 0;
+    for (i=0; i < pkt->hdr.anscount && cnt < PJ_DNS_MAX_IP_IN_A_REC ; ++i) {
+	if ((pkt->ans[i].type == PJ_DNS_TYPE_A ||
+	     pkt->ans[i].type == PJ_DNS_TYPE_AAAA) &&
+	    pj_stricmp(&pkt->ans[i].name, resname)==0)
+	{
+	    if (pkt->ans[i].type == PJ_DNS_TYPE_A) {
+		rec->addr[cnt].af = pj_AF_INET();
+		rec->addr[cnt].ip.v4 = pkt->ans[i].rdata.a.ip_addr;
+	    } else {
+		rec->addr[cnt].af = pj_AF_INET6();
+		rec->addr[cnt].ip.v6 = pkt->ans[i].rdata.aaaa.ip_addr;
+	    }
+	    ++cnt;
+	}
+    }
+    rec->addr_count = cnt;
+
+    if (cnt == 0)
+	return PJLIB_UTIL_EDNSNOANSWERREC;
+
+    return PJ_SUCCESS;
+}
+
+
 /* Set nameserver state */
 static void set_nameserver_state(pj_dns_resolver *resolver,
@@ -1035,4 +1294,5 @@
     struct nameserver *ns = &resolver->ns[index];
     enum ns_state old_state = ns->state;
+    char addr[PJ_INET6_ADDRSTRLEN];
 
     ns->state = state;
@@ -1048,6 +1308,6 @@
 
     PJ_LOG(5, (resolver->name.ptr, "Nameserver %s:%d state changed %s --> %s",
-	       pj_inet_ntoa(ns->addr.sin_addr),
-	       (int)pj_ntohs(ns->addr.sin_port),
+	       pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
+	       pj_sockaddr_get_port(&ns->addr),
 	       state_names[old_state], state_names[state]));
 }
@@ -1128,5 +1388,5 @@
 /* Update name server status */
 static void report_nameserver_status(pj_dns_resolver *resolver,
-				     const pj_sockaddr_in *ns_addr,
+				     const pj_sockaddr *ns_addr,
 				     const pj_dns_parsed_packet *pkt)
 {
@@ -1148,5 +1408,10 @@
     }
 
-    if (!pkt || rcode == PJ_DNS_RCODE_SERVFAIL ||
+    /* Some nameserver is reported to respond with PJ_DNS_RCODE_SERVFAIL for
+     * missing AAAA record, and the standard doesn't seem to specify that
+     * SERVFAIL should prevent the server to be contacted again for other
+     * queries. So let's not mark nameserver as bad for SERVFAIL response.
+     */
+    if (!pkt || /* rcode == PJ_DNS_RCODE_SERVFAIL || */
 	        rcode == PJ_DNS_RCODE_REFUSED ||
 	        rcode == PJ_DNS_RCODE_NOTAUTH) 
@@ -1165,8 +1430,5 @@
 	struct nameserver *ns = &resolver->ns[i];
 
-	if (ns->addr.sin_addr.s_addr == ns_addr->sin_addr.s_addr &&
-	    ns->addr.sin_port == ns_addr->sin_port &&
-	    ns->addr.sin_family == ns_addr->sin_family)
-	{
+	if (pj_sockaddr_cmp(&ns->addr, ns_addr) == 0) {
 	    if (q_id == ns->q_id) {
 		/* Calculate response time */
@@ -1233,8 +1495,10 @@
 	ttl = resolver->settings.cache_max_ttl;
 
+    /* Get a cache response entry */
+    cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key,
+    					      sizeof(*key), &hval);
+
     /* If TTL is zero, clear the same entry in the hash table */
     if (ttl == 0) {
-	cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
-						  sizeof(*key), &hval);
 	/* Remove the entry before releasing its pool (see ticket #1710) */
 	pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
@@ -1246,15 +1510,5 @@
     }
 
-    /* Get a cache response entry */
-    cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, 
-    					      sizeof(*key), &hval);
     if (cache == NULL) {
-	cache = alloc_entry(resolver);
-    } else if (cache->ref_cnt > 1) {
-	/* When cache entry is being used by callback (to app), just decrement
-	 * ref_cnt so it will be freed after the callback returns and allocate
-	 * new entry.
-	 */
-	cache->ref_cnt--;
 	cache = alloc_entry(resolver);
     } else {
@@ -1262,6 +1516,15 @@
 	pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
 
-	/* Reset cache to avoid bloated cache pool */
-	reset_entry(&cache);
+	if (cache->ref_cnt > 1) {
+	    /* When cache entry is being used by callback (to app),
+	     * just decrement ref_cnt so it will be freed after
+	     * the callback returns and allocate new entry.
+	     */
+	    cache->ref_cnt--;
+	    cache = alloc_entry(resolver);
+	} else {
+	    /* Reset cache to avoid bloated cache pool */
+	    reset_entry(&cache);
+	}
     }
 
@@ -1308,5 +1571,5 @@
     resolver = q->resolver;
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     /* Recheck that this query is still pending, since there is a slight
@@ -1316,5 +1579,5 @@
     if (pj_hash_get(resolver->hquerybyid, &q->id, sizeof(q->id), NULL)==NULL) {
 	/* Yeah, this query is done. */
-	pj_mutex_unlock(resolver->mutex);
+	pj_grp_lock_release(resolver->grp_lock);
 	return;
     }
@@ -1327,5 +1590,5 @@
 	status = transmit_query(resolver, q);
 	if (status == PJ_SUCCESS) {
-	    pj_mutex_unlock(resolver->mutex);
+	    pj_grp_lock_release(resolver->grp_lock);
 	    return;
 	} else {
@@ -1346,5 +1609,5 @@
 
     /* Workaround for deadlock problem in #1565 (similar to #1108) */
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 
     /* Call application callback, if any. */
@@ -1361,5 +1624,5 @@
 
     /* Workaround for deadlock problem in #1565 (similar to #1108) */
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     /* Clear data */
@@ -1378,5 +1641,5 @@
     pj_list_push_back(&resolver->query_free_nodes, q);
 
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 }
 
@@ -1391,4 +1654,9 @@
     pj_dns_parsed_packet *dns_pkt;
     pj_dns_async_query *q;
+    char addr[PJ_INET6_ADDRSTRLEN];
+    pj_sockaddr *src_addr;
+    int *src_addr_len;
+    unsigned char *rx_pkt;
+    pj_ssize_t rx_pkt_size;
     pj_status_t status;
     PJ_USE_EXCEPTION;
@@ -1396,5 +1664,22 @@
 
     resolver = (pj_dns_resolver *) pj_ioqueue_get_user_data(key);
-    pj_mutex_lock(resolver->mutex);
+    pj_assert(resolver);
+
+#if PJ_HAS_IPV6
+    if (key == resolver->udp6_key) {
+	src_addr = &resolver->udp6_src_addr;
+	src_addr_len = &resolver->udp6_addr_len;
+	rx_pkt = resolver->udp6_rx_pkt;
+	rx_pkt_size = sizeof(resolver->udp6_rx_pkt);
+    } else 
+#endif
+    {
+	src_addr = &resolver->udp_src_addr;
+	src_addr_len = &resolver->udp_addr_len;
+	rx_pkt = resolver->udp_rx_pkt;
+	rx_pkt_size = sizeof(resolver->udp_rx_pkt);
+    }
+
+    pj_grp_lock_acquire(resolver->grp_lock);
 
 
@@ -1405,9 +1690,5 @@
 	status = (pj_status_t)-bytes_read;
 	pj_strerror(status, errmsg, sizeof(errmsg));
-	PJ_LOG(4,(resolver->name.ptr, 
-		  "DNS resolver read error from %s:%d: %s", 
-		  pj_inet_ntoa(resolver->udp_src_addr.sin_addr),
-		  pj_ntohs(resolver->udp_src_addr.sin_port),
-		  errmsg));
+	PJ_LOG(4,(resolver->name.ptr, "DNS resolver read error: %s", errmsg));
 
 	goto read_next_packet;
@@ -1417,6 +1698,6 @@
 	      "Received %d bytes DNS response from %s:%d",
 	      (int)bytes_read, 
-	      pj_inet_ntoa(resolver->udp_src_addr.sin_addr),
-	      pj_ntohs(resolver->udp_src_addr.sin_port)));
+	      pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+	      pj_sockaddr_get_port(src_addr)));
 
 
@@ -1433,5 +1714,5 @@
     dns_pkt = NULL;
     PJ_TRY {
-	status = pj_dns_parse_packet(pool, resolver->udp_rx_pkt, 
+	status = pj_dns_parse_packet(pool, rx_pkt, 
 				     (unsigned)bytes_read, &dns_pkt);
     }
@@ -1442,5 +1723,5 @@
 
     /* Update nameserver status */
-    report_nameserver_status(resolver, &resolver->udp_src_addr, dns_pkt);
+    report_nameserver_status(resolver, src_addr, dns_pkt);
 
     /* Handle parse error */
@@ -1451,6 +1732,6 @@
 	PJ_LOG(3,(resolver->name.ptr, 
 		  "Error parsing DNS response from %s:%d: %s", 
-		  pj_inet_ntoa(resolver->udp_src_addr.sin_addr), 
-		  pj_ntohs(resolver->udp_src_addr.sin_port), 
+		  pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+		  pj_sockaddr_get_port(src_addr),
 		  errmsg));
 	goto read_next_packet;
@@ -1464,6 +1745,6 @@
 	PJ_LOG(5,(resolver->name.ptr, 
 		  "DNS response from %s:%d id=%d discarded",
-		  pj_inet_ntoa(resolver->udp_src_addr.sin_addr), 
-		  pj_ntohs(resolver->udp_src_addr.sin_port),
+		  pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+		  pj_sockaddr_get_port(src_addr),
 		  (unsigned)dns_pkt->hdr.id));
 	goto read_next_packet;
@@ -1483,5 +1764,5 @@
 
     /* Workaround for deadlock problem in #1108 */
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 
     /* Notify applications first, to allow application to modify the 
@@ -1504,5 +1785,5 @@
 
     /* Workaround for deadlock problem in #1108 */
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     /* Save/update response cache. */
@@ -1528,12 +1809,10 @@
 	pj_pool_release(pool);
     }
-    bytes_read = sizeof(resolver->udp_rx_pkt);
-    resolver->udp_addr_len = sizeof(resolver->udp_src_addr);
-    status = pj_ioqueue_recvfrom(resolver->udp_key, op_key, 
-				 resolver->udp_rx_pkt,
-				 &bytes_read, PJ_IOQUEUE_ALWAYS_ASYNC,
-				 &resolver->udp_src_addr, 
-				 &resolver->udp_addr_len);
-    if (status != PJ_EPENDING) {
+
+    status = pj_ioqueue_recvfrom(key, op_key, rx_pkt, &rx_pkt_size,
+				 PJ_IOQUEUE_ALWAYS_ASYNC,
+				 src_addr, src_addr_len);
+
+    if (status != PJ_EPENDING && status != PJ_ECANCELLED) {
 	char errmsg[PJ_ERR_MSG_SIZE];
 
@@ -1545,5 +1824,5 @@
     }
 
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 }
 
@@ -1571,5 +1850,5 @@
 		     PJLIB_UTIL_EDNSNOANSWERREC);
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     /* Build resource key for looking up hash tables */
@@ -1593,5 +1872,5 @@
     update_res_cache(resolver, &key, PJ_SUCCESS, set_ttl, pkt);
 
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 
     return PJ_SUCCESS;
@@ -1608,7 +1887,7 @@
     PJ_ASSERT_RETURN(resolver, 0);
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
     count = pj_hash_count(resolver->hrescache);
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 
     return count;
@@ -1626,5 +1905,5 @@
     pj_time_val now;
 
-    pj_mutex_lock(resolver->mutex);
+    pj_grp_lock_acquire(resolver->grp_lock);
 
     pj_gettimeofday(&now);
@@ -1634,11 +1913,12 @@
     PJ_LOG(3,(resolver->name.ptr, "  Name servers:"));
     for (i=0; i<resolver->ns_count; ++i) {
-	const char *state_names[] = { "probing", "active", "bad"};
+	char addr[PJ_INET6_ADDRSTRLEN];
 	struct nameserver *ns = &resolver->ns[i];
 
 	PJ_LOG(3,(resolver->name.ptr,
 		  "   NS %d: %s:%d (state=%s until %ds, rtt=%d ms)",
-		  i, pj_inet_ntoa(ns->addr.sin_addr),
-		  pj_ntohs(ns->addr.sin_port),
+		  i,
+		  pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
+		  pj_sockaddr_get_port(&ns->addr),
 		  state_names[ns->state],
 		  ns->state_expiry.sec - now.sec,
@@ -1685,5 +1965,5 @@
 	      pj_pool_get_used_size(resolver->pool)));
 
-    pj_mutex_unlock(resolver->mutex);
+    pj_grp_lock_release(resolver->grp_lock);
 #endif
 }
