]> git.ipfire.org Git - people/ms/dnsmasq.git/blobdiff - src/rfc1035.c
import of dnsmasq-2.46.tar.gz
[people/ms/dnsmasq.git] / src / rfc1035.c
index fd02f1d17bfa502865de4ec47d3d699a2bfa6a38..5b468eb96c0598a1b5383563a42a85f22278fc74 100644 (file)
@@ -1,13 +1,17 @@
-/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2008 Simon Kelley
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; version 2 dated June, 1991.
-
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
+     
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "dnsmasq.h"
@@ -17,8 +21,14 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp,
                               unsigned long ttl, unsigned int *offset, unsigned short type, 
                               unsigned short class, char *format, ...);
 
+#define CHECK_LEN(header, pp, plen, len) \
+    ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
+
+#define ADD_RDLEN(header, pp, plen, len) \
+    (!CHECK_LEN(header, pp, plen, len) ? 0 : (int)((pp) += (len)), 1)
+
 static int extract_name(HEADER *header, size_t plen, unsigned char **pp, 
-                       char *name, int isExtract)
+                       char *name, int isExtract, int extrabytes)
 {
   unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
   unsigned int j, l, hops = 0;
@@ -27,19 +37,47 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
   if (isExtract)
     *cp = 0;
 
-  while ((l = *p++))
-    {
-      unsigned int label_type = l & 0xc0;
+  while (1)
+    { 
+      unsigned int label_type;
+
+      if (!CHECK_LEN(header, p, plen, 1))
+       return 0;
+      
+      if ((l = *p++) == 0) 
+       /* end marker */
+       {
+         /* check that there are the correct no of bytes after the name */
+         if (!CHECK_LEN(header, p, plen, extrabytes))
+           return 0;
+         
+         if (isExtract)
+           {
+             if (cp != (unsigned char *)name)
+               cp--;
+             *cp = 0; /* terminate: lose final period */
+           }
+         else if (*cp != 0)
+           retvalue = 2;
+         
+         if (p1) /* we jumped via compression */
+           *pp = p1;
+         else
+           *pp = p;
+         
+         return retvalue;
+       }
+
+      label_type = l & 0xc0;
+      
       if (label_type == 0xc0) /* pointer */
        { 
-         if ((size_t)(p - (unsigned char *)header + 1) >= plen)
+         if (!CHECK_LEN(header, p, plen, 1))
            return 0;
              
          /* get offset */
          l = (l&0x3f) << 8;
          l |= *p++;
-         if (l >= plen) 
-           return 0;
          
          if (!p1) /* first jump, save location to go back to */
            p1 = p;
@@ -70,7 +108,7 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
          /* output is \[x<hex>/siz]. which is digs+9 chars */
          if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
            return 0;
-         if ((size_t)(p - (unsigned char *)header + ((count-1)>>3) + 1) >= plen)
+         if (!CHECK_LEN(header, p, plen, (count-1)>>3))
            return 0;
 
          *cp++ = '\\';
@@ -94,8 +132,9 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
        { /* label_type = 0 -> label. */
          if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
            return 0;
-         if ((size_t)(p - (unsigned char *)header + 1) >= plen)
+         if (!CHECK_LEN(header, p, plen, l))
            return 0;
+         
          for(j=0; j<l; j++, p++)
            if (isExtract)
              {
@@ -128,26 +167,7 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
          else if (*cp != 0 && *cp++ != '.')
            retvalue = 2;
        }
-      
-      if ((unsigned int)(p - (unsigned char *)header) >= plen)
-       return 0;
-    }
-
-  if (isExtract)
-    {
-      if (cp != (unsigned char *)name)
-       cp--;
-      *cp = 0; /* terminate: lose final period */
     }
-  else if (*cp != 0)
-    retvalue = 2;
-    
-  if (p1) /* we jumped via compression */
-    *pp = p1;
-  else
-    *pp = p;
-
-  return retvalue;
 }
  
 /* Max size of input string (for IPv6) is 75 chars.) */
@@ -223,7 +243,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
       if (*name == '\\' && *(name+1) == '[' && 
          (*(name+2) == 'x' || *(name+2) == 'X'))
        {         
-         for (j = 0, cp1 = name+3; *cp1 && isxdigit(*cp1) && j < 32; cp1++, j++)
+         for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
            {
              char xdig[2];
              xdig[0] = *cp1;
@@ -257,15 +277,17 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
   return 0;
 }
 
-static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen)
+static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen, int extrabytes)
 {
   while(1)
     {
-      unsigned int label_type = (*ansp) & 0xc0;
+      unsigned int label_type;
       
-      if ((unsigned int)(ansp - (unsigned char *)header) >= plen)
+      if (!CHECK_LEN(header, ansp, plen, 1))
        return NULL;
       
+      label_type = (*ansp) & 0xc0;
+
       if (label_type == 0xc0)
        {
          /* pointer for compression. */
@@ -279,6 +301,9 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
          /* Extended label type */
          unsigned int count;
          
+         if (!CHECK_LEN(header, ansp, plen, 2))
+           return NULL;
+         
          if (((*ansp++) & 0x3f) != 1)
            return NULL; /* we only understand bitstrings */
          
@@ -292,29 +317,32 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
       else
        { /* label type == 0 Bottom six bits is length */
          unsigned int len = (*ansp++) & 0x3f;
+         
+         if (!ADD_RDLEN(header, ansp, plen, len))
+           return NULL;
+
          if (len == 0)
            break; /* zero length label marks the end. */
-         
-         ansp += len;
        }
     }
+
+  if (!CHECK_LEN(header, ansp, plen, extrabytes))
+    return NULL;
   
   return ansp;
 }
 
 static unsigned char *skip_questions(HEADER *header, size_t plen)
 {
-  int q, qdcount = ntohs(header->qdcount);
+  int q;
   unsigned char *ansp = (unsigned char *)(header+1);
 
-  for (q = 0; q<qdcount; q++)
+  for (q = ntohs(header->qdcount); q != 0; q--)
     {
-      if (!(ansp = skip_name(ansp, header, plen)))
+      if (!(ansp = skip_name(ansp, header, plen, 4)))
        return NULL;
       ansp += 4; /* class and type */
     }
-  if ((unsigned int)(ansp - (unsigned char *)header) > plen) 
-     return NULL;
   
   return ansp;
 }
@@ -325,13 +353,12 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
   
   for (i = 0; i < count; i++)
     {
-      if (!(ansp = skip_name(ansp, header, plen)))
+      if (!(ansp = skip_name(ansp, header, plen, 10)))
        return NULL; 
       ansp += 8; /* type, class, TTL */
       GETSHORT(rdlen, ansp);
-      if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) 
+      if (!ADD_RDLEN(header, ansp, plen, rdlen))
        return NULL;
-      ansp += rdlen;
     }
 
   return ansp;
@@ -341,16 +368,17 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
    retransmision and to detect answers to questions we didn't ask, which 
    might be poisoning attacks. Note that we decode the name rather 
    than CRC the raw bytes, since replies might be compressed differently. 
-   We ignore case in the names for the same reason. */
+   We ignore case in the names for the same reason. Return all-ones
+   if there is not question section. */
 unsigned int questions_crc(HEADER *header, size_t plen, char *name)
 {
   int q;
   unsigned int crc = 0xffffffff;
   unsigned char *p1, *p = (unsigned char *)(header+1);
 
-  for (q = 0; q < ntohs(header->qdcount); q++
+  for (q = ntohs(header->qdcount); q != 0; q--
     {
-      if (!extract_name(header, plen, &p, name, 1))
+      if (!extract_name(header, plen, &p, name, 1, 4))
        return crc; /* bad packet */
       
       for (p1 = (unsigned char *)name; *p1; p1++)
@@ -376,7 +404,7 @@ unsigned int questions_crc(HEADER *header, size_t plen, char *name)
        }
 
       p += 4;
-      if ((unsigned int)(p - (unsigned char *)header) > plen)
+      if (!CHECK_LEN(header, p, plen, 0))
        return crc; /* bad packet */
     }
 
@@ -388,12 +416,13 @@ size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t
 {
   unsigned char *ansp = skip_questions(header, plen);
     
+  /* if packet is malformed, just return as-is. */
   if (!ansp)
-    return 0;
+    return plen;
   
   if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
                            header, plen)))
-    return 0;
+    return plen;
     
   /* restore pseudoheader */
   if (pheader && ntohs(header->arcount) == 0)
@@ -407,74 +436,140 @@ size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t
   return ansp - (unsigned char *)header;
 }
 
-unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t  *len, unsigned char **p)
+unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign)
 {
   /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. 
-     also return length of pseudoheader in *len and pointer to the UDP size in *p */
+     also return length of pseudoheader in *len and pointer to the UDP size in *p
+     Finally, check to see if a packet is signed. If it is we cannot change a single bit before
+     forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
   
   int i, arcount = ntohs(header->arcount);
-  unsigned char *ansp;
-  unsigned short rdlen, type;
+  unsigned char *ansp = (unsigned char *)(header+1);
+  unsigned short rdlen, type, class;
+  unsigned char *ret = NULL;
 
-  if (arcount == 0 || !(ansp = skip_questions(header, plen)))
+  if (is_sign)
+    {
+      *is_sign = 0;
+
+      if (header->opcode == QUERY)
+       {
+         for (i = ntohs(header->qdcount); i != 0; i--)
+           {
+             if (!(ansp = skip_name(ansp, header, plen, 4)))
+               return NULL;
+             
+             GETSHORT(type, ansp); 
+             GETSHORT(class, ansp);
+             
+             if (class == C_IN && type == T_TKEY)
+               *is_sign = 1;
+           }
+       }
+    }
+  else
+    {
+      if (!(ansp = skip_questions(header, plen)))
+       return NULL;
+    }
+    
+  if (arcount == 0)
     return NULL;
   
   if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
     return NULL; 
-    
+  
   for (i = 0; i < arcount; i++)
     {
       unsigned char *save, *start = ansp;
-      if (!(ansp = skip_name(ansp, header, plen)))
+      if (!(ansp = skip_name(ansp, header, plen, 10)))
        return NULL; 
 
       GETSHORT(type, ansp);
       save = ansp;
-      ansp += 6; /* class, TTL */
+      GETSHORT(class, ansp);
+      ansp += 4; /* TTL */
       GETSHORT(rdlen, ansp);
-      if ((size_t)(ansp + rdlen - (unsigned char *)header) > plen) 
+      if (!ADD_RDLEN(header, ansp, plen, rdlen))
        return NULL;
-       ansp += rdlen;
-       if (type == T_OPT)
+      if (type == T_OPT)
        {
          if (len)
            *len = ansp - start;
          if (p)
            *p = save;
-         return start;
+         ret = start;
        }
+      else if (is_sign && 
+              i == arcount - 1 && 
+              class == C_ANY && 
+              (type == T_SIG || type == T_TSIG))
+       *is_sign = 1;
     }
   
-  return NULL;
+  return ret;
 }
       
   
 /* is addr in the non-globally-routed IP space? */ 
-static int private_net(struct all_addr *addrp
+static int private_net(struct in_addr addr
 {
-  struct in_addr addr = *(struct in_addr *)addrp;
-  if (inet_netof(addr) == 0xA ||
-      (inet_netof(addr) >= 0xAC10 && inet_netof(addr) < 0xAC20) ||
-      (inet_netof(addr) >> 8) == 0xC0A8) 
-    return 1;
-  else 
-    return 0;
+  in_addr_t ip_addr = ntohl(addr.s_addr);
+
+  return
+    ((ip_addr & 0xFF000000) == 0x7F000000)  /* 127.0.0.0/8    (loopback) */ || 
+    ((ip_addr & 0xFFFF0000) == 0xC0A80000)  /* 192.168.0.0/16 (private)  */ ||
+    ((ip_addr & 0xFF000000) == 0x0A000000)  /* 10.0.0.0/8     (private)  */ ||
+    ((ip_addr & 0xFFF00000) == 0xAC100000)  /* 172.16.0.0/12  (private)  */ ||
+    ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;
 }
-static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
+
+static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
 {
-  for (; doctor; doctor = doctor->next)
-    if (is_same_net(doctor->in, *addr, doctor->mask))
-      {
-       addr->s_addr &= ~doctor->mask.s_addr;
-       addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
-       /* Since we munged the data, the server it came from is no longer authoritative */
-       header->aa = 0;
-       break;
-      }
+  int i, qtype, qclass, rdlen;
+  unsigned long ttl;
+
+  for (i = count; i != 0; i--)
+    {
+      if (!(p = skip_name(p, header, qlen, 10)))
+       return 0; /* bad packet */
+      
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      GETLONG(ttl, p);
+      GETSHORT(rdlen, p);
+      
+      if ((qclass == C_IN) && (qtype == T_A))
+       {
+         struct doctor *doctor;
+         struct in_addr addr;
+         
+         if (!CHECK_LEN(header, p, qlen, INADDRSZ))
+           return 0;
+          
+          /* alignment */
+         memcpy(&addr, p, INADDRSZ);
+         
+         for (doctor = daemon->doctors; doctor; doctor = doctor->next)
+           if (is_same_net(doctor->in, addr, doctor->mask))
+             {
+               addr.s_addr &= ~doctor->mask.s_addr;
+               addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
+               /* Since we munged the data, the server it came from is no longer authoritative */
+               header->aa = 0;
+               memcpy(p, &addr, INADDRSZ);
+               break;
+             }
+       }
+      
+      if (!ADD_RDLEN(header, p, qlen, rdlen))
+        return 0; /* bad packet */
+    }
+  
+  return p; 
 }
 
-static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
+static int find_soa(HEADER *header, size_t qlen)
 {
   unsigned char *p;
   int qtype, qclass, rdlen;
@@ -483,12 +578,12 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
   
   /* first move to NS section and find TTL from any SOA section */
   if (!(p = skip_questions(header, qlen)) ||
-      !(p = skip_section(p, ntohs(header->ancount), header, qlen)))
-    return 0; /* bad packet */
+      !(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
+    return 0;  /* bad packet */
   
-  for (i=0; i<ntohs(header->nscount); i++)
+  for (i = ntohs(header->nscount); i != 0; i--)
     {
-      if (!(p = skip_name(p, header, qlen)))
+      if (!(p = skip_name(p, header, qlen, 10)))
        return 0; /* bad packet */
       
       GETSHORT(qtype, p); 
@@ -503,10 +598,10 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
            minttl = ttl;
 
          /* MNAME */
-         if (!(p = skip_name(p, header, qlen)))
+         if (!(p = skip_name(p, header, qlen, 0)))
            return 0;
          /* RNAME */
-         if (!(p = skip_name(p, header, qlen)))
+         if (!(p = skip_name(p, header, qlen, 20)))
            return 0;
          p += 16; /* SERIAL REFRESH RETRY EXPIRE */
          
@@ -514,44 +609,30 @@ static int find_soa(HEADER *header, struct doctor *doctor, size_t qlen)
          if (ttl < minttl)
            minttl = ttl;
        }
-      else
-       p += rdlen;
-      
-      if ((size_t)(p - (unsigned char *)header) > qlen)
+      else if (!ADD_RDLEN(header, p, qlen, rdlen))
        return 0; /* bad packet */
     }
-  if (doctor)
-    for (i=0; i<ntohs(header->arcount); i++)
-      {
-       if (!(p = skip_name(p, header, qlen)))
-         return 0; /* bad packet */
-      
-       GETSHORT(qtype, p); 
-       GETSHORT(qclass, p);
-       GETLONG(ttl, p);
-       GETSHORT(rdlen, p);
-       
-       if ((qclass == C_IN) && (qtype == T_A))
-         dns_doctor(header, doctor, (struct in_addr *)p);
-       
-       p += rdlen;
-       
-       if ((size_t)(p - (unsigned char *)header) > qlen)
-         return 0; /* bad packet */
-      }
-  return found_soa ? minttl : 0;
+  
+  /* rewrite addresses in additioal section too */
+  if (!do_doctor(p, ntohs(header->arcount), header, qlen))
+    return 0;
+  
+  if (!found_soa)
+    minttl = daemon->neg_ttl;
+
+  return minttl;
 }
 
 /* Note that the following code can create CNAME chains that don't point to a real record,
    either because of lack of memory, or lack of SOA records.  These are treated by the cache code as 
-   expired and cleaned out that way. */
-void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, struct daemon *daemon)
+   expired and cleaned out that way. 
+   Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
+int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
 {
-  unsigned char *p, *p1, *endrr;
+  unsigned char *p, *p1, *endrr, *namep;
   int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
   unsigned long ttl = 0;
+  struct all_addr addr;
 
   cache_start_insert();
 
@@ -559,21 +640,22 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
   if (daemon->doctors)
     {
       searched_soa = 1;
-      ttl = find_soa(header, daemon->doctors, qlen);
+      ttl = find_soa(header, qlen);
     }
   
   /* go through the questions. */
   p = (unsigned char *)(header+1);
   
-  for (i = 0; i<ntohs(header->qdcount); i++)
+  for (i = ntohs(header->qdcount); i != 0; i--)
     {
       int found = 0, cname_count = 5;
       struct crec *cpp = NULL;
       int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
       unsigned long cttl = ULONG_MAX, attl;
       
-      if (!extract_name(header, qlen, &p, name, 1))
-       return; /* bad packet */
+      namep = p;
+      if (!extract_name(header, qlen, &p, name, 1, 4))
+       return 0; /* bad packet */
            
       GETSHORT(qtype, p); 
       GETSHORT(qclass, p);
@@ -585,7 +667,6 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
         represent them in the cache. */
       if (qtype == T_PTR)
        { 
-         struct all_addr addr;
          int name_encoding = in_arpa_name_2_addr(name, &addr);
          
          if (!name_encoding)
@@ -595,12 +676,15 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
            {
            cname_loop:
              if (!(p1 = skip_questions(header, qlen)))
-               return;
+               return 0;
              
-             for (j = 0; j<ntohs(header->ancount); j++
+             for (j = ntohs(header->ancount); j != 0; j--
                {
-                 if (!(res = extract_name(header, qlen, &p1, name, 0)))
-                   return; /* bad packet */
+                 unsigned char *tmp = namep;
+                 /* the loop body overwrites the original name, so get it back here. */
+                 if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
+                     !(res = extract_name(header, qlen, &p1, name, 0, 10)))
+                   return 0; /* bad packet */
                  
                  GETSHORT(aqtype, p1); 
                  GETSHORT(aqclass, p1);
@@ -614,13 +698,13 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
 
                  if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
                    {
-                     if (!extract_name(header, qlen, &p1, name, 1))
-                       return;
+                     if (!extract_name(header, qlen, &p1, name, 1, 0))
+                       return 0;
                      
                      if (aqtype == T_CNAME)
                        {
                          if (!cname_count--)
-                           return; /* looped CNAMES */
+                           return 0; /* looped CNAMES */
                          goto cname_loop;
                        }
                      
@@ -629,8 +713,8 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
                    }
                  
                  p1 = endrr;
-                 if ((size_t)(p1 - (unsigned char *)header) > qlen)
-                   return; /* bad packet */
+                 if (!CHECK_LEN(header, p1, qlen, 0))
+                   return 0; /* bad packet */
                }
            }
          
@@ -639,22 +723,29 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
              if (!searched_soa)
                {
                  searched_soa = 1;
-                 ttl = find_soa(header, NULL, qlen);
+                 ttl = find_soa(header, qlen);
                }
              if (ttl)
-               cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); 
+               cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); 
            }
        }
       else
        {
          /* everything other than PTR */
          struct crec *newc;
-         
+         int addrlen;
+
          if (qtype == T_A)
-           flags |= F_IPV4;
+           {
+             addrlen = INADDRSZ;
+             flags |= F_IPV4;
+           }
 #ifdef HAVE_IPV6
          else if (qtype == T_AAAA)
-           flags |= F_IPV6;
+           {
+             addrlen = IN6ADDRSZ;
+             flags |= F_IPV6;
+           }
 #endif
          else 
            continue;
@@ -663,12 +754,12 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
            {
            cname_loop1:
              if (!(p1 = skip_questions(header, qlen)))
-               return;
+               return 0;
              
-             for (j = 0; j<ntohs(header->ancount); j++
+             for (j = ntohs(header->ancount); j != 0; j--
                {
-                 if (!(res = extract_name(header, qlen, &p1, name, 0)))
-                   return; /* bad packet */
+                 if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
+                   return 0; /* bad packet */
                  
                  GETSHORT(aqtype, p1); 
                  GETSHORT(aqclass, p1);
@@ -681,7 +772,7 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
                      if (aqtype == T_CNAME)
                        {
                          if (!cname_count--)
-                           return; /* looped CNAMES */
+                           return 0; /* looped CNAMES */
                          newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
                          if (newc && cpp)
                            {
@@ -693,16 +784,26 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
                          if (attl < cttl)
                            cttl = attl;
                          
-                         if (!extract_name(header, qlen, &p1, name, 1))
-                           return;
+                         if (!extract_name(header, qlen, &p1, name, 1, 0))
+                           return 0;
                          goto cname_loop1;
                        }
                      else
                        {
                          found = 1;
-                         if (aqtype == T_A)
-                           dns_doctor(header, daemon->doctors, (struct in_addr *)p1);
-                         newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD);
+                         
+                         /* copy address into aligned storage */
+                         if (!CHECK_LEN(header, p1, qlen, addrlen))
+                           return 0; /* bad packet */
+                         memcpy(&addr, p1, addrlen);
+                         
+                         /* check for returned address in private space */
+                         if ((daemon->options & OPT_NO_REBIND) &&
+                             (flags & F_IPV4) &&
+                             private_net(addr.addr.addr4))
+                           return 1;
+                         
+                         newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
                          if (newc && cpp)
                            {
                              cpp->addr.cname.cache = newc;
@@ -713,8 +814,8 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
                    }
                  
                  p1 = endrr;
-                 if ((size_t)(p1 - (unsigned char *)header) > qlen)
-                   return; /* bad packet */
+                 if (!CHECK_LEN(header, p1, qlen, 0))
+                   return 0; /* bad packet */
                }
            }
          
@@ -723,13 +824,13 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
              if (!searched_soa)
                {
                  searched_soa = 1;
-                 ttl = find_soa(header, NULL, qlen);
+                 ttl = find_soa(header, qlen);
                }
              /* If there's no SOA to get the TTL from, but there is a CNAME 
-                pointing at this, inherit it's TTL */
+                pointing at this, inherit its TTL */
              if (ttl || cpp)
                {
-                 newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);    
+                 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);    
                  if (newc && cpp)
                    {
                      cpp->addr.cname.cache = newc;
@@ -740,11 +841,16 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
        }
     }
   
-  cache_end_insert();
+  /* Don't put stuff from a truncated packet into the cache, but do everything else */
+  if (!header->tc)
+    cache_end_insert();
+
+  return 0;
 }
 
 /* If the packet holds exactly one query
-   return 1 and leave the name from the query in name. */
+   return F_IPV4 or F_IPV6  and leave the name from the query in name. 
+   Abuse F_BIGNAME to indicate an NS query - yuck. */
 
 unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
 {
@@ -757,7 +863,7 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
   if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
     return 0; /* must be exactly one query. */
   
-  if (!extract_name(header, qlen, &p, name, 1))
+  if (!extract_name(header, qlen, &p, name, 1, 4))
     return 0; /* bad packet */
    
   GETSHORT(qtype, p); 
@@ -774,6 +880,8 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
        return F_IPV6;
       if (qtype == T_ANY)
        return  F_IPV4 | F_IPV6;
+      if (qtype == T_NS || qtype == T_SOA)
+       return F_QUERY | F_BIGNAME;
     }
   
   return F_QUERY;
@@ -794,7 +902,7 @@ size_t setup_reply(HEADER *header, size_t qlen,
   header->ancount = htons(0); /* no answers unless changed below */
   if (flags == F_NEG)
     header->rcode = SERVFAIL; /* couldn't get memory */
-  else if (flags == F_NOERR || flags == F_QUERY)
+  else if (flags == F_NOERR)
     header->rcode = NOERROR; /* empty domain */
   else if (flags == F_NXDOMAIN)
     header->rcode = NXDOMAIN;
@@ -821,12 +929,14 @@ size_t setup_reply(HEADER *header, size_t qlen,
 }
 
 /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
-int check_for_local_domain(char *name, time_t now, struct daemon *daemon)
+int check_for_local_domain(char *name, time_t now)
 {
   struct crec *crecp;
   struct mx_srv_record *mx;
   struct txt_record *txt;
-    
+  struct interface_name *intr;
+  struct ptr_record *ptr;
+  
   if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
       (crecp->flags & (F_HOSTS | F_DHCP)))
     return 1;
@@ -838,7 +948,15 @@ int check_for_local_domain(char *name, time_t now, struct daemon *daemon)
   for (txt = daemon->txt; txt; txt = txt->next)
     if (hostname_isequal(name, txt->name))
       return 1;
-  
+
+  for (intr = daemon->int_names; intr; intr = intr->next)
+    if (hostname_isequal(name, intr->name))
+      return 1;
+
+  for (ptr = daemon->ptr; ptr; ptr = ptr->next)
+    if (hostname_isequal(name, ptr->name))
+      return 1;
   return 0;
 }
 
@@ -857,9 +975,9 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
   if (!(p = skip_questions(header, qlen)))
     return 0; /* bad packet */
 
-  for (i=0; i<ntohs(header->ancount); i++)
+  for (i = ntohs(header->ancount); i != 0; i--)
     {
-      if (!extract_name(header, qlen, &p, name, 1))
+      if (!extract_name(header, qlen, &p, name, 1, 10))
        return 0; /* bad packet */
   
       GETSHORT(qtype, p); 
@@ -868,19 +986,25 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
       GETSHORT(rdlen, p);
       
       if (qclass == C_IN && qtype == T_A)
-       for (baddrp = baddr; baddrp; baddrp = baddrp->next)
-         if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
-           {
-             /* Found a bogus address. Insert that info here, since there no SOA record
-                to get the ttl from in the normal processing */
-             cache_start_insert();
-             cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
-             cache_end_insert();
-             
-             return 1;
-           }
+       {
+         if (!CHECK_LEN(header, p, qlen, INADDRSZ))
+           return 0;
+         
+         for (baddrp = baddr; baddrp; baddrp = baddrp->next)
+           if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
+             {
+               /* Found a bogus address. Insert that info here, since there no SOA record
+                  to get the ttl from in the normal processing */
+               cache_start_insert();
+               cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
+               cache_end_insert();
+               
+               return 1;
+             }
+       }
       
-      p += rdlen;
+      if (!ADD_RDLEN(header, p, qlen, rdlen))
+       return 0;
     }
   
   return 0;
@@ -950,6 +1074,16 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
        memcpy(p, sval, usval);
        p += usval;
        break;
+
+      case 'z':
+       sval = va_arg(ap, char *);
+       usval = sval ? strlen(sval) : 0;
+       if (usval > 255)
+         usval = 255;
+       *p++ = (unsigned char)usval;
+       memcpy(p, sval, usval);
+       p += usval;
+       break;
       }
 
   va_end(ap);  /* clean up variable argument pointer */
@@ -969,33 +1103,42 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
   return 1;
 }
 
+static unsigned long crec_ttl(struct crec *crecp, time_t now)
+{
+  /* Return 0 ttl for DHCP entries, which might change
+     before the lease expires. */
+
+  if  (crecp->flags & (F_IMMORTAL | F_DHCP))
+    return daemon->local_ttl;
+  
+  return crecp->ttd - now;
+}
+  
+
 /* return zero if we can't answer from cache, or packet size if we can */
-size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *daemon, 
+size_t answer_request(HEADER *header, char *limit, size_t qlen,  
                      struct in_addr local_addr, struct in_addr local_netmask, time_t now) 
 {
   char *name = daemon->namebuff;
   unsigned char *p, *ansp, *pheader;
-  int qtype, qclass, is_arpa;
+  int qtype, qclass;
   struct all_addr addr;
   unsigned int nameoffset;
   unsigned short flag;
-  int qdcount = ntohs(header->qdcount); 
   int q, ans, anscount = 0, addncount = 0;
   int dryrun = 0, sec_reqd = 0;
+  int is_sign;
   struct crec *crecp;
   int nxdomain = 0, auth = 1, trunc = 0;
   struct mx_srv_record *rec;
  
-  if (!qdcount || header->opcode != QUERY )
-    return 0;
-
   /* If there is an RFC2671 pseudoheader then it will be overwritten by
      partial replies, so we have to do a dry run to see if we can answer
      the query. We check to see if the do bit is set, if so we always
      forward rather than answering from the cache, which doesn't include
      security information. */
 
-  if (find_pseudoheader(header, qlen, NULL, &pheader))
+  if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
     { 
       unsigned short udpsz, ext_rcode, flags;
       unsigned char *psave = pheader;
@@ -1010,12 +1153,15 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
         than we allow, trim it so that we don't get an overlarge
         response from upstream */
 
-      if (udpsz > daemon->edns_pktsz)
+      if (!is_sign && (udpsz > daemon->edns_pktsz))
        PUTSHORT(daemon->edns_pktsz, psave); 
 
       dryrun = 1;
     }
 
+  if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
+    return 0;
+  
   for (rec = daemon->mxnames; rec; rec = rec->next)
     rec->offset = 0;
   
@@ -1027,19 +1173,15 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
   /* now process each question, answers go in RRs after the question */
   p = (unsigned char *)(header+1);
 
-  for (q=0; q<qdcount; q++)
+  for (q = ntohs(header->qdcount); q != 0; q--)
     {
       /* save pointer to name for copying into answers */
       nameoffset = p - (unsigned char *)header;
 
       /* now extract name as .-concatenated string into name */
-      if (!extract_name(header, qlen, &p, name, 1))
+      if (!extract_name(header, qlen, &p, name, 1, 4))
        return 0; /* bad packet */
-      
-      /* see if it's w.z.y.z.in-addr.arpa format */
-
-      is_arpa = in_arpa_name_2_addr(name, &addr);
-      
+            
       GETSHORT(qtype, p); 
       GETSHORT(qclass, p);
 
@@ -1055,7 +1197,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                  ans = 1;
                  if (!dryrun)
                    {
-                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
+                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>");
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                              daemon->local_ttl, NULL,
                                              T_TXT, t->class, "t", t->len, t->txt))
@@ -1070,62 +1212,102 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
        {
          if (qtype == T_PTR || qtype == T_ANY)
            {
-             if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
-               { 
-                 if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
+             /* see if it's w.z.y.z.in-addr.arpa format */
+             int is_arpa = in_arpa_name_2_addr(name, &addr);
+             struct ptr_record *ptr;
+             struct interface_name* intr = NULL;
+
+             for (ptr = daemon->ptr; ptr; ptr = ptr->next)
+               if (hostname_isequal(name, ptr->name))
+                 break;
+
+             if (is_arpa == F_IPV4)
+               for (intr = daemon->int_names; intr; intr = intr->next)
+                 {
+                   if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
+                     break;
+                   else
+                     while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                       intr = intr->next;
+                 }
+             
+             if (intr)
+               {
+                 ans = 1;
+                 if (!dryrun)
                    {
-                     /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
-                     ans = 1;
-                     nxdomain = 1;
-                     if (!dryrun)
-                       log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
+                     log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
+                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                             daemon->local_ttl, NULL,
+                                             T_PTR, C_IN, "d", intr->name))
+                       anscount++;
                    }
                }
-             else do 
-               { 
-                 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
-                 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
-                   continue;
-                 
-                 if (crecp->flags & F_NEG)
+             else if (ptr)
+               {
+                 ans = 1;
+                 if (!dryrun)
                    {
-                     ans = 1;
-                     auth = 0;
-                     if (crecp->flags & F_NXDOMAIN)
-                       nxdomain = 1;
-                     if (!dryrun)
-                       log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
+                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>");
+                     for (ptr = daemon->ptr; ptr; ptr = ptr->next)
+                       if (hostname_isequal(name, ptr->name) &&
+                           add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                               daemon->local_ttl, NULL,
+                                               T_PTR, C_IN, "d", ptr->ptr))
+                         anscount++;
+                        
                    }
-                 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
-                   {
-                     ans = 1;
-                     if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+               }
+             else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
+               do 
+                 { 
+                   /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
+                   if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
+                     continue;
+                   
+                   if (crecp->flags & F_NEG)
+                     {
+                       ans = 1;
                        auth = 0;
-                     if (!dryrun)
-                       {
-                         unsigned long ttl;
-                         /* Return 0 ttl for DHCP entries, which might change
-                            before the lease expires. */
-                         if  (crecp->flags & (F_IMMORTAL | F_DHCP))
-                           ttl = daemon->local_ttl;
-                         else
-                           ttl = crecp->ttd - now;
-                         
-                         log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
-                                   0, daemon->addn_hosts, crecp->uid);
-
-                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL,
-                                                 T_PTR, C_IN, "d", cache_get_name(crecp)))
-                           anscount++;
-                       }
-                   }
-               } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
+                       if (crecp->flags & F_NXDOMAIN)
+                         nxdomain = 1;
+                       if (!dryrun)
+                         log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
+                     }
+                   else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+                     {
+                       ans = 1;
+                       if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+                         auth = 0;
+                       if (!dryrun)
+                         {
+                           log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, 
+                                     record_source(daemon->addn_hosts, crecp->uid));
+                           
+                           if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                   crec_ttl(crecp, now), NULL,
+                                                   T_PTR, C_IN, "d", cache_get_name(crecp)))
+                             anscount++;
+                         }
+                     }
+                 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
+             else if (is_arpa == F_IPV4 && 
+                      (daemon->options & OPT_BOGUSPRIV) && 
+                      private_net(addr.addr.addr4))
+               {
+                 /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
+                 ans = 1;
+                 nxdomain = 1;
+                 if (!dryrun)
+                   log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, 
+                             name, &addr, NULL);
+               }
            }
-
+           
          for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
            {
              unsigned short type = T_A;
-
+             
              if (flag == F_IPV6)
 #ifdef HAVE_IPV6
                type = T_AAAA;
@@ -1136,13 +1318,13 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
              if (qtype != type && qtype != T_ANY)
                continue;
              
-             /* Check for "A for A"  queries. */
+             /* Check for "A for A"  queries */
              if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1)
                {
                  ans = 1;
                  if (!dryrun)
                    {
-                     log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, 0, NULL, 0);
+                     log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                              daemon->local_ttl, NULL, type, C_IN, "4", &addr))
                        anscount++;
@@ -1150,6 +1332,34 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                  continue;
                }
 
+             /* interface name stuff */
+             if (qtype == T_A)
+               {
+                 struct interface_name *intr;
+
+                 for (intr = daemon->int_names; intr; intr = intr->next)
+                   if (hostname_isequal(name, intr->name))
+                     break;
+                 
+                 if (intr)
+                   {
+                     ans = 1;
+                     if (!dryrun)
+                       {
+                         if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
+                           log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
+                         else
+                           {
+                             log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
+                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                     daemon->local_ttl, NULL, type, C_IN, "4", &addr))
+                               anscount++;
+                           }
+                       }
+                     continue;
+                   }
+               }
+
            cname_restart:
              if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
                {
@@ -1182,8 +1392,9 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                        {
                          if (!dryrun)
                            {
-                             log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid);
-                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset,
+                             log_query(crecp->flags, name, NULL, record_source(daemon->addn_hosts, crecp->uid));
+                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                     crec_ttl(crecp, now), &nameoffset,
                                                      T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
                                anscount++;
                            }
@@ -1199,7 +1410,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                          if (crecp->flags & F_NXDOMAIN)
                            nxdomain = 1;
                          if (!dryrun)
-                           log_query(crecp->flags, name, NULL, 0, NULL, 0);
+                           log_query(crecp->flags, name, NULL, NULL);
                        }
                      else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
                        {
@@ -1216,17 +1427,11 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                          ans = 1;
                          if (!dryrun)
                            {
-                             unsigned long ttl;
-                             
-                             if  (crecp->flags & (F_IMMORTAL | F_DHCP))
-                               ttl = daemon->local_ttl;
-                             else
-                               ttl = crecp->ttd - now;
-                             
                              log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
-                                       0, daemon->addn_hosts, crecp->uid);
+                                       record_source(daemon->addn_hosts, crecp->uid));
                              
-                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN, 
+                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                     crec_ttl(crecp, now), NULL, type, C_IN, 
                                                      type == T_A ? "4" : "6", &crecp->addr))
                                anscount++;
                            }
@@ -1245,7 +1450,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                  if (!dryrun)
                    {
                      unsigned int offset;
-                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
+                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
                                              &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
                        {
@@ -1262,7 +1467,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                  ans = 1;
                  if (!dryrun)
                    {
-                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
+                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, 
                                              T_MX, C_IN, "sd", 1, 
                                              (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
@@ -1282,7 +1487,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                    if (!dryrun)
                      {
                        unsigned int offset;
-                       log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0);
+                       log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>");
                        if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
                                                &offset, T_SRV, C_IN, "sssd", 
                                                rec->priority, rec->weight, rec->srvport, rec->target))
@@ -1298,9 +1503,27 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
                {
                  ans = 1;
                  if (!dryrun)
-                   log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0);
+                   log_query(F_CONFIG | F_NEG, name, NULL, NULL);
                }
            }
+
+         if (qtype == T_NAPTR || qtype == T_ANY)
+           {
+             struct naptr *na;
+             for (na = daemon->naptr; na; na = na->next)
+               if (hostname_isequal(name, na->name))
+                 {
+                   ans = 1;
+                   if (!dryrun)
+                     {
+                       log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>");
+                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
+                                               NULL, T_NAPTR, C_IN, "sszzzd", 
+                                               na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
+                         anscount++;
+                     }
+                 }
+           }
          
          if (qtype == T_MAILB)
            ans = 1, nxdomain = 1;
@@ -1309,7 +1532,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
            {
              ans = 1; 
              if (!dryrun)
-               log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
+               log_query(F_CONFIG | F_NEG, name, &addr, NULL);
            }
        }
 
@@ -1336,7 +1559,6 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
        crecp = NULL;
        while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
          {
-           unsigned long ttl;
 #ifdef HAVE_IPV6
            int type =  crecp->flags & F_IPV4 ? T_A : T_AAAA;
 #else
@@ -1345,12 +1567,8 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
            if (crecp->flags & F_NEG)
              continue;
 
-           if  (crecp->flags & (F_IMMORTAL | F_DHCP))
-             ttl = daemon->local_ttl;
-           else
-             ttl = crecp->ttd - now;
-           
-           if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN, 
+           if (add_resource_record(header, limit, NULL, rec->offset, &ansp, 
+                                   crec_ttl(crecp, now), NULL, type, C_IN, 
                                    crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
              addncount++;
          }