Diff for /embedaddon/dnsmasq/src/rfc1035.c between versions 1.1.1.2 and 1.1.1.5

version 1.1.1.2, 2014/06/15 16:31:38 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     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     it under the terms of the GNU General Public License as published by
Line 20  int extract_name(struct dns_header *header, size_t ple Line 20  int extract_name(struct dns_header *header, size_t ple
                  char *name, int isExtract, int extrabytes)                   char *name, int isExtract, int extrabytes)
 {  {
   unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;    unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
  unsigned int j, l, hops = 0;  unsigned int j, l, namelen = 0, hops = 0;
   int retvalue = 1;    int retvalue = 1;
       
   if (isExtract)    if (isExtract)
Line 36  int extract_name(struct dns_header *header, size_t ple Line 36  int extract_name(struct dns_header *header, size_t ple
       if ((l = *p++) == 0)         if ((l = *p++) == 0) 
         /* end marker */          /* end marker */
         {          {
          /* check that there are the correct no of bytes after the name */          /* check that there are the correct no. of bytes after the name */
          if (!CHECK_LEN(header, p, plen, extrabytes))          if (!CHECK_LEN(header, p1 ? p1 : p, plen, extrabytes))
             return 0;              return 0;
                       
           if (isExtract)            if (isExtract)
Line 77  int extract_name(struct dns_header *header, size_t ple Line 77  int extract_name(struct dns_header *header, size_t ple
                       
           p = l + (unsigned char *)header;            p = l + (unsigned char *)header;
         }          }
      else if (label_type == 0x80)      else if (label_type == 0x00)
        return 0; /* reserved */ 
      else if (label_type == 0x40) 
        { /* ELT */ 
          unsigned int count, digs; 
           
          if ((l & 0x3f) != 1) 
            return 0; /* we only understand bitstrings */ 
 
          if (!isExtract) 
            return 0; /* Cannot compare bitsrings */ 
           
          count = *p++; 
          if (count == 0) 
            count = 256; 
          digs = ((count-1)>>2)+1; 
           
          /* output is \[x<hex>/siz]. which is digs+9 chars */ 
          if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME) 
            return 0; 
          if (!CHECK_LEN(header, p, plen, (count-1)>>3)) 
            return 0; 
 
          *cp++ = '\\'; 
          *cp++ = '['; 
          *cp++ = 'x'; 
          for (j=0; j<digs; j++) 
            { 
              unsigned int dig; 
              if (j%2 == 0) 
                dig = *p >> 4; 
              else 
                dig = *p++ & 0x0f; 
               
              *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10; 
            }  
          cp += sprintf((char *)cp, "/%d]", count); 
          /* do this here to overwrite the zero char from sprintf */ 
          *cp++ = '.'; 
        } 
      else  
         { /* label_type = 0 -> label. */          { /* label_type = 0 -> label. */
          if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)          namelen += l + 1; /* include period */
           if (namelen >= MAXDNAME)
             return 0;              return 0;
           if (!CHECK_LEN(header, p, plen, l))            if (!CHECK_LEN(header, p, plen, l))
             return 0;              return 0;
Line 128  int extract_name(struct dns_header *header, size_t ple Line 89  int extract_name(struct dns_header *header, size_t ple
             if (isExtract)              if (isExtract)
               {                {
                 unsigned char c = *p;                  unsigned char c = *p;
                if (isascii(c) && !iscntrl(c) && c != '.')#ifdef HAVE_DNSSEC
                  *cp++ = *p;                if (option_bool(OPT_DNSSEC_VALID))
                   {
                     if (c == 0 || c == '.' || c == NAME_ESCAPE)
                       {
                         *cp++ = NAME_ESCAPE;
                         *cp++ = c+1;
                       }
                     else
                       *cp++ = c; 
                   }
                 else                  else
   #endif
                   if (c != 0 && c != '.')
                     *cp++ = c;
                   else
                   return 0;                    return 0;
               }                }
             else               else 
Line 144  int extract_name(struct dns_header *header, size_t ple Line 118  int extract_name(struct dns_header *header, size_t ple
                     cp++;                      cp++;
                     if (c1 >= 'A' && c1 <= 'Z')                      if (c1 >= 'A' && c1 <= 'Z')
                       c1 += 'a' - 'A';                        c1 += 'a' - 'A';
   #ifdef HAVE_DNSSEC
                       if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
                         c1 = (*cp++)-1;
   #endif
                       
                     if (c2 >= 'A' && c2 <= 'Z')                      if (c2 >= 'A' && c2 <= 'Z')
                       c2 += 'a' - 'A';                        c2 += 'a' - 'A';
                                         
                     if (c1 != c2)                      if (c1 != c2)
                       retvalue =  2;                        retvalue =  2;
                   }                    }
               }                }
                      
           if (isExtract)            if (isExtract)
             *cp++ = '.';              *cp++ = '.';
           else if (*cp != 0 && *cp++ != '.')            else if (*cp != 0 && *cp++ != '.')
             retvalue = 2;              retvalue = 2;
         }          }
         else
           return 0; /* label types 0x40 and 0x80 not supported */
     }      }
 }  }
     
 /* Max size of input string (for IPv6) is 75 chars.) */  /* Max size of input string (for IPv6) is 75 chars.) */
 #define MAXARPANAME 75  #define MAXARPANAME 75
int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)int in_arpa_name_2_addr(char *namein, union all_addr *addrp)
 {  {
   int j;    int j;
   char name[MAXARPANAME+1], *cp1;    char name[MAXARPANAME+1], *cp1;
Line 172  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 153  int in_arpa_name_2_addr(char *namein, struct all_addr 
   if (strlen(namein) > MAXARPANAME)    if (strlen(namein) > MAXARPANAME)
     return 0;      return 0;
   
  memset(addrp, 0, sizeof(struct all_addr));  memset(addrp, 0, sizeof(union all_addr));
   
   /* turn name into a series of asciiz strings */    /* turn name into a series of asciiz strings */
  /* j counts no of labels */  /* j counts no. of labels */
   for(j = 1,cp1 = name; *namein; cp1++, namein++)    for(j = 1,cp1 = name; *namein; cp1++, namein++)
     if (*namein == '.')      if (*namein == '.')
       {        {
Line 195  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 176  int in_arpa_name_2_addr(char *namein, struct all_addr 
   if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))    if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))
     {      {
       /* IP v4 */        /* IP v4 */
      /* address arives as a name of the form      /* address arrives as a name of the form
          www.xxx.yyy.zzz.in-addr.arpa           www.xxx.yyy.zzz.in-addr.arpa
          some of the low order address octets might be missing           some of the low order address octets might be missing
          and should be set to zero. */           and should be set to zero. */
Line 217  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 198  int in_arpa_name_2_addr(char *namein, struct all_addr 
   
       return F_IPV4;        return F_IPV4;
     }      }
 #ifdef HAVE_IPV6  
   else if (hostname_isequal(penchunk, "ip6") &&     else if (hostname_isequal(penchunk, "ip6") && 
            (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))             (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
     {      {
Line 225  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 205  int in_arpa_name_2_addr(char *namein, struct all_addr 
          Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]           Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
          or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]           or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
               
         Note that most of these the various reprentations are obsolete and          Note that most of these the various representations are obsolete and 
          left-over from the many DNS-for-IPv6 wars. We support all the formats           left-over from the many DNS-for-IPv6 wars. We support all the formats
          that we can since there is no reason not to.           that we can since there is no reason not to.
       */        */
Line 254  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 234  int in_arpa_name_2_addr(char *namein, struct all_addr 
               if (*(cp1+1) || !isxdigit((unsigned char)*cp1))                if (*(cp1+1) || !isxdigit((unsigned char)*cp1))
                 return 0;                  return 0;
                               
              for (j = sizeof(struct all_addr)-1; j>0; j--)              for (j = sizeof(struct in6_addr)-1; j>0; j--)
                 addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);                  addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);
               addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);                addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
             }              }
Line 262  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 242  int in_arpa_name_2_addr(char *namein, struct all_addr 
           return F_IPV6;            return F_IPV6;
         }          }
     }      }
 #endif  
       
   return 0;    return 0;
 }  }
Line 354  unsigned char *skip_section(unsigned char *ansp, int c Line 333  unsigned char *skip_section(unsigned char *ansp, int c
   return ansp;    return ansp;
 }  }
   
 /* CRC the question section. This is used to safely detect query   
    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. Return all-ones  
    if there is not question section. */  
 #ifndef HAVE_DNSSEC  
 unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)  
 {  
   int q;  
   unsigned int crc = 0xffffffff;  
   unsigned char *p1, *p = (unsigned char *)(header+1);  
   
   for (q = ntohs(header->qdcount); q != 0; q--)   
     {  
       if (!extract_name(header, plen, &p, name, 1, 4))  
         return crc; /* bad packet */  
         
       for (p1 = (unsigned char *)name; *p1; p1++)  
         {  
           int i = 8;  
           char c = *p1;  
   
           if (c >= 'A' && c <= 'Z')  
             c += 'a' - 'A';  
   
           crc ^= c << 24;  
           while (i--)  
             crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;  
         }  
         
       /* CRC the class and type as well */  
       for (p1 = p; p1 < p+4; p1++)  
         {  
           int i = 8;  
           crc ^= *p1 << 24;  
           while (i--)  
             crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;  
         }  
   
       p += 4;  
       if (!CHECK_LEN(header, p, plen, 0))  
         return crc; /* bad packet */  
     }  
   
   return crc;  
 }  
 #endif  
   
 size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)  size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
 {  {
   unsigned char *ansp = skip_questions(header, plen);    unsigned char *ansp = skip_questions(header, plen);
Line 427  size_t resize_packet(struct dns_header *header, size_t Line 357  size_t resize_packet(struct dns_header *header, size_t
   return ansp - (unsigned char *)header;    return ansp - (unsigned char *)header;
 }  }
   
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign)/* is addr in the non-globally-routed IP space? */ 
 int private_net(struct in_addr addr, int ban_localhost) 
 {  {
  /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.   in_addr_t ip_addr = ntohl(addr.s_addr);
     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 char *)(header+1); 
  unsigned short rdlen, type, class; 
  unsigned char *ret = NULL; 
   
  if (is_sign)  return
    {    (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost)  /* 127.0.0.0/8    (loopback) */ ||
      *is_sign = 0;    (((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
    ((ip_addr & 0xFF000000) == 0x0A000000)  /* 10.0.0.0/8     (private)  */ ||
      if (OPCODE(header) == QUERY)    ((ip_addr & 0xFFF00000) == 0xAC100000)  /* 172.16.0.0/12  (private)  */ ||
        {    ((ip_addr & 0xFFFF0000) == 0xC0A80000)  /* 192.168.0.0/16 (private)  */ ||
          for (i = ntohs(header->qdcount); i != 0; i--)    ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ||
            {    ((ip_addr & 0xFFFFFF00) == 0xC0000200)  /* 192.0.2.0/24   (test-net) */ ||
              if (!(ansp = skip_name(ansp, header, plen, 4)))    ((ip_addr & 0xFFFFFF00) == 0xC6336400)  /* 198.51.100.0/24(test-net) */ ||
                return NULL;    ((ip_addr & 0xFFFFFF00) == 0xCB007100)  /* 203.0.113.0/24 (test-net) */ ||
                  ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF)  /* 255.255.255.255/32 (broadcast)*/ ;
              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, 10))) 
        return NULL;  
 
      GETSHORT(type, ansp); 
      save = ansp; 
      GETSHORT(class, ansp); 
      ansp += 4; /* TTL */ 
      GETSHORT(rdlen, ansp); 
      if (!ADD_RDLEN(header, ansp, plen, rdlen)) 
        return NULL; 
      if (type == T_OPT) 
        { 
          if (len) 
            *len = ansp - start; 
          if (p) 
            *p = save; 
          ret = start; 
        } 
      else if (is_sign &&  
               i == arcount - 1 &&  
               class == C_ANY &&  
               type == T_TSIG) 
        *is_sign = 1; 
    } 
   
  return ret; 
 }  }
   
struct macparm {static int private_net6(struct in6_addr *a, int ban_localhost)
  unsigned char *limit; 
  struct dns_header *header; 
  size_t plen; 
  union mysockaddr *l3; 
}; 
  
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,  
                               int optno, unsigned char *opt, size_t optlen, int set_do) 
 
  unsigned char *lenp, *datap, *p; 
  int rdlen, is_sign; 
   
  if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) 
    { 
      if (is_sign) 
        return plen; 
 
      /* We are adding the pseudoheader */ 
      if (!(p = skip_questions(header, plen)) || 
          !(p = skip_section(p,  
                             ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),  
                             header, plen))) 
        return plen; 
      *p++ = 0; /* empty name */ 
      PUTSHORT(T_OPT, p); 
      PUTSHORT(daemon->edns_pktsz, p); /* max packet length */ 
      PUTSHORT(0, p);    /* extended RCODE and version */ 
      PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ 
      lenp = p; 
      PUTSHORT(0, p);    /* RDLEN */ 
      rdlen = 0; 
      if (((ssize_t)optlen) > (limit - (p + 4))) 
        return plen; /* Too big */ 
      header->arcount = htons(ntohs(header->arcount) + 1); 
      datap = p; 
    } 
  else 
    { 
      int i; 
      unsigned short code, len, flags; 
       
      /* Must be at the end, if exists */ 
      if (ntohs(header->arcount) != 1 || 
          is_sign || 
          (!(p = skip_name(p, header, plen, 10)))) 
        return plen; 
       
      p += 6; /* skip UDP length and RCODE */ 
      GETSHORT(flags, p); 
      if (set_do) 
        { 
          p -=2; 
          PUTSHORT(flags | 0x8000, p); 
        } 
 
      lenp = p; 
      GETSHORT(rdlen, p); 
      if (!CHECK_LEN(header, p, plen, rdlen)) 
        return plen; /* bad packet */ 
      datap = p; 
 
       /* no option to add */ 
      if (optno == 0) 
        return plen; 
           
      /* check if option already there */ 
      for (i = 0; i + 4 < rdlen; i += len + 4) 
        { 
          GETSHORT(code, p); 
          GETSHORT(len, p); 
          if (code == optno) 
            return plen; 
          p += len; 
        } 
       
      if (((ssize_t)optlen) > (limit - (p + 4))) 
        return plen; /* Too big */ 
    } 
   
  if (optno != 0) 
    { 
      PUTSHORT(optno, p); 
      PUTSHORT(optlen, p); 
      memcpy(p, opt, optlen); 
      p += optlen;   
    } 
 
  PUTSHORT(p - datap, lenp); 
  return p - (unsigned char *)header; 
   
} 
 
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) 
 {  {
  struct macparm *parm = parmv;  /* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
  int match = 0;  if (IN6_IS_ADDR_V4MAPPED(a))
     
  if (family == parm->l3->sa.sa_family) 
     {      {
      if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)      struct in_addr v4;
        match = 1;      v4.s_addr = ((const uint32_t *) (a))[3];
#ifdef HAVE_IPV6      return private_net(v4, ban_localhost);
      else 
        if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0) 
          match = 1; 
#endif 
     }      }
    
   if (!match)  
     return 1; /* continue */  
   
   parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit,  EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);  
     
   return 0; /* done */  
 }               
        
 size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)  
 {  
   struct macparm parm;  
        
 /* Must have an existing pseudoheader as the only ar-record,   
    or have no ar-records. Must also not be signed */  
      
   if (ntohs(header->arcount) > 1)  
     return plen;  
   
   parm.header = header;  
   parm.limit = (unsigned char *)limit;  
   parm.plen = plen;  
   parm.l3 = l3;  
   
   iface_enumerate(AF_UNSPEC, &parm, filter_mac);  
     
   return parm.plen;   
 }  
   
 struct subnet_opt {  
   u16 family;  
   u8 source_netmask, scope_netmask;  
 #ifdef HAVE_IPV6   
   u8 addr[IN6ADDRSZ];  
 #else  
   u8 addr[INADDRSZ];  
 #endif  
 };  
   
 static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)  
 {  
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */  
     
   int len;  
   void *addrp;  
   
 #ifdef HAVE_IPV6  
   if (source->sa.sa_family == AF_INET6)  
     {  
       opt->family = htons(2);  
       opt->source_netmask = daemon->addr6_netmask;  
       addrp = &source->in6.sin6_addr;  
     }  
   else  
 #endif  
     {  
       opt->family = htons(1);  
       opt->source_netmask = daemon->addr4_netmask;  
       addrp = &source->in.sin_addr;  
     }  
     
   opt->scope_netmask = 0;  
   len = 0;  
     
   if (opt->source_netmask != 0)  
     {  
       len = ((opt->source_netmask - 1) >> 3) + 1;  
       memcpy(opt->addr, addrp, len);  
       if (opt->source_netmask & 7)  
         opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));  
     }  
   
   return len + 4;  
 }  
    
 size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source)  
 {  
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */  
     
   int len;  
   struct subnet_opt opt;  
     
   len = calc_subnet_opt(&opt, source);  
   return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);  
 }  
   
 #ifdef HAVE_DNSSEC  
 size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)  
 {  
   return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);  
 }  
 #endif  
   
 int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)  
 {  
   /* Section 9.2, Check that subnet option in reply matches. */  
   
   
  int len, calc_len;  
   struct subnet_opt opt;  
   unsigned char *p;  
   int code, i, rdlen;  
     
    calc_len = calc_subnet_opt(&opt, peer);  
      
    if (!(p = skip_name(pseudoheader, header, plen, 10)))  
      return 1;  
      
    p += 8; /* skip UDP length and RCODE */  
      
    GETSHORT(rdlen, p);  
    if (!CHECK_LEN(header, p, plen, rdlen))  
      return 1; /* bad packet */  
      
    /* check if option there */  
    for (i = 0; i + 4 < rdlen; i += len + 4)  
      {  
        GETSHORT(code, p);  
        GETSHORT(len, p);  
        if (code == EDNS0_OPTION_CLIENT_SUBNET)  
          {  
            /* make sure this doesn't mismatch. */  
            opt.scope_netmask = p[3];  
            if (len != calc_len || memcmp(p, &opt, len) != 0)  
              return 0;  
          }  
        p += len;  
      }  
      
    return 1;  
 }  
   
 /* is addr in the non-globally-routed IP space? */   
 int private_net(struct in_addr addr, int ban_localhost)   
 {  
   in_addr_t ip_addr = ntohl(addr.s_addr);  
   
   return    return
    (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost)  /* 127.0.0.0/8    (loopback) */ ||     (IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */
    ((ip_addr & 0xFFFF0000) == 0xC0A80000)  /* 192.168.0.0/16 (private)  */ ||    (IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) ||    /* RFC 6303 4.3 */
    ((ip_addr & 0xFF000000) == 0x0A000000)  /* 10.0.0.0/8     (private)  */ ||    IN6_IS_ADDR_LINKLOCAL(a) ||   /* RFC 6303 4.5 */
    ((ip_addr & 0xFFF00000) == 0xAC100000)  /* 172.16.0.0/12  (private)  */ ||    IN6_IS_ADDR_SITELOCAL(a) ||
    ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;    ((unsigned char *)a)[0] == 0xfd ||   /* RFC 6303 4.4 */
     ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
 }  }
   
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored)
 {  {
   int i, qtype, qclass, rdlen;    int i, qtype, qclass, rdlen;
   
   for (i = count; i != 0; i--)    for (i = count; i != 0; i--)
     {      {
      if (name && option_bool(OPT_LOG))      if (!(p = skip_name(p, header, qlen, 10)))
        { 
          if (!extract_name(header, qlen, &p, name, 1, 10)) 
            return 0; 
        } 
      else if (!(p = skip_name(p, header, qlen, 10))) 
         return 0; /* bad packet */          return 0; /* bad packet */
               
       GETSHORT(qtype, p);         GETSHORT(qtype, p); 
Line 804  static unsigned char *do_doctor(unsigned char *p, int  Line 439  static unsigned char *do_doctor(unsigned char *p, int 
               break;                break;
             }              }
         }          }
       else if (qtype == T_TXT && name && option_bool(OPT_LOG))  
         {  
           unsigned char *p1 = p;  
           if (!CHECK_LEN(header, p1, qlen, rdlen))  
             return 0;  
           while ((p1 - p) < rdlen)  
             {  
               unsigned int i, len = *p1;  
               unsigned char *p2 = p1;  
               /* make counted string zero-term  and sanitise */  
               for (i = 0; i < len; i++)  
                 {  
                   if (!isprint((int)*(p2+1)))  
                     break;  
                     
                   *p2 = *(p2+1);  
                   p2++;  
                 }  
               *p2 = 0;  
               my_syslog(LOG_INFO, "reply %s is %s", name, p1);  
               /* restore */  
               memmove(p1 + 1, p1, i);  
               *p1 = len;  
               p1 += len+1;  
             }  
         }                   
               
       if (!ADD_RDLEN(header, p, qlen, rdlen))        if (!ADD_RDLEN(header, p, qlen, rdlen))
          return 0; /* bad packet */           return 0; /* bad packet */
Line 838  static unsigned char *do_doctor(unsigned char *p, int  Line 447  static unsigned char *do_doctor(unsigned char *p, int 
   return p;     return p; 
 }  }
   
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)static int find_soa(struct dns_header *header, size_t qlen, int *doctored)
 {  {
   unsigned char *p;    unsigned char *p;
   int qtype, qclass, rdlen;    int qtype, qclass, rdlen;
Line 847  static int find_soa(struct dns_header *header, size_t  Line 456  static int find_soa(struct dns_header *header, size_t 
       
   /* first move to NS section and find TTL from any SOA section */    /* first move to NS section and find TTL from any SOA section */
   if (!(p = skip_questions(header, qlen)) ||    if (!(p = skip_questions(header, qlen)) ||
      !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))      !(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored)))
     return 0;  /* bad packet */      return 0;  /* bad packet */
       
   for (i = ntohs(header->nscount); i != 0; i--)    for (i = ntohs(header->nscount); i != 0; i--)
Line 883  static int find_soa(struct dns_header *header, size_t  Line 492  static int find_soa(struct dns_header *header, size_t 
     }      }
       
   /* rewrite addresses in additional section too */    /* rewrite addresses in additional section too */
  if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))  if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored))
     return 0;      return 0;
       
   if (!found_soa)    if (!found_soa)
Line 892  static int find_soa(struct dns_header *header, size_t  Line 501  static int find_soa(struct dns_header *header, size_t 
   return minttl;    return minttl;
 }  }
   
   /* Print TXT reply to log */
   static int print_txt(struct dns_header *header, const size_t qlen, char *name,
                        unsigned char *p, const int ardlen, int secflag)
   {
     unsigned char *p1 = p;
     if (!CHECK_LEN(header, p1, qlen, ardlen))
       return 0;
     /* Loop over TXT payload */
     while ((p1 - p) < ardlen)
       {
         unsigned int i, len = *p1;
         unsigned char *p3 = p1;
         if ((p1 + len - p) >= ardlen)
           return 0; /* bad packet */
   
         /* make counted string zero-term and sanitise */
         for (i = 0; i < len; i++)
           {
             if (!isprint((unsigned char)*(p3+1)))
               break;
             *p3 = *(p3+1);
             p3++;
           }
   
         *p3 = 0;
         log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0);
         /* restore */
         memmove(p1 + 1, p1, i);
         *p1 = len;
         p1 += len+1;
       }
     return 1;
   }
   
 /* Note that the following code can create CNAME chains that don't point to a real record,  /* 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      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.      expired and cleaned out that way. 
   Return 1 if we reject an address because it look like part of dns-rebinding attack. */   Return 1 if we reject an address because it look like part of dns-rebinding attack. 
    Return 2 if the packet is malformed.
 */
 int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,   int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, 
                      char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)                      struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
                       int no_cache_dnssec, int secure, int *doctored)
 {  {
   unsigned char *p, *p1, *endrr, *namep;    unsigned char *p, *p1, *endrr, *namep;
  int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;  int j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
   unsigned long ttl = 0;    unsigned long ttl = 0;
  struct all_addr addr;  union all_addr addr;
 #ifdef HAVE_IPSET  #ifdef HAVE_IPSET
   char **ipsets_cur;    char **ipsets_cur;
 #else  #else
   (void)ipsets; /* unused */    (void)ipsets; /* unused */
 #endif  #endif
   #ifdef HAVE_NFTSET
     char **nftsets_cur;
   #else
     (void)nftsets; /* unused */
   #endif
     int found = 0, cname_count = CNAME_CHAIN;
     struct crec *cpp = NULL;
     int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
   #ifdef HAVE_DNSSEC
     int cname_short = 0;
   #endif
     unsigned long cttl = ULONG_MAX, attl;
       
   cache_start_insert();    cache_start_insert();
   
  /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */  /* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */
  if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))  if (daemon->doctors || option_bool(OPT_DNSSEC_VALID))
     {      {
       searched_soa = 1;        searched_soa = 1;
      ttl = find_soa(header, qlen, name, doctored);      ttl = find_soa(header, qlen, doctored);
 
       if (*doctored)
         {
           if (secure)
             return 0;
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
      if (*doctored && secure)          if (option_bool(OPT_DNSSEC_VALID))
        return 0;            for (j = 0; j < ntohs(header->ancount); j++)
               if (daemon->rr_status[j] != 0)
                 return 0;
 #endif  #endif
           }
     }      }
       
  /* go through the questions. */  namep = p = (unsigned char *)(header+1);
  p = (unsigned char *)(header+1); 
       
  for (i = ntohs(header->qdcount); i != 0; i--)  if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
    {    return 2; /* bad packet */
      int found = 0, cname_count = CNAME_CHAIN;  
      struct crec *cpp = NULL;  GETSHORT(qtype, p); 
      int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;  GETSHORT(qclass, p);
      int secflag = secure ?  F_DNSSECOK : 0;  
      unsigned long cttl = ULONG_MAX, attl;  if (qclass != C_IN)
    return 0;
      namep = p;  
      if (!extract_name(header, qlen, &p, name, 1, 4))  /* PTRs: we chase CNAMEs here, since we have no way to 
        return 0; /* bad packet */     represent them in the cache. */
             if (qtype == T_PTR)
      GETSHORT(qtype, p);     { 
      GETSHORT(qclass, p);      int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
               
      if (qclass != C_IN)      if (!(flags & F_NXDOMAIN))
        continue;        {
        cname_loop:
      /* PTRs: we chase CNAMEs here, since we have no way to           if (!(p1 = skip_questions(header, qlen)))
         represent them in the cache. */            return 2;
      if (qtype == T_PTR) 
        {  
          int name_encoding = in_arpa_name_2_addr(name, &addr); 
                       
          if (!name_encoding)          for (j = 0; j < ntohs(header->ancount); j++) 
            continue; 
 
          if (!(flags & F_NXDOMAIN)) 
             {              {
            cname_loop:              int secflag = 0;
              if (!(p1 = skip_questions(header, qlen)))              if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
                return 0;                return 2; /* bad packet */
                               
              for (j = ntohs(header->ancount); j != 0; j--)               GETSHORT(aqtype, p1); 
               GETSHORT(aqclass, p1);
               GETLONG(attl, p1);
               
               if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
                 {                  {
                  unsigned char *tmp = namep;                  (p1) -= 4;
                  /* the loop body overwrites the original name, so get it back here. */                  PUTLONG(daemon->max_ttl, p1);
                  if (!extract_name(header, qlen, &tmp, name, 1, 0) ||                }
                      !(res = extract_name(header, qlen, &p1, name, 0, 10)))              GETSHORT(ardlen, p1);
                    return 0; /* bad packet */              endrr = p1+ardlen;
                                
                  GETSHORT(aqtype, p1);               /* TTL of record is minimum of CNAMES and PTR */
                  GETSHORT(aqclass, p1);              if (attl < cttl)
                  GETLONG(attl, p1);                cttl = attl;
                  if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)              
               if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
                 {
 #ifdef HAVE_DNSSEC
                   if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0)
                     {                      {
                      (p1) -= 4;                      /* validated RR anywhere in CNAME chain, don't cache. */
                      PUTLONG(daemon->max_ttl, p1);                      if (cname_short || aqtype == T_CNAME)
                         insert = 0;
                       
                       secflag = F_DNSSECOK;
                       /* limit TTL based on signature. */
                       if (daemon->rr_status[j] < cttl)
                         cttl = daemon->rr_status[j];
                     }                      }
                  GETSHORT(ardlen, p1);#endif
                  endrr = p1+ardlen; 
                   
                  /* TTL of record is minimum of CNAMES and PTR */ 
                  if (attl < cttl) 
                    cttl = attl; 
   
                  if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))                  if (aqtype == T_CNAME)
                     log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
                   
                   if (!extract_name(header, qlen, &p1, name, 1, 0))
                     return 2;
                   
                   if (aqtype == T_CNAME)
                     {                      {
                      if (!extract_name(header, qlen, &p1, name, 1, 0))                      if (!cname_count--)
                        return 0;                        return 0; /* looped CNAMES, we can't cache. */
                      #ifdef HAVE_DNSSEC
                      if (aqtype == T_CNAME)                      cname_short = 1;
                        {#endif
                          if (!cname_count-- || secure)                      goto cname_loop;
                            return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */ 
                          goto cname_loop; 
                        } 
                       
                      cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE); 
                      found = 1;  
                     }                      }
                                       
                  p1 = endrr;                  found = 1
                  if (!CHECK_LEN(header, p1, qlen, 0))                  
                    return 0; /* bad packet */                  if (!name_encoding)
                     log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
                   else
                     {
                       log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
                       if (insert)
                         cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
                     }
                 }                  }
   
                 p1 = endrr;
                 if (!CHECK_LEN(header, p1, qlen, 0))
                   return 2; /* bad packet */
             }              }
           }
         
         if (!found && !option_bool(OPT_NO_NEG))
           {
             if (!searched_soa)
               {
                 searched_soa = 1;
                 ttl = find_soa(header, qlen, doctored);
               }
                       
           if (!found && !option_bool(OPT_NO_NEG))          flags |= F_NEG | (secure ?  F_DNSSECOK : 0);
           if (name_encoding && ttl)
             {              {
              if (!searched_soa)              flags |= F_REVERSE | name_encoding;
                {              cache_insert(NULL, &addr, C_IN, now, ttl, flags);
                  searched_soa = 1; 
                  ttl = find_soa(header, qlen, NULL, doctored); 
                } 
              if (ttl) 
                cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);      
             }              }
             
             log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
         }          }
       }
     else
       {
         /* everything other than PTR */
         struct crec *newc;
         int addrlen = 0, insert = 1;
         
         if (qtype == T_A)
           {
             addrlen = INADDRSZ;
             flags |= F_IPV4;
           }
         else if (qtype == T_AAAA)
           {
             addrlen = IN6ADDRSZ;
             flags |= F_IPV6;
           }
         else if (qtype == T_SRV)
           flags |= F_SRV;
       else        else
           insert = 0; /* NOTE: do not cache data from CNAME queries. */
         
       cname_loop1:
         if (!(p1 = skip_questions(header, qlen)))
           return 2;
         
         for (j = 0; j < ntohs(header->ancount); j++) 
         {          {
          /* everything other than PTR */          int secflag = 0;
          struct crec *newc;          
          int addrlen;          if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
            return 2; /* bad packet */
          if (qtype == T_A)          
           GETSHORT(aqtype, p1); 
           GETSHORT(aqclass, p1);
           GETLONG(attl, p1);
           if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
             {              {
              addrlen = INADDRSZ;              (p1) -= 4;
              flags |= F_IPV4;              PUTLONG(daemon->max_ttl, p1);
             }              }
#ifdef HAVE_IPV6          GETSHORT(ardlen, p1);
          else if (qtype == T_AAAA)          endrr = p1+ardlen;
           
           /* Not what we're looking for? */
           if (aqclass != C_IN || res == 2)
             {              {
              addrlen = IN6ADDRSZ;              p1 = endrr;
              flags |= F_IPV6;              if (!CHECK_LEN(header, p1, qlen, 0))
                 return 2; /* bad packet */
               continue;
             }              }
 #endif  
           else   
             continue;  
               
         cname_loop1:  
           if (!(p1 = skip_questions(header, qlen)))  
             return 0;  
                       
          for (j = ntohs(header->ancount); j != 0; j--) #ifdef HAVE_DNSSEC
           if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0)
             {              {
              if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))              secflag = F_DNSSECOK;
                return 0; /* bad packet */ 
                               
              GETSHORT(aqtype, p1);               /* limit TTl based on sig. */
              GETSHORT(aqclass, p1);              if (daemon->rr_status[j] < attl)
              GETLONG(attl, p1);                attl = daemon->rr_status[j];
              if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)            }
                {#endif    
                  (p1) -= 4;          
                  PUTLONG(daemon->max_ttl, p1);          if (aqtype == T_CNAME)
                }            {
              GETSHORT(ardlen, p1);              if (!cname_count--)
              endrr = p1+ardlen;                return 0; /* looped CNAMES */
                               
              if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))              log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
               
               if (insert)
                 {                  {
                  if (aqtype == T_CNAME)                  if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
                     {                      {
                      if (!cname_count--)                      newc->addr.cname.target.cache = NULL;
                        return 0; /* looped CNAMES */                      newc->addr.cname.is_name_ptr = 0
                      newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);                      if (cpp)
                      if (newc) 
                         {                          {
                          newc->addr.cname.target.cache = NULL;                          next_uid(newc);
                          /* anything other than zero, to avoid being mistaken for CNAME to interface-name */  
                          newc->addr.cname.uid = 1;  
                          if (cpp) 
                            { 
                              cpp->addr.cname.target.cache = newc; 
                              cpp->addr.cname.uid = newc->uid; 
                            } 
                        } 
                       
                      cpp = newc; 
                      if (attl < cttl) 
                        cttl = attl; 
                       
                      if (!extract_name(header, qlen, &p1, name, 1, 0)) 
                        return 0; 
                      goto cname_loop1; 
                    } 
                  else if (!(flags & F_NXDOMAIN)) 
                    { 
                      found = 1; 
                       
                      /* 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 (check_rebind && 
                          (flags & F_IPV4) && 
                          private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))) 
                        return 1; 
                       
#ifdef HAVE_IPSET 
                      if (ipsets && (flags & (F_IPV4 | F_IPV6))) 
                        { 
                          ipsets_cur = ipsets; 
                          while (*ipsets_cur) 
                            { 
                              log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur); 
                              add_to_ipset(*ipsets_cur++, &addr, flags, 0); 
                            } 
                        } 
#endif 
                       
                      newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag); 
                      if (newc && cpp) 
                        { 
                           cpp->addr.cname.target.cache = newc;                            cpp->addr.cname.target.cache = newc;
                           cpp->addr.cname.uid = newc->uid;                            cpp->addr.cname.uid = newc->uid;
                         }                          }
                       cpp = NULL;  
                     }                      }
                     
                     cpp = newc;
                     if (attl < cttl)
                       cttl = attl;
                 }                  }
                               
              p1 = endrr;              namep = p1;
              if (!CHECK_LEN(header, p1, qlen, 0))              if (!extract_name(header, qlen, &p1, name, 1, 0))
                return 0; /* bad packet */                return 2;
               
               if (qtype != T_CNAME)
                 goto cname_loop1;
 
               found = 1;
             }              }
                    else if (aqtype != qtype)
          if (!found && !option_bool(OPT_NO_NEG)) 
             {              {
              if (!searched_soa)#ifdef HAVE_DNSSEC
               if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG)
 #endif
                 log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
             }
           else if (!(flags & F_NXDOMAIN))
             {
               found = 1;
               
               if (flags & F_SRV)
                 {                  {
                  searched_soa = 1;                  unsigned char *tmp = namep;
                  ttl = find_soa(header, qlen, NULL, doctored);                  
                   if (!CHECK_LEN(header, p1, qlen, 6))
                     return 2; /* bad packet */
                   GETSHORT(addr.srv.priority, p1);
                   GETSHORT(addr.srv.weight, p1);
                   GETSHORT(addr.srv.srvport, p1);
                   if (!extract_name(header, qlen, &p1, name, 1, 0))
                     return 2;
                   addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
                   if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
                     return 0;
                   
                   /* we overwrote the original name, so get it back here. */
                   if (!extract_name(header, qlen, &tmp, name, 1, 0))
                     return 2;
                 }                  }
              /* If there's no SOA to get the TTL from, but there is a CNAME               else if (flags & (F_IPV4 | F_IPV6))
                 pointing at this, inherit its TTL */ 
              if (ttl || cpp) 
                 {                  {
                  newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);                        /* copy address into aligned storage */
                   if (!CHECK_LEN(header, p1, qlen, addrlen))
                     return 2; /* bad packet */
                   memcpy(&addr, p1, addrlen);
                   
                   /* check for returned address in private space */
                   if (check_rebind)
                     {
                       if ((flags & F_IPV4) &&
                           private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
                         return 1;
                       
                       if ((flags & F_IPV6) &&
                           private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
                         return 1;
                     }
                   
 #ifdef HAVE_IPSET
                   if (ipsets && (flags & (F_IPV4 | F_IPV6)))
                     for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
                       if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
                         log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
 #endif
 #ifdef HAVE_NFTSET
                   if (nftsets && (flags & (F_IPV4 | F_IPV6)))
                     for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
                       if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
                         log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
 #endif
                 }
               
               if (insert)
                 {
                   newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
                   if (newc && cpp)                    if (newc && cpp)
                     {                      {
                         next_uid(newc);
                       cpp->addr.cname.target.cache = newc;                        cpp->addr.cname.target.cache = newc;
                       cpp->addr.cname.uid = newc->uid;                        cpp->addr.cname.uid = newc->uid;
                     }                      }
                     cpp = NULL;
                 }                  }
                 
                 if (aqtype == T_TXT)
                   {
                     if (!print_txt(header, qlen, name, p1, ardlen, secflag))
                       return 2;
                   }
                 else
                   log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
             }              }
             
             p1 = endrr;
             if (!CHECK_LEN(header, p1, qlen, 0))
               return 2; /* bad packet */
         }          }
         
         if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
           {
             if (flags & F_NXDOMAIN)
               {
                 flags &= ~(F_IPV4 | F_IPV6 | F_SRV);
                 
                 /* Can store NXDOMAIN reply for any qtype. */
                 insert = 1;
               }
             
             log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0);
             
             if (!searched_soa)
               {
                 searched_soa = 1;
                 ttl = find_soa(header, qlen, doctored);
               }
             
             /* If there's no SOA to get the TTL from, but there is a CNAME 
                pointing at this, inherit its TTL */
             if (insert && !option_bool(OPT_NO_NEG) && (ttl || cpp))
               {
                 if (ttl == 0)
                   ttl = cttl;
                 
                 newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));   
                 if (newc && cpp)
                   {
                     next_uid(newc);
                     cpp->addr.cname.target.cache = newc;
                     cpp->addr.cname.uid = newc->uid;
                   }
               }
           }
     }      }
       
   /* Don't put stuff from a truncated packet into the cache.    /* Don't put stuff from a truncated packet into the cache.
Line 1159  int extract_addresses(struct dns_header *header, size_ Line 937  int extract_addresses(struct dns_header *header, size_
   return 0;    return 0;
 }  }
   
   #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
   /* Don't pass control chars and weird escapes to UBus. */
   static int safe_name(char *name)
   {
     unsigned char *r;
     
     for (r = (unsigned char *)name; *r; r++)
       if (!isprint((int)*r))
         return 0;
     
     return 1;
   }
   
   void report_addresses(struct dns_header *header, size_t len, u32 mark)
   {
     unsigned char *p, *endrr;
     int i;
     unsigned long attl;
     struct allowlist *allowlists;
     char **pattern_pos;
     
     if (RCODE(header) != NOERROR)
       return;
     
     for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
       if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask))
         for (pattern_pos = allowlists->patterns; *pattern_pos; pattern_pos++)
           if (!strcmp(*pattern_pos, "*"))
             return;
     
     if (!(p = skip_questions(header, len)))
       return;
     for (i = ntohs(header->ancount); i != 0; i--)
       {
         int aqtype, aqclass, ardlen;
         
         if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
           return;
         
         if (!CHECK_LEN(header, p, len, 10))
           return;
         GETSHORT(aqtype, p);
         GETSHORT(aqclass, p);
         GETLONG(attl, p);
         GETSHORT(ardlen, p);
         
         if (!CHECK_LEN(header, p, len, ardlen))
           return;
         endrr = p+ardlen;
         
         if (aqclass == C_IN)
           {
             if (aqtype == T_CNAME)
               {
                 if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
                   return;
                 if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
                   ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
               }
             if (aqtype == T_A)
               {
                 struct in_addr addr;
                 char ip[INET_ADDRSTRLEN];
                 if (ardlen != INADDRSZ)
                   return;
                 memcpy(&addr, p, ardlen);
                 if (inet_ntop(AF_INET, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
                   ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
               }
             else if (aqtype == T_AAAA)
               {
                 struct in6_addr addr;
                 char ip[INET6_ADDRSTRLEN];
                 if (ardlen != IN6ADDRSZ)
                   return;
                 memcpy(&addr, p, ardlen);
                 if (inet_ntop(AF_INET6, &addr, ip, sizeof ip) && safe_name(daemon->namebuff))
                   ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl);
               }
           }
         
         p = endrr;
       }
   }
   #endif
   
 /* If the packet holds exactly one query  /* If the packet holds exactly one query
    return F_IPV4 or F_IPV6  and leave the name from the query in name */     return F_IPV4 or F_IPV6  and leave the name from the query in name */
 unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)  unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
Line 1169  unsigned int extract_request(struct dns_header *header Line 1033  unsigned int extract_request(struct dns_header *header
   if (typep)    if (typep)
     *typep = 0;      *typep = 0;
   
     *name = 0; /* return empty name if no query found. */
     
   if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)    if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
     return 0; /* must be exactly one query. */      return 0; /* must be exactly one query. */
       
     if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
       return 0; /* non-standard query. */
     
   if (!extract_name(header, qlen, &p, name, 1, 4))    if (!extract_name(header, qlen, &p, name, 1, 4))
     return 0; /* bad packet */      return 0; /* bad packet */
         
Line 1190  unsigned int extract_request(struct dns_header *header Line 1059  unsigned int extract_request(struct dns_header *header
       if (qtype == T_ANY)        if (qtype == T_ANY)
         return  F_IPV4 | F_IPV6;          return  F_IPV4 | F_IPV6;
     }      }
   
   #ifdef HAVE_DNSSEC
     /* F_DNSSECOK as agument to search_servers() inhibits forwarding
        to servers for domains without a trust anchor. This make the
        behaviour for DS and DNSKEY queries we forward the same
        as for DS and DNSKEY queries we originate. */
     if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
       return F_DNSSECOK;
   #endif
       
   return F_QUERY;    return F_QUERY;
 }  }
   
void setup_reply(struct dns_header *header, unsigned int flags, int ede)
size_t setup_reply(struct dns_header *header, size_t qlen, 
                struct all_addr *addrp, unsigned int flags, unsigned long ttl) 
 {  {
   unsigned char *p = skip_questions(header, qlen);  
     
   /* clear authoritative and truncated flags, set QR flag */    /* clear authoritative and truncated flags, set QR flag */
  header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;  header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
  /* set RA flag */  /* clear AD flag, set RA flag */
  header->hb4 |= HB4_RA;  header->hb4 = (header->hb4 & ~HB4_AD) | HB4_RA;
   
   header->nscount = htons(0);    header->nscount = htons(0);
   header->arcount = htons(0);    header->arcount = htons(0);
   header->ancount = htons(0); /* no answers unless changed below */    header->ancount = htons(0); /* no answers unless changed below */
  if (flags == F_NEG)  if (flags == F_NOERR)
    SET_RCODE(header, SERVFAIL); /* couldn't get memory */ 
  else if (flags == F_NOERR) 
     SET_RCODE(header, NOERROR); /* empty domain */      SET_RCODE(header, NOERROR); /* empty domain */
   else if (flags == F_NXDOMAIN)    else if (flags == F_NXDOMAIN)
     SET_RCODE(header, NXDOMAIN);      SET_RCODE(header, NXDOMAIN);
  else if (p && flags == F_IPV4)  else if (flags & ( F_IPV4 | F_IPV6))
    { /* we know the address */ 
      SET_RCODE(header, NOERROR); 
      header->ancount = htons(1); 
      header->hb3 |= HB3_AA; 
      add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp); 
    } 
#ifdef HAVE_IPV6 
  else if (p && flags == F_IPV6) 
     {      {
       SET_RCODE(header, NOERROR);        SET_RCODE(header, NOERROR);
       header->ancount = htons(1);  
       header->hb3 |= HB3_AA;        header->hb3 |= HB3_AA;
       add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);  
     }      }
 #endif  
   else /* nowhere to forward to */    else /* nowhere to forward to */
    SET_RCODE(header, REFUSED);    {
       union all_addr a;
  return p - (unsigned char *)header;      a.log.rcode = REFUSED;
       a.log.ede = ede;
       log_query(F_CONFIG | F_RCODE, "error", &a, NULL, 0);
       SET_RCODE(header, REFUSED);
     }
 }  }
   
 /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */  /* 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)  int check_for_local_domain(char *name, time_t now)
 {  {
   struct crec *crecp;  
   struct mx_srv_record *mx;    struct mx_srv_record *mx;
   struct txt_record *txt;    struct txt_record *txt;
   struct interface_name *intr;    struct interface_name *intr;
   struct ptr_record *ptr;    struct ptr_record *ptr;
   struct naptr *naptr;    struct naptr *naptr;
   
   if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR)) &&  
       (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))  
     return 1;  
     
   for (naptr = daemon->naptr; naptr; naptr = naptr->next)    for (naptr = daemon->naptr; naptr; naptr = naptr->next)
     if (hostname_isequal(name, naptr->name))     if (hostname_issubdomain(name, naptr->name))
       return 1;        return 1;
   
    for (mx = daemon->mxnames; mx; mx = mx->next)     for (mx = daemon->mxnames; mx; mx = mx->next)
    if (hostname_isequal(name, mx->name))    if (hostname_issubdomain(name, mx->name))
       return 1;        return 1;
   
   for (txt = daemon->txt; txt; txt = txt->next)    for (txt = daemon->txt; txt; txt = txt->next)
    if (hostname_isequal(name, txt->name))    if (hostname_issubdomain(name, txt->name))
       return 1;        return 1;
   
   for (intr = daemon->int_names; intr; intr = intr->next)    for (intr = daemon->int_names; intr; intr = intr->next)
    if (hostname_isequal(name, intr->name))    if (hostname_issubdomain(name, intr->name))
       return 1;        return 1;
   
   for (ptr = daemon->ptr; ptr; ptr = ptr->next)    for (ptr = daemon->ptr; ptr; ptr = ptr->next)
    if (hostname_isequal(name, ptr->name))    if (hostname_issubdomain(name, ptr->name))
       return 1;        return 1;
 
   if (cache_find_non_terminal(name, now))
     return 1;
 
   return 0;    return 0;
 }  }
   
/* Is the packet a reply with the answer address equal to addr?static int check_bad_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr, char *name, unsigned long *ttlp)
   If so mung is into an NXDOMAIN reply and also put that information 
   in the cache. */ 
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,  
                             struct bogus_addr *baddr, time_t now) 
 {  {
   unsigned char *p;    unsigned char *p;
   int i, qtype, qclass, rdlen;    int i, qtype, qclass, rdlen;
   unsigned long ttl;    unsigned long ttl;
   struct bogus_addr *baddrp;    struct bogus_addr *baddrp;
  
   /* skip over questions */    /* skip over questions */
   if (!(p = skip_questions(header, qlen)))    if (!(p = skip_questions(header, qlen)))
     return 0; /* bad packet */      return 0; /* bad packet */
   
   for (i = ntohs(header->ancount); i != 0; i--)    for (i = ntohs(header->ancount); i != 0; i--)
     {      {
      if (!extract_name(header, qlen, &p, name, 1, 10))      if (name && !extract_name(header, qlen, &p, name, 1, 10))
         return 0; /* bad packet */          return 0; /* bad packet */
  
       if (!name && !(p = skip_name(p, header, qlen, 10)))
         return 0;
       
       GETSHORT(qtype, p);         GETSHORT(qtype, p); 
       GETSHORT(qclass, p);        GETSHORT(qclass, p);
       GETLONG(ttl, p);        GETLONG(ttl, p);
       GETSHORT(rdlen, p);        GETSHORT(rdlen, p);
   
         if (ttlp)
           *ttlp = ttl;
               
      if (qclass == C_IN && qtype == T_A)      if (qclass == C_IN)
         {          {
          if (!CHECK_LEN(header, p, qlen, INADDRSZ))          if (qtype == T_A)
            return 0;            {
                        struct in_addr addr;
          for (baddrp = baddr; baddrp; baddrp = baddrp->next)              
            if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)              if (!CHECK_LEN(header, p, qlen, INADDRSZ))
              {                return 0;
                /* Found a bogus address. Insert that info here, since there no SOA record
                   to get the ttl from in the normal processing */              memcpy(&addr, p, INADDRSZ);
                cache_start_insert();
                cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);              for (baddrp = baddr; baddrp; baddrp = baddrp->next)
                cache_end_insert();                if (!baddrp->is6 && is_same_net_prefix(addr, baddrp->addr.addr4, baddrp->prefix))
                                  return 1;
                return 1;            }
              }          else if (qtype == T_AAAA)
             {
               struct in6_addr addr;
               
               if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ))
                 return 0;
 
               memcpy(&addr, p, IN6ADDRSZ);
 
               for (baddrp = baddr; baddrp; baddrp = baddrp->next)
                 if (baddrp->is6 && is_same_net6(&addr, &baddrp->addr.addr6, baddrp->prefix))
                   return 1;
             }
         }          }
               
       if (!ADD_RDLEN(header, p, qlen, rdlen))        if (!ADD_RDLEN(header, p, qlen, rdlen))
Line 1323  int check_for_bogus_wildcard(struct dns_header *header Line 1200  int check_for_bogus_wildcard(struct dns_header *header
   return 0;    return 0;
 }  }
   
   /* Is the packet a reply with the answer address equal to addr?
      If so mung is into an NXDOMAIN reply and also put that information
      in the cache. */
   int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now)
   {
     unsigned long ttl;
   
     if (check_bad_address(header, qlen, daemon->bogus_addr, name, &ttl))
       {
         /* 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, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
         cache_end_insert();
   
         return 1;
       }
   
     return 0;
   }
   
   int check_for_ignored_address(struct dns_header *header, size_t qlen)
   {
     return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL);
   }
   
 int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,   int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, 
                         unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)                          unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
 {  {
Line 1332  int add_resource_record(struct dns_header *header, cha Line 1235  int add_resource_record(struct dns_header *header, cha
   unsigned short usval;    unsigned short usval;
   long lval;    long lval;
   char *sval;    char *sval;
     
   #define CHECK_LIMIT(size) \
     if (limit && p + (size) > (unsigned char*)limit) goto truncated;
   
   if (truncp && *truncp)  
     return 0;  
    
   va_start(ap, format);   /* make ap point to 1st unamed argument */    va_start(ap, format);   /* make ap point to 1st unamed argument */
       
     if (truncp && *truncp)
       goto truncated;
     
   if (nameoffset > 0)    if (nameoffset > 0)
     {      {
         CHECK_LIMIT(2);
       PUTSHORT(nameoffset | 0xc000, p);        PUTSHORT(nameoffset | 0xc000, p);
     }      }
   else    else
     {      {
       char *name = va_arg(ap, char *);        char *name = va_arg(ap, char *);
      if (name)      if (name && !(p = do_rfc1035_name(p, name, limit)))
        p = do_rfc1035_name(p, name);        goto truncated;
       
       if (nameoffset < 0)        if (nameoffset < 0)
         {          {
             CHECK_LIMIT(2);
           PUTSHORT(-nameoffset | 0xc000, p);            PUTSHORT(-nameoffset | 0xc000, p);
         }          }
       else        else
        *p++ = 0;        {
           CHECK_LIMIT(1);
           *p++ = 0;
         }
     }      }
   
     /* type (2) + class (2) + ttl (4) + rdlen (2) */
     CHECK_LIMIT(10);
     
   PUTSHORT(type, p);    PUTSHORT(type, p);
   PUTSHORT(class, p);    PUTSHORT(class, p);
   PUTLONG(ttl, p);      /* TTL */    PUTLONG(ttl, p);      /* TTL */
Line 1365  int add_resource_record(struct dns_header *header, cha Line 1280  int add_resource_record(struct dns_header *header, cha
   for (; *format; format++)    for (; *format; format++)
     switch (*format)      switch (*format)
       {        {
 #ifdef HAVE_IPV6  
       case '6':        case '6':
           CHECK_LIMIT(IN6ADDRSZ);
         sval = va_arg(ap, char *);           sval = va_arg(ap, char *); 
         memcpy(p, sval, IN6ADDRSZ);          memcpy(p, sval, IN6ADDRSZ);
         p += IN6ADDRSZ;          p += IN6ADDRSZ;
         break;          break;
 #endif  
                   
       case '4':        case '4':
           CHECK_LIMIT(INADDRSZ);
         sval = va_arg(ap, char *);           sval = va_arg(ap, char *); 
         memcpy(p, sval, INADDRSZ);          memcpy(p, sval, INADDRSZ);
         p += INADDRSZ;          p += INADDRSZ;
         break;          break;
                   
       case 'b':        case 'b':
           CHECK_LIMIT(1);
         usval = va_arg(ap, int);          usval = va_arg(ap, int);
         *p++ = usval;          *p++ = usval;
         break;          break;
                   
       case 's':        case 's':
           CHECK_LIMIT(2);
         usval = va_arg(ap, int);          usval = va_arg(ap, int);
         PUTSHORT(usval, p);          PUTSHORT(usval, p);
         break;          break;
                   
       case 'l':        case 'l':
           CHECK_LIMIT(4);
         lval = va_arg(ap, long);          lval = va_arg(ap, long);
         PUTLONG(lval, p);          PUTLONG(lval, p);
         break;          break;
                   
       case 'd':        case 'd':
        /* get domain-name answer arg and store it in RDATA field */        /* get domain-name answer arg and store it in RDATA field */
        if (offset)        if (offset)
          *offset = p - (unsigned char *)header;          *offset = p - (unsigned char *)header;
        p = do_rfc1035_name(p, va_arg(ap, char *));        if (!(p = do_rfc1035_name(p, va_arg(ap, char *), limit)))
        *p++ = 0;          goto truncated;
         CHECK_LIMIT(1);
         *p++ = 0;
         break;          break;
                   
       case 't':        case 't':
         usval = va_arg(ap, int);          usval = va_arg(ap, int);
           CHECK_LIMIT(usval);
         sval = va_arg(ap, char *);          sval = va_arg(ap, char *);
         if (usval != 0)          if (usval != 0)
           memcpy(p, sval, usval);            memcpy(p, sval, usval);
Line 1415  int add_resource_record(struct dns_header *header, cha Line 1336  int add_resource_record(struct dns_header *header, cha
         usval = sval ? strlen(sval) : 0;          usval = sval ? strlen(sval) : 0;
         if (usval > 255)          if (usval > 255)
           usval = 255;            usval = 255;
           CHECK_LIMIT(usval + 1);
         *p++ = (unsigned char)usval;          *p++ = (unsigned char)usval;
         memcpy(p, sval, usval);          memcpy(p, sval, usval);
         p += usval;          p += usval;
Line 1423  int add_resource_record(struct dns_header *header, cha Line 1345  int add_resource_record(struct dns_header *header, cha
   
   va_end(ap);   /* clean up variable argument pointer */    va_end(ap);   /* clean up variable argument pointer */
       
     /* Now, store real RDLength. sav already checked against limit. */
   j = p - sav - 2;    j = p - sav - 2;
  PUTSHORT(j, sav);     /* Now, store real RDLength */  PUTSHORT(j, sav);
       
   /* check for overflow of buffer */  
   if (limit && ((unsigned char *)limit - p) < 0)  
     {  
       if (truncp)  
         *truncp = 1;  
       return 0;  
     }  
     
   *pp = p;    *pp = p;
   return 1;    return 1;
     
    truncated:
     va_end(ap);
     if (truncp)
       *truncp = 1;
     return 0;
   
   #undef CHECK_LIMIT
 }  }
   
   static int crec_isstale(struct crec *crecp, time_t now)
   {
     return (!(crecp->flags & F_IMMORTAL)) && difftime(crecp->ttd, now) < 0; 
   }
   
 static unsigned long crec_ttl(struct crec *crecp, time_t now)  static unsigned long crec_ttl(struct crec *crecp, time_t now)
 {  {
     signed long ttl = difftime(crecp->ttd, now);
   
   /* Return 0 ttl for DHCP entries, which might change    /* Return 0 ttl for DHCP entries, which might change
     before the lease expires. */     before the lease expires, unless configured otherwise. */
   
  if  (crecp->flags & (F_IMMORTAL | F_DHCP))  if (crecp->flags & F_DHCP)
    return daemon->local_ttl;    {
       int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
       
       /* Apply ceiling of actual lease length to configured TTL. */
       if (!(crecp->flags & F_IMMORTAL) && ttl < conf_ttl)
         return ttl;
       
       return conf_ttl;
     }     
       
  /* Return the Max TTL value if it is lower then the actual TTL */  /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
  if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))  if (crecp->flags & F_IMMORTAL)
    return crecp->ttd - now;    return crecp->ttd;
 
   /* Stale cache entries. */
   if (ttl < 0)
     return 0;
   
   /* Return the Max TTL value if it is lower than the actual TTL */
   if (daemon->max_ttl == 0 || ((unsigned)ttl < daemon->max_ttl))
     return ttl;
   else    else
     return daemon->max_ttl;      return daemon->max_ttl;
 }  }
     
   
   static int cache_validated(const struct crec *crecp)
   {
     return (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK));
   }
   
 /* return zero if we can't answer from cache, or packet size if we can */  /* return zero if we can't answer from cache, or packet size if we can */
 size_t answer_request(struct dns_header *header, char *limit, size_t qlen,    size_t answer_request(struct dns_header *header, char *limit, size_t qlen,  
                       struct in_addr local_addr, struct in_addr local_netmask,                         struct in_addr local_addr, struct in_addr local_netmask, 
                      time_t now, int *ad_reqd, int *do_bit                      time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
                       int *stale) 
 {  {
   char *name = daemon->namebuff;    char *name = daemon->namebuff;
  unsigned char *p, *ansp, *pheader;  unsigned char *p, *ansp;
   unsigned int qtype, qclass;    unsigned int qtype, qclass;
  struct all_addr addr;  union all_addr addr;
   int nameoffset;    int nameoffset;
   unsigned short flag;    unsigned short flag;
   int q, ans, anscount = 0, addncount = 0;    int q, ans, anscount = 0, addncount = 0;
  int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;  int dryrun = 0;
  int is_sign; 
   struct crec *crecp;    struct crec *crecp;
  int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;  int nxdomain = 0, notimp = 0, auth = 1, trunc = 0, sec_data = 1;
   struct mx_srv_record *rec;    struct mx_srv_record *rec;
   size_t len;    size_t len;
   int rd_bit = (header->hb3 & HB3_RD);
 
   if (stale)
     *stale = 0;
   
   /* never answer queries with RD unset, to avoid cache snooping. */
   if (ntohs(header->ancount) != 0 ||
       ntohs(header->nscount) != 0 ||
       ntohs(header->qdcount) == 0 ||
       OPCODE(header) != QUERY )
     return 0;
 
   /* Don't return AD set if checking disabled. */    /* Don't return AD set if checking disabled. */
   if (header->hb4 & HB4_CD)    if (header->hb4 & HB4_CD)
     sec_data = 0;      sec_data = 0;
       
  /* RFC 6840 5.7 */  /* If there is an  additional data section then it will be overwritten by
  *ad_reqd = header->hb4 & HB4_AD; 
  *do_bit = 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       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     the query. */
     forward rather than answering from the cache, which doesn't include  if (ntohs(header->arcount) != 0)
     security information, unless we're in DNSSEC validation mode. */    dryrun = 1;
   
   if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))  
     {   
       unsigned short udpsz, flags;  
       unsigned char *psave = pheader;  
   
       have_pseudoheader = 1;  
   
       GETSHORT(udpsz, pheader);  
       pheader += 2; /* ext_rcode */  
       GETSHORT(flags, pheader);  
         
       if ((sec_reqd = flags & 0x8000))  
         *do_bit = 1;/* do bit */   
       *ad_reqd = 1;  
   
       /* If our client is advertising a larger UDP packet size  
          than we allow, trim it so that we don't get an overlarge  
          response from upstream */  
   
       if (!is_sign && (udpsz > daemon->edns_pktsz))  
         PUTSHORT(daemon->edns_pktsz, psave);   
   
       dryrun = 1;  
     }  
   
   if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )  
     return 0;  
     
   for (rec = daemon->mxnames; rec; rec = rec->next)    for (rec = daemon->mxnames; rec; rec = rec->next)
     rec->offset = 0;      rec->offset = 0;
       
Line 1528  size_t answer_request(struct dns_header *header, char  Line 1457  size_t answer_request(struct dns_header *header, char 
   
   for (q = ntohs(header->qdcount); q != 0; q--)    for (q = ntohs(header->qdcount); q != 0; q--)
     {      {
         int count = 255; /* catch loops */
         
       /* save pointer to name for copying into answers */        /* save pointer to name for copying into answers */
       nameoffset = p - (unsigned char *)header;        nameoffset = p - (unsigned char *)header;
   
Line 1539  size_t answer_request(struct dns_header *header, char  Line 1470  size_t answer_request(struct dns_header *header, char 
       GETSHORT(qclass, p);        GETSHORT(qclass, p);
   
       ans = 0; /* have we answered this question */        ans = 0; /* have we answered this question */
   
         if (qclass == C_IN)
           while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN)))
             {
               char *cname_target;
               int stale_flag = 0;
               
               if (crec_isstale(crecp, now))
                 {
                   if (stale)
                     *stale = 1;
                   
                   stale_flag = F_STALE;
                 }
               
               if (crecp->flags & F_NXDOMAIN)
                 {
                   if (qtype == T_CNAME)
                     {
                      if (!dryrun)
                        log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
                       auth = 0;
                       nxdomain = 1;
                       ans = 1;
                     }
                   break;
                 }  
   
               cname_target = cache_get_cname_target(crecp);
               
               /* If the client asked for DNSSEC  don't use cached data. */
               if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
                   (rd_bit && (!do_bit || cache_validated(crecp))))
                 {
                   if (crecp->flags & F_CONFIG || qtype == T_CNAME)
                     ans = 1;
                   
                   if (!(crecp->flags & F_DNSSECOK))
                     sec_data = 0;
                   
                   if (!dryrun)
                     {
                       log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                               crec_ttl(crecp, now), &nameoffset,
                                               T_CNAME, C_IN, "d", cname_target))
                         anscount++;
                     }
                   
                 }
               else
                 return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */
               
               if (qtype == T_CNAME)
                 break;
               
               strcpy(name, cname_target);
             }
               
       if (qtype == T_TXT || qtype == T_ANY)        if (qtype == T_TXT || qtype == T_ANY)
         {          {
Line 1547  size_t answer_request(struct dns_header *header, char  Line 1536  size_t answer_request(struct dns_header *header, char 
             {              {
               if (t->class == qclass && hostname_isequal(name, t->name))                if (t->class == qclass && hostname_isequal(name, t->name))
                 {                  {
                  ans = 1;                  ans = 1, sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                       unsigned long ttl = daemon->local_ttl;                        unsigned long ttl = daemon->local_ttl;
                       int ok = 1;                        int ok = 1;
                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");#ifndef NO_ID
                       /* Dynamically generate stat record */                        /* Dynamically generate stat record */
                       if (t->stat != 0)                        if (t->stat != 0)
                         {                          {
Line 1560  size_t answer_request(struct dns_header *header, char  Line 1549  size_t answer_request(struct dns_header *header, char 
                           if (!cache_make_stat(t))                            if (!cache_make_stat(t))
                             ok = 0;                              ok = 0;
                         }                          }
                      #endif
                      if (ok && add_resource_record(header, limit, &trunc, nameoffset, &ansp,                       if (ok)
                                                    ttl, NULL,                        {
                                                    T_TXT, t->class, "t", t->len, t->txt))                          log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
                        anscount++;                          if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                  ttl, NULL,
                                                   T_TXT, t->class, "t", t->len, t->txt))
                             anscount++;
                         }
                     }                      }
                 }                  }
             }              }
         }          }
   
#ifdef HAVE_DNSSEC      if (qclass == C_CHAOS)
      if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS)) 
         {          {
          int gotone = 0;          /* don't forward *.bind and *.server chaos queries - always reply with NOTIMP */
          struct blockdata *keydata;          if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name))
 
          /* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */ 
          if (sec_reqd) 
             {              {
              crecp = NULL;              if (!ans)
              while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS))) 
                if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype) 
                  break; 
            } 
           
          if (!sec_reqd || crecp) 
            { 
              if (qtype == T_DS) 
                 {                  {
                  crecp = NULL;                  notimp = 1, auth = 0;
                  while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))                  if (!dryrun)
                    if (crecp->uid == qclass)                    {
                      {                       addr.log.rcode = NOTIMP;
                        gotone = 1                       log_query(F_CONFIG | F_RCODE, name, &addr, NULL, 0);
                        if (!dryrun)                    }
                          {                  ans = 1, sec_data = 0;
                            if (crecp->flags & F_NEG) 
                              { 
                                if (crecp->flags & F_NXDOMAIN) 
                                  nxdomain = 1; 
                                log_query(F_UPSTREAM, name, NULL, "secure no DS");   
                              } 
                            else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL))) 
                              {                                                
                                struct all_addr a; 
                                a.addr.keytag =  crecp->addr.ds.keytag; 
                                log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u"); 
                                if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,  
                                                        crec_ttl(crecp, now), &nameoffset, 
                                                        T_DS, qclass, "sbbt",  
                                                        crecp->addr.ds.keytag, crecp->addr.ds.algo,  
                                                        crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata)) 
                                  anscount++; 
                                 
                              }  
                          } 
                      } 
                 }                  }
               else /* DNSKEY */  
                 {  
                   crecp = NULL;  
                   while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))  
                     if (crecp->uid == qclass)  
                       {  
                         gotone = 1;  
                         if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))  
                           {                                                     
                             struct all_addr a;  
                             a.addr.keytag =  crecp->addr.key.keytag;  
                             log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");  
                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,   
                                                     crec_ttl(crecp, now), &nameoffset,  
                                                     T_DNSKEY, qclass, "sbbt",   
                                                     crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))  
                               anscount++;  
                           }  
                       }  
                 }  
             }              }
             
           /* Now do RRSIGs */  
           if (gotone)  
             {  
               ans = 1;  
               auth = 0;  
               if (!dryrun && sec_reqd)  
                 {  
                   crecp = NULL;  
                   while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))  
                     if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&  
                         (keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))  
                       {  
                         add_resource_record(header, limit, &trunc, nameoffset, &ansp,   
                                             crec_ttl(crecp, now), &nameoffset,  
                                             T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);  
                         anscount++;  
                       }  
                 }  
             }  
         }          }
#endif       
       
       if (qclass == C_IN)        if (qclass == C_IN)
         {          {
           struct txt_record *t;            struct txt_record *t;
Line 1671  size_t answer_request(struct dns_header *header, char  Line 1589  size_t answer_request(struct dns_header *header, char 
             if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))              if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
               {                {
                 ans = 1;                  ans = 1;
                   sec_data = 0;
                 if (!dryrun)                  if (!dryrun)
                   {                    {
                    log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");                    log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, t->class);
                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                             daemon->local_ttl, NULL,                                              daemon->local_ttl, NULL,
                                             t->class, C_IN, "t", t->len, t->txt))                                              t->class, C_IN, "t", t->len, t->txt))
                      anscount ++;                      anscount++;
                   }                    }
               }                }
                                   
Line 1698  size_t answer_request(struct dns_header *header, char  Line 1617  size_t answer_request(struct dns_header *header, char 
                     struct addrlist *addrlist;                      struct addrlist *addrlist;
                                           
                     for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)                      for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                      if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)                      if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr4.s_addr == addrlist->addr.addr4.s_addr)
                         break;                          break;
                                           
                     if (addrlist)                      if (addrlist)
                       break;                        break;
                    else                    else if (!(intr->flags & INP4))
                       while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)                        while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
                         intr = intr->next;                          intr = intr->next;
                   }                    }
 #ifdef HAVE_IPV6  
               else if (is_arpa == F_IPV6)                else if (is_arpa == F_IPV6)
                 for (intr = daemon->int_names; intr; intr = intr->next)                  for (intr = daemon->int_names; intr; intr = intr->next)
                   {                    {
                     struct addrlist *addrlist;                      struct addrlist *addrlist;
                                           
                     for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)                      for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                      if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))                      if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr6, &addrlist->addr.addr6))
                         break;                          break;
                                           
                     if (addrlist)                      if (addrlist)
                       break;                        break;
                    else                    else if (!(intr->flags & INP6))
                       while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)                        while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
                         intr = intr->next;                          intr = intr->next;
                   }                    }
 #endif  
                               
               if (intr)                if (intr)
                 {                  {
                     sec_data = 0;
                   ans = 1;                    ans = 1;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                      log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);                      log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                               daemon->local_ttl, NULL,                                                daemon->local_ttl, NULL,
                                               T_PTR, C_IN, "d", intr->name))                                                T_PTR, C_IN, "d", intr->name))
Line 1740  size_t answer_request(struct dns_header *header, char  Line 1658  size_t answer_request(struct dns_header *header, char 
               else if (ptr)                else if (ptr)
                 {                  {
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>", 0);
                       for (ptr = daemon->ptr; ptr; ptr = ptr->next)                        for (ptr = daemon->ptr; ptr; ptr = ptr->next)
                         if (hostname_isequal(name, ptr->name) &&                          if (hostname_isequal(name, ptr->name) &&
                             add_resource_record(header, limit, &trunc, nameoffset, &ansp,                               add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
Line 1752  size_t answer_request(struct dns_header *header, char  Line 1671  size_t answer_request(struct dns_header *header, char 
                                                     
                     }                      }
                 }                  }
              else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))              else if (is_arpa && (crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
                 {                  {
                  if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)                  /* Don't use cache when DNSSEC data required, unless we know that
                      the zone is unsigned, which implies that we're doing
                      validation. */
                   if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
                       (rd_bit && (!do_bit || cache_validated(crecp)) ))
                     {                      {
                       if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))  
                         crecp = NULL;  
 #ifdef HAVE_DNSSEC  
                       else if (crecp->flags & F_DNSSECOK)  
                         {  
                           int gotsig = 0;  
                           struct crec *rr_crec = NULL;  
   
                           while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))  
                             {  
                               if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)  
                                 {  
                                   char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);  
                                   gotsig = 1;  
                                     
                                   if (!dryrun &&   
                                       add_resource_record(header, limit, &trunc, nameoffset, &ansp,   
                                                           rr_crec->ttd - now, &nameoffset,  
                                                           T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))  
                                     anscount++;  
                                 }  
                             }   
                             
                           if (!gotsig)  
                             crecp = NULL;  
                         }  
 #endif  
                     }  
   
                   if (crecp)  
                     {  
                       do                         do 
                         {                           { 
                             int stale_flag = 0;
                             
                             if (crec_isstale(crecp, now))
                               {
                                 if (stale)
                                   *stale = 1;
                                 
                                 stale_flag = F_STALE;
                               }
                             
                           /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */                            /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
                           if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))                            if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
                             continue;                              continue;
                                                       
                             
                           if (!(crecp->flags & F_DNSSECOK))                            if (!(crecp->flags & F_DNSSECOK))
                             sec_data = 0;                              sec_data = 0;
                                                       
                             ans = 1;
                             
                           if (crecp->flags & F_NEG)                            if (crecp->flags & F_NEG)
                             {                              {
                               ans = 1;  
                               auth = 0;                                auth = 0;
                               if (crecp->flags & F_NXDOMAIN)                                if (crecp->flags & F_NXDOMAIN)
                                 nxdomain = 1;                                  nxdomain = 1;
                               if (!dryrun)                                if (!dryrun)
                                log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);                                log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0);
                             }                              }
                          else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))                          else
                             {                              {
                               ans = 1;  
                               if (!(crecp->flags & (F_HOSTS | F_DHCP)))                                if (!(crecp->flags & (F_HOSTS | F_DHCP)))
                                 auth = 0;                                  auth = 0;
                               if (!dryrun)                                if (!dryrun)
                                 {                                  {
                                  log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,                                   log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr, 
                                            record_source(crecp->uid));                                            record_source(crecp->uid), 0);
                                                                       
                                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                           crec_ttl(crecp, now), NULL,                                                            crec_ttl(crecp, now), NULL,
Line 1827  size_t answer_request(struct dns_header *header, char  Line 1730  size_t answer_request(struct dns_header *header, char 
               else if (is_rev_synth(is_arpa, &addr, name))                else if (is_rev_synth(is_arpa, &addr, name))
                 {                  {
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                      log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);                       log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL, 0);
                                               
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                               daemon->local_ttl, NULL,                                                daemon->local_ttl, NULL,
Line 1837  size_t answer_request(struct dns_header *header, char  Line 1741  size_t answer_request(struct dns_header *header, char 
                               anscount++;                                anscount++;
                     }                      }
                 }                  }
              else if (is_arpa == F_IPV4 &&               else if (option_bool(OPT_BOGUSPRIV) &&
                       option_bool(OPT_BOGUSPRIV) &&                        ((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) &&
                       private_net(addr.addr.addr4, 1))                       !lookup_domain(name, F_DOMAINSRV, NULL, NULL))
                 {                  {
                  /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */                  /* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   nxdomain = 1;                    nxdomain = 1;
                   if (!dryrun)                    if (!dryrun)
                    log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,                     log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
                              name, &addr, NULL);                              name, &addr, NULL, 0);
                 }                  }
             }              }
            
           for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)            for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
             {              {
              unsigned short type = T_A;              unsigned short type = (flag == F_IPV6) ? T_AAAA : T_A;
               struct interface_name *intr;                struct interface_name *intr;
   
               if (flag == F_IPV6)  
 #ifdef HAVE_IPV6  
                 type = T_AAAA;  
 #else  
                 break;  
 #endif  
                 
               if (qtype != type && qtype != T_ANY)                if (qtype != type && qtype != T_ANY)
                 continue;                  continue;
                               
               /* Check for "A for A"  queries; be rather conservative   
                  about what looks like dotted-quad.  */  
               if (qtype == T_A)  
                 {  
                   char *cp;  
                   unsigned int i, a;  
                   int x;  
   
                   for (cp = name, i = 0, a = 0; *cp; i++)  
                     {  
                       if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)   
                         {  
                           i = 5;  
                           break;  
                         }  
                         
                       a = (a << 8) + x;  
                         
                       if (*cp == '.')   
                         cp++;  
                     }  
                     
                   if (i == 4)  
                     {  
                       ans = 1;  
                       if (!dryrun)  
                         {  
                           addr.addr.addr4.s_addr = htonl(a);  
                           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;  
                     }  
                 }  
   
               /* interface name stuff */                /* interface name stuff */
             intname_restart:  
               for (intr = daemon->int_names; intr; intr = intr->next)                for (intr = daemon->int_names; intr; intr = intr->next)
                 if (hostname_isequal(name, intr->name))                  if (hostname_isequal(name, intr->name))
                   break;                    break;
Line 1911  size_t answer_request(struct dns_header *header, char  Line 1771  size_t answer_request(struct dns_header *header, char 
               if (intr)                if (intr)
                 {                  {
                   struct addrlist *addrlist;                    struct addrlist *addrlist;
                  int gotit = 0;                  int gotit = 0, localise = 0;
   
                   enumerate_interfaces(0);                    enumerate_interfaces(0);
                       
                     /* See if a putative address is on the network from which we received
                        the query, is so we'll filter other answers. */
                     if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && type == T_A)
                       for (intr = daemon->int_names; intr; intr = intr->next)
                         if (hostname_isequal(name, intr->name))
                           for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                             if (!(addrlist->flags & ADDRLIST_IPV6) && 
                                 is_same_net(addrlist->addr.addr4, local_addr, local_netmask))
                               {
                                 localise = 1;
                                 break;
                               }
                                       
                   for (intr = daemon->int_names; intr; intr = intr->next)                    for (intr = daemon->int_names; intr; intr = intr->next)
                     if (hostname_isequal(name, intr->name))                      if (hostname_isequal(name, intr->name))
                       {                        {
                        ans = 1;                        for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                        if (!dryrun)                          if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
                          {                            {
                                                          if (localise && 
                            for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)                                  !is_same_net(addrlist->addr.addr4, local_addr, local_netmask))
#ifdef HAVE_IPV6                                continue;
                              if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
#endif                              if (addrlist->flags & ADDRLIST_REVONLY)
                                 continue;
 
                               ans = 1;  
                               sec_data = 0;
                               if (!dryrun)
                                 {                                  {
                                   gotit = 1;                                    gotit = 1;
                                  log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);                                  log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
                                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                           daemon->local_ttl, NULL, type, C_IN,                                                             daemon->local_ttl, NULL, type, C_IN, 
                                                           type == T_A ? "4" : "6", &addrlist->addr))                                                            type == T_A ? "4" : "6", &addrlist->addr))
                                     anscount++;                                      anscount++;
                                 }                                  }
                          }                            }
                       }                        }
                                       
                   if (!dryrun && !gotit)                    if (!dryrun && !gotit)
                    log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL);                    log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL, 0);
                                             
                   continue;                    continue;
                 }                  }
   
            cname_restart:              if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))))
              if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME | (dryrun ? F_NO_RR : 0)))) 
                 {                  {
                   int localise = 0;                    int localise = 0;
                                                    
                  /* See if a putative address is on the network from which we recieved                  /* See if a putative address is on the network from which we received
                      the query, is so we'll filter other answers. */                       the query, is so we'll filter other answers. */
                   if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)                    if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
                     {                      {
                       struct crec *save = crecp;                        struct crec *save = crecp;
                       do {                        do {
                         if ((crecp->flags & F_HOSTS) &&                          if ((crecp->flags & F_HOSTS) &&
                            is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))                            is_same_net(crecp->addr.addr4, local_addr, local_netmask))
                           {                            {
                             localise = 1;                              localise = 1;
                             break;                              break;
                           }                             } 
                        } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));                        } while ((crecp = cache_find_by_name(crecp, name, now, flag)));
                       crecp = save;                        crecp = save;
                     }                      }
   
                  /* If the client asked for DNSSEC and we can't provide RRSIGs, either                  /* If the client asked for DNSSEC  don't use cached data. */
                     because we've not doing DNSSEC or the cached answer is signed by negative,                  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
                     don't answer from the cache, forward instead. */                      (rd_bit && (!do_bit || cache_validated(crecp)) ))
                  if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd) 
                    { 
                      if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))) 
                        crecp = NULL; 
#ifdef HAVE_DNSSEC 
                      else if (crecp->flags & F_DNSSECOK) 
                        { 
                          /* We're returning validated data, need to return the RRSIG too. */ 
                          struct crec *rr_crec = NULL; 
                          int sigtype = type; 
                          /* The signature may have expired even though the data is still in cache,  
                             forward instead of answering from cache if so. */ 
                          int gotsig = 0; 
                           
                          if (crecp->flags & F_CNAME) 
                            sigtype = T_CNAME; 
                           
                          while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY))) 
                            { 
                              if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN) 
                                { 
                                  char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL); 
                                  gotsig = 1; 
                                   
                                  if (!dryrun &&  
                                      add_resource_record(header, limit, &trunc, nameoffset, &ansp,  
                                                          rr_crec->ttd - now, &nameoffset, 
                                                          T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata)) 
                                    anscount++; 
                                } 
                            } 
                           
                          if (!gotsig) 
                            crecp = NULL; 
                        } 
#endif 
                    }             
 
                  if (crecp) 
                     do                      do
                       {                         { 
                           int stale_flag = 0;
                           
                           if (crec_isstale(crecp, now))
                             {
                               if (stale)
                                 *stale = 1;
                               
                               stale_flag = F_STALE;
                             }
                           
                         /* don't answer wildcard queries with data not from /etc/hosts                          /* don't answer wildcard queries with data not from /etc/hosts
                            or DHCP leases */                             or DHCP leases */
                         if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))                          if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
Line 2016  size_t answer_request(struct dns_header *header, char  Line 1864  size_t answer_request(struct dns_header *header, char 
                         if (!(crecp->flags & F_DNSSECOK))                          if (!(crecp->flags & F_DNSSECOK))
                           sec_data = 0;                            sec_data = 0;
                                                   
                        if (crecp->flags & F_CNAME)                        if (crecp->flags & F_NEG)
                           {                            {
                            char *cname_target = cache_get_cname_target(crecp);                            ans = 1;
                                                        auth = 0;
                             if (crecp->flags & F_NXDOMAIN)
                               nxdomain = 1;
                             if (!dryrun)                              if (!dryrun)
                              {                              log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
                                log_query(crecp->flags, name, NULL, record_source(crecp->uid)); 
                                if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,  
                                                        crec_ttl(crecp, now), &nameoffset, 
                                                        T_CNAME, C_IN, "d", cname_target)) 
                                  anscount++; 
                              } 
                             
                            strcpy(name, cname_target); 
                            /* check if target interface_name */ 
                            if (crecp->addr.cname.uid == SRC_INTERFACE) 
                              goto intname_restart; 
                            else 
                              goto cname_restart; 
                           }                            }
                           
                         if (crecp->flags & F_NEG)  
                           {  
                             /* We don't cache NSEC records, so if a DNSSEC-validated negative answer  
                                is cached and the client wants DNSSEC, forward rather than answering from the cache */  
                             if (!sec_reqd || !(crecp->flags & F_DNSSECOK))  
                               {  
                                 ans = 1;  
                                 auth = 0;  
                                 if (crecp->flags & F_NXDOMAIN)  
                                   nxdomain = 1;  
                                 if (!dryrun)  
                                   log_query(crecp->flags, name, NULL, NULL);  
                               }  
                           }  
                         else                           else 
                           {                            {
                             /* If we are returning local answers depending on network,                              /* If we are returning local answers depending on network,
                                filter here. */                                 filter here. */
                             if (localise &&                               if (localise && 
                                 (crecp->flags & F_HOSTS) &&                                  (crecp->flags & F_HOSTS) &&
                                !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))                                !is_same_net(crecp->addr.addr4, local_addr, local_netmask))
                               continue;                                continue;
                                                           
                             if (!(crecp->flags & (F_HOSTS | F_DHCP)))                              if (!(crecp->flags & (F_HOSTS | F_DHCP)))
Line 2066  size_t answer_request(struct dns_header *header, char  Line 1888  size_t answer_request(struct dns_header *header, char 
                             ans = 1;                              ans = 1;
                             if (!dryrun)                              if (!dryrun)
                               {                                {
                                log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,                                log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr,
                                          record_source(crecp->uid));                                          record_source(crecp->uid), 0);
                                                                   
                                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                         crec_ttl(crecp, now), NULL, type, C_IN,                                                           crec_ttl(crecp, now), NULL, type, C_IN, 
Line 2075  size_t answer_request(struct dns_header *header, char  Line 1897  size_t answer_request(struct dns_header *header, char 
                                   anscount++;                                    anscount++;
                               }                                }
                           }                            }
                      } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));                      } while ((crecp = cache_find_by_name(crecp, name, now, flag)));
                 }                  }
               else if (is_name_synthetic(flag, name, &addr))                else if (is_name_synthetic(flag, name, &addr))
                 {                  {
                  ans = 1;                  ans = 1, sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                      log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);                      log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                               daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))                                                daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
                         anscount++;                          anscount++;
Line 2090  size_t answer_request(struct dns_header *header, char  Line 1912  size_t answer_request(struct dns_header *header, char 
                 }                  }
             }              }
   
           if (qtype == T_CNAME || qtype == T_ANY)  
             {  
               if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&  
                   (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG  | (dryrun ? F_NO_RR : 0)))))  
                 {  
                   if (!(crecp->flags & F_DNSSECOK))  
                     sec_data = 0;  
                     
                   ans = 1;  
                   if (!dryrun)  
                     {  
                       log_query(crecp->flags, name, NULL, record_source(crecp->uid));  
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,   
                                               crec_ttl(crecp, now), &nameoffset,  
                                               T_CNAME, C_IN, "d", cache_get_cname_target(crecp)))  
                         anscount++;  
                     }  
                 }  
             }  
   
           if (qtype == T_MX || qtype == T_ANY)            if (qtype == T_MX || qtype == T_ANY)
             {              {
               int found = 0;                int found = 0;
               for (rec = daemon->mxnames; rec; rec = rec->next)                for (rec = daemon->mxnames; rec; rec = rec->next)
                 if (!rec->issrv && hostname_isequal(name, rec->name))                  if (!rec->issrv && hostname_isequal(name, rec->name))
                   {                    {
                  ans = found = 1;                    ans = found = 1;
                  if (!dryrun)                    sec_data = 0;
                    {                    if (!dryrun)
                      int offset;                      {
                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");                        int offset;
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
                                              &offset, T_MX, C_IN, "sd", rec->weight, rec->target))                        if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
                        {                                                &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
                          anscount++;                          {
                          if (rec->target)                            anscount++;
                            rec->offset = offset;                            if (rec->target)
                        }                              rec->offset = offset;
                    }                          }
                       }
                   }                    }
                               
              if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&               if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
                   cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))                    cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
                 {                   { 
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, 
                                               T_MX, C_IN, "sd", 1,                                                 T_MX, C_IN, "sd", 1, 
                                               option_bool(OPT_SELFMX) ? name : daemon->mxtarget))                                                option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
Line 2155  size_t answer_request(struct dns_header *header, char  Line 1959  size_t answer_request(struct dns_header *header, char 
                 if (rec->issrv && hostname_isequal(name, rec->name))                  if (rec->issrv && hostname_isequal(name, rec->name))
                   {                    {
                     found = ans = 1;                      found = ans = 1;
                       sec_data = 0;
                     if (!dryrun)                      if (!dryrun)
                       {                        {
                         int offset;                          int offset;
                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,                           if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
                                                 &offset, T_SRV, C_IN, "sssd",                                                   &offset, T_SRV, C_IN, "sssd", 
                                                 rec->priority, rec->weight, rec->srvport, rec->target))                                                  rec->priority, rec->weight, rec->srvport, rec->target))
Line 2187  size_t answer_request(struct dns_header *header, char  Line 1992  size_t answer_request(struct dns_header *header, char 
                   *up = move;                    *up = move;
                   move->next = NULL;                    move->next = NULL;
                 }                  }
   
                 if (!found)
                   {
                     if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) &&
                         rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
                       do
                         {
                           int stale_flag = 0;
                           
                           if (crec_isstale(crecp, now))
                             {
                               if (stale)
                                 *stale = 1;
                               
                               stale_flag = F_STALE;
                             }
                           /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */
                           if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN)))
                             break;
                           
                           if (!(crecp->flags & F_DNSSECOK))
                             sec_data = 0;
                           
                           auth = 0;
                           found = ans = 1;
                           
                           if (crecp->flags & F_NEG)
                             {
                               if (crecp->flags & F_NXDOMAIN)
                                 nxdomain = 1;
                               if (!dryrun)
                                 log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
                             }
                           else if (!dryrun)
                             {
                               char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
                               log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
                               
                               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                       crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",
                                                       crecp->addr.srv.priority, crecp->addr.srv.weight, crecp->addr.srv.srvport,
                                                       target))
                                 anscount++;
                             }
                         } while ((crecp = cache_find_by_name(crecp, name, now, F_SRV)));
                       }
                               
               if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))                if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
                 {                  {
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                    log_query(F_CONFIG | F_NEG, name, NULL, NULL);                    log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0);
                 }                  }
             }              }
   
Line 2203  size_t answer_request(struct dns_header *header, char  Line 2055  size_t answer_request(struct dns_header *header, char 
                 if (hostname_isequal(name, na->name))                  if (hostname_isequal(name, na->name))
                   {                    {
                     ans = 1;                      ans = 1;
                       sec_data = 0;
                     if (!dryrun)                      if (!dryrun)
                       {                        {
                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,                           if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
                                                 NULL, T_NAPTR, C_IN, "sszzzd",                                                   NULL, T_NAPTR, C_IN, "sszzzd", 
                                                 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))                                                  na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
Line 2215  size_t answer_request(struct dns_header *header, char  Line 2068  size_t answer_request(struct dns_header *header, char 
             }              }
                       
           if (qtype == T_MAILB)            if (qtype == T_MAILB)
            ans = 1, nxdomain = 1;            ans = 1, nxdomain = 1, sec_data = 0;
   
           if (qtype == T_SOA && option_bool(OPT_FILTER))            if (qtype == T_SOA && option_bool(OPT_FILTER))
             {              {
              ans = 1;               ans = 1;
               sec_data = 0;
               if (!dryrun)                if (!dryrun)
                log_query(F_CONFIG | F_NEG, name, &addr, NULL);                log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0);
             }              }
         }          }
   
       if (!ans)        if (!ans)
        return 0; /* failed to answer a question */        {
           /* We may know that the domain doesn't exist for any RRtype. */
           if ((crecp = cache_find_by_name(NULL, name, now, F_NXDOMAIN)))
             {
               ans = nxdomain = 1;
               auth = 0;
 
               if (!(crecp->flags & F_DNSSECOK)) 
                 sec_data = 0;
               
               if (!dryrun)
                 log_query(F_NXDOMAIN | F_NEG, name, NULL, NULL, 0);
             }
           else
             return 0; /* failed to answer a question */
         }
     }      }
       
   if (dryrun)    if (dryrun)
Line 2248  size_t answer_request(struct dns_header *header, char  Line 2117  size_t answer_request(struct dns_header *header, char 
         crecp = NULL;          crecp = NULL;
         while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))          while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
           {            {
 #ifdef HAVE_IPV6  
             int type =  crecp->flags & F_IPV4 ? T_A : T_AAAA;              int type =  crecp->flags & F_IPV4 ? T_A : T_AAAA;
#else
            int type = T_A; 
#endif 
             if (crecp->flags & F_NEG)              if (crecp->flags & F_NEG)
               continue;                continue;
   
Line 2269  size_t answer_request(struct dns_header *header, char  Line 2135  size_t answer_request(struct dns_header *header, char 
   /* set RA flag */    /* set RA flag */
   header->hb4 |= HB4_RA;    header->hb4 |= HB4_RA;
         
  /* authoritive - only hosts and DHCP derived names. */  /* authoritative - only hosts and DHCP derived names. */
   if (auth)    if (auth)
     header->hb3 |= HB3_AA;      header->hb3 |= HB3_AA;
       
Line 2279  size_t answer_request(struct dns_header *header, char  Line 2145  size_t answer_request(struct dns_header *header, char 
       
   if (nxdomain)    if (nxdomain)
     SET_RCODE(header, NXDOMAIN);      SET_RCODE(header, NXDOMAIN);
     else if (notimp)
       SET_RCODE(header, NOTIMP);
   else    else
     SET_RCODE(header, NOERROR); /* no error */      SET_RCODE(header, NOERROR); /* no error */
   header->ancount = htons(anscount);    header->ancount = htons(anscount);
Line 2287  size_t answer_request(struct dns_header *header, char  Line 2155  size_t answer_request(struct dns_header *header, char 
   
   len = ansp - (unsigned char *)header;    len = ansp - (unsigned char *)header;
       
     /* Advertise our packet size limit in our reply */
   if (have_pseudoheader)    if (have_pseudoheader)
    len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);    len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
       
  if (*ad_reqd && sec_data)  if (ad_reqd && sec_data)
     header->hb4 |= HB4_AD;      header->hb4 |= HB4_AD;
   else    else
     header->hb4 &= ~HB4_AD;      header->hb4 &= ~HB4_AD;
       
   return len;    return len;
 }  }
   

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>