Index: /pjproject/trunk/pjlib-util/include/pjlib-util/resolver.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/resolver.h	(revision 5348)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/resolver.h	(revision 5349)
@@ -224,4 +224,45 @@
 
 } pj_dns_a_record;
+
+
+/**
+ * This structure represents DNS address record, i.e: DNS A and DNS AAAA
+ * records, as the result of parsing DNS response packet using
+ * #pj_dns_parse_addr_response().
+ */
+typedef struct pj_dns_addr_record
+{
+    /** The target name being queried.   */
+    pj_str_t		name;
+
+    /** If target name corresponds to a CNAME entry, the alias contains
+     *  the value of the CNAME entry, otherwise it will be empty.
+     */
+    pj_str_t		alias;
+
+    /** Number of IP addresses. */
+    unsigned		addr_count;
+
+    /** IP addresses of the host found in the response */
+    struct {
+
+	/** IP address family */
+	int		af;
+	
+	/** IP address */
+	union {
+	    /** IPv4 address */
+	    pj_in_addr	v4;
+
+	    /** IPv6 address */
+	    pj_in6_addr	v6;
+	} ip;
+
+    } addr[PJ_DNS_MAX_IP_IN_A_REC];
+
+    /** Internal buffer for hostname and alias. */
+    char		buf_[128];
+
+} pj_dns_addr_record;
 
 
@@ -409,4 +450,19 @@
 
 /**
+ * A utility function to parse a DNS response containing AAAA records into 
+ * DNS AAAA record.
+ *
+ * @param pkt	    The DNS response packet.
+ * @param rec	    The structure to be initialized with the parsed
+ *		    DNS AAAA record from the packet.
+ *
+ * @return	    PJ_SUCCESS if response can be parsed successfully.
+ */
+PJ_DECL(pj_status_t) pj_dns_parse_addr_response(
+					    const pj_dns_parsed_packet *pkt,
+					    pj_dns_addr_record *rec);
+
+
+/**
  * Put the specified DNS packet into DNS cache. This function is mainly used
  * for testing the resolver, however it can also be used to inject entries
Index: /pjproject/trunk/pjlib-util/include/pjlib-util/srv_resolver.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/srv_resolver.h	(revision 5348)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/srv_resolver.h	(revision 5349)
@@ -85,6 +85,6 @@
      * resolution when the SRV resolution fails. This option may
      * be specified together with PJ_DNS_SRV_FALLBACK_AAAA to
-     * make the resolver fallback to AAAA if SRV resolution fails,
-     * and then to DNS A resolution if the AAAA resolution fails.
+     * make the resolver fallback to both DNS A and DNS AAAA
+     * resolutions if SRV resolution fails.
      */
     PJ_DNS_SRV_FALLBACK_A	= 1,
@@ -93,7 +93,7 @@
      * Specify if the resolver should fallback with DNS AAAA
      * resolution when the SRV resolution fails. This option may
-     * be specified together with PJ_DNS_SRV_FALLBACK_A to
-     * make the resolver fallback to AAAA if SRV resolution fails,
-     * and then to DNS A resolution if the AAAA resolution fails.
+     * be specified together with PJ_DNS_SRV_FALLBACK_AAAA to
+     * make the resolver fallback to both DNS A and DNS AAAA
+     * resolutions if SRV resolution fails.
      */
     PJ_DNS_SRV_FALLBACK_AAAA	= 2,
@@ -101,9 +101,16 @@
     /**
      * Specify if the resolver should try to resolve with DNS AAAA
-     * resolution first of each targets in the DNS SRV record. If
-     * this option is not specified, the SRV resolver will query
-     * the DNS A record for the target instead.
-     */
-    PJ_DNS_SRV_RESOLVE_AAAA	= 4
+     * resolution of each targets in the DNS SRV record. If this
+     * option is not specified, the SRV resolver will query the
+     * DNS A record for the target instead.
+     */
+    PJ_DNS_SRV_RESOLVE_AAAA	= 4,
+
+    /**
+     * Specify if the resolver should try to resolve with DNS AAAA
+     * resolution only (i.e: without DNS A resolution) for each targets
+     * in the DNS SRV record.
+     */
+    PJ_DNS_SRV_RESOLVE_AAAA_ONLY = 8
 
 } pj_dns_srv_option;
@@ -132,5 +139,5 @@
 
 	/** The host address. */
-	pj_dns_a_record		server;
+	pj_dns_addr_record	server;
 
     } entry[PJ_DNS_SRV_MAX_ADDR];
Index: /pjproject/trunk/pjlib-util/src/pjlib-util-test/resolver_test.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util-test/resolver_test.c	(revision 5348)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util-test/resolver_test.c	(revision 5349)
@@ -192,4 +192,18 @@
 	size -= 6;
 
+    } else if (rr->type == PJ_DNS_TYPE_AAAA) {
+
+	if (size < 18)
+	    return -1;
+
+	/* RDLEN is 16 */
+	write16(p, 16);
+
+	/* Address */
+	pj_memcpy(p+2, &rr->rdata.aaaa.ip_addr, 16);
+
+	p += 18;
+	size -= 18;
+
     } else if (rr->type == PJ_DNS_TYPE_CNAME ||
 	       rr->type == PJ_DNS_TYPE_NS ||
@@ -331,5 +345,5 @@
 	pj_fd_set_t rset;
 	pj_time_val timeout = {0, 500};
-	pj_sockaddr_in src_addr;
+	pj_sockaddr src_addr;
 	pj_dns_parsed_packet *req;
 	char pkt[1024];
@@ -406,5 +420,5 @@
 static void destroy(void);
 
-static int init(void)
+static int init(pj_bool_t use_ipv6)
 {
     pj_status_t status;
@@ -413,7 +427,12 @@
     int i;
 
-    nameservers[0] = pj_str("127.0.0.1");
+    if (use_ipv6) {
+	nameservers[0] = pj_str("::1");
+	nameservers[1] = pj_str("::1");
+    } else {
+	nameservers[0] = pj_str("127.0.0.1");
+	nameservers[1] = pj_str("127.0.0.1");
+    }
     ports[0] = 5553;
-    nameservers[1] = pj_str("127.0.0.1");
     ports[1] = 5554;
 
@@ -426,12 +445,16 @@
     pj_assert(status == PJ_SUCCESS);
 
+    thread_quit = PJ_FALSE;
+
     for (i=0; i<2; ++i) {
-	pj_sockaddr_in addr;
-
-	status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g_server[i].sock);
+	pj_sockaddr addr;
+
+	status = pj_sock_socket((use_ipv6? pj_AF_INET6() : pj_AF_INET()),
+				pj_SOCK_DGRAM(), 0, &g_server[i].sock);
 	if (status != PJ_SUCCESS)
 	    return -10;
 
-	pj_sockaddr_in_init(&addr, NULL, (pj_uint16_t)g_server[i].port);
+	pj_sockaddr_init((use_ipv6? pj_AF_INET6() : pj_AF_INET()),
+			 &addr, NULL, (pj_uint16_t)g_server[i].port);
 
 	status = pj_sock_bind(g_server[i].sock, &addr, sizeof(addr));
@@ -687,4 +710,213 @@
 
     rc = pj_dns_parse_a_response(&pkt, &rec);
+    pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+    return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* DNS A/AAAA parser tests */
+static int addr_parser_test(void)
+{
+    pj_dns_parsed_packet pkt;
+    pj_dns_addr_record rec;
+    pj_status_t rc;
+
+    PJ_LOG(3,(THIS_FILE, "  DNS A/AAAA record parser tests"));
+
+    pkt.q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+    pkt.ans = (pj_dns_parsed_rr*)
+	      pj_pool_calloc(pool, 32, sizeof(pj_dns_parsed_rr));
+
+    /* Simple answer with direct A record, but with addition of
+     * a CNAME and another A to confuse the parser.
+     */
+    PJ_LOG(3,(THIS_FILE, "    A RR with duplicate CNAME/A"));
+    pkt.hdr.flags = 0;
+    pkt.hdr.qdcount = 1;
+    pkt.q[0].type = PJ_DNS_TYPE_A;
+    pkt.q[0].dnsclass = 1;
+    pkt.q[0].name = pj_str("ahost");
+    pkt.hdr.anscount = 4;
+
+    /* This is the RR corresponding to the query */
+    pkt.ans[0].name = pj_str("ahost");
+    pkt.ans[0].type = PJ_DNS_TYPE_A;
+    pkt.ans[0].dnsclass = 1;
+    pkt.ans[0].ttl = 1;
+    pkt.ans[0].rdata.a.ip_addr.s_addr = 0x01020304;
+
+    /* CNAME to confuse the parser */
+    pkt.ans[1].name = pj_str("ahost");
+    pkt.ans[1].type = PJ_DNS_TYPE_CNAME;
+    pkt.ans[1].dnsclass = 1;
+    pkt.ans[1].ttl = 1;
+    pkt.ans[1].rdata.cname.name = pj_str("bhost");
+
+    /* DNS A RR to confuse the parser */
+    pkt.ans[2].name = pj_str("bhost");
+    pkt.ans[2].type = PJ_DNS_TYPE_A;
+    pkt.ans[2].dnsclass = 1;
+    pkt.ans[2].ttl = 1;
+    pkt.ans[2].rdata.a.ip_addr.s_addr = 0x0203;
+
+    /* Additional RR corresponding to the query, DNS AAAA RR */
+    pkt.ans[3].name = pj_str("ahost");
+    pkt.ans[3].type = PJ_DNS_TYPE_AAAA;
+    pkt.ans[3].dnsclass = 1;
+    pkt.ans[3].ttl = 1;
+    pkt.ans[3].rdata.aaaa.ip_addr.u6_addr32[0] = 0x01020304;
+
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
+    pj_assert(rc == PJ_SUCCESS);
+    pj_assert(pj_strcmp2(&rec.name, "ahost")==0);
+    pj_assert(rec.alias.slen == 0);
+    pj_assert(rec.addr_count == 2);
+    pj_assert(rec.addr[0].af==pj_AF_INET() && rec.addr[0].ip.v4.s_addr == 0x01020304);
+    pj_assert(rec.addr[1].af==pj_AF_INET6() && rec.addr[1].ip.v6.u6_addr32[0] == 0x01020304);
+
+    /* Answer with the target corresponds to a CNAME entry, but not
+     * as the first record, and with additions of some CNAME and A
+     * entries to confuse the parser.
+     */
+    PJ_LOG(3,(THIS_FILE, "    CNAME RR with duplicate CNAME/A"));
+    pkt.hdr.flags = 0;
+    pkt.hdr.qdcount = 1;
+    pkt.q[0].type = PJ_DNS_TYPE_A;
+    pkt.q[0].dnsclass = 1;
+    pkt.q[0].name = pj_str("ahost");
+    pkt.hdr.anscount = 4;
+
+    /* This is the DNS A record for the alias */
+    pkt.ans[0].name = pj_str("ahostalias");
+    pkt.ans[0].type = PJ_DNS_TYPE_A;
+    pkt.ans[0].dnsclass = 1;
+    pkt.ans[0].ttl = 1;
+    pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202;
+
+    /* CNAME entry corresponding to the query */
+    pkt.ans[1].name = pj_str("ahost");
+    pkt.ans[1].type = PJ_DNS_TYPE_CNAME;
+    pkt.ans[1].dnsclass = 1;
+    pkt.ans[1].ttl = 1;
+    pkt.ans[1].rdata.cname.name = pj_str("ahostalias");
+
+    /* Another CNAME to confuse the parser */
+    pkt.ans[2].name = pj_str("ahost");
+    pkt.ans[2].type = PJ_DNS_TYPE_CNAME;
+    pkt.ans[2].dnsclass = 1;
+    pkt.ans[2].ttl = 1;
+    pkt.ans[2].rdata.cname.name = pj_str("ahostalias2");
+
+    /* Another DNS A to confuse the parser */
+    pkt.ans[3].name = pj_str("ahostalias2");
+    pkt.ans[3].type = PJ_DNS_TYPE_A;
+    pkt.ans[3].dnsclass = 1;
+    pkt.ans[3].ttl = 1;
+    pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303;
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
+    pj_assert(rc == PJ_SUCCESS);
+    pj_assert(pj_strcmp2(&rec.name, "ahost")==0);
+    pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0);
+    pj_assert(rec.addr_count == 1);
+    pj_assert(rec.addr[0].ip.v4.s_addr == 0x02020202);
+
+    /*
+     * No query section.
+     */
+    PJ_LOG(3,(THIS_FILE, "    No query section"));
+    pkt.hdr.qdcount = 0;
+    pkt.hdr.anscount = 0;
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
+    pj_assert(rc == PJLIB_UTIL_EDNSINANSWER);
+
+    /*
+     * No answer section.
+     */
+    PJ_LOG(3,(THIS_FILE, "    No answer section"));
+    pkt.hdr.flags = 0;
+    pkt.hdr.qdcount = 1;
+    pkt.q[0].type = PJ_DNS_TYPE_A;
+    pkt.q[0].dnsclass = 1;
+    pkt.q[0].name = pj_str("ahost");
+    pkt.hdr.anscount = 0;
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
+    pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+    /*
+     * Answer doesn't match query.
+     */
+    PJ_LOG(3,(THIS_FILE, "    Answer doesn't match query"));
+    pkt.hdr.flags = 0;
+    pkt.hdr.qdcount = 1;
+    pkt.q[0].type = PJ_DNS_TYPE_A;
+    pkt.q[0].dnsclass = 1;
+    pkt.q[0].name = pj_str("ahost");
+    pkt.hdr.anscount = 1;
+
+    /* An answer that doesn't match the query */
+    pkt.ans[0].name = pj_str("ahostalias");
+    pkt.ans[0].type = PJ_DNS_TYPE_A;
+    pkt.ans[0].dnsclass = 1;
+    pkt.ans[0].ttl = 1;
+    pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202;
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
+    pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+
+    /*
+     * DNS CNAME that doesn't have corresponding DNS A.
+     */
+    PJ_LOG(3,(THIS_FILE, "    CNAME with no matching DNS A RR (1)"));
+    pkt.hdr.flags = 0;
+    pkt.hdr.qdcount = 1;
+    pkt.q[0].type = PJ_DNS_TYPE_A;
+    pkt.q[0].dnsclass = 1;
+    pkt.q[0].name = pj_str("ahost");
+    pkt.hdr.anscount = 1;
+
+    /* The CNAME */
+    pkt.ans[0].name = pj_str("ahost");
+    pkt.ans[0].type = PJ_DNS_TYPE_CNAME;
+    pkt.ans[0].dnsclass = 1;
+    pkt.ans[0].ttl = 1;
+    pkt.ans[0].rdata.cname.name = pj_str("ahostalias");
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
+    pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+
+    /*
+     * DNS CNAME that doesn't have corresponding DNS A.
+     */
+    PJ_LOG(3,(THIS_FILE, "    CNAME with no matching DNS A RR (2)"));
+    pkt.hdr.flags = 0;
+    pkt.hdr.qdcount = 1;
+    pkt.q[0].type = PJ_DNS_TYPE_A;
+    pkt.q[0].dnsclass = 1;
+    pkt.q[0].name = pj_str("ahost");
+    pkt.hdr.anscount = 2;
+
+    /* The CNAME */
+    pkt.ans[0].name = pj_str("ahost");
+    pkt.ans[0].type = PJ_DNS_TYPE_CNAME;
+    pkt.ans[0].dnsclass = 1;
+    pkt.ans[0].ttl = 1;
+    pkt.ans[0].rdata.cname.name = pj_str("ahostalias");
+
+    /* DNS A record, but the name doesn't match */
+    pkt.ans[1].name = pj_str("ahost");
+    pkt.ans[1].type = PJ_DNS_TYPE_A;
+    pkt.ans[1].dnsclass = 1;
+    pkt.ans[1].ttl = 1;
+    pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304;
+
+    rc = pj_dns_parse_addr_response(&pkt, &rec);
     pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
 
@@ -1006,4 +1238,25 @@
 	res->ans[1].name = pj_str(alias);
 	res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR1;
+
+    } else if (pkt->q[0].type == PJ_DNS_TYPE_AAAA) {
+	char *alias = "sipalias.somedomain.com";
+
+	pj_assert(pj_strcmp2(&res->q[0].name, target)==0);
+
+	res->hdr.anscount = 2;
+	res->ans[0].type = PJ_DNS_TYPE_CNAME;
+	res->ans[0].dnsclass = 1;
+	res->ans[0].ttl = 1000;	/* resolver should select minimum TTL */
+	res->ans[0].name = res->q[0].name;
+	res->ans[0].rdata.cname.name = pj_str(alias);
+
+	res->ans[1].type = PJ_DNS_TYPE_AAAA;
+	res->ans[1].dnsclass = 1;
+	res->ans[1].ttl = 1;
+	res->ans[1].name = pj_str(alias);
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[0] = IP_ADDR1;
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[1] = IP_ADDR1;
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[2] = IP_ADDR1;
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[3] = IP_ADDR1;
     }
 
@@ -1027,9 +1280,13 @@
     PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias.somedomain.com")==0,
 		      return);
-    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].s_addr == IP_ADDR1, return);
+
+    /* IPv4 only */
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR1, return);
     PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT1, return);
 
     
 }
+
 
 static void srv_cb_1b(void *user_data,
@@ -1045,4 +1302,59 @@
     PJ_ASSERT_ON_FAIL(rec->count == 0, return);
 }
+
+
+static void srv_cb_1c(void *user_data,
+		      pj_status_t status,
+		      const pj_dns_srv_record *rec)
+{
+    PJ_UNUSED_ARG(user_data);
+
+    pj_sem_post(sem);
+
+    PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+    PJ_ASSERT_ON_FAIL(rec->count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 2, return);
+
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, "sip.somedomain.com")==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias.somedomain.com")==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT1, return);
+
+    /* IPv4 and IPv6 */
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 2, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET() &&
+		      rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[1].af == pj_AF_INET6() &&
+		      rec->entry[0].server.addr[1].ip.v6.u6_addr32[0] == IP_ADDR1, return);
+}
+
+
+static void srv_cb_1d(void *user_data,
+		      pj_status_t status,
+		      const pj_dns_srv_record *rec)
+{
+    PJ_UNUSED_ARG(user_data);
+
+    pj_sem_post(sem);
+
+    PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+    PJ_ASSERT_ON_FAIL(rec->count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 2, return);
+
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, "sip.somedomain.com")==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias.somedomain.com")==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT1, return);
+
+    /* IPv6 only */
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET6() &&
+		      rec->entry[0].server.addr[0].ip.v6.u6_addr32[0] == IP_ADDR1, return);
+}
+
 
 static int srv_resolver_test(void)
@@ -1079,4 +1391,46 @@
 		    ((set.qretr_count + 2) * set.qretr_delay));
 
+
+    /* DNS SRV option PJ_DNS_SRV_RESOLVE_AAAA */
+    PJ_LOG(3,(THIS_FILE, "  srv_resolve(): option PJ_DNS_SRV_RESOLVE_AAAA"));
+
+    g_server[0].action = ACTION_CB;
+    g_server[0].action_cb = &action1_1;
+    g_server[1].action = ACTION_CB;
+    g_server[1].action_cb = &action1_1;
+
+    g_server[0].pkt_count = 0;
+    g_server[1].pkt_count = 0;
+
+    status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver,
+				PJ_DNS_SRV_RESOLVE_AAAA,
+				NULL, &srv_cb_1c, NULL);
+    pj_assert(status == PJ_SUCCESS);
+
+    pj_sem_wait(sem);
+
+    pj_thread_sleep(1000);
+
+    /* DNS SRV option PJ_DNS_SRV_RESOLVE_AAAA_ONLY */
+    PJ_LOG(3,(THIS_FILE, "  srv_resolve(): option PJ_DNS_SRV_RESOLVE_AAAA_ONLY"));
+
+    g_server[0].action = ACTION_CB;
+    g_server[0].action_cb = &action1_1;
+    g_server[1].action = ACTION_CB;
+    g_server[1].action_cb = &action1_1;
+
+    g_server[0].pkt_count = 0;
+    g_server[1].pkt_count = 0;
+
+    status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver,
+				PJ_DNS_SRV_RESOLVE_AAAA_ONLY,
+				NULL, &srv_cb_1d, NULL);
+    pj_assert(status == PJ_SUCCESS);
+
+    pj_sem_wait(sem);
+
+    pj_thread_sleep(1000);
+
+
     /* Successful scenario */
     PJ_LOG(3,(THIS_FILE, "  srv_resolve(): parallel queries"));
@@ -1103,5 +1457,4 @@
     PJ_LOG(3,(THIS_FILE, "  srv_resolve(): cache expires scenario"));
 
-
     pj_thread_sleep(1000);
 
@@ -1114,4 +1467,5 @@
 
     pj_sem_wait(sem);
+
 
     return 0;
@@ -1172,4 +1526,25 @@
 	res->ans[1].ttl = 1;
 	res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR2;
+
+    } else if (pkt->q[0].type == PJ_DNS_TYPE_AAAA) {
+	char *alias = "sipalias01." TARGET;
+
+	pj_assert(pj_strcmp2(&res->q[0].name, TARGET)==0);
+
+	res->hdr.anscount = 2;
+	res->ans[0].type = PJ_DNS_TYPE_CNAME;
+	res->ans[0].dnsclass = 1;
+	res->ans[0].name = res->q[0].name;
+	res->ans[0].ttl = 1;
+	res->ans[0].rdata.cname.name = pj_str(alias);
+
+	res->ans[1].type = PJ_DNS_TYPE_AAAA;
+	res->ans[1].dnsclass = 1;
+	res->ans[1].ttl = 1;
+	res->ans[1].name = pj_str(alias);
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[0] = IP_ADDR2;
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[1] = IP_ADDR2;
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[2] = IP_ADDR2;
+	res->ans[1].rdata.aaaa.ip_addr.u6_addr32[3] = IP_ADDR2;
     }
 
@@ -1193,6 +1568,60 @@
     PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias01." TARGET)==0,
 		      return);
-    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].s_addr == IP_ADDR2, return);
     PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT2, return);
+
+    /* IPv4 only */
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET() &&
+		      rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR2, return);
+}
+
+static void srv_cb_2a(void *user_data,
+		      pj_status_t status,
+		      const pj_dns_srv_record *rec)
+{
+    PJ_UNUSED_ARG(user_data);
+
+    pj_sem_post(sem);
+
+    PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+    PJ_ASSERT_ON_FAIL(rec->count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 0, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 0, return);
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, TARGET)==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias01." TARGET)==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT2, return);
+
+    /* IPv4 and IPv6 */
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 2, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET() &&
+		      rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR2, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[1].af == pj_AF_INET6() &&
+		      rec->entry[0].server.addr[1].ip.v6.u6_addr32[0] == IP_ADDR2, return);
+}
+
+static void srv_cb_2b(void *user_data,
+		      pj_status_t status,
+		      const pj_dns_srv_record *rec)
+{
+    PJ_UNUSED_ARG(user_data);
+
+    pj_sem_post(sem);
+
+    PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+    PJ_ASSERT_ON_FAIL(rec->count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 0, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 0, return);
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, TARGET)==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias01." TARGET)==0,
+		      return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT2, return);
+
+    /* IPv6 only */
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+    PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET6() &&
+		      rec->entry[0].server.addr[0].ip.v6.u6_addr32[0] == IP_ADDR2, return);
 }
 
@@ -1203,4 +1632,5 @@
     pj_str_t res_name = pj_str("_sip._udp.");
 
+    /* Fallback test */
     PJ_LOG(3,(THIS_FILE, "  srv_resolve(): fallback test"));
 
@@ -1235,4 +1665,49 @@
     pj_assert(g_server[0].pkt_count == 0);
     pj_assert(g_server[1].pkt_count == 0);
+
+    /* Clear cache */
+    pj_thread_sleep(1000);
+
+    /* Fallback with PJ_DNS_SRV_FALLBACK_A and PJ_DNS_SRV_FALLBACK_AAAA */
+    PJ_LOG(3,(THIS_FILE, "  srv_resolve(): fallback to DNS A and AAAA"));
+
+    g_server[0].action = ACTION_CB;
+    g_server[0].action_cb = &action2_1;
+    g_server[1].action = ACTION_CB;
+    g_server[1].action_cb = &action2_1;
+
+    status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver,
+				PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA,
+				NULL, &srv_cb_2a, NULL);
+    if (status != PJ_SUCCESS) {
+	app_perror("   srv_resolve error", status);
+	pj_assert(status == PJ_SUCCESS);
+    }
+
+    pj_sem_wait(sem);
+
+    /* Clear cache */
+    pj_thread_sleep(1000);
+
+    /* Fallback with PJ_DNS_SRV_FALLBACK_AAAA only */
+    PJ_LOG(3,(THIS_FILE, "  srv_resolve(): fallback to DNS AAAA only"));
+
+    g_server[0].action = ACTION_CB;
+    g_server[0].action_cb = &action2_1;
+    g_server[1].action = ACTION_CB;
+    g_server[1].action_cb = &action2_1;
+
+    status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver,
+				PJ_DNS_SRV_FALLBACK_AAAA,
+				NULL, &srv_cb_2b, NULL);
+    if (status != PJ_SUCCESS) {
+	app_perror("   srv_resolve error", status);
+	pj_assert(status == PJ_SUCCESS);
+    }
+
+    pj_sem_wait(sem);
+
+    /* Clear cache */
+    pj_thread_sleep(1000);
 
     return 0;
@@ -1334,5 +1809,5 @@
 
 	for (j=0; j<PJ_DNS_MAX_IP_IN_A_REC; ++j) {
-	    pj_assert(rec->entry[i].server.addr[j].s_addr == IP_ADDR3+j);
+	    pj_assert(rec->entry[i].server.addr[j].ip.v4.s_addr == IP_ADDR3+j);
 	}
     }
@@ -1375,5 +1850,5 @@
     int rc;
     
-    rc = init();
+    rc = init(PJ_FALSE);
     if (rc != 0)
 	goto on_error;
@@ -1383,4 +1858,8 @@
 	goto on_error;
 
+    rc = addr_parser_test();
+    if (rc != 0)
+	goto on_error;
+
     rc = simple_test();
     if (rc != 0)
@@ -1396,4 +1875,29 @@
 
     destroy();
+
+
+#if PJ_HAS_IPV6
+    /* Similar tests using IPv6 socket and without parser tests */
+    PJ_LOG(3,(THIS_FILE, "  Re-run DNS resolution tests using IPv6 socket"));
+
+    rc = init(PJ_TRUE);
+    if (rc != 0)
+	goto on_error;
+
+    rc = simple_test();
+    if (rc != 0)
+	goto on_error;
+
+    rc = dns_test();
+    if (rc != 0)
+	goto on_error;
+
+    srv_resolver_test();
+    srv_resolver_fallback_test();
+    srv_resolver_many_test();
+
+    destroy();
+#endif
+
     return 0;
 
@@ -1403,3 +1907,2 @@
 }
 
-
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/resolver.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/resolver.c	(revision 5348)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/resolver.c	(revision 5349)
@@ -80,5 +80,5 @@
 struct nameserver
 {
-    pj_sockaddr_in  addr;		/**< Server address.		    */
+    pj_sockaddr     addr;		/**< Server address.		    */
 
     enum ns_state   state;		/**< Nameserver state.		    */
@@ -180,10 +180,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 */
@@ -238,4 +249,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 +267,6 @@
 {
     pj_ioqueue_callback socket_cb;
+    pj_sockaddr bound_addr;
+    pj_ssize_t rx_pkt_size;
     pj_status_t status;
 
@@ -270,12 +294,56 @@
 
     /* 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)
+	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_sock(resv->pool, resv->ioqueue,
+				      resv->udp6_sock, 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;
@@ -475,6 +543,9 @@
 	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);
@@ -613,5 +684,11 @@
 
     /* 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
+	|| pj_ioqueue_is_pending(resolver->udp6_key,
+				 &resolver->udp6_op_tx_key)
+#endif
+	)
+    {
 	++q->transmit_cnt;
 	PJ_LOG(4,(resolver->name.ptr,
@@ -643,9 +720,20 @@
 	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 PJ_HAS_IPV6
+	else {
+	    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));
+	}
+#endif
 
 	PJ_PERROR(4,(resolver->name.ptr, status,
@@ -653,7 +741,6 @@
 		  (q->transmit_cnt==0? "Transmitting":"Re-transmitting"),
 		  (int)pkt_size, servers[i],
-		  pj_inet_ntop2(pj_AF_INET(), &ns->addr.sin_addr, addr,
-		 		sizeof(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));
@@ -1029,4 +1116,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,
@@ -1037,5 +1250,5 @@
     struct nameserver *ns = &resolver->ns[index];
     enum ns_state old_state = ns->state;
-    char addr[PJ_INET_ADDRSTRLEN];
+    char addr[PJ_INET6_ADDRSTRLEN];
 
     ns->state = state;
@@ -1051,7 +1264,6 @@
 
     PJ_LOG(5, (resolver->name.ptr, "Nameserver %s:%d state changed %s --> %s",
-    	       pj_inet_ntop2(pj_AF_INET(), &ns->addr.sin_addr, addr,
-		 	     sizeof(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]));
 }
@@ -1132,5 +1344,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)
 {
@@ -1169,8 +1381,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 */
@@ -1395,5 +1604,9 @@
     pj_dns_parsed_packet *dns_pkt;
     pj_dns_async_query *q;
-    char addr[PJ_INET_ADDRSTRLEN];
+    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;
@@ -1401,4 +1614,21 @@
 
     resolver = (pj_dns_resolver *) pj_ioqueue_get_user_data(key);
+    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_mutex_lock(resolver->mutex);
 
@@ -1412,7 +1642,6 @@
 	PJ_LOG(4,(resolver->name.ptr, 
 		  "DNS resolver read error from %s:%d: %s", 
-		  pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
-		  		addr, sizeof(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));
 
@@ -1423,7 +1652,6 @@
 	      "Received %d bytes DNS response from %s:%d",
 	      (int)bytes_read, 
-	      pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
-		  	    addr, sizeof(addr)), 
-	      pj_ntohs(resolver->udp_src_addr.sin_port)));
+	      pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+	      pj_sockaddr_get_port(src_addr)));
 
 
@@ -1440,5 +1668,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);
     }
@@ -1449,5 +1677,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 */
@@ -1458,7 +1686,6 @@
 	PJ_LOG(3,(resolver->name.ptr, 
 		  "Error parsing DNS response from %s:%d: %s", 
-		  pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
-		  		addr, sizeof(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;
@@ -1472,7 +1699,6 @@
 	PJ_LOG(5,(resolver->name.ptr, 
 		  "DNS response from %s:%d id=%d discarded",
-		  pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
-		  		addr, sizeof(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;
@@ -1537,11 +1763,9 @@
 	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);
+
+    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) {
 	char errmsg[PJ_ERR_MSG_SIZE];
@@ -1643,12 +1867,12 @@
     PJ_LOG(3,(resolver->name.ptr, "  Name servers:"));
     for (i=0; i<resolver->ns_count; ++i) {
-	char addr[PJ_INET_ADDRSTRLEN];
+	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_ntop2(pj_AF_INET(), &ns->addr.sin_addr, addr,
-		 		   sizeof(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,
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/srv_resolver.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/srv_resolver.c	(revision 5348)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/srv_resolver.c	(revision 5349)
@@ -38,20 +38,24 @@
 };
 
+#pragma pack(1)
 struct srv_target
 {
     struct common	    common;
+    struct common	    common_aaaa;
     pj_dns_srv_async_query *parent;
     pj_str_t		    target_name;
     pj_dns_async_query	   *q_a;
+    pj_dns_async_query	   *q_aaaa;
     char		    target_buf[PJ_MAX_HOSTNAME];
     pj_str_t		    cname;
     char		    cname_buf[PJ_MAX_HOSTNAME];
-    unsigned		    port;
+    pj_uint16_t		    port;
     unsigned		    priority;
     unsigned		    weight;
     unsigned		    sum;
     unsigned		    addr_cnt;
-    pj_in_addr		    addr[ADDR_MAX_COUNT];
+    pj_sockaddr		    addr[ADDR_MAX_COUNT];/**< Address family and IP.*/
 };
+#pragma pack()
 
 struct pj_dns_srv_async_query
@@ -136,4 +140,8 @@
     query_job->def_port = (pj_uint16_t)def_port;
 
+    /* Normalize query job option PJ_DNS_SRV_RESOLVE_AAAA_ONLY */
+    if (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)
+	query_job->option |= PJ_DNS_SRV_RESOLVE_AAAA;
+
     /* Start the asynchronous query_job */
 
@@ -177,4 +185,9 @@
 	    pj_dns_resolver_cancel_query(srv->q_a, PJ_FALSE);
 	    srv->q_a = NULL;
+	    has_pending = PJ_TRUE;
+	}
+	if (srv->q_aaaa) {
+	    pj_dns_resolver_cancel_query(srv->q_aaaa, PJ_FALSE);
+	    srv->q_aaaa = NULL;
 	    has_pending = PJ_TRUE;
 	}
@@ -315,6 +328,6 @@
     }
 
-    /* Check for Additional Info section if A records are available, and
-     * fill in the IP address (so that we won't need to resolve the A 
+    /* Check for Additional Info section if A/AAAA records are available, and
+     * fill in the IP address (so that we won't need to resolve the A/AAAA 
      * record with another DNS query_job). 
      */
@@ -323,19 +336,46 @@
 	unsigned j;
 
-	if (rr->type != PJ_DNS_TYPE_A)
+	/* Skip non-A/AAAA record */
+	if (rr->type != PJ_DNS_TYPE_A && rr->type != PJ_DNS_TYPE_AAAA)
 	    continue;
 
-	/* Yippeaiyee!! There is an "A" record! 
+	/* Also skip if:
+	 * - it is A record and app only want AAAA record, or
+	 * - it is AAAA record and app does not want AAAA record
+	 */
+	if ((rr->type == PJ_DNS_TYPE_A &&
+	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)!=0) ||
+	    (rr->type == PJ_DNS_TYPE_AAAA &&
+	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)==0))
+	{
+	    continue;
+	}	    
+
+	/* Yippeaiyee!! There is an "A/AAAA" record! 
 	 * Update the IP address of the corresponding SRV record.
 	 */
 	for (j=0; j<query_job->srv_cnt; ++j) {
-            if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0 &&
-                query_job->srv[j].addr_cnt < ADDR_MAX_COUNT)
-            {
+	    if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0
+		&& query_job->srv[j].addr_cnt < ADDR_MAX_COUNT)
+	    {
 		unsigned cnt = query_job->srv[j].addr_cnt;
-		query_job->srv[j].addr[cnt].s_addr = rr->rdata.a.ip_addr.s_addr;
+		if (rr->type == PJ_DNS_TYPE_A) {
+		    pj_sockaddr_init(pj_AF_INET(),
+					&query_job->srv[j].addr[cnt], NULL,
+					query_job->srv[j].port);
+		    query_job->srv[j].addr[cnt].ipv4.sin_addr =
+						rr->rdata.a.ip_addr;
+		} else {
+		    pj_sockaddr_init(pj_AF_INET6(),
+					&query_job->srv[j].addr[cnt], NULL,
+					query_job->srv[j].port);
+		    query_job->srv[j].addr[cnt].ipv6.sin6_addr =
+						rr->rdata.aaaa.ip_addr;
+		}
+
 		/* Only increment host_resolved once per SRV record */
 		if (query_job->srv[j].addr_cnt == 0)
 		    ++query_job->host_resolved;
+
 		++query_job->srv[j].addr_cnt;
 		break;
@@ -355,4 +395,5 @@
 	}
 	*/
+	
     }
 
@@ -363,4 +404,5 @@
     for (i=0; i<query_job->srv_cnt; ++i) {
 	pj_in_addr addr;
+	pj_in6_addr addr6;
 
 	if (query_job->srv[i].addr_cnt != 0) {
@@ -369,8 +411,23 @@
 	}
 
-	if (pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
+	if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)==0 &&
+	    pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
 			 &addr) == PJ_SUCCESS)
 	{
-	    query_job->srv[i].addr[query_job->srv[i].addr_cnt++] = addr;
+	    unsigned cnt = query_job->srv[i].addr_cnt;
+	    pj_sockaddr_init(pj_AF_INET(), &query_job->srv[i].addr[cnt],
+			     NULL, query_job->srv[i].port);
+	    query_job->srv[i].addr[cnt].ipv4.sin_addr = addr;
+	    ++query_job->srv[i].addr_cnt;
+	    ++query_job->host_resolved;
+	} else if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)!=0 &&
+		   pj_inet_pton(pj_AF_INET6(), &query_job->srv[i].target_name,
+				&addr6) == PJ_SUCCESS)
+	{
+	    unsigned cnt = query_job->srv[i].addr_cnt;
+	    pj_sockaddr_init(pj_AF_INET6(), &query_job->srv[i].addr[cnt],
+			     NULL, query_job->srv[i].port);
+	    query_job->srv[i].addr[cnt].ipv6.sin6_addr = addr6;
+	    ++query_job->srv[i].addr_cnt;
 	    ++query_job->host_resolved;
 	}
@@ -388,9 +445,9 @@
 
     for (i=0; i<query_job->srv_cnt; ++i) {
-	char addr[PJ_INET_ADDRSTRLEN];
+	char addr[PJ_INET6_ADDRSTRLEN];
 
 	if (query_job->srv[i].addr_cnt != 0) {
-	    pj_inet_ntop(pj_AF_INET(), &query_job->srv[i].addr[0],
-			 addr, sizeof(addr));
+	    pj_sockaddr_print(&query_job->srv[i].addr[0],
+			 addr, sizeof(addr), 2);
 	} else
 	    pj_ansi_strcpy(addr, "-");
@@ -408,5 +465,7 @@
 
 
-/* Start DNS A record queries for all SRV records in the query_job structure */
+/* Start DNS A and/or AAAA record queries for all SRV records in
+ * the query_job structure.
+ */
 static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
 {
@@ -415,4 +474,5 @@
 
     query_job->dns_state = PJ_DNS_TYPE_A;
+
     for (i=0; i<query_job->srv_cnt; ++i) {
 	struct srv_target *srv = &query_job->srv[i];
@@ -424,5 +484,29 @@
 
 	srv->common.type = PJ_DNS_TYPE_A;
+	srv->common_aaaa.type = PJ_DNS_TYPE_AAAA;
 	srv->parent = query_job;
+
+	status = PJ_SUCCESS;
+
+	/* Start DNA A record query */
+	if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0)
+	{
+	    status = pj_dns_resolver_start_query(query_job->resolver,
+						 &srv->target_name,
+						 PJ_DNS_TYPE_A, 0,
+						 &dns_callback,
+						 &srv->common, &srv->q_a);
+	}
+
+	/* Start DNA AAAA record query */
+	if (status == PJ_SUCCESS &&
+	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0)
+	{
+	    status = pj_dns_resolver_start_query(query_job->resolver,
+						 &srv->target_name,
+						 PJ_DNS_TYPE_AAAA, 0,
+						 &dns_callback,
+						 &srv->common_aaaa, &srv->q_aaaa);
+	}
 
 	/* See also #1809: dns_callback() will be invoked synchronously when response
@@ -431,9 +515,4 @@
 	 * returning false error, so don't use that variable for counting errors.
 	 */
-	status = pj_dns_resolver_start_query(query_job->resolver,
-					     &srv->target_name,
-					     PJ_DNS_TYPE_A, 0,
-					     &dns_callback,
-					     srv, &srv->q_a);
 	if (status != PJ_SUCCESS) {
 	    query_job->host_resolved++;
@@ -465,4 +544,7 @@
 	srv = (struct srv_target*) common;
 	query_job = srv->parent;
+    } else if (common->type == PJ_DNS_TYPE_AAAA) {
+	srv = (struct srv_target*)((pj_int8_t*)common-sizeof(struct common));
+	query_job = srv->parent;
     } else {
 	pj_assert(!"Unexpected user data!");
@@ -475,4 +557,5 @@
 	/* We are getting SRV response */
 
+	/* Clear the outstanding job */
 	query_job->q_srv = NULL;
 
@@ -508,4 +591,6 @@
 	 */
 	if (query_job->srv_cnt == 0) {
+	    unsigned new_option = 0;
+
 	    /* Looks like we aren't getting any SRV responses.
 	     * Resolve the original target as A record by creating a 
@@ -514,5 +599,5 @@
 	    PJ_LOG(4, (query_job->objname, 
 		       "DNS SRV resolution failed for %.*s, trying "
-		       "resolving A record for %.*s",
+		       "resolving A/AAAA record for %.*s",
 		       (int)query_job->full_name.slen, 
 		       query_job->full_name.ptr,
@@ -527,9 +612,18 @@
 	    query_job->srv[i].weight = 0;
 	    query_job->srv[i].port = query_job->def_port;
-	} 
+
+	    /* Update query_job resolution option based on fallback option */
+	    if (query_job->option & PJ_DNS_SRV_FALLBACK_AAAA)
+		new_option |= (PJ_DNS_SRV_RESOLVE_AAAA |
+			       PJ_DNS_SRV_RESOLVE_AAAA_ONLY);
+	    if (query_job->option & PJ_DNS_SRV_FALLBACK_A)
+		new_option &= (~PJ_DNS_SRV_RESOLVE_AAAA_ONLY);
+	    
+	    query_job->option = new_option;
+	}
 	
 
-	/* Resolve server hostnames (DNS A record) for hosts which don't have
-	 * A record yet.
+	/* Resolve server hostnames (DNS A/AAAA record) for hosts which
+	 * don't have A/AAAA record yet.
 	 */
 	if (query_job->host_resolved != query_job->srv_cnt) {
@@ -545,15 +639,28 @@
 
     } else if (query_job->dns_state == PJ_DNS_TYPE_A) {
-
-	/* Clear the outstanding job */
-	srv->q_a = NULL;
+	pj_bool_t is_type_a, srv_completed;
+
+	/* Clear outstanding job */
+	if (common->type == PJ_DNS_TYPE_A) {
+	    srv_completed = (srv->q_aaaa == NULL);
+	    srv->q_a = NULL;
+	} else if (common->type == PJ_DNS_TYPE_AAAA) {
+	    srv_completed = (srv->q_a == NULL);
+	    srv->q_aaaa = NULL;
+	} else {
+	    pj_assert(!"Unexpected job type");
+	    query_job->last_error = status = PJ_EINVALIDOP;
+	    goto on_error;
+	}
+
+	is_type_a = (common->type == PJ_DNS_TYPE_A);
 
 	/* Check that we really have answer */
 	if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
-	    char addr[PJ_INET_ADDRSTRLEN];
-	    pj_dns_a_record rec;
+	    char addr[PJ_INET6_ADDRSTRLEN];
+	    pj_dns_addr_record rec;
 
 	    /* Parse response */
-	    status = pj_dns_parse_a_response(pkt, &rec);
+	    status = pj_dns_parse_addr_response(pkt, &rec);
 	    if (status != PJ_SUCCESS)
 		goto on_error;
@@ -562,35 +669,51 @@
 
 	    /* Update CNAME alias, if present. */
-	    if (rec.alias.slen) {
+	    if (srv->cname.slen==0 && rec.alias.slen) {
 		pj_assert(rec.alias.slen <= (int)sizeof(srv->cname_buf));
 		srv->cname.ptr = srv->cname_buf;
 		pj_strcpy(&srv->cname, &rec.alias);
-	    } else {
-		srv->cname.slen = 0;
+	    //} else {
+		//srv->cname.slen = 0;
 	    }
 
 	    /* Update IP address of the corresponding hostname or CNAME */
-	    if (srv->addr_cnt < ADDR_MAX_COUNT) {
-		srv->addr[srv->addr_cnt++].s_addr = rec.addr[0].s_addr;
-
-		PJ_LOG(5,(query_job->objname, 
-			  "DNS A for %.*s: %s",
-			  (int)srv->target_name.slen, 
-			  srv->target_name.ptr,
-			  pj_inet_ntop2(pj_AF_INET(), &rec.addr[0],
-			  		addr, sizeof(addr))));
-	    }
-
-	    /* Check for multiple IP addresses */
-	    for (i=1; i<rec.addr_count && srv->addr_cnt < ADDR_MAX_COUNT; ++i)
+	    for (i=0; i<rec.addr_count && srv->addr_cnt<ADDR_MAX_COUNT; ++i)
 	    {
-		srv->addr[srv->addr_cnt++].s_addr = rec.addr[i].s_addr;
-
-		PJ_LOG(5,(query_job->objname, 
-			  "Additional DNS A for %.*s: %s",
-			  (int)srv->target_name.slen, 
-			  srv->target_name.ptr,
-			  pj_inet_ntop2(pj_AF_INET(), &rec.addr[i],
-			  		addr, sizeof(addr))));
+		pj_bool_t added = PJ_FALSE;
+
+		if (is_type_a && rec.addr[i].af == pj_AF_INET()) {
+		    pj_sockaddr_init(pj_AF_INET(), &srv->addr[srv->addr_cnt],
+				     NULL, srv->port);
+		    srv->addr[srv->addr_cnt].ipv4.sin_addr =
+				     rec.addr[i].ip.v4;
+		    added = PJ_TRUE;
+		} else if (!is_type_a && rec.addr[i].af == pj_AF_INET6()) {
+		    pj_sockaddr_init(pj_AF_INET6(), &srv->addr[srv->addr_cnt],
+				     NULL, srv->port);
+		    srv->addr[srv->addr_cnt].ipv6.sin6_addr =
+				     rec.addr[i].ip.v6;
+		    added = PJ_TRUE;
+		} else {
+		    /* Mismatched address family, e.g: getting IPv6 address in
+		     * DNS A query resolution.
+		     */
+		    PJ_LOG(4,(query_job->objname, 
+			      "Bad address family in DNS %s query for %.*s",
+			      (is_type_a? "A" : "AAAA"),
+			      (int)srv->target_name.slen, 
+			      srv->target_name.ptr));
+		}
+
+		if (added) {
+		    PJ_LOG(5,(query_job->objname, 
+			      "DNS %s for %.*s: %s",
+			      (is_type_a? "A" : "AAAA"),
+			      (int)srv->target_name.slen, 
+			      srv->target_name.ptr,
+			      pj_sockaddr_print(&srv->addr[srv->addr_cnt],
+						addr, sizeof(addr), 2)));
+
+		    ++srv->addr_cnt;
+		}
 	    }
 
@@ -603,9 +726,15 @@
 	    /* Log error */
 	    pj_strerror(status, errmsg, sizeof(errmsg));
-	    PJ_LOG(4,(query_job->objname, "DNS A record resolution failed: %s", 
+	    PJ_LOG(4,(query_job->objname,
+		      "DNS %s record resolution failed: %s",
+		      (is_type_a? "A" : "AAAA"),
 		      errmsg));
 	}
 
-	++query_job->host_resolved;
+	/* Increment host resolved count when both DNS A and AAAA record
+	 * queries for this server are completed.
+	 */
+	if (srv_completed)
+	    ++query_job->host_resolved;
 
     } else {
@@ -624,4 +753,5 @@
 	    unsigned j;
 	    struct srv_target *srv2 = &query_job->srv[i];
+	    pj_dns_addr_record *s = &srv_rec.entry[srv_rec.count].server;
 
 	    srv_rec.entry[srv_rec.count].priority = srv2->priority;
@@ -635,8 +765,11 @@
 	    pj_assert(srv2->addr_cnt <= PJ_DNS_MAX_IP_IN_A_REC);
 
-	    for (j=0; j<srv2->addr_cnt; ++j) {
-		srv_rec.entry[srv_rec.count].server.addr[j].s_addr = 
-		    srv2->addr[j].s_addr;
-		++srv_rec.entry[srv_rec.count].server.addr_count;
+	    for (j=0; j<srv2->addr_cnt; ++j) {		
+		s->addr[j].af = srv2->addr[j].addr.sa_family;
+		if (s->addr[j].af == pj_AF_INET())
+		    s->addr[j].ip.v4 = srv2->addr[j].ipv4.sin_addr;
+		else
+		    s->addr[j].ip.v6 = srv2->addr[j].ipv6.sin6_addr;
+		++s->addr_count;
 	    }
 
@@ -681,4 +814,8 @@
 		  status,
 		  pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
+
+	/* Cancel any pending query */
+	pj_dns_srv_cancel_query(query_job, PJ_FALSE);
+
 	(*query_job->cb)(query_job->token, status, NULL);
 	return;
Index: /pjproject/trunk/pjnath/src/pjnath/stun_sock.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 5348)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 5349)
@@ -417,8 +417,9 @@
 	pj_assert(stun_sock->q == NULL);
 
-	opt = PJ_DNS_SRV_FALLBACK_A;
-	if (stun_sock->af == pj_AF_INET6()) {
-	    opt |= (PJ_DNS_SRV_RESOLVE_AAAA | PJ_DNS_SRV_FALLBACK_AAAA);
-	}
+	/* Init DNS resolution option */
+	if (stun_sock->af == pj_AF_INET6())
+	    opt = (PJ_DNS_SRV_RESOLVE_AAAA_ONLY | PJ_DNS_SRV_FALLBACK_AAAA);
+	else
+	    opt = PJ_DNS_SRV_FALLBACK_A;
 
 	status = pj_dns_srv_resolve(domain, &res_name, default_port, 
@@ -575,12 +576,16 @@
     pj_assert(rec->count);
     pj_assert(rec->entry[0].server.addr_count);
-
-    PJ_TODO(SUPPORT_IPV6_IN_RESOLVER);
-    pj_assert(stun_sock->af == pj_AF_INET());
+    pj_assert(rec->entry[0].server.addr[0].af == stun_sock->af);
 
     /* Set the address */
-    pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL,
-			rec->entry[0].port);
-    stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0];
+    pj_sockaddr_init(stun_sock->af, &stun_sock->srv_addr, NULL,
+		     rec->entry[0].port);
+    if (stun_sock->af == pj_AF_INET6()) {
+	stun_sock->srv_addr.ipv6.sin6_addr = 
+				    rec->entry[0].server.addr[0].ip.v6;
+    } else {
+	stun_sock->srv_addr.ipv4.sin_addr = 
+				    rec->entry[0].server.addr[0].ip.v4;
+    }
 
     /* Start sending Binding request */
Index: /pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5348)
+++ /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5349)
@@ -609,7 +609,14 @@
 	}
 
+	/* Init DNS resolution option for IPv6 */
+	if (sess->af == pj_AF_INET6())
+	    opt |= PJ_DNS_SRV_RESOLVE_AAAA_ONLY;
+
 	/* Fallback to DNS A only if default port is specified */
 	if (default_port>0 && default_port<65536) {
-	    opt = PJ_DNS_SRV_FALLBACK_A;
+	    if (sess->af == pj_AF_INET6())
+		opt |= PJ_DNS_SRV_FALLBACK_AAAA;
+	    else
+		opt |= PJ_DNS_SRV_FALLBACK_A;
 	    sess->default_port = (pj_uint16_t)default_port;
 	}
@@ -1719,11 +1726,16 @@
 		  cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++j) 
 	{
-	    pj_sockaddr_in *addr = &sess->srv_addr_list[cnt].ipv4;
-
-	    addr->sin_family = sess->af;
-	    addr->sin_port = pj_htons(rec->entry[i].port);
-	    addr->sin_addr.s_addr = rec->entry[i].server.addr[j].s_addr;
-
-	    ++cnt;
+	    if (rec->entry[i].server.addr[j].af == sess->af) {
+		pj_sockaddr *addr = &sess->srv_addr_list[cnt];
+
+		addr->addr.sa_family = sess->af;
+		pj_sockaddr_set_port(addr, rec->entry[i].port);
+		if (sess->af == pj_AF_INET6())
+		    addr->ipv6.sin6_addr = rec->entry[i].server.addr[j].ip.v6;
+		else
+		    addr->ipv4.sin_addr = rec->entry[i].server.addr[j].ip.v4;
+
+		++cnt;
+	    }
 	}
     }
Index: /pjproject/trunk/pjsip/src/pjsip/sip_resolve.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_resolve.c	(revision 5348)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_resolve.c	(revision 5349)
@@ -54,4 +54,5 @@
     pjsip_resolver_callback *cb;
     pj_dns_async_query	    *object;
+    pj_dns_async_query	    *object6;
     pj_status_t		     last_error;
 
@@ -65,4 +66,7 @@
     unsigned		     naptr_cnt;
     struct naptr_target	     naptr[8];
+
+    /* Query result */
+    pjsip_server_addresses   server;
 };
 
@@ -81,4 +85,7 @@
 			   pj_status_t status,
 			   pj_dns_parsed_packet *response);
+static void dns_aaaa_callback(void *user_data,
+			      pj_status_t status,
+			      pj_dns_parsed_packet *response);
 
 
@@ -207,5 +214,8 @@
     ip_addr_ver = get_ip_addr_ver(&target->addr.host);
 
-    /* Initialize address family type */
+    /* Initialize address family type. Unfortunately, target type doesn't
+     * really tell the address family type, except when IPv6 flag is
+     * explicitly set.
+     */
     if ((ip_addr_ver == 6) || (type & PJSIP_TRANSPORT_IPV6))
 	af = pj_AF_INET6();
@@ -402,17 +412,38 @@
 
     if (query->query_type == PJ_DNS_TYPE_SRV) {
+	int opt = 0;
+
+	if (af == pj_AF_UNSPEC())
+	    opt = PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA |
+		  PJ_DNS_SRV_RESOLVE_AAAA;
+	else if (af == pj_AF_INET6())
+	    opt = PJ_DNS_SRV_FALLBACK_AAAA | PJ_DNS_SRV_RESOLVE_AAAA_ONLY;
+	else /* af == pj_AF_INET() */
+	    opt = PJ_DNS_SRV_FALLBACK_A;
 
 	status = pj_dns_srv_resolve(&query->naptr[0].name,
 				    &query->naptr[0].res_type,
 				    query->req.def_port, pool, resolver->res,
-				    PJ_TRUE, query, &srv_resolver_cb, NULL);
+				    opt, query, &srv_resolver_cb, NULL);
 
     } else if (query->query_type == PJ_DNS_TYPE_A) {
 
-	status = pj_dns_resolver_start_query(resolver->res, 
-					     &query->naptr[0].name,
-					     PJ_DNS_TYPE_A, 0, 
-					     &dns_a_callback,
-    					     query, &query->object);
+	/* Resolve DNS A record if address family is not fixed to IPv6 */
+	if (af != pj_AF_INET6()) {
+	    status = pj_dns_resolver_start_query(resolver->res, 
+						 &query->naptr[0].name,
+						 PJ_DNS_TYPE_A, 0, 
+						 &dns_a_callback,
+    						 query, &query->object);
+	}
+
+	/* Resolve DNS AAAA record if address family is not fixed to IPv4 */
+	if (af != pj_AF_INET()) {
+	    status = pj_dns_resolver_start_query(resolver->res, 
+						 &query->naptr[0].name,
+						 PJ_DNS_TYPE_AAAA, 0, 
+						 &dns_aaaa_callback,
+    						 query, &query->object6);
+	}
 
     } else {
@@ -455,16 +486,38 @@
 {
     struct query *query = (struct query*) user_data;
-    pjsip_server_addresses srv;
-    pj_dns_a_record rec;
-    unsigned i;
-
-    rec.addr_count = 0;
-
-    /* Parse the response */
+    pjsip_server_addresses *srv = &query->server;
+
+    /* Reset outstanding job */
+    query->object = NULL;
+
     if (status == PJ_SUCCESS) {
-	status = pj_dns_parse_a_response(pkt, &rec);
-    }
-
-    if (status != PJ_SUCCESS) {
+	pj_dns_addr_record rec;
+	unsigned i;
+
+	/* Parse the response */
+	rec.addr_count = 0;
+	status = pj_dns_parse_addr_response(pkt, &rec);
+
+	/* Build server addresses and call callback */
+	for (i = 0; i < rec.addr_count &&
+		    srv->count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i)
+	{
+	    /* Should not happen, just in case */
+	    if (rec.addr[i].af != pj_AF_INET())
+		continue;
+
+	    srv->entry[srv->count].type = query->naptr[0].type;
+	    srv->entry[srv->count].priority = 0;
+	    srv->entry[srv->count].weight = 0;
+	    srv->entry[srv->count].addr_len = sizeof(pj_sockaddr_in);
+	    pj_sockaddr_in_init(&srv->entry[srv->count].addr.ipv4,
+				0, (pj_uint16_t)query->req.def_port);
+	    srv->entry[srv->count].addr.ipv4.sin_addr = rec.addr[i].ip.v4;
+
+	    ++srv->count;
+	}
+
+    } else {
+
 	char errmsg[PJ_ERR_MSG_SIZE];
 
@@ -474,28 +527,77 @@
 		  errmsg));
 
-	/* Call the callback */
-	(*query->cb)(status, query->token, NULL);
-	return;
-    }
-
-    /* Build server addresses and call callback */
-    srv.count = 0;
-    for (i = 0; i < rec.addr_count &&
-		srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i)
-    {
-	srv.entry[srv.count].type = query->naptr[0].type;
-	srv.entry[srv.count].priority = 0;
-	srv.entry[srv.count].weight = 0;
-	srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in);
-	pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4,
-			    0, (pj_uint16_t)query->req.def_port);
-	srv.entry[srv.count].addr.ipv4.sin_addr.s_addr =
-	    rec.addr[i].s_addr;
-
-	++srv.count;
-    }
-
-    /* Call the callback */
-    (*query->cb)(PJ_SUCCESS, query->token, &srv);
+	query->last_error = status;
+    }
+
+    /* Call the callback if all DNS queries have been completed */
+    if (query->object == NULL && query->object6 == NULL) {
+	if (srv->count > 0)
+	    (*query->cb)(PJ_SUCCESS, query->token, &query->server);
+	else
+	    (*query->cb)(query->last_error, query->token, NULL);
+    }
+}
+
+
+/* 
+ * This callback is called when target is resolved with DNS AAAA query.
+ */
+static void dns_aaaa_callback(void *user_data,
+			      pj_status_t status,
+			      pj_dns_parsed_packet *pkt)
+{
+    struct query *query = (struct query*) user_data;
+    pjsip_server_addresses *srv = &query->server;
+
+    /* Reset outstanding job */
+    query->object6 = NULL;
+
+    if (status == PJ_SUCCESS) {
+	pj_dns_addr_record rec;
+	unsigned i;
+
+	/* Parse the response */
+	rec.addr_count = 0;
+	status = pj_dns_parse_addr_response(pkt, &rec);
+
+	/* Build server addresses and call callback */
+	for (i = 0; i < rec.addr_count &&
+		    srv->count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i)
+	{
+	    /* Should not happen, just in case */
+	    if (rec.addr[i].af != pj_AF_INET6())
+		continue;
+
+	    srv->entry[srv->count].type = query->naptr[0].type |
+					  PJSIP_TRANSPORT_IPV6;
+	    srv->entry[srv->count].priority = 0;
+	    srv->entry[srv->count].weight = 0;
+	    srv->entry[srv->count].addr_len = sizeof(pj_sockaddr_in);
+	    pj_sockaddr_init(pj_AF_INET6(), &srv->entry[srv->count].addr,
+			     0, (pj_uint16_t)query->req.def_port);
+	    srv->entry[srv->count].addr.ipv6.sin6_addr = rec.addr[i].ip.v6;
+
+	    ++srv->count;
+	}
+
+    } else {
+
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	/* Log error */
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(query->objname, "DNS AAAA record resolution failed: %s", 
+		  errmsg));
+
+	query->last_error = status;
+    }
+
+    /* Call the callback if all DNS queries have been completed */
+    if (query->object == NULL && query->object6 == NULL) {
+	if (srv->count > 0)
+	    (*query->cb)(PJ_SUCCESS, query->token, &query->server);
+	else
+	    (*query->cb)(query->last_error, query->token, NULL);
+    }
 }
 
@@ -515,5 +617,5 @@
 	/* Log error */
 	pj_strerror(status, errmsg, sizeof(errmsg));
-	PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", 
+	PJ_LOG(4,(query->objname, "DNS A/AAAA record resolution failed: %s",
 		  errmsg));
 
@@ -526,7 +628,8 @@
     srv.count = 0;
     for (i=0; i<rec->count; ++i) {
+	const pj_dns_addr_record *s = &rec->entry[i].server;
 	unsigned j;
 
-	for (j = 0; j < rec->entry[i].server.addr_count &&
+	for (j = 0; j < s->addr_count &&
 		    srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j)
 	{
@@ -535,8 +638,15 @@
 	    srv.entry[srv.count].weight = rec->entry[i].weight;
 	    srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in);
-	    pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4,
-				0, (pj_uint16_t)rec->entry[i].port);
-	    srv.entry[srv.count].addr.ipv4.sin_addr.s_addr =
-		rec->entry[i].server.addr[j].s_addr;
+	    pj_sockaddr_init(s->addr[j].af,
+			     &srv.entry[srv.count].addr,
+			     0, (pj_uint16_t)rec->entry[i].port);
+	    if (s->addr[j].af == pj_AF_INET6())
+		srv.entry[srv.count].addr.ipv6.sin6_addr = s->addr[j].ip.v6;
+	    else
+		srv.entry[srv.count].addr.ipv4.sin_addr = s->addr[j].ip.v4;
+
+	    /* Update transport type if this is IPv6 */
+	    if (s->addr[j].af == pj_AF_INET6())
+		srv.entry[srv.count].type |= PJSIP_TRANSPORT_IPV6;
 
 	    ++srv.count;
