Diff for /embedaddon/dnsmasq/src/cache.c between versions 1.1 and 1.1.1.5

version 1.1, 2013/07/29 19:37:40 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 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 int uid = 0;  
 #ifdef HAVE_DNSSEC  
 static struct keydata *keyblock_free = NULL;  
 #endif  
   
   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);
   static void dump_cache_entry(struct crec *cache, time_t now);
   
 /* 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. */
   /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
 static const struct {  static const struct {
   unsigned int type;    unsigned int type;
   const char * const name;    const char * const name;
 } typestr[] = {  } typestr[] = {
  { 1,   "A" },  { 1,   "A" }, /* a host address [RFC1035] */
  { 2,   "NS" },  { 2,   "NS" }, /* an authoritative name server [RFC1035] */
  { 5,   "CNAME" },  { 3,   "MD" }, /* a mail destination (OBSOLETE - use MX) [RFC1035] */
  { 6,   "SOA" },  { 4,   "MF" }, /* a mail forwarder (OBSOLETE - use MX) [RFC1035] */
  { 10,  "NULL" },  { 5,   "CNAME" }, /* the canonical name for an alias [RFC1035] */
  { 11,  "WKS" },  { 6,   "SOA" }, /* marks the start of a zone of authority [RFC1035] */
  { 12,  "PTR" },  { 7,   "MB" }, /* a mailbox domain name (EXPERIMENTAL) [RFC1035] */
  { 13,  "HINFO" },         { 8,   "MG" }, /* a mail group member (EXPERIMENTAL) [RFC1035] */
  { 15,  "MX" },  { 9,   "MR" }, /* a mail rename domain name (EXPERIMENTAL) [RFC1035] */
  { 16,  "TXT" },  { 10,  "NULL" }, /* a null RR (EXPERIMENTAL) [RFC1035] */
  { 22,  "NSAP" },  { 11,  "WKS" }, /* a well known service description [RFC1035] */
  { 23,  "NSAP_PTR" },  { 12,  "PTR" }, /* a domain name pointer [RFC1035] */
  { 24,  "SIG" },  { 13,  "HINFO" }, /* host information [RFC1035] */
  { 25,  "KEY" },  { 14,  "MINFO" }, /* mailbox or mail list information [RFC1035] */
  { 28,  "AAAA" },  { 15,  "MX" }, /* mail exchange [RFC1035] */
  { 33,  "SRV" },  { 16,  "TXT" }, /* text strings [RFC1035] */
  { 35,  "NAPTR" },  { 17,  "RP" }, /* for Responsible Person [RFC1183] */
  { 36,  "KX" },  { 18,  "AFSDB" }, /* for AFS Data Base location [RFC1183][RFC5864] */
  { 37,  "CERT" },  { 19,  "X25" }, /* for X.25 PSDN address [RFC1183] */
  { 38,  "A6" },  { 20,  "ISDN" }, /* for ISDN address [RFC1183] */
  { 39,  "DNAME" },  { 21,  "RT" }, /* for Route Through [RFC1183] */
  { 41,  "OPT" },  { 22,  "NSAP" }, /* for NSAP address, NSAP style A record [RFC1706] */
  { 48,  "DNSKEY" },  { 23,  "NSAP_PTR" }, /* for domain name pointer, NSAP style [RFC1348][RFC1637][RFC1706] */
  { 249, "TKEY" },  { 24,  "SIG" }, /* for security signature [RFC2535][RFC2536][RFC2537][RFC2931][RFC3008][RFC3110][RFC3755][RFC4034] */
  { 250, "TSIG" },  { 25,  "KEY" }, /* for security key [RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110][RFC3755][RFC4034] */
  { 251, "IXFR" },  { 26,  "PX" }, /* X.400 mail mapping information [RFC2163] */
  { 252, "AXFR" },  { 27,  "GPOS" }, /* Geographical Position [RFC1712] */
  { 253, "MAILB" },  { 28,  "AAAA" }, /* IP6 Address [RFC3596] */
  { 254, "MAILA" },  { 29,  "LOC" }, /* Location Information [RFC1876] */
  { 255, "ANY" }  { 30,  "NXT" }, /* Next Domain (OBSOLETE) [RFC2535][RFC3755] */
   { 31,  "EID" }, /* Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
   { 32,  "NIMLOC" }, /* Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
   { 33,  "SRV" }, /* Server Selection [1][RFC2782] */
   { 34,  "ATMA" }, /* ATM Address [ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.] */
   { 35,  "NAPTR" }, /* Naming Authority Pointer [RFC2168][RFC2915][RFC3403] */
   { 36,  "KX" }, /* Key Exchanger [RFC2230] */
   { 37,  "CERT" }, /* CERT [RFC4398] */
   { 38,  "A6" }, /* A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] */
   { 39,  "DNAME" }, /* DNAME [RFC6672] */
   { 40,  "SINK" }, /* SINK [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink] 1997-11*/
   { 41,  "OPT" }, /* OPT [RFC3225][RFC6891] */
   { 42,  "APL" }, /* APL [RFC3123] */
   { 43,  "DS" }, /* Delegation Signer [RFC3658][RFC4034] */
   { 44,  "SSHFP" }, /* SSH Key Fingerprint [RFC4255] */
   { 45,  "IPSECKEY" }, /* IPSECKEY [RFC4025] */
   { 46,  "RRSIG" }, /* RRSIG [RFC3755][RFC4034] */
   { 47,  "NSEC" }, /* NSEC [RFC3755][RFC4034][RFC9077] */
   { 48,  "DNSKEY" }, /* DNSKEY [RFC3755][RFC4034] */
   { 49,  "DHCID" }, /* DHCID [RFC4701] */
   { 50,  "NSEC3" }, /* NSEC3 [RFC5155][RFC9077] */
   { 51,  "NSEC3PARAM" }, /* NSEC3PARAM [RFC5155] */
   { 52,  "TLSA" }, /* TLSA [RFC6698] */
   { 53,  "SMIMEA" }, /* S/MIME cert association [RFC8162] SMIMEA/smimea-completed-template 2015-12-01*/
   { 55,  "HIP" }, /* Host Identity Protocol [RFC8005] */
   { 56,  "NINFO" }, /* NINFO [Jim_Reid] NINFO/ninfo-completed-template 2008-01-21*/
   { 57,  "RKEY" }, /* RKEY [Jim_Reid] RKEY/rkey-completed-template 2008-01-21*/
   { 58,  "TALINK" }, /* Trust Anchor LINK [Wouter_Wijngaards] TALINK/talink-completed-template 2010-02-17*/
   { 59,  "CDS" }, /* Child DS [RFC7344] CDS/cds-completed-template 2011-06-06*/
   { 60,  "CDNSKEY" }, /* DNSKEY(s) the Child wants reflected in DS [RFC7344] 2014-06-16*/
   { 61,  "OPENPGPKEY" }, /* OpenPGP Key [RFC7929] OPENPGPKEY/openpgpkey-completed-template 2014-08-12*/
   { 62,  "CSYNC" }, /* Child-To-Parent Synchronization [RFC7477] 2015-01-27*/
   { 63,  "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/
   { 64,  "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
   { 65,  "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
   { 99,  "SPF" }, /* [RFC7208] */
   { 100, "UINFO" }, /* [IANA-Reserved] */
   { 101, "UID" }, /* [IANA-Reserved] */
   { 102, "GID" }, /* [IANA-Reserved] */
   { 103, "UNSPEC" }, /* [IANA-Reserved] */
   { 104, "NID" }, /* [RFC6742] ILNP/nid-completed-template */
   { 105, "L32" }, /* [RFC6742] ILNP/l32-completed-template */
   { 106, "L64" }, /* [RFC6742] ILNP/l64-completed-template */
   { 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */
   { 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/
   { 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/
   { 249, "TKEY" }, /* Transaction Key [RFC2930] */
   { 250, "TSIG" }, /* Transaction Signature [RFC8945] */
   { 251, "IXFR" }, /* incremental transfer [RFC1995] */
   { 252, "AXFR" }, /* transfer of an entire zone [RFC1035][RFC5936] */
   { 253, "MAILB" }, /* mailbox-related RRs (MB, MG or MR) [RFC1035] */
   { 254, "MAILA" }, /* mail agent RRs (OBSOLETE - see MX) [RFC1035] */
   { 255, "ANY" }, /* A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] */
   { 256, "URI" }, /* URI [RFC7553] URI/uri-completed-template 2011-02-22*/
   { 257, "CAA" }, /* Certification Authority Restriction [RFC8659] CAA/caa-completed-template 2011-04-07*/
   { 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
   { 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
   { 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
   { 32768,  "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
   { 32769,  "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
 };  };
   
 static void cache_free(struct crec *crecp);  static void cache_free(struct crec *crecp);
Line 72  static void cache_link(struct crec *crecp); Line 133  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);
   
   void next_uid(struct crec *crecp)
   {
     static unsigned int uid = 0;
   
     if (crecp->uid == UID_NONE)
       {
         uid++;
     
         /* uid == 0 used to indicate CNAME to interface name. */
         if (uid == UID_NONE)
           uid++;
         
         crecp->uid = uid;
       }
   }
   
 void cache_init(void)  void cache_init(void)
 {  {
   struct crec *crecp;    struct crec *crecp;
   int i;    int i;
 
   bignames_left = daemon->cachesize/10;    bignames_left = daemon->cachesize/10;
       
   if (daemon->cachesize > 0)    if (daemon->cachesize > 0)
Line 87  void cache_init(void) Line 164  void cache_init(void)
         {          {
           cache_link(crecp);            cache_link(crecp);
           crecp->flags = 0;            crecp->flags = 0;
          crecp->uid = uid++;          crecp->uid = UID_NONE;
         }          }
     }      }
       
Line 113  static void rehash(int size) Line 190  static void rehash(int size)
   else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))    else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
     return;      return;
   
  for(i = 0; i < new_size; i++)  for (i = 0; i < new_size; i++)
     new[i] = NULL;      new[i] = NULL;
   
   old = hash_table;    old = hash_table;
Line 157  static void cache_hash(struct crec *crecp) Line 234  static void cache_hash(struct crec *crecp)
      immortal entries are at the end of the hash-chain.       immortal entries are at the end of the hash-chain.
      This allows reverse searches and garbage collection to be optimised */       This allows reverse searches and garbage collection to be optimised */
   
  struct crec **up = hash_bucket(cache_get_name(crecp));  char *name = cache_get_name(crecp);
  struct crec **up = hash_bucket(name);
  if (!(crecp->flags & F_REVERSE))  unsigned int flags = crecp->flags & (F_IMMORTAL | F_REVERSE);
   
   if (!(flags & F_REVERSE))
     {      {
       while (*up && ((*up)->flags & F_REVERSE))        while (*up && ((*up)->flags & F_REVERSE))
         up = &((*up)->hash_next);           up = &((*up)->hash_next); 
               
      if (crecp->flags & F_IMMORTAL)      if (flags & F_IMMORTAL)
         while (*up && !((*up)->flags & F_IMMORTAL))          while (*up && !((*up)->flags & F_IMMORTAL))
           up = &((*up)->hash_next);            up = &((*up)->hash_next);
     }      }
   
     /* Preserve order when inserting the same name multiple times.
        Do not mess up the flag invariants. */
     while (*up &&
            hostname_isequal(cache_get_name(*up), name) &&
            flags == ((*up)->flags & (F_IMMORTAL | F_REVERSE)))
       up = &((*up)->hash_next);
     
   crecp->hash_next = *up;    crecp->hash_next = *up;
   *up = crecp;    *up = crecp;
 }  }
 
 static void cache_blockdata_free(struct crec *crecp)
 {
   if (!(crecp->flags & F_NEG))
     {
       if (crecp->flags & F_SRV)
         blockdata_free(crecp->addr.srv.target);
 #ifdef HAVE_DNSSEC
       else if (crecp->flags & F_DNSKEY)
         blockdata_free(crecp->addr.key.keydata);
       else if (crecp->flags & F_DS)
         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 = 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;
   else    else
Line 193  static void cache_free(struct crec *crecp) Line 295  static void cache_free(struct crec *crecp)
       big_free = crecp->name.bname;        big_free = crecp->name.bname;
       crecp->flags &= ~F_BIGNAME;        crecp->flags &= ~F_BIGNAME;
     }      }
#ifdef HAVE_DNSSEC
  else if (crecp->flags & (F_DNSKEY | F_DS))  cache_blockdata_free(crecp);
    keydata_free(crecp->addr.key.keydata); 
#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 235  char *cache_get_name(struct crec *crecp) Line 335  char *cache_get_name(struct crec *crecp)
   return crecp->name.sname;    return crecp->name.sname;
 }  }
   
   char *cache_get_cname_target(struct crec *crecp)
   {
     if (crecp->addr.cname.is_name_ptr)
        return crecp->addr.cname.target.name;
     else
       return cache_get_name(crecp->addr.cname.target.cache);
   }
   
   
   
 struct crec *cache_enumerate(int init)  struct crec *cache_enumerate(int init)
 {  {
   static int bucket;    static int bucket;
Line 260  struct crec *cache_enumerate(int init) Line 370  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))  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.cache &&   if (crecp->addr.cname.target.cache && 
      (crecp->addr.cname.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.cache->uid)      crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)
     return 0;      return 0;
       
   return 1;    return 1;
Line 275  static int is_outdated_cname_pointer(struct crec *crec Line 385  static int is_outdated_cname_pointer(struct crec *crec
   
 static int is_expired(time_t now, struct crec *crecp)  static int is_expired(time_t now, struct crec *crecp)
 {  {
     /* Don't dump expired entries if they are within the accepted timeout range.
        The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
        Possible values for daemon->cache_max_expiry:
         -1  == serve cached content regardless how long ago it expired
          0  == the option is disabled, expired content isn't served
         <n> == serve cached content only if it expire less than <n> seconds
                ago (where n is a positive integer) */
     if (daemon->cache_max_expiry != 0 &&
         (daemon->cache_max_expiry == -1 ||
          difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
         !(crecp->flags & (F_DS | F_DNSKEY)))
       return 0;
   
   if (crecp->flags & F_IMMORTAL)    if (crecp->flags & F_IMMORTAL)
     return 0;      return 0;
   
Line 284  static int is_expired(time_t now, struct crec *crecp) Line 407  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)/* Remove entries with a given UID from the cache */
 unsigned int cache_remove_uid(const unsigned int uid)
 {  {
     int i;
     unsigned int removed = 0;
     struct crec *crecp, **up;
   
     for (i = 0; i < hash_size; i++)
       for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
         if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
           {
             *up = crecp->hash_next;
             free(crecp);
             removed++;
           }
         else
           up = &crecp->hash_next;
     
     return removed;
   }
   
   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
      entries but only in the same hash bucket as name.       entries but only in the same hash bucket as name.
Line 293  static int cache_scan_free(char *name, struct all_addr Line 438  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))        {
          {           if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
            *up = crecp->hash_next;            {
            if (!(crecp->flags & (F_HOSTS | F_DHCP)))              /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
              {              if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || 
                cache_unlink(crecp);                  (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
                cache_free(crecp);                {
              }                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                              return crecp;
        else if ((crecp->flags & F_FORWARD) &&                   *up = crecp->hash_next;
                 ((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&                  /* If this record is for the name we're inserting and is the target
                 hostname_isequal(cache_get_name(crecp), name))                     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->flags & (F_HOSTS | F_DHCP))                  if (crecp->uid != UID_NONE)
              return 0;                    {
            *up = crecp->hash_next;                      if (target_crec)
            cache_unlink(crecp);                        *target_crec = crecp;
            cache_free(crecp);                      if (target_uid)
          }                        *target_uid = crecp->uid;
        else                    }
                   cache_unlink(crecp);
                   cache_free(crecp);
                   continue;
                 }
               
 #ifdef HAVE_DNSSEC
               /* Deletion has to be class-sensitive for DS and DNSKEY */
               if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == class)
                 {
                   if (crecp->flags & F_CONFIG)
                     return crecp;
                   *up = crecp->hash_next;
                   cache_unlink(crecp);
                   cache_free(crecp);
                   continue;
                 }
 #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 341  static int cache_scan_free(char *name, struct all_addr Line 519  static int cache_scan_free(char *name, struct all_addr
           if (is_expired(now, crecp))            if (is_expired(now, crecp))
             {              {
               *up = crecp->hash_next;                *up = crecp->hash_next;
              if (!(crecp->flags & (F_HOSTS | F_DHCP)))              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                 {                   { 
                   cache_unlink(crecp);                    cache_unlink(crecp);
                   cache_free(crecp);                    cache_free(crecp);
                 }                  }
             }              }
          else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&          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)                   addr && memcmp(&crecp->addr, addr, addrlen) == 0)
             {              {
               *up = crecp->hash_next;                *up = crecp->hash_next;
               cache_unlink(crecp);                cache_unlink(crecp);
Line 360  static int cache_scan_free(char *name, struct all_addr Line 538  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 385  void cache_start_insert(void) Line 563  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 *addrstruct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
                          time_t now,  unsigned long ttl, unsigned short flags)                          time_t now,  unsigned long ttl, unsigned int flags)
 {  {
  struct crec *new;#ifdef HAVE_DNSSEC
  union bigname *big_name = NULL;  if (flags & (F_DNSKEY | F_DS)) 
  int freed_all = flags & F_REVERSE;    {
  int free_avail = 0;      /* 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
     {
       if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < 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);
 }
   
   if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)  
     ttl = daemon->max_cache_ttl;  
   
  /* Don't log keys */static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
  if (flags & (F_IPV4 | F_IPV6))                                  time_t now,  unsigned long ttl, unsigned int flags)
    log_query(flags | F_UPSTREAM, name, addr, NULL);{
  struct crec *new, *target_crec = NULL;
   union bigname *big_name = NULL;
   int freed_all = (flags & F_REVERSE);
   struct crec *free_avail = NULL;
   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 is 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) 
          { 
            insert_error = 1; 
            return NULL; 
          } 
                 
        if (freed_all) 
          { 
            free_avail = 1; /* Must be free space now. */ 
            cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags); 
            cache_live_freed++; 
          } 
        else 
          { 
            cache_scan_free(NULL, NULL, now, 0); 
            freed_all = 1; 
          } 
        continue; 
      } 
  
    /* Check if we need to and can allocate extra memory for a long name. 
       If that fails, give up now. */ 
    if (name && (strlen(name) > SMALLDNAME-1)) 
      { 
        if (big_free) 
          {  
            big_name = big_free; 
            big_free = big_free->next; 
          } 
        else if (!bignames_left || 
                 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname)))) 
          { 
            insert_error = 1; 
            return NULL; 
          } 
        else 
          bignames_left--; 
         
      } 
   
    /* Got the rest: finally grab entry. */      /* End of LRU list is still in use: if we didn't scan all the hash
    cache_unlink(new);         chains for expired entries do that now. If we already tried that
    break;         then it's time to start spilling things. */
  }      
       /* 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)
         {
           my_syslog(LOG_ERR, _("Internal error in cache."));
           /* Log the entry we tried to delete. */
           dump_cache_entry(free_avail, now);
           insert_error = 1;
           return NULL;
         }
       
       if (freed_all)
         {
           /* For DNSSEC records, uid holds class. */
           free_avail = new; /* Must be free space now. */
           
           /* condition valid when stale-caching */
           if (difftime(now, new->ttd) < 0)
             daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
           
           cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL); 
         }
       else
         {
           cache_scan_free(NULL, NULL, class, now, 0, NULL, NULL);
           freed_all = 1;
         }
     }
       
   /* Check if we need to and can allocate extra memory for a long name.
      If that fails, give up now, always succeed for DNSSEC records. */
   if (name && (strlen(name) > SMALLDNAME-1))
     {
       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--;
       
     }
 
   /* If we freed a cache entry for our name which was a CNAME target, use that.
      and preserve the uid, so that existing CNAMES are not broken. */
   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 489  struct crec *cache_insert(char *name, struct all_addr  Line 727  struct crec *cache_insert(char *name, struct all_addr 
   else    else
     *cache_get_name(new) = 0;      *cache_get_name(new) = 0;
   
   #ifdef HAVE_DNSSEC
     if (flags & (F_DS | F_DNSKEY))
       new->uid = class;
   #endif
   
   if (addr)    if (addr)
    new->addr.addr = *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;
  
   return new;    return new;
 }  }
   
Line 515  void cache_end_insert(void) Line 758  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;
 }  }
   
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
 /* 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 *ans;    struct crec *ans;
     int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
   
     prot &= ~F_NO_RR;
     
   if (crecp) /* iterating */    if (crecp) /* iterating */
     ans = crecp->next;      ans = crecp->next;
   else    else
Line 533  struct crec *cache_find_by_name(struct crec *crecp, ch Line 927  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 545  struct crec *cache_find_by_name(struct crec *crecp, ch Line 939  struct crec *cache_find_by_name(struct crec *crecp, ch
                   (crecp->flags & prot) &&                    (crecp->flags & prot) &&
                   hostname_isequal(cache_get_name(crecp), name))                    hostname_isequal(cache_get_name(crecp), name))
                 {                  {
                  if (crecp->flags & (F_HOSTS | F_DHCP))                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                     {                      {
                       *chainp = crecp;                        *chainp = crecp;
                       chainp = &crecp->next;                        chainp = &crecp->next;
Line 570  struct crec *cache_find_by_name(struct crec *crecp, ch Line 964  struct crec *cache_find_by_name(struct crec *crecp, ch
                     }                      }
                   else                    else
                     {                      {
                      if (!insert)                      if (!insert && !no_rr)
                         {                          {
                           insert = up;                            insert = up;
                           ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);                            ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
Line 586  struct crec *cache_find_by_name(struct crec *crecp, ch Line 980  struct crec *cache_find_by_name(struct crec *crecp, ch
             {              {
               /* expired entry, free it */                /* expired entry, free it */
               *up = crecp->hash_next;                *up = crecp->hash_next;
              if (!(crecp->flags & (F_HOSTS | F_DHCP)))              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                 {                   { 
                   cache_unlink(crecp);                    cache_unlink(crecp);
                   cache_free(crecp);                    cache_free(crecp);
Line 599  struct crec *cache_find_by_name(struct crec *crecp, ch Line 993  struct crec *cache_find_by_name(struct crec *crecp, ch
   
   if (ans &&     if (ans && 
       (ans->flags & F_FORWARD) &&        (ans->flags & F_FORWARD) &&
      (ans->flags & prot) &&      (ans->flags & prot) &&     
       hostname_isequal(cache_get_name(ans), name))        hostname_isequal(cache_get_name(ans), name))
     return ans;      return ans;
       
   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 short 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 634  struct crec *cache_find_by_addr(struct crec *crecp, st Line 1024  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))                   if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                      {                       {
                        *chainp = crecp;                         *chainp = crecp;
                        chainp = &crecp->next;                         chainp = &crecp->next;
Line 652  struct crec *cache_find_by_addr(struct crec *crecp, st Line 1042  struct crec *cache_find_by_addr(struct crec *crecp, st
            else             else
              {               {
                *up = crecp->hash_next;                 *up = crecp->hash_next;
               if (!(crecp->flags & (F_HOSTS | F_DHCP)))               if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                  {                   {
                    cache_unlink(crecp);                     cache_unlink(crecp);
                    cache_free(crecp);                     cache_free(crecp);
Line 665  struct crec *cache_find_by_addr(struct crec *crecp, st Line 1055  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, 
                             unsigned int index, struct crec **rhash, int hashsz)
 {  {
  struct crec *crec;  int i;
  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_HOSTS | F_CNAME; 
        crec->name.namep = a->alias; 
        crec->addr.cname.cache = target; 
        crec->addr.cname.uid = target->uid; 
        cache_hash(crec); 
        add_hosts_cname(crec); /* handle chains */ 
      } 
} 
   
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,  
                            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)); 
  int i, nameexists = 0; 
   unsigned int j;     unsigned int j; 
     struct crec *lookup = NULL;
   
   /* Remove duplicates in hosts files. */    /* Remove duplicates in hosts files. */
  if (lookup && (lookup->flags & F_HOSTS))  while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
    {    if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
      nameexists = 1;      {
      if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)        free(cache);
        {        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
      the array rhash, hashed on address. Note that rhash and the values       the array rhash, hashed on address. Note that rhash and the values
      in ->next are only valid  whilst reading hosts files: the buckets are       in ->next are only valid  whilst reading hosts files: the buckets are
      then freed, and the ->next pointer used for other things.        then freed, and the ->next pointer used for other things. 
   
      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 767  static int eatspace(FILE *f) Line 1147  static int eatspace(FILE *f)
         }          }
   
       if (c == '\n')        if (c == '\n')
        nl = 1;        nl++;
     }      }
 }  }
                     
Line 778  static int gettok(FILE *f, char *token) Line 1158  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 794  static int gettok(FILE *f, char *token) Line 1174  static int gettok(FILE *f, char *token)
     }      }
 }  }
   
static int read_hostsfile(char *filename, 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 names_done = 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++;  
         
       /* 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 852  static int read_hostsfile(char *filename, int index, i Line 1227  static int read_hostsfile(char *filename, int index, i
           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 861  static int read_hostsfile(char *filename, int index, i Line 1236  static int read_hostsfile(char *filename, int index, i
             {              {
               /* 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++;
                     names_done++;
                 }                  }
              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++;
                     names_done++;
                 }                  }
               free(canon);                free(canon);
                               
Line 884  static int read_hostsfile(char *filename, int index, i Line 1262  static int read_hostsfile(char *filename, int index, i
           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);  
       
  my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);  if (rhash)
     rehash(name_count); 
       
     my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
     
   return name_count;    return name_count;
 }  }
                           
Line 901  void cache_reload(void) Line 1283  void cache_reload(void)
   struct hostsfile *ah;    struct hostsfile *ah;
   struct host_record *hr;    struct host_record *hr;
   struct name_list *nl;    struct name_list *nl;
     struct cname *a;
     struct crec lrec;
     struct mx_srv_record *mx;
     struct txt_record *txt;
     struct interface_name *intr;
     struct ptr_record *ptr;
     struct naptr *naptr;
   #ifdef HAVE_DNSSEC
     struct ds_config *ds;
   #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)
       {        {
           cache_blockdata_free(cache);
   
         tmp = cache->hash_next;          tmp = cache->hash_next;
        if (cache->flags & F_HOSTS)        if (cache->flags & (F_HOSTS | F_CONFIG))
           {            {
             *up = cache->hash_next;              *up = cache->hash_next;
             free(cache);              free(cache);
Line 927  void cache_reload(void) Line 1322  void cache_reload(void)
           up = &cache->hash_next;            up = &cache->hash_next;
       }        }
       
     /* Add locally-configured CNAMEs to the cache */
     for (a = daemon->cnames; a; a = a->next)
       if (a->alias[1] != '*' &&
           ((cache = whine_malloc(SIZEOF_POINTER_CREC))))
         {
           cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
           cache->ttd = a->ttl;
           cache->name.namep = a->alias;
           cache->addr.cname.target.name = a->target;
           cache->addr.cname.is_name_ptr = 1;
           cache->uid = UID_NONE;
           cache_hash(cache);
           make_non_terminals(cache);
         }
     
   #ifdef HAVE_DNSSEC
     for (ds = daemon->ds; ds; ds = ds->next)
       if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) &&
           (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
         {
           cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
           cache->ttd = daemon->local_ttl;
           cache->name.namep = ds->name;
           cache->addr.ds.keylen = ds->digestlen;
           cache->addr.ds.algo = ds->algo;
           cache->addr.ds.keytag = ds->keytag;
           cache->addr.ds.digest = ds->digest_type;
           cache->uid = ds->class;
           cache_hash(cache);
           make_non_terminals(cache);
         }
   #endif
     
   /* borrow the packet buffer for a temporary by-address hash */    /* borrow the packet buffer for a temporary by-address hash */
   memset(daemon->packet, 0, daemon->packet_buff_sz);    memset(daemon->packet, 0, daemon->packet_buff_sz);
   revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);    revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
Line 937  void cache_reload(void) Line 1365  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, 0, (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, 0, (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, 0, 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);
     }
   
char *get_domain(struct in_addr addr)  for (naptr = daemon->naptr; naptr; naptr = naptr->next)
{    {
  struct cond_domain *c;      lrec.name.namep = naptr->name;
       make_non_terminals(&lrec);
     }
   
  for (c = daemon->cond_domain; c; c = c->next)  for (mx = daemon->mxnames; mx; mx = mx->next)
    if (!c->is6 &&    {
        ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&      lrec.name.namep = mx->name;
        ntohl(addr.s_addr) <= ntohl(c->end.s_addr))      make_non_terminals(&lrec);
      return c->domain;    }
   
  return daemon->domain_suffix;  for (intr = daemon->int_names; intr; intr = intr->next)
}    {
      lrec.name.namep = intr->name;
      make_non_terminals(&lrec);
#ifdef HAVE_IPV6    }
char *get_domain6(struct in6_addr *addr) 
{ 
  struct cond_domain *c; 
 
  u64 addrpart = addr6part(addr); 
       
  for (c = daemon->cond_domain; c; c = c->next)  for (ptr = daemon->ptr; ptr; ptr = ptr->next)
    if (c->is6 &&    {
        is_same_net6(addr, &c->start6, 64) &&      lrec.name.namep = ptr->name;
        addrpart >= addr6part(&c->start6) &&      make_non_terminals(&lrec);
        addrpart <= addr6part(&c->end6))    }
      return c->domain; 
       
  return daemon->domain_suffix;#ifdef HAVE_INOTIFY
}  set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
 #endif  #endif
     
   } 
   
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
 struct in_addr a_record_from_hosts(char *name, time_t now)  struct in_addr a_record_from_hosts(char *name, time_t now)
Line 1009  struct in_addr a_record_from_hosts(char *name, time_t  Line 1445  struct in_addr a_record_from_hosts(char *name, time_t 
   struct crec *crecp = NULL;    struct crec *crecp = NULL;
   struct in_addr ret;    struct in_addr ret;
       
  while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))  /* If no DNS service, cache not initialised. */
    if (crecp->flags & F_HOSTS)  if (daemon->port != 0)
      return *(struct in_addr *)&crecp->addr;    while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
      if (crecp->flags & F_HOSTS)
         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);
       
   ret.s_addr = 0;    ret.s_addr = 0;
Line 1036  void cache_unhash_dhcp(void) Line 1474  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;  
             if (ttd == 0)  
               aliasc->flags |= F_IMMORTAL;  
             else  
               aliasc->ttd = ttd;  
             aliasc->name.namep = a->alias;  
             aliasc->addr.cname.cache = target;  
             aliasc->addr.cname.uid = target->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);
       
   while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))    while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
     {      {
       /* check all addresses associated with name */        /* check all addresses associated with name */
      if (crec->flags & F_HOSTS)      if (crec->flags & (F_HOSTS | F_CONFIG))
         {          {
           if (crec->flags & F_CNAME)            if (crec->flags & F_CNAME)
             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 1112  void cache_add_dhcp_entry(char *host_name, int prot, Line 1519  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 1121  void cache_add_dhcp_entry(char *host_name, int prot, Line 1528  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 1135  void cache_add_dhcp_entry(char *host_name, int prot, Line 1542  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 1144  void cache_add_dhcp_entry(char *host_name, int prot, Line 1551  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 = uid++;      crec->uid = UID_NONE;
       cache_hash(crec);        cache_hash(crec);
         make_non_terminals(crec);
       }
   }
   #endif
   
      add_dhcp_cname(crec, ttd);/* 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);
             if (!(crecp->flags & F_IMMORTAL))
               crecp->ttd = source->ttd;
             crecp->name.namep = name;
             
             cache_hash(crecp);
           }
       }
 }  }
   
   #ifndef NO_ID
   int cache_make_stat(struct txt_record *t)
   { 
     static char *buff = NULL;
     static int bufflen = 60;
     int len;
     struct server *serv, *serv1;
     char *p;
   
     if (!buff && !(buff = whine_malloc(60)))
       return 0;
   
     p = buff;
     
     switch (t->stat)
       {
       case TXT_STAT_CACHESIZE:
         sprintf(buff+1, "%d", daemon->cachesize);
         break;
   
       case TXT_STAT_INSERTS:
         sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
         break;
   
       case TXT_STAT_EVICTIONS:
         sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]);
         break;
   
       case TXT_STAT_MISSES:
         sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]);
         break;
   
       case TXT_STAT_HITS:
         sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
         break;
   
   #ifdef HAVE_AUTH
       case TXT_STAT_AUTH:
         sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
         break;
 #endif  #endif
   
       case TXT_STAT_SERVERS:
         /* sum counts from different records for same server */
         for (serv = daemon->servers; serv; serv = serv->next)
           serv->flags &= ~SERV_MARK;
         
         for (serv = daemon->servers; serv; serv = serv->next)
           if (!(serv->flags & SERV_MARK))
             {
               char *new, *lenp;
               int port, newlen, bytes_avail, bytes_needed;
               unsigned int queries = 0, failed_queries = 0;
               for (serv1 = serv; serv1; serv1 = serv1->next)
                 if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
                   {
                     serv1->flags |= SERV_MARK;
                     queries += serv1->queries;
                     failed_queries += serv1->failed_queries;
                   }
               port = prettyprint_addr(&serv->addr, daemon->addrbuff);
               lenp = p++; /* length */
               bytes_avail = bufflen - (p - buff );
               bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
               if (bytes_needed >= bytes_avail)
                 {
                   /* expand buffer if necessary */
                   newlen = bytes_needed + 1 + bufflen - bytes_avail;
                   if (!(new = whine_realloc(buff, newlen)))
                     return 0;
                   p = new + (p - buff);
                   lenp = p - 1;
                   buff = new;
                   bufflen = newlen;
                   bytes_avail =  bufflen - (p - buff );
                   bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
                 }
               *lenp = bytes_needed;
               p += bytes_needed;
             }
         t->txt = (unsigned char *)buff;
         t->len = p - buff;
   
         return 1;
       }
     
     len = strlen(buff+1);
     t->txt = (unsigned char *)buff;
     t->len = len + 1;
     *buff = len;
     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;
   }
   
   static void dump_cache_entry(struct crec *cache, time_t now)
   {
     (void)now;
     static char *buff = NULL;
     
     char *p, *t = " ";
     char *a = daemon->addrbuff, *n = cache_get_name(cache);
   
     /* String length is limited below */
     if (!buff && !(buff = whine_malloc(150)))
       return;
     
     p = buff;
     
     *a = 0;
     if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
       n = "<Root>";
     p += sprintf(p, "%-30.30s ", sanitise(n));
     if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(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
     else if (cache->flags & F_DS)
       {
         if (!(cache->flags & F_NEG))
           sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
                   cache->addr.ds.algo, cache->addr.ds.digest);
       }
     else if (cache->flags & F_DNSKEY)
       sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
               cache->addr.key.algo, cache->addr.key.flags);
   #endif
     else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
       { 
         a = daemon->addrbuff;
         if (cache->flags & F_IPV4)
           inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
         else if (cache->flags & F_IPV6)
           inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
       }
     
     if (cache->flags & F_IPV4)
       t = "4";
     else if (cache->flags & F_IPV6)
       t = "6";
     else if (cache->flags & F_CNAME)
       t = "C";
     else if (cache->flags & F_SRV)
       t = "V";
   #ifdef HAVE_DNSSEC
     else if (cache->flags & F_DS)
       t = "S";
     else if (cache->flags & F_DNSKEY)
       t = "K";
   #endif
     else if (!(cache->flags & F_NXDOMAIN)) /* non-terminal */
       t = "!";
     
     p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s%s ", a, t,
                  cache->flags & F_FORWARD ? "F" : " ",
                  cache->flags & F_REVERSE ? "R" : " ",
                  cache->flags & F_IMMORTAL ? "I" : " ",
                  cache->flags & F_DHCP ? "D" : " ",
                  cache->flags & F_NEG ? "N" : " ",
                  cache->flags & F_NXDOMAIN ? "X" : " ",
                  cache->flags & F_HOSTS ? "H" : " ",
                  cache->flags & F_CONFIG ? "C" : " ",
                  cache->flags & F_DNSSECOK ? "V" : " ");
   #ifdef HAVE_BROKEN_RTC
     p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
   #else
     p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd)));
   #endif
     if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0)
       p += sprintf(p, " %-40.40s", record_source(cache->uid));
     
     my_syslog(LOG_INFO, "%s", buff);
   }
   
 void dump_cache(time_t now)  void dump_cache(time_t now)
 {  {
   struct server *serv, *serv1;    struct server *serv, *serv1;
   
   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]);
   if (daemon->cache_max_expiry != 0)
     my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
 #ifdef HAVE_AUTH
   my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
 #endif
   
     blockdata_report();
   
   /* 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)
    serv->flags &= ~SERV_COUNTED;    serv->flags &= ~SERV_MARK;
       
   for (serv = daemon->servers; serv; serv = serv->next)    for (serv = daemon->servers; serv; serv = serv->next)
    if (!(serv->flags &     if (!(serv->flags & SERV_MARK))
          (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND))) 
       {        {
         int port;          int port;
        unsigned int queries = 0, failed_queries = 0;        unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
         unsigned int sigma_latency = 0, count_latency = 0;
 
         for (serv1 = serv; serv1; serv1 = serv1->next)          for (serv1 = serv; serv1; serv1 = serv1->next)
          if (!(serv1->flags &           if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
                (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&  
              sockaddr_isequal(&serv->addr, &serv1->addr)) 
             {              {
              serv1->flags |= SERV_COUNTED;              serv1->flags |= SERV_MARK;
               queries += serv1->queries;                queries += serv1->queries;
               failed_queries += serv1->failed_queries;                failed_queries += serv1->failed_queries;
                 nxdomain_replies += serv1->nxdomain_replies;
                 retrys += serv1->retrys;
                 sigma_latency += serv1->query_latency;
                 count_latency++;
             }              }
         port = prettyprint_addr(&serv->addr, daemon->addrbuff);          port = prettyprint_addr(&serv->addr, daemon->addrbuff);
        my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);        my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"),
                   daemon->addrbuff, port, queries, retrys, failed_queries, nxdomain_replies, sigma_latency/count_latency);
       }        }
  
   if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))    if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
     {      {
      struct crec *cache ;      struct crec *cache;
       int i;        int i;
      my_syslog(LOG_INFO, "Host                                     Address                        Flags     Expires");      my_syslog(LOG_INFO, "Host                           Address                                  Flags      Expires                  Source");
       my_syslog(LOG_INFO, "------------------------------ ---------------------------------------- ---------- ------------------------ ------------");
           
       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)
          {          dump_cache_entry(cache, now);
            char *a, *p = daemon->namebuff; 
            p += sprintf(p, "%-40.40s ", cache_get_name(cache)); 
            if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD)) 
              a = "";  
            else if (cache->flags & F_CNAME)  
              { 
                a = ""; 
                if (!is_outdated_cname_pointer(cache)) 
                  a = cache_get_name(cache->addr.cname.cache); 
              } 
#ifdef HAVE_DNSSEC 
            else if (cache->flags & F_DNSKEY) 
              { 
                a = daemon->addrbuff; 
                sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid); 
              } 
            else if (cache->flags & F_DS) 
              { 
                a = daemon->addrbuff; 
                sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid, 
                        cache->addr.key.algo, cache->addr.key.digest, cache->uid); 
              } 
#endif 
            else  
              {  
                a = daemon->addrbuff; 
                if (cache->flags & F_IPV4) 
                  inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN); 
#ifdef HAVE_IPV6 
                else if (cache->flags & F_IPV6) 
                  inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN); 
#endif 
              } 
 
            p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s  ", a,  
                         cache->flags & F_IPV4 ? "4" : "", 
                         cache->flags & F_IPV6 ? "6" : "", 
                         cache->flags & F_DNSKEY ? "K" : "", 
                         cache->flags & F_DS ? "S" : "", 
                         cache->flags & F_CNAME ? "C" : "", 
                         cache->flags & F_FORWARD ? "F" : " ", 
                         cache->flags & F_REVERSE ? "R" : " ", 
                         cache->flags & F_IMMORTAL ? "I" : " ", 
                         cache->flags & F_DHCP ? "D" : " ", 
                         cache->flags & F_NEG ? "N" : " ", 
                         cache->flags & F_NXDOMAIN ? "X" : " ", 
                         cache->flags & F_HOSTS ? "H" : " ", 
                         cache->flags & F_DNSSECOK ? "V" : " "); 
#ifdef HAVE_BROKEN_RTC 
            p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now)); 
#else 
            p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))); 
            /* ctime includes trailing \n - eat it */ 
            *(p-1) = 0; 
#endif 
            my_syslog(LOG_INFO, daemon->namebuff); 
          } 
     }      }
 }  }
   
char *record_source(int index)char *record_source(unsigned int index)
 {  {
   struct hostsfile *ah;    struct hostsfile *ah;
#ifdef HAVE_INOTIFY
  if (index == 0)  struct dyndir *dd;
 #endif
   
   if (index == SRC_CONFIG)
     return "config";
   else if (index == SRC_HOSTS)
     return HOSTSFILE;      return HOSTSFILE;
   
   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
   /* Dynamic directories contain multiple files */
   for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
     for (ah = dd->files; ah; ah = ah->next)
       if (ah->index == index)
         return ah->fname;
 #endif
 
   return "<unknown>";    return "<unknown>";
 }  }
   
void querystr(char *desc, char *str, unsigned short type)static char *querystr(char *desc, unsigned short type)
 {  {
   unsigned int i;    unsigned int i;
    int len = 10; /* strlen("type=xxxxx") */
  sprintf(str, "%s[type=%d]", desc, type);   const char *types = NULL;
   static char *buff = NULL;
   static int bufflen = 0;
 
   for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)    for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
     if (typestr[i].type == type)      if (typestr[i].type == type)
      sprintf(str,"%s[%s]", desc, typestr[i].name);      {
         types = typestr[i].name;
         len = strlen(types);
         break;
       }
 
   if (desc)
     {
        len += 2; /* braces */
        len += strlen(desc);
     }
   len++; /* terminator */
   
   if (!buff || bufflen < len)
     {
       if (buff)
         free(buff);
       else if (len < 20)
         len = 20;
       
       buff = whine_malloc(len);
       bufflen = len;
     }
 
   if (buff)
     {
       if (desc)
         {
           if (types)
             sprintf(buff, "%s[%s]", desc, types);
           else
             sprintf(buff, "%s[type=%d]", desc, type);
         }
       else
         {
           if (types)
             sprintf(buff, "<%s>", types);
           else
             sprintf(buff, "<type=%d>", type);
         }
     }
   
   return buff ? buff : "";
 }  }
   
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)static char *edestr(int ede)
 {  {
  char *source, *dest = daemon->addrbuff;  switch (ede)
     {
     case EDE_OTHER:                       return "other";
     case EDE_USUPDNSKEY:                  return "unsupported DNSKEY algorithm";
     case EDE_USUPDS:                      return "unsupported DS digest";
     case EDE_STALE:                       return "stale answer";
     case EDE_FORGED:                      return "forged";
     case EDE_DNSSEC_IND:                  return "DNSSEC indeterminate";
     case EDE_DNSSEC_BOGUS:                return "DNSSEC bogus";
     case EDE_SIG_EXP:                     return "DNSSEC signature expired";
     case EDE_SIG_NYV:                     return "DNSSEC sig not yet valid";
     case EDE_NO_DNSKEY:                   return "DNSKEY missing";
     case EDE_NO_RRSIG:                    return "RRSIG missing";
     case EDE_NO_ZONEKEY:                  return "no zone key bit set";
     case EDE_NO_NSEC:                     return "NSEC(3) missing";
     case EDE_CACHED_ERR:                  return "cached error";
     case EDE_NOT_READY:                   return "not ready";
     case EDE_BLOCKED:                     return "blocked";
     case EDE_CENSORED:                    return "censored";
     case EDE_FILTERED:                    return "filtered";
     case EDE_PROHIBITED:                  return "prohibited";
     case EDE_STALE_NXD:                   return "stale NXDOMAIN";
     case EDE_NOT_AUTH:                    return "not authoritative";
     case EDE_NOT_SUP:                     return "not supported";
     case EDE_NO_AUTH:                     return "no reachable authority";
     case EDE_NETERR:                      return "network error";
     case EDE_INVALID_DATA:                return "invalid data";
     default:                              return "unknown";
     }
 }
 
 void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type)
 {
   char *source, *dest = arg;
   char *verb = "is";    char *verb = "is";
     char *extra = "";
     char portstring[7]; /* space for #<portnum> */
       
   if (!option_bool(OPT_LOG))    if (!option_bool(OPT_LOG))
     return;      return;
   
     /* build query type string if requested */
     if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
       arg = querystr(arg, type);
   
   #ifdef HAVE_DNSSEC
     if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
       extra = " (DNSSEC signed)";
   #endif
   
     name = sanitise(name);
   
   if (addr)    if (addr)
     {      {
#ifdef HAVE_IPV6      dest = daemon->addrbuff;
      inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
                addr, daemon->addrbuff, ADDRSTRLEN);      if (flags & F_KEYTAG)
#else        sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
      strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);        else if (flags & F_RCODE)
#endif        {
           unsigned int rcode = addr->log.rcode;
 
           if (rcode == SERVFAIL)
             dest = "SERVFAIL";
           else if (rcode == REFUSED)
             dest = "REFUSED";
           else if (rcode == NOTIMP)
             dest = "not implemented";
           else
             sprintf(daemon->addrbuff, "%u", rcode);
 
           if (addr->log.ede != EDE_UNSET)
             {
               extra = daemon->addrbuff;
               sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
             }
         }
       else if (flags & (F_IPV4 | F_IPV6))
         {
           inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
                     addr, daemon->addrbuff, ADDRSTRLEN);
           if ((flags & F_SERVER) && type != NAMESERVER_PORT)
             {
               extra = portstring;
               sprintf(portstring, "#%u", type);
             }
         }
       else
         dest = arg;
     }      }
   
   if (flags & F_REVERSE)    if (flags & F_REVERSE)
Line 1308  void log_query(unsigned int flags, char *name, struct  Line 2091  void log_query(unsigned int flags, char *name, struct 
   if (flags & F_NEG)    if (flags & F_NEG)
     {      {
       if (flags & F_NXDOMAIN)        if (flags & F_NXDOMAIN)
        {        dest = "NXDOMAIN";
          if (flags & F_IPV4) 
            dest = "NXDOMAIN-IPv4"; 
          else if (flags & F_IPV6) 
            dest = "NXDOMAIN-IPv6"; 
          else 
            dest = "NXDOMAIN"; 
        } 
       else        else
         {                {      
           if (flags & F_IPV4)            if (flags & F_IPV4)
Line 1328  void log_query(unsigned int flags, char *name, struct  Line 2104  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 1339  void log_query(unsigned int flags, char *name, struct  Line 2117  void log_query(unsigned int flags, char *name, struct 
     source = arg;      source = arg;
   else if (flags & F_UPSTREAM)    else if (flags & F_UPSTREAM)
     source = "reply";      source = "reply";
     else if (flags & F_SECSTAT)
       {
         if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
           {
             extra = daemon->addrbuff;
             sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
           }
         source = "validation";
         dest = arg;
       }
   else if (flags & F_AUTH)    else if (flags & F_AUTH)
     source = "auth";      source = "auth";
  else if (flags & F_SERVER)   else if (flags & F_DNSSEC)
     {      {
         source = arg;
         verb = "to";
       }
      else if (flags & F_SERVER)
       {
       source = "forwarded";        source = "forwarded";
       verb = "to";        verb = "to";
     }      }
Line 1351  void log_query(unsigned int flags, char *name, struct  Line 2144  void log_query(unsigned int flags, char *name, struct 
       source = arg;        source = arg;
       verb = "from";        verb = "from";
     }      }
     else if (flags & F_IPSET)
       {
         source = type ? "ipset add" : "nftset add";
         dest = name;
         name = arg;
         verb = daemon->addrbuff;
       }
     else if (flags & F_STALE)
       source = "cached-stale";
   else    else
     source = "cached";      source = "cached";
       
  if (strlen(name) == 0)  if (name && !name[0])
     name = ".";      name = ".";
   
  my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);  if (option_bool(OPT_EXTRALOG))
} 
 
#ifdef HAVE_DNSSEC 
struct keydata *keydata_alloc(char *data, size_t len) 
{ 
  struct keydata *block, *ret = NULL; 
  struct keydata **prev = &ret; 
  while (len > 0) 
     {      {
      if (keyblock_free)      if (flags & F_NOEXTRA)
        {        my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
          block = keyblock_free; 
          keyblock_free = block->next; 
        } 
       else        else
         block = whine_malloc(sizeof(struct keydata));  
   
       if (!block)  
         {          {
          /* failed to alloc, free partial chain */           int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
          keydata_free(ret);           my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
          return NULL; 
         }          }
         
       memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);  
       data += KEYBLOCK_LEN;  
       len -= KEYBLOCK_LEN;  
       *prev = block;  
       prev = &block->next;  
       block->next = NULL;  
     }      }
    else
  return ret;    my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra);
 }  }
   
 void keydata_free(struct keydata *blocks)  
 {  
   struct keydata *tmp;  
   
   if (blocks)  
     {  
       for (tmp = blocks; tmp->next; tmp = tmp->next);  
       tmp->next = keyblock_free;  
       keyblock_free = blocks;  
     }  
 }  
 #endif  
   
         

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


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