summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO17
-rw-r--r--PKGBUILD29
-rw-r--r--adns-ipv6.patch2722
3 files changed, 2768 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 00000000000..eec63043f5a
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,17 @@
+pkgbase = adns-ipv6
+ pkgdesc = adns is an asyncronous replacement resolver library with ipv6 support
+ pkgver = 1.4
+ pkgrel = 1
+ url = http://www.chiark.greenend.org.uk/~ian/adns/
+ arch = i686
+ arch = x86_64
+ license = GPL
+ depends = glibc
+ conflicts = adns
+ source = http://www.chiark.greenend.org.uk/~ian/adns/ftp/adns-1.4.tar.gz
+ source = http://people.debian.org/~lucab/deb/adns/adns-ipv6.patch
+ md5sums = 88bc7bbf3f62a8d4fb186b8f72ead853
+ md5sums = 848281f907996d3ccfbabd5036ac51a5
+
+pkgname = adns-ipv6
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 00000000000..8d90488aad4
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,29 @@
+# Maintainer: nandub <dev+arch@nandub.info>
+
+pkgname=adns-ipv6
+_pkgname=adns
+pkgver=1.4
+pkgrel=1
+pkgdesc="adns is an asyncronous replacement resolver library with ipv6 support"
+arch=('i686' 'x86_64')
+url="http://www.chiark.greenend.org.uk/~ian/adns/"
+license=('GPL')
+depends=('glibc')
+conflicts=('adns')
+source=(http://www.chiark.greenend.org.uk/~ian/$_pkgname/ftp/$_pkgname-$pkgver.tar.gz
+ http://people.debian.org/~lucab/deb/adns/adns-ipv6.patch)
+md5sums=('88bc7bbf3f62a8d4fb186b8f72ead853'
+ '848281f907996d3ccfbabd5036ac51a5')
+
+build() {
+ cd $srcdir/$_pkgname-$pkgver
+ patch -p1 < $srcdir/adns-ipv6.patch
+ ./configure --prefix=/usr
+ make
+}
+
+package() {
+ cd $srcdir/$_pkgname-$pkgver
+ mkdir -p $pkgdir/usr/{lib,include,bin}
+ make prefix=$pkgdir/usr install
+}
diff --git a/adns-ipv6.patch b/adns-ipv6.patch
new file mode 100644
index 00000000000..c7d0d546a60
--- /dev/null
+++ b/adns-ipv6.patch
@@ -0,0 +1,2722 @@
+diff --git a/Makefile b/Makefile
+index 5ba3e19..a87bc87 100644
+--- a/Makefile
++++ b/Makefile
+@@ -56,9 +56,9 @@ dist_tmp=dist_tmp/adns-$(DISTVERSION)
+ dist: distprep
+ rm -rf dist_tmp*
+ mkdir dist_tmp $(dist_tmp)
+- find \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \
++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \
+ sed -e 's#.*#mkdir -p $(dist_tmp)/&#' | sh
+- find \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \
++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \
+ sed -e 's#.*#ln & $(dist_tmp)/&#' | sh
+ $(MAKE) -C dist_tmp/adns-$(DISTVERSION) distclean
+ cd dist_tmp && tar cf ../$(dist_tmp).tar `basename $(dist_tmp)`
+diff --git a/Makefile.in b/Makefile.in
+index 6e2e449..0babf0e 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -56,9 +56,9 @@ dist_tmp=dist_tmp/adns-$(DISTVERSION)
+ dist: distprep
+ rm -rf dist_tmp*
+ mkdir dist_tmp $(dist_tmp)
+- find \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \
++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \
+ sed -e 's#.*#mkdir -p $(dist_tmp)/&#' | sh
+- find \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \
++ find . \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \
+ sed -e 's#.*#ln & $(dist_tmp)/&#' | sh
+ $(MAKE) -C dist_tmp/adns-$(DISTVERSION) distclean
+ cd dist_tmp && tar cf ../$(dist_tmp).tar `basename $(dist_tmp)`
+diff --git a/client/adh-main.c b/client/adh-main.c
+index b6f3bd4..f2032ec 100644
+--- a/client/adh-main.c
++++ b/client/adh-main.c
+@@ -91,6 +91,7 @@ void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) {
+ { adns_r_rp, "rp" },
+ { adns_r_srv, "srv" },
+ { adns_r_addr, "addr" },
++ { adns_r_srv, "srv" },
+
+ /* types with only one version */
+ { adns_r_cname, "cname" },
+@@ -99,6 +100,7 @@ void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) {
+
+ /* raw versions */
+ { adns_r_a, "a" },
++ { adns_r_aaaa, "aaaa" },
+ { adns_r_ns_raw, "ns-" },
+ { adns_r_soa_raw, "soa-" },
+ { adns_r_ptr_raw, "ptr-" },
+diff --git a/client/adh-opts.c b/client/adh-opts.c
+index 08310e0..7b17c89 100644
+--- a/client/adh-opts.c
++++ b/client/adh-opts.c
+@@ -32,6 +32,8 @@ int ov_verbose= 0;
+ adns_rrtype ov_type= adns_r_none;
+ int ov_search=0, ov_qc_query=0, ov_qc_anshost=0, ov_qc_cname=1;
+ int ov_tcp=0, ov_cname=0, ov_format=fmt_default;
++int ov_ipflags=0;
++int ov_ip6mapped=0;
+ char *ov_id= 0;
+ struct perqueryflags_remember ov_pqfr = { 1,1,1, tm_none };
+
+@@ -114,6 +116,16 @@ static const struct optioninfo perquery_options[]= {
+ { ot_value, "CNAME ok for query domain, but not in RRs (default)",
+ "Cs", "cname-ok", &ov_cname, 0 },
+
++ { ot_desconly, "per-query IPv6 mode:" },
++ { ot_value, "Ask only for IPv6 addresses",
++ "I6", "ip6-only", &ov_ipflags, adns_qf_ip6 },
++ { ot_value, "Ask only for IPv4 addresses",
++ "I4", "ip4-only", &ov_ipflags, adns_qf_ip4 },
++ { ot_value, "Ask for both IPv4 and IPv6 addresses (default)",
++ "IX", "ipv6-mixed", &ov_ipflags, adns_qf_ip4|adns_qf_ip6 },
++ { ot_value, "Ask for both IPv4 and IPv6 addresses, using IPv4-mapped IPv6 addresses",
++ "IM", "ipv6-mapped", &ov_ip6mapped, adns_qf_ip6mapped },
++
+ { ot_desconly, "asynchronous/pipe mode options:" },
+ { ot_funcarg, "Set <id>, default is decimal sequence starting 0",
+ 0, "asynch-id", 0,0, &of_asynch_id, "id" },
+diff --git a/client/adh-query.c b/client/adh-query.c
+index 125bb33..2186004 100644
+--- a/client/adh-query.c
++++ b/client/adh-query.c
+@@ -92,24 +92,37 @@ static void prep_query(struct query_node **qun_r, int *quflags_r) {
+ (ov_qc_query ? adns_qf_quoteok_query : 0) |
+ (ov_qc_anshost ? adns_qf_quoteok_anshost : 0) |
+ (ov_qc_cname ? 0 : adns_qf_quoteok_cname) |
++ ov_ipflags | ov_ip6mapped |
+ ov_cname,
+
+ *qun_r= qun;
+ }
+
++static int a2addr(adns_rr_addr *rr, const char *addr) {
++ char *p;
++ if (strchr(addr, ':')) {
++ memset(&rr->addr.inet6, 0, sizeof(rr->addr.inet6));
++ rr->addr.sa.sa_family = AF_INET6;
++ p = (char *) &rr->addr.inet6.sin6_addr;
++ }
++ else {
++ memset(&rr->addr.inet, 0, sizeof(rr->addr.inet));
++ rr->addr.sa.sa_family = AF_INET;
++ p = (char *) &rr->addr.inet.sin_addr;
++ }
++ return inet_pton(rr->addr.sa.sa_family, addr, p) > 0;
++}
++
+ void of_ptr(const struct optioninfo *oi, const char *arg, const char *arg2) {
+ struct query_node *qun;
+ int quflags, r;
+- struct sockaddr_in sa;
+-
+- memset(&sa,0,sizeof(sa));
+- sa.sin_family= AF_INET;
+- if (!inet_aton(arg,&sa.sin_addr)) usageerr("invalid IP address %s",arg);
++ adns_rr_addr rr;
+
++ if (!a2addr(&rr, arg)) usageerr("invalid IP address %s",arg);
+ prep_query(&qun,&quflags);
+ qun->owner= xstrsave(arg);
+ r= adns_submit_reverse(ads,
+- (struct sockaddr*)&sa,
++ &rr.addr.sa,
+ ov_type == adns_r_none ? adns_r_ptr : ov_type,
+ quflags,
+ qun,
+@@ -122,17 +135,14 @@ void of_ptr(const struct optioninfo *oi, const char *arg, const char *arg2) {
+ void of_reverse(const struct optioninfo *oi, const char *arg, const char *arg2) {
+ struct query_node *qun;
+ int quflags, r;
+- struct sockaddr_in sa;
+-
+- memset(&sa,0,sizeof(sa));
+- sa.sin_family= AF_INET;
+- if (!inet_aton(arg,&sa.sin_addr)) usageerr("invalid IP address %s",arg);
++ adns_rr_addr rr;
+
++ if (!a2addr(&rr, arg)) usageerr("invalid IP address %s",arg);
+ prep_query(&qun,&quflags);
+ qun->owner= xmalloc(strlen(arg) + strlen(arg2) + 2);
+ sprintf(qun->owner, "%s %s", arg,arg2);
+ r= adns_submit_reverse_any(ads,
+- (struct sockaddr*)&sa, arg2,
++ &rr.addr.sa, arg2,
+ ov_type == adns_r_none ? adns_r_txt : ov_type,
+ quflags,
+ qun,
+diff --git a/client/adnshost.h b/client/adnshost.h
+index fcc96a3..7e2341a 100644
+--- a/client/adnshost.h
++++ b/client/adnshost.h
+@@ -81,6 +81,8 @@ extern int ov_verbose;
+ extern adns_rrtype ov_type;
+ extern int ov_search, ov_qc_query, ov_qc_anshost, ov_qc_cname;
+ extern int ov_tcp, ov_cname, ov_format;
++extern int ov_ipflags;
++extern int ov_ip6mapped;
+ extern char *ov_id;
+ extern struct perqueryflags_remember ov_pqfr;
+
+diff --git a/client/adnstest.c b/client/adnstest.c
+index 550cf27..ae70285 100644
+--- a/client/adnstest.c
++++ b/client/adnstest.c
+@@ -119,13 +119,16 @@ static const adns_rrtype defaulttypes[]= {
+ adns_r_ptr_raw,
+ adns_r_hinfo,
+ adns_r_mx_raw,
++ adns_r_srv_raw,
+ adns_r_txt,
+ adns_r_rp_raw,
++ adns_r_aaaa, /* Does the order matter? */
+
+ adns_r_addr,
+ adns_r_ns,
+ adns_r_ptr,
+ adns_r_mx,
++ adns_r_srv,
+
+ adns_r_soa,
+ adns_r_rp,
+diff --git a/regress/case-connfail.sys b/regress/case-connfail.sys
+index b62923b..2064368 100644
+--- a/regress/case-connfail.sys
++++ b/regress/case-connfail.sys
+@@ -3,76 +3,88 @@
+ start 1056289303.784817
+ socket type=SOCK_DGRAM
+ socket=6
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000031
+ fcntl fd=6 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000010
+ fcntl fd=6 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000007
+ socket type=SOCK_STREAM
+- socket=7
++ socket=8
+ +0.000059
+- fcntl fd=7 cmd=F_GETFL
++ fcntl fd=8 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000007
+- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000006
+- connect fd=7 addr=172.18.45.36:53
++ connect fd=8 addr=172.18.45.36:53
+ connect=ENOTSOCK
+ +0.000013
+- close fd=7
++ close fd=8
+ close=OK
+ +0.000031
+ socket type=SOCK_STREAM
+- socket=7
++ socket=8
+ +0.000035
+- fcntl fd=7 cmd=F_GETFL
++ fcntl fd=8 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000006
+- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000007
+- connect fd=7 addr=172.18.45.6:53
++ connect fd=8 addr=172.18.45.6:53
+ connect=ENOTSOCK
+ +0.000008
+- close fd=7
++ close fd=8
+ close=OK
+ +0.000013
+- select max=7 rfds=[6] wfds=[] efds=[] to=0.000000
++ select max=8 rfds=[6,7] wfds=[] efds=[] to=0.000000
+ select=0 rfds=[] wfds=[] efds=[]
+ +0.000036
+ socket type=SOCK_STREAM
+- socket=7
++ socket=8
+ +0.000036
+- fcntl fd=7 cmd=F_GETFL
++ fcntl fd=8 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000007
+- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000006
+- connect fd=7 addr=172.18.45.36:53
++ connect fd=8 addr=172.18.45.36:53
+ connect=ENOTSOCK
+ +0.000008
+- close fd=7
++ close fd=8
+ close=OK
+ +0.000013
+ socket type=SOCK_STREAM
+- socket=7
++ socket=8
+ +0.000036
+- fcntl fd=7 cmd=F_GETFL
++ fcntl fd=8 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000007
+- fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl fd=8 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
+ +0.000006
+- connect fd=7 addr=172.18.45.6:53
++ connect fd=8 addr=172.18.45.6:53
+ connect=ENOTSOCK
+ +0.000008
+- close fd=7
++ close fd=8
+ close=OK
+ +0.000012
+ close fd=6
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000023
+diff --git a/regress/case-flags10.sys b/regress/case-flags10.sys
+index fe0b341..99f1f8b 100644
+--- a/regress/case-flags10.sys
++++ b/regress/case-flags10.sys
+@@ -3,13 +3,25 @@ adnstest default
+ start 929580072.670441
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000191
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000084
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000061
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000001
+diff --git a/regress/case-longdom1.sys b/regress/case-longdom1.sys
+index a54e14d..6920322 100644
+--- a/regress/case-longdom1.sys
++++ b/regress/case-longdom1.sys
+@@ -3,13 +3,25 @@ adnstest default
+ start 951955690.505811
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000126
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000058
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000035
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000269
+diff --git a/regress/case-longdomsrch0.sys b/regress/case-longdomsrch0.sys
+index 298bec8..94be025 100644
+--- a/regress/case-longdomsrch0.sys
++++ b/regress/case-longdomsrch0.sys
+@@ -3,13 +3,25 @@ adnstest ndots100
+ start 951956073.321566
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000131
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000056
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000034
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000340
+diff --git a/regress/case-longlab1.sys b/regress/case-longlab1.sys
+index 5b0e46a..d832c9d 100644
+--- a/regress/case-longlab1.sys
++++ b/regress/case-longlab1.sys
+@@ -3,13 +3,25 @@ adnstest default
+ start 951955261.286712
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000128
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000053
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000033
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000238
+diff --git a/regress/case-tcpmultipart.sys b/regress/case-tcpmultipart.sys
+index d26ded2..00e2488 100644
+--- a/regress/case-tcpmultipart.sys
++++ b/regress/case-tcpmultipart.sys
+@@ -3,12 +3,21 @@ adnstest tunnel
+ start 938365454.994875
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000164
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000055
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000043
+ socket type=SOCK_STREAM
+ socket=5
+@@ -22,7 +31,7 @@ adnstest tunnel
+ connect fd=5 addr=172.31.80.9:53
+ connect=EINPROGRESS
+ +0.000414
+- select max=6 rfds=[4] wfds=[5] efds=[] to=13.998324
++ select max=8 rfds=[4,7] wfds=[5] efds=[] to=13.998324
+ select=1 rfds=[] wfds=[5] efds=[]
+ +1.-647444
+ read fd=5 buflen=1
+@@ -43,7 +52,7 @@ adnstest tunnel
+ 2d616464 72046172 70610000 0c0001.
+ write=47
+ +0.000273
+- select max=6 rfds=[4,5] wfds=[] efds=[5] to=29.644233
++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=29.644233
+ select=1 rfds=[5] wfds=[] efds=[]
+ +0.538651
+ read fd=5 buflen=2
+@@ -66,7 +75,7 @@ adnstest tunnel
+ read fd=5 buflen=297
+ read=EAGAIN
+ +0.000476
+- select max=6 rfds=[4,5] wfds=[] efds=[5] to=29.105246
++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=29.105246
+ select=1 rfds=[5] wfds=[] efds=[]
+ +1.-401146
+ read fd=5 buflen=297
+@@ -109,7 +118,7 @@ adnstest tunnel
+ read fd=5 buflen=2572
+ read=EAGAIN
+ +0.000101
+- select max=6 rfds=[4,5] wfds=[] efds=[5] to=28.502804
++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=28.502804
+ select=1 rfds=[5] wfds=[] efds=[]
+ +0.336462
+ read fd=5 buflen=2572
+@@ -148,7 +157,7 @@ adnstest tunnel
+ read fd=5 buflen=1624
+ read=EAGAIN
+ +0.000124
+- select max=6 rfds=[4,5] wfds=[] efds=[5] to=28.162903
++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=28.162903
+ select=1 rfds=[5] wfds=[] efds=[]
+ +1.-683589
+ read fd=5 buflen=1624
+@@ -187,7 +196,7 @@ adnstest tunnel
+ read fd=5 buflen=676
+ read=EAGAIN
+ +0.000114
+- select max=6 rfds=[4,5] wfds=[] efds=[5] to=27.843177
++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=27.843177
+ select=1 rfds=[5] wfds=[] efds=[]
+ +0.376863
+ read fd=5 buflen=676
+@@ -230,7 +239,7 @@ adnstest tunnel
+ read fd=5 buflen=3248
+ read=EAGAIN
+ +0.000066
+- select max=6 rfds=[4,5] wfds=[] efds=[5] to=27.454446
++ select max=8 rfds=[4,5,7] wfds=[] efds=[5] to=27.454446
+ select=1 rfds=[5] wfds=[] efds=[]
+ +0.316770
+ read fd=5 buflen=3248
+@@ -242,6 +251,9 @@ adnstest tunnel
+ +0.000429
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000375
+ close fd=5
+ close=OK
+diff --git a/regress/case-timeout.sys b/regress/case-timeout.sys
+index f810c3b..756becc 100644
+--- a/regress/case-timeout.sys
++++ b/regress/case-timeout.sys
+@@ -3,19 +3,28 @@ adnstest noserver
+ start 912889153.349504
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000193
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000088
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000072
+ sendto fd=4 addr=172.18.45.36:53
+ 311f0100 00010000 00000000 06636869 61726b08 67726565 6e656e64 036f7267
+ 02756b00 00010001.
+ sendto=40
+ +0.000617
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999383
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999383
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.008683
+ sendto fd=4 addr=172.18.45.36:53
+@@ -23,7 +32,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000406
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999594
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999594
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009544
+ sendto fd=4 addr=172.18.45.36:53
+@@ -31,7 +40,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000428
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999572
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999572
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009567
+ sendto fd=4 addr=172.18.45.36:53
+@@ -39,7 +48,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000449
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999551
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999551
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009551
+ sendto fd=4 addr=172.18.45.36:53
+@@ -47,7 +56,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000381
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999619
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999619
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009614
+ sendto fd=4 addr=172.18.45.36:53
+@@ -55,7 +64,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000383
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999617
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999617
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009622
+ sendto fd=4 addr=172.18.45.36:53
+@@ -63,7 +72,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000387
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999613
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999613
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009603
+ sendto fd=4 addr=172.18.45.36:53
+@@ -71,7 +80,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000404
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999596
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999596
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009607
+ sendto fd=4 addr=172.18.45.36:53
+@@ -79,7 +88,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000468
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999532
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999532
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009526
+ sendto fd=4 addr=172.18.45.36:53
+@@ -87,7 +96,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000431
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999569
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999569
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009564
+ sendto fd=4 addr=172.18.45.36:53
+@@ -95,7 +104,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000429
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999571
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999571
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009586
+ sendto fd=4 addr=172.18.45.36:53
+@@ -103,7 +112,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000479
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999521
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999521
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009511
+ sendto fd=4 addr=172.18.45.36:53
+@@ -111,7 +120,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000430
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999570
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999570
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009571
+ sendto fd=4 addr=172.18.45.36:53
+@@ -119,7 +128,7 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000440
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999560
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999560
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009564
+ sendto fd=4 addr=172.18.45.36:53
+@@ -127,9 +136,12 @@ adnstest noserver
+ 02756b00 00010001.
+ sendto=40
+ +0.000439
+- select max=5 rfds=[4] wfds=[] efds=[] to=1.999561
++ select max=8 rfds=[4,7] wfds=[] efds=[] to=1.999561
+ select=0 rfds=[] wfds=[] efds=[]
+ +2.009554
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000267
+diff --git a/regress/case-unknownq.sys b/regress/case-unknownq.sys
+index 736210d..052c028 100644
+--- a/regress/case-unknownq.sys
++++ b/regress/case-unknownq.sys
+@@ -3,13 +3,25 @@ adnstest default
+ start 933811310.565828
+ socket type=SOCK_DGRAM
+ socket=4
++ +0.000066
++ socket type=SOCK_DGRAM
++ socket=7
+ +0.000264
+ fcntl fd=4 cmd=F_GETFL
+ fcntl=~O_NONBLOCK&...
+ +0.000087
+ fcntl fd=4 cmd=F_SETFL O_NONBLOCK|...
+ fcntl=OK
++ +0.000264
++ fcntl fd=7 cmd=F_GETFL
++ fcntl=~O_NONBLOCK&...
++ +0.000087
++ fcntl fd=7 cmd=F_SETFL O_NONBLOCK|...
++ fcntl=OK
+ +0.000067
+ close fd=4
+ close=OK
++ +0.000067
++ close fd=7
++ close=OK
+ +0.000307
+diff --git a/regress/hcommon.c b/regress/hcommon.c
+index 0324e58..60cd7cc 100644
+--- a/regress/hcommon.c
++++ b/regress/hcommon.c
+@@ -144,10 +144,18 @@ void Qwrite( int fd , const void *buf , size_t len ) {
+ Q_vb();
+ }
+ void Tvbaddr(const struct sockaddr *addr, int len) {
+- const struct sockaddr_in *ai= (const struct sockaddr_in*)addr;
+- assert(len==sizeof(struct sockaddr_in));
+- assert(ai->sin_family==AF_INET);
+- Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port));
++ if(addr->sa_family==AF_INET) {
++ const struct sockaddr_in *ai= (const struct sockaddr_in*)addr;
++ assert(len==sizeof(struct sockaddr_in));
++ assert(ai->sin_family==AF_INET);
++ Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port));
++ } else {
++ char buf[INET6_ADDRSTRLEN];
++ const struct sockaddr_in6 *ai6= (const struct sockaddr_in6*)addr;
++ assert(len==sizeof(struct sockaddr_in6));
++ assert(ai6->sin6_family==AF_INET6);
++ Tvbf("%s:%u",inet_ntop(AF_INET6, &(ai6->sin6_addr), buf, sizeof(buf)),htons(ai6->sin6_port));
++ }
+ }
+ void Tvbbytes(const void *buf, int len) {
+ const byte *bp;
+diff --git a/regress/hcommon.c.m4 b/regress/hcommon.c.m4
+index 0f205fe..5de19d4 100644
+--- a/regress/hcommon.c.m4
++++ b/regress/hcommon.c.m4
+@@ -129,11 +129,18 @@ m4_define(`hm_specsyscall', `')
+ m4_include(`hsyscalls.i4')
+
+ void Tvbaddr(const struct sockaddr *addr, int len) {
+- const struct sockaddr_in *ai= (const struct sockaddr_in*)addr;
+-
+- assert(len==sizeof(struct sockaddr_in));
+- assert(ai->sin_family==AF_INET);
+- Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port));
++ if(addr->sa_family==AF_INET) {
++ const struct sockaddr_in *ai= (const struct sockaddr_in*)addr;
++ assert(len==sizeof(struct sockaddr_in));
++ assert(ai->sin_family==AF_INET);
++ Tvbf("%s:%u",inet_ntoa(ai->sin_addr),htons(ai->sin_port));
++ } else {
++ char buf[INET6_ADDRSTRLEN];
++ const struct sockaddr_in6 *ai6= (const struct sockaddr_in6*)addr;
++ assert(len==sizeof(struct sockaddr_in6));
++ assert(ai6->sin6_family==AF_INET6);
++ Tvbf("%s:%u",inet_ntop(AF_INET6, &(ai6->sin6_addr), buf, sizeof(buf)),htons(ai6->sin6_port));
++ }
+ }
+
+ void Tvbbytes(const void *buf, int len) {
+diff --git a/regress/hplayback.c b/regress/hplayback.c
+index 594f7e6..e0e2246 100644
+--- a/regress/hplayback.c
++++ b/regress/hplayback.c
+@@ -153,22 +153,42 @@ static void Ppollfds(struct pollfd *fds, int nfds) {
+ }
+ #endif
+ static void Paddr(struct sockaddr *addr, int *lenr) {
++ struct sockaddr_in6 *sa6= (struct sockaddr_in6*)addr;
+ struct sockaddr_in *sa= (struct sockaddr_in*)addr;
+ char *p, *ep;
+ long ul;
+ assert(*lenr >= sizeof(*sa));
+- p= strchr(vb2.buf+vb2.used,':');
+- if (!p) Psyntax("no port on address");
+- *p++= 0;
+ memset(sa,0,sizeof(*sa));
+- sa->sin_family= AF_INET;
+- if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)) Psyntax("invalid address");
+- ul= strtoul(p,&ep,10);
+- if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)");
+- if (ul >= 65536) Psyntax("port too large");
+- sa->sin_port= htons(ul);
+- *lenr= sizeof(*sa);
+- vb2.used= ep - (char*)vb2.buf;
++ if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)){
++ p= strchr(vb2.buf+vb2.used,':');
++ if (!p) Psyntax("no port on address");
++ *p++= 0;
++ sa->sin_family= AF_INET;
++ ul= strtoul(p,&ep,10);
++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)");
++ if (ul >= 65536) Psyntax("port too large");
++ sa->sin_port= htons(ul);
++ *lenr= sizeof(*sa);
++ vb2.used= ep - (char*)vb2.buf;
++ return;
++ } else {
++ assert(*lenr >= sizeof(*sa6));
++ memset(sa6,0,sizeof(*sa6));
++ if (!inet_pton(AF_INET6, vb2.buf+vb2.used, &sa6->sin6_addr)) {
++ p= strrchr(vb2.buf+vb2.used,':');
++ if (!p) Psyntax("no port on address");
++ *p++= 0;
++ sa6->sin6_family= AF_INET6;
++ ul= strtoul(p,&ep,10);
++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)");
++ if (ul >= 65536) Psyntax("port too large");
++ sa6->sin6_port= htons(ul);
++ *lenr= sizeof(*sa6);
++ vb2.used= ep - (char*)vb2.buf;
++ return;
++ }
++ }
++ Psyntax("invalid address");
+ }
+ static int Pbytes(byte *buf, int maxlen) {
+ static const char hexdigits[]= "0123456789abcdef";
+@@ -283,7 +303,6 @@ int Hpoll( struct pollfd *fds , int nfds , int timeout ) {
+ int Hsocket( int domain , int type , int protocol ) {
+ int r, amtread;
+ char *ep;
+- Tmust("socket","domain",domain==AF_INET);
+ Tmust("socket","type",type==SOCK_STREAM || type==SOCK_DGRAM);
+ Qsocket( type );
+ if (!adns__vbuf_ensure(&vb2,1000)) Tnomem();
+diff --git a/regress/hplayback.c.m4 b/regress/hplayback.c.m4
+index 868aa52..d023b7c 100644
+--- a/regress/hplayback.c.m4
++++ b/regress/hplayback.c.m4
+@@ -210,24 +210,42 @@ static void Ppollfds(struct pollfd *fds, int nfds) {
+ #endif
+
+ static void Paddr(struct sockaddr *addr, int *lenr) {
++ struct sockaddr_in6 *sa6= (struct sockaddr_in6*)addr;
+ struct sockaddr_in *sa= (struct sockaddr_in*)addr;
+ char *p, *ep;
+ long ul;
+-
+ assert(*lenr >= sizeof(*sa));
+- p= strchr(vb2.buf+vb2.used,':');
+- if (!p) Psyntax("no port on address");
+- *p++= 0;
+ memset(sa,0,sizeof(*sa));
+- sa->sin_family= AF_INET;
+- if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)) Psyntax("invalid address");
+- ul= strtoul(p,&ep,10);
+- if (*ep && *ep != hm_squote hm_squote) Psyntax("invalid port (bad syntax)");
+- if (ul >= 65536) Psyntax("port too large");
+- sa->sin_port= htons(ul);
+- *lenr= sizeof(*sa);
+-
+- vb2.used= ep - (char*)vb2.buf;
++ if (!inet_aton(vb2.buf+vb2.used,&sa->sin_addr)){
++ p= strchr(vb2.buf+vb2.used,':');
++ if (!p) Psyntax("no port on address");
++ *p++= 0;
++ sa->sin_family= AF_INET;
++ ul= strtoul(p,&ep,10);
++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)");
++ if (ul >= 65536) Psyntax("port too large");
++ sa->sin_port= htons(ul);
++ *lenr= sizeof(*sa);
++ vb2.used= ep - (char*)vb2.buf;
++ return;
++ } else {
++ assert(*lenr >= sizeof(*sa6));
++ memset(sa6,0,sizeof(*sa6));
++ if (!inet_pton(AF_INET6, vb2.buf+vb2.used, &sa6->sin6_addr)) {
++ p= strrchr(vb2.buf+vb2.used,':');
++ if (!p) Psyntax("no port on address");
++ *p++= 0;
++ sa6->sin6_family= AF_INET6;
++ ul= strtoul(p,&ep,10);
++ if (*ep && *ep != ' ') Psyntax("invalid port (bad syntax)");
++ if (ul >= 65536) Psyntax("port too large");
++ sa6->sin6_port= htons(ul);
++ *lenr= sizeof(*sa6);
++ vb2.used= ep - (char*)vb2.buf;
++ return;
++ }
++ }
++ Psyntax("invalid address");
+ }
+
+ static int Pbytes(byte *buf, int maxlen) {
+diff --git a/regress/hrecord.c b/regress/hrecord.c
+index 88e24a4..67dff6f 100644
+--- a/regress/hrecord.c
++++ b/regress/hrecord.c
+@@ -81,7 +81,6 @@ int Hpoll( struct pollfd *fds , int nfds , int timeout ) {
+ #endif
+ int Hsocket( int domain , int type , int protocol ) {
+ int r, e;
+- Tmust("socket","domain",domain==AF_INET);
+ Tmust("socket","type",type==SOCK_STREAM || type==SOCK_DGRAM);
+ Qsocket( type );
+ r= socket( domain , type , protocol );
+diff --git a/regress/hsyscalls.i4 b/regress/hsyscalls.i4
+index ad90104..b5dfc56 100644
+--- a/regress/hsyscalls.i4
++++ b/regress/hsyscalls.i4
+@@ -70,7 +70,7 @@ hm_syscall(
+
+ hm_syscall(
+ socket, `hm_rv_fd', `
+- hm_arg_must(int,domain,AF_INET) hm_na
++ hm_arg_ign(int,domain) hm_na
+ hm_arg_socktype(type) hm_na
+ hm_arg_ign(int,protocol) hm_na
+ ')
+diff --git a/src/adns.h b/src/adns.h
+index 34f9f49..aad05fd 100644
+--- a/src/adns.h
++++ b/src/adns.h
+@@ -71,6 +71,10 @@
+ extern "C" { /* I really dislike this - iwj. */
+ #endif
+
++#ifndef AF_INET6
++#include "adns-in6fake.h"
++#endif
++
+ /* All struct in_addr anywhere in adns are in NETWORK byte order. */
+
+ typedef struct adns__state *adns_state;
+@@ -87,7 +91,10 @@ typedef enum { /* In general, or together the desired flags: */
+ adns_if_eintr= 0x0020,/* allow _wait and _synchronous to return EINTR */
+ adns_if_nosigpipe= 0x0040,/* applic has SIGPIPE ignored, do not protect */
+ adns_if_checkc_entex=0x0100,/* consistency checks on entry/exit to adns fns */
+- adns_if_checkc_freq= 0x0300 /* consistency checks very frequently (slow!) */
++ adns_if_checkc_freq= 0x0300,/* consistency checks very frequently (slow!) */
++ adns_if_ip4only= 0x1000,/* make default be adns_qf_ip4 */
++ adns_if_ip6only= 0x2000,/* make default be adns_qf_ip6 */
++ adns_if_ip6mapped= 0x4000,/* make default be adns_qf_ip4|adns_qf_ip6|adns_qf_ip6mapped */
+ } adns_initflags;
+
+ typedef enum { /* In general, or together the desired flags: */
+@@ -101,9 +108,54 @@ typedef enum { /* In general, or together the desired flags: */
+ adns_qf_quotefail_cname=0x00000080,/* refuse if quote-req chars in CNAME we go via */
+ adns_qf_cname_loose= 0x00000100,/* allow refs to CNAMEs - without, get _s_cname */
+ adns_qf_cname_forbid= 0x00000200,/* don't follow CNAMEs, instead give _s_cname */
++
++ /* Affects addr queries and additional section processing */
++ adns_qf_ip4= 0x00001000, /* Ask for A records */
++ adns_qf_ip6= 0x00002000, /* Ask for AAAA records */
++ adns_qf_ip6mapped= 0x00004000, /* Return any IPv4 addresses as IPv6 mapped addresses */
++
++ adns__qf_ip_mask= 0x00003000,
+ adns__qf_internalmask= 0x0ff00000
+ } adns_queryflags;
+
++/* IPv6 support:
++ *
++ * The _qf_ip4 and _qf_ip6 says which kinds of address records (A and
++ * AAAA) we should ask for. _qf_ip6mapped says how we return ipv6
++ * addresses to the caller. Four modes of operation, corresponding to
++ * the _if_ip* flags:
++ *
++ * Record type: A AAAA
++ * flags:
++ *
++ * Default => AF_INET => AF_INET6
++ *
++ * _if_ip4only => AF_INET not used
++ *
++ * _if_ip6only not used => AF_INET6
++ *
++ * _if_ipv6mapped => AF_INET6 => AF_INET6
++ *
++ * _if_ip4only => AF_INET6 not used
++ * | _if_ipv6mapped
++ *
++ * Furthermore, there are configuration options which can prevent the
++ * use of either AAAA or A records for _r_addr; so it is safe to use
++ * _qf_ip6_mapped and _r_addr without checking explicitly whether the host
++ * has IPv6 connectivity.
++ *
++ * The corresponding _qf_ip* flags are constructed from the _if_ip*
++ * flags and the query flags submitted to functions like adns_submit.
++ * If none of _qf_ip4 and _qf_ip6 are set explicitly in the query
++ * flags, the default behaviour is used. If the flags are set, the
++ * default configuration is overridden.
++ *
++ * Applications which do not support IPv4 should set none of these
++ * flags. Applications which have been `naively' converted to use
++ * AF_INET6 throughout should set adns_if_ip6. Applications which
++ * know what they are doing should know which flags to set :-).
++ */
++
+ typedef enum {
+ adns_rrt_typemask= 0x0ffff,
+ adns__qtf_deref= 0x10000,/* dereference domains; perhaps get extra data */
+@@ -127,6 +179,8 @@ typedef enum {
+ *
+ * Don't forget adns_qf_quoteok if that's what you want. */
+
++ adns__qtf_special= 0x80000,/* no simple correspondence to a single rr type */
++
+ adns_r_none= 0,
+
+ adns_r_a= 1,
+@@ -151,6 +205,7 @@ typedef enum {
+
+ adns_r_rp_raw= 17,
+ adns_r_rp= adns_r_rp_raw|adns__qtf_mail822,
++ adns_r_aaaa= 28, /* RFC 1886 */
+
+ /* For SRV records, query domain without _qf_quoteok_query must look
+ * as expected from SRV RFC with hostname-like Name. _With_
+@@ -158,7 +213,8 @@ typedef enum {
+ adns_r_srv_raw= 33,
+ adns_r_srv= adns_r_srv_raw|adns__qtf_deref,
+
+- adns_r_addr= adns_r_a|adns__qtf_deref
++ /* FIXME: Maybe add adns__qtf_deref too? */
++ adns_r_addr= 1 | adns__qtf_special,
+
+ } adns_rrtype;
+
+@@ -284,9 +340,13 @@ typedef enum {
+
+ typedef struct {
+ int len;
++#if 0
++ int order; /* Cache index on sortlist? */
++#endif
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in inet;
++ struct sockaddr_in6 inet6;
+ } addr;
+ } adns_rr_addr;
+
+@@ -355,6 +415,7 @@ typedef struct {
+ adns_rr_intstr *(*manyistr); /* txt (list strs ends with i=-1, str=0)*/
+ adns_rr_addr *addr; /* addr */
+ struct in_addr *inaddr; /* a */
++ struct in6_addr *in6addr; /* aaaa */
+ adns_rr_hostaddr *hostaddr; /* ns */
+ adns_rr_intstrpair *intstrpair; /* hinfo */
+ adns_rr_strpair *strpair; /* rp, rp_raw */
+@@ -506,6 +567,13 @@ int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
+ * setting of adns_if_check_entex, adns_if_check_freq, or neither,
+ * in the flags passed to adns_init.
+ *
++ * in6only
++ * in4only
++ * Return only IPv6, respectively only IPv4 addresses, in
++ * _rr_addr's. This may result in an adns_s_nodata error, if the
++ * application only supports, or the remote host only has, the wrong
++ * kind of address.
++ *
+ * There are a number of environment variables which can modify the
+ * behaviour of adns. They take effect only if adns_init is used, and
+ * the caller of adns_init can disable them using adns_if_noenv. In
+@@ -589,7 +657,33 @@ int adns_submit_reverse(adns_state ads,
+ void *context,
+ adns_query *query_r);
+ /* type must be _r_ptr or _r_ptr_raw. _qf_search is ignored.
+- * addr->sa_family must be AF_INET or you get ENOSYS.
++ * addr->sa_family must be AF_INET or AF_INET6 or you get ENOSYS.
++ */
++
++int adns_getaddrinfo(adns_state ads,
++ const char *name, /* Eg, "www.example.coom" */
++ const char *service, /* Eg, "http" */
++ const char *protocol, /* Eg, "tcp" */
++ unsigned short defaultport, /* Eg, 80 */
++ adns_queryflags flags,
++ adns_answer **answer_r, int *invented_r);
++/* Does an SRV lookup (RFC2052). If this fails, tries an AAAA or A
++ * lookup instead, and if found uses getservbyname to find the port
++ * number (or failing that, uses defaultport. The defaultport is in
++ * hot byte order). In the `fallback' case, will invent an SRV record
++ * which have priority and weight == 0 and set *invented_r to 1; if
++ * real SRV records were found, will set *invented_r to 0. invented_r
++ * may be null but answer_r may not be. If _getaddrinfo returns
++ * nonzero, *answer_r and/or *invented_r may or may not have been
++ * overwritten and should not be used.
++ *
++ * NB, like adns_synchronous, can fail either by returning an errno
++ * value, or by returning an adns_answer with ->nrrs==0 and
++ * ->status!=0.
++ *
++ * You have to write two loops when using the returned value, an outer
++ * one to loop over the returned SRV's, and an inner one to loop over
++ * the addresses for each one.
+ */
+
+ int adns_submit_reverse_any(adns_state ads,
+@@ -602,7 +696,7 @@ int adns_submit_reverse_any(adns_state ads,
+ /* For RBL-style reverse `zone's; look up
+ * <reversed-address>.<zone>
+ * Any type is allowed. _qf_search is ignored.
+- * addr->sa_family must be AF_INET or you get ENOSYS.
++ * addr->sa_family must be AF_INET or AF_INET6 or you get ENOSYS.
+ */
+
+ void adns_finish(adns_state ads);
+@@ -830,7 +924,7 @@ int adns_beforepoll(adns_state ads, struct pollfd *fds,
+ * In any case this call won't block.
+ */
+
+-#define ADNS_POLLFDS_RECOMMENDED 2
++#define ADNS_POLLFDS_RECOMMENDED 3
+ /* If you allocate an fds buf with at least RECOMMENDED entries then
+ * you are unlikely to need to enlarge it. You are recommended to do
+ * so if it's convenient. However, you must be prepared for adns to
+diff --git a/src/check.c b/src/check.c
+index 41cdde5..704a15e 100644
+--- a/src/check.c
++++ b/src/check.c
+@@ -4,6 +4,7 @@
+ */
+ /*
+ * This file is part of adns, which is
++ * Copyright (C) 2009 Luca Bruno
+ * Copyright (C) 1997-2000,2003,2006 Ian Jackson
+ * Copyright (C) 1999-2000,2003,2006 Tony Finch
+ * Copyright (C) 1991 Massachusetts Institute of Technology
+@@ -24,6 +25,8 @@
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
++#include <stdlib.h>
++
+ #include "internal.h"
+
+ void adns_checkconsistency(adns_state ads, adns_query qu) {
+@@ -77,11 +80,11 @@ static void checkc_notcpbuf(adns_state ads) {
+ static void checkc_global(adns_state ads) {
+ int i;
+
+- assert(ads->udpsocket >= 0);
+-
++ assert((ads->udpsocket >= 0) || (ads->udpsocket6 >= 0));
++#if 0
+ for (i=0; i<ads->nsortlist; i++)
+ assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr));
+-
++#endif
+ assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers);
+
+ switch (ads->tcpstate) {
+diff --git a/src/event.c b/src/event.c
+index ad5861e..3fb95a1 100644
+--- a/src/event.c
++++ b/src/event.c
+@@ -6,6 +6,7 @@
+ */
+ /*
+ * This file is part of adns, which is
++ * Copyright (C) 2009 Luca Bruno
+ * Copyright (C) 1997-2000,2003,2006 Ian Jackson
+ * Copyright (C) 1999-2000,2003,2006 Tony Finch
+ * Copyright (C) 1991 Massachusetts Institute of Technology
+@@ -100,6 +101,7 @@ static void tcp_broken_events(adns_state ads) {
+ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+ int r, fd, tries;
+ struct sockaddr_in addr;
++ struct sockaddr_in6 addr6;
+ struct protoent *proto;
+
+ for (tries=0; tries<ads->nservers; tries++) {
+@@ -123,7 +125,7 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+ adns__diag(ads,-1,0,"unable to find protocol no. for TCP !");
+ return;
+ }
+- fd= socket(AF_INET,SOCK_STREAM,proto->p_proto);
++ fd= socket(ads->servers[ads->tcpserver].sin_family,SOCK_STREAM,proto->p_proto);
+ if (fd<0) {
+ adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno));
+ return;
+@@ -135,11 +137,19 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+ close(fd);
+ return;
+ }
+- memset(&addr,0,sizeof(addr));
+- addr.sin_family= AF_INET;
+- addr.sin_port= htons(DNS_PORT);
+- addr.sin_addr= ads->servers[ads->tcpserver].addr;
+- r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
++ if(ads->servers[ads->tcpserver].sin_family==AF_INET) {
++ memset(&addr,0,sizeof(addr));
++ addr.sin_family= AF_INET;
++ addr.sin_port= htons(DNS_PORT);
++ addr.sin_addr= ads->servers[ads->tcpserver].addr;
++ r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
++ } else {
++ memset(&addr6,0,sizeof(addr6));
++ addr6.sin6_family= AF_INET6;
++ addr6.sin6_port= htons(DNS_PORT);
++ addr6.sin6_addr= ads->servers[ads->tcpserver].addr6;
++ r= connect(fd,(const struct sockaddr*)&addr6,sizeof(addr6));
++ }
+ ads->tcpsocket= fd;
+ ads->tcpstate= server_connecting;
+ if (r==0) { tcp_connected(ads,now); return; }
+@@ -311,34 +321,41 @@ void adns_processtimeouts(adns_state ads, const struct timeval *now) {
+ int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
+ /* Returns the number of entries filled in. Always zeroes revents. */
+
+- assert(MAX_POLLFDS==2);
+-
+- pollfds_buf[0].fd= ads->udpsocket;
+- pollfds_buf[0].events= POLLIN;
+- pollfds_buf[0].revents= 0;
++ assert(MAX_POLLFDS==3);
++ if (ads->udpsocket >= 0) {
++ pollfds_buf[0].fd= ads->udpsocket;
++ pollfds_buf[0].events= POLLIN;
++ pollfds_buf[0].revents= 0;
++ }
++ if(ads->udpsocket6 >= 0) {
++ pollfds_buf[1].fd= ads->udpsocket6;
++ pollfds_buf[1].events= POLLIN;
++ pollfds_buf[1].revents= 0;
++ }
+
+ switch (ads->tcpstate) {
+ case server_disconnected:
+ case server_broken:
+- return 1;
++ return 2;
+ case server_connecting:
+- pollfds_buf[1].events= POLLOUT;
++ pollfds_buf[2].events= POLLOUT;
+ break;
+ case server_ok:
+- pollfds_buf[1].events=
++ pollfds_buf[2].events=
+ ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
+ break;
+ default:
+ abort();
+ }
+- pollfds_buf[1].fd= ads->tcpsocket;
+- return 2;
++ pollfds_buf[2].fd= ads->tcpsocket;
++ return 3;
+ }
+
+ int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
+ int want, dgramlen, r, udpaddrlen, serv, old_skip;
+ byte udpbuf[DNS_MAXUDP];
+ struct sockaddr_in udpaddr;
++ struct sockaddr_in6 udpaddr6;
+
+ adns__consistency(ads,0,cc_entex);
+
+@@ -431,6 +448,48 @@ int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
+ adns__procdgram(ads,udpbuf,r,serv,0,*now);
+ }
+ }
++ else if (fd == ads->udpsocket6) {
++ for (;;) {
++ udpaddrlen= sizeof(udpaddr6);
++ r= recvfrom(ads->udpsocket6,udpbuf,sizeof(udpbuf),0,
++ (struct sockaddr*)&udpaddr6,&udpaddrlen);
++ if (r<0) {
++ if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; }
++ if (errno == EINTR) continue;
++ if (errno_resources(errno)) { r= errno; goto xit; }
++ adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
++ r= 0; goto xit;
++ }
++ if (udpaddrlen != sizeof(udpaddr6)) {
++ adns__diag(ads,-1,0,"datagram received with wrong address length %d"
++ " (expected %lu)", udpaddrlen,
++ (unsigned long)sizeof(udpaddr6));
++ continue;
++ }
++ if (udpaddr6.sin6_family != AF_INET6) {
++ adns__diag(ads,-1,0,"datagram received with wrong protocol family"
++ " %u (expected %u)",udpaddr6.sin6_family,AF_INET6);
++ continue;
++ }
++ if (ntohs(udpaddr6.sin6_port) != DNS_PORT) {
++ adns__diag(ads,-1,0,"datagram received from wrong port"
++ " %u (expected %u)", ntohs(udpaddr6.sin6_port),DNS_PORT);
++ continue;
++ }
++ for (serv= 0;
++ serv < ads->nservers &&
++ (memcmp(&(ads->servers[serv].addr6.s6_addr), &(udpaddr6.sin6_addr.s6_addr), sizeof(struct in6_addr)));
++ serv++);
++ if (serv >= ads->nservers) {
++ char buf_dst[INET6_ADDRSTRLEN];
++ adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
++ inet_ntop(AF_INET6, &(udpaddr6.sin6_addr), buf_dst, INET6_ADDRSTRLEN*sizeof(char)));
++ continue;
++ }
++ adns__procdgram(ads,udpbuf,r,serv,0,*now);
++ }
++ }
++
+ r= 0;
+ xit:
+ adns__consistency(ads,0,cc_entex);
+diff --git a/src/internal.h b/src/internal.h
+index 58cd15d..e4e56c9 100644
+--- a/src/internal.h
++++ b/src/internal.h
+@@ -129,6 +129,16 @@ typedef struct typeinfo {
+ * and will not be null-terminated by convstring.
+ */
+
++ void (*submithook)(adns_query qu,
++ /* FIXME: Do we need to pass flags? Isn't qu->flags enough? */
++ adns_queryflags flags,
++ struct timeval now);
++ /* If NULL, submitting a query means to format it and send it over
++ * the wire. If non-NULL, the labels are written to qu->vb, and then
++ * this function is called. It's the hook's responsibility to submit
++ * the query, or submit some other queries and put the original on
++ * the child queue. */
++
+ adns_status (*parse)(const parseinfo *pai, int cbyte,
+ int max, void *store_r);
+ /* Parse one RR, in dgram of length dglen, starting at cbyte and
+@@ -176,6 +186,8 @@ adns_status adns__qdpl_normal(adns_state ads,
+
+ typedef struct allocnode {
+ struct allocnode *next, *back;
++ size_t size;
++ /* Needed for realloc */
+ } allocnode;
+
+ union maxalign {
+@@ -191,11 +203,16 @@ typedef struct {
+ void *ext;
+ void (*callback)(adns_query parent, adns_query child);
+ union {
+- adns_rr_addr ptr_parent_addr;
+ adns_rr_hostaddr *hostaddr;
+ } info;
+ } qcontext;
+
++typedef struct {
++ union {
++ adns_rr_addr ptr_addr;
++ } info;
++} qextra;
++
+ struct adns__query {
+ adns_state ads;
+ enum { query_tosend, query_tcpw, query_childw, query_done } state;
+@@ -242,13 +259,19 @@ struct adns__query {
+ * the vbuf is initialised but empty and everything else is zero.
+ */
+
+- int id, flags, retries;
++ int id;
++ /* -2 at allocation, -1 when done, >= 0 while the query is pending. */
++
++ int flags, retries;
+ int udpnextserver;
+ unsigned long udpsent; /* bitmap indexed by server */
+ struct timeval timeout;
+ time_t expires; /* Earliest expiry time of any record we used. */
+
+ qcontext ctx;
++ /* Information related to the parent of the query */
++ qextra extra;
++ /* Extra information about this query. */
+
+ /* Possible states:
+ *
+@@ -270,34 +293,34 @@ struct adns__query {
+ *
+ * +------------------------+
+ * START -----> | tosend/NONE |
+- * +------------------------+
+- * / |\ \
+- * too big for UDP / UDP timeout \ \ send via UDP
+- * send via TCP / more retries \ \
+- * when conn'd / desired \ \
+- * | | |
+- * v | v
+- * +-----------+ +-------------+
+- * | tcpw/tcpw | ________ | tosend/udpw |
+- * +-----------+ \ +-------------+
+- * | | | UDP timeout | |
+- * | | | no more | |
+- * | | | retries | |
+- * \ | TCP died | desired | |
+- * \ \ no more | | |
+- * \ \ servers | TCP / |
+- * \ \ to try | timeout / |
+- * got \ \ v |_ | got
+- * reply \ _| +------------------+ / reply
+- * \ | done/output FAIL | /
+- * \ +------------------+ /
+- * \ /
+- * _| |_
+- * (..... got reply ....)
+- * / \
++ * _____+------------------------+
++ * consists of __----- / |\ \
++ * child- / / UDP timeout \ \ send via UDP
++ * queries / too big for UDP/ more retries \ \
++ * only / send via TCP / desired \ \
++ * / when conn'd / | |
++ * / |_ | v
++ * | +-----------+ +-------------+
++ * | | tcpw/tcpw | ________ | tosend/udpw |
++ * | +-----------+ \ +-------------+
++ * | | | | UDP timeout | |
++ * | | | | no more | |
++ * | | | | retries | |
++ * | \ | TCP died | desired | |
++ * | \ \ no more | | |
++ * | \ \ servers | TCP / |
++ * | \ \ to try | timeout / |
++ * | got \ \ v |_ | got
++ * | reply \ _| +------------------+ / reply
++ * \ \ | done/output FAIL | /
++ * \ \ +------------------+ /
++ * \ \ /
++ * \ _| |_
++ * \ (..... got reply ....)
++ * \ / \
+ * need child query/ies / \ no child query
+- * / \
+- * |_ _|
++ * \ / \
++ * _| |_ _|
+ * +---------------+ +----------------+
+ * | childw/childw | ----------------> | done/output OK |
+ * +---------------+ children done +----------------+
+@@ -313,7 +336,7 @@ struct adns__state {
+ int configerrno;
+ struct query_queue udpw, tcpw, childw, output;
+ adns_query forallnext;
+- int nextid, udpsocket, tcpsocket;
++ int nextid, udpsocket, udpsocket6, tcpsocket;
+ vbuf tcpsend, tcprecv;
+ int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip;
+ enum adns__tcpstate {
+@@ -330,10 +353,17 @@ struct adns__state {
+ sigset_t stdsigmask;
+ struct pollfd pollfds_buf[MAX_POLLFDS];
+ struct server {
++ sa_family_t sin_family;
+ struct in_addr addr;
++ struct in6_addr addr6;
+ } servers[MAXSERVERS];
+ struct sortlist {
+- struct in_addr base, mask;
++ sa_family_t family;
++ unsigned prefix;
++ union {
++ struct in_addr inet;
++ struct in6_addr inet6;
++ } base;
+ } sortlist[MAXSORTLIST];
+ char **searchlist;
+ unsigned short rand48xsubi[3];
+@@ -401,6 +431,15 @@ void adns__sigpipe_unprotect(adns_state);
+
+ /* From transmit.c: */
+
++adns_status adns__mkquery_labels(adns_state ads, vbuf *vb,
++ const char *owner, int ol,
++ const typeinfo *typei, adns_queryflags flags);
++/* Assembles the owner part of a query packet in vb. */
++
++adns_status adns__mkquery_labels_frdgram(adns_state ads, vbuf *vb,
++ const byte *qd_dgram, int qd_dglen,
++ int qd_begin);
++
+ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+ const char *owner, int ol,
+ const typeinfo *typei, adns_rrtype type,
+@@ -408,6 +447,11 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+ /* Assembles a query packet in vb. A new id is allocated and returned.
+ */
+
++adns_status adns__mkquery_frlabels(adns_state ads, vbuf *vb, int *id_r,
++ char *l, int llen,
++ adns_rrtype type, adns_queryflags flags);
++/* Same as adns__mkquery, but with the labels preformatted. */
++
+ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+ const byte *qd_dgram, int qd_dglen,
+ int qd_begin,
+@@ -447,6 +491,9 @@ adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+ * the memory for it is _taken over_ by this routine whether it
+ * succeeds or fails (if it succeeds, the vbuf is reused for qu->vb).
+ *
++ * For query types with a submithook (i.e. adns_r_addr),
++ * vbuf should contain just the label, not a complete query.
++ *
+ * *ctx is copied byte-for-byte into the query.
+ *
+ * When the child query is done, ctx->callback will be called. The
+@@ -474,6 +521,7 @@ void adns__search_next(adns_state ads, adns_query qu, struct timeval now);
+ */
+
+ void *adns__alloc_interim(adns_query qu, size_t sz);
++void *adns__realloc_interim(adns_query qu, void *p, size_t sz);
+ void *adns__alloc_preserved(adns_query qu, size_t sz);
+ /* Allocates some memory, and records which query it came from
+ * and how much there was.
+diff --git a/src/query.c b/src/query.c
+index d09702e..2894e4d 100644
+--- a/src/query.c
++++ b/src/query.c
+@@ -36,6 +36,10 @@
+
+ #include "internal.h"
+
++#if DMALLOC
++# include <dmalloc.h>
++#endif
++
+ static adns_query query_alloc(adns_state ads,
+ const typeinfo *typei, adns_rrtype type,
+ adns_queryflags flags, struct timeval now) {
+@@ -76,6 +80,7 @@ static adns_query query_alloc(adns_state ads,
+ qu->expires= now.tv_sec + MAXTTLBELIEVE;
+
+ memset(&qu->ctx,0,sizeof(qu->ctx));
++ memset(&qu->extra,0,sizeof(qu->extra));
+
+ qu->answer->status= adns_s_ok;
+ qu->answer->cname= qu->answer->owner= 0;
+@@ -88,6 +93,20 @@ static adns_query query_alloc(adns_state ads,
+ return qu;
+ }
+
++static adns_queryflags default_ip6_flags(adns_state ads)
++{
++ adns_queryflags flags = 0;
++
++ if (!(ads->iflags & adns_if_ip4only))
++ flags |= adns_qf_ip4;
++ if (!(ads->iflags & adns_if_ip6only))
++ flags |= adns_qf_ip6;
++ if (ads->iflags & adns_if_ip6mapped)
++ flags |= adns_qf_ip6mapped;
++
++ return flags;
++}
++
+ static void query_submit(adns_state ads, adns_query qu,
+ const typeinfo *typei, vbuf *qumsg_vb, int id,
+ adns_queryflags flags, struct timeval now) {
+@@ -108,6 +127,7 @@ static void query_submit(adns_state ads, adns_query qu,
+ adns__query_send(qu,now);
+ }
+
++/* FIXME: Take a adns_rrtype type artument? */
+ adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+ const typeinfo *typei, vbuf *qumsg_vb,
+ int id,
+@@ -115,12 +135,26 @@ adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+ const qcontext *ctx) {
+ adns_query qu;
+
++ if (!(flags & adns__qf_ip_mask))
++ flags |= default_ip6_flags(ads);
++
+ qu= query_alloc(ads,typei,typei->typekey,flags,now);
+ if (!qu) { adns__vbuf_free(qumsg_vb); return adns_s_nomemory; }
+ *query_r= qu;
+
+ memcpy(&qu->ctx,ctx,sizeof(qu->ctx));
+- query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
++
++ if (typei->submithook) {
++ qu->vb = *qumsg_vb;
++ adns__vbuf_init(qumsg_vb);
++
++ typei->submithook(qu, flags, now);
++ if (qu->children.head) {
++ qu->state= query_childw;
++ LIST_LINK_TAIL(ads->childw,qu);
++ }
++ }
++ else query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
+
+ return adns_s_ok;
+ }
+@@ -133,21 +167,32 @@ static void query_simple(adns_state ads, adns_query qu,
+ int id;
+ adns_status stat;
+
+- stat= adns__mkquery(ads,&qu->vb,&id, owner,ol,
+- typei,qu->answer->type, flags);
+- if (stat) {
+- if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search)) {
+- adns__search_next(ads,qu,now);
+- return;
+- } else {
+- adns__query_fail(qu,stat);
+- return;
++ if (typei->submithook) {
++ stat= adns__mkquery_labels(ads, &qu->vb, owner, ol, typei, flags);
++ if (stat) goto fail;
++
++ typei->submithook(qu, flags, now);
++ if (qu->children.head) {
++ qu->state= query_childw;
++ LIST_LINK_TAIL(ads->childw,qu);
+ }
++ return;
+ }
++ else {
++ stat= adns__mkquery(ads,&qu->vb,&id, owner,ol,
++ typei,qu->answer->type,flags);
++ if (stat) goto fail;
+
+ vb_new= qu->vb;
+ adns__vbuf_init(&qu->vb);
+ query_submit(ads,qu, typei,&vb_new,id, flags,now);
++ return;
++ }
++ fail:
++ if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search))
++ adns__search_next(ads,qu,now);
++ else
++ adns__query_fail(qu,stat);
+ }
+
+ void adns__search_next(adns_state ads, adns_query qu, struct timeval now) {
+@@ -222,6 +267,9 @@ int adns_submit(adns_state ads,
+
+ adns__consistency(ads,0,cc_entex);
+
++ if (!(flags & adns__qf_ip_mask))
++ flags |= default_ip6_flags(ads);
++
+ typei= adns__findtype(type);
+ if (!typei) return ENOSYS;
+
+@@ -288,13 +336,13 @@ int adns_submit_reverse_any(adns_state ads,
+
+ flags &= ~adns_qf_search;
+
+- if (addr->sa_family != AF_INET) return ENOSYS;
+- iaddr= (const unsigned char*)
+- &(((const struct sockaddr_in*)addr) -> sin_addr);
+-
++ switch (addr->sa_family) {
++ default: return ENOSYS;
++ case AF_INET:
++ iaddr= (const unsigned char*) &((const struct sockaddr_in*)addr)->sin_addr;
+ lreq= strlen(zone) + 4*4 + 1;
+ if (lreq > sizeof(shortbuf)) {
+- buf= malloc(strlen(zone) + 4*4 + 1);
++ buf= malloc(lreq);
+ if (!buf) return errno;
+ buf_free= buf;
+ } else {
+@@ -302,7 +350,32 @@ int adns_submit_reverse_any(adns_state ads,
+ buf_free= 0;
+ }
+ sprintf(buf, "%d.%d.%d.%d.%s", iaddr[3], iaddr[2], iaddr[1], iaddr[0], zone);
+-
++ break;
++ case AF_INET6:
++ iaddr= (const unsigned char*) &((const struct sockaddr_in6*)addr)->sin6_addr;
++ lreq = strlen(zone) + 2*32 + 1;
++ if (lreq > sizeof(shortbuf)) {
++ buf= malloc(lreq);
++ if (!buf) return errno;
++ buf_free= buf;
++ }
++ else {
++ buf= shortbuf;
++ buf_free= 0;
++ }
++ strcpy(buf + 2*32, zone);
++ {
++ int i;
++ const unsigned char *p;
++ static const unsigned char hex[16] = "0123456789abcdef";
++ for (i = 0, p = iaddr + 15; i < 2*32; p--) {
++ buf[i++] = hex[*p & 0xf];
++ buf[i++] = '.';
++ buf[i++] = hex[*p / 0x10];
++ buf[i++] = '.';
++ }
++ }
++ }
+ r= adns_submit(ads,buf,type,flags,context,query_r);
+ free(buf_free);
+ return r;
+@@ -314,9 +387,34 @@ int adns_submit_reverse(adns_state ads,
+ adns_queryflags flags,
+ void *context,
+ adns_query *query_r) {
++ int r;
++ /* Address record used for forward lookup and consistency check */
++ adns_rr_addr rr;
++ const char *zone;
++
+ if (type != adns_r_ptr && type != adns_r_ptr_raw) return EINVAL;
+- return adns_submit_reverse_any(ads,addr,"in-addr.arpa",
++ memset(&rr, 0, sizeof(rr));
++ rr.addr.sa.sa_family = addr->sa_family;
++
++ switch (addr->sa_family) {
++ default: return ENOSYS;
++ case AF_INET:
++ zone = "in-addr.arpa";
++ rr.len = sizeof(rr.addr.inet);
++ rr.addr.inet.sin_addr = ((const struct sockaddr_in *)addr)->sin_addr;
++ break;
++ case AF_INET6:
++ zone = "ip6.arpa";
++ rr.len = sizeof(rr.addr.inet6);
++ rr.addr.inet6.sin6_addr = ((const struct sockaddr_in6 *)addr)->sin6_addr;
++ break;
++ }
++
++ r= adns_submit_reverse_any(ads,addr,zone,
+ type,flags,context,query_r);
++ if (r) return r;
++ (*query_r)->extra.info.ptr_addr = rr;
++ return 0;
+ }
+
+ int adns_synchronous(adns_state ads,
+@@ -344,9 +442,36 @@ static void *alloc_common(adns_query qu, size_t sz) {
+ an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
+ if (!an) return 0;
+ LIST_LINK_TAIL(qu->allocations,an);
++ an->size = sz;
+ return (byte*)an + MEM_ROUND(sizeof(*an));
+ }
+
++void *adns__realloc_interim(adns_query qu, void *p, size_t sz) {
++ allocnode *an;
++ allocnode *nan;
++
++ sz = MEM_ROUND(sz);
++ assert(sz); /* Freeing via realloc not supported */
++ assert(!qu->final_allocspace);
++
++ an = (allocnode *) ((byte *) p - MEM_ROUND(sizeof(*an)));
++ assert(an->size <= qu->interim_allocd);
++
++ nan = realloc(an, MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
++ if (!nan) return 0;
++
++ qu->interim_allocd -= nan->size;
++ qu->interim_allocd += sz;
++ nan->size = sz;
++
++ if (nan->next) nan->next->back = nan;
++ else qu->allocations.tail = nan;
++ if (nan->back) nan->back->next = nan;
++ else qu->allocations.head = nan;
++
++ return (byte*)nan + MEM_ROUND(sizeof(*nan));
++}
++
+ void *adns__alloc_interim(adns_query qu, size_t sz) {
+ void *rv;
+
+diff --git a/src/setup.c b/src/setup.c
+index 44c3cee..07b1f13 100644
+--- a/src/setup.c
++++ b/src/setup.c
+@@ -5,6 +5,7 @@
+ */
+ /*
+ * This file is part of adns, which is
++ * Copyright (C) 2009 Luca Bruno
+ * Copyright (C) 1997-2000,2003,2006 Ian Jackson
+ * Copyright (C) 1999-2000,2003,2006 Tony Finch
+ * Copyright (C) 1991 Massachusetts Institute of Technology
+@@ -41,12 +42,12 @@
+
+ static void readconfig(adns_state ads, const char *filename, int warnmissing);
+
+-static void addserver(adns_state ads, struct in_addr addr) {
++static void addserverv4(adns_state ads, struct in_addr addr) {
+ int i;
+ struct server *ss;
+
+ for (i=0; i<ads->nservers; i++) {
+- if (ads->servers[i].addr.s_addr == addr.s_addr) {
++ if ((ads->servers[i].sin_family == AF_INET) && (ads->servers[i].addr.s_addr == addr.s_addr)) {
+ adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr));
+ return;
+ }
+@@ -58,10 +59,35 @@ static void addserver(adns_state ads, struct in_addr addr) {
+ }
+
+ ss= ads->servers+ads->nservers;
++ ss->sin_family= AF_INET;
+ ss->addr= addr;
+ ads->nservers++;
+ }
+
++static void addserverv6(adns_state ads, struct in6_addr addr) {
++ int i;
++ struct server *ss;
++ char buf[INET6_ADDRSTRLEN];
++
++ for (i=0; i<ads->nservers; i++) {
++ if ((ads->servers[i].sin_family == AF_INET6) && !(memcmp(&(ads->servers[i].addr6.s6_addr), &(addr.s6_addr), sizeof(struct in6_addr)))) {
++ adns__debug(ads,-1,0,"duplicate nameserver %s ignored", inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN*sizeof(char)));
++ return;
++ }
++ }
++
++ if (ads->nservers>=MAXSERVERS) {
++ adns__diag(ads,-1,0,"too many nameservers, ignoring %s", inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN*sizeof(char)));
++ return;
++ }
++
++ ss= ads->servers+ads->nservers;
++ ss->sin_family= AF_INET6;
++ ss->addr6= addr;
++ ads->nservers++;
++}
++
++
+ static void freesearchlist(adns_state ads) {
+ if (ads->nsearchlist) free(*ads->searchlist);
+ free(ads->searchlist);
+@@ -105,16 +131,28 @@ static int nextword(const char **bufp_io, const char **word_r, int *l_r) {
+
+ static void ccf_nameserver(adns_state ads, const char *fn,
+ int lno, const char *buf) {
+- struct in_addr ia;
+-
+- if (!inet_aton(buf,&ia)) {
+- configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
+- return;
++ struct in_addr ia4;
++ struct in6_addr ia6;
++ char ns_name[INET6_ADDRSTRLEN];
++
++ if (!inet_aton(buf,&ia4)) {
++ if (!inet_pton(AF_INET6, buf,&ia6)) {
++ configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
++ return;
++ }
++ else {
++ adns__debug(ads,-1,0,"using nameserver %s", inet_ntop(AF_INET6, &ia6, ns_name, INET6_ADDRSTRLEN*sizeof(char)));
++ addserverv6(ads,ia6);
++
++ }
++ }
++ else {
++ adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia4));
++ addserverv4(ads,ia4);
+ }
+- adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia));
+- addserver(ads,ia);
+ }
+
++
+ static void ccf_search(adns_state ads, const char *fn,
+ int lno, const char *buf) {
+ const char *bufp, *word;
+@@ -150,6 +188,7 @@ static void ccf_search(adns_state ads, const char *fn,
+
+ static void ccf_sortlist(adns_state ads, const char *fn,
+ int lno, const char *buf) {
++ /* FIXME: Handle IPv6 addresses */
+ const char *word;
+ char tbuf[200], *slash, *ep;
+ struct in_addr base, mask;
+@@ -191,6 +230,21 @@ static void ccf_sortlist(adns_state ads, const char *fn,
+ " overlaps address `%s'",slash,tbuf);
+ continue;
+ }
++ {
++ /* Convert bitmask to prefix length */
++ unsigned long bits;
++
++ for(bits=ntohl(mask.s_addr), initial = 0;
++ bits & 0x80000000UL;
++ bits <<= 1)
++ initial++;
++
++ if (bits & 0xffffffff) {
++ configparseerr(ads,fn,lno,
++ "mask `%s' in sortlist is non-continuous",slash);
++ continue;
++ }
++ }
+ } else {
+ initial= strtoul(slash,&ep,10);
+ if (*ep || initial>32) {
+@@ -202,11 +256,11 @@ static void ccf_sortlist(adns_state ads, const char *fn,
+ } else {
+ baselocal= ntohl(base.s_addr);
+ if (!baselocal & 0x080000000UL) /* class A */
+- mask.s_addr= htonl(0x0ff000000UL);
++ initial = 8;
+ else if ((baselocal & 0x0c0000000UL) == 0x080000000UL)
+- mask.s_addr= htonl(0x0ffff0000UL); /* class B */
++ initial= 16; /* class B */
+ else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL)
+- mask.s_addr= htonl(0x0ff000000UL); /* class C */
++ initial= 24; /* class C */
+ else {
+ configparseerr(ads,fn,lno, "network address `%s'"
+ " in sortlist is not in classed ranges,"
+@@ -215,8 +269,10 @@ static void ccf_sortlist(adns_state ads, const char *fn,
+ }
+ }
+
+- ads->sortlist[ads->nsortlist].base= base;
+- ads->sortlist[ads->nsortlist].mask= mask;
++ ads->sortlist[ads->nsortlist].family= AF_INET;
++ ads->sortlist[ads->nsortlist].base.inet= base;
++ ads->sortlist[ads->nsortlist].prefix= initial;
++
+ ads->nsortlist++;
+ }
+ }
+@@ -522,7 +578,7 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
+ LIST_INIT(ads->output);
+ ads->forallnext= 0;
+ ads->nextid= 0x311f;
+- ads->udpsocket= ads->tcpsocket= -1;
++ ads->udpsocket= ads->udpsocket6= ads->tcpsocket= -1;
+ adns__vbuf_init(&ads->tcpsend);
+ adns__vbuf_init(&ads->tcprecv);
+ ads->tcprecv_skip= 0;
+@@ -550,16 +606,22 @@ static int init_finish(adns_state ads) {
+ if (ads->logfn && ads->iflags & adns_if_debug)
+ adns__lprintf(ads,"adns: no nameservers, using localhost\n");
+ ia.s_addr= htonl(INADDR_LOOPBACK);
+- addserver(ads,ia);
++ addserverv4(ads,ia);
+ }
+
+ proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
+ ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
+ if (ads->udpsocket<0) { r= errno; goto x_free; }
+
++ ads->udpsocket6= socket(AF_INET6,SOCK_DGRAM,proto->p_proto);
++ if (ads->udpsocket6<0) { r= errno; goto x_free6; }
++
+ r= adns__setnonblock(ads,ads->udpsocket);
+ if (r) { r= errno; goto x_closeudp; }
+
++ r= adns__setnonblock(ads,ads->udpsocket6);
++ if (r) { r= errno; goto x_closeudp6; }
++
+ return 0;
+
+ x_closeudp:
+@@ -567,6 +629,12 @@ static int init_finish(adns_state ads) {
+ x_free:
+ free(ads);
+ return r;
++
++ x_closeudp6:
++ close(ads->udpsocket6);
++ x_free6:
++ free(ads);
++ return r;
+ }
+
+ static void init_abort(adns_state ads) {
+@@ -678,7 +746,10 @@ void adns_finish(adns_state ads) {
+ else if (ads->output.head) adns_cancel(ads->output.head);
+ else break;
+ }
+- close(ads->udpsocket);
++ if (ads->udpsocket >= 0)
++ close(ads->udpsocket);
++ if (ads->udpsocket6 >= 0)
++ close(ads->udpsocket6);
+ if (ads->tcpsocket >= 0) close(ads->tcpsocket);
+ adns__vbuf_free(&ads->tcpsend);
+ adns__vbuf_free(&ads->tcprecv);
+diff --git a/src/transmit.c b/src/transmit.c
+index 7afb90f..fb733fc 100644
+--- a/src/transmit.c
++++ b/src/transmit.c
+@@ -5,6 +5,7 @@
+ */
+ /*
+ * This file is part of adns, which is
++ * Copyright (C) 2009 Luca Bruno
+ * Copyright (C) 1997-2000,2003,2006 Ian Jackson
+ * Copyright (C) 1999-2000,2003,2006 Tony Finch
+ * Copyright (C) 1991 Massachusetts Institute of Technology
+@@ -62,6 +63,8 @@ static adns_status mkquery_header(adns_state ads, vbuf *vb,
+ return adns_s_ok;
+ }
+
++/* FIXME: Return value is always adns_s_ok, and never used. But I
++ * don't understand why we can assert that we have space in the vbuf. */
+ static adns_status mkquery_footer(vbuf *vb, adns_rrtype type) {
+ byte *rqp;
+
+@@ -118,17 +121,15 @@ adns_status adns__qdpl_normal(adns_state ads,
+ return adns_s_ok;
+ }
+
+-adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
++adns_status adns__mkquery_labels(adns_state ads, vbuf *vb,
+ const char *owner, int ol,
+- const typeinfo *typei, adns_rrtype type,
+- adns_queryflags flags) {
++ const typeinfo *typei, adns_queryflags flags) {
+ int labelnum, ll, nbytes;
+- byte label[255];
+- byte *rqp;
++ byte label[255], *rqp;
+ const char *p, *pe;
+ adns_status st;
+
+- st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st;
++ if (!adns__vbuf_ensure(vb,ol+2)) return adns_s_nomemory;
+
+ MKQUERY_START(vb);
+
+@@ -149,22 +150,31 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+ MKQUERY_ADDB(0);
+
+ MKQUERY_STOP(vb);
++ return adns_s_ok;
++}
++
++adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
++ const char *owner, int ol,
++ const typeinfo *typei, adns_rrtype type,
++ adns_queryflags flags) {
++ adns_status st;
+
++ st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st;
++ st= adns__mkquery_labels(ads, vb, owner, ol, typei, flags); if (st) return st;
+ st= mkquery_footer(vb,type);
+
+ return adns_s_ok;
+ }
+
+-adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
++adns_status adns__mkquery_labels_frdgram(adns_state ads, vbuf *vb,
+ const byte *qd_dgram, int qd_dglen,
+- int qd_begin,
+- adns_rrtype type, adns_queryflags flags) {
++ int qd_begin) {
++ adns_status st;
+ byte *rqp;
+ findlabel_state fls;
+ int lablen, labstart;
+- adns_status st;
+
+- st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st;
++ if (!adns__vbuf_ensure(vb,qd_dglen)) return adns_s_nomemory;
+
+ MKQUERY_START(vb);
+
+@@ -181,6 +191,30 @@ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+
+ MKQUERY_STOP(vb);
+
++ return adns_s_ok;
++}
++
++adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
++ const byte *qd_dgram, int qd_dglen,
++ int qd_begin,
++ adns_rrtype type, adns_queryflags flags) {
++ adns_status st;
++
++ st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st;
++ st= adns__mkquery_labels_frdgram(ads, vb, qd_dgram, qd_dglen, qd_begin);
++ if (st) return st;
++ st= mkquery_footer(vb,type);
++
++ return adns_s_ok;
++}
++
++adns_status adns__mkquery_frlabels(adns_state ads, vbuf *vb, int *id_r,
++ char *l, int llen,
++ adns_rrtype type, adns_queryflags flags) {
++ adns_status st;
++
++ st= mkquery_header(ads,vb,id_r,llen); if (st) return st;
++ if (!adns__vbuf_append(vb, l, llen)) return adns_s_nomemory;
+ st= mkquery_footer(vb,type);
+
+ return adns_s_ok;
+@@ -251,6 +285,7 @@ static void query_usetcp(adns_query qu, struct timeval now) {
+
+ void adns__query_send(adns_query qu, struct timeval now) {
+ struct sockaddr_in servaddr;
++ struct sockaddr_in6 servaddr6;
+ int serv, r;
+ adns_state ads;
+
+@@ -266,15 +301,25 @@ void adns__query_send(adns_query qu, struct timeval now) {
+ }
+
+ serv= qu->udpnextserver;
+- memset(&servaddr,0,sizeof(servaddr));
+-
+ ads= qu->ads;
+- servaddr.sin_family= AF_INET;
+- servaddr.sin_addr= ads->servers[serv].addr;
+- servaddr.sin_port= htons(DNS_PORT);
++
++ if(ads->servers[serv].sin_family == AF_INET) {
++ memset(&servaddr,0,sizeof(servaddr));
++ servaddr.sin_family= ads->servers[serv].sin_family;
++ servaddr.sin_addr= ads->servers[serv].addr;
++ servaddr.sin_port= htons(DNS_PORT);
++ r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0,
++ (const struct sockaddr*)&servaddr,sizeof(servaddr));
++ } else {
++ memset(&servaddr6,0,sizeof(servaddr6));
++ servaddr6.sin6_family= ads->servers[serv].sin_family;
++ servaddr6.sin6_addr= ads->servers[serv].addr6;
++ servaddr6.sin6_port= htons(DNS_PORT);
++ r= sendto(ads->udpsocket6,qu->query_dgram,qu->query_dglen,0,
++ (const struct sockaddr*)&servaddr6,sizeof(servaddr6));
++ }
++
+
+- r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0,
+- (const struct sockaddr*)&servaddr,sizeof(servaddr));
+ if (r<0 && errno == EMSGSIZE) {
+ qu->retries= 0;
+ query_usetcp(qu,now);
+diff --git a/src/types.c b/src/types.c
+index 36ff879..712ae69 100644
+--- a/src/types.c
++++ b/src/types.c
+@@ -48,12 +48,15 @@
+ * _manyistr (mf,cs)
+ * _txt (pa)
+ * _inaddr (pa,dip,di)
+- * _addr (pa,di,csp,cs)
++ * _in6addr (pa,cs)
++ * _addr (sh,di,csp,cs)
+ * _domain (pap)
+ * _host_raw (pa)
+ * _hostaddr (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs)
+ * _mx_raw (pa,di)
+ * _mx (pa,di)
++ * _srv_raw (pa,di,mf,cs)
++ * _srv (pa,di,mf,cs)
+ * _inthostaddr (mf,cs)
+ * _ptr (pa)
+ * _strpair (mf,cs)
+@@ -251,14 +254,20 @@ static adns_status pa_inaddr(const parseinfo *pai, int cbyte,
+ return adns_s_ok;
+ }
+
+-static int search_sortlist(adns_state ads, struct in_addr ad) {
++static int search_sortlist_in(adns_state ads, struct in_addr ad) {
+ const struct sortlist *slp;
+ int i;
+
+ for (i=0, slp=ads->sortlist;
+- i<ads->nsortlist &&
+- !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr);
+- i++, slp++);
++ i<ads->nsortlist;
++ i++, slp++) {
++ if (slp->family == AF_INET) {
++ struct in_addr mask;
++ mask.s_addr = htonl(-1 << slp->prefix);
++ if ( (ad.s_addr & mask.s_addr ) == slp->base.inet.s_addr)
++ break;
++ }
++ }
+ return i;
+ }
+
+@@ -267,8 +276,8 @@ static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) {
+
+ if (!ads->nsortlist) return 0;
+
+- ai= search_sortlist(ads,a);
+- bi= search_sortlist(ads,b);
++ ai= search_sortlist_in(ads,a);
++ bi= search_sortlist_in(ads,b);
+ return bi<ai;
+ }
+
+@@ -289,27 +298,297 @@ static adns_status cs_inaddr(vbuf *vb, const void *datap) {
+ }
+
+ /*
+- * _addr (pa,di,csp,cs)
++ * _in6addr (pa,dip,di)
+ */
+
+-static adns_status pa_addr(const parseinfo *pai, int cbyte,
++static adns_status pa_in6addr(const parseinfo *pai, int cbyte,
+ int max, void *datap) {
+- adns_rr_addr *storeto= datap;
++ struct in_addr *storeto= datap;
++
++ if (max-cbyte != 16) return adns_s_invaliddata;
++ memcpy(storeto, pai->dgram + cbyte, 16);
++ return adns_s_ok;
++}
++
++static int search_sortlist_in6(adns_state ads, const struct in6_addr *ad) {
++ const struct sortlist *slp;
++ int i;
++
++ for (i=0, slp=ads->sortlist;
++ i<ads->nsortlist;
++ i++, slp++) {
++ if (slp->family == AF_INET6) {
++ int pb = slp->prefix / 8;
++ int mask = 0xff & (-1 << (slp->prefix % 8));
++ if (memcmp(ad->s6_addr, slp->base.inet6.s6_addr, pb) == 0
++ && (!mask
++ || (ad->s6_addr[pb] & mask) == slp->base.inet6.s6_addr[pb]))
++ break;
++ }
++ }
++ return i;
++}
++
++static int dip_in6addr(adns_state ads,
++ const struct in6_addr *a, const struct in6_addr *b) {
++ int ai, bi;
++
++ if (!ads->nsortlist) return 0;
++
++ ai= search_sortlist_in6(ads,a);
++ bi= search_sortlist_in6(ads,b);
++ return bi<ai;
++}
++
++static int di_in6addr(adns_state ads,
++ const void *datap_a, const void *datap_b) {
++ const struct in6_addr *ap= datap_a, *bp= datap_b;
++
++ return dip_in6addr(ads,ap,bp);
++}
++
++
++static adns_status cs_in6addr(vbuf *vb, const void *datap) {
++ char buf[INET6_ADDRSTRLEN];
++ const char *ia;
++
++ ia= inet_ntop(AF_INET6, datap, buf, sizeof(buf)); assert(ia);
++ CSP_ADDSTR(ia);
++ return adns_s_ok;
++}
++
++/*
++ * _addr (sh,pa,di,csp,cs)
++ */
++
++static void mk_mapped_ipv6(struct sockaddr_in6 *sa, const struct in_addr *in) {
++ memset(sa, 0, sizeof(*sa));
++ sa->sin6_family = AF_INET6;
++ sa->sin6_addr.s6_addr16[5] = 0xffff;
++ sa->sin6_addr.s6_addr32[3] = in->s_addr;
++}
++
++static void icb_addr(adns_query parent, adns_query child) {
++ adns_answer *cans= child->answer;
++ adns_answer *pans= parent->answer;
++ adns_state ads= parent->ads;
++ adns_rr_addr *addr;
++
++ int i;
++
++ if (parent->expires > child->expires) parent->expires = child->expires;
++
++ if (cans->status == adns_s_nxdomain) {
++ adns__query_fail(parent,cans->status);
++ return;
++ }
++ if (cans->status == adns_s_nodata && parent->children.head) {
++ /* We may get records from the remaining queries */
++ LIST_LINK_TAIL(ads->childw,parent);
++ return;
++ }
++ if (cans->status) {
++ if (pans->nrrs)
++ adns__query_done(parent);
++ else
++ adns__query_fail(parent,cans->status);
++ return;
++ }
++
++ assert(cans->nrrs);
++
++ /* Copy CNAME. CNAME must be consistent for both queries. */
++ if (cans->cname && pans->cname) {
++ if (strcmp(cans->cname, pans->cname)) {
++ adns__query_fail(parent, adns_s_inconsistent);
++ return;
++ }
++ }
++ else if (pans->cname) {
++ adns__query_fail(parent, adns_s_inconsistent);
++ return;
++ }
++ else if (cans->cname) {
++ size_t len;
++ if (pans->nrrs) {
++ adns__query_fail(parent, adns_s_inconsistent);
++ return;
++ }
++ len = strlen(cans->cname) + 1;
++ pans->cname = adns__alloc_preserved(parent, len);
++ if (!pans->cname) {
++ adns__query_fail(parent, adns_s_nomemory);
++ return;
++ }
++ memcpy(pans->cname, cans->cname, len);
++ }
++ if (pans->nrrs)
++ {
++ void *p = adns__realloc_interim(parent,pans->rrs.untyped,
++ sizeof(adns_rr_addr) * (cans->nrrs + pans->nrrs));
++ if (!p) {
++ adns__query_fail(parent, adns_s_nomemory);
++ return;
++ }
++ pans->rrs.untyped = p;
++ addr = pans->rrs.addr + pans->nrrs;
++ pans->nrrs += cans->nrrs;
++ }
++ else {
++ pans->rrs.untyped
++ = adns__alloc_interim(parent,sizeof(adns_rr_addr) * cans->nrrs);
++ if (!pans->rrs.untyped) {
++ adns__query_fail(parent,adns_s_nomemory);
++ return;
++ }
++ pans->nrrs = cans->nrrs;
++ addr = pans->rrs.addr;
++ }
++
++ switch (cans->type) {
++ default: abort();
++ case adns_r_a:
++ if (parent->flags & adns_qf_ip6mapped)
++ for (i = 0; i<cans->nrrs; i++) {
++ addr[i].len = sizeof(struct sockaddr_in6);
++ mk_mapped_ipv6(&addr[i].addr.inet6, &cans->rrs.inaddr[i]);
++ }
++ else
++ for (i = 0; i<cans->nrrs; i++) {
++ addr[i].len = sizeof(struct sockaddr_in);
++ memset(&addr[i].addr.inet, 0, sizeof(addr[i].addr.inet));
++ addr[i].addr.inet.sin_family = AF_INET;
++ addr[i].addr.inet.sin_addr = cans->rrs.inaddr[i];
++ }
++ break;
++ case adns_r_aaaa:
++ for (i = 0; i<cans->nrrs; i++) {
++ addr[i].len = sizeof(struct sockaddr_in6);
++ memset(&addr[i].addr.inet6, 0, sizeof(addr[i].addr.inet6));
++ addr[i].addr.inet6.sin6_family = AF_INET6;
++ addr[i].addr.inet6.sin6_addr = cans->rrs.in6addr[i];
++ }
++ break;
++ }
++
++ if (!parent->children.head) {
++ adns__query_done(parent);
++ return;
++ } else {
++ LIST_LINK_TAIL(ads->childw,parent);
++ return;
++ }
++}
++
++static void sh_addr(adns_query qu,
++ adns_queryflags flags, struct timeval now)
++{
++ adns_status st;
++ int id;
++ qcontext ctx;
++ adns_query nqu;
++ vbuf vb;
++
++ assert(flags & adns__qf_ip_mask);
++
++ /* Must have a non-negative id, or else adns__internal_check will
++ * think that we are on the output queue. */
++ qu->id = 0;
++
++ ctx.ext= 0;
++ ctx.callback= icb_addr;
++ /* What to store in ctx.info? */
++
++ adns__vbuf_init(&vb);
++
++ if (flags & adns_qf_ip4) { /* A query */
++ st= adns__mkquery_frlabels(qu->ads, &vb, &id,
++ qu->vb.buf, qu->vb.used, adns_r_a, flags);
++ if (st) { adns__query_fail(qu, st); return; }
++
++ st= adns__internal_submit(qu->ads, &nqu, adns__findtype(adns_r_a),
++ &vb, id, flags, now, &ctx);
++ if (st) { adns__query_fail(qu, st); return; }
++
++ nqu->parent = qu;
++ LIST_LINK_TAIL_PART(qu->children,nqu,siblings.);
++ }
++
++ if (flags & adns_qf_ip6) { /* AAAA query */
++ st= adns__mkquery_frlabels(qu->ads, &vb, &id,
++ qu->vb.buf, qu->vb.used, adns_r_aaaa, flags);
++ if (st) { adns__query_fail(qu, st); return; }
++
++ st= adns__internal_submit(qu->ads, &nqu, adns__findtype(adns_r_aaaa),
++ &vb, id, flags, now, &ctx);
++ if (st) { adns__query_fail(qu, st); return; }
++
++ nqu->parent = qu;
++ LIST_LINK_TAIL_PART(qu->children,nqu,siblings.);
++ }
++ assert(qu->children.head);
++}
++
++static adns_status pap_addr(const parseinfo *pai, adns_rrtype type, int cbyte,
++ int max, adns_rr_addr *rr) {
++
+ const byte *dgram= pai->dgram;
++ adns_queryflags flags = pai->qu->flags;
++
++ switch (type)
++ {
++ default: abort();
++ case adns_r_a:
++ assert(flags & adns_qf_ip4);
+
+ if (max-cbyte != 4) return adns_s_invaliddata;
+- storeto->len= sizeof(storeto->addr.inet);
+- memset(&storeto->addr,0,sizeof(storeto->addr.inet));
+- storeto->addr.inet.sin_family= AF_INET;
+- memcpy(&storeto->addr.inet.sin_addr,dgram+cbyte,4);
++
++ if (flags & adns_qf_ip6mapped) {
++ rr->len = sizeof(struct sockaddr_in6);
++ mk_mapped_ipv6(&rr->addr.inet6, (const struct in_addr *) (dgram+cbyte));
++ }
++ else {
++ rr->len= sizeof(rr->addr.inet);
++ memset(&rr->addr.inet,0,sizeof(rr->addr.inet));
++ rr->addr.inet.sin_family= AF_INET;
++ memcpy(&rr->addr.inet.sin_addr,dgram+cbyte,4);
++ }
++ break;
++ case adns_r_aaaa:
++ assert(flags & adns_qf_ip6);
++
++ if (max-cbyte != 16) return adns_s_invaliddata;
++
++ rr->len= sizeof(rr->addr.inet6);
++ memset(&rr->addr,0,sizeof(rr->addr.inet6));
++ rr->addr.inet6.sin6_family= AF_INET6;
++ memcpy(&rr->addr.inet6.sin6_addr,dgram+cbyte,16);
++
++ break;
++ }
++
+ return adns_s_ok;
+ }
+
++static int search_sortlist_addr(adns_state ads, const adns_rr_addr *ad) {
++ switch(ad->addr.sa.sa_family) {
++ default: abort();
++ case AF_INET: return search_sortlist_in(ads, ad->addr.inet.sin_addr);
++ case AF_INET6: return search_sortlist_in6(ads, &ad->addr.inet6.sin6_addr);
++ }
++}
++
++static int dip_addr(adns_state ads,
++ const adns_rr_addr *a, const adns_rr_addr *b) {
++ int ai, bi;
++ ai = search_sortlist_addr(ads, a);
++ bi = search_sortlist_addr(ads, b);
++ return bi<ai;
++}
++
+ static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) {
+ const adns_rr_addr *ap= datap_a, *bp= datap_b;
+-
+- assert(ap->addr.sa.sa_family == AF_INET);
+- return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr);
++ return dip_addr(ads, ap, bp);
+ }
+
+ static int div_addr(void *context, const void *datap_a, const void *datap_b) {
+@@ -320,7 +599,7 @@ static int div_addr(void *context, const void *datap_a, const void *datap_b) {
+
+ static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) {
+ const char *ia;
+- char buf[30];
++ char buf[INET6_ADDRSTRLEN];
+
+ switch (rrp->addr.inet.sin_family) {
+ case AF_INET:
+@@ -328,6 +607,12 @@ static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) {
+ ia= inet_ntoa(rrp->addr.inet.sin_addr); assert(ia);
+ CSP_ADDSTR(ia);
+ break;
++ case AF_INET6:
++ CSP_ADDSTR("INET6 ");
++ ia= inet_ntop(AF_INET6, &rrp->addr.inet6.sin6_addr,
++ buf, sizeof(buf)); assert(ia);
++ CSP_ADDSTR(ia);
++ break;
+ default:
+ sprintf(buf,"AF=%u",rrp->addr.sa.sa_family);
+ CSP_ADDSTR(buf);
+@@ -424,17 +709,22 @@ static adns_status pap_findaddrs(const parseinfo *pai, adns_rr_hostaddr *ha,
+ &type, &class, &ttl, &rdlen, &rdstart,
+ pai->dgram, pai->dglen, dmstart, &ownermatched);
+ if (st) return st;
+- if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) {
++ if (!ownermatched || class != DNS_CLASS_IN) {
+ if (naddrs>0) break; else continue;
+ }
++ if (! ((type == adns_r_a && (pai->qu->flags & adns_qf_ip4))
++ || (type == adns_r_aaaa && (pai->qu->flags & adns_qf_ip6)))) {
++ if (naddrs>0) break; else continue;
++ }
++
+ if (naddrs == -1) {
+ naddrs= 0;
+ }
+ if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*sizeof(adns_rr_addr)))
+ R_NOMEM;
+ adns__update_expires(pai->qu,ttl,pai->now);
+- st= pa_addr(pai, rdstart,rdstart+rdlen,
+- pai->qu->vb.buf + naddrs*sizeof(adns_rr_addr));
++ st= pap_addr(pai, type, rdstart,rdstart+rdlen,
++ (adns_rr_addr *) pai->qu->vb.buf + naddrs);
+ if (st) return st;
+ naddrs++;
+ }
+@@ -476,7 +766,6 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
+ adns_status st;
+ int dmstart, cbyte;
+ qcontext ctx;
+- int id;
+ adns_query nqu;
+ adns_queryflags nflags;
+
+@@ -500,9 +789,8 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
+ if (st) return st;
+ if (rrp->naddrs != -1) return adns_s_ok;
+
+- st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
+- pai->dgram, pai->dglen, dmstart,
+- adns_r_addr, adns_qf_quoteok_query);
++ st= adns__mkquery_labels_frdgram(pai->ads, &pai->qu->vb,
++ pai->dgram, pai->dglen, dmstart);
+ if (st) return st;
+
+ ctx.ext= 0;
+@@ -513,7 +801,7 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
+ if (!(pai->qu->flags & adns_qf_cname_loose)) nflags |= adns_qf_cname_forbid;
+
+ st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+- &pai->qu->vb, id, nflags, pai->now, &ctx);
++ &pai->qu->vb, 0, nflags, pai->now, &ctx);
+ if (st) return st;
+
+ nqu->parent= pai->qu;
+@@ -539,11 +827,7 @@ static int dip_hostaddr(adns_state ads,
+ if (ap->astatus != bp->astatus) return ap->astatus;
+ if (ap->astatus) return 0;
+
+- assert(ap->addrs[0].addr.sa.sa_family == AF_INET);
+- assert(bp->addrs[0].addr.sa.sa_family == AF_INET);
+- return dip_inaddr(ads,
+- ap->addrs[0].addr.inet.sin_addr,
+- bp->addrs[0].addr.inet.sin_addr);
++ return dip_addr(ads, &ap->addrs[0], &bp->addrs[0]);
+ }
+
+ static int di_hostaddr(adns_state ads,
+@@ -717,7 +1001,7 @@ static void icb_ptr(adns_query parent, adns_query child) {
+ return;
+ }
+
+- queried= &parent->ctx.info.ptr_parent_addr;
++ queried= &parent->extra.info.ptr_addr;
+ for (i=0, found=cans->rrs.addr; i<cans->nrrs; i++, found++) {
+ if (queried->len == found->len &&
+ !memcmp(&queried->addr,&found->addr,queried->len)) {
+@@ -734,18 +1018,12 @@ static void icb_ptr(adns_query parent, adns_query child) {
+ adns__query_fail(parent,adns_s_inconsistent);
+ }
+
++/* FIXME: Completely different in adns-1.4. */
+ static adns_status pa_ptr(const parseinfo *pai, int dmstart,
+ int max, void *datap) {
+- static const char *const (expectdomain[])= { DNS_INADDR_ARPA };
+-
+ char **rrp= datap;
+ adns_status st;
+- adns_rr_addr *ap;
+- findlabel_state fls;
+- char *ep;
+- byte ipv[4];
+- char labbuf[4];
+- int cbyte, i, lablen, labstart, l, id;
++ int cbyte;
+ adns_query nqu;
+ qcontext ctx;
+
+@@ -755,48 +1033,20 @@ static adns_status pa_ptr(const parseinfo *pai, int dmstart,
+ if (st) return st;
+ if (cbyte != max) return adns_s_invaliddata;
+
+- ap= &pai->qu->ctx.info.ptr_parent_addr;
+- if (!ap->len) {
+- adns__findlabel_start(&fls, pai->ads, -1, pai->qu,
+- pai->qu->query_dgram, pai->qu->query_dglen,
+- pai->qu->query_dglen, DNS_HDRSIZE, 0);
+- for (i=0; i<4; i++) {
+- st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+- if (lablen<=0 || lablen>3) return adns_s_querydomainwrong;
+- memcpy(labbuf, pai->qu->query_dgram + labstart, lablen);
+- labbuf[lablen]= 0;
+- ipv[3-i]= strtoul(labbuf,&ep,10);
+- if (*ep) return adns_s_querydomainwrong;
+- if (lablen>1 && pai->qu->query_dgram[labstart]=='0')
+- return adns_s_querydomainwrong;
+- }
+- for (i=0; i<sizeof(expectdomain)/sizeof(*expectdomain); i++) {
+- st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+- l= strlen(expectdomain[i]);
+- if (lablen != l ||
+- memcmp(pai->qu->query_dgram + labstart, expectdomain[i], l))
+- return adns_s_querydomainwrong;
+- }
+- st= adns__findlabel_next(&fls,&lablen,0); assert(!st);
+- if (lablen) return adns_s_querydomainwrong;
+-
+- ap->len= sizeof(struct sockaddr_in);
+- memset(&ap->addr,0,sizeof(ap->addr.inet));
+- ap->addr.inet.sin_family= AF_INET;
+- ap->addr.inet.sin_addr.s_addr=
+- htonl((ipv[0]<<24) | (ipv[1]<<16) | (ipv[2]<<8) | (ipv[3]));
+- }
++ /* Should be initialized by adns_submit_reverse. If it's not, we
++ * can't do any consistency checking. */
++ if (!pai->qu->extra.info.ptr_addr.len) return adns_s_ok;
+
+- st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
+- pai->dgram, pai->dglen, dmstart,
+- adns_r_addr, adns_qf_quoteok_query);
++ pai->qu->vb.used = 0;
++ st= adns__mkquery_labels_frdgram(pai->ads, &pai->qu->vb,
++ pai->dgram, pai->dglen, dmstart);
+ if (st) return st;
+
+ ctx.ext= 0;
+ ctx.callback= icb_ptr;
+ memset(&ctx.info,0,sizeof(ctx.info));
+ st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+- &pai->qu->vb, id,
++ &pai->qu->vb, 0,
+ adns_qf_quoteok_query, pai->now, &ctx);
+ if (st) return st;
+
+@@ -1250,13 +1500,16 @@ static void mf_flat(adns_query qu, void *data) { }
+
+ #define DEEP_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \
+ { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_##memb, \
+- printer,parser,comparer, adns__qdpl_normal,0 }
++ printer,0,parser,comparer, adns__qdpl_normal,0 }
+ #define FLAT_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \
+ { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_flat, \
+- printer,parser,comparer, adns__qdpl_normal,0 }
++ printer,0,parser,comparer, adns__qdpl_normal,0 }
+ #define XTRA_TYPE(code,rrt,fmt,memb,parser,comparer,printer,qdpl,postsort) \
+ { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_##memb, \
+- printer,parser,comparer,qdpl,postsort }
++ printer,0,parser,comparer,qdpl,postsort }
++#define SPECIAL_TYPE(code,rrt,fmt,memb,submit,comparer,printer) \
++ { adns_r_##code, rrt,fmt,TYPESZ_M(memb), mf_flat, \
++ printer,submit,0,comparer, adns__qdpl_normal,0 }
+
+ static const typeinfo typeinfos[] = {
+ /* Must be in ascending order of rrtype ! */
+@@ -1271,10 +1524,11 @@ DEEP_TYPE(hinfo, "HINFO", 0, intstrpair,pa_hinfo, 0, cs_hinfo ),
+ DEEP_TYPE(mx_raw, "MX", "raw",intstr, pa_mx_raw, di_mx_raw,cs_inthost ),
+ DEEP_TYPE(txt, "TXT", 0, manyistr,pa_txt, 0, cs_txt ),
+ DEEP_TYPE(rp_raw, "RP", "raw",strpair, pa_rp, 0, cs_rp ),
++FLAT_TYPE(aaaa, "AAAA", 0, in6addr, pa_in6addr, di_in6addr, cs_in6addr ),
+ XTRA_TYPE(srv_raw,"SRV", "raw",srvraw , pa_srvraw, di_srv, cs_srvraw,
+ qdpl_srv, postsort_srv),
+
+-FLAT_TYPE(addr, "A", "addr", addr, pa_addr, di_addr, cs_addr ),
++/* adns__qtf_deref set */
+ DEEP_TYPE(ns, "NS", "+addr",hostaddr,pa_hostaddr,di_hostaddr,cs_hostaddr ),
+ DEEP_TYPE(ptr, "PTR","checked",str, pa_ptr, 0, cs_domain ),
+ DEEP_TYPE(mx, "MX", "+addr",inthostaddr,pa_mx, di_mx, cs_inthostaddr),
+@@ -1283,6 +1537,9 @@ XTRA_TYPE(srv, "SRV","+addr",srvha, pa_srvha, di_srv, cs_srvha,
+
+ DEEP_TYPE(soa, "SOA","822", soa, pa_soa, 0, cs_soa ),
+ DEEP_TYPE(rp, "RP", "822", strpair, pa_rp, 0, cs_rp ),
++
++/* adns__qtf_special set */
++SPECIAL_TYPE(addr,"<A+AAAA>", "addr",addr,sh_addr, di_addr, cs_addr ),
+ };
+
+ static const typeinfo typeinfo_unknown=