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>