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

version 1.1.1.2, 2014/06/15 16:31:38 version 1.1.1.3, 2016/11/02 09:57:01
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 20  int extract_name(struct dns_header *header, size_t ple Line 20  int extract_name(struct dns_header *header, size_t ple
                  char *name, int isExtract, int extrabytes)                   char *name, int isExtract, int extrabytes)
 {  {
   unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;    unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
  unsigned int j, l, hops = 0;  unsigned int j, l, namelen = 0, hops = 0;
   int retvalue = 1;    int retvalue = 1;
       
   if (isExtract)    if (isExtract)
Line 77  int extract_name(struct dns_header *header, size_t ple Line 77  int extract_name(struct dns_header *header, size_t ple
                       
           p = l + (unsigned char *)header;            p = l + (unsigned char *)header;
         }          }
      else if (label_type == 0x80)      else if (label_type == 0x00)
        return 0; /* reserved */ 
      else if (label_type == 0x40) 
        { /* ELT */ 
          unsigned int count, digs; 
           
          if ((l & 0x3f) != 1) 
            return 0; /* we only understand bitstrings */ 
 
          if (!isExtract) 
            return 0; /* Cannot compare bitsrings */ 
           
          count = *p++; 
          if (count == 0) 
            count = 256; 
          digs = ((count-1)>>2)+1; 
           
          /* output is \[x<hex>/siz]. which is digs+9 chars */ 
          if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME) 
            return 0; 
          if (!CHECK_LEN(header, p, plen, (count-1)>>3)) 
            return 0; 
 
          *cp++ = '\\'; 
          *cp++ = '['; 
          *cp++ = 'x'; 
          for (j=0; j<digs; j++) 
            { 
              unsigned int dig; 
              if (j%2 == 0) 
                dig = *p >> 4; 
              else 
                dig = *p++ & 0x0f; 
               
              *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10; 
            }  
          cp += sprintf((char *)cp, "/%d]", count); 
          /* do this here to overwrite the zero char from sprintf */ 
          *cp++ = '.'; 
        } 
      else  
         { /* label_type = 0 -> label. */          { /* label_type = 0 -> label. */
          if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)          namelen += l + 1; /* include period */
           if (namelen >= MAXDNAME)
             return 0;              return 0;
           if (!CHECK_LEN(header, p, plen, l))            if (!CHECK_LEN(header, p, plen, l))
             return 0;              return 0;
Line 128  int extract_name(struct dns_header *header, size_t ple Line 89  int extract_name(struct dns_header *header, size_t ple
             if (isExtract)              if (isExtract)
               {                {
                 unsigned char c = *p;                  unsigned char c = *p;
                if (isascii(c) && !iscntrl(c) && c != '.')#ifdef HAVE_DNSSEC
                  *cp++ = *p;                if (option_bool(OPT_DNSSEC_VALID))
                   {
                     if (c == 0 || c == '.' || c == NAME_ESCAPE)
                       {
                         *cp++ = NAME_ESCAPE;
                         *cp++ = c+1;
                       }
                     else
                       *cp++ = c; 
                   }
                 else                  else
   #endif
                   if (c != 0 && c != '.')
                     *cp++ = c;
                   else
                   return 0;                    return 0;
               }                }
             else               else 
Line 144  int extract_name(struct dns_header *header, size_t ple Line 118  int extract_name(struct dns_header *header, size_t ple
                     cp++;                      cp++;
                     if (c1 >= 'A' && c1 <= 'Z')                      if (c1 >= 'A' && c1 <= 'Z')
                       c1 += 'a' - 'A';                        c1 += 'a' - 'A';
   #ifdef HAVE_DNSSEC
                       if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
                         c1 = (*cp++)-1;
   #endif
                       
                     if (c2 >= 'A' && c2 <= 'Z')                      if (c2 >= 'A' && c2 <= 'Z')
                       c2 += 'a' - 'A';                        c2 += 'a' - 'A';
                                         
                     if (c1 != c2)                      if (c1 != c2)
                       retvalue =  2;                        retvalue =  2;
                   }                    }
               }                }
                      
           if (isExtract)            if (isExtract)
             *cp++ = '.';              *cp++ = '.';
           else if (*cp != 0 && *cp++ != '.')            else if (*cp != 0 && *cp++ != '.')
             retvalue = 2;              retvalue = 2;
         }          }
         else
           return 0; /* label types 0x40 and 0x80 not supported */
     }      }
 }  }
     
Line 427  size_t resize_packet(struct dns_header *header, size_t Line 408  size_t resize_packet(struct dns_header *header, size_t
   return ansp - (unsigned char *)header;    return ansp - (unsigned char *)header;
 }  }
   
 unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign)  
 {  
   /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.   
      also return length of pseudoheader in *len and pointer to the UDP size in *p  
      Finally, check to see if a packet is signed. If it is we cannot change a single bit before  
      forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */  
     
   int i, arcount = ntohs(header->arcount);  
   unsigned char *ansp = (unsigned char *)(header+1);  
   unsigned short rdlen, type, class;  
   unsigned char *ret = NULL;  
   
   if (is_sign)  
     {  
       *is_sign = 0;  
   
       if (OPCODE(header) == QUERY)  
         {  
           for (i = ntohs(header->qdcount); i != 0; i--)  
             {  
               if (!(ansp = skip_name(ansp, header, plen, 4)))  
                 return NULL;  
                 
               GETSHORT(type, ansp);   
               GETSHORT(class, ansp);  
                 
               if (class == C_IN && type == T_TKEY)  
                 *is_sign = 1;  
             }  
         }  
     }  
   else  
     {  
       if (!(ansp = skip_questions(header, plen)))  
         return NULL;  
     }  
       
   if (arcount == 0)  
     return NULL;  
     
   if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))  
     return NULL;   
     
   for (i = 0; i < arcount; i++)  
     {  
       unsigned char *save, *start = ansp;  
       if (!(ansp = skip_name(ansp, header, plen, 10)))  
         return NULL;   
   
       GETSHORT(type, ansp);  
       save = ansp;  
       GETSHORT(class, ansp);  
       ansp += 4; /* TTL */  
       GETSHORT(rdlen, ansp);  
       if (!ADD_RDLEN(header, ansp, plen, rdlen))  
         return NULL;  
       if (type == T_OPT)  
         {  
           if (len)  
             *len = ansp - start;  
           if (p)  
             *p = save;  
           ret = start;  
         }  
       else if (is_sign &&   
                i == arcount - 1 &&   
                class == C_ANY &&   
                type == T_TSIG)  
         *is_sign = 1;  
     }  
     
   return ret;  
 }  
   
 struct macparm {  
   unsigned char *limit;  
   struct dns_header *header;  
   size_t plen;  
   union mysockaddr *l3;  
 };  
    
 static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,   
                                int optno, unsigned char *opt, size_t optlen, int set_do)  
 {   
   unsigned char *lenp, *datap, *p;  
   int rdlen, is_sign;  
     
   if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))  
     {  
       if (is_sign)  
         return plen;  
   
       /* We are adding the pseudoheader */  
       if (!(p = skip_questions(header, plen)) ||  
           !(p = skip_section(p,   
                              ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),   
                              header, plen)))  
         return plen;  
       *p++ = 0; /* empty name */  
       PUTSHORT(T_OPT, p);  
       PUTSHORT(daemon->edns_pktsz, p); /* max packet length */  
       PUTSHORT(0, p);    /* extended RCODE and version */  
       PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */  
       lenp = p;  
       PUTSHORT(0, p);    /* RDLEN */  
       rdlen = 0;  
       if (((ssize_t)optlen) > (limit - (p + 4)))  
         return plen; /* Too big */  
       header->arcount = htons(ntohs(header->arcount) + 1);  
       datap = p;  
     }  
   else  
     {  
       int i;  
       unsigned short code, len, flags;  
         
       /* Must be at the end, if exists */  
       if (ntohs(header->arcount) != 1 ||  
           is_sign ||  
           (!(p = skip_name(p, header, plen, 10))))  
         return plen;  
         
       p += 6; /* skip UDP length and RCODE */  
       GETSHORT(flags, p);  
       if (set_do)  
         {  
           p -=2;  
           PUTSHORT(flags | 0x8000, p);  
         }  
   
       lenp = p;  
       GETSHORT(rdlen, p);  
       if (!CHECK_LEN(header, p, plen, rdlen))  
         return plen; /* bad packet */  
       datap = p;  
   
        /* no option to add */  
       if (optno == 0)  
         return plen;  
             
       /* check if option already there */  
       for (i = 0; i + 4 < rdlen; i += len + 4)  
         {  
           GETSHORT(code, p);  
           GETSHORT(len, p);  
           if (code == optno)  
             return plen;  
           p += len;  
         }  
         
       if (((ssize_t)optlen) > (limit - (p + 4)))  
         return plen; /* Too big */  
     }  
     
   if (optno != 0)  
     {  
       PUTSHORT(optno, p);  
       PUTSHORT(optlen, p);  
       memcpy(p, opt, optlen);  
       p += optlen;    
     }  
   
   PUTSHORT(p - datap, lenp);  
   return p - (unsigned char *)header;  
     
 }  
   
 static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)  
 {  
   struct macparm *parm = parmv;  
   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 */  
 }               
        
 size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)  
 {  
   struct macparm parm;  
        
 /* Must have an existing pseudoheader as the only ar-record,   
    or have no ar-records. Must also not be signed */  
      
   if (ntohs(header->arcount) > 1)  
     return plen;  
   
   parm.header = header;  
   parm.limit = (unsigned char *)limit;  
   parm.plen = plen;  
   parm.l3 = l3;  
   
   iface_enumerate(AF_UNSPEC, &parm, filter_mac);  
     
   return parm.plen;   
 }  
   
 struct subnet_opt {  
   u16 family;  
   u8 source_netmask, scope_netmask;  
 #ifdef HAVE_IPV6   
   u8 addr[IN6ADDRSZ];  
 #else  
   u8 addr[INADDRSZ];  
 #endif  
 };  
   
 static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)  
 {  
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */  
     
   int len;  
   void *addrp;  
   
 #ifdef HAVE_IPV6  
   if (source->sa.sa_family == AF_INET6)  
     {  
       opt->family = htons(2);  
       opt->source_netmask = daemon->addr6_netmask;  
       addrp = &source->in6.sin6_addr;  
     }  
   else  
 #endif  
     {  
       opt->family = htons(1);  
       opt->source_netmask = daemon->addr4_netmask;  
       addrp = &source->in.sin_addr;  
     }  
     
   opt->scope_netmask = 0;  
   len = 0;  
     
   if (opt->source_netmask != 0)  
     {  
       len = ((opt->source_netmask - 1) >> 3) + 1;  
       memcpy(opt->addr, addrp, len);  
       if (opt->source_netmask & 7)  
         opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));  
     }  
   
   return len + 4;  
 }  
    
 size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source)  
 {  
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */  
     
   int len;  
   struct subnet_opt opt;  
     
   len = calc_subnet_opt(&opt, source);  
   return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);  
 }  
   
 #ifdef HAVE_DNSSEC  
 size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)  
 {  
   return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);  
 }  
 #endif  
   
 int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)  
 {  
   /* Section 9.2, Check that subnet option in reply matches. */  
   
   
  int len, calc_len;  
   struct subnet_opt opt;  
   unsigned char *p;  
   int code, i, rdlen;  
     
    calc_len = calc_subnet_opt(&opt, peer);  
      
    if (!(p = skip_name(pseudoheader, header, plen, 10)))  
      return 1;  
      
    p += 8; /* skip UDP length and RCODE */  
      
    GETSHORT(rdlen, p);  
    if (!CHECK_LEN(header, p, plen, rdlen))  
      return 1; /* bad packet */  
      
    /* check if option there */  
    for (i = 0; i + 4 < rdlen; i += len + 4)  
      {  
        GETSHORT(code, p);  
        GETSHORT(len, p);  
        if (code == EDNS0_OPTION_CLIENT_SUBNET)  
          {  
            /* make sure this doesn't mismatch. */  
            opt.scope_netmask = p[3];  
            if (len != calc_len || memcmp(p, &opt, len) != 0)  
              return 0;  
          }  
        p += len;  
      }  
      
    return 1;  
 }  
   
 /* is addr in the non-globally-routed IP space? */   /* is addr in the non-globally-routed IP space? */ 
 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);
   
   return    return
    (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost)  /* 127.0.0.0/8    (loopback) */ ||     (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost)  /* 127.0.0.0/8    (loopback) */ ||
    ((ip_addr & 0xFFFF0000) == 0xC0A80000)  /* 192.168.0.0/16 (private)  */ ||    ((ip_addr & 0xFF000000) == 0x00000000)  /* RFC 5735 section 3. "here" network */ ||
     ((ip_addr & 0xFF000000) == 0x0A000000)  /* 10.0.0.0/8     (private)  */ ||      ((ip_addr & 0xFF000000) == 0x0A000000)  /* 10.0.0.0/8     (private)  */ ||
     ((ip_addr & 0xFFF00000) == 0xAC100000)  /* 172.16.0.0/12  (private)  */ ||      ((ip_addr & 0xFFF00000) == 0xAC100000)  /* 172.16.0.0/12  (private)  */ ||
    ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;    ((ip_addr & 0xFFFF0000) == 0xC0A80000)  /* 192.168.0.0/16 (private)  */ ||
     ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ||
     ((ip_addr & 0xFFFFFF00) == 0xC0000200)  /* 192.0.2.0/24   (test-net) */ ||
     ((ip_addr & 0xFFFFFF00) == 0xC6336400)  /* 198.51.100.0/24(test-net) */ ||
     ((ip_addr & 0xFFFFFF00) == 0xCB007100)  /* 203.0.113.0/24 (test-net) */ ||
     ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF)  /* 255.255.255.255/32 (broadcast)*/ ;
 }  }
   
 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)
Line 1092  int extract_addresses(struct dns_header *header, size_ Line 764  int extract_addresses(struct dns_header *header, size_
                       memcpy(&addr, p1, addrlen);                        memcpy(&addr, p1, addrlen);
                                               
                       /* check for returned address in private space */                        /* check for returned address in private space */
                      if (check_rebind &&                      if (check_rebind)
                          (flags & F_IPV4) &&                        {
                          private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))                          if ((flags & F_IPV4) &&
                        return 1;                              private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
                             return 1;
                           
 #ifdef HAVE_IPV6
                           if ((flags & F_IPV6) &&
                               IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6))
                             {
                               struct in_addr v4;
                               v4.s_addr = ((const uint32_t *) (&addr.addr.addr6))[3];
                               if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
                                 return 1;
                             }
 #endif
                         }
                                               
 #ifdef HAVE_IPSET  #ifdef HAVE_IPSET
                       if (ipsets && (flags & (F_IPV4 | F_IPV6)))                        if (ipsets && (flags & (F_IPV4 | F_IPV6)))
Line 1198  unsigned int extract_request(struct dns_header *header Line 883  unsigned int extract_request(struct dns_header *header
 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)                  struct all_addr *addrp, unsigned int flags, unsigned long ttl)
 {  {
  unsigned char *p = skip_questions(header, qlen);  unsigned char *p;
 
   if (!(p = skip_questions(header, qlen)))
     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;
Line 1208  size_t setup_reply(struct dns_header *header, size_t q Line 896  size_t setup_reply(struct dns_header *header, size_t q
   header->nscount = htons(0);    header->nscount = htons(0);
   header->arcount = htons(0);    header->arcount = htons(0);
   header->ancount = htons(0); /* no answers unless changed below */    header->ancount = htons(0); /* no answers unless changed below */
  if (flags == F_NEG)  if (flags == F_NOERR)
    SET_RCODE(header, SERVFAIL); /* couldn't get memory */ 
  else if (flags == F_NOERR) 
     SET_RCODE(header, NOERROR); /* empty domain */      SET_RCODE(header, NOERROR); /* empty domain */
   else if (flags == F_NXDOMAIN)    else if (flags == F_NXDOMAIN)
     SET_RCODE(header, NXDOMAIN);      SET_RCODE(header, NXDOMAIN);
  else if (p && flags == F_IPV4)  else if (flags == F_IPV4)
     { /* we know the address */      { /* we know the address */
       SET_RCODE(header, NOERROR);        SET_RCODE(header, NOERROR);
       header->ancount = htons(1);        header->ancount = htons(1);
Line 1222  size_t setup_reply(struct dns_header *header, size_t q Line 908  size_t setup_reply(struct dns_header *header, size_t q
       add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);        add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
     }      }
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
  else if (p && flags == F_IPV6)  else if (flags == F_IPV6)
     {      {
       SET_RCODE(header, NOERROR);        SET_RCODE(header, NOERROR);
       header->ancount = htons(1);        header->ancount = htons(1);
Line 1246  int check_for_local_domain(char *name, time_t now) Line 932  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 | F_DS | F_NO_RR)) &&  /* 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)))        (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
     return 1;      return 1;
       
Line 1323  int check_for_bogus_wildcard(struct dns_header *header Line 1012  int check_for_bogus_wildcard(struct dns_header *header
   return 0;    return 0;
 }  }
   
   int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr)
   {
     unsigned char *p;
     int i, qtype, qclass, rdlen;
     struct bogus_addr *baddrp;
   
     /* skip over questions */
     if (!(p = skip_questions(header, qlen)))
       return 0; /* bad packet */
   
     for (i = ntohs(header->ancount); i != 0; i--)
       {
         if (!(p = skip_name(p, header, qlen, 10)))
           return 0; /* bad packet */
         
         GETSHORT(qtype, p); 
         GETSHORT(qclass, p);
         p += 4; /* TTL */
         GETSHORT(rdlen, p);
         
         if (qclass == C_IN && qtype == T_A)
           {
             if (!CHECK_LEN(header, p, qlen, INADDRSZ))
               return 0;
             
             for (baddrp = baddr; baddrp; baddrp = baddrp->next)
               if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
                 return 1;
           }
         
         if (!ADD_RDLEN(header, p, qlen, rdlen))
           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 1441  int add_resource_record(struct dns_header *header, cha Line 1167  int add_resource_record(struct dns_header *header, cha
 static unsigned long crec_ttl(struct crec *crecp, time_t now)  static unsigned long crec_ttl(struct crec *crecp, time_t now)
 {  {
   /* Return 0 ttl for DHCP entries, which might change    /* Return 0 ttl for DHCP entries, which might change
     before the lease expires. */     before the lease expires, unless configured otherwise. */
   
  if  (crecp->flags & (F_IMMORTAL | F_DHCP))  if (crecp->flags & F_DHCP)
    return daemon->local_ttl;    {
       int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
       
       /* Apply ceiling of actual lease length to configured TTL. */
       if (!(crecp->flags & F_IMMORTAL) && (crecp->ttd - now) < conf_ttl)
         return crecp->ttd - now;
       
       return conf_ttl;
     }     
       
     /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
     if (crecp->flags & F_IMMORTAL)
       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 then 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;
Line 1457  static unsigned long crec_ttl(struct crec *crecp, time Line 1195  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,                         struct in_addr local_addr, struct in_addr local_netmask, 
                      time_t now, int *ad_reqd, int *do_bit                      time_t now, int ad_reqd, int do_bit, int have_pseudoheader) 
 {  {
   char *name = daemon->namebuff;    char *name = daemon->namebuff;
  unsigned char *p, *ansp, *pheader;  unsigned char *p, *ansp;
   unsigned int qtype, qclass;    unsigned int qtype, qclass;
   struct all_addr addr;    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, have_pseudoheader = 0;  int dryrun = 0;
  int is_sign; 
   struct crec *crecp;    struct crec *crecp;
   int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;    int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
   struct mx_srv_record *rec;    struct mx_srv_record *rec;
   size_t len;    size_t len;
   
   if (ntohs(header->ancount) != 0 ||
       ntohs(header->nscount) != 0 ||
       ntohs(header->qdcount) == 0 || 
       OPCODE(header) != QUERY )
     return 0;
   
   /* Don't return AD set if checking disabled. */    /* Don't return AD set if checking disabled. */
   if (header->hb4 & HB4_CD)    if (header->hb4 & HB4_CD)
     sec_data = 0;      sec_data = 0;
       
  /* RFC 6840 5.7 */  /* If there is an  additional data section then it will be overwritten by
  *ad_reqd = header->hb4 & HB4_AD; 
  *do_bit = 0; 
 
  /* If there is an RFC2671 pseudoheader then it will be overwritten by 
      partial replies, so we have to do a dry run to see if we can answer       partial replies, so we have to do a dry run to see if we can answer
     the query. We check to see if the do bit is set, if so we always     the query. */
     forward rather than answering from the cache, which doesn't include  if (ntohs(header->arcount) != 0)
     security information, unless we're in DNSSEC validation mode. */    dryrun = 1;
   
   if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))  
     {   
       unsigned short udpsz, flags;  
       unsigned char *psave = pheader;  
   
       have_pseudoheader = 1;  
   
       GETSHORT(udpsz, pheader);  
       pheader += 2; /* ext_rcode */  
       GETSHORT(flags, pheader);  
         
       if ((sec_reqd = flags & 0x8000))  
         *do_bit = 1;/* do bit */   
       *ad_reqd = 1;  
   
       /* If our client is advertising a larger UDP packet size  
          than we allow, trim it so that we don't get an overlarge  
          response from upstream */  
   
       if (!is_sign && (udpsz > daemon->edns_pktsz))  
         PUTSHORT(daemon->edns_pktsz, psave);   
   
       dryrun = 1;  
     }  
   
   if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )  
     return 0;  
     
   for (rec = daemon->mxnames; rec; rec = rec->next)    for (rec = daemon->mxnames; rec; rec = rec->next)
     rec->offset = 0;      rec->offset = 0;
       
Line 1571  size_t answer_request(struct dns_header *header, char  Line 1282  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 1671  size_t answer_request(struct dns_header *header, char  Line 1290  size_t answer_request(struct dns_header *header, char 
             if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))              if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
               {                {
                 ans = 1;                  ans = 1;
                   sec_data = 0;
                 if (!dryrun)                  if (!dryrun)
                   {                    {
                     log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
Line 1727  size_t answer_request(struct dns_header *header, char  Line 1347  size_t answer_request(struct dns_header *header, char 
                               
               if (intr)                if (intr)
                 {                  {
                     sec_data = 0;
                   ans = 1;                    ans = 1;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
Line 1740  size_t answer_request(struct dns_header *header, char  Line 1361  size_t answer_request(struct dns_header *header, char 
               else if (ptr)                else if (ptr)
                 {                  {
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                       log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
Line 1754  size_t answer_request(struct dns_header *header, char  Line 1376  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)))
                 {                  {
                  if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)                  /* Don't use cache when DNSSEC data required, unless we know that
                      the zone is unsigned, which implies that we're doing
                      validation. */
                   if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || 
                       !do_bit || 
                       (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
                     {                      {
                       if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))  
                         crecp = NULL;  
 #ifdef HAVE_DNSSEC  
                       else if (crecp->flags & F_DNSSECOK)  
                         {  
                           int gotsig = 0;  
                           struct crec *rr_crec = NULL;  
   
                           while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))  
                             {  
                               if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)  
                                 {  
                                   char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);  
                                   gotsig = 1;  
                                     
                                   if (!dryrun &&   
                                       add_resource_record(header, limit, &trunc, nameoffset, &ansp,   
                                                           rr_crec->ttd - now, &nameoffset,  
                                                           T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))  
                                     anscount++;  
                                 }  
                             }   
                             
                           if (!gotsig)  
                             crecp = NULL;  
                         }  
 #endif  
                     }  
   
                   if (crecp)  
                     {  
                       do                         do 
                         {                           { 
                           /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */                            /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
Line 1795  size_t answer_request(struct dns_header *header, char  Line 1391  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;
                                                     
                           ans = 1;
                            
                           if (crecp->flags & F_NEG)                            if (crecp->flags & F_NEG)
                             {                              {
                               ans = 1;  
                               auth = 0;                                auth = 0;
                               if (crecp->flags & F_NXDOMAIN)                                if (crecp->flags & F_NXDOMAIN)
                                 nxdomain = 1;                                  nxdomain = 1;
                               if (!dryrun)                                if (!dryrun)
                                 log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);                                  log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
                             }                              }
                          else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))                          else
                             {                              {
                               ans = 1;  
                               if (!(crecp->flags & (F_HOSTS | F_DHCP)))                                if (!(crecp->flags & (F_HOSTS | F_DHCP)))
                                 auth = 0;                                  auth = 0;
                               if (!dryrun)                                if (!dryrun)
Line 1827  size_t answer_request(struct dns_header *header, char  Line 1423  size_t answer_request(struct dns_header *header, char 
               else if (is_rev_synth(is_arpa, &addr, name))                else if (is_rev_synth(is_arpa, &addr, name))
                 {                  {
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   if (!dryrun)                    if (!dryrun)
                     {                      {
                       log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);                         log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL); 
Line 1843  size_t answer_request(struct dns_header *header, char  Line 1440  size_t answer_request(struct dns_header *header, char 
                 {                  {
                   /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */                    /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
                   ans = 1;                    ans = 1;
                     sec_data = 0;
                   nxdomain = 1;                    nxdomain = 1;
                   if (!dryrun)                    if (!dryrun)
                     log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,                       log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, 
Line 1890  size_t answer_request(struct dns_header *header, char  Line 1488  size_t answer_request(struct dns_header *header, char 
                   if (i == 4)                    if (i == 4)
                     {                      {
                       ans = 1;                        ans = 1;
                         sec_data = 0;
                       if (!dryrun)                        if (!dryrun)
                         {                          {
                           addr.addr.addr4.s_addr = htonl(a);                            addr.addr.addr4.s_addr = htonl(a);
Line 1918  size_t answer_request(struct dns_header *header, char  Line 1517  size_t answer_request(struct dns_header *header, char 
                   for (intr = daemon->int_names; intr; intr = intr->next)                    for (intr = daemon->int_names; intr; intr = intr->next)
                     if (hostname_isequal(name, intr->name))                      if (hostname_isequal(name, intr->name))
                       {                        {
                        ans = 1;                        for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
                        if (!dryrun) 
                          { 
                             
                            for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) 
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
                              if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)                          if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
 #endif  #endif
                               {
   #ifdef HAVE_IPV6
                                 if (addrlist->flags & ADDRLIST_REVONLY)
                                   continue;
   #endif  
                                 ans = 1;  
                                 sec_data = 0;
                                 if (!dryrun)
                                 {                                  {
                                   gotit = 1;                                    gotit = 1;
                                   log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);                                    log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
Line 1934  size_t answer_request(struct dns_header *header, char  Line 1537  size_t answer_request(struct dns_header *header, char 
                                                           type == T_A ? "4" : "6", &addrlist->addr))                                                            type == T_A ? "4" : "6", &addrlist->addr))
                                     anscount++;                                      anscount++;
                                 }                                  }
                          }                            }
                       }                        }
                                       
                   if (!dryrun && !gotit)                    if (!dryrun && !gotit)
Line 1964  size_t answer_request(struct dns_header *header, char  Line 1567  size_t answer_request(struct dns_header *header, char 
                       crecp = save;                        crecp = save;
                     }                      }
   
                  /* If the client asked for DNSSEC and we can't provide RRSIGs, either                  /* If the client asked for DNSSEC  don't use cached data. */
                     because we've not doing DNSSEC or the cached answer is signed by negative,                  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
                     don't answer from the cache, forward instead. */ 
                  if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd) 
                    { 
                      if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))) 
                        crecp = NULL; 
#ifdef HAVE_DNSSEC 
                      else if (crecp->flags & F_DNSSECOK) 
                        { 
                          /* We're returning validated data, need to return the RRSIG too. */ 
                          struct crec *rr_crec = NULL; 
                          int sigtype = type; 
                          /* The signature may have expired even though the data is still in cache,  
                             forward instead of answering from cache if so. */ 
                          int gotsig = 0; 
                           
                          if (crecp->flags & F_CNAME) 
                            sigtype = T_CNAME; 
                           
                          while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY))) 
                            { 
                              if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN) 
                                { 
                                  char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL); 
                                  gotsig = 1; 
                                   
                                  if (!dryrun &&  
                                      add_resource_record(header, limit, &trunc, nameoffset, &ansp,  
                                                          rr_crec->ttd - now, &nameoffset, 
                                                          T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata)) 
                                    anscount++; 
                                } 
                            } 
                           
                          if (!gotsig) 
                            crecp = NULL; 
                        } 
#endif 
                    }             
 
                  if (crecp) 
                     do                      do
                       {                         { 
                         /* don't answer wildcard queries with data not from /etc/hosts                          /* don't answer wildcard queries with data not from /etc/hosts
Line 2039  size_t answer_request(struct dns_header *header, char  Line 1602  size_t answer_request(struct dns_header *header, char 
                                                   
                         if (crecp->flags & F_NEG)                          if (crecp->flags & F_NEG)
                           {                            {
                            /* We don't cache NSEC records, so if a DNSSEC-validated negative answer                            ans = 1;
                               is cached and the client wants DNSSEC, forward rather than answering from the cache */                            auth = 0;
                            if (!sec_reqd || !(crecp->flags & F_DNSSECOK))                            if (crecp->flags & F_NXDOMAIN)
                              {                              nxdomain = 1;
                                ans = 1;                            if (!dryrun)
                                auth = 0;                              log_query(crecp->flags, name, NULL, NULL);
                                if (crecp->flags & F_NXDOMAIN) 
                                  nxdomain = 1; 
                                if (!dryrun) 
                                  log_query(crecp->flags, name, NULL, NULL); 
                              } 
                           }                            }
                         else                           else 
                           {                            {
Line 2287  size_t answer_request(struct dns_header *header, char  Line 1845  size_t answer_request(struct dns_header *header, char 
   
   len = ansp - (unsigned char *)header;    len = ansp - (unsigned char *)header;
       
     /* Advertise our packet size limit in our reply */
   if (have_pseudoheader)    if (have_pseudoheader)
    len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);    len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
       
  if (*ad_reqd && sec_data)  if (ad_reqd && sec_data)
     header->hb4 |= HB4_AD;      header->hb4 |= HB4_AD;
   else    else
     header->hb4 &= ~HB4_AD;      header->hb4 &= ~HB4_AD;
       
   return len;    return len;
 }  }
   

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


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