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

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.2, 2014/06/15 16:31:38
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2014 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 16 Line 16
   
 #include "dnsmasq.h"  #include "dnsmasq.h"
   
   
 #define CHECK_LEN(header, pp, plen, len) \  
     ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))  
   
 #define ADD_RDLEN(header, pp, plen, len) \  
   (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))  
   
 int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,   int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 
                  char *name, int isExtract, int extrabytes)                   char *name, int isExtract, int extrabytes)
 {  {
Line 274  int in_arpa_name_2_addr(char *namein, struct all_addr  Line 267  int in_arpa_name_2_addr(char *namein, struct all_addr 
   return 0;    return 0;
 }  }
   
static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
 {  {
   while(1)    while(1)
     {      {
Line 344  unsigned char *skip_questions(struct dns_header *heade Line 337  unsigned char *skip_questions(struct dns_header *heade
   return ansp;    return ansp;
 }  }
   
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
 {  {
   int i, rdlen;    int i, rdlen;
       
Line 367  static unsigned char *skip_section(unsigned char *ansp Line 360  static unsigned char *skip_section(unsigned char *ansp
    than CRC the raw bytes, since replies might be compressed differently.      than CRC the raw bytes, since replies might be compressed differently. 
    We ignore case in the names for the same reason. Return all-ones     We ignore case in the names for the same reason. Return all-ones
    if there is not question section. */     if there is not question section. */
   #ifndef HAVE_DNSSEC
 unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)  unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
 {  {
   int q;    int q;
Line 407  unsigned int questions_crc(struct dns_header *header,  Line 401  unsigned int questions_crc(struct dns_header *header, 
   
   return crc;    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 500  unsigned char *find_pseudoheader(struct dns_header *he Line 494  unsigned char *find_pseudoheader(struct dns_header *he
       else if (is_sign &&         else if (is_sign && 
                i == arcount - 1 &&                  i == arcount - 1 && 
                class == C_ANY &&                  class == C_ANY && 
               (type == T_SIG || type == T_TSIG))               type == T_TSIG)
         *is_sign = 1;          *is_sign = 1;
     }      }
       
Line 513  struct macparm { Line 507  struct macparm {
   size_t plen;    size_t plen;
   union mysockaddr *l3;    union mysockaddr *l3;
 };  };
 
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)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)
  struct macparm *parm = parmv;
  int match = 0; 
  unsigned short rdlen; 
  struct dns_header *header = parm->header; 
   unsigned char *lenp, *datap, *p;    unsigned char *lenp, *datap, *p;
     int rdlen, is_sign;
       
  if (family == parm->l3->sa.sa_family)  if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
     {      {
      if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)      if (is_sign)
        match = 1;        return plen;
#ifdef HAVE_IPV6
      else 
        if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0) 
          match = 1; 
#endif 
    } 
  
  if (!match) 
    return 1; /* continue */ 
   
  if (ntohs(header->arcount) == 0) 
    { 
       /* We are adding the pseudoheader */        /* We are adding the pseudoheader */
      if (!(p = skip_questions(header, parm->plen)) ||      if (!(p = skip_questions(header, plen)) ||
           !(p = skip_section(p,             !(p = skip_section(p, 
                             ntohs(header->ancount) + ntohs(header->nscount)                             ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount)
                             header, parm->plen)))                             header, plen)))
        return 0;        return plen;
       *p++ = 0; /* empty name */        *p++ = 0; /* empty name */
       PUTSHORT(T_OPT, p);        PUTSHORT(T_OPT, p);
      PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */      PUTSHORT(daemon->edns_pktsz, p); /* max packet length */
      PUTLONG(0, p);    /* extended RCODE */      PUTSHORT(0, p);    /* extended RCODE and version */
       PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
       lenp = p;        lenp = p;
       PUTSHORT(0, p);    /* RDLEN */        PUTSHORT(0, p);    /* RDLEN */
       rdlen = 0;        rdlen = 0;
      if (((ssize_t)maclen) > (parm->limit - (p + 4)))      if (((ssize_t)optlen) > (limit - (p + 4)))
        return 0; /* Too big */        return plen; /* Too big */
      header->arcount = htons(1);      header->arcount = htons(ntohs(header->arcount) + 1);
       datap = p;        datap = p;
     }      }
   else    else
     {      {
      int i, is_sign;      int i;
      unsigned short code, len;      unsigned short code, len, flags;
               
         /* Must be at the end, if exists */
       if (ntohs(header->arcount) != 1 ||        if (ntohs(header->arcount) != 1 ||
           !(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) ||  
           is_sign ||            is_sign ||
          (!(p = skip_name(p, header, parm->plen, 10))))          (!(p = skip_name(p, header, plen, 10))))
        return 0;        return plen;
               
      p += 8; /* skip UDP length and RCODE */      p += 6; /* skip UDP length and RCODE */
            GETSHORT(flags, p);
       if (set_do)
         {
           p -=2;
           PUTSHORT(flags | 0x8000, p);
         }
 
       lenp = p;        lenp = p;
       GETSHORT(rdlen, p);        GETSHORT(rdlen, p);
      if (!CHECK_LEN(header, p, parm->plen, rdlen))      if (!CHECK_LEN(header, p, plen, rdlen))
        return 0; /* bad packet */        return plen; /* bad packet */
       datap = p;        datap = p;
   
          /* no option to add */
         if (optno == 0)
           return plen;
             
       /* check if option already there */        /* check if option already there */
       for (i = 0; i + 4 < rdlen; i += len + 4)        for (i = 0; i + 4 < rdlen; i += len + 4)
         {          {
           GETSHORT(code, p);            GETSHORT(code, p);
           GETSHORT(len, p);            GETSHORT(len, p);
          if (code == EDNS0_OPTION_MAC)          if (code == optno)
            return 0;            return plen;
           p += len;            p += len;
         }          }
               
      if (((ssize_t)maclen) > (parm->limit - (p + 4)))      if (((ssize_t)optlen) > (limit - (p + 4)))
        return 0; /* Too big */        return plen; /* Too big */
     }      }
       
  PUTSHORT(EDNS0_OPTION_MAC, p);  if (optno != 0)
  PUTSHORT(maclen, p);    {
  memcpy(p, mac, maclen);      PUTSHORT(optno, p);
  p += maclen;        PUTSHORT(optlen, p);
       memcpy(p, opt, optlen);
       p += optlen;  
     }
   
   PUTSHORT(p - datap, lenp);    PUTSHORT(p - datap, lenp);
  parm->plen = p - (unsigned char *)header;  return p - (unsigned char *)header;
       
   }
   
   static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
   {
     struct macparm *parm = parmv;
     int match = 0;
       
     if (family == parm->l3->sa.sa_family)
       {
         if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
           match = 1;
   #ifdef HAVE_IPV6
         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 */    return 0; /* done */
 }               }             
             
   
 size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)  size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
 {  {
   struct macparm parm;    struct macparm parm;
Line 621  size_t add_mac(struct dns_header *header, size_t plen, Line 638  size_t add_mac(struct dns_header *header, size_t plen,
   return parm.plen;     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? */   /* is addr in the non-globally-routed IP space? */ 
static int private_net(struct in_addr addr, int ban_localhost) int private_net(struct in_addr addr, int ban_localhost) 
 {  {
   in_addr_t ip_addr = ntohl(addr.s_addr);    in_addr_t ip_addr = ntohl(addr.s_addr);
   
Line 635  static int private_net(struct in_addr addr, int ban_lo Line 754  static int private_net(struct in_addr addr, int ban_lo
     ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;      ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;
 }  }
   
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
 {  {
   int i, qtype, qclass, rdlen;    int i, qtype, qclass, rdlen;
   
Line 680  static unsigned char *do_doctor(unsigned char *p, int  Line 799  static unsigned char *do_doctor(unsigned char *p, int 
               addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);                addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
               /* Since we munged the data, the server it came from is no longer authoritative */                /* Since we munged the data, the server it came from is no longer authoritative */
               header->hb3 &= ~HB3_AA;                header->hb3 &= ~HB3_AA;
                 *doctored = 1;
               memcpy(p, &addr, INADDRSZ);                memcpy(p, &addr, INADDRSZ);
               break;                break;
             }              }
Line 718  static unsigned char *do_doctor(unsigned char *p, int  Line 838  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)static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
 {  {
   unsigned char *p;    unsigned char *p;
   int qtype, qclass, rdlen;    int qtype, qclass, rdlen;
Line 727  static int find_soa(struct dns_header *header, size_t  Line 847  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)))      !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, 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 762  static int find_soa(struct dns_header *header, size_t  Line 882  static int find_soa(struct dns_header *header, size_t 
         return 0; /* bad packet */          return 0; /* bad packet */
     }      }
       
  /* rewrite addresses in additioal section too */  /* rewrite addresses in additional section too */
  if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))  if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
     return 0;      return 0;
       
   if (!found_soa)    if (!found_soa)
Line 777  static int find_soa(struct dns_header *header, size_t  Line 897  static int find_soa(struct dns_header *header, size_t 
    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. */
 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 checking_disabled)                      char **ipsets, 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 i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
Line 792  int extract_addresses(struct dns_header *header, size_ Line 912  int extract_addresses(struct dns_header *header, size_
   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 and logging side-effects, so don't call it lazily if there are any. */
  if (daemon->doctors || option_bool(OPT_LOG))  if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
     {      {
       searched_soa = 1;        searched_soa = 1;
      ttl = find_soa(header, qlen, name);      ttl = find_soa(header, qlen, name, doctored);
 #ifdef HAVE_DNSSEC
       if (*doctored && secure)
         return 0;
 #endif
     }      }
       
   /* go through the questions. */    /* go through the questions. */
Line 803  int extract_addresses(struct dns_header *header, size_ Line 927  int extract_addresses(struct dns_header *header, size_
       
   for (i = ntohs(header->qdcount); i != 0; i--)    for (i = ntohs(header->qdcount); i != 0; i--)
     {      {
      int found = 0, cname_count = 5;      int found = 0, cname_count = CNAME_CHAIN;
       struct crec *cpp = NULL;        struct crec *cpp = NULL;
       int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;        int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
         int secflag = secure ?  F_DNSSECOK : 0;
       unsigned long cttl = ULONG_MAX, attl;        unsigned long cttl = ULONG_MAX, attl;
      
       namep = p;        namep = p;
       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 863  int extract_addresses(struct dns_header *header, size_ Line 988  int extract_addresses(struct dns_header *header, size_
                                               
                       if (aqtype == T_CNAME)                        if (aqtype == T_CNAME)
                         {                          {
                          if (!cname_count--)                          if (!cname_count-- || secure)
                            return 0; /* looped CNAMES */                            return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
                           goto cname_loop;                            goto cname_loop;
                         }                          }
                                               
                      cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);                      cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE);
                       found = 1;                         found = 1; 
                     }                      }
                                       
Line 883  int extract_addresses(struct dns_header *header, size_ Line 1008  int extract_addresses(struct dns_header *header, size_
               if (!searched_soa)                if (!searched_soa)
                 {                  {
                   searched_soa = 1;                    searched_soa = 1;
                  ttl = find_soa(header, qlen, NULL);                  ttl = find_soa(header, qlen, NULL, doctored);
                 }                  }
               if (ttl)                if (ttl)
                cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);                      cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);     
             }              }
         }          }
       else        else
Line 910  int extract_addresses(struct dns_header *header, size_ Line 1035  int extract_addresses(struct dns_header *header, size_
           else             else 
             continue;              continue;
                           
          if (!(flags & F_NXDOMAIN))        cname_loop1:
           if (!(p1 = skip_questions(header, qlen)))
             return 0;
           
           for (j = ntohs(header->ancount); j != 0; j--) 
             {              {
            cname_loop1:              if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
              if (!(p1 = skip_questions(header, qlen)))                return 0; /* bad packet */
                return 0; 
                               
              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)
                 {                  {
                  if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))                  (p1) -= 4;
                    return 0; /* bad packet */                  PUTLONG(daemon->max_ttl, p1);
                                  }
                  GETSHORT(aqtype, p1);               GETSHORT(ardlen, p1);
                  GETSHORT(aqclass, p1);              endrr = p1+ardlen;
                  GETLONG(attl, p1);              
                  if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)              if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
                 {
                   if (aqtype == T_CNAME)
                     {                      {
                      (p1) -= 4;                      if (!cname_count--)
                      PUTLONG(daemon->max_ttl, p1);                        return 0; /* looped CNAMES */
                    }                      newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);
                  GETSHORT(ardlen, p1);                      if (newc)
                  endrr = p1+ardlen; 
                   
                  if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) 
                    { 
                      if (aqtype == T_CNAME) 
                         {                          {
                          if (!cname_count--)                          newc->addr.cname.target.cache = NULL;
                            return 0; /* looped CNAMES */                          /* anything other than zero, to avoid being mistaken for CNAME to interface-name */ 
                          newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);                          newc->addr.cname.uid = 1
                          if (newc)                          if (cpp)
                             {                              {
                              newc->addr.cname.cache = NULL;                              cpp->addr.cname.target.cache = newc;
                              if (cpp)                              cpp->addr.cname.uid = newc->uid;
                                { 
                                  cpp->addr.cname.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                      
                        {                      cpp = newc;
                          found = 1;                      if (attl < cttl)
                                                  cttl = attl;
                          /* copy address into aligned storage */                      
                          if (!CHECK_LEN(header, p1, qlen, addrlen))                      if (!extract_name(header, qlen, &p1, name, 1, 0))
                            return 0; /* bad packet */                        return 0;
                          memcpy(&addr, p1, addrlen);                      goto cname_loop1;
                                              }
                          /* check for returned address in private space */                  else if (!(flags & F_NXDOMAIN))
                          if (check_rebind &&                    {
                              (flags & F_IPV4) &&                      found = 1;
                              private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))                      
                            return 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  #ifdef HAVE_IPSET
                          if (ipsets && (flags & (F_IPV4 | F_IPV6)))                      if (ipsets && (flags & (F_IPV4 | F_IPV6)))
                         {
                           ipsets_cur = ipsets;
                           while (*ipsets_cur)
                             {                              {
                              ipsets_cur = ipsets;                              log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
                              while (*ipsets_cur)                              add_to_ipset(*ipsets_cur++, &addr, flags, 0);
                                add_to_ipset(*ipsets_cur++, &addr, flags, 0); 
                             }                              }
                           }
 #endif  #endif
                                                
                          newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);                      newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
                          if (newc && cpp)                      if (newc && cpp)
                            {                        {
                              cpp->addr.cname.cache = newc;                          cpp->addr.cname.target.cache = newc;
                              cpp->addr.cname.uid = newc->uid;                          cpp->addr.cname.uid = newc->uid;
                            } 
                          cpp = NULL; 
                         }                          }
                         cpp = NULL;
                     }                      }
                     
                   p1 = endrr;  
                   if (!CHECK_LEN(header, p1, qlen, 0))  
                     return 0; /* bad packet */  
                 }                  }
                 
                 p1 = endrr;
                 if (!CHECK_LEN(header, p1, qlen, 0))
                   return 0; /* bad packet */
             }              }
                       
           if (!found && !option_bool(OPT_NO_NEG))            if (!found && !option_bool(OPT_NO_NEG))
Line 1002  int extract_addresses(struct dns_header *header, size_ Line 1129  int extract_addresses(struct dns_header *header, size_
               if (!searched_soa)                if (!searched_soa)
                 {                  {
                   searched_soa = 1;                    searched_soa = 1;
                  ttl = find_soa(header, qlen, NULL);                  ttl = find_soa(header, qlen, NULL, doctored);
                 }                  }
               /* If there's no SOA to get the TTL from, but there is a CNAME                 /* If there's no SOA to get the TTL from, but there is a CNAME 
                  pointing at this, inherit its TTL */                   pointing at this, inherit its TTL */
               if (ttl || cpp)                if (ttl || cpp)
                 {                  {
                  newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);                        newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);     
                   if (newc && cpp)                    if (newc && cpp)
                     {                      {
                      cpp->addr.cname.cache = newc;                      cpp->addr.cname.target.cache = newc;
                       cpp->addr.cname.uid = newc->uid;                        cpp->addr.cname.uid = newc->uid;
                     }                      }
                 }                  }
Line 1020  int extract_addresses(struct dns_header *header, size_ Line 1147  int extract_addresses(struct dns_header *header, size_
     }      }
       
   /* Don't put stuff from a truncated packet into the cache.    /* Don't put stuff from a truncated packet into the cache.
      Don't cache replies where DNSSEC validation was turned off, either  
      the upstream server told us so, or the original query specified it.   
      Don't cache replies from non-recursive nameservers, since we may get a        Don't cache replies from non-recursive nameservers, since we may get a 
      reply containing a CNAME but not its target, even though the target        reply containing a CNAME but not its target, even though the target 
      does exist. */       does exist. */
   if (!(header->hb3 & HB3_TC) &&     if (!(header->hb3 & HB3_TC) && 
       !(header->hb4 & HB4_CD) &&        !(header->hb4 & HB4_CD) &&
       (header->hb4 & HB4_RA) &&        (header->hb4 & HB4_RA) &&
      !checking_disabled)      !no_cache_dnssec)
     cache_end_insert();      cache_end_insert();
   
   return 0;    return 0;
Line 1036  int extract_addresses(struct dns_header *header, size_ Line 1161  int extract_addresses(struct dns_header *header, size_
   
 /* 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)
 {  {
   unsigned char *p = (unsigned char *)(header+1);    unsigned char *p = (unsigned char *)(header+1);
Line 1122  int check_for_local_domain(char *name, time_t now) Line 1246  int check_for_local_domain(char *name, time_t now)
   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)) &&  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)))      (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
     return 1;      return 1;
       
   for (naptr = daemon->naptr; naptr; naptr = naptr->next)    for (naptr = daemon->naptr; naptr; naptr = naptr->next)
Line 1185  int check_for_bogus_wildcard(struct dns_header *header Line 1309  int check_for_bogus_wildcard(struct dns_header *header
                 /* Found a bogus address. Insert that info here, since there no SOA record                  /* Found a bogus address. Insert that info here, since there no SOA record
                    to get the ttl from in the normal processing */                     to get the ttl from in the normal processing */
                 cache_start_insert();                  cache_start_insert();
                cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);                cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
                 cache_end_insert();                  cache_end_insert();
                                   
                 return 1;                  return 1;
Line 1255  int add_resource_record(struct dns_header *header, cha Line 1379  int add_resource_record(struct dns_header *header, cha
         p += INADDRSZ;          p += INADDRSZ;
         break;          break;
                   
         case 'b':
           usval = va_arg(ap, int);
           *p++ = usval;
           break;
           
       case 's':        case 's':
         usval = va_arg(ap, int);          usval = va_arg(ap, int);
         PUTSHORT(usval, p);          PUTSHORT(usval, p);
Line 1327  static unsigned long crec_ttl(struct crec *crecp, time Line 1456  static unsigned long crec_ttl(struct crec *crecp, time
   
 /* 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, time_t now                      struct in_addr local_addr, struct in_addr local_netmask, 
                       time_t now, int *ad_reqd, int *do_bit) 
 {  {
   char *name = daemon->namebuff;    char *name = daemon->namebuff;
   unsigned char *p, *ansp, *pheader;    unsigned char *p, *ansp, *pheader;
  int qtype, qclass;  unsigned int qtype, qclass;
   struct all_addr addr;    struct 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;  int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
   int is_sign;    int is_sign;
   struct crec *crecp;    struct crec *crecp;
  int nxdomain = 0, auth = 1, trunc = 0;  int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
   struct mx_srv_record *rec;    struct mx_srv_record *rec;
     size_t len;
     
     /* Don't return AD set if checking disabled. */
     if (header->hb4 & HB4_CD)
       sec_data = 0;
     
     /* RFC 6840 5.7 */
     *ad_reqd = header->hb4 & HB4_AD;
     *do_bit = 0;
   
   /* If there is an RFC2671 pseudoheader then it will be overwritten by    /* 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. We check to see if the do bit is set, if so we always
      forward rather than answering from the cache, which doesn't include       forward rather than answering from the cache, which doesn't include
     security information. */     security information, unless we're in DNSSEC validation mode. */
   
   if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))    if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
     {       { 
       unsigned short udpsz, flags;        unsigned short udpsz, flags;
       unsigned char *psave = pheader;        unsigned char *psave = pheader;
   
         have_pseudoheader = 1;
   
       GETSHORT(udpsz, pheader);        GETSHORT(udpsz, pheader);
       pheader += 2; /* ext_rcode */        pheader += 2; /* ext_rcode */
       GETSHORT(flags, pheader);        GETSHORT(flags, pheader);
               
      sec_reqd = flags & 0x8000; /* do bit */       if ((sec_reqd = flags & 0x8000))
         *do_bit = 1;/* do bit */ 
       *ad_reqd = 1;
   
       /* If our client is advertising a larger UDP packet size        /* If our client is advertising a larger UDP packet size
          than we allow, trim it so that we don't get an overlarge           than we allow, trim it so that we don't get an overlarge
Line 1407  size_t answer_request(struct dns_header *header, char  Line 1550  size_t answer_request(struct dns_header *header, char 
                   ans = 1;                    ans = 1;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                         unsigned long ttl = daemon->local_ttl;
                         int ok = 1;
                       log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                       /* Dynamically generate stat record */
                                              daemon->local_ttl, NULL,                      if (t->stat != 0)
                                              T_TXT, t->class, "t", t->len, t->txt))                        {
                           ttl = 0;
                           if (!cache_make_stat(t))
                             ok = 0;
                         }
                       
                       if (ok && add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                     ttl, NULL,
                                                     T_TXT, t->class, "t", t->len, t->txt))
                         anscount++;                          anscount++;
   
                     }                      }
Line 1418  size_t answer_request(struct dns_header *header, char  Line 1571  size_t answer_request(struct dns_header *header, char 
             }              }
         }          }
   
   #ifdef HAVE_DNSSEC
         if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
           {
             int gotone = 0;
             struct blockdata *keydata;
   
             /* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
             if (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)
                     break;
               }
             
             if (!sec_reqd || crecp)
               {
                 if (qtype == T_DS)
                   {
                     crecp = NULL;
                     while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
                       if (crecp->uid == qclass)
                         {
                           gotone = 1; 
                           if (!dryrun)
                             {
                               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 1450  size_t answer_request(struct dns_header *header, char  Line 1695  size_t answer_request(struct dns_header *header, char 
               if (is_arpa == F_IPV4)                if (is_arpa == F_IPV4)
                 for (intr = daemon->int_names; intr; intr = intr->next)                  for (intr = daemon->int_names; intr; intr = intr->next)
                   {                    {
                    if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)                    struct addrlist *addrlist;
                     
                     for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                       if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
                         break;
                     
                     if (addrlist)
                       break;                        break;
                     else                      else
                       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)
                   for (intr = daemon->int_names; intr; intr = intr->next)
                     {
                       struct addrlist *addrlist;
                       
                       for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                         if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
                           break;
                       
                       if (addrlist)
                         break;
                       else
                         while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
                           intr = intr->next;
                     }
   #endif
                               
               if (intr)                if (intr)
                 {                  {
                   ans = 1;                    ans = 1;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                      log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);                      log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
                       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 1485  size_t answer_request(struct dns_header *header, char  Line 1753  size_t answer_request(struct dns_header *header, char 
                     }                      }
                 }                  }
               else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))                else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
                do                 {
                  {                   if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
                    /* 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 (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
                      continue;                        crecp = NULL;
                    #ifdef HAVE_DNSSEC
                    if (crecp->flags & F_NEG)                      else if (crecp->flags & F_DNSSECOK)
                      {                        {
                        ans = 1;                          int gotsig = 0;
                        auth = 0;                          struct crec *rr_crec = NULL;
                        if (crecp->flags & F_NXDOMAIN)
                          nxdomain = 1;                          while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
                        if (!dryrun)                            {
                          log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);                              if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
                      }                                {
                    else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)                                  char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
                      {                                  gotsig = 1;
                        ans = 1;                                  
                        if (!(crecp->flags & (F_HOSTS | F_DHCP)))                                  if (!dryrun && 
                          auth = 0;                                      add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                        if (!dryrun)                                                          rr_crec->ttd - now, &nameoffset,
                          {                                                          T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
                            log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,                                     anscount++;
                                      record_source(crecp->uid));                                }
                                                        } 
                            if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                           
                                                    crec_ttl(crecp, now), NULL,                          if (!gotsig)
                                                    T_PTR, C_IN, "d", cache_get_name(crecp)))                            crecp = NULL;
                         }
 #endif
                     }
 
                   if (crecp)
                     {
                       do 
                         { 
                           /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
                           if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
                             continue;
                           
                           if (!(crecp->flags & F_DNSSECOK))
                             sec_data = 0;
                           
                           if (crecp->flags & F_NEG)
                             {
                               ans = 1;
                               auth = 0;
                               if (crecp->flags & F_NXDOMAIN)
                                 nxdomain = 1;
                               if (!dryrun)
                                 log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
                             }
                           else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
                             {
                               ans = 1;
                               if (!(crecp->flags & (F_HOSTS | F_DHCP)))
                                 auth = 0;
                               if (!dryrun)
                                 {
                                   log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, 
                                             record_source(crecp->uid));
                                   
                                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                           crec_ttl(crecp, now), NULL,
                                                           T_PTR, C_IN, "d", cache_get_name(crecp)))
                                     anscount++;
                                 }
                             }
                         } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
                     }
                 }
               else if (is_rev_synth(is_arpa, &addr, name))
                 {
                   ans = 1;
                   if (!dryrun)
                     {
                       log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL); 
                       
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                               daemon->local_ttl, NULL,
                                               T_PTR, C_IN, "d", name))
                               anscount++;                                anscount++;
                          }                    }
                      }                }
                  } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); 
               else if (is_arpa == F_IPV4 &&                 else if (is_arpa == F_IPV4 && 
                        option_bool(OPT_BOGUSPRIV) &&                          option_bool(OPT_BOGUSPRIV) && 
                        private_net(addr.addr.addr4, 1))                         private_net(addr.addr.addr4, 1))
Line 1533  size_t answer_request(struct dns_header *header, char  Line 1853  size_t answer_request(struct dns_header *header, char 
           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 = T_A;
                            struct interface_name *intr;
 
               if (flag == F_IPV6)                if (flag == F_IPV6)
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
                 type = T_AAAA;                  type = T_AAAA;
Line 1582  size_t answer_request(struct dns_header *header, char  Line 1903  size_t answer_request(struct dns_header *header, char 
                 }                  }
   
               /* interface name stuff */                /* interface name stuff */
              if (qtype == T_A)            intname_restart:
               for (intr = daemon->int_names; intr; intr = intr->next)
                 if (hostname_isequal(name, intr->name))
                   break;
               
               if (intr)
                 {                  {
                  struct interface_name *intr;                  struct addrlist *addrlist;
                   int gotit = 0;
   
                     enumerate_interfaces(0);
                     
                   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;                      {
                         ans = 1;
                         if (!dryrun)
                           {
                             
                             for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
 #ifdef HAVE_IPV6
                               if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
 #endif
                                 {
                                   gotit = 1;
                                   log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
                                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                           daemon->local_ttl, NULL, type, C_IN, 
                                                           type == T_A ? "4" : "6", &addrlist->addr))
                                     anscount++;
                                 }
                           }
                       }
                                       
                  if (intr)                  if (!dryrun && !gotit)
                    {                    log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL);
                      ans = 1;                     
                      if (!dryrun)                  continue;
                        { 
                          if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1) 
                            log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL); 
                          else 
                            { 
                              log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL); 
                              if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,  
                                                      daemon->local_ttl, NULL, type, C_IN, "4", &addr)) 
                                anscount++; 
                            } 
                        } 
                      continue; 
                    } 
                 }                  }
   
             cname_restart:              cname_restart:
              if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))              if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME | (dryrun ? F_NO_RR : 0))))
                 {                  {
                   int localise = 0;                    int localise = 0;
                                       
Line 1629  size_t answer_request(struct dns_header *header, char  Line 1963  size_t answer_request(struct dns_header *header, char 
                         } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));                          } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
                       crecp = save;                        crecp = save;
                     }                      }
                          
                  do                  /* If the client asked for DNSSEC and we can't provide RRSIGs, either
                    {                      because we've not doing DNSSEC or the cached answer is signed by negative,
                      /* don't answer wildcard queries with data not from /etc/hosts                     don't answer from the cache, forward instead. */
                         or DHCP leases */                  if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
                      if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))                    {
                        break;                      if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
                                              crecp = NULL;
                      if (crecp->flags & F_CNAME)#ifdef HAVE_DNSSEC
                       else if (crecp->flags & F_DNSSECOK)
                         {                          {
                          if (!dryrun)                          /* We're returning validated data, need to return the RRSIG too. */
                            {                          struct crec *rr_crec = NULL;
                              log_query(crecp->flags, name, NULL, record_source(crecp->uid));                          int sigtype = type;
                              if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                           /* The signature may have expired even though the data is still in cache
                                                      crec_ttl(crecp, now), &nameoffset,                             forward instead of answering from cache if so. */
                                                      T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))                          int gotsig = 0;
                                anscount++; 
                            } 
                                                       
                          strcpy(name, cache_get_name(crecp->addr.cname.cache));                          if (crecp->flags & F_CNAME)
                          goto cname_restart;                            sigtype = T_CNAME;
                        } 
                       
                      if (crecp->flags & F_NEG) 
                        { 
                          ans = 1; 
                          auth = 0; 
                          if (crecp->flags & F_NXDOMAIN) 
                            nxdomain = 1; 
                          if (!dryrun) 
                            log_query(crecp->flags, name, NULL, NULL); 
                        } 
                      else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) 
                        { 
                          /* If we are returning local answers depending on network, 
                             filter here. */ 
                          if (localise &&  
                              (crecp->flags & F_HOSTS) && 
                              !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) 
                            continue; 
        
                          if (!(crecp->flags & (F_HOSTS | F_DHCP))) 
                            auth = 0; 
                                                       
                          ans = 1;                          while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
                          if (!dryrun) 
                             {                              {
                              log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,                              if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
                                        record_source(crecp->uid));                                {
                                                                char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
                              if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                                   gotsig = 1;
                                                      crec_ttl(crecp, now), NULL, type, C_IN,                                   
                                                      type == T_A ? "4" : "6", &crecp->addr))                                  if (!dryrun && 
                                anscount++;                                      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;
                         }                          }
                    } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));#endif
                     }            
 
                   if (crecp)
                     do
                       { 
                         /* don't answer wildcard queries with data not from /etc/hosts
                            or DHCP leases */
                         if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                           break;
                         
                         if (!(crecp->flags & F_DNSSECOK))
                           sec_data = 0;
                         
                         if (crecp->flags & F_CNAME)
                           {
                             char *cname_target = cache_get_cname_target(crecp);
                             
                             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", 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 
                           {
                             /* If we are returning local answers depending on network,
                                filter here. */
                             if (localise && 
                                 (crecp->flags & F_HOSTS) &&
                                 !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
                               continue;
                             
                             if (!(crecp->flags & (F_HOSTS | F_DHCP)))
                               auth = 0;
                             
                             ans = 1;
                             if (!dryrun)
                               {
                                 log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
                                           record_source(crecp->uid));
                                 
                                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                         crec_ttl(crecp, now), NULL, type, C_IN, 
                                                         type == T_A ? "4" : "6", &crecp->addr))
                                   anscount++;
                               }
                           }
                       } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
                 }                  }
                 else if (is_name_synthetic(flag, name, &addr))
                   {
                     ans = 1;
                     if (!dryrun)
                       {
                         log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                 daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
                           anscount++;
                       }
                   }
             }              }
   
           if (qtype == T_CNAME || qtype == T_ANY)            if (qtype == T_CNAME || qtype == T_ANY)
             {              {
               if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&                if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
                  (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP))))                  (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;                    ans = 1;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                       log_query(crecp->flags, name, NULL, record_source(crecp->uid));                        log_query(crecp->flags, name, NULL, record_source(crecp->uid));
                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                               crec_ttl(crecp, now), &nameoffset,                                                crec_ttl(crecp, now), &nameoffset,
                                              T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))                                              T_CNAME, C_IN, "d", cache_get_cname_target(crecp)))
                         anscount++;                          anscount++;
                     }                      }
                 }                  }
             }              }
          
           if (qtype == T_MX || qtype == T_ANY)            if (qtype == T_MX || qtype == T_ANY)
             {              {
               int found = 0;                int found = 0;
Line 1728  size_t answer_request(struct dns_header *header, char  Line 2132  size_t answer_request(struct dns_header *header, char 
                   }                    }
                               
               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))                  cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
                 {                   { 
                   ans = 1;                    ans = 1;
                   if (!dryrun)                    if (!dryrun)
Line 1872  size_t answer_request(struct dns_header *header, char  Line 2276  size_t answer_request(struct dns_header *header, char 
   /* truncation */    /* truncation */
   if (trunc)    if (trunc)
     header->hb3 |= HB3_TC;      header->hb3 |= HB3_TC;
  
  if (anscount == 0 && nxdomain)  if (nxdomain)
     SET_RCODE(header, NXDOMAIN);      SET_RCODE(header, NXDOMAIN);
   else    else
     SET_RCODE(header, NOERROR); /* no error */      SET_RCODE(header, NOERROR); /* no error */
   header->ancount = htons(anscount);    header->ancount = htons(anscount);
   header->nscount = htons(0);    header->nscount = htons(0);
   header->arcount = htons(addncount);    header->arcount = htons(addncount);
  return ansp - (unsigned char *)header;
   len = ansp - (unsigned char *)header;
   
   if (have_pseudoheader)
     len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
   
   if (*ad_reqd && sec_data)
     header->hb4 |= HB4_AD;
   else
     header->hb4 &= ~HB4_AD;
   
   return len;
 }  }
   

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


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