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