blob: 896132b3f9e6958624759d8100e658a64678f0d1 [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 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.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 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/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
17#include "dnsmasq.h"
18
Simon Kelley5aabfc72007-08-29 11:24:47 +010019static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +010020#ifdef HAVE_DHCP
21static struct crec *dhcp_spare = NULL;
22#endif
23static struct crec *new_chain = NULL;
Simon Kelley5aabfc72007-08-29 11:24:47 +010024static int cache_inserted = 0, cache_live_freed = 0, insert_error;
25static union bigname *big_free = NULL;
26static int bignames_left, hash_size;
27static int uid = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000028
Simon Kelley16972692006-10-16 20:04:18 +010029/* type->string mapping: this is also used by the name-hash function as a mixing table. */
30static 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" },
Simon Kelley1a6bca82008-07-11 11:11:42 +010050 { 35, "NAPTR" },
Simon Kelley16972692006-10-16 20:04:18 +010051 { 36, "KX" },
52 { 37, "CERT" },
53 { 38, "A6" },
54 { 39, "DNAME" },
55 { 41, "OPT" },
Simon Kelley832af0b2007-01-21 20:01:28 +000056 { 48, "DNSKEY" },
57 { 249, "TKEY" },
Simon Kelley16972692006-10-16 20:04:18 +010058 { 250, "TSIG" },
59 { 251, "IXFR" },
60 { 252, "AXFR" },
61 { 253, "MAILB" },
62 { 254, "MAILA" },
63 { 255, "ANY" }
64};
65
Simon Kelley9e4abcb2004-01-22 19:47:41 +000066static void cache_free(struct crec *crecp);
67static void cache_unlink(struct crec *crecp);
68static void cache_link(struct crec *crecp);
Simon Kelley4011c4e2006-10-28 16:26:19 +010069static void rehash(int size);
70static void cache_hash(struct crec *crecp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000071
Simon Kelley5aabfc72007-08-29 11:24:47 +010072void cache_init(void)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000073{
74 struct crec *crecp;
75 int i;
76
Simon Kelley5aabfc72007-08-29 11:24:47 +010077 bignames_left = daemon->cachesize/10;
78
79 if (daemon->cachesize > 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080 {
Simon Kelley5aabfc72007-08-29 11:24:47 +010081 crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
Simon Kelley9e4abcb2004-01-22 19:47:41 +000082
Simon Kelley5aabfc72007-08-29 11:24:47 +010083 for (i=0; i < daemon->cachesize; i++, crecp++)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000084 {
85 cache_link(crecp);
86 crecp->flags = 0;
Simon Kelley26128d22004-11-14 16:43:54 +000087 crecp->uid = uid++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000088 }
89 }
90
Simon Kelley4011c4e2006-10-28 16:26:19 +010091 /* create initial hash table*/
Simon Kelley5aabfc72007-08-29 11:24:47 +010092 rehash(daemon->cachesize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000093}
94
Simon Kelley4011c4e2006-10-28 16:26:19 +010095/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
96 but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
97 will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
98 expand the table. */
99static void rehash(int size)
100{
101 struct crec **new, **old, *p, *tmp;
102 int i, new_size, old_size;
103
104 /* hash_size is a power of two. */
105 for (new_size = 64; new_size < size/10; new_size = new_size << 1);
106
107 /* must succeed in getting first instance, failure later is non-fatal */
108 if (!hash_table)
109 new = safe_malloc(new_size * sizeof(struct crec *));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100110 else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
Simon Kelley4011c4e2006-10-28 16:26:19 +0100111 return;
112
113 for(i = 0; i < new_size; i++)
114 new[i] = NULL;
115
116 old = hash_table;
117 old_size = hash_size;
118 hash_table = new;
119 hash_size = new_size;
120
121 if (old)
122 {
123 for (i = 0; i < old_size; i++)
124 for (p = old[i]; p ; p = tmp)
125 {
126 tmp = p->hash_next;
127 cache_hash(p);
128 }
129 free(old);
130 }
131}
132
Simon Kelley3d8df262005-08-29 12:19:27 +0100133static struct crec **hash_bucket(char *name)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000134{
Simon Kelley4011c4e2006-10-28 16:26:19 +0100135 unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
Simon Kelley16972692006-10-16 20:04:18 +0100136 const unsigned char *mix_tab = (const unsigned char*)typestr;
137
Simon Kelley3d8df262005-08-29 12:19:27 +0100138 while((c = (unsigned char) *name++))
Simon Kelley16972692006-10-16 20:04:18 +0100139 {
140 /* don't use tolower and friends here - they may be messed up by LOCALE */
141 if (c >= 'A' && c <= 'Z')
142 c += 'a' - 'A';
Simon Kelley4011c4e2006-10-28 16:26:19 +0100143 val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c);
Simon Kelley16972692006-10-16 20:04:18 +0100144 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000145
146 /* hash_size is a power of two */
Simon Kelley16972692006-10-16 20:04:18 +0100147 return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000148}
149
150static void cache_hash(struct crec *crecp)
151{
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000152 /* maintain an invariant that all entries with F_REVERSE set
153 are at the start of the hash-chain and all non-reverse
154 immortal entries are at the end of the hash-chain.
155 This allows reverse searches and garbage collection to be optimised */
156
157 struct crec **up = hash_bucket(cache_get_name(crecp));
158
159 if (!(crecp->flags & F_REVERSE))
160 {
161 while (*up && ((*up)->flags & F_REVERSE))
162 up = &((*up)->hash_next);
163
164 if (crecp->flags & F_IMMORTAL)
Simon Kelley6b010842007-02-12 20:32:07 +0000165 while (*up && !((*up)->flags & F_IMMORTAL))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000166 up = &((*up)->hash_next);
167 }
168 crecp->hash_next = *up;
169 *up = crecp;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000170}
171
172static void cache_free(struct crec *crecp)
173{
174 crecp->flags &= ~F_FORWARD;
175 crecp->flags &= ~F_REVERSE;
Simon Kelley26128d22004-11-14 16:43:54 +0000176 crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100177
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000178 if (cache_tail)
179 cache_tail->next = crecp;
180 else
181 cache_head = crecp;
182 crecp->prev = cache_tail;
183 crecp->next = NULL;
184 cache_tail = crecp;
185
186 /* retrieve big name for further use. */
187 if (crecp->flags & F_BIGNAME)
188 {
189 crecp->name.bname->next = big_free;
190 big_free = crecp->name.bname;
191 crecp->flags &= ~F_BIGNAME;
192 }
193}
194
195/* insert a new cache entry at the head of the list (youngest entry) */
196static void cache_link(struct crec *crecp)
197{
198 if (cache_head) /* check needed for init code */
199 cache_head->prev = crecp;
200 crecp->next = cache_head;
201 crecp->prev = NULL;
202 cache_head = crecp;
203 if (!cache_tail)
204 cache_tail = crecp;
205}
206
207/* remove an arbitrary cache entry for promotion */
208static void cache_unlink (struct crec *crecp)
209{
210 if (crecp->prev)
211 crecp->prev->next = crecp->next;
212 else
213 cache_head = crecp->next;
214
215 if (crecp->next)
216 crecp->next->prev = crecp->prev;
217 else
218 cache_tail = crecp->prev;
219}
220
221char *cache_get_name(struct crec *crecp)
222{
223 if (crecp->flags & F_BIGNAME)
224 return crecp->name.bname->name;
Simon Kelley28866e92011-02-14 20:19:14 +0000225 else if (crecp->flags & F_NAMEP)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000226 return crecp->name.namep;
227
228 return crecp->name.sname;
229}
230
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100231static int is_outdated_cname_pointer(struct crec *crecp)
232{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100233 if (!(crecp->flags & F_CNAME))
234 return 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100235
236 if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100237 return 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100238
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100239 return 1;
240}
241
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000242static int is_expired(time_t now, struct crec *crecp)
243{
244 if (crecp->flags & F_IMMORTAL)
245 return 0;
246
247 if (difftime(now, crecp->ttd) < 0)
248 return 0;
Simon Kelley4011c4e2006-10-28 16:26:19 +0100249
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000250 return 1;
251}
252
253static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000254{
255 /* Scan and remove old entries.
256 If (flags & F_FORWARD) then remove any forward entries for name and any expired
257 entries but only in the same hash bucket as name.
258 If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
259 entries in the whole cache.
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000260 If (flags == 0) remove any expired entries in the whole cache.
261
262 In the flags & F_FORWARD case, the return code is valid, and returns zero if the
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000263 name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000264
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000265 We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
266 so that when we hit an entry which isn't reverse and is immortal, we're done. */
267
268 struct crec *crecp, **up;
269
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000270 if (flags & F_FORWARD)
271 {
Simon Kelley6b010842007-02-12 20:32:07 +0000272 for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000273 if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
274 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000275 *up = crecp->hash_next;
276 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000277 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000278 cache_unlink(crecp);
279 cache_free(crecp);
280 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000281 }
282 else if ((crecp->flags & F_FORWARD) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000283 ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000284 hostname_isequal(cache_get_name(crecp), name))
285 {
286 if (crecp->flags & (F_HOSTS | F_DHCP))
287 return 0;
288 *up = crecp->hash_next;
289 cache_unlink(crecp);
290 cache_free(crecp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000291 }
292 else
293 up = &crecp->hash_next;
294 }
295 else
296 {
297 int i;
298#ifdef HAVE_IPV6
299 int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
300#else
301 int addrlen = INADDRSZ;
302#endif
303 for (i = 0; i < hash_size; i++)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000304 for (crecp = hash_table[i], up = &hash_table[i];
305 crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
306 crecp = crecp->hash_next)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000307 if (is_expired(now, crecp))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000308 {
309 *up = crecp->hash_next;
310 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
311 {
312 cache_unlink(crecp);
313 cache_free(crecp);
314 }
315 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000316 else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
317 (flags & crecp->flags & F_REVERSE) &&
318 (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
319 memcmp(&crecp->addr.addr, addr, addrlen) == 0)
320 {
321 *up = crecp->hash_next;
322 cache_unlink(crecp);
323 cache_free(crecp);
324 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000325 else
326 up = &crecp->hash_next;
327 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000328
329 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000330}
331
332/* Note: The normal calling sequence is
333 cache_start_insert
334 cache_insert * n
335 cache_end_insert
336
337 but an abort can cause the cache_end_insert to be missed
338 in which can the next cache_start_insert cleans things up. */
339
340void cache_start_insert(void)
341{
342 /* Free any entries which didn't get committed during the last
343 insert due to error.
344 */
345 while (new_chain)
346 {
347 struct crec *tmp = new_chain->next;
348 cache_free(new_chain);
349 new_chain = tmp;
350 }
351 new_chain = NULL;
352 insert_error = 0;
353}
354
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100355struct crec *cache_insert(char *name, struct all_addr *addr,
356 time_t now, unsigned long ttl, unsigned short flags)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000357{
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000358 struct crec *new;
359 union bigname *big_name = NULL;
360 int freed_all = flags & F_REVERSE;
Simon Kelley9e038942008-05-30 20:06:34 +0100361 int free_avail = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000362
Simon Kelley1a6bca82008-07-11 11:11:42 +0100363 log_query(flags | F_UPSTREAM, name, addr, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000364
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000365 /* if previous insertion failed give up now. */
366 if (insert_error)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100367 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000368
369 /* First remove any expired entries and entries for the name/address we
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000370 are currently inserting. Fail is we attempt to delete a name from
371 /etc/hosts or DHCP. */
372 if (!cache_scan_free(name, addr, now, flags))
373 {
374 insert_error = 1;
375 return NULL;
376 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000377
378 /* Now get a cache entry from the end of the LRU list */
379 while (1) {
380 if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
381 {
382 insert_error = 1;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100383 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000384 }
385
386 /* End of LRU list is still in use: if we didn't scan all the hash
387 chains for expired entries do that now. If we already tried that
388 then it's time to start spilling things. */
389
390 if (new->flags & (F_FORWARD | F_REVERSE))
391 {
Simon Kelley9e038942008-05-30 20:06:34 +0100392 /* If free_avail set, we believe that an entry has been freed.
393 Bugs have been known to make this not true, resulting in
Simon Kelley1a6bca82008-07-11 11:11:42 +0100394 a tight loop here. If that happens, abandon the
Simon Kelley9e038942008-05-30 20:06:34 +0100395 insert. Once in this state, all inserts will probably fail. */
Simon Kelley9e038942008-05-30 20:06:34 +0100396 if (free_avail)
397 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 insert_error = 1;
Simon Kelley9e038942008-05-30 20:06:34 +0100399 return NULL;
400 }
401
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000402 if (freed_all)
403 {
Simon Kelley9e038942008-05-30 20:06:34 +0100404 free_avail = 1; /* Must be free space now. */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100405 cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000406 cache_live_freed++;
407 }
408 else
409 {
410 cache_scan_free(NULL, NULL, now, 0);
411 freed_all = 1;
412 }
413 continue;
414 }
415
416 /* Check if we need to and can allocate extra memory for a long name.
417 If that fails, give up now. */
418 if (name && (strlen(name) > SMALLDNAME-1))
419 {
420 if (big_free)
421 {
422 big_name = big_free;
423 big_free = big_free->next;
424 }
425 else if (!bignames_left ||
Simon Kelley5aabfc72007-08-29 11:24:47 +0100426 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000427 {
428 insert_error = 1;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100429 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000430 }
431 else
432 bignames_left--;
433
434 }
435
436 /* Got the rest: finally grab entry. */
437 cache_unlink(new);
438 break;
439 }
440
441 new->flags = flags;
442 if (big_name)
443 {
444 new->name.bname = big_name;
445 new->flags |= F_BIGNAME;
446 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100447
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000448 if (name)
449 strcpy(cache_get_name(new), name);
450 else
451 *cache_get_name(new) = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100452
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000453 if (addr)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100454 new->addr.addr = *addr;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100455 else
456 new->addr.cname.cache = NULL;
457
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000458 new->ttd = now + (time_t)ttl;
459 new->next = new_chain;
460 new_chain = new;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100461
462 return new;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000463}
464
465/* after end of insertion, commit the new entries */
466void cache_end_insert(void)
467{
468 if (insert_error)
469 return;
470
471 while (new_chain)
472 {
473 struct crec *tmp = new_chain->next;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100474 /* drop CNAMEs which didn't find a target. */
475 if (is_outdated_cname_pointer(new_chain))
476 cache_free(new_chain);
477 else
478 {
479 cache_hash(new_chain);
480 cache_link(new_chain);
481 cache_inserted++;
482 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000483 new_chain = tmp;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000484 }
485 new_chain = NULL;
486}
487
488struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
489{
490 struct crec *ans;
491
492 if (crecp) /* iterating */
493 ans = crecp->next;
494 else
495 {
496 /* first search, look for relevant entries and push to top of list
497 also free anything which has expired */
498 struct crec *next, **up, **insert = NULL, **chainp = &ans;
Simon Kelley28866e92011-02-14 20:19:14 +0000499 unsigned short ins_flags = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000500
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000501 for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
502 {
503 next = crecp->hash_next;
504
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000505 if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000506 {
507 if ((crecp->flags & F_FORWARD) &&
508 (crecp->flags & prot) &&
509 hostname_isequal(cache_get_name(crecp), name))
510 {
511 if (crecp->flags & (F_HOSTS | F_DHCP))
512 {
513 *chainp = crecp;
514 chainp = &crecp->next;
515 }
516 else
517 {
518 cache_unlink(crecp);
519 cache_link(crecp);
520 }
521
Simon Kelley824af852008-02-12 20:43:05 +0000522 /* Move all but the first entry up the hash chain
523 this implements round-robin.
524 Make sure that re-ordering doesn't break the hash-chain
525 order invariants.
526 */
Simon Kelley9e038942008-05-30 20:06:34 +0100527 if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000528 {
529 *up = crecp->hash_next;
530 crecp->hash_next = *insert;
531 *insert = crecp;
532 insert = &crecp->hash_next;
533 }
Simon Kelley9e038942008-05-30 20:06:34 +0100534 else
535 {
536 if (!insert)
537 {
538 insert = up;
539 ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
540 }
541 up = &crecp->hash_next;
542 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000543 }
544 else
545 /* case : not expired, incorrect entry. */
546 up = &crecp->hash_next;
547 }
548 else
549 {
550 /* expired entry, free it */
551 *up = crecp->hash_next;
552 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
553 {
554 cache_unlink(crecp);
555 cache_free(crecp);
556 }
557 }
558 }
559
560 *chainp = cache_head;
561 }
562
563 if (ans &&
564 (ans->flags & F_FORWARD) &&
565 (ans->flags & prot) &&
566 hostname_isequal(cache_get_name(ans), name))
567 return ans;
568
569 return NULL;
570}
571
572struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
573 time_t now, unsigned short prot)
574{
575 struct crec *ans;
576#ifdef HAVE_IPV6
577 int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
578#else
579 int addrlen = INADDRSZ;
580#endif
581
582 if (crecp) /* iterating */
583 ans = crecp->next;
584 else
585 {
586 /* first search, look for relevant entries and push to top of list
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000587 also free anything which has expired. All the reverse entries are at the
588 start of the hash chain, so we can give up when we find the first
589 non-REVERSE one. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000590 int i;
591 struct crec **up, **chainp = &ans;
592
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000593 for (i=0; i<hash_size; i++)
594 for (crecp = hash_table[i], up = &hash_table[i];
595 crecp && (crecp->flags & F_REVERSE);
596 crecp = crecp->hash_next)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000597 if (!is_expired(now, crecp))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000598 {
Simon Kelley6b010842007-02-12 20:32:07 +0000599 if ((crecp->flags & prot) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100600 memcmp(&crecp->addr.addr, addr, addrlen) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000601 {
602 if (crecp->flags & (F_HOSTS | F_DHCP))
603 {
604 *chainp = crecp;
605 chainp = &crecp->next;
606 }
607 else
608 {
609 cache_unlink(crecp);
610 cache_link(crecp);
611 }
612 }
613 up = &crecp->hash_next;
614 }
615 else
616 {
617 *up = crecp->hash_next;
618 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
619 {
620 cache_unlink(crecp);
621 cache_free(crecp);
622 }
623 }
624
625 *chainp = cache_head;
626 }
627
628 if (ans &&
629 (ans->flags & F_REVERSE) &&
630 (ans->flags & prot) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100631 memcmp(&ans->addr.addr, addr, addrlen) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000632 return ans;
633
634 return NULL;
635}
636
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100637static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000638 unsigned short flags, int index, struct crec **rhash, int hashsz)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000639{
640 struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
Simon Kelley9009d742008-11-14 20:04:27 +0000641 int i, nameexists = 0;
642 struct cname *a;
Simon Kelley205fafa2012-01-11 21:31:51 +0000643 unsigned int j;
Simon Kelley9009d742008-11-14 20:04:27 +0000644
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000645 /* Remove duplicates in hosts files. */
Simon Kelley9009d742008-11-14 20:04:27 +0000646 if (lookup && (lookup->flags & F_HOSTS))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000647 {
Simon Kelley9009d742008-11-14 20:04:27 +0000648 nameexists = 1;
649 if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)
650 {
651 free(cache);
652 return;
653 }
654 }
655
656 /* Ensure there is only one address -> name mapping (first one trumps)
Simon Kelley205fafa2012-01-11 21:31:51 +0000657 We do this by steam here, The entries are kept in hash chains, linked
658 by ->next (which is unused at this point) held in hash buckets in
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000659 the array rhash, hashed on address. Note that rhash and the values
660 in ->next are only valid whilst reading hosts files: the buckets are
661 then freed, and the ->next pointer used for other things.
Simon Kelley205fafa2012-01-11 21:31:51 +0000662
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000663 Only insert each unique address once into this hashing structure.
Simon Kelley205fafa2012-01-11 21:31:51 +0000664
665 This complexity avoids O(n^2) divergent CPU use whilst reading
666 large (10000 entry) hosts files. */
Simon Kelley9009d742008-11-14 20:04:27 +0000667
Simon Kelley205fafa2012-01-11 21:31:51 +0000668 /* hash address */
669 for (j = 0, i = 0; i < addrlen; i++)
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000670 j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
Simon Kelley915363f2012-01-11 22:00:48 +0000671
672 for (lookup = rhash[j]; lookup; lookup = lookup->next)
Simon Kelley805a1132012-01-13 11:51:46 +0000673 if ((lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
Simon Kelley205fafa2012-01-11 21:31:51 +0000674 memcmp(&lookup->addr.addr, addr, addrlen) == 0)
Simon Kelley9009d742008-11-14 20:04:27 +0000675 {
Simon Kelley205fafa2012-01-11 21:31:51 +0000676 flags &= ~F_REVERSE;
677 break;
Simon Kelley9009d742008-11-14 20:04:27 +0000678 }
679
Simon Kelley915363f2012-01-11 22:00:48 +0000680 /* maintain address hash chain, insert new unique address */
681 if (!lookup)
682 {
683 cache->next = rhash[j];
684 rhash[j] = cache;
685 }
686
Simon Kelley9009d742008-11-14 20:04:27 +0000687 cache->flags = flags;
688 cache->uid = index;
Simon Kelley915363f2012-01-11 22:00:48 +0000689 memcpy(&cache->addr.addr, addr, addrlen);
Simon Kelley9009d742008-11-14 20:04:27 +0000690 cache_hash(cache);
691
692 /* don't need to do alias stuff for second and subsequent addresses. */
693 if (!nameexists)
694 for (a = daemon->cnames; a; a = a->next)
695 if (hostname_isequal(cache->name.sname, a->target) &&
Simon Kelley1f15b812009-10-13 17:49:32 +0100696 (lookup = whine_malloc(sizeof(struct crec))))
Simon Kelley9009d742008-11-14 20:04:27 +0000697 {
Simon Kelley28866e92011-02-14 20:19:14 +0000698 lookup->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
Simon Kelley1f15b812009-10-13 17:49:32 +0100699 lookup->name.namep = a->alias;
Simon Kelley9009d742008-11-14 20:04:27 +0000700 lookup->addr.cname.cache = cache;
701 lookup->addr.cname.uid = index;
702 cache_hash(lookup);
703 }
704}
705
706static int eatspace(FILE *f)
707{
708 int c, nl = 0;
709
710 while (1)
711 {
712 if ((c = getc(f)) == '#')
713 while (c != '\n' && c != EOF)
714 c = getc(f);
Simon Kelley832af0b2007-01-21 20:01:28 +0000715
Simon Kelley9009d742008-11-14 20:04:27 +0000716 if (c == EOF)
717 return 1;
718
719 if (!isspace(c))
720 {
721 ungetc(c, f);
722 return nl;
723 }
724
725 if (c == '\n')
726 nl = 1;
727 }
728}
729
730static int gettok(FILE *f, char *token)
731{
732 int c, count = 0;
733
734 while (1)
735 {
736 if ((c = getc(f)) == EOF)
737 return (count == 0) ? EOF : 1;
738
739 if (isspace(c) || c == '#')
740 {
741 ungetc(c, f);
742 return eatspace(f);
743 }
744
745 if (count < (MAXDNAME - 1))
746 {
747 token[count++] = c;
748 token[count] = 0;
749 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000750 }
751}
752
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000753static int read_hostsfile(char *filename, int index, int cache_size, struct crec **rhash, int hashsz)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000754{
755 FILE *f = fopen(filename, "r");
Simon Kelley9009d742008-11-14 20:04:27 +0000756 char *token = daemon->namebuff, *domain_suffix = NULL;
Simon Kelley4011c4e2006-10-28 16:26:19 +0100757 int addr_count = 0, name_count = cache_size, lineno = 0;
Simon Kelley205fafa2012-01-11 21:31:51 +0000758 unsigned short flags = 0;
759 struct all_addr addr;
760 int atnl, addrlen = 0;
Simon Kelley4011c4e2006-10-28 16:26:19 +0100761
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000762 if (!f)
763 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100764 my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
Simon Kelley4011c4e2006-10-28 16:26:19 +0100765 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000766 }
Simon Kelley9009d742008-11-14 20:04:27 +0000767
768 eatspace(f);
769
770 while ((atnl = gettok(f, token)) != EOF)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000771 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000772 lineno++;
Simon Kelley9009d742008-11-14 20:04:27 +0000773
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000774#ifdef HAVE_IPV6
Simon Kelley3d8df262005-08-29 12:19:27 +0100775 if (inet_pton(AF_INET, token, &addr) > 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000776 {
777 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
778 addrlen = INADDRSZ;
Simon Kelley9009d742008-11-14 20:04:27 +0000779 domain_suffix = get_domain(addr.addr.addr4);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000780 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100781 else if (inet_pton(AF_INET6, token, &addr) > 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000782 {
783 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
784 addrlen = IN6ADDRSZ;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000785 domain_suffix = get_domain6(&addr.addr.addr6);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000786 }
787#else
Simon Kelley9009d742008-11-14 20:04:27 +0000788 if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
789 {
790 flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
791 addrlen = INADDRSZ;
792 domain_suffix = get_domain(addr.addr.addr4);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000793 }
794#endif
795 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000796 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100797 my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
Simon Kelley9009d742008-11-14 20:04:27 +0000798 while (atnl == 0)
799 atnl = gettok(f, token);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000800 continue;
801 }
Simon Kelley9009d742008-11-14 20:04:27 +0000802
Simon Kelley9009d742008-11-14 20:04:27 +0000803 addr_count++;
804
805 /* rehash every 1000 names. */
806 if ((name_count - cache_size) > 1000)
807 {
808 rehash(name_count);
809 cache_size = name_count;
810 }
811
812 while (atnl == 0)
813 {
814 struct crec *cache;
Simon Kelley1f15b812009-10-13 17:49:32 +0100815 int fqdn, nomem;
816 char *canon;
Simon Kelley9009d742008-11-14 20:04:27 +0000817
818 if ((atnl = gettok(f, token)) == EOF)
819 break;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000820
Simon Kelley9009d742008-11-14 20:04:27 +0000821 fqdn = !!strchr(token, '.');
822
Simon Kelley1f15b812009-10-13 17:49:32 +0100823 if ((canon = canonicalise(token, &nomem)))
Simon Kelley9009d742008-11-14 20:04:27 +0000824 {
825 /* If set, add a version of the name with a default domain appended */
Simon Kelley28866e92011-02-14 20:19:14 +0000826 if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
Simon Kelley9009d742008-11-14 20:04:27 +0000827 (cache = whine_malloc(sizeof(struct crec) +
Simon Kelley1f15b812009-10-13 17:49:32 +0100828 strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
Simon Kelley9009d742008-11-14 20:04:27 +0000829 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100830 strcpy(cache->name.sname, canon);
Simon Kelley9009d742008-11-14 20:04:27 +0000831 strcat(cache->name.sname, ".");
832 strcat(cache->name.sname, domain_suffix);
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000833 add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
Simon Kelley9009d742008-11-14 20:04:27 +0000834 name_count++;
835 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100836 if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
Simon Kelley9009d742008-11-14 20:04:27 +0000837 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100838 strcpy(cache->name.sname, canon);
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000839 add_hosts_entry(cache, &addr, addrlen, flags, index, rhash, hashsz);
Simon Kelley9009d742008-11-14 20:04:27 +0000840 name_count++;
841 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100842 free(canon);
843
Simon Kelley9009d742008-11-14 20:04:27 +0000844 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100845 else if (!nomem)
Simon Kelley9009d742008-11-14 20:04:27 +0000846 my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
847 }
848 }
849
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000850 fclose(f);
Simon Kelley4011c4e2006-10-28 16:26:19 +0100851 rehash(name_count);
Simon Kelley9009d742008-11-14 20:04:27 +0000852
Simon Kelleyf2621c72007-04-29 19:47:21 +0100853 my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
Simon Kelley9009d742008-11-14 20:04:27 +0000854
Simon Kelley4011c4e2006-10-28 16:26:19 +0100855 return name_count;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000856}
857
Simon Kelley7622fc02009-06-04 20:32:05 +0100858void cache_reload(void)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000859{
860 struct crec *cache, **up, *tmp;
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000861 int revhashsz, i, total_size = daemon->cachesize;
Simon Kelley7622fc02009-06-04 20:32:05 +0100862 struct hostsfile *ah;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000863
Simon Kelley59353a62004-11-21 19:34:28 +0000864 cache_inserted = cache_live_freed = 0;
865
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000866 for (i=0; i<hash_size; i++)
867 for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
868 {
869 tmp = cache->hash_next;
870 if (cache->flags & F_HOSTS)
871 {
872 *up = cache->hash_next;
873 free(cache);
874 }
875 else if (!(cache->flags & F_DHCP))
876 {
877 *up = cache->hash_next;
878 if (cache->flags & F_BIGNAME)
879 {
880 cache->name.bname->next = big_free;
881 big_free = cache->name.bname;
882 }
883 cache->flags = 0;
884 }
885 else
886 up = &cache->hash_next;
887 }
888
Simon Kelley28866e92011-02-14 20:19:14 +0000889 if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000890 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100891 if (daemon->cachesize > 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100892 my_syslog(LOG_INFO, _("cleared cache"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000893 return;
894 }
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000895
896 /* borrow the packet buffer for a temporary by-address hash */
897 memset(daemon->packet, 0, daemon->packet_buff_sz);
898 revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
899 /* we overwrote the buffer... */
900 daemon->srv_save = NULL;
901
Simon Kelley28866e92011-02-14 20:19:14 +0000902 if (!option_bool(OPT_NO_HOSTS))
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000903 total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz);
Simon Kelley28866e92011-02-14 20:19:14 +0000904
905 daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
Simon Kelley7622fc02009-06-04 20:32:05 +0100906 for (ah = daemon->addn_hosts; ah; ah = ah->next)
907 if (!(ah->flags & AH_INACTIVE))
Simon Kelley1ab62ae2012-01-12 11:33:16 +0000908 total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000909}
910
Simon Kelley7622fc02009-06-04 20:32:05 +0100911char *get_domain(struct in_addr addr)
912{
913 struct cond_domain *c;
914
915 for (c = daemon->cond_domain; c; c = c->next)
Simon Kelleyd74942a2012-02-07 20:51:56 +0000916 if (!c->is6 &&
917 ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100918 ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
919 return c->domain;
920
921 return daemon->domain_suffix;
922}
923
Simon Kelley4cb1b322012-02-06 14:30:41 +0000924
925#ifdef HAVE_IPV6
926char *get_domain6(struct in6_addr *addr)
927{
Simon Kelleyd74942a2012-02-07 20:51:56 +0000928 struct cond_domain *c;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000929
930 u64 addrpart = addr6part(addr);
931
Simon Kelleyd74942a2012-02-07 20:51:56 +0000932 for (c = daemon->cond_domain; c; c = c->next)
933 if (c->is6 &&
934 is_same_net6(addr, &c->start6, 64) &&
935 addrpart >= addr6part(&c->start6) &&
936 addrpart <= addr6part(&c->end6))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000937 return c->domain;
938
939 return daemon->domain_suffix;
940}
941#endif
942
Simon Kelley7622fc02009-06-04 20:32:05 +0100943#ifdef HAVE_DHCP
Simon Kelley7de060b2011-08-26 17:24:52 +0100944struct in_addr a_record_from_hosts(char *name, time_t now)
945{
946 struct crec *crecp = NULL;
947 struct in_addr ret;
948
949 while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
950 if (crecp->flags & F_HOSTS)
951 return *(struct in_addr *)&crecp->addr;
952
953 my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
954
955 ret.s_addr = 0;
956 return ret;
957}
958
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000959void cache_unhash_dhcp(void)
960{
Simon Kelley6b010842007-02-12 20:32:07 +0000961 struct crec *cache, **up;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000962 int i;
963
964 for (i=0; i<hash_size; i++)
965 for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
966 if (cache->flags & F_DHCP)
Simon Kelley6b010842007-02-12 20:32:07 +0000967 {
968 *up = cache->hash_next;
969 cache->next = dhcp_spare;
970 dhcp_spare = cache;
971 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000972 else
973 up = &cache->hash_next;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000974}
975
Simon Kelley4cb1b322012-02-06 14:30:41 +0000976void cache_add_dhcp_entry(char *host_name, int prot,
977 struct all_addr *host_address, time_t ttd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000978{
Simon Kelley9009d742008-11-14 20:04:27 +0000979 struct crec *crec = NULL, *aliasc;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000980 unsigned short flags = F_IPV4;
Simon Kelley824af852008-02-12 20:43:05 +0000981 int in_hosts = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000982 struct cname *a;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000983 size_t addrlen = sizeof(struct in_addr);
984
985#ifdef HAVE_IPV6
986 if (prot == AF_INET6)
987 {
988 flags = F_IPV6;
989 addrlen = sizeof(struct in6_addr);
990 }
991#endif
Simon Kelley9009d742008-11-14 20:04:27 +0000992
Simon Kelley4cb1b322012-02-06 14:30:41 +0000993 while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000994 {
Simon Kelley824af852008-02-12 20:43:05 +0000995 /* check all addresses associated with name */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000996 if (crec->flags & F_HOSTS)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000997 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100998 /* if in hosts, don't need DHCP record */
999 in_hosts = 1;
1000
Simon Kelley4cb1b322012-02-06 14:30:41 +00001001 inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001002 if (crec->flags & F_CNAME)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001003
Simon Kelley28866e92011-02-14 20:19:14 +00001004 my_syslog(MS_DHCP | LOG_WARNING,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001005 _("%s is a CNAME, not giving it to the DHCP lease of %s"),
Simon Kelley4cb1b322012-02-06 14:30:41 +00001006 host_name, daemon->addrbuff);
1007 else if (memcmp(&crec->addr.addr, host_address, addrlen) != 0)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001008 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001009 inet_ntop(prot, &crec->addr.addr, daemon->namebuff, MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00001010 my_syslog(MS_DHCP | LOG_WARNING,
Simon Kelleyf2621c72007-04-29 19:47:21 +01001011 _("not giving name %s to the DHCP lease of %s because "
1012 "the name exists in %s with address %s"),
Simon Kelley4cb1b322012-02-06 14:30:41 +00001013 host_name, daemon->addrbuff,
Simon Kelley7622fc02009-06-04 20:32:05 +01001014 record_source(crec->uid), daemon->namebuff);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001015 }
Simon Kelley1ab84e22004-01-29 16:48:35 +00001016 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001017 else if (!(crec->flags & F_DHCP))
Simon Kelley824af852008-02-12 20:43:05 +00001018 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001019 cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
Simon Kelley824af852008-02-12 20:43:05 +00001020 /* scan_free deletes all addresses associated with name */
1021 break;
1022 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001023 }
Simon Kelley824af852008-02-12 20:43:05 +00001024
1025 if (in_hosts)
1026 return;
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001027
Simon Kelley4cb1b322012-02-06 14:30:41 +00001028 if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
Simon Kelley824af852008-02-12 20:43:05 +00001029 {
1030 if (crec->flags & F_NEG)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001031 {
1032 flags |= F_REVERSE;
1033 cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
1034 }
Simon Kelley824af852008-02-12 20:43:05 +00001035 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001036 else
1037 flags |= F_REVERSE;
Simon Kelley824af852008-02-12 20:43:05 +00001038
1039 if ((crec = dhcp_spare))
Simon Kelley6b010842007-02-12 20:32:07 +00001040 dhcp_spare = dhcp_spare->next;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001041 else /* need new one */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001042 crec = whine_malloc(sizeof(struct crec));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001043
1044 if (crec) /* malloc may fail */
1045 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001046 crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001047 if (ttd == 0)
1048 crec->flags |= F_IMMORTAL;
1049 else
1050 crec->ttd = ttd;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001051 crec->addr.addr = *host_address;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001052 crec->name.namep = host_name;
Simon Kelley9009d742008-11-14 20:04:27 +00001053 crec->uid = uid++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001054 cache_hash(crec);
Simon Kelley9009d742008-11-14 20:04:27 +00001055
1056 for (a = daemon->cnames; a; a = a->next)
1057 if (hostname_isequal(host_name, a->target))
1058 {
1059 if ((aliasc = dhcp_spare))
1060 dhcp_spare = dhcp_spare->next;
1061 else /* need new one */
1062 aliasc = whine_malloc(sizeof(struct crec));
1063
1064 if (aliasc)
1065 {
Simon Kelley28866e92011-02-14 20:19:14 +00001066 aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
Simon Kelley9009d742008-11-14 20:04:27 +00001067 if (ttd == 0)
1068 aliasc->flags |= F_IMMORTAL;
1069 else
1070 aliasc->ttd = ttd;
1071 aliasc->name.namep = a->alias;
1072 aliasc->addr.cname.cache = crec;
1073 aliasc->addr.cname.uid = crec->uid;
1074 cache_hash(aliasc);
1075 }
1076 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001077 }
1078}
Simon Kelley7622fc02009-06-04 20:32:05 +01001079#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001080
Simon Kelley9009d742008-11-14 20:04:27 +00001081
Simon Kelley5aabfc72007-08-29 11:24:47 +01001082void dump_cache(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001083{
Simon Kelley824af852008-02-12 20:43:05 +00001084 struct server *serv, *serv1;
1085
1086 my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
1087 my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
1088 daemon->cachesize, cache_live_freed, cache_inserted);
1089 my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
1090 daemon->queries_forwarded, daemon->local_answer);
1091
Simon Kelley824af852008-02-12 20:43:05 +00001092 /* sum counts from different records for same server */
1093 for (serv = daemon->servers; serv; serv = serv->next)
1094 serv->flags &= ~SERV_COUNTED;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001095
Simon Kelley824af852008-02-12 20:43:05 +00001096 for (serv = daemon->servers; serv; serv = serv->next)
Simon Kelley28866e92011-02-14 20:19:14 +00001097 if (!(serv->flags &
1098 (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
Simon Kelley824af852008-02-12 20:43:05 +00001099 {
1100 int port;
1101 unsigned int queries = 0, failed_queries = 0;
1102 for (serv1 = serv; serv1; serv1 = serv1->next)
Simon Kelley28866e92011-02-14 20:19:14 +00001103 if (!(serv1->flags &
1104 (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
1105 sockaddr_isequal(&serv->addr, &serv1->addr))
Simon Kelley824af852008-02-12 20:43:05 +00001106 {
1107 serv1->flags |= SERV_COUNTED;
1108 queries += serv1->queries;
1109 failed_queries += serv1->failed_queries;
1110 }
Simon Kelleyc72daea2012-01-05 21:33:27 +00001111 port = prettyprint_addr(&serv->addr, daemon->addrbuff);
1112 my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
Simon Kelley824af852008-02-12 20:43:05 +00001113 }
1114
Simon Kelley28866e92011-02-14 20:19:14 +00001115 if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001116 {
1117 struct crec *cache ;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001118 int i;
Simon Kelley28866e92011-02-14 20:19:14 +00001119 my_syslog(LOG_INFO, "Host Address Flags Expires");
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001120
1121 for (i=0; i<hash_size; i++)
1122 for (cache = hash_table[i]; cache; cache = cache->hash_next)
1123 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001124 char *a, *p = daemon->namebuff;
1125 p += sprintf(p, "%-40.40s ", cache_get_name(cache));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001126 if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001127 a = "";
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001128 else if (cache->flags & F_CNAME)
1129 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001130 a = "";
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001131 if (!is_outdated_cname_pointer(cache))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001132 a = cache_get_name(cache->addr.cname.cache);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001133 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001134#ifdef HAVE_IPV6
Simon Kelleyf2621c72007-04-29 19:47:21 +01001135 else
1136 {
Simon Kelleyc72daea2012-01-05 21:33:27 +00001137 a = daemon->addrbuff;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001138 if (cache->flags & F_IPV4)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001139 inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001140 else if (cache->flags & F_IPV6)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001141 inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001142 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001143#else
1144 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001145 a = inet_ntoa(cache->addr.addr.addr.addr4);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001146#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001147 p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
1148 cache->flags & F_IPV4 ? "4" : "",
1149 cache->flags & F_IPV6 ? "6" : "",
1150 cache->flags & F_CNAME ? "C" : "",
1151 cache->flags & F_FORWARD ? "F" : " ",
1152 cache->flags & F_REVERSE ? "R" : " ",
1153 cache->flags & F_IMMORTAL ? "I" : " ",
1154 cache->flags & F_DHCP ? "D" : " ",
1155 cache->flags & F_NEG ? "N" : " ",
1156 cache->flags & F_NXDOMAIN ? "X" : " ",
1157 cache->flags & F_HOSTS ? "H" : " ");
Simon Kelley44a2a312004-03-10 20:04:35 +00001158#ifdef HAVE_BROKEN_RTC
Simon Kelleyf2621c72007-04-29 19:47:21 +01001159 p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
Simon Kelley44a2a312004-03-10 20:04:35 +00001160#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001161 p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
1162 /* ctime includes trailing \n - eat it */
1163 *(p-1) = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001164#endif
Simon Kelley28866e92011-02-14 20:19:14 +00001165 my_syslog(LOG_INFO, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001166 }
1167 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001168}
1169
Simon Kelley7622fc02009-06-04 20:32:05 +01001170char *record_source(int index)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001171{
Simon Kelley7622fc02009-06-04 20:32:05 +01001172 struct hostsfile *ah;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001173
Simon Kelley7622fc02009-06-04 20:32:05 +01001174 if (index == 0)
1175 return HOSTSFILE;
1176
1177 for (ah = daemon->addn_hosts; ah; ah = ah->next)
1178 if (ah->index == index)
1179 return ah->fname;
1180
1181 return "<unknown>";
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001182}
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001183
Simon Kelley1a6bca82008-07-11 11:11:42 +01001184void querystr(char *str, unsigned short type)
1185{
1186 unsigned int i;
1187
1188 sprintf(str, "query[type=%d]", type);
1189 for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
1190 if (typestr[i].type == type)
1191 sprintf(str,"query[%s]", typestr[i].name);
1192}
1193
Simon Kelley28866e92011-02-14 20:19:14 +00001194void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001195{
Simon Kelleyc72daea2012-01-05 21:33:27 +00001196 char *source, *dest = daemon->addrbuff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001197 char *verb = "is";
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001198
Simon Kelley28866e92011-02-14 20:19:14 +00001199 if (!option_bool(OPT_LOG))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001200 return;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001201
1202 if (addr)
1203 {
1204#ifdef HAVE_IPV6
1205 inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
Simon Kelleyc72daea2012-01-05 21:33:27 +00001206 addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001207#else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001208 strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001209#endif
1210 }
1211
1212 if (flags & F_REVERSE)
1213 {
1214 dest = name;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001215 name = daemon->addrbuff;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001216 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001217
1218 if (flags & F_NEG)
1219 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001220 if (flags & F_NXDOMAIN)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001221 {
1222 if (flags & F_IPV4)
1223 dest = "NXDOMAIN-IPv4";
Simon Kelley824af852008-02-12 20:43:05 +00001224 else if (flags & F_IPV6)
1225 dest = "NXDOMAIN-IPv6";
1226 else
1227 dest = "NXDOMAIN";
Simon Kelley5aabfc72007-08-29 11:24:47 +01001228 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001229 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01001230 {
1231 if (flags & F_IPV4)
1232 dest = "NODATA-IPv4";
Simon Kelley824af852008-02-12 20:43:05 +00001233 else if (flags & F_IPV6)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001234 dest = "NODATA-IPv6";
Simon Kelley824af852008-02-12 20:43:05 +00001235 else
1236 dest = "NODATA";
Simon Kelley5aabfc72007-08-29 11:24:47 +01001237 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001238 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001239 else if (flags & F_CNAME)
Simon Kelley28866e92011-02-14 20:19:14 +00001240 dest = "<CNAME>";
1241 else if (flags & F_RRNAME)
1242 dest = arg;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001243
Simon Kelley1f15b812009-10-13 17:49:32 +01001244 if (flags & F_CONFIG)
1245 source = "config";
1246 else if (flags & F_DHCP)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001247 source = "DHCP";
1248 else if (flags & F_HOSTS)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001249 source = arg;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001250 else if (flags & F_UPSTREAM)
1251 source = "reply";
1252 else if (flags & F_SERVER)
1253 {
1254 source = "forwarded";
1255 verb = "to";
1256 }
1257 else if (flags & F_QUERY)
1258 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001259 source = arg;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001260 verb = "from";
1261 }
1262 else
1263 source = "cached";
1264
Simon Kelley3d8df262005-08-29 12:19:27 +01001265 if (strlen(name) == 0)
1266 name = ".";
1267
Simon Kelley28866e92011-02-14 20:19:14 +00001268 my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001269}
1270