blob: a49016a8b68262b7b4f69fc8bf5a81c11588e7b4 [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* dnsmasq is Copyright (c) 2000 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.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13#include "dnsmasq.h"
14
15static struct crec *cache_head, *cache_tail, **hash_table;
16static struct crec *dhcp_inuse, *dhcp_spare, *new_chain;
17static int cache_inserted, cache_live_freed, insert_error;
18static union bigname *big_free;
19static int bignames_left, log_queries, cache_size, hash_size;
Simon Kelley26128d22004-11-14 16:43:54 +000020static int uid;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000021
22static void cache_free(struct crec *crecp);
23static void cache_unlink(struct crec *crecp);
24static void cache_link(struct crec *crecp);
Simon Kelleyfd9fa482004-10-21 20:24:00 +010025static char *record_source(struct hostsfile *add_hosts, int index);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000026
27void cache_init(int size, int logq)
28{
29 struct crec *crecp;
30 int i;
31
32 log_queries = logq;
33 cache_head = cache_tail = NULL;
34 dhcp_inuse = dhcp_spare = NULL;
35 new_chain = NULL;
36 cache_size = size;
37 big_free = NULL;
38 bignames_left = size/10;
Simon Kelley26128d22004-11-14 16:43:54 +000039 uid = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000040
41 cache_inserted = cache_live_freed = 0;
42
43 if (cache_size > 0)
44 {
45 crecp = safe_malloc(size*sizeof(struct crec));
46
47 for (i=0; i<size; i++, crecp++)
48 {
49 cache_link(crecp);
50 crecp->flags = 0;
Simon Kelley26128d22004-11-14 16:43:54 +000051 crecp->uid = uid++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000052 }
53 }
54
55 /* hash_size is a power of two. */
56 for (hash_size = 64; hash_size < cache_size/10; hash_size = hash_size << 1);
57 hash_table = safe_malloc(hash_size*sizeof(struct crec *));
58 for(i=0; i < hash_size; i++)
59 hash_table[i] = NULL;
60}
61
62static struct crec **hash_bucket(unsigned char *name)
63{
64 unsigned int c, val = 0;
65
66 /* don't use tolower and friends here - they may be messed up by LOCALE */
67 while((c = *name++))
68 if (c >= 'A' && c <= 'Z')
69 val += c + 'a' - 'A';
70 else
71 val += c;
72
73 /* hash_size is a power of two */
74 return hash_table + (val & (hash_size - 1));
75}
76
77static void cache_hash(struct crec *crecp)
78{
79 struct crec **bucket = hash_bucket(cache_get_name(crecp));
80 crecp->hash_next = *bucket;
81 *bucket = crecp;
82}
83
84static void cache_free(struct crec *crecp)
85{
86 crecp->flags &= ~F_FORWARD;
87 crecp->flags &= ~F_REVERSE;
Simon Kelley26128d22004-11-14 16:43:54 +000088 crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
Simon Kelleyfd9fa482004-10-21 20:24:00 +010089
Simon Kelley9e4abcb2004-01-22 19:47:41 +000090 if (cache_tail)
91 cache_tail->next = crecp;
92 else
93 cache_head = crecp;
94 crecp->prev = cache_tail;
95 crecp->next = NULL;
96 cache_tail = crecp;
97
98 /* retrieve big name for further use. */
99 if (crecp->flags & F_BIGNAME)
100 {
101 crecp->name.bname->next = big_free;
102 big_free = crecp->name.bname;
103 crecp->flags &= ~F_BIGNAME;
104 }
105}
106
107/* insert a new cache entry at the head of the list (youngest entry) */
108static void cache_link(struct crec *crecp)
109{
110 if (cache_head) /* check needed for init code */
111 cache_head->prev = crecp;
112 crecp->next = cache_head;
113 crecp->prev = NULL;
114 cache_head = crecp;
115 if (!cache_tail)
116 cache_tail = crecp;
117}
118
119/* remove an arbitrary cache entry for promotion */
120static void cache_unlink (struct crec *crecp)
121{
122 if (crecp->prev)
123 crecp->prev->next = crecp->next;
124 else
125 cache_head = crecp->next;
126
127 if (crecp->next)
128 crecp->next->prev = crecp->prev;
129 else
130 cache_tail = crecp->prev;
131}
132
133char *cache_get_name(struct crec *crecp)
134{
135 if (crecp->flags & F_BIGNAME)
136 return crecp->name.bname->name;
137 else if (crecp->flags & F_DHCP)
138 return crecp->name.namep;
139
140 return crecp->name.sname;
141}
142
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100143static int is_outdated_cname_pointer(struct crec *crecp)
144{
145 struct crec *target = crecp->addr.cname.cache;
146
147 if (!(crecp->flags & F_CNAME))
148 return 0;
149
150 if (!target)
151 return 1;
152
153 if (crecp->addr.cname.uid == target->uid)
154 return 0;
155
156 return 1;
157}
158
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000159static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
160{
161 /* Scan and remove old entries.
162 If (flags & F_FORWARD) then remove any forward entries for name and any expired
163 entries but only in the same hash bucket as name.
164 If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
165 entries in the whole cache.
166 If (flags == 0) remove any expired entries in the whole cache. */
167
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100168#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6 | F_CNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000169 struct crec *crecp, **up;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100170 flags &= (F_FORWARD | F_REVERSE | F_IPV6 | F_IPV4 | F_CNAME);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000171
172 if (flags & F_FORWARD)
173 {
174 for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
175 if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100176 is_outdated_cname_pointer(crecp) ||
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000177 ((flags == (crecp->flags & F_CACHESTATUS)) && hostname_isequal(cache_get_name(crecp), name)))
178 {
179 *up = crecp->hash_next;
180 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
181 {
182 cache_unlink(crecp);
183 cache_free(crecp);
184 }
185 }
186 else
187 up = &crecp->hash_next;
188 }
189 else
190 {
191 int i;
192#ifdef HAVE_IPV6
193 int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
194#else
195 int addrlen = INADDRSZ;
196#endif
197 for (i = 0; i < hash_size; i++)
198 for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
199 if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100200 ((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr.addr, addr, addrlen) == 0))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000201 {
202 *up = crecp->hash_next;
203 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
204 {
205 cache_unlink(crecp);
206 cache_free(crecp);
207 }
208 }
209 else
210 up = &crecp->hash_next;
211 }
212}
213
214/* Note: The normal calling sequence is
215 cache_start_insert
216 cache_insert * n
217 cache_end_insert
218
219 but an abort can cause the cache_end_insert to be missed
220 in which can the next cache_start_insert cleans things up. */
221
222void cache_start_insert(void)
223{
224 /* Free any entries which didn't get committed during the last
225 insert due to error.
226 */
227 while (new_chain)
228 {
229 struct crec *tmp = new_chain->next;
230 cache_free(new_chain);
231 new_chain = tmp;
232 }
233 new_chain = NULL;
234 insert_error = 0;
235}
236
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100237struct crec *cache_insert(char *name, struct all_addr *addr,
238 time_t now, unsigned long ttl, unsigned short flags)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000239{
240#ifdef HAVE_IPV6
241 int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
242#else
243 int addrlen = INADDRSZ;
244#endif
245 struct crec *new;
246 union bigname *big_name = NULL;
247 int freed_all = flags & F_REVERSE;
248
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100249 log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000250
251 /* name is needed as workspace by log_query in this case */
252 if ((flags & F_NEG) && (flags & F_REVERSE))
253 name = NULL;
254
255 /* CONFIG bit no needed except for logging */
256 flags &= ~F_CONFIG;
257
258 /* if previous insertion failed give up now. */
259 if (insert_error)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100260 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000261
262 /* First remove any expired entries and entries for the name/address we
263 are currently inserting. */
264 cache_scan_free(name, addr, now, flags);
265
266 /* Now get a cache entry from the end of the LRU list */
267 while (1) {
268 if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
269 {
270 insert_error = 1;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100271 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000272 }
273
274 /* End of LRU list is still in use: if we didn't scan all the hash
275 chains for expired entries do that now. If we already tried that
276 then it's time to start spilling things. */
277
278 if (new->flags & (F_FORWARD | F_REVERSE))
279 {
280 if (freed_all)
281 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100282 cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000283 cache_live_freed++;
284 }
285 else
286 {
287 cache_scan_free(NULL, NULL, now, 0);
288 freed_all = 1;
289 }
290 continue;
291 }
292
293 /* Check if we need to and can allocate extra memory for a long name.
294 If that fails, give up now. */
295 if (name && (strlen(name) > SMALLDNAME-1))
296 {
297 if (big_free)
298 {
299 big_name = big_free;
300 big_free = big_free->next;
301 }
302 else if (!bignames_left ||
303 !(big_name = (union bigname *)malloc(sizeof(union bigname))))
304 {
305 insert_error = 1;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100306 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000307 }
308 else
309 bignames_left--;
310
311 }
312
313 /* Got the rest: finally grab entry. */
314 cache_unlink(new);
315 break;
316 }
317
318 new->flags = flags;
319 if (big_name)
320 {
321 new->name.bname = big_name;
322 new->flags |= F_BIGNAME;
323 }
324 if (name)
325 strcpy(cache_get_name(new), name);
326 else
327 *cache_get_name(new) = 0;
328 if (addr)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100329 memcpy(&new->addr.addr, addr, addrlen);
330 else
331 new->addr.cname.cache = NULL;
332
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000333 new->ttd = now + (time_t)ttl;
334 new->next = new_chain;
335 new_chain = new;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100336
337 return new;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000338}
339
340/* after end of insertion, commit the new entries */
341void cache_end_insert(void)
342{
343 if (insert_error)
344 return;
345
346 while (new_chain)
347 {
348 struct crec *tmp = new_chain->next;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100349 /* drop CNAMEs which didn't find a target. */
350 if (is_outdated_cname_pointer(new_chain))
351 cache_free(new_chain);
352 else
353 {
354 cache_hash(new_chain);
355 cache_link(new_chain);
356 cache_inserted++;
357 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000358 new_chain = tmp;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000359 }
360 new_chain = NULL;
361}
362
363struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
364{
365 struct crec *ans;
366
367 if (crecp) /* iterating */
368 ans = crecp->next;
369 else
370 {
371 /* first search, look for relevant entries and push to top of list
372 also free anything which has expired */
373 struct crec *next, **up, **insert = NULL, **chainp = &ans;
374
375 for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
376 {
377 next = crecp->hash_next;
378
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100379 if (!is_outdated_cname_pointer(crecp) &&
380 ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000381 {
382 if ((crecp->flags & F_FORWARD) &&
383 (crecp->flags & prot) &&
384 hostname_isequal(cache_get_name(crecp), name))
385 {
386 if (crecp->flags & (F_HOSTS | F_DHCP))
387 {
388 *chainp = crecp;
389 chainp = &crecp->next;
390 }
391 else
392 {
393 cache_unlink(crecp);
394 cache_link(crecp);
395 }
396
397 /* move all but the first entry up the hash chain
398 this implements round-robin */
399 if (!insert)
400 {
401 insert = up;
402 up = &crecp->hash_next;
403 }
404 else
405 {
406 *up = crecp->hash_next;
407 crecp->hash_next = *insert;
408 *insert = crecp;
409 insert = &crecp->hash_next;
410 }
411 }
412 else
413 /* case : not expired, incorrect entry. */
414 up = &crecp->hash_next;
415 }
416 else
417 {
418 /* expired entry, free it */
419 *up = crecp->hash_next;
420 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
421 {
422 cache_unlink(crecp);
423 cache_free(crecp);
424 }
425 }
426 }
427
428 *chainp = cache_head;
429 }
430
431 if (ans &&
432 (ans->flags & F_FORWARD) &&
433 (ans->flags & prot) &&
434 hostname_isequal(cache_get_name(ans), name))
435 return ans;
436
437 return NULL;
438}
439
440struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
441 time_t now, unsigned short prot)
442{
443 struct crec *ans;
444#ifdef HAVE_IPV6
445 int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
446#else
447 int addrlen = INADDRSZ;
448#endif
449
450 if (crecp) /* iterating */
451 ans = crecp->next;
452 else
453 {
454 /* first search, look for relevant entries and push to top of list
455 also free anything which has expired */
456 int i;
457 struct crec **up, **chainp = &ans;
458
459 for(i=0; i<hash_size; i++)
460 for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
461 if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
462 {
463 if ((crecp->flags & F_REVERSE) &&
464 (crecp->flags & prot) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100465 memcmp(&crecp->addr.addr, addr, addrlen) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000466 {
467 if (crecp->flags & (F_HOSTS | F_DHCP))
468 {
469 *chainp = crecp;
470 chainp = &crecp->next;
471 }
472 else
473 {
474 cache_unlink(crecp);
475 cache_link(crecp);
476 }
477 }
478 up = &crecp->hash_next;
479 }
480 else
481 {
482 *up = crecp->hash_next;
483 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
484 {
485 cache_unlink(crecp);
486 cache_free(crecp);
487 }
488 }
489
490 *chainp = cache_head;
491 }
492
493 if (ans &&
494 (ans->flags & F_REVERSE) &&
495 (ans->flags & prot) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100496 memcmp(&ans->addr.addr, addr, addrlen) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000497 return ans;
498
499 return NULL;
500}
501
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100502static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
503 unsigned short flags, int index)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000504{
505 struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
506
507 /* Remove duplicates in hosts files. */
Simon Kelleyde379512004-06-22 20:23:33 +0100508 if (lookup && (lookup->flags & F_HOSTS) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100509 memcmp(&lookup->addr.addr, addr, addrlen) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000510 free(cache);
511 else
512 {
Simon Kelleyde379512004-06-22 20:23:33 +0100513 /* Ensure there is only one address -> name mapping (first one trumps) */
514 if (cache_find_by_addr(NULL, addr, 0, flags & (F_IPV4 | F_IPV6)))
515 flags &= ~F_REVERSE;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000516 cache->flags = flags;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100517 cache->uid = index;
518 memcpy(&cache->addr.addr, addr, addrlen);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000519 cache_hash(cache);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000520 }
521}
522
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100523static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int index)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000524{
525 FILE *f = fopen(filename, "r");
526 char *line;
527 int count = 0, lineno = 0;
528
529 if (!f)
530 {
531 syslog(LOG_ERR, "failed to load names from %s: %m", filename);
532 return;
533 }
534
535 while ((line = fgets(buff, MAXDNAME, f)))
536 {
537 struct all_addr addr;
538 char *token = strtok(line, " \t\n\r");
539 int addrlen;
540 unsigned short flags;
541
542 lineno++;
543
544 if (!token || (*token == '#'))
545 continue;
546
547#ifdef HAVE_IPV6
548 if (inet_pton(AF_INET, token, &addr) == 1)
549 {
550 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
551 addrlen = INADDRSZ;
552 }
553 else if (inet_pton(AF_INET6, token, &addr) == 1)
554 {
555 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
556 addrlen = IN6ADDRSZ;
557 }
558#else
559 if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
560 {
561 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
562 addrlen = INADDRSZ;
563 }
564#endif
565 else
566 continue;
567
568 while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
569 {
570 struct crec *cache;
571 if (canonicalise(token))
572 {
573 count++;
574 /* If set, add a version of the name with a default domain appended */
575 if ((opts & OPT_EXPAND) && domain_suffix && !strchr(token, '.') &&
576 (cache = malloc(sizeof(struct crec) +
577 strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
578 {
579 strcpy(cache->name.sname, token);
580 strcat(cache->name.sname, ".");
581 strcat(cache->name.sname, domain_suffix);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100582 add_hosts_entry(cache, &addr, addrlen, flags, index);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000583 }
584 if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
585 {
586 strcpy(cache->name.sname, token);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100587 add_hosts_entry(cache, &addr, addrlen, flags, index);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000588 }
589 }
590 else
591 syslog(LOG_ERR, "bad name at %s line %d", filename, lineno);
592 }
593 }
594
595 fclose(f);
596
597 syslog(LOG_INFO, "read %s - %d addresses", filename, count);
598}
599
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100600void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000601{
602 struct crec *cache, **up, *tmp;
603 int i;
604
605 for (i=0; i<hash_size; i++)
606 for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
607 {
608 tmp = cache->hash_next;
609 if (cache->flags & F_HOSTS)
610 {
611 *up = cache->hash_next;
612 free(cache);
613 }
614 else if (!(cache->flags & F_DHCP))
615 {
616 *up = cache->hash_next;
617 if (cache->flags & F_BIGNAME)
618 {
619 cache->name.bname->next = big_free;
620 big_free = cache->name.bname;
621 }
622 cache->flags = 0;
623 }
624 else
625 up = &cache->hash_next;
626 }
627
628 if ((opts & OPT_NO_HOSTS) && !addn_hosts)
629 {
630 if (cache_size > 0)
631 syslog(LOG_INFO, "cleared cache");
632 return;
633 }
634
635 if (!(opts & OPT_NO_HOSTS))
636 read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100637 while (addn_hosts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000638 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100639 read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index);
640 addn_hosts = addn_hosts->next;
641 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000642}
643
644void cache_unhash_dhcp(void)
645{
646 struct crec *tmp, *cache, **up;
647 int i;
648
649 for (i=0; i<hash_size; i++)
650 for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
651 if (cache->flags & F_DHCP)
652 *up = cache->hash_next;
653 else
654 up = &cache->hash_next;
655
656 /* prev field links all dhcp entries */
657 for (cache = dhcp_inuse; cache; cache = tmp)
658 {
659 tmp = cache->prev;
660 cache->prev = dhcp_spare;
661 dhcp_spare = cache;
662 }
663
664 dhcp_inuse = NULL;
665}
666
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100667void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
668 struct in_addr *host_address, time_t ttd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000669{
670 struct crec *crec;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100671 unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
672
Simon Kelley9c74ec02004-08-13 21:13:03 +0100673 if (!host_name)
674 return;
675
Simon Kelley26128d22004-11-14 16:43:54 +0000676 if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4 | F_CNAME)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000677 {
678 if (crec->flags & F_HOSTS)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000679 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100680 if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
681 {
682 strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
683 syslog(LOG_WARNING,
Simon Kelley26128d22004-11-14 16:43:54 +0000684 "not giving name %s to the DHCP lease of %s because "
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100685 "the name exists in %s with address %s",
686 host_name, inet_ntoa(*host_address),
687 record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
688 }
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100689 return;
Simon Kelley1ab84e22004-01-29 16:48:35 +0000690 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000691 else if (!(crec->flags & F_DHCP))
Simon Kelley26128d22004-11-14 16:43:54 +0000692 cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000693 }
694
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100695 if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
696 {
697 if (crec->flags & F_NEG)
698 cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
699 else
700 /* avoid multiple reverse mappings */
701 flags &= ~F_REVERSE;
702 }
703
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000704 if ((crec = dhcp_spare))
705 dhcp_spare = dhcp_spare->prev;
706 else /* need new one */
707 crec = malloc(sizeof(struct crec));
708
709 if (crec) /* malloc may fail */
710 {
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100711 crec->flags = flags;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000712 if (ttd == 0)
713 crec->flags |= F_IMMORTAL;
714 else
715 crec->ttd = ttd;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100716 crec->addr.addr.addr.addr4 = *host_address;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000717 crec->name.namep = host_name;
718 crec->prev = dhcp_inuse;
719 dhcp_inuse = crec;
720 cache_hash(crec);
721 }
722}
723
724
725
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100726void dump_cache(struct daemon *daemon)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000727{
Simon Kelley1cff1662004-03-12 08:12:58 +0000728 syslog(LOG_INFO, "cache size %d, %d/%d cache insertions re-used unexpired cache entries.",
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100729 daemon->cachesize, cache_live_freed, cache_inserted);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000730
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100731 if (daemon->options & (OPT_DEBUG | OPT_LOG))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000732 {
733 struct crec *cache ;
734 char addrbuff[ADDRSTRLEN];
735 int i;
736 syslog(LOG_DEBUG, "Host Address Flags Expires\n");
737
738 for (i=0; i<hash_size; i++)
739 for (cache = hash_table[i]; cache; cache = cache->hash_next)
740 {
741 if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
742 addrbuff[0] = 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100743 else if (cache->flags & F_CNAME)
744 {
745 addrbuff[0] = 0;
746 addrbuff[ADDRSTRLEN-1] = 0;
747 if (!is_outdated_cname_pointer(cache))
748 strncpy(addrbuff, cache_get_name(cache->addr.cname.cache), ADDRSTRLEN);
749 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000750#ifdef HAVE_IPV6
751 else if (cache->flags & F_IPV4)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100752 inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000753 else if (cache->flags & F_IPV6)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100754 inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000755#else
756 else
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100757 strcpy(addrbuff, inet_ntoa(cache->addr.addr.addr.addr4));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000758#endif
Simon Kelley44a2a312004-03-10 20:04:35 +0000759 syslog(LOG_DEBUG,
760#ifdef HAVE_BROKEN_RTC
761 "%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %ld\n",
762#else
763 "%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s",
764#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000765 cache_get_name(cache), addrbuff,
766 cache->flags & F_IPV4 ? "4" : "",
767 cache->flags & F_IPV6 ? "6" : "",
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100768 cache->flags & F_CNAME ? "C" : "",
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000769 cache->flags & F_FORWARD ? "F" : " ",
770 cache->flags & F_REVERSE ? "R" : " ",
771 cache->flags & F_IMMORTAL ? "I" : " ",
772 cache->flags & F_DHCP ? "D" : " ",
773 cache->flags & F_NEG ? "N" : " ",
774 cache->flags & F_NXDOMAIN ? "X" : " ",
775 cache->flags & F_HOSTS ? "H" : " ",
Simon Kelley44a2a312004-03-10 20:04:35 +0000776#ifdef HAVE_BROKEN_RTC
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100777 cache->flags & F_IMMORTAL ? 0: (unsigned long)cache->ttd
Simon Kelley44a2a312004-03-10 20:04:35 +0000778#else
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100779 cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))
Simon Kelley44a2a312004-03-10 20:04:35 +0000780#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100781 );
782 }
783 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000784}
785
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100786static char *record_source(struct hostsfile *addn_hosts, int index)
787{
788 char *source = HOSTSFILE;
789 while (addn_hosts)
790 {
791 if (addn_hosts->index == index)
792 {
793 source = addn_hosts->fname;
794 break;
795 }
796 addn_hosts = addn_hosts->next;
797 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000798
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100799 return source;
800}
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100801
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100802void log_query(unsigned short flags, char *name, struct all_addr *addr,
803 unsigned short type, struct hostsfile *addn_hosts, int index)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000804{
805 char *source;
806 char *verb = "is";
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100807 char types[20];
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000808 char addrbuff[ADDRSTRLEN];
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100809
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000810 if (!log_queries)
811 return;
812
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100813 strcpy(types, " ");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100814
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000815 if (flags & F_NEG)
816 {
817 if (flags & F_REVERSE)
818#ifdef HAVE_IPV6
819 inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
820 addr, name, MAXDNAME);
821#else
822 strcpy(name, inet_ntoa(addr->addr.addr4));
823#endif
824
825 if (flags & F_NXDOMAIN)
Simon Kelley44a2a312004-03-10 20:04:35 +0000826 strcpy(addrbuff, "<NXDOMAIN>");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000827 else
Simon Kelley44a2a312004-03-10 20:04:35 +0000828 strcpy(addrbuff, "<NODATA>");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000829
830 if (flags & F_IPV4)
Simon Kelley44a2a312004-03-10 20:04:35 +0000831 strcat(addrbuff, "-IPv4");
832 else if (flags & F_IPV6)
833 strcat(addrbuff, "-IPv6");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000834 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100835 else if (flags & F_CNAME)
836 strcpy(addrbuff, "<CNAME>");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000837 else
838#ifdef HAVE_IPV6
839 inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
840 addr, addrbuff, ADDRSTRLEN);
841#else
842 strcpy(addrbuff, inet_ntoa(addr->addr.addr4));
843#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100844
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000845 if (flags & F_DHCP)
846 source = "DHCP";
847 else if (flags & F_HOSTS)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100848 source = record_source(addn_hosts, index);
849 else if (flags & F_CONFIG)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000850 source = "config";
851 else if (flags & F_UPSTREAM)
852 source = "reply";
853 else if (flags & F_SERVER)
854 {
855 source = "forwarded";
856 verb = "to";
857 }
858 else if (flags & F_QUERY)
859 {
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100860 unsigned int i;
861 static struct {
862 unsigned int type;
863 char *name;
864 } typestr[] = {
865 { 1, "A" },
866 { 2, "NS" },
867 { 5, "CNAME" },
868 { 6, "SOA" },
869 { 10, "NULL" },
870 { 11, "WKS" },
871 { 12, "PTR" },
872 { 13, "HINFO" },
873 { 15, "MX" },
874 { 16, "TXT" },
875 { 22, "NSAP" },
876 { 23, "NSAP_PTR" },
877 { 24, "SIG" },
878 { 25, "KEY" },
879 { 28, "AAAA" },
880 { 33, "SRV" },
881 { 36, "KX" },
882 { 37, "CERT" },
883 { 38, "A6" },
884 { 39, "DNAME" },
885 { 41, "OPT" },
886 { 250, "TSIG" },
887 { 251, "IXFR" },
888 { 252, "AXFR" },
889 { 253, "MAILB" },
890 { 254, "MAILA" },
891 { 255, "ANY" }
892 };
893
894 if (type != 0)
895 {
896 sprintf(types, "[type=%d] ", type);
897 for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
898 if (typestr[i].type == type)
899 sprintf(types,"[%s] ", typestr[i].name);
900 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000901 source = "query";
902 verb = "from";
903 }
904 else
905 source = "cached";
906
907 if ((flags & F_FORWARD) | (flags & F_NEG))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100908 syslog(LOG_DEBUG, "%s %s%s%s %s", source, name, types, verb, addrbuff);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000909 else if (flags & F_REVERSE)
910 syslog(LOG_DEBUG, "%s %s is %s", source, addrbuff, name);
911}
912