Diff for /embedaddon/dnsmasq/src/cache.c between versions 1.1.1.2 and 1.1.1.4

version 1.1.1.2, 2014/06/15 16:31:38 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2014 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 21  static struct crec *cache_head = NULL, *cache_tail = N Line 21  static struct crec *cache_head = NULL, *cache_tail = N
 static struct crec *dhcp_spare = NULL;  static struct crec *dhcp_spare = NULL;
 #endif  #endif
 static struct crec *new_chain = NULL;  static struct crec *new_chain = NULL;
static int cache_inserted = 0, cache_live_freed = 0, insert_error;static int insert_error;
 static union bigname *big_free = NULL;  static union bigname *big_free = NULL;
 static int bignames_left, hash_size;  static int bignames_left, hash_size;
   
   static void make_non_terminals(struct crec *source);
   static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
                                     time_t now,  unsigned long ttl, unsigned int flags);
   
 /* type->string mapping: this is also used by the name-hash function as a mixing table. */  /* type->string mapping: this is also used by the name-hash function as a mixing table. */
 static const struct {  static const struct {
   unsigned int type;    unsigned int type;
Line 45  static const struct { Line 49  static const struct {
   { 24,  "SIG" },    { 24,  "SIG" },
   { 25,  "KEY" },    { 25,  "KEY" },
   { 28,  "AAAA" },    { 28,  "AAAA" },
     { 29,  "LOC" },
   { 33,  "SRV" },    { 33,  "SRV" },
   { 35,  "NAPTR" },    { 35,  "NAPTR" },
   { 36,  "KX" },    { 36,  "KX" },
Line 57  static const struct { Line 62  static const struct {
   { 47,  "NSEC" },    { 47,  "NSEC" },
   { 48,  "DNSKEY" },    { 48,  "DNSKEY" },
   { 50,  "NSEC3" },    { 50,  "NSEC3" },
     { 51,  "NSEC3PARAM" },
     { 52,  "TLSA" },
     { 53,  "SMIMEA" },
     { 55,  "HIP" },
   { 249, "TKEY" },    { 249, "TKEY" },
   { 250, "TSIG" },    { 250, "TSIG" },
   { 251, "IXFR" },    { 251, "IXFR" },
   { 252, "AXFR" },    { 252, "AXFR" },
   { 253, "MAILB" },    { 253, "MAILB" },
   { 254, "MAILA" },    { 254, "MAILA" },
  { 255, "ANY" }  { 255, "ANY" },
   { 257, "CAA" }
 };  };
   
 static void cache_free(struct crec *crecp);  static void cache_free(struct crec *crecp);
Line 72  static void cache_link(struct crec *crecp); Line 82  static void cache_link(struct crec *crecp);
 static void rehash(int size);  static void rehash(int size);
 static void cache_hash(struct crec *crecp);  static void cache_hash(struct crec *crecp);
   
static unsigned int next_uid(void)void next_uid(struct crec *crecp)
 {  {
   static unsigned int uid = 0;    static unsigned int uid = 0;
   
  uid++;  if (crecp->uid == UID_NONE)
     {
       uid++;
       
  /* uid == 0 used to indicate CNAME to interface name. */      /* uid == 0 used to indicate CNAME to interface name. */
  if (uid == SRC_INTERFACE)      if (uid == UID_NONE)
    uid++;        uid++;
        
  return uid;      crecp->uid = uid;
     }
 }  }
   
 void cache_init(void)  void cache_init(void)
Line 100  void cache_init(void) Line 113  void cache_init(void)
         {          {
           cache_link(crecp);            cache_link(crecp);
           crecp->flags = 0;            crecp->flags = 0;
          crecp->uid = next_uid();          crecp->uid = UID_NONE;
         }          }
     }      }
       
Line 185  static void cache_hash(struct crec *crecp) Line 198  static void cache_hash(struct crec *crecp)
   *up = crecp;    *up = crecp;
 }  }
   
 #ifdef HAVE_DNSSEC  
 static void cache_blockdata_free(struct crec *crecp)  static void cache_blockdata_free(struct crec *crecp)
 {  {
  if (crecp->flags & F_DNSKEY)  if (!(crecp->flags & F_NEG))
     {      {
      if (crecp->flags & F_DS)      if (crecp->flags & F_SRV)
        blockdata_free(crecp->addr.sig.keydata);        blockdata_free(crecp->addr.srv.target);
      else#ifdef HAVE_DNSSEC
       else if (crecp->flags & F_DNSKEY)
         blockdata_free(crecp->addr.key.keydata);          blockdata_free(crecp->addr.key.keydata);
         else if (crecp->flags & F_DS)
           blockdata_free(crecp->addr.ds.keydata);
   #endif
     }      }
   else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))  
     blockdata_free(crecp->addr.ds.keydata);  
 }  }
 #endif  
   
 static void cache_free(struct crec *crecp)  static void cache_free(struct crec *crecp)
 {  {
   crecp->flags &= ~F_FORWARD;    crecp->flags &= ~F_FORWARD;
   crecp->flags &= ~F_REVERSE;    crecp->flags &= ~F_REVERSE;
  crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */  crecp->uid = UID_NONE; /* invalidate CNAMES pointing to this. */
   
   if (cache_tail)    if (cache_tail)
     cache_tail->next = crecp;      cache_tail->next = crecp;
Line 222  static void cache_free(struct crec *crecp) Line 235  static void cache_free(struct crec *crecp)
       crecp->flags &= ~F_BIGNAME;        crecp->flags &= ~F_BIGNAME;
     }      }
   
 #ifdef HAVE_DNSSEC  
   cache_blockdata_free(crecp);    cache_blockdata_free(crecp);
 #endif  
 }      }    
   
 /* insert a new cache entry at the head of the list (youngest entry) */  /* insert a new cache entry at the head of the list (youngest entry) */
Line 265  char *cache_get_name(struct crec *crecp) Line 276  char *cache_get_name(struct crec *crecp)
   
 char *cache_get_cname_target(struct crec *crecp)  char *cache_get_cname_target(struct crec *crecp)
 {  {
  if (crecp->addr.cname.uid != SRC_INTERFACE)  if (crecp->addr.cname.is_name_ptr)
      return crecp->addr.cname.target.name;
   else
     return cache_get_name(crecp->addr.cname.target.cache);      return cache_get_name(crecp->addr.cname.target.cache);
   
   return crecp->addr.cname.target.int_name->name;  
 }  }
   
   
Line 298  struct crec *cache_enumerate(int init) Line 309  struct crec *cache_enumerate(int init)
   
 static int is_outdated_cname_pointer(struct crec *crecp)  static int is_outdated_cname_pointer(struct crec *crecp)
 {  {
  if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_INTERFACE)  if (!(crecp->flags & F_CNAME) || crecp->addr.cname.is_name_ptr)
     return 0;      return 0;
       
   /* NB. record may be reused as DS or DNSKEY, where uid is     /* NB. record may be reused as DS or DNSKEY, where uid is 
      overloaded for something completely different */       overloaded for something completely different */
   if (crecp->addr.cname.target.cache &&     if (crecp->addr.cname.target.cache && 
      (crecp->addr.cname.target.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&      !(crecp->addr.cname.target.cache->flags & (F_DNSKEY | F_DS)) &&
       crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)        crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)
     return 0;      return 0;
       
Line 322  static int is_expired(time_t now, struct crec *crecp) Line 333  static int is_expired(time_t now, struct crec *crecp)
   return 1;    return 1;
 }  }
   
static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
                                     unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
 {  {
   /* Scan and remove old entries.    /* Scan and remove old entries.
      If (flags & F_FORWARD) then remove any forward entries for name and any expired       If (flags & F_FORWARD) then remove any forward entries for name and any expired
Line 331  static int cache_scan_free(char *name, struct all_addr Line 343  static int cache_scan_free(char *name, struct all_addr
      entries in the whole cache.       entries in the whole cache.
      If (flags == 0) remove any expired entries in the whole cache.        If (flags == 0) remove any expired entries in the whole cache. 
   
     In the flags & F_FORWARD case, the return code is valid, and returns zero if the     In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer
     name exists in the cache as a HOSTS or DHCP entry (these are never deleted)     to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
   
      We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>       We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
     so that when we hit an entry which isn't reverse and is immortal, we're done. */     so that when we hit an entry which isn't reverse and is immortal, we're done. 
 
      If we free a crec which is a CNAME target, return the entry and uid in target_crec and target_uid.
      This entry will get re-used with the same name, to preserve CNAMEs. */
     
   struct crec *crecp, **up;    struct crec *crecp, **up;
   
     (void)class;
       
   if (flags & F_FORWARD)    if (flags & F_FORWARD)
     {      {
       for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)        for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
         {          {
           if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))  
             {   
               *up = crecp->hash_next;  
               if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))  
                 {  
                   cache_unlink(crecp);  
                   cache_free(crecp);  
                 }  
               continue;  
             }   
           
           if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))            if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
             {              {
               /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */                /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
              if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||               if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV)) || 
                   (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))                    (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
                 {                  {
                   if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))                    if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                    return 0;                    return crecp;
                   *up = crecp->hash_next;                    *up = crecp->hash_next;
                     /* If this record is for the name we're inserting and is the target
                        of a CNAME record. Make the new record for the same name, in the same
                        crec, with the same uid to avoid breaking the existing CNAME. */
                     if (crecp->uid != UID_NONE)
                       {
                         if (target_crec)
                           *target_crec = crecp;
                         if (target_uid)
                           *target_uid = crecp->uid;
                       }
                   cache_unlink(crecp);                    cache_unlink(crecp);
                   cache_free(crecp);                    cache_free(crecp);
                   continue;                    continue;
                 }                  }
                               
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
              /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also               /* Deletion has to be class-sensitive for DS and DNSKEY */
                 type-covered sensitive for  RRSIG */              if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == class)
              if ((flags & (F_DNSKEY | F_DS)) && 
                  (flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) && 
                  crecp->uid == addr->addr.dnssec.class && 
                  (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||  
                   crecp->addr.sig.type_covered == addr->addr.dnssec.type)) 
                 {                  {
                   if (crecp->flags & F_CONFIG)                    if (crecp->flags & F_CONFIG)
                    return 0;                    return crecp;
                   *up = crecp->hash_next;                    *up = crecp->hash_next;
                   cache_unlink(crecp);                    cache_unlink(crecp);
                   cache_free(crecp);                    cache_free(crecp);
Line 386  static int cache_scan_free(char *name, struct all_addr Line 397  static int cache_scan_free(char *name, struct all_addr
                 }                  }
 #endif  #endif
             }              }
   
             if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
               { 
                 *up = crecp->hash_next;
                 if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                   {
                     cache_unlink(crecp);
                     cache_free(crecp);
                   }
                 continue;
               } 
             
           up = &crecp->hash_next;            up = &crecp->hash_next;
         }          }
     }      }
   else    else
     {      {
       int i;        int i;
 #ifdef HAVE_IPV6  
       int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;        int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
#else
      int addrlen = INADDRSZ; 
#endif  
       for (i = 0; i < hash_size; i++)        for (i = 0; i < hash_size; i++)
         for (crecp = hash_table[i], up = &hash_table[i];           for (crecp = hash_table[i], up = &hash_table[i]; 
              crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));               crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
Line 413  static int cache_scan_free(char *name, struct all_addr Line 433  static int cache_scan_free(char *name, struct all_addr
           else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&            else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
                    (flags & crecp->flags & F_REVERSE) &&                      (flags & crecp->flags & F_REVERSE) && 
                    (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&                     (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
                   memcmp(&crecp->addr.addr, addr, addrlen) == 0)                   memcmp(&crecp->addr, addr, addrlen) == 0)
             {              {
               *up = crecp->hash_next;                *up = crecp->hash_next;
               cache_unlink(crecp);                cache_unlink(crecp);
Line 423  static int cache_scan_free(char *name, struct all_addr Line 443  static int cache_scan_free(char *name, struct all_addr
             up = &crecp->hash_next;              up = &crecp->hash_next;
     }      }
       
  return 1;  return NULL;
 }  }
   
 /* Note: The normal calling sequence is  /* Note: The normal calling sequence is
Line 448  void cache_start_insert(void) Line 468  void cache_start_insert(void)
   new_chain = NULL;    new_chain = NULL;
   insert_error = 0;    insert_error = 0;
 }  }
    
 struct crec *cache_insert(char *name, struct all_addr *addr,   
                           time_t now,  unsigned long ttl, unsigned short flags)  
 {  
   struct crec *new;  
   union bigname *big_name = NULL;  
   int freed_all = flags & F_REVERSE;  
   int free_avail = 0;  
   
  /* Don't log DNSSEC records here, done elsewhere */struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
  if (flags & (F_IPV4 | F_IPV6 | F_CNAME))                          time_t now,  unsigned long ttl, unsigned int flags)
 {
 #ifdef HAVE_DNSSEC
   if (flags & (F_DNSKEY | F_DS)) 
     {      {
         /* The DNSSEC validation process works by getting needed records into the
            cache, then retrying the validation until they are all in place.
            This can be messed up by very short TTLs, and _really_ messed up by
            zero TTLs, so we force the TTL to be at least long enough to do a validation.
            Ideally, we should use some kind of reference counting so that records are
            locked until the validation that asked for them is complete, but this
            is much easier, and just as effective. */
         if (ttl < DNSSEC_MIN_TTL)
           ttl = DNSSEC_MIN_TTL;
       }
     else
   #endif
       {
         /* Don't log DNSSEC records here, done elsewhere */
       log_query(flags | F_UPSTREAM, name, addr, NULL);        log_query(flags | F_UPSTREAM, name, addr, NULL);
       /* Don;t mess with TTL for DNSSEC records. */  
       if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)        if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
         ttl = daemon->max_cache_ttl;          ttl = daemon->max_cache_ttl;
    }      if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
         ttl = daemon->min_cache_ttl;
     }   
   
   return really_insert(name, addr, class, now, ttl, flags);
 }
   
   
   static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
                                     time_t now,  unsigned long ttl, unsigned int flags)
   {
     struct crec *new, *target_crec = NULL;
     union bigname *big_name = NULL;
     int freed_all = flags & F_REVERSE;
     int free_avail = 0;
     unsigned int target_uid;
     
   /* if previous insertion failed give up now. */    /* if previous insertion failed give up now. */
   if (insert_error)    if (insert_error)
     return NULL;      return NULL;
   
     /* we don't cache zero-TTL records. */
     if (ttl == 0)
       {
         insert_error = 1;
         return NULL;
       }
       
   /* First remove any expired entries and entries for the name/address we    /* First remove any expired entries and entries for the name/address we
     are currently inserting. Fail if we attempt to delete a name from     are currently inserting. */
     /etc/hosts or DHCP. */  if ((new = cache_scan_free(name, addr, class, now, flags, &target_crec, &target_uid)))
  if (!cache_scan_free(name, addr, now, flags)) 
     {      {
         /* We're trying to insert a record over one from 
            /etc/hosts or DHCP, or other config. If the 
            existing record is for an A or AAAA or CNAME and
            the record we're trying to insert is the same, 
            just drop the insert, but don't error the whole process. */
         if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD) && addr)
           {
             if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
                 new->addr.addr4.s_addr == addr->addr4.s_addr)
               return new;
             else if ((flags & F_IPV6) && (new->flags & F_IPV6) &&
                      IN6_ARE_ADDR_EQUAL(&new->addr.addr6, &addr->addr6))
               return new;
           }
   
       insert_error = 1;        insert_error = 1;
       return NULL;        return NULL;
     }      }
       
   /* Now get a cache entry from the end of the LRU list */    /* Now get a cache entry from the end of the LRU list */
  while (1) {  if (!target_crec)
    if (!(new = cache_tail)) /* no entries left - cache is too small, bail */    while (1) {
      {      if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
        insert_error = 1;        {
        return NULL;          insert_error = 1;
      }          return NULL;
            }
    /* End of LRU list is still in use: if we didn't scan all the hash      
       chains for expired entries do that now. If we already tried that      /* Free entry at end of LRU list, use it. */
       then it's time to start spilling things. */      if (!(new->flags & (F_FORWARD | F_REVERSE)))
            break;
    if (new->flags & (F_FORWARD | F_REVERSE)) 
       
        /* If free_avail set, we believe that an entry has been freed. 
           Bugs have been known to make this not true, resulting in 
           a tight loop here. If that happens, abandon the 
           insert. Once in this state, all inserts will probably fail. */ 
        if (free_avail) 
          { 
            static int warned = 0; 
            if (!warned) 
              { 
                my_syslog(LOG_ERR, _("Internal error in cache.")); 
                warned = 1; 
              } 
            insert_error = 1; 
            return NULL; 
          } 
                 
        if (freed_all) 
          { 
            struct all_addr free_addr = new->addr.addr;; 
   
#ifdef HAVE_DNSSEC      /* End of LRU list is still in use: if we didn't scan all the hash
            /* For DNSSEC records, addr holds class and type_covered for RRSIG */         chains for expired entries do that now. If we already tried that
            if (new->flags & (F_DS | F_DNSKEY))         then it's time to start spilling things. */
              {      
                free_addr.addr.dnssec.class = new->uid;      /* If free_avail set, we believe that an entry has been freed.
                if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))         Bugs have been known to make this not true, resulting in
                  free_addr.addr.dnssec.type = new->addr.sig.type_covered;         a tight loop here. If that happens, abandon the
              }         insert. Once in this state, all inserts will probably fail. */
#endif      if (free_avail)
                    {
            free_avail = 1; /* Must be free space now. */          static int warned = 0;
            cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);          if (!warned)
            cache_live_freed++;            {
          }              my_syslog(LOG_ERR, _("Internal error in cache."));
        else              warned = 1;
          {            }
            cache_scan_free(NULL, NULL, now, 0);          insert_error = 1;
            freed_all = 1;          return NULL;
          }        }
        continue;      
      }      if (freed_all)
         {
    /* Check if we need to and can allocate extra memory for a long name.          /* For DNSSEC records, uid holds class. */
       If that fails, give up now, always succeed for DNSSEC records. */          free_avail = 1; /* Must be free space now. */
    if (name && (strlen(name) > SMALLDNAME-1))          cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
      {          daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
        if (big_free)        }
          {       else
            big_name = big_free;        {
            big_free = big_free->next;          cache_scan_free(NULL, NULL, class, now, 0, NULL, NULL);
          }          freed_all = 1;
        else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||        }
                 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))    }
          {      
            insert_error = 1;  /* Check if we need to and can allocate extra memory for a long name.
            return NULL;     If that fails, give up now, always succeed for DNSSEC records. */
          }  if (name && (strlen(name) > SMALLDNAME-1))
        else if (bignames_left != 0)    {
          bignames_left--;      if (big_free)
                { 
      }          big_name = big_free;
           big_free = big_free->next;
         }
       else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
                !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
         {
           insert_error = 1;
           return NULL;
         }
       else if (bignames_left != 0)
         bignames_left--;
       
     }
   
    /* Got the rest: finally grab entry. */  /* If we freed a cache entry for our name which was a CNAME target, use that.
    cache_unlink(new);     and preserve the uid, so that existing CNAMES are not broken. */
    break;  if (target_crec)
  }    {
       new = target_crec;
       new->uid = target_uid;
     }
       
     /* Got the rest: finally grab entry. */
     cache_unlink(new);
     
   new->flags = flags;    new->flags = flags;
   if (big_name)    if (big_name)
     {      {
Line 572  struct crec *cache_insert(char *name, struct all_addr  Line 633  struct crec *cache_insert(char *name, struct all_addr 
   else    else
     *cache_get_name(new) = 0;      *cache_get_name(new) = 0;
   
   if (addr)  
     {  
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
      if (flags & (F_DS | F_DNSKEY))  if (flags & (F_DS | F_DNSKEY))
        new->uid = addr->addr.dnssec.class;    new->uid = class;
      else 
 #endif  #endif
         new->addr.addr = *addr;   
     }  
   
     if (addr)
       new->addr = *addr;  
   
   new->ttd = now + (time_t)ttl;    new->ttd = now + (time_t)ttl;
   new->next = new_chain;    new->next = new_chain;
   new_chain = new;    new_chain = new;
Line 605  void cache_end_insert(void) Line 664  void cache_end_insert(void)
         {          {
           cache_hash(new_chain);            cache_hash(new_chain);
           cache_link(new_chain);            cache_link(new_chain);
          cache_inserted++;          daemon->metrics[METRIC_DNS_CACHE_INSERTED]++;
 
           /* If we're a child process, send this cache entry up the pipe to the master.
              The marshalling process is rather nasty. */
           if (daemon->pipe_to_parent != -1)
             {
               char *name = cache_get_name(new_chain);
               ssize_t m = strlen(name);
               unsigned int flags = new_chain->flags;
 #ifdef HAVE_DNSSEC
               u16 class = new_chain->uid;
 #endif
               
               read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
               read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0);
               read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
               read_write(daemon->pipe_to_parent, (unsigned  char *)&flags, sizeof(flags), 0);
 
               if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
                 read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
               if (flags & F_SRV)
                 {
                   /* A negative SRV entry is possible and has no data, obviously. */
                   if (!(flags & F_NEG))
                     blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent);
                 }
 #ifdef HAVE_DNSSEC
               if (flags & F_DNSKEY)
                 {
                   read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
                   blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
                 }
               else if (flags & F_DS)
                 {
                   read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
                   /* A negative DS entry is possible and has no data, obviously. */
                   if (!(flags & F_NEG))
                     blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent);
                 }
 #endif
             }
         }          }
         
       new_chain = tmp;        new_chain = tmp;
     }      }
   
     /* signal end of cache insert in master process */
     if (daemon->pipe_to_parent != -1)
       {
         ssize_t m = -1;
         read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
       }
         
   new_chain = NULL;    new_chain = NULL;
 }  }
   
   
   /* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */
   int cache_recv_insert(time_t now, int fd)
   {
     ssize_t m;
     union all_addr addr;
     unsigned long ttl;
     time_t ttd;
     unsigned int flags;
     struct crec *crecp = NULL;
     
     cache_start_insert();
     
     while(1)
       {
    
         if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
           return 0;
         
         if (m == -1)
           {
             cache_end_insert();
             return 1;
           }
   
         if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) ||
             !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) ||
             !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1))
           return 0;
   
         daemon->namebuff[m] = 0;
   
         ttl = difftime(ttd, now);
         
         if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
           {
             unsigned short class = C_IN;
   
             if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
               return 0;
   
             if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen)))
               return 0;
           
   #ifdef HAVE_DNSSEC
              if (flags & F_DNSKEY)
                {
                  if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
                      !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
                    return 0;
                }
              else  if (flags & F_DS)
                {
                   if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
                       (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
                     return 0;
                }
   #endif
                  
             crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
           }
         else if (flags & F_CNAME)
           {
             struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
             /* This relies on the fact that the target of a CNAME immediately precedes
                it because of the order of extraction in extract_addresses, and
                the order reversal on the new_chain. */
             if (newc)
               {
                  newc->addr.cname.is_name_ptr = 0;
                  
                  if (!crecp)
                    newc->addr.cname.target.cache = NULL;
                  else
                   {
                     next_uid(crecp);
                     newc->addr.cname.target.cache = crecp;
                     newc->addr.cname.uid = crecp->uid;
                   }
               }
           }
       }
   }
           
   int cache_find_non_terminal(char *name, time_t now)
   {
     struct crec *crecp;
   
     for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
       if (!is_outdated_cname_pointer(crecp) &&
           !is_expired(now, crecp) &&
           (crecp->flags & F_FORWARD) &&
           !(crecp->flags & F_NXDOMAIN) && 
           hostname_isequal(name, cache_get_name(crecp)))
         return 1;
   
     return 0;
   }
   
 struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)  struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
 {  {
   struct crec *ans;    struct crec *ans;
Line 626  struct crec *cache_find_by_name(struct crec *crecp, ch Line 833  struct crec *cache_find_by_name(struct crec *crecp, ch
       /* first search, look for relevant entries and push to top of list        /* first search, look for relevant entries and push to top of list
          also free anything which has expired */           also free anything which has expired */
       struct crec *next, **up, **insert = NULL, **chainp = &ans;        struct crec *next, **up, **insert = NULL, **chainp = &ans;
      unsigned short ins_flags = 0;      unsigned int ins_flags = 0;
               
       for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)        for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
         {          {
Line 635  struct crec *cache_find_by_name(struct crec *crecp, ch Line 842  struct crec *cache_find_by_name(struct crec *crecp, ch
           if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))            if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
             {              {
               if ((crecp->flags & F_FORWARD) &&                 if ((crecp->flags & F_FORWARD) && 
 #ifdef HAVE_DNSSEC  
                   ((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&  
 #endif  
                   (crecp->flags & prot) &&                    (crecp->flags & prot) &&
                   hostname_isequal(cache_get_name(crecp), name))                    hostname_isequal(cache_get_name(crecp), name))
                 {                  {
Line 695  struct crec *cache_find_by_name(struct crec *crecp, ch Line 899  struct crec *cache_find_by_name(struct crec *crecp, ch
   
   if (ans &&     if (ans && 
       (ans->flags & F_FORWARD) &&        (ans->flags & F_FORWARD) &&
 #ifdef HAVE_DNSSEC  
       ((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&  
 #endif  
       (ans->flags & prot) &&             (ans->flags & prot) &&     
       hostname_isequal(cache_get_name(ans), name))        hostname_isequal(cache_get_name(ans), name))
     return ans;      return ans;
Line 705  struct crec *cache_find_by_name(struct crec *crecp, ch Line 906  struct crec *cache_find_by_name(struct crec *crecp, ch
   return NULL;    return NULL;
 }  }
   
struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr, struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr, 
                                 time_t now, unsigned int prot)                                  time_t now, unsigned int prot)
 {  {
   struct crec *ans;    struct crec *ans;
 #ifdef HAVE_IPV6  
   int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;    int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
 #else  
   int addrlen = INADDRSZ;  
 #endif  
       
   if (crecp) /* iterating */    if (crecp) /* iterating */
     ans = crecp->next;      ans = crecp->next;
Line 733  struct crec *cache_find_by_addr(struct crec *crecp, st Line 930  struct crec *cache_find_by_addr(struct crec *crecp, st
            if (!is_expired(now, crecp))             if (!is_expired(now, crecp))
              {                     {      
                if ((crecp->flags & prot) &&                 if ((crecp->flags & prot) &&
                   memcmp(&crecp->addr.addr, addr, addrlen) == 0)                   memcmp(&crecp->addr, addr, addrlen) == 0)
                  {                             {          
                    if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))                     if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                      {                       {
Line 764  struct crec *cache_find_by_addr(struct crec *crecp, st Line 961  struct crec *cache_find_by_addr(struct crec *crecp, st
   if (ans &&     if (ans && 
       (ans->flags & F_REVERSE) &&        (ans->flags & F_REVERSE) &&
       (ans->flags & prot) &&        (ans->flags & prot) &&
      memcmp(&ans->addr.addr, addr, addrlen) == 0)      memcmp(&ans->addr, addr, addrlen) == 0)
     return ans;      return ans;
       
   return NULL;    return NULL;
 }  }
   
static void add_hosts_cname(struct crec *target)static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen, 
{ 
  struct crec *crec; 
  struct cname *a; 
   
  for (a = daemon->cnames; a; a = a->next) 
    if (hostname_isequal(cache_get_name(target), a->target) && 
        (crec = whine_malloc(sizeof(struct crec)))) 
      { 
        crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME; 
        crec->name.namep = a->alias; 
        crec->addr.cname.target.cache = target; 
        crec->addr.cname.uid = target->uid; 
        crec->uid = next_uid(); 
        cache_hash(crec); 
        add_hosts_cname(crec); /* handle chains */ 
      } 
} 
   
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,  
                             unsigned int index, struct crec **rhash, int hashsz)                              unsigned int index, struct crec **rhash, int hashsz)
 {  {
   struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));    struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
  int i, nameexists = 0;  int i;
   unsigned int j;     unsigned int j; 
   
   /* Remove duplicates in hosts files. */    /* Remove duplicates in hosts files. */
  if (lookup && (lookup->flags & F_HOSTS))  if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
     {      {
      nameexists = 1;      free(cache);
      if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)      return;
        { 
          free(cache); 
          return; 
        } 
     }      }
      
   /* Ensure there is only one address -> name mapping (first one trumps)     /* Ensure there is only one address -> name mapping (first one trumps) 
      We do this by steam here, The entries are kept in hash chains, linked       We do this by steam here, The entries are kept in hash chains, linked
      by ->next (which is unused at this point) held in hash buckets in       by ->next (which is unused at this point) held in hash buckets in
Line 817  static void add_hosts_entry(struct crec *cache, struct Line 991  static void add_hosts_entry(struct crec *cache, struct
      Only insert each unique address once into this hashing structure.       Only insert each unique address once into this hashing structure.
   
      This complexity avoids O(n^2) divergent CPU use whilst reading       This complexity avoids O(n^2) divergent CPU use whilst reading
     large (10000 entry) hosts files. */     large (10000 entry) hosts files. 
 
      Note that we only do this process when bulk-reading hosts files, 
      for incremental reads, rhash is NULL, and we use cache lookups
      instead.
   */
       
  /* hash address */  if (rhash)
  for (j = 0, i = 0; i < addrlen; i++) 
    j = (j*2 +((unsigned char *)addr)[i]) % hashsz; 
   
  for (lookup = rhash[j]; lookup; lookup = lookup->next) 
    if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) && 
        memcmp(&lookup->addr.addr, addr, addrlen) == 0) 
      { 
        cache->flags &= ~F_REVERSE; 
        break; 
      } 
   
  /* maintain address hash chain, insert new unique address */ 
  if (!lookup) 
     {      {
      cache->next = rhash[j];      /* hash address */
      rhash[j] = cache;      for (j = 0, i = 0; i < addrlen; i++)
         j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
       
       for (lookup = rhash[j]; lookup; lookup = lookup->next)
         if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
             memcmp(&lookup->addr, addr, addrlen) == 0)
           {
             cache->flags &= ~F_REVERSE;
             break;
           }
       
       /* maintain address hash chain, insert new unique address */
       if (!lookup)
         {
           cache->next = rhash[j];
           rhash[j] = cache;
         }
     }      }
    else
     {
       /* incremental read, lookup in cache */
       lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
       if (lookup && lookup->flags & F_HOSTS)
         cache->flags &= ~F_REVERSE;
     }
 
   cache->uid = index;    cache->uid = index;
  memcpy(&cache->addr.addr, addr, addrlen);    memcpy(&cache->addr, addr, addrlen);  
   cache_hash(cache);    cache_hash(cache);
    make_non_terminals(cache);
  /* don't need to do alias stuff for second and subsequent addresses. */ 
  if (!nameexists) 
    add_hosts_cname(cache); 
 }  }
   
 static int eatspace(FILE *f)  static int eatspace(FILE *f)
Line 867  static int eatspace(FILE *f) Line 1053  static int eatspace(FILE *f)
         }          }
   
       if (c == '\n')        if (c == '\n')
        nl = 1;        nl++;
     }      }
 }  }
                     
Line 878  static int gettok(FILE *f, char *token) Line 1064  static int gettok(FILE *f, char *token)
   while (1)    while (1)
     {      {
       if ((c = getc(f)) == EOF)        if ((c = getc(f)) == EOF)
        return (count == 0) ? EOF : 1;        return (count == 0) ? -1 : 1;
   
       if (isspace(c) || c == '#')        if (isspace(c) || c == '#')
         {          {
Line 894  static int gettok(FILE *f, char *token) Line 1080  static int gettok(FILE *f, char *token)
     }      }
 }  }
   
static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
 {    {  
   FILE *f = fopen(filename, "r");    FILE *f = fopen(filename, "r");
   char *token = daemon->namebuff, *domain_suffix = NULL;    char *token = daemon->namebuff, *domain_suffix = NULL;
  int addr_count = 0, name_count = cache_size, lineno = 0;  int addr_count = 0, name_count = cache_size, lineno = 1;
  unsigned short flags = 0;  unsigned int flags = 0;
  struct all_addr addr;  union all_addr addr;
   int atnl, addrlen = 0;    int atnl, addrlen = 0;
   
   if (!f)    if (!f)
     {      {
       my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));        my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
      return 0;      return cache_size;
     }      }
       
  eatspace(f);  lineno += eatspace(f);
       
  while ((atnl = gettok(f, token)) != EOF)  while ((atnl = gettok(f, token)) != -1)
     {      {
       lineno++;  
         
       if (inet_pton(AF_INET, token, &addr) > 0)        if (inet_pton(AF_INET, token, &addr) > 0)
         {          {
           flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;            flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
           addrlen = INADDRSZ;            addrlen = INADDRSZ;
          domain_suffix = get_domain(addr.addr.addr4);          domain_suffix = get_domain(addr.addr4);
         }          }
 #ifdef HAVE_IPV6  
       else if (inet_pton(AF_INET6, token, &addr) > 0)        else if (inet_pton(AF_INET6, token, &addr) > 0)
         {          {
           flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;            flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
           addrlen = IN6ADDRSZ;            addrlen = IN6ADDRSZ;
          domain_suffix = get_domain6(&addr.addr.addr6);          domain_suffix = get_domain6(&addr.addr6);
         }          }
 #endif  
       else        else
         {          {
           my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);             my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); 
           while (atnl == 0)            while (atnl == 0)
             atnl = gettok(f, token);              atnl = gettok(f, token);
             lineno += atnl;
           continue;            continue;
         }          }
               
       addr_count++;        addr_count++;
               
       /* rehash every 1000 names. */        /* rehash every 1000 names. */
      if ((name_count - cache_size) > 1000)      if (rhash && ((name_count - cache_size) > 1000))
         {          {
           rehash(name_count);            rehash(name_count);
           cache_size = name_count;            cache_size = name_count;
Line 952  static int read_hostsfile(char *filename, unsigned int Line 1135  static int read_hostsfile(char *filename, unsigned int
           int fqdn, nomem;            int fqdn, nomem;
           char *canon;            char *canon;
                       
          if ((atnl = gettok(f, token)) == EOF)          if ((atnl = gettok(f, token)) == -1)
             break;              break;
   
           fqdn = !!strchr(token, '.');            fqdn = !!strchr(token, '.');
Line 961  static int read_hostsfile(char *filename, unsigned int Line 1144  static int read_hostsfile(char *filename, unsigned int
             {              {
               /* If set, add a version of the name with a default domain appended */                /* If set, add a version of the name with a default domain appended */
               if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&                 if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn && 
                  (cache = whine_malloc(sizeof(struct crec) +                   (cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 2 + strlen(domain_suffix))))
                                        strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME))) 
                 {                  {
                   strcpy(cache->name.sname, canon);                    strcpy(cache->name.sname, canon);
                   strcat(cache->name.sname, ".");                    strcat(cache->name.sname, ".");
                   strcat(cache->name.sname, domain_suffix);                    strcat(cache->name.sname, domain_suffix);
                   cache->flags = flags;                    cache->flags = flags;
                     cache->ttd = daemon->local_ttl;
                   add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);                    add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                   name_count++;                    name_count++;
                 }                  }
              if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))              if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
                 {                  {
                   strcpy(cache->name.sname, canon);                    strcpy(cache->name.sname, canon);
                   cache->flags = flags;                    cache->flags = flags;
                     cache->ttd = daemon->local_ttl;
                   add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);                    add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                   name_count++;                    name_count++;
                 }                  }
Line 984  static int read_hostsfile(char *filename, unsigned int Line 1168  static int read_hostsfile(char *filename, unsigned int
           else if (!nomem)            else if (!nomem)
             my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);               my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno); 
         }          }
   
         lineno += atnl;
     }       } 
   
   fclose(f);    fclose(f);
   rehash(name_count);  
       
     if (rhash)
       rehash(name_count); 
     
   my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);    my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
       
   return name_count;    return name_count;
Line 1002  void cache_reload(void) Line 1190  void cache_reload(void)
   struct host_record *hr;    struct host_record *hr;
   struct name_list *nl;    struct name_list *nl;
   struct cname *a;    struct cname *a;
     struct crec lrec;
     struct mx_srv_record *mx;
     struct txt_record *txt;
   struct interface_name *intr;    struct interface_name *intr;
     struct ptr_record *ptr;
     struct naptr *naptr;
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
   struct ds_config *ds;    struct ds_config *ds;
 #endif  #endif
   
  cache_inserted = cache_live_freed = 0;  daemon->metrics[METRIC_DNS_CACHE_INSERTED] = 0;
   daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED] = 0;
       
   for (i=0; i<hash_size; i++)    for (i=0; i<hash_size; i++)
     for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)      for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
       {        {
 #ifdef HAVE_DNSSEC  
         cache_blockdata_free(cache);          cache_blockdata_free(cache);
#endif
         tmp = cache->hash_next;          tmp = cache->hash_next;
         if (cache->flags & (F_HOSTS | F_CONFIG))          if (cache->flags & (F_HOSTS | F_CONFIG))
           {            {
Line 1035  void cache_reload(void) Line 1228  void cache_reload(void)
           up = &cache->hash_next;            up = &cache->hash_next;
       }        }
       
  /* Add CNAMEs to interface_names to the cache */  /* Add locally-configured CNAMEs to the cache */
   for (a = daemon->cnames; a; a = a->next)    for (a = daemon->cnames; a; a = a->next)
    for (intr = daemon->int_names; intr; intr = intr->next)    if (a->alias[1] != '*' &&
      if (hostname_isequal(a->target, intr->name) &&        ((cache = whine_malloc(SIZEOF_POINTER_CREC))))
          ((cache = whine_malloc(sizeof(struct crec)))))      {
        {        cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
          cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;        cache->ttd = a->ttl;
          cache->name.namep = a->alias;        cache->name.namep = a->alias;
          cache->addr.cname.target.int_name = intr;        cache->addr.cname.target.name = a->target;
          cache->addr.cname.uid = SRC_INTERFACE;        cache->addr.cname.is_name_ptr = 1;
          cache->uid = next_uid();        cache->uid = UID_NONE;
          cache_hash(cache);        cache_hash(cache);
          add_hosts_cname(cache); /* handle chains */        make_non_terminals(cache);
        }      }
  
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
   for (ds = daemon->ds; ds; ds = ds->next)    for (ds = daemon->ds; ds; ds = ds->next)
    if ((cache = whine_malloc(sizeof(struct crec))) &&    if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) &&
         (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))          (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
       {        {
         cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;          cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
           cache->ttd = daemon->local_ttl;
         cache->name.namep = ds->name;          cache->name.namep = ds->name;
         cache->addr.ds.keylen = ds->digestlen;          cache->addr.ds.keylen = ds->digestlen;
         cache->addr.ds.algo = ds->algo;          cache->addr.ds.algo = ds->algo;
Line 1063  void cache_reload(void) Line 1257  void cache_reload(void)
         cache->addr.ds.digest = ds->digest_type;          cache->addr.ds.digest = ds->digest_type;
         cache->uid = ds->class;          cache->uid = ds->class;
         cache_hash(cache);          cache_hash(cache);
           make_non_terminals(cache);
       }        }
 #endif  #endif
       
Line 1076  void cache_reload(void) Line 1271  void cache_reload(void)
   for (hr = daemon->host_records; hr; hr = hr->next)    for (hr = daemon->host_records; hr; hr = hr->next)
     for (nl = hr->names; nl; nl = nl->next)      for (nl = hr->names; nl; nl = nl->next)
       {        {
        if (hr->addr.s_addr != 0 &&        if ((hr->flags & HR_4) &&
            (cache = whine_malloc(sizeof(struct crec))))            (cache = whine_malloc(SIZEOF_POINTER_CREC)))
           {            {
             cache->name.namep = nl->name;              cache->name.namep = nl->name;
               cache->ttd = hr->ttl;
             cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;              cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
            add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);            add_hosts_entry(cache, (union all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
           }            }
#ifdef HAVE_IPV6
        if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&        if ((hr->flags & HR_6) &&
            (cache = whine_malloc(sizeof(struct crec))))            (cache = whine_malloc(SIZEOF_POINTER_CREC)))
           {            {
             cache->name.namep = nl->name;              cache->name.namep = nl->name;
               cache->ttd = hr->ttl;
             cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;              cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
            add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);            add_hosts_entry(cache, (union all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
           }            }
 #endif  
       }        }
                   
   if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)    if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
     {      {
       if (daemon->cachesize > 0)        if (daemon->cachesize > 0)
         my_syslog(LOG_INFO, _("cleared cache"));          my_syslog(LOG_INFO, _("cleared cache"));
       return;  
     }      }
      else
  if (!option_bool(OPT_NO_HOSTS))    {
    total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);      if (!option_bool(OPT_NO_HOSTS))
                   total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
  daemon->addn_hosts = expand_filelist(daemon->addn_hosts);      
  for (ah = daemon->addn_hosts; ah; ah = ah->next)      daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
    if (!(ah->flags & AH_INACTIVE))      for (ah = daemon->addn_hosts; ah; ah = ah->next)
      total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);        if (!(ah->flags & AH_INACTIVE))
           total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
     }
   
   /* Make non-terminal records for all locally-define RRs */
   lrec.flags = F_FORWARD | F_CONFIG | F_NAMEP | F_IMMORTAL;
   
   for (txt = daemon->txt; txt; txt = txt->next)
     {
       lrec.name.namep = txt->name;
       make_non_terminals(&lrec);
     }
 
   for (naptr = daemon->naptr; naptr; naptr = naptr->next)
     {
       lrec.name.namep = naptr->name;
       make_non_terminals(&lrec);
     }
 
   for (mx = daemon->mxnames; mx; mx = mx->next)
     {
       lrec.name.namep = mx->name;
       make_non_terminals(&lrec);
     }
 
   for (intr = daemon->int_names; intr; intr = intr->next)
     {
       lrec.name.namep = intr->name;
       make_non_terminals(&lrec);
     }
   
   for (ptr = daemon->ptr; ptr; ptr = ptr->next)
     {
       lrec.name.namep = ptr->name;
       make_non_terminals(&lrec);
     }
   
 #ifdef HAVE_INOTIFY
   set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
 #endif
   
 }   } 
   
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
Line 1118  struct in_addr a_record_from_hosts(char *name, time_t  Line 1353  struct in_addr a_record_from_hosts(char *name, time_t 
       
   while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))    while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
     if (crecp->flags & F_HOSTS)      if (crecp->flags & F_HOSTS)
      return *(struct in_addr *)&crecp->addr;      return crecp->addr.addr4;
   
   my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);    my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
       
Line 1143  void cache_unhash_dhcp(void) Line 1378  void cache_unhash_dhcp(void)
         up = &cache->hash_next;          up = &cache->hash_next;
 }  }
   
 static void add_dhcp_cname(struct crec *target, time_t ttd)  
 {  
   struct crec *aliasc;  
   struct cname *a;  
     
   for (a = daemon->cnames; a; a = a->next)  
     if (hostname_isequal(cache_get_name(target), a->target))  
       {  
         if ((aliasc = dhcp_spare))  
           dhcp_spare = dhcp_spare->next;  
         else /* need new one */  
           aliasc = whine_malloc(sizeof(struct crec));  
           
         if (aliasc)  
           {  
             aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;  
             if (ttd == 0)  
               aliasc->flags |= F_IMMORTAL;  
             else  
               aliasc->ttd = ttd;  
             aliasc->name.namep = a->alias;  
             aliasc->addr.cname.target.cache = target;  
             aliasc->addr.cname.uid = target->uid;  
             aliasc->uid = next_uid();  
             cache_hash(aliasc);  
             add_dhcp_cname(aliasc, ttd);  
           }  
       }  
 }  
   
 void cache_add_dhcp_entry(char *host_name, int prot,  void cache_add_dhcp_entry(char *host_name, int prot,
                          struct all_addr *host_address, time_t ttd)                           union all_addr *host_address, time_t ttd) 
 {  {
   struct crec *crec = NULL, *fail_crec = NULL;    struct crec *crec = NULL, *fail_crec = NULL;
  unsigned short flags = F_IPV4;  unsigned int flags = F_IPV4;
   int in_hosts = 0;    int in_hosts = 0;
   size_t addrlen = sizeof(struct in_addr);    size_t addrlen = sizeof(struct in_addr);
   
 #ifdef HAVE_IPV6  
   if (prot == AF_INET6)    if (prot == AF_INET6)
     {      {
       flags = F_IPV6;        flags = F_IPV6;
       addrlen = sizeof(struct in6_addr);        addrlen = sizeof(struct in6_addr);
     }      }
 #endif  
       
   inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);    inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
       
Line 1200  void cache_add_dhcp_entry(char *host_name, int prot, Line 1403  void cache_add_dhcp_entry(char *host_name, int prot,
             my_syslog(MS_DHCP | LOG_WARNING,               my_syslog(MS_DHCP | LOG_WARNING, 
                       _("%s is a CNAME, not giving it to the DHCP lease of %s"),                        _("%s is a CNAME, not giving it to the DHCP lease of %s"),
                       host_name, daemon->addrbuff);                        host_name, daemon->addrbuff);
          else if (memcmp(&crec->addr.addr, host_address, addrlen) == 0)          else if (memcmp(&crec->addr, host_address, addrlen) == 0)
             in_hosts = 1;              in_hosts = 1;
           else            else
             fail_crec = crec;              fail_crec = crec;
         }          }
       else if (!(crec->flags & F_DHCP))        else if (!(crec->flags & F_DHCP))
         {          {
          cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));          cache_scan_free(host_name, NULL, C_IN, 0, crec->flags & (flags | F_CNAME | F_FORWARD), NULL, NULL);
           /* scan_free deletes all addresses associated with name */            /* scan_free deletes all addresses associated with name */
           break;            break;
         }          }
Line 1220  void cache_add_dhcp_entry(char *host_name, int prot, Line 1423  void cache_add_dhcp_entry(char *host_name, int prot,
   /* Name in hosts, address doesn't match */    /* Name in hosts, address doesn't match */
   if (fail_crec)    if (fail_crec)
     {      {
      inet_ntop(prot, &fail_crec->addr.addr, daemon->namebuff, MAXDNAME);      inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAME);
       my_syslog(MS_DHCP | LOG_WARNING,         my_syslog(MS_DHCP | LOG_WARNING, 
                 _("not giving name %s to the DHCP lease of %s because "                  _("not giving name %s to the DHCP lease of %s because "
                   "the name exists in %s with address %s"),                     "the name exists in %s with address %s"), 
Line 1229  void cache_add_dhcp_entry(char *host_name, int prot, Line 1432  void cache_add_dhcp_entry(char *host_name, int prot,
       return;        return;
     }           }     
       
  if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))  if ((crec = cache_find_by_addr(NULL, (union all_addr *)host_address, 0, flags)))
     {      {
       if (crec->flags & F_NEG)        if (crec->flags & F_NEG)
         {          {
           flags |= F_REVERSE;            flags |= F_REVERSE;
          cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);          cache_scan_free(NULL, (union all_addr *)host_address, C_IN, 0, flags, NULL, NULL);
         }          }
     }      }
   else    else
Line 1243  void cache_add_dhcp_entry(char *host_name, int prot, Line 1446  void cache_add_dhcp_entry(char *host_name, int prot,
   if ((crec = dhcp_spare))    if ((crec = dhcp_spare))
     dhcp_spare = dhcp_spare->next;      dhcp_spare = dhcp_spare->next;
   else /* need new one */    else /* need new one */
    crec = whine_malloc(sizeof(struct crec));    crec = whine_malloc(SIZEOF_POINTER_CREC);
       
   if (crec) /* malloc may fail */    if (crec) /* malloc may fail */
     {      {
Line 1252  void cache_add_dhcp_entry(char *host_name, int prot, Line 1455  void cache_add_dhcp_entry(char *host_name, int prot,
         crec->flags |= F_IMMORTAL;          crec->flags |= F_IMMORTAL;
       else        else
         crec->ttd = ttd;          crec->ttd = ttd;
      crec->addr.addr = *host_address;      crec->addr = *host_address;
       crec->name.namep = host_name;        crec->name.namep = host_name;
      crec->uid = next_uid();      crec->uid = UID_NONE;
       cache_hash(crec);        cache_hash(crec);
      make_non_terminals(crec);
      add_dhcp_cname(crec, ttd); 
     }      }
 }  }
 #endif  #endif
   
   /* Called when we put a local or DHCP name into the cache.
      Creates empty cache entries for subnames (ie,
      for three.two.one, for two.one and one), without
      F_IPV4 or F_IPV6 or F_CNAME set. These convert
      NXDOMAIN answers to NoData ones. */
   static void make_non_terminals(struct crec *source)
   {
     char *name = cache_get_name(source);
     struct crec *crecp, *tmp, **up;
     int type = F_HOSTS | F_CONFIG;
   #ifdef HAVE_DHCP
     if (source->flags & F_DHCP)
       type = F_DHCP;
   #endif
     
     /* First delete any empty entries for our new real name. Note that
        we only delete empty entries deriving from DHCP for a new DHCP-derived
        entry and vice-versa for HOSTS and CONFIG. This ensures that 
        non-terminals from DHCP go when we reload DHCP and 
        for HOSTS/CONFIG when we re-read. */
     for (up = hash_bucket(name), crecp = *up; crecp; crecp = tmp)
       {
         tmp = crecp->hash_next;
   
         if (!is_outdated_cname_pointer(crecp) &&
             (crecp->flags & F_FORWARD) &&
             (crecp->flags & type) &&
             !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS)) && 
             hostname_isequal(name, cache_get_name(crecp)))
           {
             *up = crecp->hash_next;
   #ifdef HAVE_DHCP
             if (type & F_DHCP)
               {
                 crecp->next = dhcp_spare;
                 dhcp_spare = crecp;
               }
             else
   #endif
               free(crecp);
             break;
           }
         else
            up = &crecp->hash_next;
       }
        
     while ((name = strchr(name, '.')))
       {
         name++;
   
         /* Look for one existing, don't need another */
         for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
           if (!is_outdated_cname_pointer(crecp) &&
               (crecp->flags & F_FORWARD) &&
               (crecp->flags & type) &&
               hostname_isequal(name, cache_get_name(crecp)))
             break;
         
         if (crecp)
           {
             /* If the new name expires later, transfer that time to
                empty non-terminal entry. */
             if (!(crecp->flags & F_IMMORTAL))
               {
                 if (source->flags & F_IMMORTAL)
                   crecp->flags |= F_IMMORTAL;
                 else if (difftime(crecp->ttd, source->ttd) < 0)
                   crecp->ttd = source->ttd;
               }
             continue;
           }
         
   #ifdef HAVE_DHCP
         if ((source->flags & F_DHCP) && dhcp_spare)
           {
             crecp = dhcp_spare;
             dhcp_spare = dhcp_spare->next;
           }
         else
   #endif
           crecp = whine_malloc(SIZEOF_POINTER_CREC);
   
         if (crecp)
           {
             crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
             crecp->ttd = source->ttd;
             crecp->name.namep = name;
             
             cache_hash(crecp);
           }
       }
   }
   
   #ifndef NO_ID
 int cache_make_stat(struct txt_record *t)  int cache_make_stat(struct txt_record *t)
 {   { 
   static char *buff = NULL;    static char *buff = NULL;
Line 1282  int cache_make_stat(struct txt_record *t) Line 1578  int cache_make_stat(struct txt_record *t)
       break;        break;
   
     case TXT_STAT_INSERTS:      case TXT_STAT_INSERTS:
      sprintf(buff+1, "%d", cache_inserted);      sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
       break;        break;
   
     case TXT_STAT_EVICTIONS:      case TXT_STAT_EVICTIONS:
      sprintf(buff+1, "%d", cache_live_freed);      sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]);
       break;        break;
   
     case TXT_STAT_MISSES:      case TXT_STAT_MISSES:
      sprintf(buff+1, "%u", daemon->queries_forwarded);      sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]);
       break;        break;
   
     case TXT_STAT_HITS:      case TXT_STAT_HITS:
      sprintf(buff+1, "%u", daemon->local_answer);      sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
       break;        break;
   
 #ifdef HAVE_AUTH  #ifdef HAVE_AUTH
     case TXT_STAT_AUTH:      case TXT_STAT_AUTH:
      sprintf(buff+1, "%u", daemon->auth_answer);      sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
       break;        break;
 #endif  #endif
   
Line 1326  int cache_make_stat(struct txt_record *t) Line 1622  int cache_make_stat(struct txt_record *t)
                 }                  }
             port = prettyprint_addr(&serv->addr, daemon->addrbuff);              port = prettyprint_addr(&serv->addr, daemon->addrbuff);
             lenp = p++; /* length */              lenp = p++; /* length */
            bytes_avail = (p - buff) + bufflen;            bytes_avail = bufflen - (p - buff );
             bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);              bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
             if (bytes_needed >= bytes_avail)              if (bytes_needed >= bytes_avail)
               {                {
Line 1340  int cache_make_stat(struct txt_record *t) Line 1636  int cache_make_stat(struct txt_record *t)
                 lenp = p - 1;                  lenp = p - 1;
                 buff = new;                  buff = new;
                 bufflen = newlen;                  bufflen = newlen;
                bytes_avail = (p - buff) + bufflen;                bytes_avail =  bufflen - (p - buff );
                 bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);                  bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
               }                }
             *lenp = bytes_needed;              *lenp = bytes_needed;
Line 1357  int cache_make_stat(struct txt_record *t) Line 1653  int cache_make_stat(struct txt_record *t)
   *buff = len;    *buff = len;
   return 1;    return 1;
 }  }
   #endif
   
   /* There can be names in the cache containing control chars, don't 
      mess up logging or open security holes. */
   static char *sanitise(char *name)
   {
     unsigned char *r;
     if (name)
       for (r = (unsigned char *)name; *r; r++)
         if (!isprint((int)*r))
           return "<name unprintable>";
   
     return name;
   }
   
   
 void dump_cache(time_t now)  void dump_cache(time_t now)
 {  {
   struct server *serv, *serv1;    struct server *serv, *serv1;
   char *t = "";  
   
   my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);    my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
   my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),     my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."), 
            daemon->cachesize, cache_live_freed, cache_inserted);            daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
   my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),     my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), 
            daemon->queries_forwarded, daemon->local_answer);            daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
 #ifdef HAVE_AUTH  #ifdef HAVE_AUTH
  my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);  my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
 #endif  #endif
#ifdef HAVE_DNSSEC
   blockdata_report();    blockdata_report();
 #endif  
   
   /* sum counts from different records for same server */    /* sum counts from different records for same server */
   for (serv = daemon->servers; serv; serv = serv->next)    for (serv = daemon->servers; serv; serv = serv->next)
Line 1407  void dump_cache(time_t now) Line 1716  void dump_cache(time_t now)
       for (i=0; i<hash_size; i++)        for (i=0; i<hash_size; i++)
         for (cache = hash_table[i]; cache; cache = cache->hash_next)          for (cache = hash_table[i]; cache; cache = cache->hash_next)
           {            {
               char *t = " ";
             char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);              char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
             *a = 0;              *a = 0;
             if (strlen(n) == 0 && !(cache->flags & F_REVERSE))              if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
               n = "<Root>";                n = "<Root>";
            p += sprintf(p, "%-40.40s ", n);            p += sprintf(p, "%-30.30s ", sanitise(n));
             if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))              if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
              a = cache_get_cname_target(cache);              a = sanitise(cache_get_cname_target(cache));
             else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
               {
                 int targetlen = cache->addr.srv.targetlen;
                 ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
                                       cache->addr.srv.weight, cache->addr.srv.srvport);
 
                 if (targetlen > (40 - len))
                   targetlen = 40 - len;
                 blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
                 a[len + targetlen] = 0;         
               }
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
             else if (cache->flags & F_DS)              else if (cache->flags & F_DS)
               {                {
                if (cache->flags & F_DNSKEY)                if (!(cache->flags & F_NEG))
                  /* RRSIG */ 
                  sprintf(a, "%5u %3u %s", cache->addr.sig.keytag, 
                          cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered)); 
                else if (!(cache->flags & F_NEG)) 
                   sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,                    sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
                           cache->addr.ds.algo, cache->addr.ds.digest);                            cache->addr.ds.algo, cache->addr.ds.digest);
               }                }
Line 1433  void dump_cache(time_t now) Line 1750  void dump_cache(time_t now)
               {                 { 
                 a = daemon->addrbuff;                  a = daemon->addrbuff;
                 if (cache->flags & F_IPV4)                  if (cache->flags & F_IPV4)
                  inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);                  inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
#ifdef HAVE_IPV6 
                 else if (cache->flags & F_IPV6)                  else if (cache->flags & F_IPV6)
                  inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);                  inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
#endif 
               }                }
   
             if (cache->flags & F_IPV4)              if (cache->flags & F_IPV4)
Line 1446  void dump_cache(time_t now) Line 1761  void dump_cache(time_t now)
               t = "6";                t = "6";
             else if (cache->flags & F_CNAME)              else if (cache->flags & F_CNAME)
               t = "C";                t = "C";
               else if (cache->flags & F_SRV)
                 t = "V";
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
             else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))  
               t = "G"; /* DNSKEY and DS set -> RRISG */  
             else if (cache->flags & F_DS)              else if (cache->flags & F_DS)
               t = "S";                t = "S";
             else if (cache->flags & F_DNSKEY)              else if (cache->flags & F_DNSKEY)
               t = "K";                t = "K";
 #endif  #endif
            p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s  ", a, t,            p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s  ", a, t,
                          cache->flags & F_FORWARD ? "F" : " ",                           cache->flags & F_FORWARD ? "F" : " ",
                          cache->flags & F_REVERSE ? "R" : " ",                           cache->flags & F_REVERSE ? "R" : " ",
                          cache->flags & F_IMMORTAL ? "I" : " ",                           cache->flags & F_IMMORTAL ? "I" : " ",
Line 1470  void dump_cache(time_t now) Line 1785  void dump_cache(time_t now)
             /* ctime includes trailing \n - eat it */              /* ctime includes trailing \n - eat it */
             *(p-1) = 0;              *(p-1) = 0;
 #endif  #endif
            my_syslog(LOG_INFO, daemon->namebuff);            my_syslog(LOG_INFO, "%s", daemon->namebuff);
           }            }
     }      }
 }  }
Line 1487  char *record_source(unsigned int index) Line 1802  char *record_source(unsigned int index)
   for (ah = daemon->addn_hosts; ah; ah = ah->next)    for (ah = daemon->addn_hosts; ah; ah = ah->next)
     if (ah->index == index)      if (ah->index == index)
       return ah->fname;        return ah->fname;
  
 #ifdef HAVE_INOTIFY
   for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
      if (ah->index == index)
        return ah->fname;
 #endif
 
   return "<unknown>";    return "<unknown>";
 }  }
   
Line 1507  char *querystr(char *desc, unsigned short type) Line 1828  char *querystr(char *desc, unsigned short type)
         break;          break;
       }        }
   
  len += 3; /* braces, terminator */  if (desc)
  len += strlen(desc);    {
       len += 2; /* braces */
        len += strlen(desc);
     }
   len++; /* terminator */
   
   if (!buff || bufflen < len)    if (!buff || bufflen < len)
     {      {
       if (buff)        if (buff)
Line 1523  char *querystr(char *desc, unsigned short type) Line 1848  char *querystr(char *desc, unsigned short type)
   
   if (buff)    if (buff)
     {      {
      if (types)      if (desc)
        sprintf(buff, "%s[%s]", desc, types);        {
           if (types)
             sprintf(buff, "%s[%s]", desc, types);
           else
             sprintf(buff, "%s[type=%d]", desc, type);
         }
       else        else
        sprintf(buff, "%s[type=%d]", desc, type);        {
           if (types)
             sprintf(buff, "<%s>", types);
           else
             sprintf(buff, "type=%d", type);
         }
     }      }
  
   return buff ? buff : "";    return buff ? buff : "";
 }  }
   
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
 {  {
   char *source, *dest = daemon->addrbuff;    char *source, *dest = daemon->addrbuff;
   char *verb = "is";    char *verb = "is";
Line 1540  void log_query(unsigned int flags, char *name, struct  Line 1875  void log_query(unsigned int flags, char *name, struct 
   if (!option_bool(OPT_LOG))    if (!option_bool(OPT_LOG))
     return;      return;
   
     name = sanitise(name);
   
   if (addr)    if (addr)
     {      {
       if (flags & F_KEYTAG)        if (flags & F_KEYTAG)
        sprintf(daemon->addrbuff, arg, addr->addr.keytag);        sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
      else      else if (flags & F_RCODE)
         {          {
#ifdef HAVE_IPV6          unsigned int rcode = addr->log.rcode;
          inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
                    addr, daemon->addrbuff, ADDRSTRLEN);           if (rcode == SERVFAIL)
#else             dest = "SERVFAIL";
          strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);             else if (rcode == REFUSED)
#endif             dest = "REFUSED";
            else if (rcode == NOTIMP)
              dest = "not implemented";
            else
              sprintf(daemon->addrbuff, "%u", rcode);
         }          }
         else
           inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
                     addr, daemon->addrbuff, ADDRSTRLEN);
         
     }      }
   else    else
     dest = arg;      dest = arg;
Line 1579  void log_query(unsigned int flags, char *name, struct  Line 1924  void log_query(unsigned int flags, char *name, struct 
     }      }
   else if (flags & F_CNAME)    else if (flags & F_CNAME)
     dest = "<CNAME>";      dest = "<CNAME>";
     else if (flags & F_SRV)
       dest = "<SRV>";
   else if (flags & F_RRNAME)    else if (flags & F_RRNAME)
     dest = arg;      dest = arg;
           
Line 1622  void log_query(unsigned int flags, char *name, struct  Line 1969  void log_query(unsigned int flags, char *name, struct 
   if (strlen(name) == 0)    if (strlen(name) == 0)
     name = ".";      name = ".";
   
  my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);  if (option_bool(OPT_EXTRALOG))
     {
       int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
       if (flags & F_NOEXTRA)
         my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
       else
         my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
     }
   else
     my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
 }  }
   
     

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


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