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