1/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
2
3   This program is free software; you can redistribute it and/or modify
4   it under the terms of the GNU General Public License as published by
5   the Free Software Foundation; version 2 dated June, 1991, or
6   (at your option) version 3 dated 29 June, 2007.
7
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   GNU General Public License for more details.
12
13   You should have received a copy of the GNU General Public License
14   along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*/
16
17#include "dnsmasq.h"
18
19#ifdef HAVE_AUTH
20
21static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
22{
23  struct addrlist *subnet;
24
25  for (subnet = zone->subnet; subnet; subnet = subnet->next)
26    {
27      if (!(subnet->flags & ADDRLIST_IPV6))
28	{
29	  struct in_addr netmask, addr = addr_u->addr.addr4;
30
31	  if (!(flag & F_IPV4))
32	    continue;
33
34	  netmask.s_addr = htonl(~(in_addr_t)0 << (32 - subnet->prefixlen));
35
36	  if  (is_same_net(addr, subnet->addr.addr.addr4, netmask))
37	    return subnet;
38	}
39#ifdef HAVE_IPV6
40      else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen))
41	return subnet;
42#endif
43
44    }
45  return NULL;
46}
47
48static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
49{
50  /* No zones specified, no filter */
51  if (!zone->subnet)
52    return 1;
53
54  return find_subnet(zone, flag, addr_u) != NULL;
55}
56
57int in_zone(struct auth_zone *zone, char *name, char **cut)
58{
59  size_t namelen = strlen(name);
60  size_t domainlen = strlen(zone->domain);
61
62  if (cut)
63    *cut = NULL;
64
65  if (namelen >= domainlen &&
66      hostname_isequal(zone->domain, &name[namelen - domainlen]))
67    {
68
69      if (namelen == domainlen)
70	return 1;
71
72      if (name[namelen - domainlen - 1] == '.')
73	{
74	  if (cut)
75	    *cut = &name[namelen - domainlen - 1];
76	  return 1;
77	}
78    }
79
80  return 0;
81}
82
83
84size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query)
85{
86  char *name = daemon->namebuff;
87  unsigned char *p, *ansp;
88  int qtype, qclass;
89  int nameoffset, axfroffset = 0;
90  int q, anscount = 0, authcount = 0;
91  struct crec *crecp;
92  int  auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
93  struct auth_zone *zone = NULL;
94  struct addrlist *subnet = NULL;
95  char *cut;
96  struct mx_srv_record *rec, *move, **up;
97  struct txt_record *txt;
98  struct interface_name *intr;
99  struct naptr *na;
100  struct all_addr addr;
101  struct cname *a;
102
103  if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
104    return 0;
105
106  /* determine end of question section (we put answers there) */
107  if (!(ansp = skip_questions(header, qlen)))
108    return 0; /* bad packet */
109
110  /* now process each question, answers go in RRs after the question */
111  p = (unsigned char *)(header+1);
112
113  for (q = ntohs(header->qdcount); q != 0; q--)
114    {
115      unsigned short flag = 0;
116      int found = 0;
117
118      /* save pointer to name for copying into answers */
119      nameoffset = p - (unsigned char *)header;
120
121      /* now extract name as .-concatenated string into name */
122      if (!extract_name(header, qlen, &p, name, 1, 4))
123	return 0; /* bad packet */
124
125      GETSHORT(qtype, p);
126      GETSHORT(qclass, p);
127
128      if (qclass != C_IN)
129	{
130	  auth = 0;
131	  continue;
132	}
133
134      if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
135	  (flag = in_arpa_name_2_addr(name, &addr)) &&
136	  !local_query)
137	{
138	  for (zone = daemon->auth_zones; zone; zone = zone->next)
139	    if ((subnet = find_subnet(zone, flag, &addr)))
140	      break;
141
142	  if (!zone)
143	    {
144	      auth = 0;
145	      continue;
146	    }
147	  else if (qtype == T_SOA)
148	    soa = 1, found = 1;
149	  else if (qtype == T_NS)
150	    ns = 1, found = 1;
151	}
152
153      if (qtype == T_PTR && flag)
154	{
155	  intr = NULL;
156
157	  if (flag == F_IPV4)
158	    for (intr = daemon->int_names; intr; intr = intr->next)
159	      {
160		struct addrlist *addrlist;
161
162		for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
163		  if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
164		    break;
165
166		if (addrlist)
167		  break;
168		else
169		  while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
170		    intr = intr->next;
171	      }
172#ifdef HAVE_IPV6
173	  else if (flag == F_IPV6)
174	    for (intr = daemon->int_names; intr; intr = intr->next)
175	      {
176		struct addrlist *addrlist;
177
178		for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
179		  if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
180		    break;
181
182		if (addrlist)
183		  break;
184		else
185		  while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
186		    intr = intr->next;
187	      }
188#endif
189
190	  if (intr)
191	    {
192	      if (local_query || in_zone(zone, intr->name, NULL))
193		{
194		  found = 1;
195		  log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
196		  if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
197					  daemon->auth_ttl, NULL,
198					  T_PTR, C_IN, "d", intr->name))
199		    anscount++;
200		}
201	    }
202
203	  if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
204	    do {
205	      strcpy(name, cache_get_name(crecp));
206
207	      if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
208		{
209		  char *p = strchr(name, '.');
210		  if (p)
211		    *p = 0; /* must be bare name */
212
213		  /* add  external domain */
214		  if (zone)
215		    {
216		      strcat(name, ".");
217		      strcat(name, zone->domain);
218		    }
219		  log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
220		  found = 1;
221		  if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
222					  daemon->auth_ttl, NULL,
223					  T_PTR, C_IN, "d", name))
224		    anscount++;
225		}
226	      else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
227		{
228		  log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
229		  found = 1;
230		  if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
231					  daemon->auth_ttl, NULL,
232					  T_PTR, C_IN, "d", name))
233		    anscount++;
234		}
235	      else
236		continue;
237
238	    } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
239
240	  if (found)
241	    nxdomain = 0;
242	  else
243	    log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
244
245	  continue;
246	}
247
248    cname_restart:
249      if (found)
250	/* NS and SOA .arpa requests have set found above. */
251	cut = NULL;
252      else
253	{
254	  for (zone = daemon->auth_zones; zone; zone = zone->next)
255	    if (in_zone(zone, name, &cut))
256	      break;
257
258	  if (!zone)
259	    {
260	      auth = 0;
261	      continue;
262	    }
263	}
264
265      for (rec = daemon->mxnames; rec; rec = rec->next)
266	if (!rec->issrv && hostname_isequal(name, rec->name))
267	  {
268	    nxdomain = 0;
269
270	    if (qtype == T_MX)
271	      {
272		found = 1;
273		log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
274		if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
275					NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
276		  anscount++;
277	      }
278	  }
279
280      for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
281	if (rec->issrv && hostname_isequal(name, rec->name))
282	  {
283	    nxdomain = 0;
284
285	    if (qtype == T_SRV)
286	      {
287		found = 1;
288		log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
289		if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
290					NULL, T_SRV, C_IN, "sssd",
291					rec->priority, rec->weight, rec->srvport, rec->target))
292
293		  anscount++;
294	      }
295
296	    /* unlink first SRV record found */
297	    if (!move)
298	      {
299		move = rec;
300		*up = rec->next;
301	      }
302	    else
303	      up = &rec->next;
304	  }
305	else
306	  up = &rec->next;
307
308      /* put first SRV record back at the end. */
309      if (move)
310	{
311	  *up = move;
312	  move->next = NULL;
313	}
314
315      for (txt = daemon->rr; txt; txt = txt->next)
316	if (hostname_isequal(name, txt->name))
317	  {
318	    nxdomain = 0;
319	    if (txt->class == qtype)
320	      {
321		found = 1;
322		log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
323		if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
324					NULL, txt->class, C_IN, "t", txt->len, txt->txt))
325		  anscount++;
326	      }
327	  }
328
329      for (txt = daemon->txt; txt; txt = txt->next)
330	if (txt->class == C_IN && hostname_isequal(name, txt->name))
331	  {
332	    nxdomain = 0;
333	    if (qtype == T_TXT)
334	      {
335		found = 1;
336		log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
337		if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
338					NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
339		  anscount++;
340	      }
341	  }
342
343       for (na = daemon->naptr; na; na = na->next)
344	 if (hostname_isequal(name, na->name))
345	   {
346	     nxdomain = 0;
347	     if (qtype == T_NAPTR)
348	       {
349		 found = 1;
350		 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
351		 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
352					 NULL, T_NAPTR, C_IN, "sszzzd",
353					 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
354			  anscount++;
355	       }
356	   }
357
358       if (qtype == T_A)
359	 flag = F_IPV4;
360
361#ifdef HAVE_IPV6
362       if (qtype == T_AAAA)
363	 flag = F_IPV6;
364#endif
365
366       for (intr = daemon->int_names; intr; intr = intr->next)
367	 if (hostname_isequal(name, intr->name))
368	   {
369	     struct addrlist *addrlist;
370
371	     nxdomain = 0;
372
373	     if (flag)
374	       for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
375		 if (((addrlist->flags & ADDRLIST_IPV6)  ? T_AAAA : T_A) == qtype &&
376		     (local_query || filter_zone(zone, flag, &addrlist->addr)))
377		   {
378#ifdef HAVE_IPV6
379		     if (addrlist->flags & ADDRLIST_REVONLY)
380		       continue;
381#endif
382		     found = 1;
383		     log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
384		     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
385					     daemon->auth_ttl, NULL, qtype, C_IN,
386					     qtype == T_A ? "4" : "6", &addrlist->addr))
387		       anscount++;
388		   }
389	     }
390
391       for (a = daemon->cnames; a; a = a->next)
392	 if (hostname_isequal(name, a->alias) )
393	   {
394	     log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
395	     strcpy(name, a->target);
396	     if (!strchr(name, '.'))
397	       {
398		 strcat(name, ".");
399		 strcat(name, zone->domain);
400	       }
401	     found = 1;
402	     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
403				     daemon->auth_ttl, &nameoffset,
404				     T_CNAME, C_IN, "d", name))
405	       anscount++;
406
407	     goto cname_restart;
408	   }
409
410      if (!cut)
411	{
412	  nxdomain = 0;
413
414	  if (qtype == T_SOA)
415	    {
416	      auth = soa = 1; /* inhibits auth section */
417	      found = 1;
418	      log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
419	    }
420      	  else if (qtype == T_AXFR)
421	    {
422	      struct iname *peers;
423
424	      if (peer_addr->sa.sa_family == AF_INET)
425		peer_addr->in.sin_port = 0;
426#ifdef HAVE_IPV6
427	      else
428		{
429		  peer_addr->in6.sin6_port = 0;
430		  peer_addr->in6.sin6_scope_id = 0;
431		}
432#endif
433
434	      for (peers = daemon->auth_peers; peers; peers = peers->next)
435		if (sockaddr_isequal(peer_addr, &peers->addr))
436		  break;
437
438	      /* Refuse all AXFR unless --auth-sec-servers is set */
439	      if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
440		{
441		  if (peer_addr->sa.sa_family == AF_INET)
442		    inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
443#ifdef HAVE_IPV6
444		  else
445		    inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
446#endif
447
448		  my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
449		  return 0;
450		}
451
452	      auth = 1;
453	      soa = 1; /* inhibits auth section */
454	      ns = 1; /* ensure we include NS records! */
455	      axfr = 1;
456	      found = 1;
457	      axfroffset = nameoffset;
458	      log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
459	    }
460      	  else if (qtype == T_NS)
461	    {
462	      auth = 1;
463	      ns = 1; /* inhibits auth section */
464	      found = 1;
465	      log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
466	    }
467	}
468
469      if (!option_bool(OPT_DHCP_FQDN) && cut)
470	{
471	  *cut = 0; /* remove domain part */
472
473	  if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
474	    {
475	      if (crecp->flags & F_DHCP)
476		do
477		  {
478		    nxdomain = 0;
479		    if ((crecp->flags & flag) &&
480			(local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
481		      {
482			*cut = '.'; /* restore domain part */
483			log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
484			*cut  = 0; /* remove domain part */
485			found = 1;
486			if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
487						daemon->auth_ttl, NULL, qtype, C_IN,
488						qtype == T_A ? "4" : "6", &crecp->addr))
489			  anscount++;
490		      }
491		  } while ((crecp = cache_find_by_name(crecp, name, now,  F_IPV4 | F_IPV6)));
492	    }
493
494	  *cut = '.'; /* restore domain part */
495	}
496
497      if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
498	{
499	  if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
500	    do
501	      {
502		 nxdomain = 0;
503		 if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
504		   {
505		     log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
506		     found = 1;
507		     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
508					     daemon->auth_ttl, NULL, qtype, C_IN,
509					     qtype == T_A ? "4" : "6", &crecp->addr))
510		       anscount++;
511		   }
512	      } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
513	}
514
515      if (!found)
516	log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
517
518    }
519
520  /* Add auth section */
521  if (auth && zone)
522    {
523      char *authname;
524      int newoffset, offset = 0;
525
526      if (!subnet)
527	authname = zone->domain;
528      else
529	{
530	  /* handle NS and SOA for PTR records */
531
532	  authname = name;
533
534	  if (!(subnet->flags & ADDRLIST_IPV6))
535	    {
536	      in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8;
537	      char *p = name;
538
539	      if (subnet->prefixlen >= 24)
540		p += sprintf(p, "%d.", a & 0xff);
541	      a = a >> 8;
542	      if (subnet->prefixlen >= 16 )
543		p += sprintf(p, "%d.", a & 0xff);
544	      a = a >> 8;
545	      p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
546
547	    }
548#ifdef HAVE_IPV6
549	  else
550	    {
551	      char *p = name;
552	      int i;
553
554	      for (i = subnet->prefixlen-1; i >= 0; i -= 4)
555		{
556		  int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3];
557		  p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
558		}
559	      p += sprintf(p, "ip6.arpa");
560
561	    }
562#endif
563	}
564
565      /* handle NS and SOA in auth section or for explicit queries */
566       newoffset = ansp - (unsigned char *)header;
567       if (((anscount == 0 && !ns) || soa) &&
568	  add_resource_record(header, limit, &trunc, 0, &ansp,
569			      daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
570			      authname, daemon->authserver,  daemon->hostmaster,
571			      daemon->soa_sn, daemon->soa_refresh,
572			      daemon->soa_retry, daemon->soa_expiry,
573			      daemon->auth_ttl))
574	{
575	  offset = newoffset;
576	  if (soa)
577	    anscount++;
578	  else
579	    authcount++;
580	}
581
582      if (anscount != 0 || ns)
583	{
584	  struct name_list *secondary;
585
586	  newoffset = ansp - (unsigned char *)header;
587	  if (add_resource_record(header, limit, &trunc, -offset, &ansp,
588				  daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
589	    {
590	      if (offset == 0)
591		offset = newoffset;
592	      if (ns)
593		anscount++;
594	      else
595		authcount++;
596	    }
597
598	  if (!subnet)
599	    for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
600	      if (add_resource_record(header, limit, &trunc, offset, &ansp,
601				      daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
602		{
603		  if (ns)
604		    anscount++;
605		  else
606		    authcount++;
607		}
608	}
609
610      if (axfr)
611	{
612	  for (rec = daemon->mxnames; rec; rec = rec->next)
613	    if (in_zone(zone, rec->name, &cut))
614	      {
615		if (cut)
616		   *cut = 0;
617
618		if (rec->issrv)
619		  {
620		    if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
621					    NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
622					    rec->priority, rec->weight, rec->srvport, rec->target))
623
624		      anscount++;
625		  }
626		else
627		  {
628		    if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
629					    NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
630		      anscount++;
631		  }
632
633		/* restore config data */
634		if (cut)
635		  *cut = '.';
636	      }
637
638	  for (txt = daemon->rr; txt; txt = txt->next)
639	    if (in_zone(zone, txt->name, &cut))
640	      {
641		if (cut)
642		  *cut = 0;
643
644		if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
645					NULL, txt->class, C_IN, "t",  cut ? txt->name : NULL, txt->len, txt->txt))
646		  anscount++;
647
648		/* restore config data */
649		if (cut)
650		  *cut = '.';
651	      }
652
653	  for (txt = daemon->txt; txt; txt = txt->next)
654	    if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
655	      {
656		if (cut)
657		  *cut = 0;
658
659		if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
660					NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
661		  anscount++;
662
663		/* restore config data */
664		if (cut)
665		  *cut = '.';
666	      }
667
668	  for (na = daemon->naptr; na; na = na->next)
669	    if (in_zone(zone, na->name, &cut))
670	      {
671		if (cut)
672		  *cut = 0;
673
674		if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
675					NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
676					na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
677		  anscount++;
678
679		/* restore config data */
680		if (cut)
681		  *cut = '.';
682	      }
683
684	  for (intr = daemon->int_names; intr; intr = intr->next)
685	    if (in_zone(zone, intr->name, &cut))
686	      {
687		struct addrlist *addrlist;
688
689		if (cut)
690		  *cut = 0;
691
692		for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
693		  if (!(addrlist->flags & ADDRLIST_IPV6) &&
694		      (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) &&
695		      add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
696					  daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
697		    anscount++;
698
699#ifdef HAVE_IPV6
700		for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
701		  if ((addrlist->flags & ADDRLIST_IPV6) &&
702		      (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
703		      add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
704					  daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
705		    anscount++;
706#endif
707
708		/* restore config data */
709		if (cut)
710		  *cut = '.';
711	      }
712
713	  for (a = daemon->cnames; a; a = a->next)
714	    if (in_zone(zone, a->alias, &cut))
715	      {
716		strcpy(name, a->target);
717		if (!strchr(name, '.'))
718		  {
719		    strcat(name, ".");
720		    strcat(name, zone->domain);
721		  }
722
723		if (cut)
724		  *cut = 0;
725
726		if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
727					daemon->auth_ttl, NULL,
728					T_CNAME, C_IN, "d",  cut ? a->alias : NULL, name))
729		  anscount++;
730	      }
731
732	  cache_enumerate(1);
733	  while ((crecp = cache_enumerate(0)))
734	    {
735	      if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
736		  !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
737		  (crecp->flags & F_FORWARD))
738		{
739		  if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
740		    {
741		      char *cache_name = cache_get_name(crecp);
742		      if (!strchr(cache_name, '.') &&
743			  (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
744			{
745			  qtype = T_A;
746#ifdef HAVE_IPV6
747			  if (crecp->flags & F_IPV6)
748			    qtype = T_AAAA;
749#endif
750			  if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
751						  daemon->auth_ttl, NULL, qtype, C_IN,
752						  (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
753			    anscount++;
754			}
755		    }
756
757		  if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
758		    {
759		      strcpy(name, cache_get_name(crecp));
760		      if (in_zone(zone, name, &cut) &&
761			  (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
762			{
763			  qtype = T_A;
764#ifdef HAVE_IPV6
765			  if (crecp->flags & F_IPV6)
766			    qtype = T_AAAA;
767#endif
768			   if (cut)
769			     *cut = 0;
770
771			   if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
772						   daemon->auth_ttl, NULL, qtype, C_IN,
773						   (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
774			     anscount++;
775			}
776		    }
777		}
778	    }
779
780	  /* repeat SOA as last record */
781	  if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
782				  daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
783				  daemon->authserver,  daemon->hostmaster,
784				  daemon->soa_sn, daemon->soa_refresh,
785				  daemon->soa_retry, daemon->soa_expiry,
786				  daemon->auth_ttl))
787	    anscount++;
788
789	}
790
791    }
792
793  /* done all questions, set up header and return length of result */
794  /* clear authoritative and truncated flags, set QR flag */
795  header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
796
797  if (local_query)
798    {
799      /* set RA flag */
800      header->hb4 |= HB4_RA;
801    }
802  else
803    {
804      /* clear RA flag */
805      header->hb4 &= ~HB4_RA;
806    }
807
808  /* authoritive */
809  if (auth)
810    header->hb3 |= HB3_AA;
811
812  /* truncation */
813  if (trunc)
814    header->hb3 |= HB3_TC;
815
816  if ((auth || local_query) && nxdomain)
817    SET_RCODE(header, NXDOMAIN);
818  else
819    SET_RCODE(header, NOERROR); /* no error */
820  header->ancount = htons(anscount);
821  header->nscount = htons(authcount);
822  header->arcount = htons(0);
823  return ansp - (unsigned char *)header;
824}
825
826#endif
827
828
829
830