1 /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
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.
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.
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/>.
21 static struct subnet
*filter_zone(struct auth_zone
*zone
, int flag
, struct all_addr
*addr_u
)
23 struct subnet
*subnet
;
25 for (subnet
= zone
->subnet
; subnet
; subnet
= subnet
->next
)
27 if (subnet
->is6
&& (flag
& F_IPV4
))
32 struct in_addr addr
= addr_u
->addr
.addr4
;
35 mask
.s_addr
= htonl(~((1 << (32 - subnet
->prefixlen
)) - 1));
37 if (is_same_net(addr
, subnet
->addr4
, mask
))
41 else if (is_same_net6(&(addr_u
->addr
.addr6
), &subnet
->addr6
, subnet
->prefixlen
))
49 static int filter_constructed_dhcp(struct auth_zone
*zone
, int flag
, struct all_addr
*addr_u
)
52 struct dhcp_context
*context
;
55 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
56 if ((context
->flags
& CONTEXT_CONSTRUCTED
) &&
57 !(context
->flags
& CONTEXT_NOAUTH
) &&
58 is_same_net6(&(addr_u
->addr
.addr6
), &context
->start6
, context
->prefix
))
62 return filter_zone(zone
, flag
, addr_u
) != NULL
;
65 static int in_zone(struct auth_zone
*zone
, char *name
, char **cut
)
67 size_t namelen
= strlen(name
);
68 size_t domainlen
= strlen(zone
->domain
);
73 if (namelen
>= domainlen
&&
74 hostname_isequal(zone
->domain
, &name
[namelen
- domainlen
]))
77 if (namelen
== domainlen
)
80 if (name
[namelen
- domainlen
- 1] == '.')
83 *cut
= &name
[namelen
- domainlen
- 1];
92 size_t answer_auth(struct dns_header
*header
, char *limit
, size_t qlen
, time_t now
, union mysockaddr
*peer_addr
)
94 char *name
= daemon
->namebuff
;
95 unsigned char *p
, *ansp
;
97 int nameoffset
, axfroffset
= 0;
98 int q
, anscount
= 0, authcount
= 0;
100 int auth
= 1, trunc
= 0, nxdomain
= 1, soa
= 0, ns
= 0, axfr
= 0;
101 struct auth_zone
*zone
= NULL
;
102 struct subnet
*subnet
= NULL
;
104 struct mx_srv_record
*rec
, *move
, **up
;
105 struct txt_record
*txt
;
106 struct interface_name
*intr
;
108 struct all_addr addr
;
111 if (ntohs(header
->qdcount
) == 0 || OPCODE(header
) != QUERY
)
114 /* determine end of question section (we put answers there) */
115 if (!(ansp
= skip_questions(header
, qlen
)))
116 return 0; /* bad packet */
118 /* now process each question, answers go in RRs after the question */
119 p
= (unsigned char *)(header
+1);
121 for (q
= ntohs(header
->qdcount
); q
!= 0; q
--)
123 unsigned short flag
= 0;
126 /* save pointer to name for copying into answers */
127 nameoffset
= p
- (unsigned char *)header
;
129 /* now extract name as .-concatenated string into name */
130 if (!extract_name(header
, qlen
, &p
, name
, 1, 4))
131 return 0; /* bad packet */
144 if (!(flag
= in_arpa_name_2_addr(name
, &addr
)))
147 for (zone
= daemon
->auth_zones
; zone
; zone
= zone
->next
)
148 if ((subnet
= filter_zone(zone
, flag
, &addr
)))
160 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
162 struct addrlist
*addrlist
;
164 for (addrlist
= intr
->addr4
; addrlist
; addrlist
= addrlist
->next
)
165 if (addr
.addr
.addr4
.s_addr
== addrlist
->addr
.addr
.addr4
.s_addr
)
171 while (intr
->next
&& strcmp(intr
->intr
, intr
->next
->intr
) == 0)
175 else if (flag
== F_IPV6
)
176 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
178 struct addrlist
*addrlist
;
180 for (addrlist
= intr
->addr6
; addrlist
; addrlist
= addrlist
->next
)
181 if (IN6_ARE_ADDR_EQUAL(&addr
.addr
.addr6
, &addrlist
->addr
.addr
.addr6
))
187 while (intr
->next
&& strcmp(intr
->intr
, intr
->next
->intr
) == 0)
194 if (in_zone(zone
, intr
->name
, NULL
))
197 log_query(flag
| F_REVERSE
| F_CONFIG
, intr
->name
, &addr
, NULL
);
198 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
199 daemon
->auth_ttl
, NULL
,
200 T_PTR
, C_IN
, "d", intr
->name
))
205 if ((crecp
= cache_find_by_addr(NULL
, &addr
, now
, flag
)))
207 strcpy(name
, cache_get_name(crecp
));
209 if (crecp
->flags
& F_DHCP
&& !option_bool(OPT_DHCP_FQDN
))
211 char *p
= strchr(name
, '.');
213 *p
= 0; /* must be bare name */
215 /* add external domain */
217 strcat(name
, zone
->domain
);
218 log_query(flag
| F_DHCP
| F_REVERSE
, name
, &addr
, record_source(crecp
->uid
));
220 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
221 daemon
->auth_ttl
, NULL
,
222 T_PTR
, C_IN
, "d", name
))
225 else if (crecp
->flags
& (F_DHCP
| F_HOSTS
) && in_zone(zone
, name
, NULL
))
227 log_query(crecp
->flags
& ~F_FORWARD
, name
, &addr
, record_source(crecp
->uid
));
229 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
230 daemon
->auth_ttl
, NULL
,
231 T_PTR
, C_IN
, "d", name
))
237 } while ((crecp
= cache_find_by_addr(crecp
, &addr
, now
, flag
)));
240 log_query(flag
| F_NEG
| F_NXDOMAIN
| F_REVERSE
| F_AUTH
, NULL
, &addr
, NULL
);
246 for (zone
= daemon
->auth_zones
; zone
; zone
= zone
->next
)
247 if (in_zone(zone
, name
, &cut
))
256 for (rec
= daemon
->mxnames
; rec
; rec
= rec
->next
)
257 if (!rec
->issrv
&& hostname_isequal(name
, rec
->name
))
264 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<MX>");
265 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
266 NULL
, T_MX
, C_IN
, "sd", rec
->weight
, rec
->target
))
271 for (move
= NULL
, up
= &daemon
->mxnames
, rec
= daemon
->mxnames
; rec
; rec
= rec
->next
)
272 if (rec
->issrv
&& hostname_isequal(name
, rec
->name
))
279 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<SRV>");
280 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
281 NULL
, T_SRV
, C_IN
, "sssd",
282 rec
->priority
, rec
->weight
, rec
->srvport
, rec
->target
))
287 /* unlink first SRV record found */
299 /* put first SRV record back at the end. */
306 for (txt
= daemon
->rr
; txt
; txt
= txt
->next
)
307 if (hostname_isequal(name
, txt
->name
))
310 if (txt
->class == qtype
)
313 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<RR>");
314 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
315 NULL
, txt
->class, C_IN
, "t", txt
->len
, txt
->txt
))
320 for (txt
= daemon
->txt
; txt
; txt
= txt
->next
)
321 if (txt
->class == C_IN
&& hostname_isequal(name
, txt
->name
))
327 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<TXT>");
328 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
329 NULL
, T_TXT
, C_IN
, "t", txt
->len
, txt
->txt
))
334 for (na
= daemon
->naptr
; na
; na
= na
->next
)
335 if (hostname_isequal(name
, na
->name
))
338 if (qtype
== T_NAPTR
)
341 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<NAPTR>");
342 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
343 NULL
, T_NAPTR
, C_IN
, "sszzzd",
344 na
->order
, na
->pref
, na
->flags
, na
->services
, na
->regexp
, na
->replace
))
357 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
358 if (hostname_isequal(name
, intr
->name
))
360 struct addrlist
*addrlist
;
362 addrlist
= intr
->addr4
;
365 addrlist
= intr
->addr6
;
369 for (; addrlist
; addrlist
= addrlist
->next
)
370 if (filter_constructed_dhcp(zone
, flag
, &addrlist
->addr
))
373 log_query(F_FORWARD
| F_CONFIG
| flag
, name
, &addrlist
->addr
, NULL
);
374 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
375 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
376 qtype
== T_A
? "4" : "6", &addrlist
->addr
))
381 for (a
= daemon
->cnames
; a
; a
= a
->next
)
382 if (hostname_isequal(name
, a
->alias
) )
384 log_query(F_CONFIG
| F_CNAME
, name
, NULL
, NULL
);
385 strcpy(name
, a
->target
);
386 if (!strchr(name
, '.'))
389 strcat(name
, zone
->domain
);
392 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
393 daemon
->auth_ttl
, NULL
,
394 T_CNAME
, C_IN
, "d", name
))
406 soa
= 1; /* inhibits auth section */
408 log_query(F_RRNAME
| F_AUTH
, zone
->domain
, NULL
, "<SOA>");
410 else if (qtype
== T_AXFR
)
414 if (peer_addr
->sa
.sa_family
== AF_INET
)
415 peer_addr
->in
.sin_port
= 0;
418 peer_addr
->in6
.sin6_port
= 0;
421 for (peers
= daemon
->auth_peers
; peers
; peers
= peers
->next
)
422 if (sockaddr_isequal(peer_addr
, &peers
->addr
))
425 /* Refuse all AXFR unless --auth-sec-servers is set */
426 if ((!peers
&& daemon
->auth_peers
) || !daemon
->secondary_forward_server
)
428 if (peer_addr
->sa
.sa_family
== AF_INET
)
429 inet_ntop(AF_INET
, &peer_addr
->in
.sin_addr
, daemon
->addrbuff
, ADDRSTRLEN
);
432 inet_ntop(AF_INET6
, &peer_addr
->in6
.sin6_addr
, daemon
->addrbuff
, ADDRSTRLEN
);
435 my_syslog(LOG_WARNING
, _("ignoring zone transfer request from %s"), daemon
->addrbuff
);
439 soa
= 1; /* inhibits auth section */
440 ns
= 1; /* ensure we include NS records! */
443 axfroffset
= nameoffset
;
444 log_query(F_RRNAME
| F_AUTH
, zone
->domain
, NULL
, "<AXFR>");
446 else if (qtype
== T_NS
)
448 ns
= 1; /* inhibits auth section */
450 log_query(F_RRNAME
| F_AUTH
, zone
->domain
, NULL
, "<NS>");
454 if (!option_bool(OPT_DHCP_FQDN
) && cut
)
456 *cut
= 0; /* remove domain part */
458 if (!strchr(name
, '.') && (crecp
= cache_find_by_name(NULL
, name
, now
, F_IPV4
| F_IPV6
)))
460 if (crecp
->flags
& F_DHCP
)
464 if ((crecp
->flags
& flag
) &&
465 (filter_constructed_dhcp(zone
, flag
, &(crecp
->addr
.addr
))))
467 *cut
= '.'; /* restore domain part */
468 log_query(crecp
->flags
, name
, &crecp
->addr
.addr
, record_source(crecp
->uid
));
469 *cut
= 0; /* remove domain part */
471 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
472 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
473 qtype
== T_A
? "4" : "6", &crecp
->addr
))
476 } while ((crecp
= cache_find_by_name(crecp
, name
, now
, F_IPV4
| F_IPV6
)));
479 *cut
= '.'; /* restore domain part */
482 if ((crecp
= cache_find_by_name(NULL
, name
, now
, F_IPV4
| F_IPV6
)))
484 if ((crecp
->flags
& F_HOSTS
) || (((crecp
->flags
& F_DHCP
) && option_bool(OPT_DHCP_FQDN
))))
488 if ((crecp
->flags
& flag
) && filter_constructed_dhcp(zone
, flag
, &(crecp
->addr
.addr
)))
490 log_query(crecp
->flags
, name
, &crecp
->addr
.addr
, record_source(crecp
->uid
));
492 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
493 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
494 qtype
== T_A
? "4" : "6", &crecp
->addr
))
497 } while ((crecp
= cache_find_by_name(crecp
, name
, now
, F_IPV4
| F_IPV6
)));
501 log_query(flag
| F_NEG
| (nxdomain
? F_NXDOMAIN
: 0) | F_FORWARD
| F_AUTH
, name
, NULL
, NULL
);
505 /* Add auth section */
509 int newoffset
, offset
= 0;
512 authname
= zone
->domain
;
515 /* handle NS and SOA for PTR records */
521 in_addr_t a
= ntohl(subnet
->addr4
.s_addr
) >> 8;
524 if (subnet
->prefixlen
>= 24)
525 p
+= sprintf(p
, "%d.", a
& 0xff);
527 if (subnet
->prefixlen
>= 16 )
528 p
+= sprintf(p
, "%d.", a
& 0xff);
530 p
+= sprintf(p
, "%d.in-addr.arpa", a
& 0xff);
539 for (i
= subnet
->prefixlen
-1; i
>= 0; i
-= 4)
541 int dig
= ((unsigned char *)&subnet
->addr6
)[i
>>3];
542 p
+= sprintf(p
, "%.1x.", (i
>>2) & 1 ? dig
& 15 : dig
>> 4);
544 p
+= sprintf(p
, "ip6.arpa");
550 /* handle NS and SOA in auth section or for explicit queries */
551 newoffset
= ansp
- (unsigned char *)header
;
552 if (((anscount
== 0 && !ns
) || soa
) &&
553 add_resource_record(header
, limit
, &trunc
, 0, &ansp
,
554 daemon
->auth_ttl
, NULL
, T_SOA
, C_IN
, "ddlllll",
555 authname
, daemon
->authserver
, daemon
->hostmaster
,
556 daemon
->soa_sn
, daemon
->soa_refresh
,
557 daemon
->soa_retry
, daemon
->soa_expiry
,
567 if (anscount
!= 0 || ns
)
569 struct name_list
*secondary
;
571 newoffset
= ansp
- (unsigned char *)header
;
572 if (add_resource_record(header
, limit
, &trunc
, -offset
, &ansp
,
573 daemon
->auth_ttl
, NULL
, T_NS
, C_IN
, "d", offset
== 0 ? authname
: NULL
, daemon
->authserver
))
584 for (secondary
= daemon
->secondary_forward_server
; secondary
; secondary
= secondary
->next
)
585 if (add_resource_record(header
, limit
, &trunc
, offset
, &ansp
,
586 daemon
->auth_ttl
, NULL
, T_NS
, C_IN
, "d", secondary
->name
))
597 for (rec
= daemon
->mxnames
; rec
; rec
= rec
->next
)
598 if (in_zone(zone
, rec
->name
, &cut
))
605 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
606 NULL
, T_SRV
, C_IN
, "sssd", cut
? rec
->name
: NULL
,
607 rec
->priority
, rec
->weight
, rec
->srvport
, rec
->target
))
613 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
614 NULL
, T_MX
, C_IN
, "sd", cut
? rec
->name
: NULL
, rec
->weight
, rec
->target
))
618 /* restore config data */
623 for (txt
= daemon
->rr
; txt
; txt
= txt
->next
)
624 if (in_zone(zone
, txt
->name
, &cut
))
629 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
630 NULL
, txt
->class, C_IN
, "t", cut
? txt
->name
: NULL
, txt
->len
, txt
->txt
))
633 /* restore config data */
638 for (txt
= daemon
->txt
; txt
; txt
= txt
->next
)
639 if (txt
->class == C_IN
&& in_zone(zone
, txt
->name
, &cut
))
644 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
645 NULL
, T_TXT
, C_IN
, "t", cut
? txt
->name
: NULL
, txt
->len
, txt
->txt
))
648 /* restore config data */
653 for (na
= daemon
->naptr
; na
; na
= na
->next
)
654 if (in_zone(zone
, na
->name
, &cut
))
659 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
660 NULL
, T_NAPTR
, C_IN
, "sszzzd", cut
? na
->name
: NULL
,
661 na
->order
, na
->pref
, na
->flags
, na
->services
, na
->regexp
, na
->replace
))
664 /* restore config data */
669 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
670 if (in_zone(zone
, intr
->name
, &cut
))
672 struct addrlist
*addrlist
;
677 for (addrlist
= intr
->addr4
; addrlist
; addrlist
= addrlist
->next
)
678 if (filter_constructed_dhcp(zone
, F_IPV4
, &addrlist
->addr
) &&
679 add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
680 daemon
->auth_ttl
, NULL
, T_A
, C_IN
, "4", cut
? intr
->name
: NULL
, &addrlist
->addr
))
684 for (addrlist
= intr
->addr6
; addrlist
; addrlist
= addrlist
->next
)
685 if (filter_constructed_dhcp(zone
, F_IPV6
, &addrlist
->addr
) &&
686 add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
687 daemon
->auth_ttl
, NULL
, T_AAAA
, C_IN
, "6", cut
? intr
->name
: NULL
, &addrlist
->addr
))
691 /* restore config data */
696 for (a
= daemon
->cnames
; a
; a
= a
->next
)
697 if (in_zone(zone
, a
->alias
, &cut
))
699 strcpy(name
, a
->target
);
700 if (!strchr(name
, '.'))
703 strcat(name
, zone
->domain
);
709 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
710 daemon
->auth_ttl
, NULL
,
711 T_CNAME
, C_IN
, "d", cut
? a
->alias
: NULL
, name
))
716 while ((crecp
= cache_enumerate(0)))
718 if ((crecp
->flags
& (F_IPV4
| F_IPV6
)) &&
719 !(crecp
->flags
& (F_NEG
| F_NXDOMAIN
)) &&
720 (crecp
->flags
& F_FORWARD
))
722 if ((crecp
->flags
& F_DHCP
) && !option_bool(OPT_DHCP_FQDN
))
724 char *cache_name
= cache_get_name(crecp
);
725 if (!strchr(cache_name
, '.') && filter_constructed_dhcp(zone
, (crecp
->flags
& (F_IPV6
| F_IPV4
)), &(crecp
->addr
.addr
)))
729 if (crecp
->flags
& F_IPV6
)
732 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
733 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
734 (crecp
->flags
& F_IPV4
) ? "4" : "6", cache_name
, &crecp
->addr
))
739 if ((crecp
->flags
& F_HOSTS
) || (((crecp
->flags
& F_DHCP
) && option_bool(OPT_DHCP_FQDN
))))
741 strcpy(name
, cache_get_name(crecp
));
742 if (in_zone(zone
, name
, &cut
) && filter_constructed_dhcp(zone
, (crecp
->flags
& (F_IPV6
| F_IPV4
)), &(crecp
->addr
.addr
)))
746 if (crecp
->flags
& F_IPV6
)
752 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
753 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
754 (crecp
->flags
& F_IPV4
) ? "4" : "6", cut
? name
: NULL
, &crecp
->addr
))
761 /* repeat SOA as last record */
762 if (add_resource_record(header
, limit
, &trunc
, axfroffset
, &ansp
,
763 daemon
->auth_ttl
, NULL
, T_SOA
, C_IN
, "ddlllll",
764 daemon
->authserver
, daemon
->hostmaster
,
765 daemon
->soa_sn
, daemon
->soa_refresh
,
766 daemon
->soa_retry
, daemon
->soa_expiry
,
774 /* done all questions, set up header and return length of result */
775 /* clear authoritative and truncated flags, set QR flag */
776 header
->hb3
= (header
->hb3
& ~(HB3_AA
| HB3_TC
)) | HB3_QR
;
778 header
->hb4
&= ~HB4_RA
;
782 header
->hb3
|= HB3_AA
;
786 header
->hb3
|= HB3_TC
;
788 if (anscount
== 0 && auth
&& nxdomain
)
789 SET_RCODE(header
, NXDOMAIN
);
791 SET_RCODE(header
, NOERROR
); /* no error */
792 header
->ancount
= htons(anscount
);
793 header
->nscount
= htons(authcount
);
794 header
->arcount
= htons(0);
795 return ansp
- (unsigned char *)header
;