blob: 744658097c39d75c6ed947ea9919728334c5263a [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley4f7b3042012-11-28 21:27:02 +00002
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
Simon Kelley4820dce2012-12-18 18:30:30 +000019#ifdef HAVE_AUTH
Simon Kelleyb75e9362012-12-07 11:50:41 +000020
Simon Kelleycc921df2019-01-02 22:48:59 +000021static struct addrlist *find_addrlist(struct addrlist *list, int flag, union all_addr *addr_u)
Mathias Kresin094bfae2016-07-24 14:15:22 +010022{
23 do {
24 if (!(list->flags & ADDRLIST_IPV6))
25 {
Simon Kelleycc921df2019-01-02 22:48:59 +000026 struct in_addr netmask, addr = addr_u->addr4;
Mathias Kresin094bfae2016-07-24 14:15:22 +010027
28 if (!(flag & F_IPV4))
29 continue;
30
31 netmask.s_addr = htonl(~(in_addr_t)0 << (32 - list->prefixlen));
32
Simon Kelleycc921df2019-01-02 22:48:59 +000033 if (is_same_net(addr, list->addr.addr4, netmask))
Mathias Kresin094bfae2016-07-24 14:15:22 +010034 return list;
35 }
Simon Kelleycc921df2019-01-02 22:48:59 +000036 else if (is_same_net6(&(addr_u->addr6), &list->addr.addr6, list->prefixlen))
Mathias Kresin094bfae2016-07-24 14:15:22 +010037 return list;
Mathias Kresin094bfae2016-07-24 14:15:22 +010038
39 } while ((list = list->next));
40
41 return NULL;
42}
43
Simon Kelleycc921df2019-01-02 22:48:59 +000044static struct addrlist *find_subnet(struct auth_zone *zone, int flag, union all_addr *addr_u)
Simon Kelley4f7b3042012-11-28 21:27:02 +000045{
Mathias Kresin094bfae2016-07-24 14:15:22 +010046 if (!zone->subnet)
47 return NULL;
48
49 return find_addrlist(zone->subnet, flag, addr_u);
50}
Simon Kelley4f7b3042012-11-28 21:27:02 +000051
Simon Kelleycc921df2019-01-02 22:48:59 +000052static struct addrlist *find_exclude(struct auth_zone *zone, int flag, union all_addr *addr_u)
Mathias Kresin094bfae2016-07-24 14:15:22 +010053{
54 if (!zone->exclude)
55 return NULL;
56
57 return find_addrlist(zone->exclude, flag, addr_u);
Simon Kelley4f7b3042012-11-28 21:27:02 +000058}
59
Simon Kelleycc921df2019-01-02 22:48:59 +000060static int filter_zone(struct auth_zone *zone, int flag, union all_addr *addr_u)
Simon Kelleyc50f25a2013-11-21 11:29:27 +000061{
Mathias Kresin094bfae2016-07-24 14:15:22 +010062 if (find_exclude(zone, flag, addr_u))
63 return 0;
64
65 /* No subnets specified, no filter */
Simon Kelleyc50f25a2013-11-21 11:29:27 +000066 if (!zone->subnet)
67 return 1;
68
69 return find_subnet(zone, flag, addr_u) != NULL;
70}
71
Simon Kelleyb485ed92013-10-18 22:00:39 +010072int in_zone(struct auth_zone *zone, char *name, char **cut)
Simon Kelleyb75e9362012-12-07 11:50:41 +000073{
74 size_t namelen = strlen(name);
75 size_t domainlen = strlen(zone->domain);
76
77 if (cut)
78 *cut = NULL;
79
80 if (namelen >= domainlen &&
81 hostname_isequal(zone->domain, &name[namelen - domainlen]))
82 {
83
84 if (namelen == domainlen)
85 return 1;
86
87 if (name[namelen - domainlen - 1] == '.')
88 {
89 if (cut)
90 *cut = &name[namelen - domainlen - 1];
91 return 1;
92 }
93 }
94
95 return 0;
96}
Simon Kelley4f7b3042012-11-28 21:27:02 +000097
98
Simon Kelleyfa14bec2015-12-20 17:12:16 +000099size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
100 int local_query, int do_bit, int have_pseudoheader)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000101{
102 char *name = daemon->namebuff;
103 unsigned char *p, *ansp;
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100104 int qtype, qclass, rc;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000105 int nameoffset, axfroffset = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000106 int q, anscount = 0, authcount = 0;
107 struct crec *crecp;
Simon Kelley19b16892013-10-20 10:19:39 +0100108 int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000109 struct auth_zone *zone = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +0000110 struct addrlist *subnet = NULL;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000111 char *cut;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000112 struct mx_srv_record *rec, *move, **up;
113 struct txt_record *txt;
114 struct interface_name *intr;
115 struct naptr *na;
Simon Kelleycc921df2019-01-02 22:48:59 +0000116 union all_addr addr;
Simon Kelleyb637d782016-12-13 16:44:11 +0000117 struct cname *a, *candidate;
118 unsigned int wclen;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000119
Simon Kelley4f7b3042012-11-28 21:27:02 +0000120 if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
121 return 0;
Simon Kelley6008bdb2013-10-21 21:47:03 +0100122
Simon Kelley4f7b3042012-11-28 21:27:02 +0000123 /* determine end of question section (we put answers there) */
124 if (!(ansp = skip_questions(header, qlen)))
125 return 0; /* bad packet */
126
127 /* now process each question, answers go in RRs after the question */
128 p = (unsigned char *)(header+1);
129
130 for (q = ntohs(header->qdcount); q != 0; q--)
131 {
Simon Kelley5b99eae2019-01-06 23:09:50 +0000132 unsigned int flag = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000133 int found = 0;
Simon Kelleyb637d782016-12-13 16:44:11 +0000134 int cname_wildcard = 0;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000135
Simon Kelley4f7b3042012-11-28 21:27:02 +0000136 /* save pointer to name for copying into answers */
137 nameoffset = p - (unsigned char *)header;
138
139 /* now extract name as .-concatenated string into name */
140 if (!extract_name(header, qlen, &p, name, 1, 4))
141 return 0; /* bad packet */
142
143 GETSHORT(qtype, p);
144 GETSHORT(qclass, p);
145
146 if (qclass != C_IN)
Simon Kelleyf8abe0c2012-12-15 11:59:25 +0000147 {
148 auth = 0;
149 continue;
150 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000151
Simon Kelley78c61842015-04-16 15:05:30 +0100152 if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
153 (flag = in_arpa_name_2_addr(name, &addr)) &&
154 !local_query)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000155 {
Simon Kelley78c61842015-04-16 15:05:30 +0100156 for (zone = daemon->auth_zones; zone; zone = zone->next)
157 if ((subnet = find_subnet(zone, flag, &addr)))
158 break;
159
160 if (!zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000161 {
Simon Kelley78c61842015-04-16 15:05:30 +0100162 auth = 0;
163 continue;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000164 }
Simon Kelley78c61842015-04-16 15:05:30 +0100165 else if (qtype == T_SOA)
166 soa = 1, found = 1;
167 else if (qtype == T_NS)
168 ns = 1, found = 1;
169 }
Simon Kelley19b16892013-10-20 10:19:39 +0100170
Simon Kelley78c61842015-04-16 15:05:30 +0100171 if (qtype == T_PTR && flag)
172 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100173 intr = NULL;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000174
Simon Kelley115ac3e2013-05-20 11:28:32 +0100175 if (flag == F_IPV4)
176 for (intr = daemon->int_names; intr; intr = intr->next)
177 {
178 struct addrlist *addrlist;
179
Simon Kelley376d48c2013-11-13 13:04:30 +0000180 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelleycc921df2019-01-02 22:48:59 +0000181 if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr4.s_addr == addrlist->addr.addr4.s_addr)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100182 break;
183
184 if (addrlist)
185 break;
186 else
187 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
188 intr = intr->next;
189 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100190 else if (flag == F_IPV6)
191 for (intr = daemon->int_names; intr; intr = intr->next)
192 {
193 struct addrlist *addrlist;
194
Simon Kelley376d48c2013-11-13 13:04:30 +0000195 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelleycc921df2019-01-02 22:48:59 +0000196 if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr6, &addrlist->addr.addr6))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100197 break;
198
199 if (addrlist)
200 break;
201 else
202 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
203 intr = intr->next;
204 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100205
206 if (intr)
207 {
Simon Kelley38440b22015-04-12 21:52:47 +0100208 if (local_query || in_zone(zone, intr->name, NULL))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100209 {
210 found = 1;
Simon Kelley8ab91e92013-10-21 20:50:04 +0100211 log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelley115ac3e2013-05-20 11:28:32 +0100212 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
213 daemon->auth_ttl, NULL,
214 T_PTR, C_IN, "d", intr->name))
215 anscount++;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000216 }
217 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100218
Simon Kelley4f7b3042012-11-28 21:27:02 +0000219 if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
220 do {
221 strcpy(name, cache_get_name(crecp));
222
223 if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
224 {
225 char *p = strchr(name, '.');
226 if (p)
227 *p = 0; /* must be bare name */
228
229 /* add external domain */
Simon Kelley38440b22015-04-12 21:52:47 +0100230 if (zone)
231 {
232 strcat(name, ".");
233 strcat(name, zone->domain);
234 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000235 log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000236 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000237 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
238 daemon->auth_ttl, NULL,
239 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000240 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000241 }
Simon Kelley38440b22015-04-12 21:52:47 +0100242 else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000243 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000244 log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000245 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000246 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
247 daemon->auth_ttl, NULL,
248 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000249 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000250 }
251 else
252 continue;
253
254 } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000255
Simon Kelley10068602014-04-03 21:16:40 +0100256 if (found)
257 nxdomain = 0;
258 else
259 log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000260
261 continue;
262 }
263
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000264 cname_restart:
Simon Kelley78c61842015-04-16 15:05:30 +0100265 if (found)
266 /* NS and SOA .arpa requests have set found above. */
267 cut = NULL;
268 else
Simon Kelley4f7b3042012-11-28 21:27:02 +0000269 {
Simon Kelley78c61842015-04-16 15:05:30 +0100270 for (zone = daemon->auth_zones; zone; zone = zone->next)
271 if (in_zone(zone, name, &cut))
272 break;
273
274 if (!zone)
275 {
276 auth = 0;
277 continue;
278 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000279 }
280
Simon Kelley8273ea52012-11-29 21:12:33 +0000281 for (rec = daemon->mxnames; rec; rec = rec->next)
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100282 if (!rec->issrv && (rc = hostname_issubdomain(name, rec->name)))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000283 {
284 nxdomain = 0;
285
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100286 if (rc == 2 && qtype == T_MX)
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000287 {
288 found = 1;
289 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
290 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
291 NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
292 anscount++;
293 }
294 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000295
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000296 for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100297 if (rec->issrv && (rc = hostname_issubdomain(name, rec->name)))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000298 {
299 nxdomain = 0;
300
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100301 if (rc == 2 && qtype == T_SRV)
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000302 {
303 found = 1;
304 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
305 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
306 NULL, T_SRV, C_IN, "sssd",
307 rec->priority, rec->weight, rec->srvport, rec->target))
308
309 anscount++;
310 }
311
312 /* unlink first SRV record found */
313 if (!move)
314 {
315 move = rec;
316 *up = rec->next;
317 }
318 else
319 up = &rec->next;
320 }
321 else
322 up = &rec->next;
323
324 /* put first SRV record back at the end. */
325 if (move)
326 {
327 *up = move;
328 move->next = NULL;
329 }
330
331 for (txt = daemon->rr; txt; txt = txt->next)
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100332 if ((rc = hostname_issubdomain(name, txt->name)))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000333 {
334 nxdomain = 0;
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100335 if (rc == 2 && txt->class == qtype)
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000336 {
337 found = 1;
Simon Kelleyb758b672018-08-23 21:41:23 +0100338 log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, txt->class));
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000339 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
340 NULL, txt->class, C_IN, "t", txt->len, txt->txt))
341 anscount++;
342 }
343 }
344
345 for (txt = daemon->txt; txt; txt = txt->next)
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100346 if (txt->class == C_IN && (rc = hostname_issubdomain(name, txt->name)))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000347 {
348 nxdomain = 0;
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100349 if (rc == 2 && qtype == T_TXT)
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000350 {
351 found = 1;
352 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
353 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
354 NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
355 anscount++;
356 }
357 }
358
359 for (na = daemon->naptr; na; na = na->next)
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100360 if ((rc = hostname_issubdomain(name, na->name)))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000361 {
362 nxdomain = 0;
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100363 if (rc == 2 && qtype == T_NAPTR)
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000364 {
365 found = 1;
366 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
367 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
368 NULL, T_NAPTR, C_IN, "sszzzd",
369 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
370 anscount++;
371 }
372 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100373
374 if (qtype == T_A)
375 flag = F_IPV4;
376
Simon Kelley115ac3e2013-05-20 11:28:32 +0100377 if (qtype == T_AAAA)
378 flag = F_IPV6;
Simon Kelley115ac3e2013-05-20 11:28:32 +0100379
Simon Kelley8ab91e92013-10-21 20:50:04 +0100380 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100381 if ((rc = hostname_issubdomain(name, intr->name)))
Simon Kelley8ab91e92013-10-21 20:50:04 +0100382 {
383 struct addrlist *addrlist;
384
Simon Kelley8ab91e92013-10-21 20:50:04 +0100385 nxdomain = 0;
386
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100387 if (rc == 2 && flag)
Simon Kelley376d48c2013-11-13 13:04:30 +0000388 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
389 if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
390 (local_query || filter_zone(zone, flag, &addrlist->addr)))
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100391 {
Simon Kelley47669362014-12-17 12:41:56 +0000392 if (addrlist->flags & ADDRLIST_REVONLY)
393 continue;
Simon Kelleyee875042018-10-23 22:10:17 +0100394
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100395 found = 1;
396 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
397 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
398 daemon->auth_ttl, NULL, qtype, C_IN,
399 qtype == T_A ? "4" : "6", &addrlist->addr))
400 anscount++;
401 }
402 }
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000403
Simon Kelleye1ff4192012-12-09 17:08:47 +0000404 if (!cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000405 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000406 nxdomain = 0;
407
408 if (qtype == T_SOA)
409 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100410 auth = soa = 1; /* inhibits auth section */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000411 found = 1;
412 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
413 }
414 else if (qtype == T_AXFR)
415 {
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000416 struct iname *peers;
417
418 if (peer_addr->sa.sa_family == AF_INET)
419 peer_addr->in.sin_port = 0;
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000420 else
Simon Kelley39341552015-01-18 22:11:10 +0000421 {
422 peer_addr->in6.sin6_port = 0;
423 peer_addr->in6.sin6_scope_id = 0;
424 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000425
426 for (peers = daemon->auth_peers; peers; peers = peers->next)
427 if (sockaddr_isequal(peer_addr, &peers->addr))
428 break;
429
Simon Kelley090856c2018-06-02 18:37:07 +0100430 /* Refuse all AXFR unless --auth-sec-servers or auth-peers is set */
431 if ((!daemon->secondary_forward_server && !daemon->auth_peers) ||
432 (daemon->auth_peers && !peers))
Simon Kelley49678762012-12-09 18:24:58 +0000433 {
Simon Kelley49678762012-12-09 18:24:58 +0000434 if (peer_addr->sa.sa_family == AF_INET)
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000435 inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000436 else
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000437 inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000438
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000439 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
440 return 0;
Simon Kelley49678762012-12-09 18:24:58 +0000441 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000442
Simon Kelley5f8002f2013-10-21 17:40:18 +0100443 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000444 soa = 1; /* inhibits auth section */
445 ns = 1; /* ensure we include NS records! */
446 axfr = 1;
447 found = 1;
448 axfroffset = nameoffset;
449 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
450 }
451 else if (qtype == T_NS)
452 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100453 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000454 ns = 1; /* inhibits auth section */
455 found = 1;
456 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
457 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000458 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000459
Simon Kelleyb75e9362012-12-07 11:50:41 +0000460 if (!option_bool(OPT_DHCP_FQDN) && cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000461 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000462 *cut = 0; /* remove domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000463
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000464 if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000465 {
466 if (crecp->flags & F_DHCP)
467 do
468 {
469 nxdomain = 0;
Simon Kelley60225f42012-12-28 11:29:01 +0000470 if ((crecp->flags & flag) &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000471 (local_query || filter_zone(zone, flag, &(crecp->addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000472 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000473 *cut = '.'; /* restore domain part */
Simon Kelleycc921df2019-01-02 22:48:59 +0000474 log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
Simon Kelleyb75e9362012-12-07 11:50:41 +0000475 *cut = 0; /* remove domain part */
Simon Kelley8273ea52012-11-29 21:12:33 +0000476 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000477 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
478 daemon->auth_ttl, NULL, qtype, C_IN,
479 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000480 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000481 }
482 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
483 }
484
Simon Kelleyb75e9362012-12-07 11:50:41 +0000485 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000486 }
487
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000488 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000489 {
490 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
491 do
492 {
493 nxdomain = 0;
Simon Kelleycc921df2019-01-02 22:48:59 +0000494 if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000495 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000496 log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000497 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000498 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
499 daemon->auth_ttl, NULL, qtype, C_IN,
500 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000501 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000502 }
503 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
504 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000505
Simon Kelley62f9c0d2017-02-19 23:07:01 +0000506 /* Only supply CNAME if no record for any type is known. */
507 if (nxdomain)
Simon Kelleyb637d782016-12-13 16:44:11 +0000508 {
509 /* Check for possible wildcard match against *.domain
510 return length of match, to get longest.
511 Note that if return length of wildcard section, so
512 we match b.simon to _both_ *.simon and b.simon
513 but return a longer (better) match to b.simon.
514 */
515 for (wclen = 0, candidate = NULL, a = daemon->cnames; a; a = a->next)
516 if (a->alias[0] == '*')
517 {
518 char *test = name;
519
520 while ((test = strchr(test+1, '.')))
521 {
522 if (hostname_isequal(test, &(a->alias[1])))
523 {
524 if (strlen(test) > wclen && !cname_wildcard)
525 {
526 wclen = strlen(test);
527 candidate = a;
528 cname_wildcard = 1;
529 }
530 break;
531 }
532 }
533
534 }
535 else if (hostname_isequal(a->alias, name) && strlen(a->alias) > wclen)
536 {
537 /* Simple case, no wildcard */
538 wclen = strlen(a->alias);
539 candidate = a;
540 }
541
542 if (candidate)
543 {
544 log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
545 strcpy(name, candidate->target);
546 if (!strchr(name, '.'))
547 {
548 strcat(name, ".");
549 strcat(name, zone->domain);
550 }
551 found = 1;
552 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
553 daemon->auth_ttl, &nameoffset,
554 T_CNAME, C_IN, "d", name))
555 anscount++;
556
557 goto cname_restart;
558 }
Simon Kelleyb6f926f2018-08-21 17:46:52 +0100559 else if (cache_find_non_terminal(name, now))
560 nxdomain = 0;
Simon Kelleyb637d782016-12-13 16:44:11 +0000561
562 log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
563 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000564
Simon Kelley4f7b3042012-11-28 21:27:02 +0000565 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000566
Simon Kelley4f7b3042012-11-28 21:27:02 +0000567 /* Add auth section */
Simon Kelleyaa67fe72013-02-04 21:32:34 +0000568 if (auth && zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000569 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000570 char *authname;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000571 int newoffset, offset = 0;
572
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000573 if (!subnet)
Simon Kelleyb75e9362012-12-07 11:50:41 +0000574 authname = zone->domain;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000575 else
576 {
577 /* handle NS and SOA for PTR records */
Simon Kelleyb75e9362012-12-07 11:50:41 +0000578
579 authname = name;
580
Simon Kelley376d48c2013-11-13 13:04:30 +0000581 if (!(subnet->flags & ADDRLIST_IPV6))
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000582 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000583 in_addr_t a = ntohl(subnet->addr.addr4.s_addr) >> 8;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000584 char *p = name;
585
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100586 if (subnet->prefixlen >= 24)
Rosen Penevcbd29e52017-06-27 22:29:51 +0100587 p += sprintf(p, "%u.", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000588 a = a >> 8;
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100589 if (subnet->prefixlen >= 16 )
Rosen Penevcbd29e52017-06-27 22:29:51 +0100590 p += sprintf(p, "%u.", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000591 a = a >> 8;
Rosen Penevcbd29e52017-06-27 22:29:51 +0100592 p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000593
594 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000595 else
596 {
597 char *p = name;
598 int i;
599
600 for (i = subnet->prefixlen-1; i >= 0; i -= 4)
601 {
Simon Kelleycc921df2019-01-02 22:48:59 +0000602 int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3];
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000603 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
604 }
605 p += sprintf(p, "ip6.arpa");
606
607 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000608 }
609
610 /* handle NS and SOA in auth section or for explicit queries */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000611 newoffset = ansp - (unsigned char *)header;
612 if (((anscount == 0 && !ns) || soa) &&
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000613 add_resource_record(header, limit, &trunc, 0, &ansp,
614 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
Simon Kelleyb75e9362012-12-07 11:50:41 +0000615 authname, daemon->authserver, daemon->hostmaster,
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000616 daemon->soa_sn, daemon->soa_refresh,
617 daemon->soa_retry, daemon->soa_expiry,
618 daemon->auth_ttl))
619 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000620 offset = newoffset;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000621 if (soa)
622 anscount++;
623 else
624 authcount++;
625 }
Simon Kelleye1ff4192012-12-09 17:08:47 +0000626
627 if (anscount != 0 || ns)
628 {
629 struct name_list *secondary;
630
631 newoffset = ansp - (unsigned char *)header;
632 if (add_resource_record(header, limit, &trunc, -offset, &ansp,
633 daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
634 {
635 if (offset == 0)
636 offset = newoffset;
637 if (ns)
638 anscount++;
639 else
640 authcount++;
641 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000642
Simon Kelleye1ff4192012-12-09 17:08:47 +0000643 if (!subnet)
644 for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
645 if (add_resource_record(header, limit, &trunc, offset, &ansp,
646 daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
647 {
648 if (ns)
649 anscount++;
650 else
651 authcount++;
652 }
653 }
654
Simon Kelleyb75e9362012-12-07 11:50:41 +0000655 if (axfr)
656 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000657 for (rec = daemon->mxnames; rec; rec = rec->next)
658 if (in_zone(zone, rec->name, &cut))
659 {
660 if (cut)
661 *cut = 0;
662
663 if (rec->issrv)
664 {
665 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
666 NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
667 rec->priority, rec->weight, rec->srvport, rec->target))
668
669 anscount++;
670 }
671 else
672 {
673 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
674 NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
675 anscount++;
676 }
677
678 /* restore config data */
679 if (cut)
680 *cut = '.';
681 }
682
683 for (txt = daemon->rr; txt; txt = txt->next)
684 if (in_zone(zone, txt->name, &cut))
685 {
686 if (cut)
687 *cut = 0;
688
689 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
690 NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
691 anscount++;
692
693 /* restore config data */
694 if (cut)
695 *cut = '.';
696 }
697
698 for (txt = daemon->txt; txt; txt = txt->next)
699 if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
700 {
701 if (cut)
702 *cut = 0;
703
704 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
705 NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
706 anscount++;
707
708 /* restore config data */
709 if (cut)
710 *cut = '.';
711 }
712
713 for (na = daemon->naptr; na; na = na->next)
714 if (in_zone(zone, na->name, &cut))
715 {
716 if (cut)
717 *cut = 0;
718
719 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
720 NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
721 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
722 anscount++;
723
724 /* restore config data */
725 if (cut)
726 *cut = '.';
727 }
728
729 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100730 if (in_zone(zone, intr->name, &cut))
Simon Kelleye1ff4192012-12-09 17:08:47 +0000731 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100732 struct addrlist *addrlist;
733
Simon Kelleye1ff4192012-12-09 17:08:47 +0000734 if (cut)
735 *cut = 0;
736
Simon Kelley376d48c2013-11-13 13:04:30 +0000737 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000738 if (!(addrlist->flags & ADDRLIST_IPV6) &&
739 (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100740 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
741 daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
742 anscount++;
743
Simon Kelley376d48c2013-11-13 13:04:30 +0000744 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000745 if ((addrlist->flags & ADDRLIST_IPV6) &&
746 (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100747 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
748 daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
749 anscount++;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000750
751 /* restore config data */
752 if (cut)
753 *cut = '.';
754 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100755
Simon Kelleye1ff4192012-12-09 17:08:47 +0000756 for (a = daemon->cnames; a; a = a->next)
757 if (in_zone(zone, a->alias, &cut))
758 {
759 strcpy(name, a->target);
760 if (!strchr(name, '.'))
761 {
762 strcat(name, ".");
763 strcat(name, zone->domain);
764 }
765
766 if (cut)
767 *cut = 0;
768
769 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
770 daemon->auth_ttl, NULL,
771 T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
772 anscount++;
773 }
774
Simon Kelleyb75e9362012-12-07 11:50:41 +0000775 cache_enumerate(1);
776 while ((crecp = cache_enumerate(0)))
777 {
778 if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
779 !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
780 (crecp->flags & F_FORWARD))
781 {
782 if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
783 {
784 char *cache_name = cache_get_name(crecp);
Simon Kelley19b16892013-10-20 10:19:39 +0100785 if (!strchr(cache_name, '.') &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000786 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr))) &&
Simon Kelleyee875042018-10-23 22:10:17 +0100787 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
788 daemon->auth_ttl, NULL, (crecp->flags & F_IPV6) ? T_AAAA : T_A, C_IN,
789 (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
790 anscount++;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000791 }
792
793 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
794 {
795 strcpy(name, cache_get_name(crecp));
Simon Kelley19b16892013-10-20 10:19:39 +0100796 if (in_zone(zone, name, &cut) &&
Simon Kelleycc921df2019-01-02 22:48:59 +0000797 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000798 {
Simon Kelleyee875042018-10-23 22:10:17 +0100799 if (cut)
800 *cut = 0;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000801
Simon Kelleyee875042018-10-23 22:10:17 +0100802 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
803 daemon->auth_ttl, NULL, (crecp->flags & F_IPV6) ? T_AAAA : T_A, C_IN,
804 (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
805 anscount++;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000806 }
807 }
808 }
809 }
810
811 /* repeat SOA as last record */
812 if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
813 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
814 daemon->authserver, daemon->hostmaster,
815 daemon->soa_sn, daemon->soa_refresh,
816 daemon->soa_retry, daemon->soa_expiry,
817 daemon->auth_ttl))
818 anscount++;
Simon Kelley4c985da2013-03-22 14:07:38 +0000819
Simon Kelleyb75e9362012-12-07 11:50:41 +0000820 }
Simon Kelley4c985da2013-03-22 14:07:38 +0000821
822 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000823
Simon Kelley4f7b3042012-11-28 21:27:02 +0000824 /* done all questions, set up header and return length of result */
825 /* clear authoritative and truncated flags, set QR flag */
826 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
Simon Kelley93bafe62013-10-21 21:19:34 +0100827
828 if (local_query)
829 {
830 /* set RA flag */
831 header->hb4 |= HB4_RA;
832 }
833 else
834 {
835 /* clear RA flag */
836 header->hb4 &= ~HB4_RA;
837 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000838
Josh Soref730c6742017-02-06 16:14:04 +0000839 /* authoritative */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000840 if (auth)
841 header->hb3 |= HB3_AA;
842
843 /* truncation */
844 if (trunc)
845 header->hb3 |= HB3_TC;
846
Simon Kelley57310502013-10-21 18:26:20 +0100847 if ((auth || local_query) && nxdomain)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000848 SET_RCODE(header, NXDOMAIN);
849 else
850 SET_RCODE(header, NOERROR); /* no error */
851 header->ancount = htons(anscount);
852 header->nscount = htons(authcount);
Simon Kelleyaa792352012-12-06 19:41:35 +0000853 header->arcount = htons(0);
Simon Kelleyfa14bec2015-12-20 17:12:16 +0000854
855 /* Advertise our packet size limit in our reply */
856 if (have_pseudoheader)
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000857 return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
Simon Kelleyfa14bec2015-12-20 17:12:16 +0000858
Simon Kelley4f7b3042012-11-28 21:27:02 +0000859 return ansp - (unsigned char *)header;
860}
861
Simon Kelley4820dce2012-12-18 18:30:30 +0000862#endif
Simon Kelleyb75e9362012-12-07 11:50:41 +0000863
Simon Kelley4f7b3042012-11-28 21:27:02 +0000864
865