]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/cache.c
Improve performance when reading large hostfiles.
[people/ms/dnsmasq.git] / src / cache.c
1 /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
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 static int uid = 0;
28
29 /* type->string mapping: this is also used by the name-hash function as a mixing table. */
30 static const struct {
31 unsigned int type;
32 const char * const name;
33 } typestr[] = {
34 { 1, "A" },
35 { 2, "NS" },
36 { 5, "CNAME" },
37 { 6, "SOA" },
38 { 10, "NULL" },
39 { 11, "WKS" },
40 { 12, "PTR" },
41 { 13, "HINFO" },
42 { 15, "MX" },
43 { 16, "TXT" },
44 { 22, "NSAP" },
45 { 23, "NSAP_PTR" },
46 { 24, "SIG" },
47 { 25, "KEY" },
48 { 28, "AAAA" },
49 { 33, "SRV" },
50 { 35, "NAPTR" },
51 { 36, "KX" },
52 { 37, "CERT" },
53 { 38, "A6" },
54 { 39, "DNAME" },
55 { 41, "OPT" },
56 { 48, "DNSKEY" },
57 { 249, "TKEY" },
58 { 250, "TSIG" },
59 { 251, "IXFR" },
60 { 252, "AXFR" },
61 { 253, "MAILB" },
62 { 254, "MAILA" },
63 { 255, "ANY" }
64 };
65
66 static void cache_free(struct crec *crecp);
67 static void cache_unlink(struct crec *crecp);
68 static void cache_link(struct crec *crecp);
69 static void rehash(int size);
70 static void cache_hash(struct crec *crecp);
71
72 void cache_init(void)
73 {
74 struct crec *crecp;
75 int i;
76
77 bignames_left = daemon->cachesize/10;
78
79 if (daemon->cachesize > 0)
80 {
81 crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
82
83 for (i=0; i < daemon->cachesize; i++, crecp++)
84 {
85 cache_link(crecp);
86 crecp->flags = 0;
87 crecp->uid = uid++;
88 }
89 }
90
91 /* create initial hash table*/
92 rehash(daemon->cachesize);
93 }
94
95 /* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
96 but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
97 will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
98 expand the table. */
99 static void rehash(int size)
100 {
101 struct crec **new, **old, *p, *tmp;
102 int i, new_size, old_size;
103
104 /* hash_size is a power of two. */
105 for (new_size = 64; new_size < size/10; new_size = new_size << 1);
106
107 /* must succeed in getting first instance, failure later is non-fatal */
108 if (!hash_table)
109 new = safe_malloc(new_size * sizeof(struct crec *));
110 else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
111 return;
112
113 for(i = 0; i < new_size; i++)
114 new[i] = NULL;
115
116 old = hash_table;
117 old_size = hash_size;
118 hash_table = new;
119 hash_size = new_size;
120
121 if (old)
122 {
123 for (i = 0; i < old_size; i++)
124 for (p = old[i]; p ; p = tmp)
125 {
126 tmp = p->hash_next;
127 cache_hash(p);
128 }
129 free(old);
130 }
131 }
132
133 static struct crec **hash_bucket(char *name)
134 {
135 unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
136 const unsigned char *mix_tab = (const unsigned char*)typestr;
137
138 while((c = (unsigned char) *name++))
139 {
140 /* don't use tolower and friends here - they may be messed up by LOCALE */
141 if (c >= 'A' && c <= 'Z')
142 c += 'a' - 'A';
143 val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c);
144 }
145
146 /* hash_size is a power of two */
147 return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
148 }
149
150 static void cache_hash(struct crec *crecp)
151 {
152 /* maintain an invariant that all entries with F_REVERSE set
153 are at the start of the hash-chain and all non-reverse
154 immortal entries are at the end of the hash-chain.
155 This allows reverse searches and garbage collection to be optimised */
156
157 struct crec **up = hash_bucket(cache_get_name(crecp));
158
159 if (!(crecp->flags & F_REVERSE))
160 {
161 while (*up && ((*up)->flags & F_REVERSE))
162 up = &((*up)->hash_next);
163
164 if (crecp->flags & F_IMMORTAL)
165 while (*up && !((*up)->flags & F_IMMORTAL))
166 up = &((*up)->hash_next);
167 }
168 crecp->hash_next = *up;
169 *up = crecp;
170 }
171
172 static void cache_free(struct crec *crecp)
173 {
174 crecp->flags &= ~F_FORWARD;
175 crecp->flags &= ~F_REVERSE;
176 crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
177
178 if (cache_tail)
179 cache_tail->next = crecp;
180 else
181 cache_head = crecp;
182 crecp->prev = cache_tail;
183 crecp->next = NULL;
184 cache_tail = crecp;
185
186 /* retrieve big name for further use. */
187 if (crecp->flags & F_BIGNAME)
188 {
189 crecp->name.bname->next = big_free;
190 big_free = crecp->name.bname;
191 crecp->flags &= ~F_BIGNAME;
192 }
193 }
194
195 /* insert a new cache entry at the head of the list (youngest entry) */
196 static void cache_link(struct crec *crecp)
197 {
198 if (cache_head) /* check needed for init code */
199 cache_head->prev = crecp;
200 crecp->next = cache_head;
201 crecp->prev = NULL;
202 cache_head = crecp;
203 if (!cache_tail)
204 cache_tail = crecp;
205 }
206
207 /* remove an arbitrary cache entry for promotion */
208 static void cache_unlink (struct crec *crecp)
209 {
210 if (crecp->prev)
211 crecp->prev->next = crecp->next;
212 else
213 cache_head = crecp->next;
214
215 if (crecp->next)
216 crecp->next->prev = crecp->prev;
217 else
218 cache_tail = crecp->prev;
219 }
220
221 char *cache_get_name(struct crec *crecp)
222 {
223 if (crecp->flags & F_BIGNAME)
224 return crecp->name.bname->name;
225 else if (crecp->flags & F_NAMEP)
226 return crecp->name.namep;
227
228 return crecp->name.sname;
229 }
230
231 static int is_outdated_cname_pointer(struct crec *crecp)
232 {
233 if (!(crecp->flags & F_CNAME))
234 return 0;
235
236 if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
237 return 0;
238
239 return 1;
240 }
241
242 static int is_expired(time_t now, struct crec *crecp)
243 {
244 if (crecp->flags & F_IMMORTAL)
245 return 0;
246
247 if (difftime(now, crecp->ttd) < 0)
248 return 0;
249
250 return 1;
251 }
252
253 static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
254 {
255 /* Scan and remove old entries.
256 If (flags & F_FORWARD) then remove any forward entries for name and any expired
257 entries but only in the same hash bucket as name.
258 If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
259 entries in the whole cache.
260 If (flags == 0) remove any expired entries in the whole cache.
261
262 In the flags & F_FORWARD case, the return code is valid, and returns zero if the
263 name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
264
265 We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
266 so that when we hit an entry which isn't reverse and is immortal, we're done. */
267
268 struct crec *crecp, **up;
269
270 if (flags & F_FORWARD)
271 {
272 for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
273 if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
274 {
275 *up = crecp->hash_next;
276 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
277 {
278 cache_unlink(crecp);
279 cache_free(crecp);
280 }
281 }
282 else if ((crecp->flags & F_FORWARD) &&
283 ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
284 hostname_isequal(cache_get_name(crecp), name))
285 {
286 if (crecp->flags & (F_HOSTS | F_DHCP))
287 return 0;
288 *up = crecp->hash_next;
289 cache_unlink(crecp);
290 cache_free(crecp);
291 }
292 else
293 up = &crecp->hash_next;
294 }
295 else
296 {
297 int i;
298 #ifdef HAVE_IPV6
299 int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
300 #else
301 int addrlen = INADDRSZ;
302 #endif
303 for (i = 0; i < hash_size; i++)
304 for (crecp = hash_table[i], up = &hash_table[i];
305 crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
306 crecp = crecp->hash_next)
307 if (is_expired(now, crecp))
308 {
309 *up = crecp->hash_next;
310 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
311 {
312 cache_unlink(crecp);
313 cache_free(crecp);
314 }
315 }
316 else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
317 (flags & crecp->flags & F_REVERSE) &&
318 (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
319 memcmp(&crecp->addr.addr, addr, addrlen) == 0)
320 {
321 *up = crecp->hash_next;
322 cache_unlink(crecp);
323 cache_free(crecp);
324 }
325 else
326 up = &crecp->hash_next;
327 }
328
329 return 1;
330 }
331
332 /* Note: The normal calling sequence is
333 cache_start_insert
334 cache_insert * n
335 cache_end_insert
336
337 but an abort can cause the cache_end_insert to be missed
338 in which can the next cache_start_insert cleans things up. */
339
340 void cache_start_insert(void)
341 {
342 /* Free any entries which didn't get committed during the last
343 insert due to error.
344 */
345 while (new_chain)
346 {
347 struct crec *tmp = new_chain->next;
348 cache_free(new_chain);
349 new_chain = tmp;
350 }
351 new_chain = NULL;
352 insert_error = 0;
353 }
354
355 struct crec *cache_insert(char *name, struct all_addr *addr,
356 time_t now, unsigned long ttl, unsigned short flags)
357 {
358 struct crec *new;
359 union bigname *big_name = NULL;
360 int freed_all = flags & F_REVERSE;
361 int free_avail = 0;
362
363 log_query(flags | F_UPSTREAM, name, addr, NULL);
364
365 /* if previous insertion failed give up now. */
366 if (insert_error)
367 return NULL;
368
369 /* First remove any expired entries and entries for the name/address we
370 are currently inserting. Fail is we attempt to delete a name from
371 /etc/hosts or DHCP. */
372 if (!cache_scan_free(name, addr, now, flags))
373 {
374 insert_error = 1;
375 return NULL;
376 }
377
378 /* Now get a cache entry from the end of the LRU list */
379 while (1) {
380 if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
381 {
382 insert_error = 1;
383 return NULL;
384 }
385
386 /* End of LRU list is still in use: if we didn't scan all the hash
387 chains for expired entries do that now. If we already tried that
388 then it's time to start spilling things. */
389
390 if (new->flags & (F_FORWARD | F_REVERSE))
391 {
392 /* If free_avail set, we believe that an entry has been freed.
393 Bugs have been known to make this not true, resulting in
394 a tight loop here. If that happens, abandon the
395 insert. Once in this state, all inserts will probably fail. */
396 if (free_avail)
397 {
398 insert_error = 1;
399 return NULL;
400 }
401
402 if (freed_all)
403 {
404 free_avail = 1; /* Must be free space now. */
405 cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
406 cache_live_freed++;
407 }
408 else
409 {
410 cache_scan_free(NULL, NULL, now, 0);
411 freed_all = 1;
412 }
413 continue;
414 }
415
416 /* Check if we need to and can allocate extra memory for a long name.
417 If that fails, give up now. */
418 if (name && (strlen(name) > SMALLDNAME-1))
419 {
420 if (big_free)
421 {
422 big_name = big_free;
423 big_free = big_free->next;
424 }
425 else if (!bignames_left ||
426 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
427 {
428 insert_error = 1;
429 return NULL;
430 }
431 else
432 bignames_left--;
433
434 }
435
436 /* Got the rest: finally grab entry. */
437 cache_unlink(new);
438 break;
439 }
440
441 new->flags = flags;
442 if (big_name)
443 {
444 new->name.bname = big_name;
445 new->flags |= F_BIGNAME;
446 }
447
448 if (name)
449 strcpy(cache_get_name(new), name);
450 else
451 *cache_get_name(new) = 0;
452
453 if (addr)
454 new->addr.addr = *addr;
455 else
456 new->addr.cname.cache = NULL;
457
458 new->ttd = now + (time_t)ttl;
459 new->next = new_chain;
460 new_chain = new;
461
462 return new;
463 }
464
465 /* after end of insertion, commit the new entries */
466 void cache_end_insert(void)
467 {
468 if (insert_error)
469 return;
470
471 while (new_chain)
472 {
473 struct crec *tmp = new_chain->next;
474 /* drop CNAMEs which didn't find a target. */
475 if (is_outdated_cname_pointer(new_chain))
476 cache_free(new_chain);
477 else
478 {
479 cache_hash(new_chain);
480 cache_link(new_chain);
481 cache_inserted++;
482 }
483 new_chain = tmp;
484 }
485 new_chain = NULL;
486 }
487
488 struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
489 {
490 struct crec *ans;
491
492 if (crecp) /* iterating */
493 ans = crecp->next;
494 else
495 {
496 /* first search, look for relevant entries and push to top of list
497 also free anything which has expired */
498 struct crec *next, **up, **insert = NULL, **chainp = &ans;
499 unsigned short ins_flags = 0;
500
501 for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
502 {
503 next = crecp->hash_next;
504
505 if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
506 {
507 if ((crecp->flags & F_FORWARD) &&
508 (crecp->flags & prot) &&
509 hostname_isequal(cache_get_name(crecp), name))
510 {
511 if (crecp->flags & (F_HOSTS | F_DHCP))
512 {
513 *chainp = crecp;
514 chainp = &crecp->next;
515 }
516 else
517 {
518 cache_unlink(crecp);
519 cache_link(crecp);
520 }
521
522 /* Move all but the first entry up the hash chain
523 this implements round-robin.
524 Make sure that re-ordering doesn't break the hash-chain
525 order invariants.
526 */
527 if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
528 {
529 *up = crecp->hash_next;
530 crecp->hash_next = *insert;
531 *insert = crecp;
532 insert = &crecp->hash_next;
533 }
534 else
535 {
536 if (!insert)
537 {
538 insert = up;
539 ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
540 }
541 up = &crecp->hash_next;
542 }
543 }
544 else
545 /* case : not expired, incorrect entry. */
546 up = &crecp->hash_next;
547 }
548 else
549 {
550 /* expired entry, free it */
551 *up = crecp->hash_next;
552 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
553 {
554 cache_unlink(crecp);
555 cache_free(crecp);
556 }
557 }
558 }
559
560 *chainp = cache_head;
561 }
562
563 if (ans &&
564 (ans->flags & F_FORWARD) &&
565 (ans->flags & prot) &&
566 hostname_isequal(cache_get_name(ans), name))
567 return ans;
568
569 return NULL;
570 }
571
572 struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
573 time_t now, unsigned short prot)
574 {
575 struct crec *ans;
576 #ifdef HAVE_IPV6
577 int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
578 #else
579 int addrlen = INADDRSZ;
580 #endif
581
582 if (crecp) /* iterating */
583 ans = crecp->next;
584 else
585 {
586 /* first search, look for relevant entries and push to top of list
587 also free anything which has expired. All the reverse entries are at the
588 start of the hash chain, so we can give up when we find the first
589 non-REVERSE one. */
590 int i;
591 struct crec **up, **chainp = &ans;
592
593 for (i=0; i<hash_size; i++)
594 for (crecp = hash_table[i], up = &hash_table[i];
595 crecp && (crecp->flags & F_REVERSE);
596 crecp = crecp->hash_next)
597 if (!is_expired(now, crecp))
598 {
599 if ((crecp->flags & prot) &&
600 memcmp(&crecp->addr.addr, addr, addrlen) == 0)
601 {
602 if (crecp->flags & (F_HOSTS | F_DHCP))
603 {
604 *chainp = crecp;
605 chainp = &crecp->next;
606 }
607 else
608 {
609 cache_unlink(crecp);
610 cache_link(crecp);
611 }
612 }
613 up = &crecp->hash_next;
614 }
615 else
616 {
617 *up = crecp->hash_next;
618 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
619 {
620 cache_unlink(crecp);
621 cache_free(crecp);
622 }
623 }
624
625 *chainp = cache_head;
626 }
627
628 if (ans &&
629 (ans->flags & F_REVERSE) &&
630 (ans->flags & prot) &&
631 memcmp(&ans->addr.addr, addr, addrlen) == 0)
632 return ans;
633
634 return NULL;
635 }
636
637 static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
638 unsigned short flags, int index, struct crec **rhash)
639 {
640 struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
641 int i, nameexists = 0;
642 struct cname *a;
643 unsigned int j;
644
645 /* Remove duplicates in hosts files. */
646 if (lookup && (lookup->flags & F_HOSTS))
647 {
648 nameexists = 1;
649 if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)
650 {
651 free(cache);
652 return;
653 }
654 }
655
656 /* Ensure there is only one address -> name mapping (first one trumps)
657 We do this by steam here, The entries are kept in hash chains, linked
658 by ->next (which is unused at this point) held in hash buckets in
659 the array rhash. Note that rhash and the values in ->next are only valid
660 whilst reading hosts files: the buckets are then freed, and the
661 ->next pointer used for other things.
662
663 We search and bail at the first matching address that came from
664 a HOSTS file. Since the first host entry gets reverse, we know
665 then that it must exist without searching exhaustively for it.
666
667 This complexity avoids O(n^2) divergent CPU use whilst reading
668 large (10000 entry) hosts files. */
669
670 /* hash address */
671 for (j = 0, i = 0; i < addrlen; i++)
672 j += ((unsigned char *)addr)[i] + (j << 6) + (j << 16) - j;
673
674 for (lookup = rhash[j % RHASHSIZE]; lookup; lookup = lookup->next)
675 if ((lookup->flags & F_HOSTS) &&
676 (lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
677 memcmp(&lookup->addr.addr, addr, addrlen) == 0)
678 {
679 flags &= ~F_REVERSE;
680 break;
681 }
682
683 cache->flags = flags;
684 cache->uid = index;
685
686 /* maintain address has chain */
687 cache->next = rhash[j % RHASHSIZE];
688 rhash[j % RHASHSIZE] = cache;
689
690 memcpy(&cache->addr.addr, addr, addrlen);
691
692 cache_hash(cache);
693
694 /* don't need to do alias stuff for second and subsequent addresses. */
695 if (!nameexists)
696 for (a = daemon->cnames; a; a = a->next)
697 if (hostname_isequal(cache->name.sname, a->target) &&
698 (lookup = whine_malloc(sizeof(struct crec))))
699 {
700 lookup->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
701 lookup->name.namep = a->alias;
702 lookup->addr.cname.cache = cache;
703 lookup->addr.cname.uid = index;
704 cache_hash(lookup);
705 }
706 }
707
708 static int eatspace(FILE *f)
709 {
710 int c, nl = 0;
711
712 while (1)
713 {
714 if ((c = getc(f)) == '#')
715 while (c != '\n' && c != EOF)
716 c = getc(f);
717
718 if (c == EOF)
719 return 1;
720
721 if (!isspace(c))
722 {
723 ungetc(c, f);
724 return nl;
725 }
726
727 if (c == '\n')
728 nl = 1;
729 }
730 }
731
732 static int gettok(FILE *f, char *token)
733 {
734 int c, count = 0;
735
736 while (1)
737 {
738 if ((c = getc(f)) == EOF)
739 return (count == 0) ? EOF : 1;
740
741 if (isspace(c) || c == '#')
742 {
743 ungetc(c, f);
744 return eatspace(f);
745 }
746
747 if (count < (MAXDNAME - 1))
748 {
749 token[count++] = c;
750 token[count] = 0;
751 }
752 }
753 }
754
755 static int read_hostsfile(char *filename, int index, int cache_size, struct crec **rhash)
756 {
757 FILE *f = fopen(filename, "r");
758 char *token = daemon->namebuff, *domain_suffix = NULL;
759 int addr_count = 0, name_count = cache_size, lineno = 0;
760 unsigned short flags = 0;
761 struct all_addr addr;
762 int atnl, addrlen = 0;
763
764 if (!f)
765 {
766 my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
767 return 0;
768 }
769
770 eatspace(f);
771
772 while ((atnl = gettok(f, token)) != EOF)
773 {
774 lineno++;
775
776 #ifdef HAVE_IPV6
777 if (inet_pton(AF_INET, token, &addr) > 0)
778 {
779 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
780 addrlen = INADDRSZ;
781 domain_suffix = get_domain(addr.addr.addr4);
782 }
783 else if (inet_pton(AF_INET6, token, &addr) > 0)
784 {
785 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
786 addrlen = IN6ADDRSZ;
787 domain_suffix = daemon->domain_suffix;
788 }
789 #else
790 if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
791 {
792 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
793 addrlen = INADDRSZ;
794 domain_suffix = get_domain(addr.addr.addr4);
795 }
796 #endif
797 else
798 {
799 my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
800 while (atnl == 0)
801 atnl = gettok(f, token);
802 continue;
803 }
804
805 addr_count++;
806
807 /* rehash every 1000 names. */
808 if ((name_count - cache_size) > 1000)
809 {
810 rehash(name_count);
811 cache_size = name_count;
812 }
813
814 while (atnl == 0)
815 {
816 struct crec *cache;
817 int fqdn, nomem;
818 char *canon;
819
820 if ((atnl = gettok(f, token)) == EOF)
821 break;
822
823 fqdn = !!strchr(token, '.');
824
825 if ((canon = canonicalise(token, &nomem)))
826 {
827 /* If set, add a version of the name with a default domain appended */
828 if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
829 (cache = whine_malloc(sizeof(struct crec) +
830 strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
831 {
832 strcpy(cache->name.sname, canon);
833 strcat(cache->name.sname, ".");
834 strcat(cache->name.sname, domain_suffix);
835 add_hosts_entry(cache, &addr, addrlen, flags, index, rhash);
836 name_count++;
837 }
838 if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
839 {
840 strcpy(cache->name.sname, canon);
841 add_hosts_entry(cache, &addr, addrlen, flags, index, rhash);
842 name_count++;
843 }
844 free(canon);
845
846 }
847 else if (!nomem)
848 my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
849 }
850 }
851
852 fclose(f);
853 rehash(name_count);
854
855 my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
856
857 return name_count;
858 }
859
860 void cache_reload(void)
861 {
862 struct crec *cache, **up, *tmp;
863 int i, total_size = daemon->cachesize;
864 struct hostsfile *ah;
865 struct crec **reverse_hash;
866
867 cache_inserted = cache_live_freed = 0;
868
869 for (i=0; i<hash_size; i++)
870 for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
871 {
872 tmp = cache->hash_next;
873 if (cache->flags & F_HOSTS)
874 {
875 *up = cache->hash_next;
876 free(cache);
877 }
878 else if (!(cache->flags & F_DHCP))
879 {
880 *up = cache->hash_next;
881 if (cache->flags & F_BIGNAME)
882 {
883 cache->name.bname->next = big_free;
884 big_free = cache->name.bname;
885 }
886 cache->flags = 0;
887 }
888 else
889 up = &cache->hash_next;
890 }
891
892 if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
893 {
894 if (daemon->cachesize > 0)
895 my_syslog(LOG_INFO, _("cleared cache"));
896 return;
897 }
898
899 if (!(reverse_hash = whine_malloc(sizeof(struct crec *) * RHASHSIZE)))
900 return;
901
902 for (i = 0; i < RHASHSIZE; i++)
903 reverse_hash[i] = NULL;
904
905 if (!option_bool(OPT_NO_HOSTS))
906 total_size = read_hostsfile(HOSTSFILE, 0, total_size, reverse_hash);
907
908 daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
909 for (ah = daemon->addn_hosts; ah; ah = ah->next)
910 if (!(ah->flags & AH_INACTIVE))
911 total_size = read_hostsfile(ah->fname, ah->index, total_size, reverse_hash);
912
913 free(reverse_hash);
914 }
915
916 char *get_domain(struct in_addr addr)
917 {
918 struct cond_domain *c;
919
920 for (c = daemon->cond_domain; c; c = c->next)
921 if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
922 ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
923 return c->domain;
924
925 return daemon->domain_suffix;
926 }
927
928 #ifdef HAVE_DHCP
929 struct in_addr a_record_from_hosts(char *name, time_t now)
930 {
931 struct crec *crecp = NULL;
932 struct in_addr ret;
933
934 while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
935 if (crecp->flags & F_HOSTS)
936 return *(struct in_addr *)&crecp->addr;
937
938 my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
939
940 ret.s_addr = 0;
941 return ret;
942 }
943
944 void cache_unhash_dhcp(void)
945 {
946 struct crec *cache, **up;
947 int i;
948
949 for (i=0; i<hash_size; i++)
950 for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
951 if (cache->flags & F_DHCP)
952 {
953 *up = cache->hash_next;
954 cache->next = dhcp_spare;
955 dhcp_spare = cache;
956 }
957 else
958 up = &cache->hash_next;
959 }
960
961 void cache_add_dhcp_entry(char *host_name,
962 struct in_addr *host_address, time_t ttd)
963 {
964 struct crec *crec = NULL, *aliasc;
965 unsigned short flags = F_NAMEP | F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
966 int in_hosts = 0;
967 struct cname *a;
968
969 while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME)))
970 {
971 /* check all addresses associated with name */
972 if (crec->flags & F_HOSTS)
973 {
974 /* if in hosts, don't need DHCP record */
975 in_hosts = 1;
976
977 if (crec->flags & F_CNAME)
978 my_syslog(MS_DHCP | LOG_WARNING,
979 _("%s is a CNAME, not giving it to the DHCP lease of %s"),
980 host_name, inet_ntoa(*host_address));
981 else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
982 {
983 strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
984 my_syslog(MS_DHCP | LOG_WARNING,
985 _("not giving name %s to the DHCP lease of %s because "
986 "the name exists in %s with address %s"),
987 host_name, inet_ntoa(*host_address),
988 record_source(crec->uid), daemon->namebuff);
989 }
990 }
991 else if (!(crec->flags & F_DHCP))
992 {
993 cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
994 /* scan_free deletes all addresses associated with name */
995 break;
996 }
997 }
998
999 if (in_hosts)
1000 return;
1001
1002 if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
1003 {
1004 if (crec->flags & F_NEG)
1005 cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
1006 else
1007 /* avoid multiple reverse mappings */
1008 flags &= ~F_REVERSE;
1009 }
1010
1011 if ((crec = dhcp_spare))
1012 dhcp_spare = dhcp_spare->next;
1013 else /* need new one */
1014 crec = whine_malloc(sizeof(struct crec));
1015
1016 if (crec) /* malloc may fail */
1017 {
1018 crec->flags = flags;
1019 if (ttd == 0)
1020 crec->flags |= F_IMMORTAL;
1021 else
1022 crec->ttd = ttd;
1023 crec->addr.addr.addr.addr4 = *host_address;
1024 crec->name.namep = host_name;
1025 crec->uid = uid++;
1026 cache_hash(crec);
1027
1028 for (a = daemon->cnames; a; a = a->next)
1029 if (hostname_isequal(host_name, a->target))
1030 {
1031 if ((aliasc = dhcp_spare))
1032 dhcp_spare = dhcp_spare->next;
1033 else /* need new one */
1034 aliasc = whine_malloc(sizeof(struct crec));
1035
1036 if (aliasc)
1037 {
1038 aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
1039 if (ttd == 0)
1040 aliasc->flags |= F_IMMORTAL;
1041 else
1042 aliasc->ttd = ttd;
1043 aliasc->name.namep = a->alias;
1044 aliasc->addr.cname.cache = crec;
1045 aliasc->addr.cname.uid = crec->uid;
1046 cache_hash(aliasc);
1047 }
1048 }
1049 }
1050 }
1051 #endif
1052
1053
1054 void dump_cache(time_t now)
1055 {
1056 struct server *serv, *serv1;
1057
1058 my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
1059 my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
1060 daemon->cachesize, cache_live_freed, cache_inserted);
1061 my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
1062 daemon->queries_forwarded, daemon->local_answer);
1063
1064 /* sum counts from different records for same server */
1065 for (serv = daemon->servers; serv; serv = serv->next)
1066 serv->flags &= ~SERV_COUNTED;
1067
1068 for (serv = daemon->servers; serv; serv = serv->next)
1069 if (!(serv->flags &
1070 (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
1071 {
1072 int port;
1073 unsigned int queries = 0, failed_queries = 0;
1074 for (serv1 = serv; serv1; serv1 = serv1->next)
1075 if (!(serv1->flags &
1076 (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
1077 sockaddr_isequal(&serv->addr, &serv1->addr))
1078 {
1079 serv1->flags |= SERV_COUNTED;
1080 queries += serv1->queries;
1081 failed_queries += serv1->failed_queries;
1082 }
1083 port = prettyprint_addr(&serv->addr, daemon->addrbuff);
1084 my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
1085 }
1086
1087 if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
1088 {
1089 struct crec *cache ;
1090 int i;
1091 my_syslog(LOG_INFO, "Host Address Flags Expires");
1092
1093 for (i=0; i<hash_size; i++)
1094 for (cache = hash_table[i]; cache; cache = cache->hash_next)
1095 {
1096 char *a, *p = daemon->namebuff;
1097 p += sprintf(p, "%-40.40s ", cache_get_name(cache));
1098 if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
1099 a = "";
1100 else if (cache->flags & F_CNAME)
1101 {
1102 a = "";
1103 if (!is_outdated_cname_pointer(cache))
1104 a = cache_get_name(cache->addr.cname.cache);
1105 }
1106 #ifdef HAVE_IPV6
1107 else
1108 {
1109 a = daemon->addrbuff;
1110 if (cache->flags & F_IPV4)
1111 inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
1112 else if (cache->flags & F_IPV6)
1113 inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
1114 }
1115 #else
1116 else
1117 a = inet_ntoa(cache->addr.addr.addr.addr4);
1118 #endif
1119 p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
1120 cache->flags & F_IPV4 ? "4" : "",
1121 cache->flags & F_IPV6 ? "6" : "",
1122 cache->flags & F_CNAME ? "C" : "",
1123 cache->flags & F_FORWARD ? "F" : " ",
1124 cache->flags & F_REVERSE ? "R" : " ",
1125 cache->flags & F_IMMORTAL ? "I" : " ",
1126 cache->flags & F_DHCP ? "D" : " ",
1127 cache->flags & F_NEG ? "N" : " ",
1128 cache->flags & F_NXDOMAIN ? "X" : " ",
1129 cache->flags & F_HOSTS ? "H" : " ");
1130 #ifdef HAVE_BROKEN_RTC
1131 p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
1132 #else
1133 p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
1134 /* ctime includes trailing \n - eat it */
1135 *(p-1) = 0;
1136 #endif
1137 my_syslog(LOG_INFO, daemon->namebuff);
1138 }
1139 }
1140 }
1141
1142 char *record_source(int index)
1143 {
1144 struct hostsfile *ah;
1145
1146 if (index == 0)
1147 return HOSTSFILE;
1148
1149 for (ah = daemon->addn_hosts; ah; ah = ah->next)
1150 if (ah->index == index)
1151 return ah->fname;
1152
1153 return "<unknown>";
1154 }
1155
1156 void querystr(char *str, unsigned short type)
1157 {
1158 unsigned int i;
1159
1160 sprintf(str, "query[type=%d]", type);
1161 for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
1162 if (typestr[i].type == type)
1163 sprintf(str,"query[%s]", typestr[i].name);
1164 }
1165
1166 void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
1167 {
1168 char *source, *dest = daemon->addrbuff;
1169 char *verb = "is";
1170
1171 if (!option_bool(OPT_LOG))
1172 return;
1173
1174 if (addr)
1175 {
1176 #ifdef HAVE_IPV6
1177 inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
1178 addr, daemon->addrbuff, ADDRSTRLEN);
1179 #else
1180 strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
1181 #endif
1182 }
1183
1184 if (flags & F_REVERSE)
1185 {
1186 dest = name;
1187 name = daemon->addrbuff;
1188 }
1189
1190 if (flags & F_NEG)
1191 {
1192 if (flags & F_NXDOMAIN)
1193 {
1194 if (flags & F_IPV4)
1195 dest = "NXDOMAIN-IPv4";
1196 else if (flags & F_IPV6)
1197 dest = "NXDOMAIN-IPv6";
1198 else
1199 dest = "NXDOMAIN";
1200 }
1201 else
1202 {
1203 if (flags & F_IPV4)
1204 dest = "NODATA-IPv4";
1205 else if (flags & F_IPV6)
1206 dest = "NODATA-IPv6";
1207 else
1208 dest = "NODATA";
1209 }
1210 }
1211 else if (flags & F_CNAME)
1212 dest = "<CNAME>";
1213 else if (flags & F_RRNAME)
1214 dest = arg;
1215
1216 if (flags & F_CONFIG)
1217 source = "config";
1218 else if (flags & F_DHCP)
1219 source = "DHCP";
1220 else if (flags & F_HOSTS)
1221 source = arg;
1222 else if (flags & F_UPSTREAM)
1223 source = "reply";
1224 else if (flags & F_SERVER)
1225 {
1226 source = "forwarded";
1227 verb = "to";
1228 }
1229 else if (flags & F_QUERY)
1230 {
1231 source = arg;
1232 verb = "from";
1233 }
1234 else
1235 source = "cached";
1236
1237 if (strlen(name) == 0)
1238 name = ".";
1239
1240 my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
1241 }
1242