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