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