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

1.1.1.2 ! misho       1: /* dnsmasq is Copyright (c) 2000-2014 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;
                     24: static int cache_inserted = 0, cache_live_freed = 0, insert_error;
                     25: static union bigname *big_free = NULL;
                     26: static int bignames_left, hash_size;
                     27: 
                     28: /* type->string mapping: this is also used by the name-hash function as a mixing table. */
                     29: static const struct {
                     30:   unsigned int type;
                     31:   const char * const name;
                     32: } typestr[] = {
                     33:   { 1,   "A" },
                     34:   { 2,   "NS" },
                     35:   { 5,   "CNAME" },
                     36:   { 6,   "SOA" },
                     37:   { 10,  "NULL" },
                     38:   { 11,  "WKS" },
                     39:   { 12,  "PTR" },
                     40:   { 13,  "HINFO" },    
                     41:   { 15,  "MX" },
                     42:   { 16,  "TXT" },
                     43:   { 22,  "NSAP" },
                     44:   { 23,  "NSAP_PTR" },
                     45:   { 24,  "SIG" },
                     46:   { 25,  "KEY" },
                     47:   { 28,  "AAAA" },
                     48:   { 33,  "SRV" },
                     49:   { 35,  "NAPTR" },
                     50:   { 36,  "KX" },
                     51:   { 37,  "CERT" },
                     52:   { 38,  "A6" },
                     53:   { 39,  "DNAME" },
                     54:   { 41,  "OPT" },
1.1.1.2 ! misho      55:   { 43,  "DS" },
        !            56:   { 46,  "RRSIG" },
        !            57:   { 47,  "NSEC" },
1.1       misho      58:   { 48,  "DNSKEY" },
1.1.1.2 ! misho      59:   { 50,  "NSEC3" },
1.1       misho      60:   { 249, "TKEY" },
                     61:   { 250, "TSIG" },
                     62:   { 251, "IXFR" },
                     63:   { 252, "AXFR" },
                     64:   { 253, "MAILB" },
                     65:   { 254, "MAILA" },
                     66:   { 255, "ANY" }
                     67: };
                     68: 
                     69: static void cache_free(struct crec *crecp);
                     70: static void cache_unlink(struct crec *crecp);
                     71: static void cache_link(struct crec *crecp);
                     72: static void rehash(int size);
                     73: static void cache_hash(struct crec *crecp);
                     74: 
1.1.1.2 ! misho      75: static unsigned int next_uid(void)
        !            76: {
        !            77:   static unsigned int uid = 0;
        !            78: 
        !            79:   uid++;
        !            80:   
        !            81:   /* uid == 0 used to indicate CNAME to interface name. */
        !            82:   if (uid == SRC_INTERFACE)
        !            83:     uid++;
        !            84:   
        !            85:   return uid;
        !            86: }
        !            87: 
1.1       misho      88: void cache_init(void)
                     89: {
                     90:   struct crec *crecp;
                     91:   int i;
1.1.1.2 ! misho      92:  
1.1       misho      93:   bignames_left = daemon->cachesize/10;
                     94:   
                     95:   if (daemon->cachesize > 0)
                     96:     {
                     97:       crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
                     98:       
                     99:       for (i=0; i < daemon->cachesize; i++, crecp++)
                    100:        {
                    101:          cache_link(crecp);
                    102:          crecp->flags = 0;
1.1.1.2 ! misho     103:          crecp->uid = next_uid();
1.1       misho     104:        }
                    105:     }
                    106:   
                    107:   /* create initial hash table*/
                    108:   rehash(daemon->cachesize);
                    109: }
                    110: 
                    111: /* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
                    112:    but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
                    113:    will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
                    114:    expand the table. */
                    115: static void rehash(int size)
                    116: {
                    117:   struct crec **new, **old, *p, *tmp;
                    118:   int i, new_size, old_size;
                    119: 
                    120:   /* hash_size is a power of two. */
                    121:   for (new_size = 64; new_size < size/10; new_size = new_size << 1);
                    122:   
                    123:   /* must succeed in getting first instance, failure later is non-fatal */
                    124:   if (!hash_table)
                    125:     new = safe_malloc(new_size * sizeof(struct crec *));
                    126:   else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
                    127:     return;
                    128: 
                    129:   for(i = 0; i < new_size; i++)
                    130:     new[i] = NULL;
                    131: 
                    132:   old = hash_table;
                    133:   old_size = hash_size;
                    134:   hash_table = new;
                    135:   hash_size = new_size;
                    136:   
                    137:   if (old)
                    138:     {
                    139:       for (i = 0; i < old_size; i++)
                    140:        for (p = old[i]; p ; p = tmp)
                    141:          {
                    142:            tmp = p->hash_next;
                    143:            cache_hash(p);
                    144:          }
                    145:       free(old);
                    146:     }
                    147: }
                    148:   
                    149: static struct crec **hash_bucket(char *name)
                    150: {
                    151:   unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
                    152:   const unsigned char *mix_tab = (const unsigned char*)typestr; 
                    153: 
                    154:   while((c = (unsigned char) *name++))
                    155:     {
                    156:       /* don't use tolower and friends here - they may be messed up by LOCALE */
                    157:       if (c >= 'A' && c <= 'Z')
                    158:        c += 'a' - 'A';
                    159:       val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c);
                    160:     } 
                    161:   
                    162:   /* hash_size is a power of two */
                    163:   return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
                    164: }
                    165: 
                    166: static void cache_hash(struct crec *crecp)
                    167: {
                    168:   /* maintain an invariant that all entries with F_REVERSE set
                    169:      are at the start of the hash-chain  and all non-reverse
                    170:      immortal entries are at the end of the hash-chain.
                    171:      This allows reverse searches and garbage collection to be optimised */
                    172: 
                    173:   struct crec **up = hash_bucket(cache_get_name(crecp));
                    174: 
                    175:   if (!(crecp->flags & F_REVERSE))
                    176:     {
                    177:       while (*up && ((*up)->flags & F_REVERSE))
                    178:        up = &((*up)->hash_next); 
                    179:       
                    180:       if (crecp->flags & F_IMMORTAL)
                    181:        while (*up && !((*up)->flags & F_IMMORTAL))
                    182:          up = &((*up)->hash_next);
                    183:     }
                    184:   crecp->hash_next = *up;
                    185:   *up = crecp;
                    186: }
1.1.1.2 ! misho     187: 
        !           188: #ifdef HAVE_DNSSEC
        !           189: static void cache_blockdata_free(struct crec *crecp)
        !           190: {
        !           191:   if (crecp->flags & F_DNSKEY)
        !           192:     {
        !           193:       if (crecp->flags & F_DS)
        !           194:        blockdata_free(crecp->addr.sig.keydata);
        !           195:       else
        !           196:        blockdata_free(crecp->addr.key.keydata);
        !           197:     }
        !           198:   else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
        !           199:     blockdata_free(crecp->addr.ds.keydata);
        !           200: }
        !           201: #endif
        !           202: 
1.1       misho     203: static void cache_free(struct crec *crecp)
                    204: {
                    205:   crecp->flags &= ~F_FORWARD;
                    206:   crecp->flags &= ~F_REVERSE;
1.1.1.2 ! misho     207:   crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
        !           208: 
1.1       misho     209:   if (cache_tail)
                    210:     cache_tail->next = crecp;
                    211:   else
                    212:     cache_head = crecp;
                    213:   crecp->prev = cache_tail;
                    214:   crecp->next = NULL;
                    215:   cache_tail = crecp;
                    216:   
                    217:   /* retrieve big name for further use. */
                    218:   if (crecp->flags & F_BIGNAME)
                    219:     {
                    220:       crecp->name.bname->next = big_free;
                    221:       big_free = crecp->name.bname;
                    222:       crecp->flags &= ~F_BIGNAME;
                    223:     }
1.1.1.2 ! misho     224: 
1.1       misho     225: #ifdef HAVE_DNSSEC
1.1.1.2 ! misho     226:   cache_blockdata_free(crecp);
1.1       misho     227: #endif
                    228: }    
                    229: 
                    230: /* insert a new cache entry at the head of the list (youngest entry) */
                    231: static void cache_link(struct crec *crecp)
                    232: {
                    233:   if (cache_head) /* check needed for init code */
                    234:     cache_head->prev = crecp;
                    235:   crecp->next = cache_head;
                    236:   crecp->prev = NULL;
                    237:   cache_head = crecp;
                    238:   if (!cache_tail)
                    239:     cache_tail = crecp;
                    240: }
                    241: 
                    242: /* remove an arbitrary cache entry for promotion */ 
                    243: static void cache_unlink (struct crec *crecp)
                    244: {
                    245:   if (crecp->prev)
                    246:     crecp->prev->next = crecp->next;
                    247:   else
                    248:     cache_head = crecp->next;
                    249: 
                    250:   if (crecp->next)
                    251:     crecp->next->prev = crecp->prev;
                    252:   else
                    253:     cache_tail = crecp->prev;
                    254: }
                    255: 
                    256: char *cache_get_name(struct crec *crecp)
                    257: {
                    258:   if (crecp->flags & F_BIGNAME)
                    259:     return crecp->name.bname->name;
                    260:   else if (crecp->flags & F_NAMEP) 
                    261:     return crecp->name.namep;
                    262:   
                    263:   return crecp->name.sname;
                    264: }
                    265: 
1.1.1.2 ! misho     266: char *cache_get_cname_target(struct crec *crecp)
        !           267: {
        !           268:   if (crecp->addr.cname.uid != SRC_INTERFACE)
        !           269:     return cache_get_name(crecp->addr.cname.target.cache);
        !           270: 
        !           271:   return crecp->addr.cname.target.int_name->name;
        !           272: }
        !           273: 
        !           274: 
        !           275: 
1.1       misho     276: struct crec *cache_enumerate(int init)
                    277: {
                    278:   static int bucket;
                    279:   static struct crec *cache;
                    280: 
                    281:   if (init)
                    282:     {
                    283:       bucket = 0;
                    284:       cache = NULL;
                    285:     }
                    286:   else if (cache && cache->hash_next)
                    287:     cache = cache->hash_next;
                    288:   else
                    289:     {
                    290:        cache = NULL; 
                    291:        while (bucket < hash_size)
                    292:         if ((cache = hash_table[bucket++]))
                    293:           break;
                    294:     }
                    295:   
                    296:   return cache;
                    297: }
                    298: 
                    299: static int is_outdated_cname_pointer(struct crec *crecp)
                    300: {
1.1.1.2 ! misho     301:   if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_INTERFACE)
1.1       misho     302:     return 0;
                    303:   
                    304:   /* NB. record may be reused as DS or DNSKEY, where uid is 
                    305:      overloaded for something completely different */
1.1.1.2 ! misho     306:   if (crecp->addr.cname.target.cache && 
        !           307:       (crecp->addr.cname.target.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&
        !           308:       crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)
1.1       misho     309:     return 0;
                    310:   
                    311:   return 1;
                    312: }
                    313: 
                    314: static int is_expired(time_t now, struct crec *crecp)
                    315: {
                    316:   if (crecp->flags & F_IMMORTAL)
                    317:     return 0;
                    318: 
                    319:   if (difftime(now, crecp->ttd) < 0)
                    320:     return 0;
                    321:   
                    322:   return 1;
                    323: }
                    324: 
                    325: static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
                    326: {
                    327:   /* Scan and remove old entries.
                    328:      If (flags & F_FORWARD) then remove any forward entries for name and any expired
                    329:      entries but only in the same hash bucket as name.
                    330:      If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
                    331:      entries in the whole cache.
                    332:      If (flags == 0) remove any expired entries in the whole cache. 
                    333: 
                    334:      In the flags & F_FORWARD case, the return code is valid, and returns zero if the
                    335:      name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
                    336: 
                    337:      We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
                    338:      so that when we hit an entry which isn't reverse and is immortal, we're done. */
                    339:  
                    340:   struct crec *crecp, **up;
                    341:   
                    342:   if (flags & F_FORWARD)
                    343:     {
                    344:       for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
1.1.1.2 ! misho     345:        {
        !           346:          if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
        !           347:            { 
        !           348:              *up = crecp->hash_next;
        !           349:              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
        !           350:                {
        !           351:                  cache_unlink(crecp);
        !           352:                  cache_free(crecp);
        !           353:                }
        !           354:              continue;
        !           355:            } 
        !           356:        
        !           357:          if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
        !           358:            {
        !           359:              /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
        !           360:              if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || 
        !           361:                  (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
        !           362:                {
        !           363:                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
        !           364:                    return 0;
        !           365:                  *up = crecp->hash_next;
        !           366:                  cache_unlink(crecp);
        !           367:                  cache_free(crecp);
        !           368:                  continue;
        !           369:                }
        !           370:              
        !           371: #ifdef HAVE_DNSSEC
        !           372:              /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also 
        !           373:                 type-covered sensitive for  RRSIG */
        !           374:              if ((flags & (F_DNSKEY | F_DS)) &&
        !           375:                  (flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
        !           376:                  crecp->uid == addr->addr.dnssec.class &&
        !           377:                  (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) || 
        !           378:                   crecp->addr.sig.type_covered == addr->addr.dnssec.type))
        !           379:                {
        !           380:                  if (crecp->flags & F_CONFIG)
        !           381:                    return 0;
        !           382:                  *up = crecp->hash_next;
        !           383:                  cache_unlink(crecp);
        !           384:                  cache_free(crecp);
        !           385:                  continue;
        !           386:                }
        !           387: #endif
        !           388:            }
1.1       misho     389:          up = &crecp->hash_next;
1.1.1.2 ! misho     390:        }
1.1       misho     391:     }
                    392:   else
                    393:     {
                    394:       int i;
                    395: #ifdef HAVE_IPV6
                    396:       int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
                    397: #else
                    398:       int addrlen = INADDRSZ;
                    399: #endif 
                    400:       for (i = 0; i < hash_size; i++)
                    401:        for (crecp = hash_table[i], up = &hash_table[i]; 
                    402:             crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
                    403:             crecp = crecp->hash_next)
                    404:          if (is_expired(now, crecp))
                    405:            {
                    406:              *up = crecp->hash_next;
1.1.1.2 ! misho     407:              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
1.1       misho     408:                { 
                    409:                  cache_unlink(crecp);
                    410:                  cache_free(crecp);
                    411:                }
                    412:            }
1.1.1.2 ! misho     413:          else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
1.1       misho     414:                   (flags & crecp->flags & F_REVERSE) && 
                    415:                   (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
                    416:                   memcmp(&crecp->addr.addr, addr, addrlen) == 0)
                    417:            {
                    418:              *up = crecp->hash_next;
                    419:              cache_unlink(crecp);
                    420:              cache_free(crecp);
                    421:            }
                    422:          else
                    423:            up = &crecp->hash_next;
                    424:     }
                    425:   
                    426:   return 1;
                    427: }
                    428: 
                    429: /* Note: The normal calling sequence is
                    430:    cache_start_insert
                    431:    cache_insert * n
                    432:    cache_end_insert
                    433: 
                    434:    but an abort can cause the cache_end_insert to be missed 
                    435:    in which can the next cache_start_insert cleans things up. */
                    436: 
                    437: void cache_start_insert(void)
                    438: {
                    439:   /* Free any entries which didn't get committed during the last
                    440:      insert due to error.
                    441:   */
                    442:   while (new_chain)
                    443:     {
                    444:       struct crec *tmp = new_chain->next;
                    445:       cache_free(new_chain);
                    446:       new_chain = tmp;
                    447:     }
                    448:   new_chain = NULL;
                    449:   insert_error = 0;
                    450: }
                    451:  
                    452: struct crec *cache_insert(char *name, struct all_addr *addr, 
                    453:                          time_t now,  unsigned long ttl, unsigned short flags)
                    454: {
                    455:   struct crec *new;
                    456:   union bigname *big_name = NULL;
                    457:   int freed_all = flags & F_REVERSE;
                    458:   int free_avail = 0;
                    459: 
1.1.1.2 ! misho     460:   /* Don't log DNSSEC records here, done elsewhere */
        !           461:   if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
        !           462:     {
        !           463:       log_query(flags | F_UPSTREAM, name, addr, NULL);
        !           464:       /* Don;t mess with TTL for DNSSEC records. */
        !           465:       if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
        !           466:        ttl = daemon->max_cache_ttl;
        !           467:     }
1.1       misho     468: 
                    469:   /* if previous insertion failed give up now. */
                    470:   if (insert_error)
                    471:     return NULL;
1.1.1.2 ! misho     472:   
1.1       misho     473:   /* First remove any expired entries and entries for the name/address we
1.1.1.2 ! misho     474:      are currently inserting. Fail if we attempt to delete a name from
1.1       misho     475:      /etc/hosts or DHCP. */
                    476:   if (!cache_scan_free(name, addr, now, flags))
                    477:     {
                    478:       insert_error = 1;
                    479:       return NULL;
                    480:     }
                    481:   
                    482:   /* Now get a cache entry from the end of the LRU list */
                    483:   while (1) {
                    484:     if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
                    485:       {
                    486:        insert_error = 1;
                    487:        return NULL;
                    488:       }
                    489:     
                    490:     /* End of LRU list is still in use: if we didn't scan all the hash
                    491:        chains for expired entries do that now. If we already tried that
                    492:        then it's time to start spilling things. */
                    493:     
                    494:     if (new->flags & (F_FORWARD | F_REVERSE))
                    495:       { 
                    496:        /* If free_avail set, we believe that an entry has been freed.
                    497:           Bugs have been known to make this not true, resulting in
                    498:           a tight loop here. If that happens, abandon the
                    499:           insert. Once in this state, all inserts will probably fail. */
                    500:        if (free_avail)
                    501:          {
1.1.1.2 ! misho     502:            static int warned = 0;
        !           503:            if (!warned)
        !           504:              {
        !           505:                my_syslog(LOG_ERR, _("Internal error in cache."));
        !           506:                warned = 1;
        !           507:              }
1.1       misho     508:            insert_error = 1;
                    509:            return NULL;
                    510:          }
                    511:                
                    512:        if (freed_all)
                    513:          {
1.1.1.2 ! misho     514:            struct all_addr free_addr = new->addr.addr;;
        !           515: 
        !           516: #ifdef HAVE_DNSSEC
        !           517:            /* For DNSSEC records, addr holds class and type_covered for RRSIG */
        !           518:            if (new->flags & (F_DS | F_DNSKEY))
        !           519:              {
        !           520:                free_addr.addr.dnssec.class = new->uid;
        !           521:                if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
        !           522:                  free_addr.addr.dnssec.type = new->addr.sig.type_covered;
        !           523:              }
        !           524: #endif
        !           525:            
1.1       misho     526:            free_avail = 1; /* Must be free space now. */
1.1.1.2 ! misho     527:            cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
1.1       misho     528:            cache_live_freed++;
                    529:          }
                    530:        else
                    531:          {
                    532:            cache_scan_free(NULL, NULL, now, 0);
                    533:            freed_all = 1;
                    534:          }
                    535:        continue;
                    536:       }
                    537:  
                    538:     /* Check if we need to and can allocate extra memory for a long name.
1.1.1.2 ! misho     539:        If that fails, give up now, always succeed for DNSSEC records. */
1.1       misho     540:     if (name && (strlen(name) > SMALLDNAME-1))
                    541:       {
                    542:        if (big_free)
                    543:          { 
                    544:            big_name = big_free;
                    545:            big_free = big_free->next;
                    546:          }
1.1.1.2 ! misho     547:        else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
1.1       misho     548:                 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
                    549:          {
                    550:            insert_error = 1;
                    551:            return NULL;
                    552:          }
1.1.1.2 ! misho     553:        else if (bignames_left != 0)
1.1       misho     554:          bignames_left--;
                    555:        
                    556:       }
                    557: 
                    558:     /* Got the rest: finally grab entry. */
                    559:     cache_unlink(new);
                    560:     break;
                    561:   }
                    562:   
                    563:   new->flags = flags;
                    564:   if (big_name)
                    565:     {
                    566:       new->name.bname = big_name;
                    567:       new->flags |= F_BIGNAME;
                    568:     }
                    569: 
                    570:   if (name)
                    571:     strcpy(cache_get_name(new), name);
                    572:   else
                    573:     *cache_get_name(new) = 0;
                    574: 
                    575:   if (addr)
1.1.1.2 ! misho     576:     {
        !           577: #ifdef HAVE_DNSSEC
        !           578:       if (flags & (F_DS | F_DNSKEY))
        !           579:        new->uid = addr->addr.dnssec.class;
        !           580:       else
        !           581: #endif
        !           582:        new->addr.addr = *addr; 
        !           583:     }
1.1       misho     584: 
                    585:   new->ttd = now + (time_t)ttl;
                    586:   new->next = new_chain;
                    587:   new_chain = new;
                    588: 
                    589:   return new;
                    590: }
                    591: 
                    592: /* after end of insertion, commit the new entries */
                    593: void cache_end_insert(void)
                    594: {
                    595:   if (insert_error)
                    596:     return;
                    597:   
                    598:   while (new_chain)
                    599:     { 
                    600:       struct crec *tmp = new_chain->next;
                    601:       /* drop CNAMEs which didn't find a target. */
                    602:       if (is_outdated_cname_pointer(new_chain))
                    603:        cache_free(new_chain);
                    604:       else
                    605:        {
                    606:          cache_hash(new_chain);
                    607:          cache_link(new_chain);
                    608:          cache_inserted++;
                    609:        }
                    610:       new_chain = tmp;
                    611:     }
                    612:   new_chain = NULL;
                    613: }
                    614: 
1.1.1.2 ! misho     615: struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
1.1       misho     616: {
                    617:   struct crec *ans;
1.1.1.2 ! misho     618:   int no_rr = prot & F_NO_RR;
1.1       misho     619: 
1.1.1.2 ! misho     620:   prot &= ~F_NO_RR;
        !           621:   
1.1       misho     622:   if (crecp) /* iterating */
                    623:     ans = crecp->next;
                    624:   else
                    625:     {
                    626:       /* first search, look for relevant entries and push to top of list
                    627:         also free anything which has expired */
                    628:       struct crec *next, **up, **insert = NULL, **chainp = &ans;
                    629:       unsigned short ins_flags = 0;
                    630:       
                    631:       for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
                    632:        {
                    633:          next = crecp->hash_next;
                    634:          
                    635:          if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
                    636:            {
                    637:              if ((crecp->flags & F_FORWARD) && 
1.1.1.2 ! misho     638: #ifdef HAVE_DNSSEC
        !           639:                  ((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
        !           640: #endif
1.1       misho     641:                  (crecp->flags & prot) &&
                    642:                  hostname_isequal(cache_get_name(crecp), name))
                    643:                {
1.1.1.2 ! misho     644:                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
1.1       misho     645:                    {
                    646:                      *chainp = crecp;
                    647:                      chainp = &crecp->next;
                    648:                    }
                    649:                  else
                    650:                    {
                    651:                      cache_unlink(crecp);
                    652:                      cache_link(crecp);
                    653:                    }
                    654:                      
                    655:                  /* Move all but the first entry up the hash chain
                    656:                     this implements round-robin. 
                    657:                     Make sure that re-ordering doesn't break the hash-chain
                    658:                     order invariants. 
                    659:                  */
                    660:                  if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
                    661:                    {
                    662:                      *up = crecp->hash_next;
                    663:                      crecp->hash_next = *insert;
                    664:                      *insert = crecp;
                    665:                      insert = &crecp->hash_next;
                    666:                    }
                    667:                  else
                    668:                    {
1.1.1.2 ! misho     669:                      if (!insert && !no_rr)
1.1       misho     670:                        {
                    671:                          insert = up;
                    672:                          ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
                    673:                        }
                    674:                      up = &crecp->hash_next; 
                    675:                    }
                    676:                }
                    677:              else
                    678:                /* case : not expired, incorrect entry. */
                    679:                up = &crecp->hash_next; 
                    680:            }
                    681:          else
                    682:            {
                    683:              /* expired entry, free it */
                    684:              *up = crecp->hash_next;
1.1.1.2 ! misho     685:              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
1.1       misho     686:                { 
                    687:                  cache_unlink(crecp);
                    688:                  cache_free(crecp);
                    689:                }
                    690:            }
                    691:        }
                    692:          
                    693:       *chainp = cache_head;
                    694:     }
                    695: 
                    696:   if (ans && 
                    697:       (ans->flags & F_FORWARD) &&
1.1.1.2 ! misho     698: #ifdef HAVE_DNSSEC
        !           699:       ((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) &&
        !           700: #endif
        !           701:       (ans->flags & prot) &&     
1.1       misho     702:       hostname_isequal(cache_get_name(ans), name))
                    703:     return ans;
                    704:   
                    705:   return NULL;
                    706: }
                    707: 
                    708: struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr, 
1.1.1.2 ! misho     709:                                time_t now, unsigned int prot)
1.1       misho     710: {
                    711:   struct crec *ans;
                    712: #ifdef HAVE_IPV6
                    713:   int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
                    714: #else
                    715:   int addrlen = INADDRSZ;
                    716: #endif
                    717:   
                    718:   if (crecp) /* iterating */
                    719:     ans = crecp->next;
                    720:   else
                    721:     {  
                    722:       /* first search, look for relevant entries and push to top of list
                    723:         also free anything which has expired. All the reverse entries are at the
                    724:         start of the hash chain, so we can give up when we find the first 
                    725:         non-REVERSE one.  */
                    726:        int i;
                    727:        struct crec **up, **chainp = &ans;
                    728:        
                    729:        for (i=0; i<hash_size; i++)
                    730:         for (crecp = hash_table[i], up = &hash_table[i]; 
                    731:              crecp && (crecp->flags & F_REVERSE);
                    732:              crecp = crecp->hash_next)
                    733:           if (!is_expired(now, crecp))
                    734:             {      
                    735:               if ((crecp->flags & prot) &&
                    736:                   memcmp(&crecp->addr.addr, addr, addrlen) == 0)
                    737:                 {          
1.1.1.2 ! misho     738:                   if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
1.1       misho     739:                     {
                    740:                       *chainp = crecp;
                    741:                       chainp = &crecp->next;
                    742:                     }
                    743:                   else
                    744:                     {
                    745:                       cache_unlink(crecp);
                    746:                       cache_link(crecp);
                    747:                     }
                    748:                 }
                    749:               up = &crecp->hash_next;
                    750:             }
                    751:           else
                    752:             {
                    753:               *up = crecp->hash_next;
1.1.1.2 ! misho     754:               if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
1.1       misho     755:                 {
                    756:                   cache_unlink(crecp);
                    757:                   cache_free(crecp);
                    758:                 }
                    759:             }
                    760:        
                    761:        *chainp = cache_head;
                    762:     }
                    763:   
                    764:   if (ans && 
                    765:       (ans->flags & F_REVERSE) &&
                    766:       (ans->flags & prot) &&
                    767:       memcmp(&ans->addr.addr, addr, addrlen) == 0)
                    768:     return ans;
                    769:   
                    770:   return NULL;
                    771: }
                    772: 
                    773: static void add_hosts_cname(struct crec *target)
                    774: {
                    775:   struct crec *crec;
                    776:   struct cname *a;
                    777:   
                    778:   for (a = daemon->cnames; a; a = a->next)
                    779:     if (hostname_isequal(cache_get_name(target), a->target) &&
                    780:        (crec = whine_malloc(sizeof(struct crec))))
                    781:       {
1.1.1.2 ! misho     782:        crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
1.1       misho     783:        crec->name.namep = a->alias;
1.1.1.2 ! misho     784:        crec->addr.cname.target.cache = target;
1.1       misho     785:        crec->addr.cname.uid = target->uid;
1.1.1.2 ! misho     786:        crec->uid = next_uid();
1.1       misho     787:        cache_hash(crec);
                    788:        add_hosts_cname(crec); /* handle chains */
                    789:       }
                    790: }
                    791:   
                    792: static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, 
1.1.1.2 ! misho     793:                            unsigned int index, struct crec **rhash, int hashsz)
1.1       misho     794: {
                    795:   struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
                    796:   int i, nameexists = 0;
                    797:   unsigned int j; 
                    798: 
                    799:   /* Remove duplicates in hosts files. */
                    800:   if (lookup && (lookup->flags & F_HOSTS))
                    801:     {
                    802:       nameexists = 1;
                    803:       if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)
                    804:        {
                    805:          free(cache);
                    806:          return;
                    807:        }
                    808:     }
                    809:   
                    810:   /* Ensure there is only one address -> name mapping (first one trumps) 
                    811:      We do this by steam here, The entries are kept in hash chains, linked
                    812:      by ->next (which is unused at this point) held in hash buckets in
                    813:      the array rhash, hashed on address. Note that rhash and the values
                    814:      in ->next are only valid  whilst reading hosts files: the buckets are
                    815:      then freed, and the ->next pointer used for other things. 
                    816: 
                    817:      Only insert each unique address once into this hashing structure.
                    818: 
                    819:      This complexity avoids O(n^2) divergent CPU use whilst reading
                    820:      large (10000 entry) hosts files. */
                    821:   
                    822:   /* hash address */
                    823:   for (j = 0, i = 0; i < addrlen; i++)
                    824:     j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
                    825:   
                    826:   for (lookup = rhash[j]; lookup; lookup = lookup->next)
                    827:     if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
                    828:        memcmp(&lookup->addr.addr, addr, addrlen) == 0)
                    829:       {
                    830:        cache->flags &= ~F_REVERSE;
                    831:        break;
                    832:       }
                    833:   
                    834:   /* maintain address hash chain, insert new unique address */
                    835:   if (!lookup)
                    836:     {
                    837:       cache->next = rhash[j];
                    838:       rhash[j] = cache;
                    839:     }
                    840:   
                    841:   cache->uid = index;
                    842:   memcpy(&cache->addr.addr, addr, addrlen);  
                    843:   cache_hash(cache);
                    844:   
                    845:   /* don't need to do alias stuff for second and subsequent addresses. */
                    846:   if (!nameexists)
                    847:     add_hosts_cname(cache);
                    848: }
                    849: 
                    850: static int eatspace(FILE *f)
                    851: {
                    852:   int c, nl = 0;
                    853: 
                    854:   while (1)
                    855:     {
                    856:       if ((c = getc(f)) == '#')
                    857:        while (c != '\n' && c != EOF)
                    858:          c = getc(f);
                    859:       
                    860:       if (c == EOF)
                    861:        return 1;
                    862: 
                    863:       if (!isspace(c))
                    864:        {
                    865:          ungetc(c, f);
                    866:          return nl;
                    867:        }
                    868: 
                    869:       if (c == '\n')
                    870:        nl = 1;
                    871:     }
                    872: }
                    873:         
                    874: static int gettok(FILE *f, char *token)
                    875: {
                    876:   int c, count = 0;
                    877:  
                    878:   while (1)
                    879:     {
                    880:       if ((c = getc(f)) == EOF)
                    881:        return (count == 0) ? EOF : 1;
                    882: 
                    883:       if (isspace(c) || c == '#')
                    884:        {
                    885:          ungetc(c, f);
                    886:          return eatspace(f);
                    887:        }
                    888:       
                    889:       if (count < (MAXDNAME - 1))
                    890:        {
                    891:          token[count++] = c;
                    892:          token[count] = 0;
                    893:        }
                    894:     }
                    895: }
                    896: 
1.1.1.2 ! misho     897: static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
1.1       misho     898: {  
                    899:   FILE *f = fopen(filename, "r");
                    900:   char *token = daemon->namebuff, *domain_suffix = NULL;
                    901:   int addr_count = 0, name_count = cache_size, lineno = 0;
                    902:   unsigned short flags = 0;
                    903:   struct all_addr addr;
                    904:   int atnl, addrlen = 0;
                    905: 
                    906:   if (!f)
                    907:     {
                    908:       my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
                    909:       return 0;
                    910:     }
                    911:   
                    912:   eatspace(f);
                    913:   
                    914:   while ((atnl = gettok(f, token)) != EOF)
                    915:     {
                    916:       lineno++;
                    917:       
                    918:       if (inet_pton(AF_INET, token, &addr) > 0)
                    919:        {
                    920:          flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
                    921:          addrlen = INADDRSZ;
                    922:          domain_suffix = get_domain(addr.addr.addr4);
                    923:        }
                    924: #ifdef HAVE_IPV6
                    925:       else if (inet_pton(AF_INET6, token, &addr) > 0)
                    926:        {
                    927:          flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
                    928:          addrlen = IN6ADDRSZ;
                    929:          domain_suffix = get_domain6(&addr.addr.addr6);
                    930:        }
                    931: #endif
                    932:       else
                    933:        {
                    934:          my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); 
                    935:          while (atnl == 0)
                    936:            atnl = gettok(f, token);
                    937:          continue;
                    938:        }
                    939:       
                    940:       addr_count++;
                    941:       
                    942:       /* rehash every 1000 names. */
                    943:       if ((name_count - cache_size) > 1000)
                    944:        {
                    945:          rehash(name_count);
                    946:          cache_size = name_count;
                    947:        } 
                    948:       
                    949:       while (atnl == 0)
                    950:        {
                    951:          struct crec *cache;
                    952:          int fqdn, nomem;
                    953:          char *canon;
                    954:          
                    955:          if ((atnl = gettok(f, token)) == EOF)
                    956:            break;
                    957: 
                    958:          fqdn = !!strchr(token, '.');
                    959: 
                    960:          if ((canon = canonicalise(token, &nomem)))
                    961:            {
                    962:              /* If set, add a version of the name with a default domain appended */
                    963:              if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn && 
                    964:                  (cache = whine_malloc(sizeof(struct crec) + 
                    965:                                        strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
                    966:                {
                    967:                  strcpy(cache->name.sname, canon);
                    968:                  strcat(cache->name.sname, ".");
                    969:                  strcat(cache->name.sname, domain_suffix);
                    970:                  cache->flags = flags;
                    971:                  add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                    972:                  name_count++;
                    973:                }
                    974:              if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
                    975:                {
                    976:                  strcpy(cache->name.sname, canon);
                    977:                  cache->flags = flags;
                    978:                  add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                    979:                  name_count++;
                    980:                }
                    981:              free(canon);
                    982:              
                    983:            }
                    984:          else if (!nomem)
                    985:            my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno); 
                    986:        }
                    987:     } 
                    988: 
                    989:   fclose(f);
                    990:   rehash(name_count);
                    991:   
                    992:   my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
                    993:   
                    994:   return name_count;
                    995: }
                    996:            
                    997: void cache_reload(void)
                    998: {
                    999:   struct crec *cache, **up, *tmp;
                   1000:   int revhashsz, i, total_size = daemon->cachesize;
                   1001:   struct hostsfile *ah;
                   1002:   struct host_record *hr;
                   1003:   struct name_list *nl;
1.1.1.2 ! misho    1004:   struct cname *a;
        !          1005:   struct interface_name *intr;
        !          1006: #ifdef HAVE_DNSSEC
        !          1007:   struct ds_config *ds;
        !          1008: #endif
1.1       misho    1009: 
                   1010:   cache_inserted = cache_live_freed = 0;
                   1011:   
                   1012:   for (i=0; i<hash_size; i++)
                   1013:     for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
                   1014:       {
1.1.1.2 ! misho    1015: #ifdef HAVE_DNSSEC
        !          1016:        cache_blockdata_free(cache);
        !          1017: #endif
1.1       misho    1018:        tmp = cache->hash_next;
1.1.1.2 ! misho    1019:        if (cache->flags & (F_HOSTS | F_CONFIG))
1.1       misho    1020:          {
                   1021:            *up = cache->hash_next;
                   1022:            free(cache);
                   1023:          }
                   1024:        else if (!(cache->flags & F_DHCP))
                   1025:          {
                   1026:            *up = cache->hash_next;
                   1027:            if (cache->flags & F_BIGNAME)
                   1028:              {
                   1029:                cache->name.bname->next = big_free;
                   1030:                big_free = cache->name.bname;
                   1031:              }
                   1032:            cache->flags = 0;
                   1033:          }
                   1034:        else
                   1035:          up = &cache->hash_next;
                   1036:       }
                   1037:   
1.1.1.2 ! misho    1038:   /* Add CNAMEs to interface_names to the cache */
        !          1039:   for (a = daemon->cnames; a; a = a->next)
        !          1040:     for (intr = daemon->int_names; intr; intr = intr->next)
        !          1041:       if (hostname_isequal(a->target, intr->name) &&
        !          1042:          ((cache = whine_malloc(sizeof(struct crec)))))
        !          1043:        {
        !          1044:          cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
        !          1045:          cache->name.namep = a->alias;
        !          1046:          cache->addr.cname.target.int_name = intr;
        !          1047:          cache->addr.cname.uid = SRC_INTERFACE;
        !          1048:          cache->uid = next_uid();
        !          1049:          cache_hash(cache);
        !          1050:          add_hosts_cname(cache); /* handle chains */
        !          1051:        }
        !          1052: 
        !          1053: #ifdef HAVE_DNSSEC
        !          1054:   for (ds = daemon->ds; ds; ds = ds->next)
        !          1055:     if ((cache = whine_malloc(sizeof(struct crec))) &&
        !          1056:        (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
        !          1057:       {
        !          1058:        cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
        !          1059:        cache->name.namep = ds->name;
        !          1060:        cache->addr.ds.keylen = ds->digestlen;
        !          1061:        cache->addr.ds.algo = ds->algo;
        !          1062:        cache->addr.ds.keytag = ds->keytag;
        !          1063:        cache->addr.ds.digest = ds->digest_type;
        !          1064:        cache->uid = ds->class;
        !          1065:        cache_hash(cache);
        !          1066:       }
        !          1067: #endif
        !          1068:   
1.1       misho    1069:   /* borrow the packet buffer for a temporary by-address hash */
                   1070:   memset(daemon->packet, 0, daemon->packet_buff_sz);
                   1071:   revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
                   1072:   /* we overwrote the buffer... */
                   1073:   daemon->srv_save = NULL;
                   1074: 
                   1075:   /* Do host_records in config. */
                   1076:   for (hr = daemon->host_records; hr; hr = hr->next)
                   1077:     for (nl = hr->names; nl; nl = nl->next)
                   1078:       {
                   1079:        if (hr->addr.s_addr != 0 &&
                   1080:            (cache = whine_malloc(sizeof(struct crec))))
                   1081:          {
                   1082:            cache->name.namep = nl->name;
                   1083:            cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
1.1.1.2 ! misho    1084:            add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
1.1       misho    1085:          }
                   1086: #ifdef HAVE_IPV6
                   1087:        if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
                   1088:            (cache = whine_malloc(sizeof(struct crec))))
                   1089:          {
                   1090:            cache->name.namep = nl->name;
                   1091:            cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
1.1.1.2 ! misho    1092:            add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
1.1       misho    1093:          }
                   1094: #endif
                   1095:       }
                   1096:        
                   1097:   if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
                   1098:     {
                   1099:       if (daemon->cachesize > 0)
                   1100:        my_syslog(LOG_INFO, _("cleared cache"));
                   1101:       return;
                   1102:     }
                   1103:     
                   1104:   if (!option_bool(OPT_NO_HOSTS))
1.1.1.2 ! misho    1105:     total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
1.1       misho    1106:           
                   1107:   daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
                   1108:   for (ah = daemon->addn_hosts; ah; ah = ah->next)
                   1109:     if (!(ah->flags & AH_INACTIVE))
                   1110:       total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
                   1111: } 
                   1112: 
                   1113: #ifdef HAVE_DHCP
                   1114: struct in_addr a_record_from_hosts(char *name, time_t now)
                   1115: {
                   1116:   struct crec *crecp = NULL;
                   1117:   struct in_addr ret;
                   1118:   
                   1119:   while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
                   1120:     if (crecp->flags & F_HOSTS)
                   1121:       return *(struct in_addr *)&crecp->addr;
                   1122: 
                   1123:   my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
                   1124:   
                   1125:   ret.s_addr = 0;
                   1126:   return ret;
                   1127: }
                   1128: 
                   1129: void cache_unhash_dhcp(void)
                   1130: {
                   1131:   struct crec *cache, **up;
                   1132:   int i;
                   1133: 
                   1134:   for (i=0; i<hash_size; i++)
                   1135:     for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
                   1136:       if (cache->flags & F_DHCP)
                   1137:        {
                   1138:          *up = cache->hash_next;
                   1139:          cache->next = dhcp_spare;
                   1140:          dhcp_spare = cache;
                   1141:        }
                   1142:       else
                   1143:        up = &cache->hash_next;
                   1144: }
                   1145: 
                   1146: static void add_dhcp_cname(struct crec *target, time_t ttd)
                   1147: {
                   1148:   struct crec *aliasc;
                   1149:   struct cname *a;
                   1150:   
                   1151:   for (a = daemon->cnames; a; a = a->next)
                   1152:     if (hostname_isequal(cache_get_name(target), a->target))
                   1153:       {
                   1154:        if ((aliasc = dhcp_spare))
                   1155:          dhcp_spare = dhcp_spare->next;
                   1156:        else /* need new one */
                   1157:          aliasc = whine_malloc(sizeof(struct crec));
                   1158:        
                   1159:        if (aliasc)
                   1160:          {
1.1.1.2 ! misho    1161:            aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
1.1       misho    1162:            if (ttd == 0)
                   1163:              aliasc->flags |= F_IMMORTAL;
                   1164:            else
                   1165:              aliasc->ttd = ttd;
                   1166:            aliasc->name.namep = a->alias;
1.1.1.2 ! misho    1167:            aliasc->addr.cname.target.cache = target;
1.1       misho    1168:            aliasc->addr.cname.uid = target->uid;
1.1.1.2 ! misho    1169:            aliasc->uid = next_uid();
1.1       misho    1170:            cache_hash(aliasc);
                   1171:            add_dhcp_cname(aliasc, ttd);
                   1172:          }
                   1173:       }
                   1174: }
                   1175: 
                   1176: void cache_add_dhcp_entry(char *host_name, int prot,
                   1177:                          struct all_addr *host_address, time_t ttd) 
                   1178: {
                   1179:   struct crec *crec = NULL, *fail_crec = NULL;
                   1180:   unsigned short flags = F_IPV4;
                   1181:   int in_hosts = 0;
                   1182:   size_t addrlen = sizeof(struct in_addr);
                   1183: 
                   1184: #ifdef HAVE_IPV6
                   1185:   if (prot == AF_INET6)
                   1186:     {
                   1187:       flags = F_IPV6;
                   1188:       addrlen = sizeof(struct in6_addr);
                   1189:     }
                   1190: #endif
                   1191:   
                   1192:   inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
                   1193:   
                   1194:   while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
                   1195:     {
                   1196:       /* check all addresses associated with name */
1.1.1.2 ! misho    1197:       if (crec->flags & (F_HOSTS | F_CONFIG))
1.1       misho    1198:        {
                   1199:          if (crec->flags & F_CNAME)
                   1200:            my_syslog(MS_DHCP | LOG_WARNING, 
                   1201:                      _("%s is a CNAME, not giving it to the DHCP lease of %s"),
                   1202:                      host_name, daemon->addrbuff);
                   1203:          else if (memcmp(&crec->addr.addr, host_address, addrlen) == 0)
                   1204:            in_hosts = 1;
                   1205:          else
                   1206:            fail_crec = crec;
                   1207:        }
                   1208:       else if (!(crec->flags & F_DHCP))
                   1209:        {
                   1210:          cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
                   1211:          /* scan_free deletes all addresses associated with name */
                   1212:          break;
                   1213:        }
                   1214:     }
                   1215:   
                   1216:   /* if in hosts, don't need DHCP record */
                   1217:   if (in_hosts)
                   1218:     return;
                   1219:   
                   1220:   /* Name in hosts, address doesn't match */
                   1221:   if (fail_crec)
                   1222:     {
                   1223:       inet_ntop(prot, &fail_crec->addr.addr, daemon->namebuff, MAXDNAME);
                   1224:       my_syslog(MS_DHCP | LOG_WARNING, 
                   1225:                _("not giving name %s to the DHCP lease of %s because "
                   1226:                  "the name exists in %s with address %s"), 
                   1227:                host_name, daemon->addrbuff,
                   1228:                record_source(fail_crec->uid), daemon->namebuff);
                   1229:       return;
                   1230:     }    
                   1231:   
                   1232:   if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
                   1233:     {
                   1234:       if (crec->flags & F_NEG)
                   1235:        {
                   1236:          flags |= F_REVERSE;
                   1237:          cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
                   1238:        }
                   1239:     }
                   1240:   else
                   1241:     flags |= F_REVERSE;
                   1242:   
                   1243:   if ((crec = dhcp_spare))
                   1244:     dhcp_spare = dhcp_spare->next;
                   1245:   else /* need new one */
                   1246:     crec = whine_malloc(sizeof(struct crec));
                   1247:   
                   1248:   if (crec) /* malloc may fail */
                   1249:     {
                   1250:       crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
                   1251:       if (ttd == 0)
                   1252:        crec->flags |= F_IMMORTAL;
                   1253:       else
                   1254:        crec->ttd = ttd;
                   1255:       crec->addr.addr = *host_address;
                   1256:       crec->name.namep = host_name;
1.1.1.2 ! misho    1257:       crec->uid = next_uid();
1.1       misho    1258:       cache_hash(crec);
                   1259: 
                   1260:       add_dhcp_cname(crec, ttd);
                   1261:     }
                   1262: }
                   1263: #endif
                   1264: 
1.1.1.2 ! misho    1265: int cache_make_stat(struct txt_record *t)
        !          1266: { 
        !          1267:   static char *buff = NULL;
        !          1268:   static int bufflen = 60;
        !          1269:   int len;
        !          1270:   struct server *serv, *serv1;
        !          1271:   char *p;
        !          1272: 
        !          1273:   if (!buff && !(buff = whine_malloc(60)))
        !          1274:     return 0;
        !          1275: 
        !          1276:   p = buff;
        !          1277:   
        !          1278:   switch (t->stat)
        !          1279:     {
        !          1280:     case TXT_STAT_CACHESIZE:
        !          1281:       sprintf(buff+1, "%d", daemon->cachesize);
        !          1282:       break;
        !          1283: 
        !          1284:     case TXT_STAT_INSERTS:
        !          1285:       sprintf(buff+1, "%d", cache_inserted);
        !          1286:       break;
        !          1287: 
        !          1288:     case TXT_STAT_EVICTIONS:
        !          1289:       sprintf(buff+1, "%d", cache_live_freed);
        !          1290:       break;
        !          1291: 
        !          1292:     case TXT_STAT_MISSES:
        !          1293:       sprintf(buff+1, "%u", daemon->queries_forwarded);
        !          1294:       break;
        !          1295: 
        !          1296:     case TXT_STAT_HITS:
        !          1297:       sprintf(buff+1, "%u", daemon->local_answer);
        !          1298:       break;
        !          1299: 
        !          1300: #ifdef HAVE_AUTH
        !          1301:     case TXT_STAT_AUTH:
        !          1302:       sprintf(buff+1, "%u", daemon->auth_answer);
        !          1303:       break;
        !          1304: #endif
        !          1305: 
        !          1306:     case TXT_STAT_SERVERS:
        !          1307:       /* sum counts from different records for same server */
        !          1308:       for (serv = daemon->servers; serv; serv = serv->next)
        !          1309:        serv->flags &= ~SERV_COUNTED;
        !          1310:       
        !          1311:       for (serv = daemon->servers; serv; serv = serv->next)
        !          1312:        if (!(serv->flags & 
        !          1313:              (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
        !          1314:          {
        !          1315:            char *new, *lenp;
        !          1316:            int port, newlen, bytes_avail, bytes_needed;
        !          1317:            unsigned int queries = 0, failed_queries = 0;
        !          1318:            for (serv1 = serv; serv1; serv1 = serv1->next)
        !          1319:              if (!(serv1->flags & 
        !          1320:                    (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) && 
        !          1321:                  sockaddr_isequal(&serv->addr, &serv1->addr))
        !          1322:                {
        !          1323:                  serv1->flags |= SERV_COUNTED;
        !          1324:                  queries += serv1->queries;
        !          1325:                  failed_queries += serv1->failed_queries;
        !          1326:                }
        !          1327:            port = prettyprint_addr(&serv->addr, daemon->addrbuff);
        !          1328:            lenp = p++; /* length */
        !          1329:            bytes_avail = (p - buff) + bufflen;
        !          1330:            bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
        !          1331:            if (bytes_needed >= bytes_avail)
        !          1332:              {
        !          1333:                /* expand buffer if necessary */
        !          1334:                newlen = bytes_needed + 1 + bufflen - bytes_avail;
        !          1335:                if (!(new = whine_malloc(newlen)))
        !          1336:                  return 0;
        !          1337:                memcpy(new, buff, bufflen);
        !          1338:                free(buff);
        !          1339:                p = new + (p - buff);
        !          1340:                lenp = p - 1;
        !          1341:                buff = new;
        !          1342:                bufflen = newlen;
        !          1343:                bytes_avail = (p - buff) + bufflen;
        !          1344:                bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
        !          1345:              }
        !          1346:            *lenp = bytes_needed;
        !          1347:            p += bytes_needed;
        !          1348:          }
        !          1349:       t->txt = (unsigned char *)buff;
        !          1350:       t->len = p - buff;
        !          1351:       return 1;
        !          1352:     }
        !          1353:   
        !          1354:   len = strlen(buff+1);
        !          1355:   t->txt = (unsigned char *)buff;
        !          1356:   t->len = len + 1;
        !          1357:   *buff = len;
        !          1358:   return 1;
        !          1359: }
1.1       misho    1360: 
                   1361: void dump_cache(time_t now)
                   1362: {
                   1363:   struct server *serv, *serv1;
1.1.1.2 ! misho    1364:   char *t = "";
1.1       misho    1365: 
                   1366:   my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
                   1367:   my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."), 
                   1368:            daemon->cachesize, cache_live_freed, cache_inserted);
                   1369:   my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), 
                   1370:            daemon->queries_forwarded, daemon->local_answer);
1.1.1.2 ! misho    1371: #ifdef HAVE_AUTH
        !          1372:   my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
        !          1373: #endif
        !          1374: #ifdef HAVE_DNSSEC
        !          1375:   blockdata_report();
        !          1376: #endif
1.1       misho    1377: 
                   1378:   /* sum counts from different records for same server */
                   1379:   for (serv = daemon->servers; serv; serv = serv->next)
                   1380:     serv->flags &= ~SERV_COUNTED;
                   1381:   
                   1382:   for (serv = daemon->servers; serv; serv = serv->next)
                   1383:     if (!(serv->flags & 
                   1384:          (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
                   1385:       {
                   1386:        int port;
                   1387:        unsigned int queries = 0, failed_queries = 0;
                   1388:        for (serv1 = serv; serv1; serv1 = serv1->next)
                   1389:          if (!(serv1->flags & 
                   1390:                (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) && 
                   1391:              sockaddr_isequal(&serv->addr, &serv1->addr))
                   1392:            {
                   1393:              serv1->flags |= SERV_COUNTED;
                   1394:              queries += serv1->queries;
                   1395:              failed_queries += serv1->failed_queries;
                   1396:            }
                   1397:        port = prettyprint_addr(&serv->addr, daemon->addrbuff);
                   1398:        my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
                   1399:       }
                   1400:   
                   1401:   if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
                   1402:     {
                   1403:       struct crec *cache ;
                   1404:       int i;
1.1.1.2 ! misho    1405:       my_syslog(LOG_INFO, "Host                                     Address                        Flags      Expires");
1.1       misho    1406:     
                   1407:       for (i=0; i<hash_size; i++)
                   1408:        for (cache = hash_table[i]; cache; cache = cache->hash_next)
                   1409:          {
1.1.1.2 ! misho    1410:            char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
        !          1411:            *a = 0;
        !          1412:            if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
        !          1413:              n = "<Root>";
        !          1414:            p += sprintf(p, "%-40.40s ", n);
        !          1415:            if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
        !          1416:              a = cache_get_cname_target(cache);
1.1       misho    1417: #ifdef HAVE_DNSSEC
                   1418:            else if (cache->flags & F_DS)
                   1419:              {
1.1.1.2 ! misho    1420:                if (cache->flags & F_DNSKEY)
        !          1421:                  /* RRSIG */
        !          1422:                  sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
        !          1423:                          cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
        !          1424:                else if (!(cache->flags & F_NEG))
        !          1425:                  sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
        !          1426:                          cache->addr.ds.algo, cache->addr.ds.digest);
1.1       misho    1427:              }
1.1.1.2 ! misho    1428:            else if (cache->flags & F_DNSKEY)
        !          1429:              sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
        !          1430:                      cache->addr.key.algo, cache->addr.key.flags);
1.1       misho    1431: #endif
1.1.1.2 ! misho    1432:            else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
1.1       misho    1433:              { 
                   1434:                a = daemon->addrbuff;
                   1435:                if (cache->flags & F_IPV4)
                   1436:                  inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
                   1437: #ifdef HAVE_IPV6
                   1438:                else if (cache->flags & F_IPV6)
                   1439:                  inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
                   1440: #endif
                   1441:              }
                   1442: 
1.1.1.2 ! misho    1443:            if (cache->flags & F_IPV4)
        !          1444:              t = "4";
        !          1445:            else if (cache->flags & F_IPV6)
        !          1446:              t = "6";
        !          1447:            else if (cache->flags & F_CNAME)
        !          1448:              t = "C";
        !          1449: #ifdef HAVE_DNSSEC
        !          1450:            else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
        !          1451:              t = "G"; /* DNSKEY and DS set -> RRISG */
        !          1452:            else if (cache->flags & F_DS)
        !          1453:              t = "S";
        !          1454:            else if (cache->flags & F_DNSKEY)
        !          1455:              t = "K";
        !          1456: #endif
        !          1457:            p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s  ", a, t,
1.1       misho    1458:                         cache->flags & F_FORWARD ? "F" : " ",
                   1459:                         cache->flags & F_REVERSE ? "R" : " ",
                   1460:                         cache->flags & F_IMMORTAL ? "I" : " ",
                   1461:                         cache->flags & F_DHCP ? "D" : " ",
                   1462:                         cache->flags & F_NEG ? "N" : " ",
                   1463:                         cache->flags & F_NXDOMAIN ? "X" : " ",
                   1464:                         cache->flags & F_HOSTS ? "H" : " ",
                   1465:                         cache->flags & F_DNSSECOK ? "V" : " ");
                   1466: #ifdef HAVE_BROKEN_RTC
                   1467:            p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
                   1468: #else
                   1469:            p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
                   1470:            /* ctime includes trailing \n - eat it */
                   1471:            *(p-1) = 0;
                   1472: #endif
                   1473:            my_syslog(LOG_INFO, daemon->namebuff);
                   1474:          }
                   1475:     }
                   1476: }
                   1477: 
1.1.1.2 ! misho    1478: char *record_source(unsigned int index)
1.1       misho    1479: {
                   1480:   struct hostsfile *ah;
                   1481: 
1.1.1.2 ! misho    1482:   if (index == SRC_CONFIG)
        !          1483:     return "config";
        !          1484:   else if (index == SRC_HOSTS)
1.1       misho    1485:     return HOSTSFILE;
                   1486: 
                   1487:   for (ah = daemon->addn_hosts; ah; ah = ah->next)
                   1488:     if (ah->index == index)
                   1489:       return ah->fname;
                   1490:   
                   1491:   return "<unknown>";
                   1492: }
                   1493: 
1.1.1.2 ! misho    1494: char *querystr(char *desc, unsigned short type)
1.1       misho    1495: {
                   1496:   unsigned int i;
1.1.1.2 ! misho    1497:   int len = 10; /* strlen("type=xxxxx") */
        !          1498:   const char *types = NULL;
        !          1499:   static char *buff = NULL;
        !          1500:   static int bufflen = 0;
        !          1501: 
1.1       misho    1502:   for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
                   1503:     if (typestr[i].type == type)
1.1.1.2 ! misho    1504:       {
        !          1505:        types = typestr[i].name;
        !          1506:        len = strlen(types);
        !          1507:        break;
        !          1508:       }
        !          1509: 
        !          1510:   len += 3; /* braces, terminator */
        !          1511:   len += strlen(desc);
        !          1512: 
        !          1513:   if (!buff || bufflen < len)
        !          1514:     {
        !          1515:       if (buff)
        !          1516:        free(buff);
        !          1517:       else if (len < 20)
        !          1518:        len = 20;
        !          1519:       
        !          1520:       buff = whine_malloc(len);
        !          1521:       bufflen = len;
        !          1522:     }
        !          1523: 
        !          1524:   if (buff)
        !          1525:     {
        !          1526:       if (types)
        !          1527:        sprintf(buff, "%s[%s]", desc, types);
        !          1528:       else
        !          1529:        sprintf(buff, "%s[type=%d]", desc, type);
        !          1530:     }
        !          1531: 
        !          1532:   return buff ? buff : "";
1.1       misho    1533: }
                   1534: 
                   1535: void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
                   1536: {
                   1537:   char *source, *dest = daemon->addrbuff;
                   1538:   char *verb = "is";
                   1539:   
                   1540:   if (!option_bool(OPT_LOG))
                   1541:     return;
                   1542: 
                   1543:   if (addr)
                   1544:     {
1.1.1.2 ! misho    1545:       if (flags & F_KEYTAG)
        !          1546:        sprintf(daemon->addrbuff, arg, addr->addr.keytag);
        !          1547:       else
        !          1548:        {
1.1       misho    1549: #ifdef HAVE_IPV6
1.1.1.2 ! misho    1550:          inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
        !          1551:                    addr, daemon->addrbuff, ADDRSTRLEN);
1.1       misho    1552: #else
1.1.1.2 ! misho    1553:          strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);  
1.1       misho    1554: #endif
1.1.1.2 ! misho    1555:        }
1.1       misho    1556:     }
1.1.1.2 ! misho    1557:   else
        !          1558:     dest = arg;
1.1       misho    1559: 
                   1560:   if (flags & F_REVERSE)
                   1561:     {
                   1562:       dest = name;
                   1563:       name = daemon->addrbuff;
                   1564:     }
                   1565:   
                   1566:   if (flags & F_NEG)
                   1567:     {
                   1568:       if (flags & F_NXDOMAIN)
1.1.1.2 ! misho    1569:        dest = "NXDOMAIN";
1.1       misho    1570:       else
                   1571:        {      
                   1572:          if (flags & F_IPV4)
                   1573:            dest = "NODATA-IPv4";
                   1574:          else if (flags & F_IPV6)
                   1575:            dest = "NODATA-IPv6";
                   1576:          else
                   1577:            dest = "NODATA";
                   1578:        }
                   1579:     }
                   1580:   else if (flags & F_CNAME)
                   1581:     dest = "<CNAME>";
                   1582:   else if (flags & F_RRNAME)
                   1583:     dest = arg;
                   1584:     
                   1585:   if (flags & F_CONFIG)
                   1586:     source = "config";
                   1587:   else if (flags & F_DHCP)
                   1588:     source = "DHCP";
                   1589:   else if (flags & F_HOSTS)
                   1590:     source = arg;
                   1591:   else if (flags & F_UPSTREAM)
                   1592:     source = "reply";
1.1.1.2 ! misho    1593:   else if (flags & F_SECSTAT)
        !          1594:     source = "validation";
1.1       misho    1595:   else if (flags & F_AUTH)
                   1596:     source = "auth";
                   1597:   else if (flags & F_SERVER)
                   1598:     {
                   1599:       source = "forwarded";
                   1600:       verb = "to";
                   1601:     }
                   1602:   else if (flags & F_QUERY)
                   1603:     {
                   1604:       source = arg;
                   1605:       verb = "from";
                   1606:     }
1.1.1.2 ! misho    1607:   else if (flags & F_DNSSEC)
        !          1608:     {
        !          1609:       source = arg;
        !          1610:       verb = "to";
        !          1611:     }
        !          1612:   else if (flags & F_IPSET)
        !          1613:     {
        !          1614:       source = "ipset add";
        !          1615:       dest = name;
        !          1616:       name = arg;
        !          1617:       verb = daemon->addrbuff;
        !          1618:     }
1.1       misho    1619:   else
                   1620:     source = "cached";
                   1621:   
                   1622:   if (strlen(name) == 0)
                   1623:     name = ".";
                   1624: 
                   1625:   my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
                   1626: }
                   1627: 
1.1.1.2 ! misho    1628:  

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