adns-1.4-ipv6
authorAnas Nashif <anas.nashif@intel.com>
Wed, 7 Nov 2012 17:28:16 +0000 (09:28 -0800)
committerAnas Nashif <anas.nashif@intel.com>
Wed, 7 Nov 2012 17:28:16 +0000 (09:28 -0800)
13 files changed:
Makefile.in
client/adh-main.c
client/adh-opts.c
client/adh-query.c
client/adnshost.h
client/adnstest.c
src/adns.h
src/check.c
src/internal.h
src/query.c
src/setup.c
src/transmit.c
src/types.c

index 6e2e449..0babf0e 100644 (file)
@@ -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)`
index b6f3bd4..f2032ec 100644 (file)
@@ -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-" },
index 08310e0..7b17c89 100644 (file)
@@ -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" },
index 125bb33..2186004 100644 (file)
@@ -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,
index fcc96a3..7e2341a 100644 (file)
@@ -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;
 
index 550cf27..ae70285 100644 (file)
@@ -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,
index 34f9f49..34350ca 100644 (file)
@@ -52,7 +52,7 @@
  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
  *
- *  $Id: adns.h,v 1.96 2006/08/09 11:16:59 ian Exp $
+ *  $Id: adns.h,v 1.19 2007/10/03 20:01:05 nisse Exp $
  */
 
 #ifndef ADNS_H_INCLUDED
 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);
index 41cdde5..8925ecb 100644 (file)
@@ -24,6 +24,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) {
@@ -78,10 +80,10 @@ static void checkc_global(adns_state ads) {
   int i;
   
   assert(ads->udpsocket >= 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) {
index 58cd15d..1eb166d 100644 (file)
@@ -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    +----------------+
@@ -333,7 +356,12 @@ struct adns__state {
     struct in_addr addr;
   } 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 +429,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 +445,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 +489,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 +519,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.
index d09702e..2894e4d 100644 (file)
 
 #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;
   
index 44c3cee..57037cc 100644 (file)
@@ -150,6 +150,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 +192,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 +218,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 +231,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++;
   }
 }
index 7afb90f..73ecd91 100644 (file)
@@ -62,6 +62,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 +120,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 +149,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 +190,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;
index 36ff879..bed890a 100644 (file)
  * _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,14 @@ 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 +1035,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 +1502,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 +1526,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 +1539,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=