DNSSEC consolidation.
diff --git a/src/dns-protocol.h b/src/dns-protocol.h
index 8518dd0..90257ad 100644
--- a/src/dns-protocol.h
+++ b/src/dns-protocol.h
@@ -39,16 +39,29 @@
 #define C_ANY           255             /* wildcard match */
 
 #define T_A		1
-#define T_NS            2               
+#define T_NS            2
+#define T_MD            3
+#define T_MF            4             
 #define T_CNAME		5
 #define T_SOA		6
+#define T_MB            7
+#define T_MG            8
+#define T_MR            9
 #define T_PTR		12
+#define T_MINFO         14
 #define T_MX		15
 #define T_TXT		16
+#define T_RP            17
+#define T_AFSDB         18
+#define T_RT            21
 #define T_SIG		24
+#define T_PX            26
 #define T_AAAA		28
+#define T_NXT           30
 #define T_SRV		33
 #define T_NAPTR		35
+#define T_KX            36
+#define T_DNAME         39
 #define T_OPT		41
 #define T_DS            43
 #define T_RRSIG         46
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 230f35b..ca4fa61 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1054,8 +1054,6 @@
 size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
 int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
 int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
-int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, 
-		   int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo, int keytag);
 int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
 int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
 
diff --git a/src/dnssec.c b/src/dnssec.c
index f9d3aab..daf1dd3 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -20,378 +20,12 @@
 #ifdef HAVE_DNSSEC
 
 #include "dnssec-crypto.h"
-#include <assert.h>
-
-/* Maximum length in octects of a domain name, in wire format */
-#define MAXCDNAME  256 
-
-#define MAXRRSET 16
 
 #define SERIAL_UNDEF  -100
 #define SERIAL_EQ        0
 #define SERIAL_LT       -1
 #define SERIAL_GT        1
 
-/* Implement RFC1982 wrapped compare for 32-bit numbers */
-static int serial_compare_32(unsigned long s1, unsigned long s2)
-{
-  if (s1 == s2)
-    return SERIAL_EQ;
-
-  if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
-      (s1 > s2 && (s1 - s2) > (1UL<<31)))
-    return SERIAL_LT;
-  if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
-      (s1 > s2 && (s1 - s2) < (1UL<<31)))
-    return SERIAL_GT;
-  return SERIAL_UNDEF;
-}
-
-
-/* process_domain_name() - do operations with domain names in canonicalized wire format.
- *
- * Handling domain names in wire format can be done with buffers as large as MAXCDNAME (256),
- * while the representation format (as created by, eg., extract_name) requires MAXDNAME (1024).
- *
- * With "canonicalized wire format", we mean the standard DNS wire format, eg:
- *
- *   <3>www<7>example<3>org<0>
- *
- * with all ÅSCII letters converted to lowercase, and no wire-level compression.
- *
- * The function works with two different buffers:
- *    - Input buffer: 'rdata' is a pointer to the actual wire data, and 'rdlen' is
- *      the total length till the end of the rdata or DNS packet section. Both
- *      variables are updated after processing the domain name, so that rdata points
- *      after it, and rdlen is decreased by the amount of the processed octects.
- *    - Output buffer: 'out' points to it. In some cases, this buffer can be prefilled
- *      and used as additional input (see below).
- *
- * The argument "action" decides what to do with the submitted domain name:
- *
- *    PDN_EXTRACT:
- *       Extract the domain name from input buffer into the output buffer, possibly uncompressing it.
- *       Return the length of the domain name in the output buffer in octects, or zero if error.
- *
- *    PDN_COMPARE:
- *       Compare the domain name in the input buffer and the one in the output buffer (ignoring
- *       differences in compression). Returns 0 in case of error, a positive number
- *       if they are equal, or a negative number if they are different. This function always
- *       consumes the whole name in the input buffer (there is no early exit).
- *
- *    PDN_ORDER:
- *       Order between the domain name in the input buffer and the domain name in the output buffer.
- *       Returns 0 if the names are equal, 1 if input > output, or -1 if input < output. This
- *       function early-exits when it finds a difference, so rdata might not be fully updated.
- *
- * Notice: because of compression, rdata/rdlen might be updated with a different quantity than
- * the returned number of octects. For instance, if we extract a compressed domain name, rdata/rdlen
- * might be updated only by 2 bytes (that is, rdata is incresed by 2, and rdlen decreased by 2),
- * because it then reuses existing data elsewhere in the DNS packet, while the return value might be
- * larger, reflecting the total number of octects composing the domain name.
- *
- */
-#define PWN_EXTRACT   0
-#define PWN_COMPARE   1
-#define PWN_ORDER     2
-static int process_domain_name(struct dns_header *header, size_t pktlen,
-                               unsigned char** rdata, size_t* rdlen,
-                               unsigned char *out, int action)
-{
-  int hops = 0, total = 0, i;
-  unsigned char label_type;
-  unsigned char *end = (unsigned char *)header + pktlen;
-  unsigned char count; unsigned char *p = *rdata;
-  int nonequal = 0;
-
-#define PROCESS(ch) \
-  do { \
-    if (action == PWN_EXTRACT) \
-      *out++ = ch; \
-    else if (action == PWN_COMPARE) \
-      { \
-        if (*out++ != ch) \
-          nonequal = 1; \
-      } \
-    else if (action == PWN_ORDER) \
-      { \
-        char _ch = *out++; \
-        if (ch < _ch) \
-          return -1; \
-        else if (_ch > ch) \
-          return 1; \
-      } \
-  } while (0)
-
-  while (1)
-    {
-      if (p >= end)
-        return 0;
-      if (!(count = *p++))
-        break;
-      label_type = count & 0xC0;
-      if (label_type == 0xC0)
-        {
-          int l2;
-          if (p >= end)
-            return 0;
-          l2 = *p++;
-          if (hops == 0)
-            {
-              if (p - *rdata > *rdlen)
-                return 0;
-              *rdlen -= p - *rdata;
-              *rdata = p;
-            }
-          if (++hops == 256)
-            return 0;
-          p = (unsigned char*)header + (count & 0x3F) * 256 + l2;
-        }
-      else if (label_type == 0x00)
-        {
-          if (p+count-1 >= end)
-            return 0;
-          total += count+1;
-          if (total >= MAXCDNAME)
-            return 0;
-          PROCESS(count);
-          for (i = 0; i < count; ++i)
-            {
-              unsigned char ch = *p++;
-              if (ch >= 'A' && ch <= 'Z')
-                ch += 'a' - 'A';
-              PROCESS(ch);
-            }
-        }
-      else
-        return 0; /* unsupported label_type */
-    }
-
-  if (hops == 0)
-    {
-      if (p - *rdata > *rdlen)
-        return 0;
-      *rdlen -= p - *rdata;
-      *rdata = p;
-    }
-  ++total;
-  if (total >= MAXCDNAME)
-    return 0;
-  PROCESS(0);
-
-  /* If we arrived here without early-exit, they're equal */
-  if (action == PWN_ORDER)
-    return 0;
-  return nonequal ? -total : total;
-
-  #undef PROCESS
-}
-
-
-/* RDATA meta-description.
- *
- * RFC4034 §6.2 introduces the concept of a "canonical form of a RR". This canonical
- * form is used in two important points within the DNSSEC protocol/algorithm:
- *
- * 1) When computing the hash for verifying the RRSIG signature, we need to do it on
- *    the canonical form.
- * 2) When ordering a RRset in canonical order (§6.3), we need to lexicographically sort
- *    the RRs in canonical form.
- *
- * The canonical form of a RR is specifically tricky because it also affects the RDATA,
- * which is different for each RR type; in fact, RFC4034 says that "domain names in
- * RDATA must be canonicalized" (= uncompressed and lower-cased).
- *
- * To handle this correctly, we then need a way to describe how the RDATA section is
- * composed for each RR type; we don't need to describe every field, but just to specify
- * where domain names are. The following array contains this description, and it is
- * used by rrset_canonical_order() and verifyalg_add_rdata(), to adjust their behaviour
- * for each RR type.
- *
- * The format of the description is very easy, for instance:
- *
- *   { 12, RDESC_DOMAIN, RDESC_DOMAIN, 4, RDESC_DOMAIN, RDESC_END }
- *
- * This means that this (ficticious) RR type has a RDATA section containing 12 octects
- * (we don't care what they contain), followed by 2 domain names, followed by 4 octects,
- * followed by 1 domain name, and then followed by an unspecificied number of octects (0
- * or more).
- */
-
-#define RDESC_DOMAIN   -1
-#define RDESC_END       0
-static const int rdata_description[][8] =
-{
-  /**/            { RDESC_END },
-  /* 1: A */      { RDESC_END },
-  /* 2: NS */     { RDESC_DOMAIN, RDESC_END },
-  /* 3: .. */     { RDESC_END },
-  /* 4: .. */     { RDESC_END },
-  /* 5: CNAME */  { RDESC_DOMAIN, RDESC_END },
-  /* 6: SOA */    { RDESC_DOMAIN, RDESC_DOMAIN, RDESC_END },
-  /* 7: */        { RDESC_END },
-  /* 8: */        { RDESC_END },
-  /* 9: */        { RDESC_END },
-  /* 10: */       { RDESC_END },
-  /* 11: */       { RDESC_END },
-  /* 12: */       { RDESC_END },
-  /* 13: */       { RDESC_END },
-  /* 14: */       { RDESC_END },
-  /* 15: MX */    { 2, RDESC_DOMAIN, RDESC_END },
-};
-
-
-/* On-the-fly rdata canonicalization
- *
- * This set of functions allow the user to iterate over the rdata section of a RR
- * while canonicalizing it on-the-fly. This is a great memory saving since the user
- * doesn't need to allocate memory for a copy of the whole rdata section.
- *
- * Sample usage:
- *
- *    RDataCFrom cf;
- *    rdata_cfrom_init(
- *       &cf,
- *       header, pktlen,     // dns_header
- *       rdata,              // pointer to rdata section
- *       rrtype,             // RR tyep
- *       tmpbuf);            // temporary buf (MAXCDNAME)
- *
- *    while ((p = rdata_cfrom_next(&cf, &len))
- *      {
- *         // Process p[0..len]
- *      }
- *
- *    if (rdata_cfrom_error(&cf))
- *      // error occurred while parsing
- *
- */
-typedef struct
-{
-  struct dns_header *header;
-  size_t pktlen;
-  unsigned char *rdata;
-  unsigned char *tmpbuf;
-  size_t rdlen;
-  int rrtype;
-  int cnt;
-} RDataCForm;
-
-static void rdata_cform_init(RDataCForm *ctx, struct dns_header *header, size_t pktlen,
-                             unsigned char *rdata, int rrtype, unsigned char *tmpbuf)
-{
-  if (rrtype >= countof(rdata_description))
-    rrtype = 0;
-  ctx->header = header;
-  ctx->pktlen = pktlen;
-  ctx->rdata = rdata;
-  ctx->rrtype = rrtype;
-  ctx->tmpbuf = tmpbuf;
-  ctx->cnt = -1;
-  GETSHORT(ctx->rdlen, ctx->rdata);
-}
-
-static int rdata_cform_error(RDataCForm *ctx)
-{
-  return ctx->cnt == -2;
-}
-
-static unsigned char *rdata_cform_next(RDataCForm *ctx, size_t *len)
-{
-  if (ctx->cnt != -1 && rdata_description[ctx->rrtype][ctx->cnt] == RDESC_END)
-    return NULL;
-
-  int d = rdata_description[ctx->rrtype][++ctx->cnt];
-  if (d == RDESC_DOMAIN)
-    {
-      *len = process_domain_name(ctx->header, ctx->pktlen, &ctx->rdata, &ctx->rdlen, ctx->tmpbuf, PWN_EXTRACT);
-      if (!*len)
-        {
-          ctx->cnt = -2;
-          return NULL;
-        }
-      return ctx->tmpbuf;
-    }
-  else if (d == RDESC_END)
-    {
-      *len = ctx->rdlen;
-      return ctx->rdata;
-    }
-  else
-    {
-      unsigned char *ret = ctx->rdata;
-      ctx->rdlen -= d;
-      ctx->rdata += d;
-      *len = d;
-      return ret;
-    }
-}
-
-
-/* Check whether today/now is between date_start and date_end */
-static int check_date_range(unsigned long date_start, unsigned long date_end)
-{
-  /* TODO: double-check that time(0) is the correct time we are looking for */
-  /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
-  unsigned long curtime = time(0);
-
-  /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
-  return serial_compare_32(curtime, date_start) == SERIAL_GT
-         && serial_compare_32(curtime, date_end) == SERIAL_LT;
-}
-
-
-/* Sort RRs within a RRset in canonical order, according to RFC4034, §6.3
-   Notice that the RRDATA sections have been already normalized, so a memcpy
-   is sufficient.
-   NOTE: r1/r2 point immediately after the owner name. */
-
-struct {
-  struct dns_header *header;
-  size_t pktlen;
-} rrset_canonical_order_ctx;
-
-static int rrset_canonical_order(const void *r1, const void *r2)
-{
-  size_t r1len, r2len;
-  int rrtype;
-  unsigned char *pr1=*(unsigned char**)r1, *pr2=*(unsigned char**)r2;
-  unsigned char tmp1[MAXCDNAME], tmp2[MAXCDNAME];   /* TODO: use part of daemon->namebuff */
-  
-  GETSHORT(rrtype, pr1);
-  pr1 += 6; pr2 += 8;
-
-  RDataCForm cf1, cf2;
-  rdata_cform_init(&cf1, rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
-                   pr1, rrtype, tmp1);
-  rdata_cform_init(&cf2, rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
-                   pr2, rrtype, tmp2);
-  while ((pr1 = rdata_cform_next(&cf1, &r1len)) &&
-         (pr2 = rdata_cform_next(&cf2, &r2len)))
-    {
-      int res = memcmp(pr1, pr2, MIN(r1len,r2len));
-      if (res != 0)
-        return res;
-      if (r1len < r2len)
-        return -1;
-      if (r2len > r1len)
-        return 1;
-    }
-
-  /* If we reached this point, the two RRs are identical (or an error occurred).
-     RFC2181 says that an RRset is not allowed to contain duplicate
-     records. If it happens, it is a protocol error and anything goes. */
-  return 1;
-}
-
-typedef struct PendingRRSIGValidation
-{
-  VerifyAlgCtx *alg;
-  char *signer_name;
-  int keytag;
-} PendingRRSIGValidation;
-
-
 /* Convert from presentation format to wire format, in place.
    Also map UC -> LC.
    Note that using extract_name to get presentation format
@@ -442,80 +76,423 @@
   *(l-1) = 0;
 }
 
-
-/* Pass a resource record's rdata field through the currently-initailized digest algorithm.
-
-   We must pass the record in DNS wire format, but if the record contains domain names,
-   they must be uncompressed. This makes things very tricky, because  */
-static int digestalg_add_rdata(int sigtype, struct dns_header *header, size_t pktlen,
-                               unsigned char *rdata)
+/* Implement RFC1982 wrapped compare for 32-bit numbers */
+static int serial_compare_32(unsigned long s1, unsigned long s2)
 {
-  size_t len;
-  unsigned char *p;
-  unsigned short total;
-  unsigned char tmpbuf[MAXDNAME]; /* TODO: reuse part of daemon->namebuff */
-  RDataCForm cf1, cf2;
+  if (s1 == s2)
+    return SERIAL_EQ;
 
-  /* Initialize two iterations over the canonical form*/
-  rdata_cform_init(&cf1, header, pktlen, rdata, sigtype, tmpbuf);
-  cf2 = cf1;
+  if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
+      (s1 > s2 && (s1 - s2) > (1UL<<31)))
+    return SERIAL_LT;
+  if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
+      (s1 > s2 && (s1 - s2) < (1UL<<31)))
+    return SERIAL_GT;
+  return SERIAL_UNDEF;
+}
 
-  /* Iteration 1: go through the canonical record and count the total octects.
-     This number might be different from the non-canonical rdata length
-     because of domain names compression. */
-  total = 0;
-  while ((p = rdata_cform_next(&cf1, &len)))
-    total += len;
-  if (rdata_cform_error(&cf1))
+/* Check whether today/now is between date_start and date_end */
+static int check_date_range(unsigned long date_start, unsigned long date_end)
+{
+  unsigned long curtime = time(0);
+  
+  /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
+  return serial_compare_32(curtime, date_start) == SERIAL_GT
+    && serial_compare_32(curtime, date_end) == SERIAL_LT;
+}
+
+static u16 *get_desc(int type)
+{
+  /* List of RRtypes which include domains in the data.
+     0 -> domain
+     integer -> no of plain bytes
+     -1 -> end
+
+     zero is not a valid RRtype, so the final entry is returned for
+     anything which needs no mangling.
+  */
+  
+  static u16 rr_desc[] = 
+    { 
+      T_NS, 0, -1, 
+      T_MD, 0, -1,
+      T_MF, 0, -1,
+      T_CNAME, 0, -1,
+      T_SOA, 0, 0, -1,
+      T_MB, 0, -1,
+      T_MG, 0, -1,
+      T_MR, 0, -1,
+      T_PTR, 0, -1,
+      T_MINFO, 0, 0, -1,
+      T_MX, 2, 0, -1,
+      T_RP, 0, 0, -1,
+      T_AFSDB, 2, 0, -1,
+      T_RT, 2, 0, -1,
+      T_SIG, 18, 0, -1,
+      T_PX, 2, 0, 0, -1,
+      T_NXT, 0, -1,
+      T_KX, 2, 0, -1,
+      T_SRV, 6, 0, -1,
+      T_DNAME, 0, -1,
+      T_RRSIG, 18, 0, -1,
+      T_NSEC, 0, -1,
+      0, -1 /* wildcard/catchall */
+    }; 
+  
+  u16 *p = rr_desc;
+  
+  while (*p != type && *p != 0)
+    while (*p++ != (u16)-1);
+
+  return p+1;
+}
+
+/* Return bytes of canonicalised rdata, when the return value is zero, the remaining 
+   data, pointed to by *p, should be used raw. */
+static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, 
+		     unsigned char **p, u16 **desc)
+{
+  int d = **desc;
+  
+  (*desc)++;
+  
+  /* No more data needs mangling */
+  if (d == (u16)-1)
     return 0;
-
-  /* Iteration 2: process the canonical record through the hash function */
-  total = htons(total);
-  digestalg_add_data(&total, 2);
-
-  while ((p = rdata_cform_next(&cf2, &len)))
-    digestalg_add_data(p, len);
-
-  return 1;
-}
-
-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr)
-{
-  unsigned char *p;
-  char types[20];
   
-  querystr("dnssec", types, type);
-
-  if (addr->sa.sa_family == AF_INET) 
-    log_query(F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
-#ifdef HAVE_IPV6
+  if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
+    /* domain-name, canonicalise */
+    return to_wire(buff);
   else
-    log_query(F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
-#endif
-  
-  header->qdcount = htons(1);
-  header->ancount = htons(0);
-  header->nscount = htons(0);
-  header->arcount = htons(0);
-
-  header->hb3 = HB3_RD; 
-  SET_OPCODE(header, QUERY);
-  header->hb4 = HB4_CD;
-
-  /* ID filled in later */
-
-  p = (unsigned char *)(header+1);
-	
-  p = do_rfc1035_name(p, name);
-  *p++ = 0;
-  PUTSHORT(type, p);
-  PUTSHORT(class, p);
-
-  return add_do_bit(header, p - (unsigned char *)header, end);
+    { 
+      /* plain data preceding a domain-name, don't run off the end of the data */
+      if ((end - *p) < d)
+	d = end - *p;
+      
+      if (d != 0)
+	{
+	  memcpy(buff, *p, d);
+	  *p += d;
+	}
+      
+      return d;
+    }
 }
+
+/* Bubble sort the RRset into the canonical order. 
+   Note that the byte-streams from two RRs may get unsynced: consider 
+   RRs which have two domain-names at the start and then other data.
+   The domain-names may have different lengths in each RR, but sort equal
+
+   ------------
+   |abcde|fghi|
+   ------------
+   |abcd|efghi|
+   ------------
+
+   leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
+*/
+
+static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, 
+		       unsigned char **rrset, char *buff1, char *buff2)
+{
+  int swap, quit, i;
   
+  do
+    {
+      for (swap = 0, i = 0; i < rrsetidx-1; i++)
+	{
+	  int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
+	  u16 *dp1, *dp2;
+	  unsigned char *end1, *end2;
+	  unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
+	  unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
+	  
+	  p1 += 8; /* skip class, type, ttl */
+	  GETSHORT(rdlen1, p1);
+	  end1 = p1 + rdlen1;
+	  
+	  p2 += 8; /* skip class, type, ttl */
+	  GETSHORT(rdlen2, p2);
+	  end2 = p2 + rdlen2; 
+	  
+	  dp1 = dp2 = rr_desc;
+	  
+	  for (quit = 0, left1 = 0, left2 = 0; !quit;)
+	    {
+	      if ((len1 = get_rdata(header, plen, end1, buff1 + left1, &p1, &dp1)) == 0)
+		{
+		  quit = 1;
+		  len1 = end1 - p1;
+		  memcpy(buff1 + left1, p1, len1);
+		}
+	      len1 += left1;
+	      
+	      if ((len2 = get_rdata(header, plen, end2, buff2 + left2, &p2, &dp2)) == 0)
+		{
+		  quit = 1;
+		  len2 = end2 - p2;
+		  memcpy(buff2 + left2, p2, len2);
+		}
+	      len2 += left2;
+	       
+	      if (len1 > len2)
+		{
+		  left1 = len1 - len2;
+		  left2 = 0;
+		  len = len2;
+		}
+	      else
+		{
+		  left2 = len2 - len1;
+		  left1 = 0;
+		  len = len1;
+		}
+	      
+	      rc = memcmp(buff1, buff2, len);
+	      
+	      if (rc == 1 || (rc == 0 && quit && len2 > len1))
+		{
+		  unsigned char *tmp = rrset[i+1];
+		  rrset[i+1] = rrset[i];
+		  rrset[i] = tmp;
+		  swap = quit = 1;
+		}
+	    }
+	}
+    } while (swap);
+}
+
+/* Validate a single RRset (class, type, name) in the supplied DNS reply 
+   Return code:
+   STAT_SECURE   if it validates.
+   STAT_INSECURE can't validate (no RRSIG, bad packet).
+   STAT_BOGUS    signature is wrong.
+   STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
+
+   if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
+   otherwise find the key in the cache.
+*/
+static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, 
+		   int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
+{
+  static unsigned char **rrset = NULL, **sigs = NULL;
+  static int rrset_sz = 0, sig_sz = 0;
+  
+  unsigned char *p;
+  int rrsetidx, sigidx, res, rdlen, j;
+  struct crec *crecp = NULL;
+  int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag;
+  u16 *rr_desc = get_desc(type);
+
+  if (!(p = skip_questions(header, plen)))
+    return STAT_INSECURE;
+
+  /* look for an RRSIG record for this RRset and get pointers to each record */
+  for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount); 
+       j != 0; j--) 
+    {
+      unsigned char *pstart, *pdata;
+      int stype, sclass, sttl;
+
+      pstart = p;
+      
+      if (!(res = extract_name(header, plen, &p, name, 0, 10)))
+	return STAT_INSECURE; /* bad packet */
+      
+      GETSHORT(stype, p);
+      GETSHORT(sclass, p);
+      GETLONG(sttl, p);
+      
+      pdata = p;
+
+      GETSHORT(rdlen, p);
+      
+      (void)sttl;
+        
+      if (!CHECK_LEN(header, p, plen, rdlen))
+	 return STAT_INSECURE; /* bad packet */
+
+      if (res == 1 && sclass == class)
+	{
+	  if (stype == type)
+	    {
+	      if (rrsetidx == rrset_sz)
+		{
+		  unsigned char **new;
+
+		  /* expand */
+		  if (!(new = whine_malloc((rrset_sz + 5) * sizeof(unsigned char **))))
+		    return STAT_INSECURE;
+		  
+		  if (rrset)
+		    {
+		      memcpy(new, rrset, rrset_sz * sizeof(unsigned char **));
+		      free(rrset);
+		    }
+		  
+		  rrset = new;
+		  rrset_sz += 5;
+		}
+	      rrset[rrsetidx++] = pstart;
+	    }
+	  
+	  if (stype == T_RRSIG)
+	    {
+	      if (sigidx == sig_sz)
+		{
+		  unsigned char **new;
+		  
+		  /* expand */
+		  if (!(new = whine_malloc((sig_sz + 5) * sizeof(unsigned char **))))
+		    return STAT_INSECURE;
+		  
+		  if (sigs)
+		    {
+		      memcpy(new, sigs, sig_sz * sizeof(unsigned char **));
+		      free(sigs);
+		    }
+		  
+		  sigs = new;
+		  sig_sz += 5;
+		}
+	      sigs[sigidx++] = pdata;
+	    }
+	}
+     
+      if (!ADD_RDLEN(header, p, plen, rdlen))
+	return STAT_INSECURE;
+    }
+  
+  /* RRset empty, no RRSIGs */
+  if (rrsetidx == 0 || sigidx == 0)
+    return STAT_INSECURE; 
+  
+  /* Sort RRset records into canonical order. 
+     Note that at this point keyname and name buffs are
+     unused, and used as workspace by the sort. */
+  sort_rrset(header, plen, rr_desc, rrsetidx, rrset, name, keyname);
+         
+  /* Now try all the sigs to try and find one which validates */
+  for (j = 0; j <sigidx; j++)
+    {
+      unsigned char *psav;
+      int i, wire_len;
+      VerifyAlgCtx *alg;
+      u32 nsigttl;
+      
+      p = sigs[j];
+      
+      GETSHORT(rdlen, p);
+      
+      if (rdlen < 18)
+	return STAT_INSECURE; /* bad packet */ 
+      
+      psav = p;
+      
+      GETSHORT(type_covered, p);
+      algo = *p++;
+      labels = *p++;
+      GETLONG(orig_ttl, p);
+      GETLONG(sig_expiration, p);
+      GETLONG(sig_inception, p);
+      GETSHORT(key_tag, p);
+      
+      if (type_covered != type ||
+	  !check_date_range(sig_inception, sig_expiration) ||
+	  !verifyalg_supported(algo))
+	{
+	  /* covers wrong type or out of date - skip */
+	  p = psav;
+	  if (!ADD_RDLEN(header, p, plen, rdlen))
+	    return STAT_INSECURE;
+	  continue;
+	}
+      
+      if (!extract_name(header, plen, &p, keyname, 1, 0))
+	return STAT_INSECURE;
+      
+      /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
+      if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
+	return STAT_NEED_KEY;
+      
+      alg = verifyalg_alloc(algo);
+      alg->sig = p;
+      alg->siglen = rdlen - (p - psav);
+       
+      nsigttl = htonl(orig_ttl);
+      
+      digestalg_begin(alg->vtbl->digest_algo);
+      digestalg_add_data(psav, 18);
+      wire_len = to_wire(keyname);
+      digestalg_add_data(keyname, wire_len);
+      from_wire(keyname);
+      
+      /* TODO wildcard rules : 4035 5.3.2 */
+      for (i = 0; i < rrsetidx; ++i)
+	{
+	  int seg;
+	  unsigned char *end, *cp;
+	  u16 len, *dp;
+
+	  p = rrset[i];
+	  if (!extract_name(header, plen, &p, name, 1, 10)) 
+	    return STAT_INSECURE;
+	  wire_len = to_wire(name);
+	  digestalg_add_data(name, wire_len);
+	  from_wire(name); /* leave name unchanged on exit */
+	  digestalg_add_data(p, 4); /* class and type */
+	  digestalg_add_data(&nsigttl, 4);
+	  
+	  p += 8; /* skip class, type, ttl */
+	  GETSHORT(rdlen, p);
+	  end = p + rdlen;
+	  
+	  /* canonicalise rdata and calculate length of same, use name buffer as workspace */
+	  cp = p;
+	  dp = rr_desc;
+	  for (len = 0; (seg = get_rdata(header, plen, end, name, &cp, &dp)) != 0; len += seg);
+	  len += end - cp;
+	  len = htons(len);
+	  digestalg_add_data(&len, 2); 
+	  
+	  /* Now canonicalise again and digest. */
+	  cp = p;
+	  dp = rr_desc;
+	  while ((seg = get_rdata(header, plen, end, name, &cp, &dp)))
+	    digestalg_add_data(name, seg);
+	  if (cp != end)
+	    digestalg_add_data(cp,  end - cp);
+	  
+	  /* namebuff used for workspace, above, restore for next loop
+	     and to leave unchanged on exit */
+	  p = (unsigned char*)(rrset[i]);
+	  extract_name(header, plen, &p, name, 1, 0);
+	}
+    
+      memcpy(alg->digest, digestalg_final(),  digestalg_len());
+
+      if (key)
+	{
+	  if (algo_in == algo && keytag_in == key_tag &&
+	      alg->vtbl->verify(alg, key, keylen))
+	    return STAT_SECURE;
+	}
+      else
+	{
+	  /* iterate through all possible keys 4035 5.3.1 */
+	  for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
+	    if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag &&
+		alg->vtbl->verify(alg, crecp->addr.key.keydata, crecp->uid))
+	      return STAT_SECURE;
+	}
+    }
+
+  return STAT_BOGUS;
+}
+ 
 /* The DNS packet is expected to contain the answer to a DNSKEY query.
-   Leave name of qury in name.
+   Leave name of query in name.
    Put all DNSKEYs in the answer which are valid into the cache.
    return codes:
          STAT_INSECURE bad packet, no DNSKEYs in reply.
@@ -531,12 +508,13 @@
   int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
   struct blockdata *key;
 
-  if (ntohs(header->qdcount) != 1)
-    return STAT_INSECURE;
- 
-  if (!extract_name(header, plen, &p, name, 1, 4))
-    return STAT_INSECURE;
-  
+  if (ntohs(header->qdcount) != 1 ||
+      !extract_name(header, plen, &p, name, 1, 4))
+    {
+      strcpy(name, "<none>");
+      return STAT_INSECURE;
+    }
+
   GETSHORT(qtype, p);
   GETSHORT(qclass, p);
   
@@ -641,7 +619,6 @@
   return STAT_BOGUS;
 }
 
-
 /* The DNS packet is expected to contain the answer to a DS query
    Leave name of DS query in name.
    Put all DSs in the answer which are valid into the cache.
@@ -659,12 +636,13 @@
   int qtype, qclass, val, j, gotone;
   struct blockdata *key;
 
-  if (ntohs(header->qdcount) != 1)
-    return STAT_INSECURE;
- 
-  if (!extract_name(header, plen, &p, name, 1, 4))
-    return STAT_INSECURE;
-   
+  if (ntohs(header->qdcount) != 1 ||
+      !extract_name(header, plen, &p, name, 1, 4))
+    {
+      strcpy(name, "<none>");
+      return STAT_INSECURE;
+    }
+
   GETSHORT(qtype, p);
   GETSHORT(qclass, p);
 
@@ -735,179 +713,6 @@
   return STAT_SECURE;
 }
 
-
-
-/* Validate a single RRset (class, type, name) in the supplied DNS reply 
-   Return code:
-   STAT_SECURE   if it validates.
-   STAT_INSECURE can't validate (no RRSIG, bad packet).
-   STAT_BOGUS    signature is wrong.
-   STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
-
-   if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
-   otherwise find the key in the cache.
-*/
-int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, 
-		   int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
-{
-  unsigned char *p;
-  int rrsetidx, sigidx, res, rdlen, j;
-  struct crec *crecp = NULL;
-  void *rrset[MAXRRSET], *sigs[MAXRRSET];  /* TODO: max RRset size? */
-  int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag;
-
-  if (!(p = skip_questions(header, plen)))
-    return STAT_INSECURE;
-
-  /* look for an RRSIG record for this RRset and get pointers to each record */
-  for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount); 
-       j != 0; j--) 
-    {
-      unsigned char *pstart;
-      int stype, sclass, sttl;
-
-      if (!(res = extract_name(header, plen, &p, name, 0, 10)))
-	return STAT_INSECURE; /* bad packet */
-      
-      pstart = p;
-      
-      GETSHORT(stype, p);
-      GETSHORT(sclass, p);
-      GETLONG(sttl, p);
-      GETSHORT(rdlen, p);
-      
-      (void)sttl;
-        
-      if (!CHECK_LEN(header, p, plen, rdlen))
-	 return STAT_INSECURE; /* bad packet */
-
-      if (res == 1 && sclass == class)
-	{
-	  if (stype == type)
-	    {
-	      rrset[rrsetidx++] = pstart;
-	      if (rrsetidx == MAXRRSET)
-		return STAT_INSECURE; /* RRSET too big TODO */
-	    }
-	  
-	  if (stype == T_RRSIG)
-	    {
-	      sigs[sigidx++] = pstart;
-	      if (sigidx == MAXRRSET)
-		return STAT_INSECURE; /* RRSET too big TODO */
-	    }
-	}
-     
-      if (!ADD_RDLEN(header, p, plen, rdlen))
-	return STAT_INSECURE;
-    }
-  
-  /* RRset empty, no RRSIGs */
-  if (rrsetidx == 0 || sigidx == 0)
-    return STAT_INSECURE;
-     
-  /* Now try all the sigs to try and find one which validates */
-  for (j = 0; j <sigidx; j++)
-    {
-      unsigned char *psav;
-      int i, wire_len;
-      VerifyAlgCtx *alg;
-      u16 ntype, nclass;
-      u32 nsigttl;
-      
-      p = sigs[j] + 8; /* skip type, class and ttl */
-      
-      GETSHORT(rdlen, p);
-      
-      if (rdlen < 18)
-	return STAT_INSECURE; /* bad packet */ 
-      
-      psav = p;
-      
-      GETSHORT(type_covered, p);
-      algo = *p++;
-      labels = *p++;
-      GETLONG(orig_ttl, p);
-      GETLONG(sig_expiration, p);
-      GETLONG(sig_inception, p);
-      GETSHORT(key_tag, p);
-      
-      if (type_covered != type ||
-	  !check_date_range(sig_inception, sig_expiration) ||
-	  !verifyalg_supported(algo))
-	{
-	  /* covers wrong type or out of date - skip */
-	  p = psav;
-	  if (!ADD_RDLEN(header, p, plen, rdlen))
-	    return STAT_INSECURE;
-	  continue;
-	}
-      
-      if (!extract_name(header, plen, &p, keyname, 1, 0))
-	return STAT_INSECURE;
-      
-      /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
-      if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
-	return STAT_NEED_KEY;
-      
-      /* Sort RRset records in canonical order. */
-      rrset_canonical_order_ctx.header = header;
-      rrset_canonical_order_ctx.pktlen = plen;
-      qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
-      
-      alg = verifyalg_alloc(algo);
-      alg->sig = p;
-      alg->siglen = rdlen - (p - psav);
-       
-      ntype = htons(type);
-      nclass = htons(class);
-      nsigttl = htonl(orig_ttl);
-      
-      digestalg_begin(alg->vtbl->digest_algo);
-      digestalg_add_data(psav, 18);
-      wire_len = to_wire(keyname);
-      digestalg_add_data(keyname, wire_len);
-      from_wire(keyname);
-      
-      /* TODO wildcard rules : 4035 5.3.2 */
-      for (i = 0; i < rrsetidx; ++i)
-	{
-	  p = (unsigned char*)(rrset[i]);
-	  
-	  wire_len = to_wire(name);
-	  digestalg_add_data(name, wire_len);
-	  from_wire(name);
-	  digestalg_add_data(&ntype, 2);
-	  digestalg_add_data(&nclass, 2);
-	  digestalg_add_data(&nsigttl, 4);
-	  
-	  p += 8;
-	  if (!digestalg_add_rdata(type, header, plen, p))
-	    return STAT_INSECURE;
-	}
-    
-      memcpy(alg->digest, digestalg_final(),  digestalg_len());
-
-      if (key)
-	{
-	  if (algo_in == algo && keytag_in == key_tag &&
-	      alg->vtbl->verify(alg, key, keylen))
-	    return STAT_SECURE;
-	}
-      else
-	{
-	  /* iterate through all possible keys 4035 5.3.1 */
-	  for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
-	    if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag &&
-		alg->vtbl->verify(alg, crecp->addr.key.keydata, crecp->uid))
-	      return STAT_SECURE;
-	}
-    }
-
-  return STAT_BOGUS;
-}
- 
-
 /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
 int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class)
 {
@@ -987,5 +792,39 @@
     }
 }
 
+size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr)
+{
+  unsigned char *p;
+  char types[20];
+  
+  querystr("dnssec", types, type);
 
+  if (addr->sa.sa_family == AF_INET) 
+    log_query(F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
+#ifdef HAVE_IPV6
+  else
+    log_query(F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
+#endif
+  
+  header->qdcount = htons(1);
+  header->ancount = htons(0);
+  header->nscount = htons(0);
+  header->arcount = htons(0);
+
+  header->hb3 = HB3_RD; 
+  SET_OPCODE(header, QUERY);
+  header->hb4 = HB4_CD;
+
+  /* ID filled in later */
+
+  p = (unsigned char *)(header+1);
+	
+  p = do_rfc1035_name(p, name);
+  *p++ = 0;
+  PUTSHORT(type, p);
+  PUTSHORT(class, p);
+
+  return add_do_bit(header, p - (unsigned char *)header, end);
+}
+  
 #endif /* HAVE_DNSSEC */
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 7d48910..0b254e3 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -493,7 +493,7 @@
       else if (is_sign && 
 	       i == arcount - 1 && 
 	       class == C_ANY && 
-	       (type == T_SIG || type == T_TSIG))
+	       type == T_TSIG)
 	*is_sign = 1;
     }