Annotation of embedaddon/dnsmasq/src/cache.c, revision 1.1.1.5

1.1.1.5 ! misho       1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
1.1       misho       2: 
                      3:    This program is free software; you can redistribute it and/or modify
                      4:    it under the terms of the GNU General Public License as published by
                      5:    the Free Software Foundation; version 2 dated June, 1991, or
                      6:    (at your option) version 3 dated 29 June, 2007.
                      7:  
                      8:    This program is distributed in the hope that it will be useful,
                      9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
                     10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     11:    GNU General Public License for more details.
                     12:      
                     13:    You should have received a copy of the GNU General Public License
                     14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
                     15: */
                     16: 
                     17: #include "dnsmasq.h"
                     18: 
                     19: static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
                     20: #ifdef HAVE_DHCP
                     21: static struct crec *dhcp_spare = NULL;
                     22: #endif
                     23: static struct crec *new_chain = NULL;
1.1.1.4   misho      24: static int insert_error;
1.1       misho      25: static union bigname *big_free = NULL;
                     26: static int bignames_left, hash_size;
                     27: 
1.1.1.4   misho      28: static void make_non_terminals(struct crec *source);
                     29: static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
                     30:                                  time_t now,  unsigned long ttl, unsigned int flags);
1.1.1.5 ! misho      31: static void dump_cache_entry(struct crec *cache, time_t now);
1.1.1.4   misho      32: 
1.1       misho      33: /* type->string mapping: this is also used by the name-hash function as a mixing table. */
1.1.1.5 ! misho      34: /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
1.1       misho      35: static const struct {
                     36:   unsigned int type;
                     37:   const char * const name;
                     38: } typestr[] = {
1.1.1.5 ! misho      39:   { 1,   "A" }, /* a host address [RFC1035] */
        !            40:   { 2,   "NS" }, /* an authoritative name server [RFC1035] */
        !            41:   { 3,   "MD" }, /* a mail destination (OBSOLETE - use MX) [RFC1035] */
        !            42:   { 4,   "MF" }, /* a mail forwarder (OBSOLETE - use MX) [RFC1035] */
        !            43:   { 5,   "CNAME" }, /* the canonical name for an alias [RFC1035] */
        !            44:   { 6,   "SOA" }, /* marks the start of a zone of authority [RFC1035] */
        !            45:   { 7,   "MB" }, /* a mailbox domain name (EXPERIMENTAL) [RFC1035] */
        !            46:   { 8,   "MG" }, /* a mail group member (EXPERIMENTAL) [RFC1035] */
        !            47:   { 9,   "MR" }, /* a mail rename domain name (EXPERIMENTAL) [RFC1035] */
        !            48:   { 10,  "NULL" }, /* a null RR (EXPERIMENTAL) [RFC1035] */
        !            49:   { 11,  "WKS" }, /* a well known service description [RFC1035] */
        !            50:   { 12,  "PTR" }, /* a domain name pointer [RFC1035] */
        !            51:   { 13,  "HINFO" }, /* host information [RFC1035] */
        !            52:   { 14,  "MINFO" }, /* mailbox or mail list information [RFC1035] */
        !            53:   { 15,  "MX" }, /* mail exchange [RFC1035] */
        !            54:   { 16,  "TXT" }, /* text strings [RFC1035] */
        !            55:   { 17,  "RP" }, /* for Responsible Person [RFC1183] */
        !            56:   { 18,  "AFSDB" }, /* for AFS Data Base location [RFC1183][RFC5864] */
        !            57:   { 19,  "X25" }, /* for X.25 PSDN address [RFC1183] */
        !            58:   { 20,  "ISDN" }, /* for ISDN address [RFC1183] */
        !            59:   { 21,  "RT" }, /* for Route Through [RFC1183] */
        !            60:   { 22,  "NSAP" }, /* for NSAP address, NSAP style A record [RFC1706] */
        !            61:   { 23,  "NSAP_PTR" }, /* for domain name pointer, NSAP style [RFC1348][RFC1637][RFC1706] */
        !            62:   { 24,  "SIG" }, /* for security signature [RFC2535][RFC2536][RFC2537][RFC2931][RFC3008][RFC3110][RFC3755][RFC4034] */
        !            63:   { 25,  "KEY" }, /* for security key [RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110][RFC3755][RFC4034] */
        !            64:   { 26,  "PX" }, /* X.400 mail mapping information [RFC2163] */
        !            65:   { 27,  "GPOS" }, /* Geographical Position [RFC1712] */
        !            66:   { 28,  "AAAA" }, /* IP6 Address [RFC3596] */
        !            67:   { 29,  "LOC" }, /* Location Information [RFC1876] */
        !            68:   { 30,  "NXT" }, /* Next Domain (OBSOLETE) [RFC2535][RFC3755] */
        !            69:   { 31,  "EID" }, /* Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
        !            70:   { 32,  "NIMLOC" }, /* Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
        !            71:   { 33,  "SRV" }, /* Server Selection [1][RFC2782] */
        !            72:   { 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.] */
        !            73:   { 35,  "NAPTR" }, /* Naming Authority Pointer [RFC2168][RFC2915][RFC3403] */
        !            74:   { 36,  "KX" }, /* Key Exchanger [RFC2230] */
        !            75:   { 37,  "CERT" }, /* CERT [RFC4398] */
        !            76:   { 38,  "A6" }, /* A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] */
        !            77:   { 39,  "DNAME" }, /* DNAME [RFC6672] */
        !            78:   { 40,  "SINK" }, /* SINK [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink] 1997-11*/
        !            79:   { 41,  "OPT" }, /* OPT [RFC3225][RFC6891] */
        !            80:   { 42,  "APL" }, /* APL [RFC3123] */
        !            81:   { 43,  "DS" }, /* Delegation Signer [RFC3658][RFC4034] */
        !            82:   { 44,  "SSHFP" }, /* SSH Key Fingerprint [RFC4255] */
        !            83:   { 45,  "IPSECKEY" }, /* IPSECKEY [RFC4025] */
        !            84:   { 46,  "RRSIG" }, /* RRSIG [RFC3755][RFC4034] */
        !            85:   { 47,  "NSEC" }, /* NSEC [RFC3755][RFC4034][RFC9077] */
        !            86:   { 48,  "DNSKEY" }, /* DNSKEY [RFC3755][RFC4034] */
        !            87:   { 49,  "DHCID" }, /* DHCID [RFC4701] */
        !            88:   { 50,  "NSEC3" }, /* NSEC3 [RFC5155][RFC9077] */
        !            89:   { 51,  "NSEC3PARAM" }, /* NSEC3PARAM [RFC5155] */
        !            90:   { 52,  "TLSA" }, /* TLSA [RFC6698] */
        !            91:   { 53,  "SMIMEA" }, /* S/MIME cert association [RFC8162] SMIMEA/smimea-completed-template 2015-12-01*/
        !            92:   { 55,  "HIP" }, /* Host Identity Protocol [RFC8005] */
        !            93:   { 56,  "NINFO" }, /* NINFO [Jim_Reid] NINFO/ninfo-completed-template 2008-01-21*/
        !            94:   { 57,  "RKEY" }, /* RKEY [Jim_Reid] RKEY/rkey-completed-template 2008-01-21*/
        !            95:   { 58,  "TALINK" }, /* Trust Anchor LINK [Wouter_Wijngaards] TALINK/talink-completed-template 2010-02-17*/
        !            96:   { 59,  "CDS" }, /* Child DS [RFC7344] CDS/cds-completed-template 2011-06-06*/
        !            97:   { 60,  "CDNSKEY" }, /* DNSKEY(s) the Child wants reflected in DS [RFC7344] 2014-06-16*/
        !            98:   { 61,  "OPENPGPKEY" }, /* OpenPGP Key [RFC7929] OPENPGPKEY/openpgpkey-completed-template 2014-08-12*/
        !            99:   { 62,  "CSYNC" }, /* Child-To-Parent Synchronization [RFC7477] 2015-01-27*/
        !           100:   { 63,  "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/
        !           101:   { 64,  "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
        !           102:   { 65,  "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
        !           103:   { 99,  "SPF" }, /* [RFC7208] */
        !           104:   { 100, "UINFO" }, /* [IANA-Reserved] */
        !           105:   { 101, "UID" }, /* [IANA-Reserved] */
        !           106:   { 102, "GID" }, /* [IANA-Reserved] */
        !           107:   { 103, "UNSPEC" }, /* [IANA-Reserved] */
        !           108:   { 104, "NID" }, /* [RFC6742] ILNP/nid-completed-template */
        !           109:   { 105, "L32" }, /* [RFC6742] ILNP/l32-completed-template */
        !           110:   { 106, "L64" }, /* [RFC6742] ILNP/l64-completed-template */
        !           111:   { 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */
        !           112:   { 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/
        !           113:   { 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/
        !           114:   { 249, "TKEY" }, /* Transaction Key [RFC2930] */
        !           115:   { 250, "TSIG" }, /* Transaction Signature [RFC8945] */
        !           116:   { 251, "IXFR" }, /* incremental transfer [RFC1995] */
        !           117:   { 252, "AXFR" }, /* transfer of an entire zone [RFC1035][RFC5936] */
        !           118:   { 253, "MAILB" }, /* mailbox-related RRs (MB, MG or MR) [RFC1035] */
        !           119:   { 254, "MAILA" }, /* mail agent RRs (OBSOLETE - see MX) [RFC1035] */
        !           120:   { 255, "ANY" }, /* A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] */
        !           121:   { 256, "URI" }, /* URI [RFC7553] URI/uri-completed-template 2011-02-22*/
        !           122:   { 257, "CAA" }, /* Certification Authority Restriction [RFC8659] CAA/caa-completed-template 2011-04-07*/
        !           123:   { 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
        !           124:   { 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
        !           125:   { 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
        !           126:   { 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*/
        !           127:   { 32769,  "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
1.1       misho     128: };
                    129: 
                    130: static void cache_free(struct crec *crecp);
                    131: static void cache_unlink(struct crec *crecp);
                    132: static void cache_link(struct crec *crecp);
                    133: static void rehash(int size);
                    134: static void cache_hash(struct crec *crecp);
                    135: 
1.1.1.4   misho     136: void next_uid(struct crec *crecp)
1.1.1.2   misho     137: {
                    138:   static unsigned int uid = 0;
                    139: 
1.1.1.4   misho     140:   if (crecp->uid == UID_NONE)
                    141:     {
                    142:       uid++;
1.1.1.2   misho     143:   
1.1.1.4   misho     144:       /* uid == 0 used to indicate CNAME to interface name. */
                    145:       if (uid == UID_NONE)
                    146:        uid++;
                    147:       
                    148:       crecp->uid = uid;
                    149:     }
1.1.1.2   misho     150: }
                    151: 
1.1       misho     152: void cache_init(void)
                    153: {
                    154:   struct crec *crecp;
                    155:   int i;
1.1.1.2   misho     156:  
1.1       misho     157:   bignames_left = daemon->cachesize/10;
                    158:   
                    159:   if (daemon->cachesize > 0)
                    160:     {
                    161:       crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
                    162:       
                    163:       for (i=0; i < daemon->cachesize; i++, crecp++)
                    164:        {
                    165:          cache_link(crecp);
                    166:          crecp->flags = 0;
1.1.1.4   misho     167:          crecp->uid = UID_NONE;
1.1       misho     168:        }
                    169:     }
                    170:   
                    171:   /* create initial hash table*/
                    172:   rehash(daemon->cachesize);
                    173: }
                    174: 
                    175: /* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
                    176:    but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
                    177:    will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
                    178:    expand the table. */
                    179: static void rehash(int size)
                    180: {
                    181:   struct crec **new, **old, *p, *tmp;
                    182:   int i, new_size, old_size;
                    183: 
                    184:   /* hash_size is a power of two. */
                    185:   for (new_size = 64; new_size < size/10; new_size = new_size << 1);
                    186:   
                    187:   /* must succeed in getting first instance, failure later is non-fatal */
                    188:   if (!hash_table)
                    189:     new = safe_malloc(new_size * sizeof(struct crec *));
                    190:   else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
                    191:     return;
                    192: 
1.1.1.5 ! misho     193:   for (i = 0; i < new_size; i++)
1.1       misho     194:     new[i] = NULL;
                    195: 
                    196:   old = hash_table;
                    197:   old_size = hash_size;
                    198:   hash_table = new;
                    199:   hash_size = new_size;
                    200:   
                    201:   if (old)
                    202:     {
                    203:       for (i = 0; i < old_size; i++)
                    204:        for (p = old[i]; p ; p = tmp)
                    205:          {
                    206:            tmp = p->hash_next;
                    207:            cache_hash(p);
                    208:          }
                    209:       free(old);
                    210:     }
                    211: }
                    212:   
                    213: static struct crec **hash_bucket(char *name)
                    214: {
                    215:   unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
                    216:   const unsigned char *mix_tab = (const unsigned char*)typestr; 
                    217: 
                    218:   while((c = (unsigned char) *name++))
                    219:     {
                    220:       /* don't use tolower and friends here - they may be messed up by LOCALE */
                    221:       if (c >= 'A' && c <= 'Z')
                    222:        c += 'a' - 'A';
                    223:       val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c);
                    224:     } 
                    225:   
                    226:   /* hash_size is a power of two */
                    227:   return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
                    228: }
                    229: 
                    230: static void cache_hash(struct crec *crecp)
                    231: {
                    232:   /* maintain an invariant that all entries with F_REVERSE set
                    233:      are at the start of the hash-chain  and all non-reverse
                    234:      immortal entries are at the end of the hash-chain.
                    235:      This allows reverse searches and garbage collection to be optimised */
                    236: 
1.1.1.5 ! misho     237:   char *name = cache_get_name(crecp);
        !           238:   struct crec **up = hash_bucket(name);
        !           239:   unsigned int flags = crecp->flags & (F_IMMORTAL | F_REVERSE);
        !           240:   
        !           241:   if (!(flags & F_REVERSE))
1.1       misho     242:     {
                    243:       while (*up && ((*up)->flags & F_REVERSE))
                    244:        up = &((*up)->hash_next); 
                    245:       
1.1.1.5 ! misho     246:       if (flags & F_IMMORTAL)
1.1       misho     247:        while (*up && !((*up)->flags & F_IMMORTAL))
                    248:          up = &((*up)->hash_next);
                    249:     }
1.1.1.5 ! misho     250: 
        !           251:   /* Preserve order when inserting the same name multiple times.
        !           252:      Do not mess up the flag invariants. */
        !           253:   while (*up &&
        !           254:         hostname_isequal(cache_get_name(*up), name) &&
        !           255:         flags == ((*up)->flags & (F_IMMORTAL | F_REVERSE)))
        !           256:     up = &((*up)->hash_next);
        !           257:   
1.1       misho     258:   crecp->hash_next = *up;
                    259:   *up = crecp;
                    260: }
1.1.1.2   misho     261: 
                    262: static void cache_blockdata_free(struct crec *crecp)
                    263: {
1.1.1.4   misho     264:   if (!(crecp->flags & F_NEG))
                    265:     {
                    266:       if (crecp->flags & F_SRV)
                    267:        blockdata_free(crecp->addr.srv.target);
                    268: #ifdef HAVE_DNSSEC
                    269:       else if (crecp->flags & F_DNSKEY)
                    270:        blockdata_free(crecp->addr.key.keydata);
                    271:       else if (crecp->flags & F_DS)
                    272:        blockdata_free(crecp->addr.ds.keydata);
1.1.1.2   misho     273: #endif
1.1.1.4   misho     274:     }
                    275: }
1.1.1.2   misho     276: 
1.1       misho     277: static void cache_free(struct crec *crecp)
                    278: {
                    279:   crecp->flags &= ~F_FORWARD;
                    280:   crecp->flags &= ~F_REVERSE;
1.1.1.4   misho     281:   crecp->uid = UID_NONE; /* invalidate CNAMES pointing to this. */
1.1.1.2   misho     282: 
1.1       misho     283:   if (cache_tail)
                    284:     cache_tail->next = crecp;
                    285:   else
                    286:     cache_head = crecp;
                    287:   crecp->prev = cache_tail;
                    288:   crecp->next = NULL;
                    289:   cache_tail = crecp;
                    290:   
                    291:   /* retrieve big name for further use. */
                    292:   if (crecp->flags & F_BIGNAME)
                    293:     {
                    294:       crecp->name.bname->next = big_free;
                    295:       big_free = crecp->name.bname;
                    296:       crecp->flags &= ~F_BIGNAME;
                    297:     }
1.1.1.2   misho     298: 
                    299:   cache_blockdata_free(crecp);
1.1       misho     300: }    
                    301: 
                    302: /* insert a new cache entry at the head of the list (youngest entry) */
                    303: static void cache_link(struct crec *crecp)
                    304: {
                    305:   if (cache_head) /* check needed for init code */
                    306:     cache_head->prev = crecp;
                    307:   crecp->next = cache_head;
                    308:   crecp->prev = NULL;
                    309:   cache_head = crecp;
                    310:   if (!cache_tail)
                    311:     cache_tail = crecp;
                    312: }
                    313: 
                    314: /* remove an arbitrary cache entry for promotion */ 
                    315: static void cache_unlink (struct crec *crecp)
                    316: {
                    317:   if (crecp->prev)
                    318:     crecp->prev->next = crecp->next;
                    319:   else
                    320:     cache_head = crecp->next;
                    321: 
                    322:   if (crecp->next)
                    323:     crecp->next->prev = crecp->prev;
                    324:   else
                    325:     cache_tail = crecp->prev;
                    326: }
                    327: 
                    328: char *cache_get_name(struct crec *crecp)
                    329: {
                    330:   if (crecp->flags & F_BIGNAME)
                    331:     return crecp->name.bname->name;
                    332:   else if (crecp->flags & F_NAMEP) 
                    333:     return crecp->name.namep;
                    334:   
                    335:   return crecp->name.sname;
                    336: }
                    337: 
1.1.1.2   misho     338: char *cache_get_cname_target(struct crec *crecp)
                    339: {
1.1.1.4   misho     340:   if (crecp->addr.cname.is_name_ptr)
                    341:      return crecp->addr.cname.target.name;
                    342:   else
1.1.1.2   misho     343:     return cache_get_name(crecp->addr.cname.target.cache);
                    344: }
                    345: 
                    346: 
                    347: 
1.1       misho     348: struct crec *cache_enumerate(int init)
                    349: {
                    350:   static int bucket;
                    351:   static struct crec *cache;
                    352: 
                    353:   if (init)
                    354:     {
                    355:       bucket = 0;
                    356:       cache = NULL;
                    357:     }
                    358:   else if (cache && cache->hash_next)
                    359:     cache = cache->hash_next;
                    360:   else
                    361:     {
                    362:        cache = NULL; 
                    363:        while (bucket < hash_size)
                    364:         if ((cache = hash_table[bucket++]))
                    365:           break;
                    366:     }
                    367:   
                    368:   return cache;
                    369: }
                    370: 
                    371: static int is_outdated_cname_pointer(struct crec *crecp)
                    372: {
1.1.1.4   misho     373:   if (!(crecp->flags & F_CNAME) || crecp->addr.cname.is_name_ptr)
1.1       misho     374:     return 0;
                    375:   
                    376:   /* NB. record may be reused as DS or DNSKEY, where uid is 
                    377:      overloaded for something completely different */
1.1.1.2   misho     378:   if (crecp->addr.cname.target.cache && 
1.1.1.4   misho     379:       !(crecp->addr.cname.target.cache->flags & (F_DNSKEY | F_DS)) &&
1.1.1.2   misho     380:       crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)
1.1       misho     381:     return 0;
                    382:   
                    383:   return 1;
                    384: }
                    385: 
                    386: static int is_expired(time_t now, struct crec *crecp)
                    387: {
1.1.1.5 ! misho     388:   /* Don't dump expired entries if they are within the accepted timeout range.
        !           389:      The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
        !           390:      Possible values for daemon->cache_max_expiry:
        !           391:       -1  == serve cached content regardless how long ago it expired
        !           392:        0  == the option is disabled, expired content isn't served
        !           393:       <n> == serve cached content only if it expire less than <n> seconds
        !           394:              ago (where n is a positive integer) */
        !           395:   if (daemon->cache_max_expiry != 0 &&
        !           396:       (daemon->cache_max_expiry == -1 ||
        !           397:        difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
        !           398:       !(crecp->flags & (F_DS | F_DNSKEY)))
        !           399:     return 0;
        !           400: 
1.1       misho     401:   if (crecp->flags & F_IMMORTAL)
                    402:     return 0;
                    403: 
                    404:   if (difftime(now, crecp->ttd) < 0)
                    405:     return 0;
                    406:   
                    407:   return 1;
                    408: }
                    409: 
1.1.1.5 ! misho     410: /* Remove entries with a given UID from the cache */
        !           411: unsigned int cache_remove_uid(const unsigned int uid)
        !           412: {
        !           413:   int i;
        !           414:   unsigned int removed = 0;
        !           415:   struct crec *crecp, **up;
        !           416: 
        !           417:   for (i = 0; i < hash_size; i++)
        !           418:     for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
        !           419:       if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
        !           420:        {
        !           421:          *up = crecp->hash_next;
        !           422:          free(crecp);
        !           423:          removed++;
        !           424:        }
        !           425:       else
        !           426:        up = &crecp->hash_next;
        !           427:   
        !           428:   return removed;
        !           429: }
        !           430: 
1.1.1.4   misho     431: static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
                    432:                                    unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
1.1       misho     433: {
                    434:   /* Scan and remove old entries.
                    435:      If (flags & F_FORWARD) then remove any forward entries for name and any expired
                    436:      entries but only in the same hash bucket as name.
                    437:      If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
                    438:      entries in the whole cache.
                    439:      If (flags == 0) remove any expired entries in the whole cache. 
                    440: 
1.1.1.3   misho     441:      In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer
                    442:      to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
1.1       misho     443: 
                    444:      We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
1.1.1.4   misho     445:      so that when we hit an entry which isn't reverse and is immortal, we're done. 
                    446: 
                    447:      If we free a crec which is a CNAME target, return the entry and uid in target_crec and target_uid.
                    448:      This entry will get re-used with the same name, to preserve CNAMEs. */
1.1       misho     449:  
                    450:   struct crec *crecp, **up;
1.1.1.4   misho     451: 
                    452:   (void)class;
1.1       misho     453:   
                    454:   if (flags & F_FORWARD)
                    455:     {
                    456:       for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
1.1.1.2   misho     457:        {
                    458:          if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
                    459:            {
                    460:              /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
1.1.1.5 ! misho     461:              if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || 
1.1.1.2   misho     462:                  (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
                    463:                {
                    464:                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
1.1.1.3   misho     465:                    return crecp;
1.1.1.2   misho     466:                  *up = crecp->hash_next;
1.1.1.4   misho     467:                  /* If this record is for the name we're inserting and is the target
                    468:                     of a CNAME record. Make the new record for the same name, in the same
                    469:                     crec, with the same uid to avoid breaking the existing CNAME. */
                    470:                  if (crecp->uid != UID_NONE)
                    471:                    {
                    472:                      if (target_crec)
                    473:                        *target_crec = crecp;
                    474:                      if (target_uid)
                    475:                        *target_uid = crecp->uid;
                    476:                    }
1.1.1.2   misho     477:                  cache_unlink(crecp);
                    478:                  cache_free(crecp);
                    479:                  continue;
                    480:                }
                    481:              
                    482: #ifdef HAVE_DNSSEC
1.1.1.3   misho     483:              /* Deletion has to be class-sensitive for DS and DNSKEY */
1.1.1.4   misho     484:              if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == class)
1.1.1.2   misho     485:                {
                    486:                  if (crecp->flags & F_CONFIG)
1.1.1.3   misho     487:                    return crecp;
1.1.1.2   misho     488:                  *up = crecp->hash_next;
                    489:                  cache_unlink(crecp);
                    490:                  cache_free(crecp);
                    491:                  continue;
                    492:                }
                    493: #endif
                    494:            }
1.1.1.4   misho     495: 
                    496:          if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
                    497:            { 
                    498:              *up = crecp->hash_next;
                    499:              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                    500:                {
                    501:                  cache_unlink(crecp);
                    502:                  cache_free(crecp);
                    503:                }
                    504:              continue;
                    505:            } 
                    506:          
1.1       misho     507:          up = &crecp->hash_next;
1.1.1.2   misho     508:        }
1.1       misho     509:     }
                    510:   else
                    511:     {
                    512:       int i;
                    513:       int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
1.1.1.4   misho     514: 
1.1       misho     515:       for (i = 0; i < hash_size; i++)
                    516:        for (crecp = hash_table[i], up = &hash_table[i]; 
                    517:             crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
                    518:             crecp = crecp->hash_next)
                    519:          if (is_expired(now, crecp))
                    520:            {
                    521:              *up = crecp->hash_next;
1.1.1.2   misho     522:              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
1.1       misho     523:                { 
                    524:                  cache_unlink(crecp);
                    525:                  cache_free(crecp);
                    526:                }
                    527:            }
1.1.1.2   misho     528:          else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
1.1       misho     529:                   (flags & crecp->flags & F_REVERSE) && 
                    530:                   (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
1.1.1.5 ! misho     531:                   addr && memcmp(&crecp->addr, addr, addrlen) == 0)
1.1       misho     532:            {
                    533:              *up = crecp->hash_next;
                    534:              cache_unlink(crecp);
                    535:              cache_free(crecp);
                    536:            }
                    537:          else
                    538:            up = &crecp->hash_next;
                    539:     }
                    540:   
1.1.1.3   misho     541:   return NULL;
1.1       misho     542: }
                    543: 
                    544: /* Note: The normal calling sequence is
                    545:    cache_start_insert
                    546:    cache_insert * n
                    547:    cache_end_insert
                    548: 
                    549:    but an abort can cause the cache_end_insert to be missed 
                    550:    in which can the next cache_start_insert cleans things up. */
                    551: 
                    552: void cache_start_insert(void)
                    553: {
                    554:   /* Free any entries which didn't get committed during the last
                    555:      insert due to error.
                    556:   */
                    557:   while (new_chain)
                    558:     {
                    559:       struct crec *tmp = new_chain->next;
                    560:       cache_free(new_chain);
                    561:       new_chain = tmp;
                    562:     }
                    563:   new_chain = NULL;
                    564:   insert_error = 0;
                    565: }
                    566: 
1.1.1.4   misho     567: struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
                    568:                          time_t now,  unsigned long ttl, unsigned int flags)
                    569: {
                    570: #ifdef HAVE_DNSSEC
                    571:   if (flags & (F_DNSKEY | F_DS)) 
                    572:     {
                    573:       /* The DNSSEC validation process works by getting needed records into the
                    574:         cache, then retrying the validation until they are all in place.
                    575:         This can be messed up by very short TTLs, and _really_ messed up by
                    576:         zero TTLs, so we force the TTL to be at least long enough to do a validation.
                    577:         Ideally, we should use some kind of reference counting so that records are
                    578:         locked until the validation that asked for them is complete, but this
                    579:         is much easier, and just as effective. */
                    580:       if (ttl < DNSSEC_MIN_TTL)
                    581:        ttl = DNSSEC_MIN_TTL;
                    582:     }
                    583:   else
                    584: #endif
1.1.1.2   misho     585:     {
                    586:       if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
                    587:        ttl = daemon->max_cache_ttl;
1.1.1.3   misho     588:       if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
                    589:        ttl = daemon->min_cache_ttl;
1.1.1.4   misho     590:     }  
                    591:   
                    592:   return really_insert(name, addr, class, now, ttl, flags);
                    593: }
1.1       misho     594: 
1.1.1.4   misho     595: 
                    596: static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
                    597:                                  time_t now,  unsigned long ttl, unsigned int flags)
                    598: {
                    599:   struct crec *new, *target_crec = NULL;
                    600:   union bigname *big_name = NULL;
1.1.1.5 ! misho     601:   int freed_all = (flags & F_REVERSE);
        !           602:   struct crec *free_avail = NULL;
1.1.1.4   misho     603:   unsigned int target_uid;
                    604:   
1.1       misho     605:   /* if previous insertion failed give up now. */
                    606:   if (insert_error)
                    607:     return NULL;
1.1.1.4   misho     608: 
                    609:   /* we don't cache zero-TTL records. */
                    610:   if (ttl == 0)
                    611:     {
                    612:       insert_error = 1;
                    613:       return NULL;
                    614:     }
1.1.1.2   misho     615:   
1.1       misho     616:   /* First remove any expired entries and entries for the name/address we
1.1.1.3   misho     617:      are currently inserting. */
1.1.1.4   misho     618:   if ((new = cache_scan_free(name, addr, class, now, flags, &target_crec, &target_uid)))
1.1       misho     619:     {
1.1.1.3   misho     620:       /* We're trying to insert a record over one from 
                    621:         /etc/hosts or DHCP, or other config. If the 
1.1.1.4   misho     622:         existing record is for an A or AAAA or CNAME and
1.1.1.3   misho     623:         the record we're trying to insert is the same, 
                    624:         just drop the insert, but don't error the whole process. */
                    625:       if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD) && addr)
                    626:        {
                    627:          if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
1.1.1.4   misho     628:              new->addr.addr4.s_addr == addr->addr4.s_addr)
1.1.1.3   misho     629:            return new;
                    630:          else if ((flags & F_IPV6) && (new->flags & F_IPV6) &&
1.1.1.4   misho     631:                   IN6_ARE_ADDR_EQUAL(&new->addr.addr6, &addr->addr6))
1.1.1.3   misho     632:            return new;
                    633:        }
1.1.1.4   misho     634: 
1.1       misho     635:       insert_error = 1;
                    636:       return NULL;
                    637:     }
                    638:   
                    639:   /* Now get a cache entry from the end of the LRU list */
1.1.1.4   misho     640:   if (!target_crec)
                    641:     while (1) {
                    642:       if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
                    643:        {
                    644:          insert_error = 1;
                    645:          return NULL;
                    646:        }
                    647:       
                    648:       /* Free entry at end of LRU list, use it. */
                    649:       if (!(new->flags & (F_FORWARD | F_REVERSE)))
1.1.1.5 ! misho     650:        break; 
1.1.1.2   misho     651: 
1.1.1.4   misho     652:       /* End of LRU list is still in use: if we didn't scan all the hash
                    653:         chains for expired entries do that now. If we already tried that
                    654:         then it's time to start spilling things. */
                    655:       
                    656:       /* If free_avail set, we believe that an entry has been freed.
                    657:         Bugs have been known to make this not true, resulting in
                    658:         a tight loop here. If that happens, abandon the
                    659:         insert. Once in this state, all inserts will probably fail. */
                    660:       if (free_avail)
                    661:        {
1.1.1.5 ! misho     662:          my_syslog(LOG_ERR, _("Internal error in cache."));
        !           663:          /* Log the entry we tried to delete. */
        !           664:          dump_cache_entry(free_avail, now);
1.1.1.4   misho     665:          insert_error = 1;
                    666:          return NULL;
                    667:        }
                    668:       
                    669:       if (freed_all)
                    670:        {
                    671:          /* For DNSSEC records, uid holds class. */
1.1.1.5 ! misho     672:          free_avail = new; /* Must be free space now. */
        !           673:          
        !           674:          /* condition valid when stale-caching */
        !           675:          if (difftime(now, new->ttd) < 0)
        !           676:            daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
        !           677:          
        !           678:          cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL); 
1.1.1.4   misho     679:        }
                    680:       else
                    681:        {
                    682:          cache_scan_free(NULL, NULL, class, now, 0, NULL, NULL);
                    683:          freed_all = 1;
                    684:        }
                    685:     }
                    686:       
                    687:   /* Check if we need to and can allocate extra memory for a long name.
                    688:      If that fails, give up now, always succeed for DNSSEC records. */
                    689:   if (name && (strlen(name) > SMALLDNAME-1))
                    690:     {
                    691:       if (big_free)
                    692:        { 
                    693:          big_name = big_free;
                    694:          big_free = big_free->next;
                    695:        }
                    696:       else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
                    697:               !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
                    698:        {
                    699:          insert_error = 1;
                    700:          return NULL;
                    701:        }
                    702:       else if (bignames_left != 0)
                    703:        bignames_left--;
                    704:       
                    705:     }
1.1       misho     706: 
1.1.1.4   misho     707:   /* If we freed a cache entry for our name which was a CNAME target, use that.
                    708:      and preserve the uid, so that existing CNAMES are not broken. */
                    709:   if (target_crec)
                    710:     {
                    711:       new = target_crec;
                    712:       new->uid = target_uid;
                    713:     }
                    714:   
                    715:   /* Got the rest: finally grab entry. */
                    716:   cache_unlink(new);
1.1       misho     717:   
                    718:   new->flags = flags;
                    719:   if (big_name)
                    720:     {
                    721:       new->name.bname = big_name;
                    722:       new->flags |= F_BIGNAME;
                    723:     }
                    724: 
                    725:   if (name)
                    726:     strcpy(cache_get_name(new), name);
                    727:   else
                    728:     *cache_get_name(new) = 0;
                    729: 
1.1.1.2   misho     730: #ifdef HAVE_DNSSEC
1.1.1.4   misho     731:   if (flags & (F_DS | F_DNSKEY))
                    732:     new->uid = class;
1.1.1.2   misho     733: #endif
1.1.1.4   misho     734: 
                    735:   if (addr)
                    736:     new->addr = *addr; 
1.1       misho     737: 
                    738:   new->ttd = now + (time_t)ttl;
                    739:   new->next = new_chain;
                    740:   new_chain = new;
1.1.1.5 ! misho     741:   
1.1       misho     742:   return new;
                    743: }
                    744: 
                    745: /* after end of insertion, commit the new entries */
                    746: void cache_end_insert(void)
                    747: {
                    748:   if (insert_error)
                    749:     return;
                    750:   
                    751:   while (new_chain)
                    752:     { 
                    753:       struct crec *tmp = new_chain->next;
                    754:       /* drop CNAMEs which didn't find a target. */
                    755:       if (is_outdated_cname_pointer(new_chain))
                    756:        cache_free(new_chain);
                    757:       else
                    758:        {
                    759:          cache_hash(new_chain);
                    760:          cache_link(new_chain);
1.1.1.4   misho     761:          daemon->metrics[METRIC_DNS_CACHE_INSERTED]++;
                    762: 
                    763:          /* If we're a child process, send this cache entry up the pipe to the master.
                    764:             The marshalling process is rather nasty. */
                    765:          if (daemon->pipe_to_parent != -1)
                    766:            {
                    767:              char *name = cache_get_name(new_chain);
                    768:              ssize_t m = strlen(name);
                    769:              unsigned int flags = new_chain->flags;
                    770: #ifdef HAVE_DNSSEC
                    771:              u16 class = new_chain->uid;
                    772: #endif
                    773:              
                    774:              read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
                    775:              read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0);
                    776:              read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
                    777:              read_write(daemon->pipe_to_parent, (unsigned  char *)&flags, sizeof(flags), 0);
                    778: 
                    779:              if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
                    780:                read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
                    781:              if (flags & F_SRV)
                    782:                {
                    783:                  /* A negative SRV entry is possible and has no data, obviously. */
                    784:                  if (!(flags & F_NEG))
                    785:                    blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent);
                    786:                }
                    787: #ifdef HAVE_DNSSEC
                    788:              if (flags & F_DNSKEY)
                    789:                {
                    790:                  read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
                    791:                  blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
                    792:                }
                    793:              else if (flags & F_DS)
                    794:                {
                    795:                  read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
                    796:                  /* A negative DS entry is possible and has no data, obviously. */
                    797:                  if (!(flags & F_NEG))
                    798:                    blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent);
                    799:                }
                    800: #endif
                    801:            }
1.1       misho     802:        }
1.1.1.4   misho     803:       
1.1       misho     804:       new_chain = tmp;
                    805:     }
1.1.1.4   misho     806: 
                    807:   /* signal end of cache insert in master process */
                    808:   if (daemon->pipe_to_parent != -1)
                    809:     {
                    810:       ssize_t m = -1;
                    811:       read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
                    812:     }
                    813:       
1.1       misho     814:   new_chain = NULL;
                    815: }
                    816: 
1.1.1.4   misho     817: 
                    818: /* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */
                    819: int cache_recv_insert(time_t now, int fd)
                    820: {
                    821:   ssize_t m;
                    822:   union all_addr addr;
                    823:   unsigned long ttl;
                    824:   time_t ttd;
                    825:   unsigned int flags;
                    826:   struct crec *crecp = NULL;
                    827:   
                    828:   cache_start_insert();
                    829:   
                    830:   while(1)
                    831:     {
                    832:  
                    833:       if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
                    834:        return 0;
                    835:       
                    836:       if (m == -1)
                    837:        {
                    838:          cache_end_insert();
                    839:          return 1;
                    840:        }
                    841: 
                    842:       if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) ||
                    843:          !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) ||
                    844:          !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1))
                    845:        return 0;
                    846: 
                    847:       daemon->namebuff[m] = 0;
                    848: 
                    849:       ttl = difftime(ttd, now);
                    850:       
                    851:       if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
                    852:        {
                    853:          unsigned short class = C_IN;
                    854: 
                    855:          if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
                    856:            return 0;
                    857: 
                    858:          if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen)))
                    859:            return 0;
                    860:        
                    861: #ifdef HAVE_DNSSEC
                    862:           if (flags & F_DNSKEY)
                    863:             {
                    864:               if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
                    865:                   !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
                    866:                 return 0;
                    867:             }
                    868:           else  if (flags & F_DS)
                    869:             {
                    870:                if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
                    871:                    (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
                    872:                  return 0;
                    873:             }
                    874: #endif
                    875:               
                    876:          crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
                    877:        }
                    878:       else if (flags & F_CNAME)
                    879:        {
                    880:          struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
                    881:          /* This relies on the fact that the target of a CNAME immediately precedes
                    882:             it because of the order of extraction in extract_addresses, and
                    883:             the order reversal on the new_chain. */
                    884:          if (newc)
                    885:            {
                    886:               newc->addr.cname.is_name_ptr = 0;
                    887:               
                    888:               if (!crecp)
                    889:                 newc->addr.cname.target.cache = NULL;
                    890:               else
                    891:                {
                    892:                  next_uid(crecp);
                    893:                  newc->addr.cname.target.cache = crecp;
                    894:                  newc->addr.cname.uid = crecp->uid;
                    895:                }
                    896:            }
                    897:        }
                    898:     }
                    899: }
                    900:        
                    901: int cache_find_non_terminal(char *name, time_t now)
                    902: {
                    903:   struct crec *crecp;
                    904: 
                    905:   for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
                    906:     if (!is_outdated_cname_pointer(crecp) &&
                    907:        !is_expired(now, crecp) &&
                    908:        (crecp->flags & F_FORWARD) &&
                    909:        !(crecp->flags & F_NXDOMAIN) && 
                    910:        hostname_isequal(name, cache_get_name(crecp)))
                    911:       return 1;
                    912: 
                    913:   return 0;
                    914: }
                    915: 
1.1.1.2   misho     916: struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
1.1       misho     917: {
                    918:   struct crec *ans;
1.1.1.5 ! misho     919:   int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
1.1       misho     920: 
1.1.1.2   misho     921:   prot &= ~F_NO_RR;
                    922:   
1.1       misho     923:   if (crecp) /* iterating */
                    924:     ans = crecp->next;
                    925:   else
                    926:     {
                    927:       /* first search, look for relevant entries and push to top of list
                    928:         also free anything which has expired */
                    929:       struct crec *next, **up, **insert = NULL, **chainp = &ans;
1.1.1.4   misho     930:       unsigned int ins_flags = 0;
1.1       misho     931:       
                    932:       for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
                    933:        {
                    934:          next = crecp->hash_next;
                    935:          
                    936:          if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
                    937:            {
                    938:              if ((crecp->flags & F_FORWARD) && 
                    939:                  (crecp->flags & prot) &&
                    940:                  hostname_isequal(cache_get_name(crecp), name))
                    941:                {
1.1.1.2   misho     942:                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
1.1       misho     943:                    {
                    944:                      *chainp = crecp;
                    945:                      chainp = &crecp->next;
                    946:                    }
                    947:                  else
                    948:                    {
                    949:                      cache_unlink(crecp);
                    950:                      cache_link(crecp);
                    951:                    }
                    952:                      
                    953:                  /* Move all but the first entry up the hash chain
                    954:                     this implements round-robin. 
                    955:                     Make sure that re-ordering doesn't break the hash-chain
                    956:                     order invariants. 
                    957:                  */
                    958:                  if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
                    959:                    {
                    960:                      *up = crecp->hash_next;
                    961:                      crecp->hash_next = *insert;
                    962:                      *insert = crecp;
                    963:                      insert = &crecp->hash_next;
                    964:                    }
                    965:                  else
                    966:                    {
1.1.1.2   misho     967:                      if (!insert && !no_rr)
1.1       misho     968:                        {
                    969:                          insert = up;
                    970:                          ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
                    971:                        }
                    972:                      up = &crecp->hash_next; 
                    973:                    }
                    974:                }
                    975:              else
                    976:                /* case : not expired, incorrect entry. */
                    977:                up = &crecp->hash_next; 
                    978:            }
                    979:          else
                    980:            {
                    981:              /* expired entry, free it */
                    982:              *up = crecp->hash_next;
1.1.1.2   misho     983:              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
1.1       misho     984:                { 
                    985:                  cache_unlink(crecp);
                    986:                  cache_free(crecp);
                    987:                }
                    988:            }
                    989:        }
                    990:          
                    991:       *chainp = cache_head;
                    992:     }
                    993: 
                    994:   if (ans && 
                    995:       (ans->flags & F_FORWARD) &&
1.1.1.2   misho     996:       (ans->flags & prot) &&     
1.1       misho     997:       hostname_isequal(cache_get_name(ans), name))
                    998:     return ans;
                    999:   
                   1000:   return NULL;
                   1001: }
                   1002: 
1.1.1.4   misho    1003: struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr, 
1.1.1.2   misho    1004:                                time_t now, unsigned int prot)
1.1       misho    1005: {
                   1006:   struct crec *ans;
                   1007:   int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
                   1008:   
                   1009:   if (crecp) /* iterating */
                   1010:     ans = crecp->next;
                   1011:   else
                   1012:     {  
                   1013:       /* first search, look for relevant entries and push to top of list
                   1014:         also free anything which has expired. All the reverse entries are at the
                   1015:         start of the hash chain, so we can give up when we find the first 
                   1016:         non-REVERSE one.  */
                   1017:        int i;
                   1018:        struct crec **up, **chainp = &ans;
                   1019:        
                   1020:        for (i=0; i<hash_size; i++)
                   1021:         for (crecp = hash_table[i], up = &hash_table[i]; 
                   1022:              crecp && (crecp->flags & F_REVERSE);
                   1023:              crecp = crecp->hash_next)
                   1024:           if (!is_expired(now, crecp))
                   1025:             {      
                   1026:               if ((crecp->flags & prot) &&
1.1.1.4   misho    1027:                   memcmp(&crecp->addr, addr, addrlen) == 0)
1.1       misho    1028:                 {          
1.1.1.2   misho    1029:                   if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
1.1       misho    1030:                     {
                   1031:                       *chainp = crecp;
                   1032:                       chainp = &crecp->next;
                   1033:                     }
                   1034:                   else
                   1035:                     {
                   1036:                       cache_unlink(crecp);
                   1037:                       cache_link(crecp);
                   1038:                     }
                   1039:                 }
                   1040:               up = &crecp->hash_next;
                   1041:             }
                   1042:           else
                   1043:             {
                   1044:               *up = crecp->hash_next;
1.1.1.2   misho    1045:               if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
1.1       misho    1046:                 {
                   1047:                   cache_unlink(crecp);
                   1048:                   cache_free(crecp);
                   1049:                 }
                   1050:             }
                   1051:        
                   1052:        *chainp = cache_head;
                   1053:     }
                   1054:   
                   1055:   if (ans && 
                   1056:       (ans->flags & F_REVERSE) &&
                   1057:       (ans->flags & prot) &&
1.1.1.4   misho    1058:       memcmp(&ans->addr, addr, addrlen) == 0)
1.1       misho    1059:     return ans;
                   1060:   
                   1061:   return NULL;
                   1062: }
                   1063: 
1.1.1.4   misho    1064: static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen, 
1.1.1.2   misho    1065:                            unsigned int index, struct crec **rhash, int hashsz)
1.1       misho    1066: {
1.1.1.4   misho    1067:   int i;
1.1       misho    1068:   unsigned int j; 
1.1.1.5 ! misho    1069:   struct crec *lookup = NULL;
1.1       misho    1070: 
                   1071:   /* Remove duplicates in hosts files. */
1.1.1.5 ! misho    1072:   while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
        !          1073:     if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
        !          1074:       {
        !          1075:        free(cache);
        !          1076:        return;
        !          1077:       }
1.1.1.4   misho    1078:     
1.1       misho    1079:   /* Ensure there is only one address -> name mapping (first one trumps) 
                   1080:      We do this by steam here, The entries are kept in hash chains, linked
                   1081:      by ->next (which is unused at this point) held in hash buckets in
                   1082:      the array rhash, hashed on address. Note that rhash and the values
                   1083:      in ->next are only valid  whilst reading hosts files: the buckets are
                   1084:      then freed, and the ->next pointer used for other things. 
                   1085:      Only insert each unique address once into this hashing structure.
                   1086: 
                   1087:      This complexity avoids O(n^2) divergent CPU use whilst reading
1.1.1.3   misho    1088:      large (10000 entry) hosts files. 
                   1089: 
                   1090:      Note that we only do this process when bulk-reading hosts files, 
                   1091:      for incremental reads, rhash is NULL, and we use cache lookups
                   1092:      instead.
                   1093:   */
1.1       misho    1094:   
1.1.1.3   misho    1095:   if (rhash)
1.1       misho    1096:     {
1.1.1.3   misho    1097:       /* hash address */
                   1098:       for (j = 0, i = 0; i < addrlen; i++)
                   1099:        j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
                   1100:       
                   1101:       for (lookup = rhash[j]; lookup; lookup = lookup->next)
                   1102:        if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
1.1.1.4   misho    1103:            memcmp(&lookup->addr, addr, addrlen) == 0)
1.1.1.3   misho    1104:          {
                   1105:            cache->flags &= ~F_REVERSE;
                   1106:            break;
                   1107:          }
                   1108:       
                   1109:       /* maintain address hash chain, insert new unique address */
                   1110:       if (!lookup)
                   1111:        {
                   1112:          cache->next = rhash[j];
                   1113:          rhash[j] = cache;
                   1114:        }
1.1       misho    1115:     }
1.1.1.3   misho    1116:   else
                   1117:     {
                   1118:       /* incremental read, lookup in cache */
                   1119:       lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
                   1120:       if (lookup && lookup->flags & F_HOSTS)
                   1121:        cache->flags &= ~F_REVERSE;
                   1122:     }
                   1123: 
1.1       misho    1124:   cache->uid = index;
1.1.1.4   misho    1125:   memcpy(&cache->addr, addr, addrlen);  
1.1       misho    1126:   cache_hash(cache);
1.1.1.4   misho    1127:   make_non_terminals(cache);
1.1       misho    1128: }
                   1129: 
                   1130: static int eatspace(FILE *f)
                   1131: {
                   1132:   int c, nl = 0;
                   1133: 
                   1134:   while (1)
                   1135:     {
                   1136:       if ((c = getc(f)) == '#')
                   1137:        while (c != '\n' && c != EOF)
                   1138:          c = getc(f);
                   1139:       
                   1140:       if (c == EOF)
                   1141:        return 1;
                   1142: 
                   1143:       if (!isspace(c))
                   1144:        {
                   1145:          ungetc(c, f);
                   1146:          return nl;
                   1147:        }
                   1148: 
                   1149:       if (c == '\n')
1.1.1.4   misho    1150:        nl++;
1.1       misho    1151:     }
                   1152: }
                   1153:         
                   1154: static int gettok(FILE *f, char *token)
                   1155: {
                   1156:   int c, count = 0;
                   1157:  
                   1158:   while (1)
                   1159:     {
                   1160:       if ((c = getc(f)) == EOF)
1.1.1.4   misho    1161:        return (count == 0) ? -1 : 1;
1.1       misho    1162: 
                   1163:       if (isspace(c) || c == '#')
                   1164:        {
                   1165:          ungetc(c, f);
                   1166:          return eatspace(f);
                   1167:        }
                   1168:       
                   1169:       if (count < (MAXDNAME - 1))
                   1170:        {
                   1171:          token[count++] = c;
                   1172:          token[count] = 0;
                   1173:        }
                   1174:     }
                   1175: }
                   1176: 
1.1.1.3   misho    1177: int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
1.1       misho    1178: {  
                   1179:   FILE *f = fopen(filename, "r");
                   1180:   char *token = daemon->namebuff, *domain_suffix = NULL;
1.1.1.5 ! misho    1181:   int names_done = 0, name_count = cache_size, lineno = 1;
1.1.1.4   misho    1182:   unsigned int flags = 0;
                   1183:   union all_addr addr;
1.1       misho    1184:   int atnl, addrlen = 0;
                   1185: 
                   1186:   if (!f)
                   1187:     {
                   1188:       my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
1.1.1.3   misho    1189:       return cache_size;
1.1       misho    1190:     }
                   1191:   
1.1.1.4   misho    1192:   lineno += eatspace(f);
1.1       misho    1193:   
1.1.1.4   misho    1194:   while ((atnl = gettok(f, token)) != -1)
1.1       misho    1195:     {
                   1196:       if (inet_pton(AF_INET, token, &addr) > 0)
                   1197:        {
                   1198:          flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
                   1199:          addrlen = INADDRSZ;
1.1.1.4   misho    1200:          domain_suffix = get_domain(addr.addr4);
1.1       misho    1201:        }
                   1202:       else if (inet_pton(AF_INET6, token, &addr) > 0)
                   1203:        {
                   1204:          flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
                   1205:          addrlen = IN6ADDRSZ;
1.1.1.4   misho    1206:          domain_suffix = get_domain6(&addr.addr6);
1.1       misho    1207:        }
                   1208:       else
                   1209:        {
                   1210:          my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); 
                   1211:          while (atnl == 0)
                   1212:            atnl = gettok(f, token);
1.1.1.4   misho    1213:          lineno += atnl;
1.1       misho    1214:          continue;
                   1215:        }
                   1216:       
                   1217:       /* rehash every 1000 names. */
1.1.1.3   misho    1218:       if (rhash && ((name_count - cache_size) > 1000))
1.1       misho    1219:        {
                   1220:          rehash(name_count);
                   1221:          cache_size = name_count;
                   1222:        } 
                   1223:       
                   1224:       while (atnl == 0)
                   1225:        {
                   1226:          struct crec *cache;
                   1227:          int fqdn, nomem;
                   1228:          char *canon;
                   1229:          
1.1.1.4   misho    1230:          if ((atnl = gettok(f, token)) == -1)
1.1       misho    1231:            break;
                   1232: 
                   1233:          fqdn = !!strchr(token, '.');
                   1234: 
                   1235:          if ((canon = canonicalise(token, &nomem)))
                   1236:            {
                   1237:              /* If set, add a version of the name with a default domain appended */
                   1238:              if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn && 
1.1.1.4   misho    1239:                  (cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 2 + strlen(domain_suffix))))
1.1       misho    1240:                {
                   1241:                  strcpy(cache->name.sname, canon);
                   1242:                  strcat(cache->name.sname, ".");
                   1243:                  strcat(cache->name.sname, domain_suffix);
                   1244:                  cache->flags = flags;
1.1.1.3   misho    1245:                  cache->ttd = daemon->local_ttl;
1.1       misho    1246:                  add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                   1247:                  name_count++;
1.1.1.5 ! misho    1248:                  names_done++;
1.1       misho    1249:                }
1.1.1.4   misho    1250:              if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
1.1       misho    1251:                {
                   1252:                  strcpy(cache->name.sname, canon);
                   1253:                  cache->flags = flags;
1.1.1.3   misho    1254:                  cache->ttd = daemon->local_ttl;
1.1       misho    1255:                  add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                   1256:                  name_count++;
1.1.1.5 ! misho    1257:                  names_done++;
1.1       misho    1258:                }
                   1259:              free(canon);
                   1260:              
                   1261:            }
                   1262:          else if (!nomem)
                   1263:            my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno); 
                   1264:        }
1.1.1.4   misho    1265: 
                   1266:       lineno += atnl;
1.1       misho    1267:     } 
                   1268: 
                   1269:   fclose(f);
1.1.1.3   misho    1270:   
                   1271:   if (rhash)
                   1272:     rehash(name_count); 
1.1       misho    1273:   
1.1.1.5 ! misho    1274:   my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
1.1       misho    1275:   
                   1276:   return name_count;
                   1277: }
                   1278:            
                   1279: void cache_reload(void)
                   1280: {
                   1281:   struct crec *cache, **up, *tmp;
                   1282:   int revhashsz, i, total_size = daemon->cachesize;
                   1283:   struct hostsfile *ah;
                   1284:   struct host_record *hr;
                   1285:   struct name_list *nl;
1.1.1.2   misho    1286:   struct cname *a;
1.1.1.4   misho    1287:   struct crec lrec;
                   1288:   struct mx_srv_record *mx;
                   1289:   struct txt_record *txt;
1.1.1.2   misho    1290:   struct interface_name *intr;
1.1.1.4   misho    1291:   struct ptr_record *ptr;
                   1292:   struct naptr *naptr;
1.1.1.2   misho    1293: #ifdef HAVE_DNSSEC
                   1294:   struct ds_config *ds;
                   1295: #endif
1.1       misho    1296: 
1.1.1.4   misho    1297:   daemon->metrics[METRIC_DNS_CACHE_INSERTED] = 0;
                   1298:   daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED] = 0;
1.1       misho    1299:   
                   1300:   for (i=0; i<hash_size; i++)
                   1301:     for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
                   1302:       {
1.1.1.2   misho    1303:        cache_blockdata_free(cache);
1.1.1.4   misho    1304: 
1.1       misho    1305:        tmp = cache->hash_next;
1.1.1.2   misho    1306:        if (cache->flags & (F_HOSTS | F_CONFIG))
1.1       misho    1307:          {
                   1308:            *up = cache->hash_next;
                   1309:            free(cache);
                   1310:          }
                   1311:        else if (!(cache->flags & F_DHCP))
                   1312:          {
                   1313:            *up = cache->hash_next;
                   1314:            if (cache->flags & F_BIGNAME)
                   1315:              {
                   1316:                cache->name.bname->next = big_free;
                   1317:                big_free = cache->name.bname;
                   1318:              }
                   1319:            cache->flags = 0;
                   1320:          }
                   1321:        else
                   1322:          up = &cache->hash_next;
                   1323:       }
                   1324:   
1.1.1.4   misho    1325:   /* Add locally-configured CNAMEs to the cache */
1.1.1.2   misho    1326:   for (a = daemon->cnames; a; a = a->next)
1.1.1.4   misho    1327:     if (a->alias[1] != '*' &&
                   1328:        ((cache = whine_malloc(SIZEOF_POINTER_CREC))))
                   1329:       {
                   1330:        cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
                   1331:        cache->ttd = a->ttl;
                   1332:        cache->name.namep = a->alias;
                   1333:        cache->addr.cname.target.name = a->target;
                   1334:        cache->addr.cname.is_name_ptr = 1;
                   1335:        cache->uid = UID_NONE;
                   1336:        cache_hash(cache);
                   1337:        make_non_terminals(cache);
                   1338:       }
                   1339:   
1.1.1.2   misho    1340: #ifdef HAVE_DNSSEC
                   1341:   for (ds = daemon->ds; ds; ds = ds->next)
1.1.1.4   misho    1342:     if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) &&
1.1.1.2   misho    1343:        (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
                   1344:       {
                   1345:        cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
1.1.1.3   misho    1346:        cache->ttd = daemon->local_ttl;
1.1.1.2   misho    1347:        cache->name.namep = ds->name;
                   1348:        cache->addr.ds.keylen = ds->digestlen;
                   1349:        cache->addr.ds.algo = ds->algo;
                   1350:        cache->addr.ds.keytag = ds->keytag;
                   1351:        cache->addr.ds.digest = ds->digest_type;
                   1352:        cache->uid = ds->class;
                   1353:        cache_hash(cache);
1.1.1.4   misho    1354:        make_non_terminals(cache);
1.1.1.2   misho    1355:       }
                   1356: #endif
                   1357:   
1.1       misho    1358:   /* borrow the packet buffer for a temporary by-address hash */
                   1359:   memset(daemon->packet, 0, daemon->packet_buff_sz);
                   1360:   revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
                   1361:   /* we overwrote the buffer... */
                   1362:   daemon->srv_save = NULL;
                   1363: 
                   1364:   /* Do host_records in config. */
                   1365:   for (hr = daemon->host_records; hr; hr = hr->next)
                   1366:     for (nl = hr->names; nl; nl = nl->next)
                   1367:       {
1.1.1.4   misho    1368:        if ((hr->flags & HR_4) &&
                   1369:            (cache = whine_malloc(SIZEOF_POINTER_CREC)))
1.1       misho    1370:          {
                   1371:            cache->name.namep = nl->name;
1.1.1.3   misho    1372:            cache->ttd = hr->ttl;
1.1       misho    1373:            cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
1.1.1.4   misho    1374:            add_hosts_entry(cache, (union all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
1.1       misho    1375:          }
1.1.1.4   misho    1376: 
                   1377:        if ((hr->flags & HR_6) &&
                   1378:            (cache = whine_malloc(SIZEOF_POINTER_CREC)))
1.1       misho    1379:          {
                   1380:            cache->name.namep = nl->name;
1.1.1.3   misho    1381:            cache->ttd = hr->ttl;
1.1       misho    1382:            cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
1.1.1.4   misho    1383:            add_hosts_entry(cache, (union all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
1.1       misho    1384:          }
                   1385:       }
                   1386:        
                   1387:   if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
                   1388:     {
                   1389:       if (daemon->cachesize > 0)
                   1390:        my_syslog(LOG_INFO, _("cleared cache"));
                   1391:     }
1.1.1.3   misho    1392:   else
                   1393:     {
                   1394:       if (!option_bool(OPT_NO_HOSTS))
                   1395:        total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
                   1396:       
                   1397:       daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
                   1398:       for (ah = daemon->addn_hosts; ah; ah = ah->next)
                   1399:        if (!(ah->flags & AH_INACTIVE))
                   1400:          total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
                   1401:     }
1.1.1.4   misho    1402:   
                   1403:   /* Make non-terminal records for all locally-define RRs */
                   1404:   lrec.flags = F_FORWARD | F_CONFIG | F_NAMEP | F_IMMORTAL;
                   1405:   
                   1406:   for (txt = daemon->txt; txt; txt = txt->next)
                   1407:     {
                   1408:       lrec.name.namep = txt->name;
                   1409:       make_non_terminals(&lrec);
                   1410:     }
                   1411: 
                   1412:   for (naptr = daemon->naptr; naptr; naptr = naptr->next)
                   1413:     {
                   1414:       lrec.name.namep = naptr->name;
                   1415:       make_non_terminals(&lrec);
                   1416:     }
1.1.1.3   misho    1417: 
1.1.1.4   misho    1418:   for (mx = daemon->mxnames; mx; mx = mx->next)
                   1419:     {
                   1420:       lrec.name.namep = mx->name;
                   1421:       make_non_terminals(&lrec);
                   1422:     }
                   1423: 
                   1424:   for (intr = daemon->int_names; intr; intr = intr->next)
                   1425:     {
                   1426:       lrec.name.namep = intr->name;
                   1427:       make_non_terminals(&lrec);
                   1428:     }
                   1429:   
                   1430:   for (ptr = daemon->ptr; ptr; ptr = ptr->next)
                   1431:     {
                   1432:       lrec.name.namep = ptr->name;
                   1433:       make_non_terminals(&lrec);
                   1434:     }
                   1435:   
1.1.1.3   misho    1436: #ifdef HAVE_INOTIFY
                   1437:   set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
                   1438: #endif
                   1439:   
1.1       misho    1440: } 
                   1441: 
                   1442: #ifdef HAVE_DHCP
                   1443: struct in_addr a_record_from_hosts(char *name, time_t now)
                   1444: {
                   1445:   struct crec *crecp = NULL;
                   1446:   struct in_addr ret;
                   1447:   
1.1.1.5 ! misho    1448:   /* If no DNS service, cache not initialised. */
        !          1449:   if (daemon->port != 0)
        !          1450:     while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
        !          1451:       if (crecp->flags & F_HOSTS)
        !          1452:        return crecp->addr.addr4;
        !          1453:   
1.1       misho    1454:   my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
                   1455:   
                   1456:   ret.s_addr = 0;
                   1457:   return ret;
                   1458: }
                   1459: 
                   1460: void cache_unhash_dhcp(void)
                   1461: {
                   1462:   struct crec *cache, **up;
                   1463:   int i;
                   1464: 
                   1465:   for (i=0; i<hash_size; i++)
                   1466:     for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
                   1467:       if (cache->flags & F_DHCP)
                   1468:        {
                   1469:          *up = cache->hash_next;
                   1470:          cache->next = dhcp_spare;
                   1471:          dhcp_spare = cache;
                   1472:        }
                   1473:       else
                   1474:        up = &cache->hash_next;
                   1475: }
                   1476: 
                   1477: void cache_add_dhcp_entry(char *host_name, int prot,
1.1.1.4   misho    1478:                          union all_addr *host_address, time_t ttd) 
1.1       misho    1479: {
                   1480:   struct crec *crec = NULL, *fail_crec = NULL;
1.1.1.4   misho    1481:   unsigned int flags = F_IPV4;
1.1       misho    1482:   int in_hosts = 0;
                   1483:   size_t addrlen = sizeof(struct in_addr);
                   1484: 
                   1485:   if (prot == AF_INET6)
                   1486:     {
                   1487:       flags = F_IPV6;
                   1488:       addrlen = sizeof(struct in6_addr);
                   1489:     }
                   1490:   
                   1491:   inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
                   1492:   
                   1493:   while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
                   1494:     {
                   1495:       /* check all addresses associated with name */
1.1.1.2   misho    1496:       if (crec->flags & (F_HOSTS | F_CONFIG))
1.1       misho    1497:        {
                   1498:          if (crec->flags & F_CNAME)
                   1499:            my_syslog(MS_DHCP | LOG_WARNING, 
                   1500:                      _("%s is a CNAME, not giving it to the DHCP lease of %s"),
                   1501:                      host_name, daemon->addrbuff);
1.1.1.4   misho    1502:          else if (memcmp(&crec->addr, host_address, addrlen) == 0)
1.1       misho    1503:            in_hosts = 1;
                   1504:          else
                   1505:            fail_crec = crec;
                   1506:        }
                   1507:       else if (!(crec->flags & F_DHCP))
                   1508:        {
1.1.1.4   misho    1509:          cache_scan_free(host_name, NULL, C_IN, 0, crec->flags & (flags | F_CNAME | F_FORWARD), NULL, NULL);
1.1       misho    1510:          /* scan_free deletes all addresses associated with name */
                   1511:          break;
                   1512:        }
                   1513:     }
                   1514:   
                   1515:   /* if in hosts, don't need DHCP record */
                   1516:   if (in_hosts)
                   1517:     return;
                   1518:   
                   1519:   /* Name in hosts, address doesn't match */
                   1520:   if (fail_crec)
                   1521:     {
1.1.1.4   misho    1522:       inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAME);
1.1       misho    1523:       my_syslog(MS_DHCP | LOG_WARNING, 
                   1524:                _("not giving name %s to the DHCP lease of %s because "
                   1525:                  "the name exists in %s with address %s"), 
                   1526:                host_name, daemon->addrbuff,
                   1527:                record_source(fail_crec->uid), daemon->namebuff);
                   1528:       return;
                   1529:     }    
                   1530:   
1.1.1.4   misho    1531:   if ((crec = cache_find_by_addr(NULL, (union all_addr *)host_address, 0, flags)))
1.1       misho    1532:     {
                   1533:       if (crec->flags & F_NEG)
                   1534:        {
                   1535:          flags |= F_REVERSE;
1.1.1.4   misho    1536:          cache_scan_free(NULL, (union all_addr *)host_address, C_IN, 0, flags, NULL, NULL);
1.1       misho    1537:        }
                   1538:     }
                   1539:   else
                   1540:     flags |= F_REVERSE;
                   1541:   
                   1542:   if ((crec = dhcp_spare))
                   1543:     dhcp_spare = dhcp_spare->next;
                   1544:   else /* need new one */
1.1.1.4   misho    1545:     crec = whine_malloc(SIZEOF_POINTER_CREC);
1.1       misho    1546:   
                   1547:   if (crec) /* malloc may fail */
                   1548:     {
                   1549:       crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
                   1550:       if (ttd == 0)
                   1551:        crec->flags |= F_IMMORTAL;
                   1552:       else
                   1553:        crec->ttd = ttd;
1.1.1.4   misho    1554:       crec->addr = *host_address;
1.1       misho    1555:       crec->name.namep = host_name;
1.1.1.4   misho    1556:       crec->uid = UID_NONE;
1.1       misho    1557:       cache_hash(crec);
1.1.1.4   misho    1558:       make_non_terminals(crec);
1.1       misho    1559:     }
                   1560: }
                   1561: #endif
                   1562: 
1.1.1.4   misho    1563: /* Called when we put a local or DHCP name into the cache.
                   1564:    Creates empty cache entries for subnames (ie,
                   1565:    for three.two.one, for two.one and one), without
                   1566:    F_IPV4 or F_IPV6 or F_CNAME set. These convert
                   1567:    NXDOMAIN answers to NoData ones. */
                   1568: static void make_non_terminals(struct crec *source)
                   1569: {
                   1570:   char *name = cache_get_name(source);
                   1571:   struct crec *crecp, *tmp, **up;
                   1572:   int type = F_HOSTS | F_CONFIG;
                   1573: #ifdef HAVE_DHCP
                   1574:   if (source->flags & F_DHCP)
                   1575:     type = F_DHCP;
                   1576: #endif
                   1577:   
                   1578:   /* First delete any empty entries for our new real name. Note that
                   1579:      we only delete empty entries deriving from DHCP for a new DHCP-derived
                   1580:      entry and vice-versa for HOSTS and CONFIG. This ensures that 
                   1581:      non-terminals from DHCP go when we reload DHCP and 
                   1582:      for HOSTS/CONFIG when we re-read. */
                   1583:   for (up = hash_bucket(name), crecp = *up; crecp; crecp = tmp)
                   1584:     {
                   1585:       tmp = crecp->hash_next;
                   1586: 
                   1587:       if (!is_outdated_cname_pointer(crecp) &&
                   1588:          (crecp->flags & F_FORWARD) &&
                   1589:          (crecp->flags & type) &&
                   1590:          !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS)) && 
                   1591:          hostname_isequal(name, cache_get_name(crecp)))
                   1592:        {
                   1593:          *up = crecp->hash_next;
                   1594: #ifdef HAVE_DHCP
                   1595:          if (type & F_DHCP)
                   1596:            {
                   1597:              crecp->next = dhcp_spare;
                   1598:              dhcp_spare = crecp;
                   1599:            }
                   1600:          else
                   1601: #endif
                   1602:            free(crecp);
                   1603:          break;
                   1604:        }
                   1605:       else
                   1606:         up = &crecp->hash_next;
                   1607:     }
                   1608:      
                   1609:   while ((name = strchr(name, '.')))
                   1610:     {
                   1611:       name++;
                   1612: 
                   1613:       /* Look for one existing, don't need another */
                   1614:       for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
                   1615:        if (!is_outdated_cname_pointer(crecp) &&
                   1616:            (crecp->flags & F_FORWARD) &&
                   1617:            (crecp->flags & type) &&
                   1618:            hostname_isequal(name, cache_get_name(crecp)))
                   1619:          break;
                   1620:       
                   1621:       if (crecp)
                   1622:        {
                   1623:          /* If the new name expires later, transfer that time to
                   1624:             empty non-terminal entry. */
                   1625:          if (!(crecp->flags & F_IMMORTAL))
                   1626:            {
                   1627:              if (source->flags & F_IMMORTAL)
                   1628:                crecp->flags |= F_IMMORTAL;
                   1629:              else if (difftime(crecp->ttd, source->ttd) < 0)
                   1630:                crecp->ttd = source->ttd;
                   1631:            }
                   1632:          continue;
                   1633:        }
                   1634:       
                   1635: #ifdef HAVE_DHCP
                   1636:       if ((source->flags & F_DHCP) && dhcp_spare)
                   1637:        {
                   1638:          crecp = dhcp_spare;
                   1639:          dhcp_spare = dhcp_spare->next;
                   1640:        }
                   1641:       else
                   1642: #endif
                   1643:        crecp = whine_malloc(SIZEOF_POINTER_CREC);
                   1644: 
                   1645:       if (crecp)
                   1646:        {
                   1647:          crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
1.1.1.5 ! misho    1648:          if (!(crecp->flags & F_IMMORTAL))
        !          1649:            crecp->ttd = source->ttd;
1.1.1.4   misho    1650:          crecp->name.namep = name;
                   1651:          
                   1652:          cache_hash(crecp);
                   1653:        }
                   1654:     }
                   1655: }
                   1656: 
                   1657: #ifndef NO_ID
1.1.1.2   misho    1658: int cache_make_stat(struct txt_record *t)
                   1659: { 
                   1660:   static char *buff = NULL;
                   1661:   static int bufflen = 60;
                   1662:   int len;
                   1663:   struct server *serv, *serv1;
                   1664:   char *p;
                   1665: 
                   1666:   if (!buff && !(buff = whine_malloc(60)))
                   1667:     return 0;
                   1668: 
                   1669:   p = buff;
                   1670:   
                   1671:   switch (t->stat)
                   1672:     {
                   1673:     case TXT_STAT_CACHESIZE:
                   1674:       sprintf(buff+1, "%d", daemon->cachesize);
                   1675:       break;
                   1676: 
                   1677:     case TXT_STAT_INSERTS:
1.1.1.4   misho    1678:       sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
1.1.1.2   misho    1679:       break;
                   1680: 
                   1681:     case TXT_STAT_EVICTIONS:
1.1.1.4   misho    1682:       sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]);
1.1.1.2   misho    1683:       break;
                   1684: 
                   1685:     case TXT_STAT_MISSES:
1.1.1.4   misho    1686:       sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]);
1.1.1.2   misho    1687:       break;
                   1688: 
                   1689:     case TXT_STAT_HITS:
1.1.1.4   misho    1690:       sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
1.1.1.2   misho    1691:       break;
                   1692: 
                   1693: #ifdef HAVE_AUTH
                   1694:     case TXT_STAT_AUTH:
1.1.1.4   misho    1695:       sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
1.1.1.2   misho    1696:       break;
                   1697: #endif
                   1698: 
                   1699:     case TXT_STAT_SERVERS:
                   1700:       /* sum counts from different records for same server */
                   1701:       for (serv = daemon->servers; serv; serv = serv->next)
1.1.1.5 ! misho    1702:        serv->flags &= ~SERV_MARK;
1.1.1.2   misho    1703:       
                   1704:       for (serv = daemon->servers; serv; serv = serv->next)
1.1.1.5 ! misho    1705:        if (!(serv->flags & SERV_MARK))
1.1.1.2   misho    1706:          {
                   1707:            char *new, *lenp;
                   1708:            int port, newlen, bytes_avail, bytes_needed;
                   1709:            unsigned int queries = 0, failed_queries = 0;
                   1710:            for (serv1 = serv; serv1; serv1 = serv1->next)
1.1.1.5 ! misho    1711:              if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
1.1.1.2   misho    1712:                {
1.1.1.5 ! misho    1713:                  serv1->flags |= SERV_MARK;
1.1.1.2   misho    1714:                  queries += serv1->queries;
                   1715:                  failed_queries += serv1->failed_queries;
                   1716:                }
                   1717:            port = prettyprint_addr(&serv->addr, daemon->addrbuff);
                   1718:            lenp = p++; /* length */
1.1.1.3   misho    1719:            bytes_avail = bufflen - (p - buff );
1.1.1.2   misho    1720:            bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
                   1721:            if (bytes_needed >= bytes_avail)
                   1722:              {
                   1723:                /* expand buffer if necessary */
                   1724:                newlen = bytes_needed + 1 + bufflen - bytes_avail;
1.1.1.5 ! misho    1725:                if (!(new = whine_realloc(buff, newlen)))
1.1.1.2   misho    1726:                  return 0;
                   1727:                p = new + (p - buff);
                   1728:                lenp = p - 1;
                   1729:                buff = new;
                   1730:                bufflen = newlen;
1.1.1.3   misho    1731:                bytes_avail =  bufflen - (p - buff );
1.1.1.2   misho    1732:                bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
                   1733:              }
                   1734:            *lenp = bytes_needed;
                   1735:            p += bytes_needed;
                   1736:          }
                   1737:       t->txt = (unsigned char *)buff;
                   1738:       t->len = p - buff;
1.1.1.5 ! misho    1739: 
1.1.1.2   misho    1740:       return 1;
                   1741:     }
                   1742:   
                   1743:   len = strlen(buff+1);
                   1744:   t->txt = (unsigned char *)buff;
                   1745:   t->len = len + 1;
                   1746:   *buff = len;
                   1747:   return 1;
                   1748: }
1.1.1.4   misho    1749: #endif
1.1       misho    1750: 
1.1.1.3   misho    1751: /* There can be names in the cache containing control chars, don't 
                   1752:    mess up logging or open security holes. */
                   1753: static char *sanitise(char *name)
                   1754: {
                   1755:   unsigned char *r;
                   1756:   if (name)
                   1757:     for (r = (unsigned char *)name; *r; r++)
                   1758:       if (!isprint((int)*r))
                   1759:        return "<name unprintable>";
                   1760: 
                   1761:   return name;
                   1762: }
                   1763: 
1.1.1.5 ! misho    1764: static void dump_cache_entry(struct crec *cache, time_t now)
        !          1765: {
        !          1766:   (void)now;
        !          1767:   static char *buff = NULL;
        !          1768:   
        !          1769:   char *p, *t = " ";
        !          1770:   char *a = daemon->addrbuff, *n = cache_get_name(cache);
        !          1771: 
        !          1772:   /* String length is limited below */
        !          1773:   if (!buff && !(buff = whine_malloc(150)))
        !          1774:     return;
        !          1775:   
        !          1776:   p = buff;
        !          1777:   
        !          1778:   *a = 0;
        !          1779:   if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
        !          1780:     n = "<Root>";
        !          1781:   p += sprintf(p, "%-30.30s ", sanitise(n));
        !          1782:   if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
        !          1783:     a = sanitise(cache_get_cname_target(cache));
        !          1784:   else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
        !          1785:     {
        !          1786:       int targetlen = cache->addr.srv.targetlen;
        !          1787:       ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
        !          1788:                            cache->addr.srv.weight, cache->addr.srv.srvport);
        !          1789:       
        !          1790:       if (targetlen > (40 - len))
        !          1791:        targetlen = 40 - len;
        !          1792:       blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
        !          1793:       a[len + targetlen] = 0;          
        !          1794:     }
        !          1795: #ifdef HAVE_DNSSEC
        !          1796:   else if (cache->flags & F_DS)
        !          1797:     {
        !          1798:       if (!(cache->flags & F_NEG))
        !          1799:        sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
        !          1800:                cache->addr.ds.algo, cache->addr.ds.digest);
        !          1801:     }
        !          1802:   else if (cache->flags & F_DNSKEY)
        !          1803:     sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
        !          1804:            cache->addr.key.algo, cache->addr.key.flags);
        !          1805: #endif
        !          1806:   else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
        !          1807:     { 
        !          1808:       a = daemon->addrbuff;
        !          1809:       if (cache->flags & F_IPV4)
        !          1810:        inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
        !          1811:       else if (cache->flags & F_IPV6)
        !          1812:        inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
        !          1813:     }
        !          1814:   
        !          1815:   if (cache->flags & F_IPV4)
        !          1816:     t = "4";
        !          1817:   else if (cache->flags & F_IPV6)
        !          1818:     t = "6";
        !          1819:   else if (cache->flags & F_CNAME)
        !          1820:     t = "C";
        !          1821:   else if (cache->flags & F_SRV)
        !          1822:     t = "V";
        !          1823: #ifdef HAVE_DNSSEC
        !          1824:   else if (cache->flags & F_DS)
        !          1825:     t = "S";
        !          1826:   else if (cache->flags & F_DNSKEY)
        !          1827:     t = "K";
        !          1828: #endif
        !          1829:   else if (!(cache->flags & F_NXDOMAIN)) /* non-terminal */
        !          1830:     t = "!";
        !          1831:   
        !          1832:   p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s%s ", a, t,
        !          1833:               cache->flags & F_FORWARD ? "F" : " ",
        !          1834:               cache->flags & F_REVERSE ? "R" : " ",
        !          1835:               cache->flags & F_IMMORTAL ? "I" : " ",
        !          1836:               cache->flags & F_DHCP ? "D" : " ",
        !          1837:               cache->flags & F_NEG ? "N" : " ",
        !          1838:               cache->flags & F_NXDOMAIN ? "X" : " ",
        !          1839:               cache->flags & F_HOSTS ? "H" : " ",
        !          1840:               cache->flags & F_CONFIG ? "C" : " ",
        !          1841:               cache->flags & F_DNSSECOK ? "V" : " ");
        !          1842: #ifdef HAVE_BROKEN_RTC
        !          1843:   p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
        !          1844: #else
        !          1845:   p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd)));
        !          1846: #endif
        !          1847:   if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0)
        !          1848:     p += sprintf(p, " %-40.40s", record_source(cache->uid));
        !          1849:   
        !          1850:   my_syslog(LOG_INFO, "%s", buff);
        !          1851: }
1.1.1.3   misho    1852: 
1.1       misho    1853: void dump_cache(time_t now)
                   1854: {
                   1855:   struct server *serv, *serv1;
                   1856: 
                   1857:   my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
                   1858:   my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."), 
1.1.1.4   misho    1859:            daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
1.1       misho    1860:   my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), 
1.1.1.4   misho    1861:            daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
1.1.1.5 ! misho    1862:   if (daemon->cache_max_expiry != 0)
        !          1863:     my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
1.1.1.2   misho    1864: #ifdef HAVE_AUTH
1.1.1.4   misho    1865:   my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
1.1.1.2   misho    1866: #endif
1.1.1.4   misho    1867: 
1.1.1.2   misho    1868:   blockdata_report();
1.1       misho    1869: 
                   1870:   /* sum counts from different records for same server */
                   1871:   for (serv = daemon->servers; serv; serv = serv->next)
1.1.1.5 ! misho    1872:     serv->flags &= ~SERV_MARK;
1.1       misho    1873:   
                   1874:   for (serv = daemon->servers; serv; serv = serv->next)
1.1.1.5 ! misho    1875:     if (!(serv->flags & SERV_MARK))
1.1       misho    1876:       {
                   1877:        int port;
1.1.1.5 ! misho    1878:        unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
        !          1879:        unsigned int sigma_latency = 0, count_latency = 0;
        !          1880: 
1.1       misho    1881:        for (serv1 = serv; serv1; serv1 = serv1->next)
1.1.1.5 ! misho    1882:          if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
1.1       misho    1883:            {
1.1.1.5 ! misho    1884:              serv1->flags |= SERV_MARK;
1.1       misho    1885:              queries += serv1->queries;
                   1886:              failed_queries += serv1->failed_queries;
1.1.1.5 ! misho    1887:              nxdomain_replies += serv1->nxdomain_replies;
        !          1888:              retrys += serv1->retrys;
        !          1889:              sigma_latency += serv1->query_latency;
        !          1890:              count_latency++;
1.1       misho    1891:            }
                   1892:        port = prettyprint_addr(&serv->addr, daemon->addrbuff);
1.1.1.5 ! misho    1893:        my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"),
        !          1894:                  daemon->addrbuff, port, queries, retrys, failed_queries, nxdomain_replies, sigma_latency/count_latency);
1.1       misho    1895:       }
1.1.1.5 ! misho    1896: 
1.1       misho    1897:   if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
                   1898:     {
1.1.1.5 ! misho    1899:       struct crec *cache;
1.1       misho    1900:       int i;
1.1.1.5 ! misho    1901:       my_syslog(LOG_INFO, "Host                           Address                                  Flags      Expires                  Source");
        !          1902:       my_syslog(LOG_INFO, "------------------------------ ---------------------------------------- ---------- ------------------------ ------------");
1.1       misho    1903:     
                   1904:       for (i=0; i<hash_size; i++)
                   1905:        for (cache = hash_table[i]; cache; cache = cache->hash_next)
1.1.1.5 ! misho    1906:          dump_cache_entry(cache, now);
1.1       misho    1907:     }
                   1908: }
                   1909: 
1.1.1.2   misho    1910: char *record_source(unsigned int index)
1.1       misho    1911: {
                   1912:   struct hostsfile *ah;
1.1.1.5 ! misho    1913: #ifdef HAVE_INOTIFY
        !          1914:   struct dyndir *dd;
        !          1915: #endif
        !          1916:   
1.1.1.2   misho    1917:   if (index == SRC_CONFIG)
                   1918:     return "config";
                   1919:   else if (index == SRC_HOSTS)
1.1       misho    1920:     return HOSTSFILE;
                   1921: 
                   1922:   for (ah = daemon->addn_hosts; ah; ah = ah->next)
                   1923:     if (ah->index == index)
                   1924:       return ah->fname;
1.1.1.3   misho    1925: 
                   1926: #ifdef HAVE_INOTIFY
1.1.1.5 ! misho    1927:   /* Dynamic directories contain multiple files */
        !          1928:   for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
        !          1929:     for (ah = dd->files; ah; ah = ah->next)
        !          1930:       if (ah->index == index)
        !          1931:        return ah->fname;
1.1.1.3   misho    1932: #endif
                   1933: 
1.1       misho    1934:   return "<unknown>";
                   1935: }
                   1936: 
1.1.1.5 ! misho    1937: static char *querystr(char *desc, unsigned short type)
1.1       misho    1938: {
                   1939:   unsigned int i;
1.1.1.2   misho    1940:   int len = 10; /* strlen("type=xxxxx") */
                   1941:   const char *types = NULL;
                   1942:   static char *buff = NULL;
                   1943:   static int bufflen = 0;
                   1944: 
1.1       misho    1945:   for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
                   1946:     if (typestr[i].type == type)
1.1.1.2   misho    1947:       {
                   1948:        types = typestr[i].name;
                   1949:        len = strlen(types);
                   1950:        break;
                   1951:       }
                   1952: 
1.1.1.4   misho    1953:   if (desc)
                   1954:     {
                   1955:        len += 2; /* braces */
                   1956:        len += strlen(desc);
                   1957:     }
                   1958:   len++; /* terminator */
                   1959:   
1.1.1.2   misho    1960:   if (!buff || bufflen < len)
                   1961:     {
                   1962:       if (buff)
                   1963:        free(buff);
                   1964:       else if (len < 20)
                   1965:        len = 20;
                   1966:       
                   1967:       buff = whine_malloc(len);
                   1968:       bufflen = len;
                   1969:     }
                   1970: 
                   1971:   if (buff)
                   1972:     {
1.1.1.4   misho    1973:       if (desc)
                   1974:        {
                   1975:          if (types)
                   1976:            sprintf(buff, "%s[%s]", desc, types);
                   1977:          else
                   1978:            sprintf(buff, "%s[type=%d]", desc, type);
                   1979:        }
1.1.1.2   misho    1980:       else
1.1.1.4   misho    1981:        {
                   1982:          if (types)
                   1983:            sprintf(buff, "<%s>", types);
                   1984:          else
1.1.1.5 ! misho    1985:            sprintf(buff, "<type=%d>", type);
1.1.1.4   misho    1986:        }
1.1.1.2   misho    1987:     }
1.1.1.4   misho    1988:   
1.1.1.2   misho    1989:   return buff ? buff : "";
1.1       misho    1990: }
                   1991: 
1.1.1.5 ! misho    1992: static char *edestr(int ede)
        !          1993: {
        !          1994:   switch (ede)
        !          1995:     {
        !          1996:     case EDE_OTHER:                       return "other";
        !          1997:     case EDE_USUPDNSKEY:                  return "unsupported DNSKEY algorithm";
        !          1998:     case EDE_USUPDS:                      return "unsupported DS digest";
        !          1999:     case EDE_STALE:                       return "stale answer";
        !          2000:     case EDE_FORGED:                      return "forged";
        !          2001:     case EDE_DNSSEC_IND:                  return "DNSSEC indeterminate";
        !          2002:     case EDE_DNSSEC_BOGUS:                return "DNSSEC bogus";
        !          2003:     case EDE_SIG_EXP:                     return "DNSSEC signature expired";
        !          2004:     case EDE_SIG_NYV:                     return "DNSSEC sig not yet valid";
        !          2005:     case EDE_NO_DNSKEY:                   return "DNSKEY missing";
        !          2006:     case EDE_NO_RRSIG:                    return "RRSIG missing";
        !          2007:     case EDE_NO_ZONEKEY:                  return "no zone key bit set";
        !          2008:     case EDE_NO_NSEC:                     return "NSEC(3) missing";
        !          2009:     case EDE_CACHED_ERR:                  return "cached error";
        !          2010:     case EDE_NOT_READY:                   return "not ready";
        !          2011:     case EDE_BLOCKED:                     return "blocked";
        !          2012:     case EDE_CENSORED:                    return "censored";
        !          2013:     case EDE_FILTERED:                    return "filtered";
        !          2014:     case EDE_PROHIBITED:                  return "prohibited";
        !          2015:     case EDE_STALE_NXD:                   return "stale NXDOMAIN";
        !          2016:     case EDE_NOT_AUTH:                    return "not authoritative";
        !          2017:     case EDE_NOT_SUP:                     return "not supported";
        !          2018:     case EDE_NO_AUTH:                     return "no reachable authority";
        !          2019:     case EDE_NETERR:                      return "network error";
        !          2020:     case EDE_INVALID_DATA:                return "invalid data";
        !          2021:     default:                              return "unknown";
        !          2022:     }
        !          2023: }
        !          2024: 
        !          2025: void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type)
1.1       misho    2026: {
1.1.1.5 ! misho    2027:   char *source, *dest = arg;
1.1       misho    2028:   char *verb = "is";
1.1.1.5 ! misho    2029:   char *extra = "";
        !          2030:   char portstring[7]; /* space for #<portnum> */
1.1       misho    2031:   
                   2032:   if (!option_bool(OPT_LOG))
                   2033:     return;
                   2034: 
1.1.1.5 ! misho    2035:   /* build query type string if requested */
        !          2036:   if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
        !          2037:     arg = querystr(arg, type);
        !          2038: 
        !          2039: #ifdef HAVE_DNSSEC
        !          2040:   if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
        !          2041:     extra = " (DNSSEC signed)";
        !          2042: #endif
        !          2043: 
1.1.1.3   misho    2044:   name = sanitise(name);
                   2045: 
1.1       misho    2046:   if (addr)
                   2047:     {
1.1.1.5 ! misho    2048:       dest = daemon->addrbuff;
        !          2049: 
1.1.1.2   misho    2050:       if (flags & F_KEYTAG)
1.1.1.4   misho    2051:        sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
                   2052:       else if (flags & F_RCODE)
1.1.1.2   misho    2053:        {
1.1.1.4   misho    2054:          unsigned int rcode = addr->log.rcode;
                   2055: 
1.1.1.5 ! misho    2056:          if (rcode == SERVFAIL)
        !          2057:            dest = "SERVFAIL";
        !          2058:          else if (rcode == REFUSED)
        !          2059:            dest = "REFUSED";
        !          2060:          else if (rcode == NOTIMP)
        !          2061:            dest = "not implemented";
        !          2062:          else
        !          2063:            sprintf(daemon->addrbuff, "%u", rcode);
        !          2064: 
        !          2065:          if (addr->log.ede != EDE_UNSET)
        !          2066:            {
        !          2067:              extra = daemon->addrbuff;
        !          2068:              sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
        !          2069:            }
        !          2070:        }
        !          2071:       else if (flags & (F_IPV4 | F_IPV6))
        !          2072:        {
        !          2073:          inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
        !          2074:                    addr, daemon->addrbuff, ADDRSTRLEN);
        !          2075:          if ((flags & F_SERVER) && type != NAMESERVER_PORT)
        !          2076:            {
        !          2077:              extra = portstring;
        !          2078:              sprintf(portstring, "#%u", type);
        !          2079:            }
1.1.1.2   misho    2080:        }
1.1.1.4   misho    2081:       else
1.1.1.5 ! misho    2082:        dest = arg;
1.1       misho    2083:     }
                   2084: 
                   2085:   if (flags & F_REVERSE)
                   2086:     {
                   2087:       dest = name;
                   2088:       name = daemon->addrbuff;
                   2089:     }
                   2090:   
                   2091:   if (flags & F_NEG)
                   2092:     {
                   2093:       if (flags & F_NXDOMAIN)
1.1.1.2   misho    2094:        dest = "NXDOMAIN";
1.1       misho    2095:       else
                   2096:        {      
                   2097:          if (flags & F_IPV4)
                   2098:            dest = "NODATA-IPv4";
                   2099:          else if (flags & F_IPV6)
                   2100:            dest = "NODATA-IPv6";
                   2101:          else
                   2102:            dest = "NODATA";
                   2103:        }
                   2104:     }
                   2105:   else if (flags & F_CNAME)
                   2106:     dest = "<CNAME>";
1.1.1.4   misho    2107:   else if (flags & F_SRV)
                   2108:     dest = "<SRV>";
1.1       misho    2109:   else if (flags & F_RRNAME)
                   2110:     dest = arg;
                   2111:     
                   2112:   if (flags & F_CONFIG)
                   2113:     source = "config";
                   2114:   else if (flags & F_DHCP)
                   2115:     source = "DHCP";
                   2116:   else if (flags & F_HOSTS)
                   2117:     source = arg;
                   2118:   else if (flags & F_UPSTREAM)
                   2119:     source = "reply";
1.1.1.2   misho    2120:   else if (flags & F_SECSTAT)
1.1.1.5 ! misho    2121:     {
        !          2122:       if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
        !          2123:        {
        !          2124:          extra = daemon->addrbuff;
        !          2125:          sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
        !          2126:        }
        !          2127:       source = "validation";
        !          2128:       dest = arg;
        !          2129:     }
1.1       misho    2130:   else if (flags & F_AUTH)
                   2131:     source = "auth";
1.1.1.5 ! misho    2132:    else if (flags & F_DNSSEC)
        !          2133:     {
        !          2134:       source = arg;
        !          2135:       verb = "to";
        !          2136:     }
        !          2137:    else if (flags & F_SERVER)
1.1       misho    2138:     {
                   2139:       source = "forwarded";
                   2140:       verb = "to";
                   2141:     }
                   2142:   else if (flags & F_QUERY)
                   2143:     {
                   2144:       source = arg;
                   2145:       verb = "from";
                   2146:     }
1.1.1.2   misho    2147:   else if (flags & F_IPSET)
                   2148:     {
1.1.1.5 ! misho    2149:       source = type ? "ipset add" : "nftset add";
1.1.1.2   misho    2150:       dest = name;
                   2151:       name = arg;
                   2152:       verb = daemon->addrbuff;
                   2153:     }
1.1.1.5 ! misho    2154:   else if (flags & F_STALE)
        !          2155:     source = "cached-stale";
1.1       misho    2156:   else
                   2157:     source = "cached";
                   2158:   
1.1.1.5 ! misho    2159:   if (name && !name[0])
1.1       misho    2160:     name = ".";
                   2161: 
1.1.1.3   misho    2162:   if (option_bool(OPT_EXTRALOG))
                   2163:     {
                   2164:       if (flags & F_NOEXTRA)
1.1.1.5 ! misho    2165:        my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
1.1.1.3   misho    2166:       else
1.1.1.5 ! misho    2167:        {
        !          2168:           int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
        !          2169:           my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
        !          2170:        }
1.1.1.3   misho    2171:     }
                   2172:   else
1.1.1.5 ! misho    2173:     my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra);
1.1       misho    2174: }

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