Ticket #419: initial support for DNS AAAA resolution

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1587 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib-util/include/pjlib-util/dns.h b/pjlib-util/include/pjlib-util/dns.h
index 4505eb3..10ba116 100644
--- a/pjlib-util/include/pjlib-util/dns.h
+++ b/pjlib-util/include/pjlib-util/dns.h
@@ -41,7 +41,10 @@
  *
  * This module provides low-level services to parse and packetize DNS queries
  * and responses. The functions support building a DNS query packet and parse
- * the data in the DNS response.
+ * the data in the DNS response. This implementation conforms to the 
+ * following specifications:
+ *  - RFC 1035: DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+ *  - RFC 1886: DNS Extensions to support IP version 6
  *
  * To create a DNS query packet, application should call #pj_dns_make_query()
  * function, specifying the desired DNS query type, the name to be resolved,
@@ -258,9 +261,14 @@
 
 	/** A Resource Data (PJ_DNS_TYPE_A, 1) */
 	struct a {
-	    pj_in_addr	ip_addr;/**< IP host address string.		    */
+	    pj_in_addr	ip_addr;/**< IPv4 address in network byte order.    */
 	} a;
 
+	/** AAAA Resource Data (PJ_DNS_TYPE_AAAA, 28) */
+	struct aaaa {
+	    pj_in6_addr	ip_addr;/**< IPv6 address in network byte order.    */
+	} aaaa;
+
     } rdata;
 
 } pj_dns_parsed_rr;
diff --git a/pjlib-util/include/pjlib-util/srv_resolver.h b/pjlib-util/include/pjlib-util/srv_resolver.h
index 7673bfb..27f3e28 100644
--- a/pjlib-util/include/pjlib-util/srv_resolver.h
+++ b/pjlib-util/include/pjlib-util/srv_resolver.h
@@ -75,6 +75,40 @@
  */
 
 /**
+ * Flags to be specified when starting the DNS SRV query.
+ */
+enum pj_dns_srv_option
+{
+    /**
+     * Specify if the resolver should fallback with DNS A
+     * 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.
+     */
+    PJ_DNS_SRV_FALLBACK_A	= 1,
+
+    /**
+     * 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.
+     */
+    PJ_DNS_SRV_FALLBACK_AAAA	= 2,
+
+    /**
+     * 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
+
+} pj_dns_srv_option;
+
+
+/**
  * This structure represents DNS SRV records as the result of DNS SRV
  * resolution using #pj_dns_srv_resolve().
  */
@@ -85,7 +119,7 @@
 
     /** Address records. */
     struct
-v    {
+    {
 	/** Server priority (the lower the higher the priority). */
 	unsigned		priority;
 
@@ -125,8 +159,13 @@
  *			resolved with DNS A resolution.
  * @param pool		Memory pool used to allocate memory for the query.
  * @param resolver	The resolver instance.
- * @param fallback_a	Specify if the resolver should fallback with DNS A
- *			resolution when the SRV resolution fails.
+ * @param option	Option flags, which can be constructed from
+ *			#pj_dns_srv_option bitmask. Note that this argument
+ *			was called "fallback_a" in pjsip version 0.8.0 and
+ *			older, but the new option should be backward 
+ *			compatible with existing applications. If application
+ *			specifies PJ_TRUE as "fallback_a" value, it will
+ *			correspond to PJ_DNS_SRV_FALLBACK_A option.
  * @param token		Arbitrary data to be associated with this query when
  *			the calback is called.
  * @param cb		Pointer to callback function to receive the
@@ -142,7 +181,7 @@
 					unsigned def_port,
 					pj_pool_t *pool,
 					pj_dns_resolver *resolver,
-					pj_bool_t fallback_a,
+					unsigned option,
 					void *token,
 					pj_dns_srv_resolver_cb *cb,
 					pj_dns_async_query **p_query);
diff --git a/pjlib-util/src/pjlib-util/dns.c b/pjlib-util/src/pjlib-util/dns.c
index f8bc900..53066ad 100644
--- a/pjlib-util/src/pjlib-util/dns.c
+++ b/pjlib-util/src/pjlib-util/dns.c
@@ -29,6 +29,7 @@
 {
     switch (type) {
     case PJ_DNS_TYPE_A:	    return "A";
+    case PJ_DNS_TYPE_AAAA:  return "AAAA";
     case PJ_DNS_TYPE_SRV:   return "SRV";
     case PJ_DNS_TYPE_NS:    return "NS";
     case PJ_DNS_TYPE_CNAME: return "CNAME";
@@ -346,6 +347,10 @@
 	pj_memcpy(&rr->rdata.a.ip_addr, p, 4);
 	p += 4;
 
+    } else if (rr->type == PJ_DNS_TYPE_AAAA) {
+	pj_memcpy(&rr->rdata.aaaa.ip_addr, p, 16);
+	p += 16;
+
     } else if (rr->type == PJ_DNS_TYPE_CNAME ||
 	       rr->type == PJ_DNS_TYPE_NS ||
 	       rr->type == PJ_DNS_TYPE_PTR) 
@@ -591,6 +596,9 @@
 			 pool, &dst->rdata.srv.target);
     } else if (src->type == PJ_DNS_TYPE_A) {
 	dst->rdata.a.ip_addr.s_addr =  src->rdata.a.ip_addr.s_addr;
+    } else if (src->type == PJ_DNS_TYPE_AAAA) {
+	pj_memcpy(&dst->rdata.aaaa.ip_addr, &src->rdata.aaaa.ip_addr,
+		  sizeof(pj_in6_addr));
     } else if (src->type == PJ_DNS_TYPE_CNAME) {
 	pj_strdup(pool, &dst->rdata.cname.name, &src->rdata.cname.name);
     } else if (src->type == PJ_DNS_TYPE_NS) {
diff --git a/pjlib-util/src/pjlib-util/dns_dump.c b/pjlib-util/src/pjlib-util/dns_dump.c
index d493323..6bb5cdc 100644
--- a/pjlib-util/src/pjlib-util/dns_dump.c
+++ b/pjlib-util/src/pjlib-util/dns_dump.c
@@ -117,6 +117,11 @@
     } else if (rr->type == PJ_DNS_TYPE_A) {
 	PJ_LOG(3,(THIS_FILE, "    IP address: %s",
 		  pj_inet_ntoa(rr->rdata.a.ip_addr)));
+    } else if (rr->type == PJ_DNS_TYPE_AAAA) {
+	char addr[PJ_INET6_ADDRSTRLEN];
+	PJ_LOG(3,(THIS_FILE, "    IPv6 address: %s",
+		  pj_inet_ntop(pj_AF_INET6(), &rr->rdata.aaaa.ip_addr,
+			       addr, sizeof(addr))));
     }
 }
 
diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c
index a6af7ac..ca5892d 100644
--- a/pjlib-util/src/pjlib-util/srv_resolver.c
+++ b/pjlib-util/src/pjlib-util/srv_resolver.c
@@ -55,7 +55,7 @@
     pj_status_t		     last_error;
 
     /* Original request: */
-    pj_bool_t		     fallback_a;
+    unsigned		     option;
     pj_str_t		     full_name;
     pj_str_t		     domain_part;
     pj_uint16_t		     def_port;
@@ -85,7 +85,7 @@
 					unsigned def_port,
 					pj_pool_t *pool,
 					pj_dns_resolver *resolver,
-					pj_bool_t fallback_a,
+					unsigned option,
 					void *token,
 					pj_dns_srv_resolver_cb *cb,
 					pj_dns_async_query **p_query)
@@ -116,7 +116,7 @@
     query_job->resolver = resolver;
     query_job->token = token;
     query_job->cb = cb;
-    query_job->fallback_a = fallback_a;
+    query_job->option = option;
     query_job->full_name = target_name;
     query_job->domain_part.ptr = target_name.ptr + len;
     query_job->domain_part.slen = target_name.slen - len;
@@ -417,7 +417,9 @@
 		      errmsg));
 
 	    /* Trigger error when fallback is disabled */
-	    if (query_job->fallback_a == PJ_FALSE) {
+	    if ((query_job->option &
+		 (PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA)) == 0) 
+	    {
 		goto on_error;
 	    }
 	}
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
index eafc29e..156ded8 100644
--- a/pjlib/src/pj/sock_bsd.c
+++ b/pjlib/src/pj/sock_bsd.c
@@ -307,6 +307,9 @@
 
 {
     PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL);
+
+    *dst = '\0';
+
     PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL);
 
 #if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0