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 addrlist
*find_subnet(struct auth_zone
*zone
, int flag
, struct all_addr
*addr_u
)
23 struct addrlist
*subnet
;
25 for (subnet
= zone
->subnet
; subnet
; subnet
= subnet
->next
)
27 if (!(subnet
->flags
& ADDRLIST_IPV6
))
29 struct in_addr netmask
, addr
= addr_u
->addr
.addr4
;
34 netmask
.s_addr
= htonl(~((1 << (32 - subnet
->prefixlen
)) - 1));
36 if (is_same_net(addr
, subnet
->addr
.addr
.addr4
, netmask
))
40 else if (is_same_net6(&(addr_u
->addr
.addr6
), &subnet
->addr
.addr
.addr6
, subnet
->prefixlen
))
48 static int filter_zone(struct auth_zone
*zone
, int flag
, struct all_addr
*addr_u
)
50 /* No zones specified, no filter */
54 return find_subnet(zone
, flag
, addr_u
) != NULL
;
57 int in_zone(struct auth_zone
*zone
, char *name
, char **cut
)
59 size_t namelen
= strlen(name
);
60 size_t domainlen
= strlen(zone
->domain
);
65 if (namelen
>= domainlen
&&
66 hostname_isequal(zone
->domain
, &name
[namelen
- domainlen
]))
69 if (namelen
== domainlen
)
72 if (name
[namelen
- domainlen
- 1] == '.')
75 *cut
= &name
[namelen
- domainlen
- 1];
84 size_t answer_auth(struct dns_header
*header
, char *limit
, size_t qlen
, time_t now
, union mysockaddr
*peer_addr
, int local_query
)
86 char *name
= daemon
->namebuff
;
87 unsigned char *p
, *ansp
;
89 int nameoffset
, axfroffset
= 0;
90 int q
, anscount
= 0, authcount
= 0;
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
;
96 struct mx_srv_record
*rec
, *move
, **up
;
97 struct txt_record
*txt
;
98 struct interface_name
*intr
;
100 struct all_addr addr
;
103 if (ntohs(header
->qdcount
) == 0 || OPCODE(header
) != QUERY
)
106 /* determine end of question section (we put answers there) */
107 if (!(ansp
= skip_questions(header
, qlen
)))
108 return 0; /* bad packet */
110 /* now process each question, answers go in RRs after the question */
111 p
= (unsigned char *)(header
+1);
113 for (q
= ntohs(header
->qdcount
); q
!= 0; q
--)
115 unsigned short flag
= 0;
118 /* save pointer to name for copying into answers */
119 nameoffset
= p
- (unsigned char *)header
;
121 /* now extract name as .-concatenated string into name */
122 if (!extract_name(header
, qlen
, &p
, name
, 1, 4))
123 return 0; /* bad packet */
136 if (!(flag
= in_arpa_name_2_addr(name
, &addr
)))
141 for (zone
= daemon
->auth_zones
; zone
; zone
= zone
->next
)
142 if ((subnet
= find_subnet(zone
, flag
, &addr
)))
155 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
157 struct addrlist
*addrlist
;
159 for (addrlist
= intr
->addr
; addrlist
; addrlist
= addrlist
->next
)
160 if (!(addrlist
->flags
& ADDRLIST_IPV6
) && addr
.addr
.addr4
.s_addr
== addrlist
->addr
.addr
.addr4
.s_addr
)
166 while (intr
->next
&& strcmp(intr
->intr
, intr
->next
->intr
) == 0)
170 else if (flag
== F_IPV6
)
171 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
173 struct addrlist
*addrlist
;
175 for (addrlist
= intr
->addr
; addrlist
; addrlist
= addrlist
->next
)
176 if ((addrlist
->flags
& ADDRLIST_IPV6
) && IN6_ARE_ADDR_EQUAL(&addr
.addr
.addr6
, &addrlist
->addr
.addr
.addr6
))
182 while (intr
->next
&& strcmp(intr
->intr
, intr
->next
->intr
) == 0)
189 if (in_zone(zone
, intr
->name
, NULL
))
192 log_query(flag
| F_REVERSE
| F_CONFIG
, intr
->name
, &addr
, NULL
);
193 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
194 daemon
->auth_ttl
, NULL
,
195 T_PTR
, C_IN
, "d", intr
->name
))
200 if ((crecp
= cache_find_by_addr(NULL
, &addr
, now
, flag
)))
202 strcpy(name
, cache_get_name(crecp
));
204 if (crecp
->flags
& F_DHCP
&& !option_bool(OPT_DHCP_FQDN
))
206 char *p
= strchr(name
, '.');
208 *p
= 0; /* must be bare name */
210 /* add external domain */
212 strcat(name
, zone
->domain
);
213 log_query(flag
| F_DHCP
| F_REVERSE
, name
, &addr
, record_source(crecp
->uid
));
215 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
216 daemon
->auth_ttl
, NULL
,
217 T_PTR
, C_IN
, "d", name
))
220 else if (crecp
->flags
& (F_DHCP
| F_HOSTS
) && in_zone(zone
, name
, NULL
))
222 log_query(crecp
->flags
& ~F_FORWARD
, name
, &addr
, record_source(crecp
->uid
));
224 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
225 daemon
->auth_ttl
, NULL
,
226 T_PTR
, C_IN
, "d", name
))
232 } while ((crecp
= cache_find_by_addr(crecp
, &addr
, now
, flag
)));
235 log_query(flag
| F_NEG
| F_NXDOMAIN
| F_REVERSE
| F_AUTH
, NULL
, &addr
, NULL
);
241 for (zone
= daemon
->auth_zones
; zone
; zone
= zone
->next
)
242 if (in_zone(zone
, name
, &cut
))
251 for (rec
= daemon
->mxnames
; rec
; rec
= rec
->next
)
252 if (!rec
->issrv
&& hostname_isequal(name
, rec
->name
))
259 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<MX>");
260 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
261 NULL
, T_MX
, C_IN
, "sd", rec
->weight
, rec
->target
))
266 for (move
= NULL
, up
= &daemon
->mxnames
, rec
= daemon
->mxnames
; rec
; rec
= rec
->next
)
267 if (rec
->issrv
&& hostname_isequal(name
, rec
->name
))
274 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<SRV>");
275 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
276 NULL
, T_SRV
, C_IN
, "sssd",
277 rec
->priority
, rec
->weight
, rec
->srvport
, rec
->target
))
282 /* unlink first SRV record found */
294 /* put first SRV record back at the end. */
301 for (txt
= daemon
->rr
; txt
; txt
= txt
->next
)
302 if (hostname_isequal(name
, txt
->name
))
305 if (txt
->class == qtype
)
308 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<RR>");
309 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
310 NULL
, txt
->class, C_IN
, "t", txt
->len
, txt
->txt
))
315 for (txt
= daemon
->txt
; txt
; txt
= txt
->next
)
316 if (txt
->class == C_IN
&& hostname_isequal(name
, txt
->name
))
322 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<TXT>");
323 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
324 NULL
, T_TXT
, C_IN
, "t", txt
->len
, txt
->txt
))
329 for (na
= daemon
->naptr
; na
; na
= na
->next
)
330 if (hostname_isequal(name
, na
->name
))
333 if (qtype
== T_NAPTR
)
336 log_query(F_CONFIG
| F_RRNAME
, name
, NULL
, "<NAPTR>");
337 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
, daemon
->auth_ttl
,
338 NULL
, T_NAPTR
, C_IN
, "sszzzd",
339 na
->order
, na
->pref
, na
->flags
, na
->services
, na
->regexp
, na
->replace
))
352 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
353 if (hostname_isequal(name
, intr
->name
))
355 struct addrlist
*addrlist
;
360 for (addrlist
= intr
->addr
; addrlist
; addrlist
= addrlist
->next
)
361 if (((addrlist
->flags
& ADDRLIST_IPV6
) ? T_AAAA
: T_A
) == qtype
&&
362 (local_query
|| filter_zone(zone
, flag
, &addrlist
->addr
)))
365 log_query(F_FORWARD
| F_CONFIG
| flag
, name
, &addrlist
->addr
, NULL
);
366 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
367 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
368 qtype
== T_A
? "4" : "6", &addrlist
->addr
))
373 for (a
= daemon
->cnames
; a
; a
= a
->next
)
374 if (hostname_isequal(name
, a
->alias
) )
376 log_query(F_CONFIG
| F_CNAME
, name
, NULL
, NULL
);
377 strcpy(name
, a
->target
);
378 if (!strchr(name
, '.'))
381 strcat(name
, zone
->domain
);
384 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
385 daemon
->auth_ttl
, &nameoffset
,
386 T_CNAME
, C_IN
, "d", name
))
398 auth
= soa
= 1; /* inhibits auth section */
400 log_query(F_RRNAME
| F_AUTH
, zone
->domain
, NULL
, "<SOA>");
402 else if (qtype
== T_AXFR
)
406 if (peer_addr
->sa
.sa_family
== AF_INET
)
407 peer_addr
->in
.sin_port
= 0;
410 peer_addr
->in6
.sin6_port
= 0;
413 for (peers
= daemon
->auth_peers
; peers
; peers
= peers
->next
)
414 if (sockaddr_isequal(peer_addr
, &peers
->addr
))
417 /* Refuse all AXFR unless --auth-sec-servers is set */
418 if ((!peers
&& daemon
->auth_peers
) || !daemon
->secondary_forward_server
)
420 if (peer_addr
->sa
.sa_family
== AF_INET
)
421 inet_ntop(AF_INET
, &peer_addr
->in
.sin_addr
, daemon
->addrbuff
, ADDRSTRLEN
);
424 inet_ntop(AF_INET6
, &peer_addr
->in6
.sin6_addr
, daemon
->addrbuff
, ADDRSTRLEN
);
427 my_syslog(LOG_WARNING
, _("ignoring zone transfer request from %s"), daemon
->addrbuff
);
432 soa
= 1; /* inhibits auth section */
433 ns
= 1; /* ensure we include NS records! */
436 axfroffset
= nameoffset
;
437 log_query(F_RRNAME
| F_AUTH
, zone
->domain
, NULL
, "<AXFR>");
439 else if (qtype
== T_NS
)
442 ns
= 1; /* inhibits auth section */
444 log_query(F_RRNAME
| F_AUTH
, zone
->domain
, NULL
, "<NS>");
448 if (!option_bool(OPT_DHCP_FQDN
) && cut
)
450 *cut
= 0; /* remove domain part */
452 if (!strchr(name
, '.') && (crecp
= cache_find_by_name(NULL
, name
, now
, F_IPV4
| F_IPV6
)))
454 if (crecp
->flags
& F_DHCP
)
458 if ((crecp
->flags
& flag
) &&
459 (local_query
|| filter_zone(zone
, flag
, &(crecp
->addr
.addr
))))
461 *cut
= '.'; /* restore domain part */
462 log_query(crecp
->flags
, name
, &crecp
->addr
.addr
, record_source(crecp
->uid
));
463 *cut
= 0; /* remove domain part */
465 if (add_resource_record(header
, limit
, &trunc
, nameoffset
, &ansp
,
466 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
467 qtype
== T_A
? "4" : "6", &crecp
->addr
))
470 } while ((crecp
= cache_find_by_name(crecp
, name
, now
, F_IPV4
| F_IPV6
)));
473 *cut
= '.'; /* restore domain part */
476 if ((crecp
= cache_find_by_name(NULL
, name
, now
, F_IPV4
| F_IPV6
)))
478 if ((crecp
->flags
& F_HOSTS
) || (((crecp
->flags
& F_DHCP
) && option_bool(OPT_DHCP_FQDN
))))
482 if ((crecp
->flags
& flag
) && (local_query
|| filter_zone(zone
, flag
, &(crecp
->addr
.addr
))))
484 log_query(crecp
->flags
, name
, &crecp
->addr
.addr
, record_source(crecp
->uid
));
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
))
491 } while ((crecp
= cache_find_by_name(crecp
, name
, now
, F_IPV4
| F_IPV6
)));
495 log_query(flag
| F_NEG
| (nxdomain
? F_NXDOMAIN
: 0) | F_FORWARD
| F_AUTH
, name
, NULL
, NULL
);
499 /* Add auth section */
503 int newoffset
, offset
= 0;
506 authname
= zone
->domain
;
509 /* handle NS and SOA for PTR records */
513 if (!(subnet
->flags
& ADDRLIST_IPV6
))
515 in_addr_t a
= ntohl(subnet
->addr
.addr
.addr4
.s_addr
) >> 8;
518 if (subnet
->prefixlen
>= 24)
519 p
+= sprintf(p
, "%d.", a
& 0xff);
521 if (subnet
->prefixlen
>= 16 )
522 p
+= sprintf(p
, "%d.", a
& 0xff);
524 p
+= sprintf(p
, "%d.in-addr.arpa", a
& 0xff);
533 for (i
= subnet
->prefixlen
-1; i
>= 0; i
-= 4)
535 int dig
= ((unsigned char *)&subnet
->addr
.addr
.addr6
)[i
>>3];
536 p
+= sprintf(p
, "%.1x.", (i
>>2) & 1 ? dig
& 15 : dig
>> 4);
538 p
+= sprintf(p
, "ip6.arpa");
544 /* handle NS and SOA in auth section or for explicit queries */
545 newoffset
= ansp
- (unsigned char *)header
;
546 if (((anscount
== 0 && !ns
) || soa
) &&
547 add_resource_record(header
, limit
, &trunc
, 0, &ansp
,
548 daemon
->auth_ttl
, NULL
, T_SOA
, C_IN
, "ddlllll",
549 authname
, daemon
->authserver
, daemon
->hostmaster
,
550 daemon
->soa_sn
, daemon
->soa_refresh
,
551 daemon
->soa_retry
, daemon
->soa_expiry
,
561 if (anscount
!= 0 || ns
)
563 struct name_list
*secondary
;
565 newoffset
= ansp
- (unsigned char *)header
;
566 if (add_resource_record(header
, limit
, &trunc
, -offset
, &ansp
,
567 daemon
->auth_ttl
, NULL
, T_NS
, C_IN
, "d", offset
== 0 ? authname
: NULL
, daemon
->authserver
))
578 for (secondary
= daemon
->secondary_forward_server
; secondary
; secondary
= secondary
->next
)
579 if (add_resource_record(header
, limit
, &trunc
, offset
, &ansp
,
580 daemon
->auth_ttl
, NULL
, T_NS
, C_IN
, "d", secondary
->name
))
591 for (rec
= daemon
->mxnames
; rec
; rec
= rec
->next
)
592 if (in_zone(zone
, rec
->name
, &cut
))
599 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
600 NULL
, T_SRV
, C_IN
, "sssd", cut
? rec
->name
: NULL
,
601 rec
->priority
, rec
->weight
, rec
->srvport
, rec
->target
))
607 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
608 NULL
, T_MX
, C_IN
, "sd", cut
? rec
->name
: NULL
, rec
->weight
, rec
->target
))
612 /* restore config data */
617 for (txt
= daemon
->rr
; txt
; txt
= txt
->next
)
618 if (in_zone(zone
, txt
->name
, &cut
))
623 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
624 NULL
, txt
->class, C_IN
, "t", cut
? txt
->name
: NULL
, txt
->len
, txt
->txt
))
627 /* restore config data */
632 for (txt
= daemon
->txt
; txt
; txt
= txt
->next
)
633 if (txt
->class == C_IN
&& in_zone(zone
, txt
->name
, &cut
))
638 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
639 NULL
, T_TXT
, C_IN
, "t", cut
? txt
->name
: NULL
, txt
->len
, txt
->txt
))
642 /* restore config data */
647 for (na
= daemon
->naptr
; na
; na
= na
->next
)
648 if (in_zone(zone
, na
->name
, &cut
))
653 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
, daemon
->auth_ttl
,
654 NULL
, T_NAPTR
, C_IN
, "sszzzd", cut
? na
->name
: NULL
,
655 na
->order
, na
->pref
, na
->flags
, na
->services
, na
->regexp
, na
->replace
))
658 /* restore config data */
663 for (intr
= daemon
->int_names
; intr
; intr
= intr
->next
)
664 if (in_zone(zone
, intr
->name
, &cut
))
666 struct addrlist
*addrlist
;
671 for (addrlist
= intr
->addr
; addrlist
; addrlist
= addrlist
->next
)
672 if (!(addrlist
->flags
& ADDRLIST_IPV6
) &&
673 (local_query
|| filter_zone(zone
, F_IPV4
, &addrlist
->addr
)) &&
674 add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
675 daemon
->auth_ttl
, NULL
, T_A
, C_IN
, "4", cut
? intr
->name
: NULL
, &addrlist
->addr
))
679 for (addrlist
= intr
->addr
; addrlist
; addrlist
= addrlist
->next
)
680 if ((addrlist
->flags
& ADDRLIST_IPV6
) &&
681 (local_query
|| filter_zone(zone
, F_IPV6
, &addrlist
->addr
)) &&
682 add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
683 daemon
->auth_ttl
, NULL
, T_AAAA
, C_IN
, "6", cut
? intr
->name
: NULL
, &addrlist
->addr
))
687 /* restore config data */
692 for (a
= daemon
->cnames
; a
; a
= a
->next
)
693 if (in_zone(zone
, a
->alias
, &cut
))
695 strcpy(name
, a
->target
);
696 if (!strchr(name
, '.'))
699 strcat(name
, zone
->domain
);
705 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
706 daemon
->auth_ttl
, NULL
,
707 T_CNAME
, C_IN
, "d", cut
? a
->alias
: NULL
, name
))
712 while ((crecp
= cache_enumerate(0)))
714 if ((crecp
->flags
& (F_IPV4
| F_IPV6
)) &&
715 !(crecp
->flags
& (F_NEG
| F_NXDOMAIN
)) &&
716 (crecp
->flags
& F_FORWARD
))
718 if ((crecp
->flags
& F_DHCP
) && !option_bool(OPT_DHCP_FQDN
))
720 char *cache_name
= cache_get_name(crecp
);
721 if (!strchr(cache_name
, '.') &&
722 (local_query
|| filter_zone(zone
, (crecp
->flags
& (F_IPV6
| F_IPV4
)), &(crecp
->addr
.addr
))))
726 if (crecp
->flags
& F_IPV6
)
729 if (add_resource_record(header
, limit
, &trunc
, -axfroffset
, &ansp
,
730 daemon
->auth_ttl
, NULL
, qtype
, C_IN
,
731 (crecp
->flags
& F_IPV4
) ? "4" : "6", cache_name
, &crecp
->addr
))
736 if ((crecp
->flags
& F_HOSTS
) || (((crecp
->flags
& F_DHCP
) && option_bool(OPT_DHCP_FQDN
))))
738 strcpy(name
, cache_get_name(crecp
));
739 if (in_zone(zone
, name
, &cut
) &&
740 (local_query
|| filter_zone(zone
, (crecp
->flags
& (F_IPV6
| F_IPV4
)), &(crecp
->addr
.addr
))))
744 if (crecp
->flags
& F_IPV6
)
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", cut
? name
: NULL
, &crecp
->addr
))
759 /* repeat SOA as last record */
760 if (add_resource_record(header
, limit
, &trunc
, axfroffset
, &ansp
,
761 daemon
->auth_ttl
, NULL
, T_SOA
, C_IN
, "ddlllll",
762 daemon
->authserver
, daemon
->hostmaster
,
763 daemon
->soa_sn
, daemon
->soa_refresh
,
764 daemon
->soa_retry
, daemon
->soa_expiry
,
772 /* done all questions, set up header and return length of result */
773 /* clear authoritative and truncated flags, set QR flag */
774 header
->hb3
= (header
->hb3
& ~(HB3_AA
| HB3_TC
)) | HB3_QR
;
779 header
->hb4
|= HB4_RA
;
784 header
->hb4
&= ~HB4_RA
;
789 header
->hb3
|= HB3_AA
;
793 header
->hb3
|= HB3_TC
;
795 if ((auth
|| local_query
) && nxdomain
)
796 SET_RCODE(header
, NXDOMAIN
);
798 SET_RCODE(header
, NOERROR
); /* no error */
799 header
->ancount
= htons(anscount
);
800 header
->nscount
= htons(authcount
);
801 header
->arcount
= htons(0);
802 return ansp
- (unsigned char *)header
;