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