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