Diff for /embedaddon/dnsmasq/src/rfc1035.c between versions 1.1.1.3 and 1.1.1.4

version 1.1.1.3, 2016/11/02 09:57:01 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley/* dnsmasq is Copyright (c) 2000-2021 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 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 143  int extract_name(struct dns_header *header, size_t ple Line 143  int extract_name(struct dns_header *header, size_t ple
     
 /* 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 153  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 176  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 198  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 206  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 235  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 243  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 335  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 426  int private_net(struct in_addr addr, int ban_localhost Line 375  int private_net(struct in_addr addr, int ban_localhost
     ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF)  /* 255.255.255.255/32 (broadcast)*/ ;      ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF)  /* 255.255.255.255/32 (broadcast)*/ ;
 }  }
   
   static int private_net6(struct in6_addr *a)
   {
     return 
       IN6_IS_ADDR_UNSPECIFIED(a) || /* RFC 6303 4.3 */
       IN6_IS_ADDR_LOOPBACK(a) ||    /* RFC 6303 4.3 */
       IN6_IS_ADDR_LINKLOCAL(a) ||   /* RFC 6303 4.5 */
       ((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, char *name, int *doctored)
 {  {
   int i, qtype, qclass, rdlen;    int i, qtype, qclass, rdlen;
Line 485  static unsigned char *do_doctor(unsigned char *p, int  Line 444  static unsigned char *do_doctor(unsigned char *p, int 
             {              {
               unsigned int i, len = *p1;                unsigned int i, len = *p1;
               unsigned char *p2 = p1;                unsigned char *p2 = p1;
                 if ((p1 + len - p) >= rdlen)
                   return 0; /* bad packet */
               /* make counted string zero-term  and sanitise */                /* make counted string zero-term  and sanitise */
               for (i = 0; i < len; i++)                for (i = 0; i < len; i++)
                 {                  {
Line 569  static int find_soa(struct dns_header *header, size_t  Line 530  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 no_cache_dnssec, int secure, int *doctored)                      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;
   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
   
       
   cache_start_insert();    cache_start_insert();
   
Line 588  int extract_addresses(struct dns_header *header, size_ Line 551  int extract_addresses(struct dns_header *header, size_
     {      {
       searched_soa = 1;        searched_soa = 1;
       ttl = find_soa(header, qlen, name, doctored);        ttl = find_soa(header, qlen, name, 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 (i = 0; i < ntohs(header->ancount); i++)
               if (daemon->rr_status[i] != 0)
                 return 0;
 #endif  #endif
           }
     }      }
       
   /* go through the questions. */    /* go through the questions. */
Line 602  int extract_addresses(struct dns_header *header, size_ Line 573  int extract_addresses(struct dns_header *header, size_
       int found = 0, cname_count = CNAME_CHAIN;        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;#ifdef HAVE_DNSSEC
       int cname_short = 0;
 #endif
       unsigned long cttl = ULONG_MAX, attl;        unsigned long cttl = ULONG_MAX, attl;
   
       namep = p;        namep = p;
Line 630  int extract_addresses(struct dns_header *header, size_ Line 603  int extract_addresses(struct dns_header *header, size_
               if (!(p1 = skip_questions(header, qlen)))                if (!(p1 = skip_questions(header, qlen)))
                 return 0;                  return 0;
                               
              for (j = ntohs(header->ancount); j != 0; j--)               for (j = 0; j < ntohs(header->ancount); j++) 
                 {                  {
                     int secflag = 0;
                   unsigned char *tmp = namep;                    unsigned char *tmp = namep;
                   /* the loop body overwrites the original name, so get it back here. */                    /* the loop body overwrites the original name, so get it back here. */
                   if (!extract_name(header, qlen, &tmp, name, 1, 0) ||                    if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
Line 657  int extract_addresses(struct dns_header *header, size_ Line 631  int extract_addresses(struct dns_header *header, size_
                     {                      {
                       if (!extract_name(header, qlen, &p1, name, 1, 0))                        if (!extract_name(header, qlen, &p1, name, 1, 0))
                         return 0;                          return 0;
                      #ifdef HAVE_DNSSEC
                       if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
                         {
                           /* validated RR anywhere in CNAME chain, don't cache. */
                           if (cname_short || aqtype == T_CNAME)
                             return 0;
 
                           secflag = F_DNSSECOK;
                           /* limit TTL based on signature. */
                           if (daemon->rr_status[j] < cttl)
                             cttl = daemon->rr_status[j];
                         }
 #endif
 
                       if (aqtype == T_CNAME)                        if (aqtype == T_CNAME)
                         {                          {
                          if (!cname_count-- || secure)                          if (!cname_count--)
                            return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */                            return 0; /* looped CNAMES, we can't cache. */
 #ifdef HAVE_DNSSEC
                           cname_short = 1;
 #endif
                           goto cname_loop;                            goto cname_loop;
                         }                          }
                                               
                      cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE);                      cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
                       found = 1;                         found = 1; 
                     }                      }
                                       
Line 683  int extract_addresses(struct dns_header *header, size_ Line 673  int extract_addresses(struct dns_header *header, size_
                   ttl = find_soa(header, qlen, NULL, doctored);                    ttl = find_soa(header, qlen, NULL, doctored);
                 }                  }
               if (ttl)                if (ttl)
                cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);                       cache_insert(NULL, &addr, C_IN, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ?  F_DNSSECOK : 0));    
             }              }
         }          }
       else        else
         {          {
           /* everything other than PTR */            /* everything other than PTR */
           struct crec *newc;            struct crec *newc;
          int addrlen;          int addrlen = 0;
   
           if (qtype == T_A)            if (qtype == T_A)
             {              {
               addrlen = INADDRSZ;                addrlen = INADDRSZ;
               flags |= F_IPV4;                flags |= F_IPV4;
             }              }
 #ifdef HAVE_IPV6  
           else if (qtype == T_AAAA)            else if (qtype == T_AAAA)
             {              {
               addrlen = IN6ADDRSZ;                addrlen = IN6ADDRSZ;
               flags |= F_IPV6;                flags |= F_IPV6;
             }              }
#endif          else if (qtype == T_SRV)
          else             flags |= F_SRV;
           else
             continue;              continue;
                           
         cname_loop1:          cname_loop1:
           if (!(p1 = skip_questions(header, qlen)))            if (!(p1 = skip_questions(header, qlen)))
             return 0;              return 0;
                       
          for (j = ntohs(header->ancount); j != 0; j--)           for (j = 0; j < ntohs(header->ancount); j++) 
             {              {
                 int secflag = 0;
                 
               if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))                if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
                 return 0; /* bad packet */                  return 0; /* bad packet */
                               
Line 729  int extract_addresses(struct dns_header *header, size_ Line 721  int extract_addresses(struct dns_header *header, size_
                               
               if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))                if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
                 {                  {
   #ifdef HAVE_DNSSEC
                     if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
                       {
                         secflag = F_DNSSECOK;
   
                         /* limit TTl based on sig. */
                         if (daemon->rr_status[j] < attl)
                           attl = daemon->rr_status[j];
                       }
   #endif            
                   if (aqtype == T_CNAME)                    if (aqtype == T_CNAME)
                     {                      {
                       if (!cname_count--)                        if (!cname_count--)
                         return 0; /* looped CNAMES */                          return 0; /* looped CNAMES */
                      newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);
                      if (newc)                      if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
                         {                          {
                           newc->addr.cname.target.cache = NULL;                            newc->addr.cname.target.cache = NULL;
                          /* anything other than zero, to avoid being mistaken for CNAME to interface-name */                           newc->addr.cname.is_name_ptr = 0; 
                          newc->addr.cname.uid = 1;  
                           if (cpp)                            if (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;
                             }                              }
Line 750  int extract_addresses(struct dns_header *header, size_ Line 752  int extract_addresses(struct dns_header *header, size_
                       if (attl < cttl)                        if (attl < cttl)
                         cttl = attl;                          cttl = attl;
                                               
                         namep = p1;
                       if (!extract_name(header, qlen, &p1, name, 1, 0))                        if (!extract_name(header, qlen, &p1, name, 1, 0))
                         return 0;                          return 0;
                         
                       goto cname_loop1;                        goto cname_loop1;
                     }                      }
                   else if (!(flags & F_NXDOMAIN))                    else if (!(flags & F_NXDOMAIN))
                     {                      {
                       found = 1;                        found = 1;
                                               
                      /* copy address into aligned storage */                      if (flags & F_SRV)
                      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) 
                         {                          {
                          if ((flags & F_IPV4) &&                           unsigned char *tmp = namep;
                              private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
                            return 1;                           if (!CHECK_LEN(header, p1, qlen, 6))
                                                       return 0; /* bad packet */
#ifdef HAVE_IPV6                           GETSHORT(addr.srv.priority, p1);
                          if ((flags & F_IPV6) &&                           GETSHORT(addr.srv.weight, p1);
                              IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6))                           GETSHORT(addr.srv.srvport, p1);
                            if (!extract_name(header, qlen, &p1, name, 1, 0))
                              return 0;
                            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 0;
                         }
                       else
                         {
                           /* 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)
                             {                              {
                              struct in_addr v4;                              if ((flags & F_IPV4) &&
                              v4.s_addr = ((const uint32_t *) (&addr.addr.addr6))[3];                                  private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
                              if (private_net(v4, !option_bool(OPT_LOCAL_REBIND))) 
                                 return 1;                                  return 1;
   
                                 /* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
                                 if (flags & F_IPV6)
                                   {
                                     if (IN6_IS_ADDR_V4MAPPED(&addr.addr6))
                                       {
                                         struct in_addr v4;
                                         v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
                                         if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
                                           return 1;
                                       }
   
                                     /* Check for link-local (LL) and site-local (ULA) IPv6 addresses */
                                     if (IN6_IS_ADDR_LINKLOCAL(&addr.addr6) ||
                                         IN6_IS_ADDR_SITELOCAL(&addr.addr6))
                                       return 1;
   
                                     /* Check for the IPv6 loopback address (::1) when
                                        option rebind-localhost-ok is NOT set */
                                     if (!option_bool(OPT_LOCAL_REBIND) &&
                                         IN6_IS_ADDR_LOOPBACK(&addr.addr6))
                                       return 1;
                                   }
                             }                              }
#endif
                        } 
                       
 #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) 
                             {                              {
                              log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);                              ipsets_cur = ipsets;
                              add_to_ipset(*ipsets_cur++, &addr, flags, 0);                              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  #endif
                           }
                                               
                      newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);                      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;
                         }                          }
Line 820  int extract_addresses(struct dns_header *header, size_ Line 859  int extract_addresses(struct dns_header *header, size_
                  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 | secflag);                         newc = cache_insert(name, NULL, C_IN, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));     
                   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;
                     }                      }
Line 875  unsigned int extract_request(struct dns_header *header Line 915  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;
     }      }
   
     /* 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 (qtype == T_DS || qtype == T_DNSKEY)
       return F_DNSSECOK;
       
   return F_QUERY;    return F_QUERY;
 }  }
   
   
 size_t setup_reply(struct dns_header *header, size_t qlen,  size_t setup_reply(struct dns_header *header, size_t qlen,
                struct all_addr *addrp, unsigned int flags, unsigned long ttl)                   union all_addr *addrp, unsigned int flags, unsigned long ttl)
 {  {
   unsigned char *p;    unsigned char *p;
  
   if (!(p = skip_questions(header, qlen)))    if (!(p = skip_questions(header, qlen)))
     return 0;      return 0;
       
   /* 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);
Line 900  size_t setup_reply(struct dns_header *header, size_t q Line 946  size_t setup_reply(struct dns_header *header, size_t q
     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 (flags == F_IPV4)  else if (flags == F_SERVFAIL)
    { /* we know the address */    {
      SET_RCODE(header, NOERROR);      union all_addr a;
      header->ancount = htons(1);      a.log.rcode = SERVFAIL;
      header->hb3 |= HB3_AA;      log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
      add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);      SET_RCODE(header, SERVFAIL);
     }      }
#ifdef HAVE_IPV6  else if (flags & ( F_IPV4 | F_IPV6))
  else if (flags == F_IPV6) 
     {      {
      SET_RCODE(header, NOERROR);      if (flags & F_IPV4)
      header->ancount = htons(1);        { /* we know the address */
      header->hb3 |= HB3_AA;          SET_RCODE(header, NOERROR);
      add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);          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);
         }
       
       if (flags & F_IPV6)
         {
           SET_RCODE(header, NOERROR);
           header->ancount = htons(ntohs(header->ancount) + 1);
           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;
       a.log.rcode = REFUSED;
       log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
       SET_RCODE(header, REFUSED);
     }
   
   return p - (unsigned char *)header;    return p - (unsigned char *)header;
 }  }
   
 /* 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;
   
   /* Note: the call to cache_find_by_name is intended to find any record which matches  
      ie A, AAAA, CNAME. */  
   
   if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |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;
 }  }
   
Line 998  int check_for_bogus_wildcard(struct dns_header *header Line 1053  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);                cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
                 cache_end_insert();                  cache_end_insert();
                                   
                 return 1;                  return 1;
Line 1049  int check_for_ignored_address(struct dns_header *heade Line 1104  int check_for_ignored_address(struct dns_header *heade
   return 0;    return 0;
 }  }
   
   
 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 1058  int add_resource_record(struct dns_header *header, cha Line 1114  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 1091  int add_resource_record(struct dns_header *header, cha Line 1159  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 1141  int add_resource_record(struct dns_header *header, cha Line 1215  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 1149  int add_resource_record(struct dns_header *header, cha Line 1224  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 unsigned long crec_ttl(struct crec *crecp, time_t now)  static unsigned long crec_ttl(struct crec *crecp, time_t now)
Line 1184  static unsigned long crec_ttl(struct crec *crecp, time Line 1260  static unsigned long crec_ttl(struct crec *crecp, time
   if (crecp->flags & F_IMMORTAL)    if (crecp->flags & F_IMMORTAL)
     return crecp->ttd;      return crecp->ttd;
   
  /* Return the Max TTL value if it is lower then the actual TTL */  /* Return the Max TTL value if it is lower than the actual TTL */
   if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))    if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
     return crecp->ttd - now;      return crecp->ttd - now;
   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, 
Line 1200  size_t answer_request(struct dns_header *header, char  Line 1280  size_t answer_request(struct dns_header *header, char 
   char *name = daemon->namebuff;    char *name = daemon->namebuff;
   unsigned char *p, *ansp;    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;    int dryrun = 0;
   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);
 
   /* never answer queries with RD unset, to avoid cache snooping. */
   if (ntohs(header->ancount) != 0 ||    if (ntohs(header->ancount) != 0 ||
       ntohs(header->nscount) != 0 ||        ntohs(header->nscount) != 0 ||
      ntohs(header->qdcount) == 0 ||       ntohs(header->qdcount) == 0 ||
       OPCODE(header) != QUERY )        OPCODE(header) != QUERY )
     return 0;      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;
Line 1239  size_t answer_request(struct dns_header *header, char  Line 1321  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 1250  size_t answer_request(struct dns_header *header, char  Line 1334  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 */
      
       while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME)))
         {
           char *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(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++;
                 }
 
             }
           else
             return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */
 
           strcpy(name, cname_target);
         }
           
       if (qtype == T_TXT || qtype == T_ANY)        if (qtype == T_TXT || qtype == T_ANY)
         {          {
           struct txt_record *t;            struct txt_record *t;
Line 1258  size_t answer_request(struct dns_header *header, char  Line 1372  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 1271  size_t answer_request(struct dns_header *header, char  Line 1385  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>");
                        anscount++;                          if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                                   ttl, NULL,
                                                   T_TXT, t->class, "t", t->len, t->txt))
                             anscount++;
                         }
                     }
                 }
             }
         }
   
         if (qclass == C_CHAOS)
           {
             /* don't forward *.bind and *.server chaos queries - always reply with NOTIMP */
             if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name))
               {
                 if (!ans)
                   {
                     notimp = 1, auth = 0;
                     if (!dryrun)
                       {
                          addr.log.rcode = NOTIMP;
                          log_query(F_CONFIG | F_RCODE, name, &addr, NULL);
                     }                      }
                     ans = 1, sec_data = 0;
                 }                  }
             }              }
         }          }
Line 1293  size_t answer_request(struct dns_header *header, char  Line 1428  size_t answer_request(struct dns_header *header, char 
                 sec_data = 0;                  sec_data = 0;
                 if (!dryrun)                  if (!dryrun)
                   {                    {
                    log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");                    log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(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 1318  size_t answer_request(struct dns_header *header, char  Line 1453  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)
Line 1327  size_t answer_request(struct dns_header *header, char  Line 1462  size_t answer_request(struct dns_header *header, char 
                       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)
Line 1343  size_t answer_request(struct dns_header *header, char  Line 1477  size_t answer_request(struct dns_header *header, char 
                       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)
                 {                  {
Line 1379  size_t answer_request(struct dns_header *header, char  Line 1512  size_t answer_request(struct dns_header *header, char 
                   /* Don't use cache when DNSSEC data required, unless we know that                    /* Don't use cache when DNSSEC data required, unless we know that
                      the zone is unsigned, which implies that we're doing                       the zone is unsigned, which implies that we're doing
                      validation. */                       validation. */
                  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||                   if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
                      !do_bit ||                       (rd_bit && (!do_bit || cache_validated(crecp)) ))
                      (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) 
                     {                      {
                       do                         do 
                         {                           { 
Line 1434  size_t answer_request(struct dns_header *header, char  Line 1566  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)) ||
                       private_net(addr.addr.addr4, 1))                       (is_arpa == F_IPV4 && private_net(addr.addr4, 1))))
                 {                  {
                  /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */                  struct server *serv;
                  ans = 1;                  unsigned int namelen = strlen(name);
                  sec_data = 0;                  char *nameend = name + namelen;
                  nxdomain = 1; 
                  if (!dryrun) 
                    log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,  
                              name, &addr, NULL); 
                } 
            } 
             
          for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) 
            { 
              unsigned short type = T_A; 
              struct interface_name *intr; 
   
              if (flag == F_IPV6)                  /* see if have rev-server set */
#ifdef HAVE_IPV6                  for (serv = daemon->servers; serv; serv = serv->next)
                type = T_AAAA; 
#else 
                break; 
#endif 
               
              if (qtype != type && qtype != T_ANY) 
                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)                       unsigned int domainlen;
                        {                      char *matchstart;
                          i = 5;
                          break;                      if ((serv->flags & (SERV_HAS_DOMAIN | SERV_NO_ADDR)) != SERV_HAS_DOMAIN)
                        }                        continue;
                      
                      a = (a << 8) + x;                      domainlen = strlen(serv->domain);
                                            if (domainlen == 0 || domainlen > namelen)
                      if (*cp == '.'                        continue;
                        cp++;
                       matchstart = nameend - domainlen;
                       if (hostname_isequal(matchstart, serv->domain) &&
                           (namelen == domainlen || *(matchstart-1) == '.' ))
                         break;
                     }                      }
                  
                  if (i == 4)                  /* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */
                   if (!serv)
                     {                      {
                       ans = 1;                        ans = 1;
                       sec_data = 0;                        sec_data = 0;
                         nxdomain = 1;
                       if (!dryrun)                        if (!dryrun)
                        {                        log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN,
                          addr.addr.addr4.s_addr = htonl(a);                                  name, &addr, NULL);
                          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; 
                     }                      }
                 }                  }
               }
   
             for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
               {
                 unsigned short type = (flag == F_IPV6) ? T_AAAA : T_A;
                 struct interface_name *intr;
   
                 if (qtype != type && qtype != T_ANY)
                   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 1510  size_t answer_request(struct dns_header *header, char  Line 1622  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))
                       {                        {
                         for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)                          for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
 #ifdef HAVE_IPV6  
                           if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)                            if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
 #endif  
                             {                              {
#ifdef HAVE_IPV6                              if (localise && 
                                   !is_same_net(addrlist->addr.addr4, local_addr, local_netmask))
                                 continue;
 
                               if (addrlist->flags & ADDRLIST_REVONLY)                                if (addrlist->flags & ADDRLIST_REVONLY)
                                 continue;                                  continue;
#endif  
                               ans = 1;                                  ans = 1;  
                               sec_data = 0;                                sec_data = 0;
                               if (!dryrun)                                if (!dryrun)
Line 1546  size_t answer_request(struct dns_header *header, char  Line 1672  size_t answer_request(struct dns_header *header, char 
                   continue;                    continue;
                 }                  }
   
            cname_restart:              if ((crecp = cache_find_by_name(NULL, name, now, flag | (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  don't use cached data. */                    /* If the client asked for DNSSEC  don't use cached data. */
                  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))                  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
                       (rd_bit && (!do_bit || cache_validated(crecp)) ))
                     do                      do
                       {                         { 
                         /* don't answer wildcard queries with data not from /etc/hosts                          /* don't answer wildcard queries with data not from /etc/hosts
Line 1579  size_t answer_request(struct dns_header *header, char  Line 1705  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)  
                           {  
                             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)                          if (crecp->flags & F_NEG)
                           {                            {
                             ans = 1;                              ans = 1;
Line 1615  size_t answer_request(struct dns_header *header, char  Line 1720  size_t answer_request(struct dns_header *header, char 
                                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 1624  size_t answer_request(struct dns_header *header, char  Line 1729  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(crecp->flags & ~F_REVERSE, name, &crecp->addr,
                                           record_source(crecp->uid));                                            record_source(crecp->uid));
                                                                   
                                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,                                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
Line 1633  size_t answer_request(struct dns_header *header, char  Line 1738  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);
Line 1648  size_t answer_request(struct dns_header *header, char  Line 1753  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>");
                                              &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>");
Line 1713  size_t answer_request(struct dns_header *header, char  Line 1800  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;
Line 1745  size_t answer_request(struct dns_header *header, char  Line 1833  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 | (dryrun ? F_NO_RR : 0))) &&
                       rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
                     {
                       if (!(crecp->flags & F_DNSSECOK))
                         sec_data = 0;
                       
                       auth = 0;
                       found = ans = 1;
                       
                       do {
                         if (crecp->flags & F_NEG)
                           {
                             if (crecp->flags & F_NXDOMAIN)
                               nxdomain = 1;
                             if (!dryrun)
                               log_query(crecp->flags, name, NULL, NULL);
                           }
                         else if (!dryrun)
                           {
                             char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
                             log_query(crecp->flags, name, 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);
                 }                  }
Line 1761  size_t answer_request(struct dns_header *header, char  Line 1884  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>");
Line 1773  size_t answer_request(struct dns_header *header, char  Line 1897  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);
             }              }
Line 1806  size_t answer_request(struct dns_header *header, char  Line 1931  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 1827  size_t answer_request(struct dns_header *header, char  Line 1949  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 1837  size_t answer_request(struct dns_header *header, char  Line 1959  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);

Removed from v.1.1.1.3  
changed lines
  Added in v.1.1.1.4


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