Index: pjproject/trunk/pjlib-util/src/pjlib-util/srv_resolver.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/srv_resolver.c	(revision 5311)
+++ 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;
