blob: c8b89b548be8b4af6e57937f979edd5900b08261 [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
Mathias Kresin094bfae2016-07-24 14:15:22 +010021static struct addrlist *find_addrlist(struct addrlist *list, int flag, struct all_addr *addr_u)
22{
23 do {
24 if (!(list->flags & ADDRLIST_IPV6))
25 {
26 struct in_addr netmask, addr = addr_u->addr.addr4;
27
28 if (!(flag & F_IPV4))
29 continue;
30
31 netmask.s_addr = htonl(~(in_addr_t)0 << (32 - list->prefixlen));
32
33 if (is_same_net(addr, list->addr.addr.addr4, netmask))
34 return list;
35 }
36#ifdef HAVE_IPV6
37 else if (is_same_net6(&(addr_u->addr.addr6), &list->addr.addr.addr6, list->prefixlen))
38 return list;
39#endif
40
41 } while ((list = list->next));
42
43 return NULL;
44}
45
Simon Kelleyc50f25a2013-11-21 11:29:27 +000046static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
Simon Kelley4f7b3042012-11-28 21:27:02 +000047{
Mathias Kresin094bfae2016-07-24 14:15:22 +010048 if (!zone->subnet)
49 return NULL;
50
51 return find_addrlist(zone->subnet, flag, addr_u);
52}
Simon Kelley4f7b3042012-11-28 21:27:02 +000053
Mathias Kresin094bfae2016-07-24 14:15:22 +010054static struct addrlist *find_exclude(struct auth_zone *zone, int flag, struct all_addr *addr_u)
55{
56 if (!zone->exclude)
57 return NULL;
58
59 return find_addrlist(zone->exclude, flag, addr_u);
Simon Kelley4f7b3042012-11-28 21:27:02 +000060}
61
Simon Kelleyc50f25a2013-11-21 11:29:27 +000062static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
63{
Mathias Kresin094bfae2016-07-24 14:15:22 +010064 if (find_exclude(zone, flag, addr_u))
65 return 0;
66
67 /* No subnets specified, no filter */
Simon Kelleyc50f25a2013-11-21 11:29:27 +000068 if (!zone->subnet)
69 return 1;
70
71 return find_subnet(zone, flag, addr_u) != NULL;
72}
73
Simon Kelleyb485ed92013-10-18 22:00:39 +010074int in_zone(struct auth_zone *zone, char *name, char **cut)
Simon Kelleyb75e9362012-12-07 11:50:41 +000075{
76 size_t namelen = strlen(name);
77 size_t domainlen = strlen(zone->domain);
78
79 if (cut)
80 *cut = NULL;
81
82 if (namelen >= domainlen &&
83 hostname_isequal(zone->domain, &name[namelen - domainlen]))
84 {
85
86 if (namelen == domainlen)
87 return 1;
88
89 if (name[namelen - domainlen - 1] == '.')
90 {
91 if (cut)
92 *cut = &name[namelen - domainlen - 1];
93 return 1;
94 }
95 }
96
97 return 0;
98}
Simon Kelley4f7b3042012-11-28 21:27:02 +000099
100
Simon Kelleyfa14bec2015-12-20 17:12:16 +0000101size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
102 int local_query, int do_bit, int have_pseudoheader)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000103{
104 char *name = daemon->namebuff;
105 unsigned char *p, *ansp;
106 int qtype, qclass;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000107 int nameoffset, axfroffset = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000108 int q, anscount = 0, authcount = 0;
109 struct crec *crecp;
Simon Kelley19b16892013-10-20 10:19:39 +0100110 int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000111 struct auth_zone *zone = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +0000112 struct addrlist *subnet = NULL;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000113 char *cut;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000114 struct mx_srv_record *rec, *move, **up;
115 struct txt_record *txt;
116 struct interface_name *intr;
117 struct naptr *na;
118 struct all_addr addr;
Simon Kelleyb637d782016-12-13 16:44:11 +0000119 struct cname *a, *candidate;
120 unsigned int wclen;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000121
Simon Kelley4f7b3042012-11-28 21:27:02 +0000122 if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
123 return 0;
Simon Kelley6008bdb2013-10-21 21:47:03 +0100124
Simon Kelley4f7b3042012-11-28 21:27:02 +0000125 /* determine end of question section (we put answers there) */
126 if (!(ansp = skip_questions(header, qlen)))
127 return 0; /* bad packet */
128
129 /* now process each question, answers go in RRs after the question */
130 p = (unsigned char *)(header+1);
131
132 for (q = ntohs(header->qdcount); q != 0; q--)
133 {
Simon Kelley8273ea52012-11-29 21:12:33 +0000134 unsigned short flag = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000135 int found = 0;
Simon Kelleyb637d782016-12-13 16:44:11 +0000136 int cname_wildcard = 0;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000137
Simon Kelley4f7b3042012-11-28 21:27:02 +0000138 /* save pointer to name for copying into answers */
139 nameoffset = p - (unsigned char *)header;
140
141 /* now extract name as .-concatenated string into name */
142 if (!extract_name(header, qlen, &p, name, 1, 4))
143 return 0; /* bad packet */
144
145 GETSHORT(qtype, p);
146 GETSHORT(qclass, p);
147
148 if (qclass != C_IN)
Simon Kelleyf8abe0c2012-12-15 11:59:25 +0000149 {
150 auth = 0;
151 continue;
152 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000153
Simon Kelley78c61842015-04-16 15:05:30 +0100154 if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
155 (flag = in_arpa_name_2_addr(name, &addr)) &&
156 !local_query)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000157 {
Simon Kelley78c61842015-04-16 15:05:30 +0100158 for (zone = daemon->auth_zones; zone; zone = zone->next)
159 if ((subnet = find_subnet(zone, flag, &addr)))
160 break;
161
162 if (!zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000163 {
Simon Kelley78c61842015-04-16 15:05:30 +0100164 auth = 0;
165 continue;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000166 }
Simon Kelley78c61842015-04-16 15:05:30 +0100167 else if (qtype == T_SOA)
168 soa = 1, found = 1;
169 else if (qtype == T_NS)
170 ns = 1, found = 1;
171 }
Simon Kelley19b16892013-10-20 10:19:39 +0100172
Simon Kelley78c61842015-04-16 15:05:30 +0100173 if (qtype == T_PTR && flag)
174 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100175 intr = NULL;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000176
Simon Kelley115ac3e2013-05-20 11:28:32 +0100177 if (flag == F_IPV4)
178 for (intr = daemon->int_names; intr; intr = intr->next)
179 {
180 struct addrlist *addrlist;
181
Simon Kelley376d48c2013-11-13 13:04:30 +0000182 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
183 if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100184 break;
185
186 if (addrlist)
187 break;
188 else
189 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
190 intr = intr->next;
191 }
192#ifdef HAVE_IPV6
193 else if (flag == F_IPV6)
194 for (intr = daemon->int_names; intr; intr = intr->next)
195 {
196 struct addrlist *addrlist;
197
Simon Kelley376d48c2013-11-13 13:04:30 +0000198 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
199 if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100200 break;
201
202 if (addrlist)
203 break;
204 else
205 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
206 intr = intr->next;
207 }
208#endif
209
210 if (intr)
211 {
Simon Kelley38440b22015-04-12 21:52:47 +0100212 if (local_query || in_zone(zone, intr->name, NULL))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100213 {
214 found = 1;
Simon Kelley8ab91e92013-10-21 20:50:04 +0100215 log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelley115ac3e2013-05-20 11:28:32 +0100216 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
217 daemon->auth_ttl, NULL,
218 T_PTR, C_IN, "d", intr->name))
219 anscount++;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000220 }
221 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100222
Simon Kelley4f7b3042012-11-28 21:27:02 +0000223 if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
224 do {
225 strcpy(name, cache_get_name(crecp));
226
227 if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
228 {
229 char *p = strchr(name, '.');
230 if (p)
231 *p = 0; /* must be bare name */
232
233 /* add external domain */
Simon Kelley38440b22015-04-12 21:52:47 +0100234 if (zone)
235 {
236 strcat(name, ".");
237 strcat(name, zone->domain);
238 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000239 log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000240 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000241 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
242 daemon->auth_ttl, NULL,
243 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000244 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000245 }
Simon Kelley38440b22015-04-12 21:52:47 +0100246 else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000247 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000248 log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000249 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000250 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
251 daemon->auth_ttl, NULL,
252 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000253 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000254 }
255 else
256 continue;
257
258 } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000259
Simon Kelley10068602014-04-03 21:16:40 +0100260 if (found)
261 nxdomain = 0;
262 else
263 log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000264
265 continue;
266 }
267
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000268 cname_restart:
Simon Kelley78c61842015-04-16 15:05:30 +0100269 if (found)
270 /* NS and SOA .arpa requests have set found above. */
271 cut = NULL;
272 else
Simon Kelley4f7b3042012-11-28 21:27:02 +0000273 {
Simon Kelley78c61842015-04-16 15:05:30 +0100274 for (zone = daemon->auth_zones; zone; zone = zone->next)
275 if (in_zone(zone, name, &cut))
276 break;
277
278 if (!zone)
279 {
280 auth = 0;
281 continue;
282 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000283 }
284
Simon Kelley8273ea52012-11-29 21:12:33 +0000285 for (rec = daemon->mxnames; rec; rec = rec->next)
286 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000287 {
288 nxdomain = 0;
289
290 if (qtype == T_MX)
291 {
292 found = 1;
293 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
294 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
295 NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
296 anscount++;
297 }
298 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000299
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000300 for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
301 if (rec->issrv && hostname_isequal(name, rec->name))
302 {
303 nxdomain = 0;
304
305 if (qtype == T_SRV)
306 {
307 found = 1;
308 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
309 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
310 NULL, T_SRV, C_IN, "sssd",
311 rec->priority, rec->weight, rec->srvport, rec->target))
312
313 anscount++;
314 }
315
316 /* unlink first SRV record found */
317 if (!move)
318 {
319 move = rec;
320 *up = rec->next;
321 }
322 else
323 up = &rec->next;
324 }
325 else
326 up = &rec->next;
327
328 /* put first SRV record back at the end. */
329 if (move)
330 {
331 *up = move;
332 move->next = NULL;
333 }
334
335 for (txt = daemon->rr; txt; txt = txt->next)
336 if (hostname_isequal(name, txt->name))
337 {
338 nxdomain = 0;
339 if (txt->class == qtype)
340 {
341 found = 1;
342 log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
343 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
344 NULL, txt->class, C_IN, "t", txt->len, txt->txt))
345 anscount++;
346 }
347 }
348
349 for (txt = daemon->txt; txt; txt = txt->next)
350 if (txt->class == C_IN && hostname_isequal(name, txt->name))
351 {
352 nxdomain = 0;
353 if (qtype == T_TXT)
354 {
355 found = 1;
356 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
357 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
358 NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
359 anscount++;
360 }
361 }
362
363 for (na = daemon->naptr; na; na = na->next)
364 if (hostname_isequal(name, na->name))
365 {
366 nxdomain = 0;
367 if (qtype == T_NAPTR)
368 {
369 found = 1;
370 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
371 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
372 NULL, T_NAPTR, C_IN, "sszzzd",
373 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
374 anscount++;
375 }
376 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100377
378 if (qtype == T_A)
379 flag = F_IPV4;
380
381#ifdef HAVE_IPV6
382 if (qtype == T_AAAA)
383 flag = F_IPV6;
384#endif
385
Simon Kelley8ab91e92013-10-21 20:50:04 +0100386 for (intr = daemon->int_names; intr; intr = intr->next)
387 if (hostname_isequal(name, intr->name))
388 {
389 struct addrlist *addrlist;
390
Simon Kelley8ab91e92013-10-21 20:50:04 +0100391 nxdomain = 0;
392
393 if (flag)
Simon Kelley376d48c2013-11-13 13:04:30 +0000394 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
395 if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
396 (local_query || filter_zone(zone, flag, &addrlist->addr)))
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100397 {
Simon Kelley47669362014-12-17 12:41:56 +0000398#ifdef HAVE_IPV6
399 if (addrlist->flags & ADDRLIST_REVONLY)
400 continue;
401#endif
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100402 found = 1;
403 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
404 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
405 daemon->auth_ttl, NULL, qtype, C_IN,
406 qtype == T_A ? "4" : "6", &addrlist->addr))
407 anscount++;
408 }
409 }
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000410
Simon Kelleye1ff4192012-12-09 17:08:47 +0000411 if (!cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000412 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000413 nxdomain = 0;
414
415 if (qtype == T_SOA)
416 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100417 auth = soa = 1; /* inhibits auth section */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000418 found = 1;
419 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
420 }
421 else if (qtype == T_AXFR)
422 {
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000423 struct iname *peers;
424
425 if (peer_addr->sa.sa_family == AF_INET)
426 peer_addr->in.sin_port = 0;
427#ifdef HAVE_IPV6
428 else
Simon Kelley39341552015-01-18 22:11:10 +0000429 {
430 peer_addr->in6.sin6_port = 0;
431 peer_addr->in6.sin6_scope_id = 0;
432 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000433#endif
434
435 for (peers = daemon->auth_peers; peers; peers = peers->next)
436 if (sockaddr_isequal(peer_addr, &peers->addr))
437 break;
438
Simon Kelley090856c2018-06-02 18:37:07 +0100439 /* Refuse all AXFR unless --auth-sec-servers or auth-peers is set */
440 if ((!daemon->secondary_forward_server && !daemon->auth_peers) ||
441 (daemon->auth_peers && !peers))
Simon Kelley49678762012-12-09 18:24:58 +0000442 {
Simon Kelley49678762012-12-09 18:24:58 +0000443 if (peer_addr->sa.sa_family == AF_INET)
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000444 inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000445#ifdef HAVE_IPV6
446 else
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000447 inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000448#endif
449
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000450 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
451 return 0;
Simon Kelley49678762012-12-09 18:24:58 +0000452 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000453
Simon Kelley5f8002f2013-10-21 17:40:18 +0100454 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000455 soa = 1; /* inhibits auth section */
456 ns = 1; /* ensure we include NS records! */
457 axfr = 1;
458 found = 1;
459 axfroffset = nameoffset;
460 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
461 }
462 else if (qtype == T_NS)
463 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100464 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000465 ns = 1; /* inhibits auth section */
466 found = 1;
467 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
468 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000469 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000470
Simon Kelleyb75e9362012-12-07 11:50:41 +0000471 if (!option_bool(OPT_DHCP_FQDN) && cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000472 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000473 *cut = 0; /* remove domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000474
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000475 if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000476 {
477 if (crecp->flags & F_DHCP)
478 do
479 {
480 nxdomain = 0;
Simon Kelley60225f42012-12-28 11:29:01 +0000481 if ((crecp->flags & flag) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000482 (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000483 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000484 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000485 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelleyb75e9362012-12-07 11:50:41 +0000486 *cut = 0; /* remove domain part */
Simon Kelley8273ea52012-11-29 21:12:33 +0000487 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000488 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
489 daemon->auth_ttl, NULL, qtype, C_IN,
490 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000491 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000492 }
493 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
494 }
495
Simon Kelleyb75e9362012-12-07 11:50:41 +0000496 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000497 }
498
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000499 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000500 {
501 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
502 do
503 {
504 nxdomain = 0;
Simon Kelley376d48c2013-11-13 13:04:30 +0000505 if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000506 {
507 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000508 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000509 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
510 daemon->auth_ttl, NULL, qtype, C_IN,
511 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000512 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000513 }
514 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
515 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000516
Simon Kelley62f9c0d2017-02-19 23:07:01 +0000517 /* Only supply CNAME if no record for any type is known. */
518 if (nxdomain)
Simon Kelleyb637d782016-12-13 16:44:11 +0000519 {
520 /* Check for possible wildcard match against *.domain
521 return length of match, to get longest.
522 Note that if return length of wildcard section, so
523 we match b.simon to _both_ *.simon and b.simon
524 but return a longer (better) match to b.simon.
525 */
526 for (wclen = 0, candidate = NULL, a = daemon->cnames; a; a = a->next)
527 if (a->alias[0] == '*')
528 {
529 char *test = name;
530
531 while ((test = strchr(test+1, '.')))
532 {
533 if (hostname_isequal(test, &(a->alias[1])))
534 {
535 if (strlen(test) > wclen && !cname_wildcard)
536 {
537 wclen = strlen(test);
538 candidate = a;
539 cname_wildcard = 1;
540 }
541 break;
542 }
543 }
544
545 }
546 else if (hostname_isequal(a->alias, name) && strlen(a->alias) > wclen)
547 {
548 /* Simple case, no wildcard */
549 wclen = strlen(a->alias);
550 candidate = a;
551 }
552
553 if (candidate)
554 {
555 log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
556 strcpy(name, candidate->target);
557 if (!strchr(name, '.'))
558 {
559 strcat(name, ".");
560 strcat(name, zone->domain);
561 }
562 found = 1;
563 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
564 daemon->auth_ttl, &nameoffset,
565 T_CNAME, C_IN, "d", name))
566 anscount++;
567
568 goto cname_restart;
569 }
570
571 log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
572 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000573
Simon Kelley4f7b3042012-11-28 21:27:02 +0000574 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000575
Simon Kelley4f7b3042012-11-28 21:27:02 +0000576 /* Add auth section */
Simon Kelleyaa67fe72013-02-04 21:32:34 +0000577 if (auth && zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000578 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000579 char *authname;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000580 int newoffset, offset = 0;
581
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000582 if (!subnet)
Simon Kelleyb75e9362012-12-07 11:50:41 +0000583 authname = zone->domain;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000584 else
585 {
586 /* handle NS and SOA for PTR records */
Simon Kelleyb75e9362012-12-07 11:50:41 +0000587
588 authname = name;
589
Simon Kelley376d48c2013-11-13 13:04:30 +0000590 if (!(subnet->flags & ADDRLIST_IPV6))
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000591 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000592 in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000593 char *p = name;
594
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100595 if (subnet->prefixlen >= 24)
Rosen Penevcbd29e52017-06-27 22:29:51 +0100596 p += sprintf(p, "%u.", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000597 a = a >> 8;
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100598 if (subnet->prefixlen >= 16 )
Rosen Penevcbd29e52017-06-27 22:29:51 +0100599 p += sprintf(p, "%u.", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000600 a = a >> 8;
Rosen Penevcbd29e52017-06-27 22:29:51 +0100601 p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000602
603 }
604#ifdef HAVE_IPV6
605 else
606 {
607 char *p = name;
608 int i;
609
610 for (i = subnet->prefixlen-1; i >= 0; i -= 4)
611 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000612 int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3];
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000613 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
614 }
615 p += sprintf(p, "ip6.arpa");
616
617 }
618#endif
619 }
620
621 /* handle NS and SOA in auth section or for explicit queries */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000622 newoffset = ansp - (unsigned char *)header;
623 if (((anscount == 0 && !ns) || soa) &&
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000624 add_resource_record(header, limit, &trunc, 0, &ansp,
625 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
Simon Kelleyb75e9362012-12-07 11:50:41 +0000626 authname, daemon->authserver, daemon->hostmaster,
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000627 daemon->soa_sn, daemon->soa_refresh,
628 daemon->soa_retry, daemon->soa_expiry,
629 daemon->auth_ttl))
630 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000631 offset = newoffset;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000632 if (soa)
633 anscount++;
634 else
635 authcount++;
636 }
Simon Kelleye1ff4192012-12-09 17:08:47 +0000637
638 if (anscount != 0 || ns)
639 {
640 struct name_list *secondary;
641
642 newoffset = ansp - (unsigned char *)header;
643 if (add_resource_record(header, limit, &trunc, -offset, &ansp,
644 daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
645 {
646 if (offset == 0)
647 offset = newoffset;
648 if (ns)
649 anscount++;
650 else
651 authcount++;
652 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000653
Simon Kelleye1ff4192012-12-09 17:08:47 +0000654 if (!subnet)
655 for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
656 if (add_resource_record(header, limit, &trunc, offset, &ansp,
657 daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
658 {
659 if (ns)
660 anscount++;
661 else
662 authcount++;
663 }
664 }
665
Simon Kelleyb75e9362012-12-07 11:50:41 +0000666 if (axfr)
667 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000668 for (rec = daemon->mxnames; rec; rec = rec->next)
669 if (in_zone(zone, rec->name, &cut))
670 {
671 if (cut)
672 *cut = 0;
673
674 if (rec->issrv)
675 {
676 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
677 NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
678 rec->priority, rec->weight, rec->srvport, rec->target))
679
680 anscount++;
681 }
682 else
683 {
684 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
685 NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
686 anscount++;
687 }
688
689 /* restore config data */
690 if (cut)
691 *cut = '.';
692 }
693
694 for (txt = daemon->rr; txt; txt = txt->next)
695 if (in_zone(zone, txt->name, &cut))
696 {
697 if (cut)
698 *cut = 0;
699
700 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
701 NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
702 anscount++;
703
704 /* restore config data */
705 if (cut)
706 *cut = '.';
707 }
708
709 for (txt = daemon->txt; txt; txt = txt->next)
710 if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
711 {
712 if (cut)
713 *cut = 0;
714
715 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
716 NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
717 anscount++;
718
719 /* restore config data */
720 if (cut)
721 *cut = '.';
722 }
723
724 for (na = daemon->naptr; na; na = na->next)
725 if (in_zone(zone, na->name, &cut))
726 {
727 if (cut)
728 *cut = 0;
729
730 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
731 NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
732 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
733 anscount++;
734
735 /* restore config data */
736 if (cut)
737 *cut = '.';
738 }
739
740 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100741 if (in_zone(zone, intr->name, &cut))
Simon Kelleye1ff4192012-12-09 17:08:47 +0000742 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100743 struct addrlist *addrlist;
744
Simon Kelleye1ff4192012-12-09 17:08:47 +0000745 if (cut)
746 *cut = 0;
747
Simon Kelley376d48c2013-11-13 13:04:30 +0000748 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000749 if (!(addrlist->flags & ADDRLIST_IPV6) &&
750 (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100751 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
752 daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
753 anscount++;
754
755#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +0000756 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000757 if ((addrlist->flags & ADDRLIST_IPV6) &&
758 (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100759 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
760 daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
761 anscount++;
762#endif
Simon Kelleye1ff4192012-12-09 17:08:47 +0000763
764 /* restore config data */
765 if (cut)
766 *cut = '.';
767 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100768
Simon Kelleye1ff4192012-12-09 17:08:47 +0000769 for (a = daemon->cnames; a; a = a->next)
770 if (in_zone(zone, a->alias, &cut))
771 {
772 strcpy(name, a->target);
773 if (!strchr(name, '.'))
774 {
775 strcat(name, ".");
776 strcat(name, zone->domain);
777 }
778
779 if (cut)
780 *cut = 0;
781
782 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
783 daemon->auth_ttl, NULL,
784 T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
785 anscount++;
786 }
787
Simon Kelleyb75e9362012-12-07 11:50:41 +0000788 cache_enumerate(1);
789 while ((crecp = cache_enumerate(0)))
790 {
791 if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
792 !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
793 (crecp->flags & F_FORWARD))
794 {
795 if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
796 {
797 char *cache_name = cache_get_name(crecp);
Simon Kelley19b16892013-10-20 10:19:39 +0100798 if (!strchr(cache_name, '.') &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000799 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000800 {
801 qtype = T_A;
802#ifdef HAVE_IPV6
803 if (crecp->flags & F_IPV6)
804 qtype = T_AAAA;
805#endif
806 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
807 daemon->auth_ttl, NULL, qtype, C_IN,
808 (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
809 anscount++;
810 }
811 }
812
813 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
814 {
815 strcpy(name, cache_get_name(crecp));
Simon Kelley19b16892013-10-20 10:19:39 +0100816 if (in_zone(zone, name, &cut) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000817 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000818 {
819 qtype = T_A;
820#ifdef HAVE_IPV6
821 if (crecp->flags & F_IPV6)
822 qtype = T_AAAA;
823#endif
824 if (cut)
Simon Kelleye1ff4192012-12-09 17:08:47 +0000825 *cut = 0;
826
827 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
828 daemon->auth_ttl, NULL, qtype, C_IN,
829 (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
830 anscount++;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000831 }
832 }
833 }
834 }
835
836 /* repeat SOA as last record */
837 if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
838 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
839 daemon->authserver, daemon->hostmaster,
840 daemon->soa_sn, daemon->soa_refresh,
841 daemon->soa_retry, daemon->soa_expiry,
842 daemon->auth_ttl))
843 anscount++;
Simon Kelley4c985da2013-03-22 14:07:38 +0000844
Simon Kelleyb75e9362012-12-07 11:50:41 +0000845 }
Simon Kelley4c985da2013-03-22 14:07:38 +0000846
847 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000848
Simon Kelley4f7b3042012-11-28 21:27:02 +0000849 /* done all questions, set up header and return length of result */
850 /* clear authoritative and truncated flags, set QR flag */
851 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
Simon Kelley93bafe62013-10-21 21:19:34 +0100852
853 if (local_query)
854 {
855 /* set RA flag */
856 header->hb4 |= HB4_RA;
857 }
858 else
859 {
860 /* clear RA flag */
861 header->hb4 &= ~HB4_RA;
862 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000863
Josh Soref730c6742017-02-06 16:14:04 +0000864 /* authoritative */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000865 if (auth)
866 header->hb3 |= HB3_AA;
867
868 /* truncation */
869 if (trunc)
870 header->hb3 |= HB3_TC;
871
Simon Kelley57310502013-10-21 18:26:20 +0100872 if ((auth || local_query) && nxdomain)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000873 SET_RCODE(header, NXDOMAIN);
874 else
875 SET_RCODE(header, NOERROR); /* no error */
876 header->ancount = htons(anscount);
877 header->nscount = htons(authcount);
Simon Kelleyaa792352012-12-06 19:41:35 +0000878 header->arcount = htons(0);
Simon Kelleyfa14bec2015-12-20 17:12:16 +0000879
880 /* Advertise our packet size limit in our reply */
881 if (have_pseudoheader)
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000882 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 +0000883
Simon Kelley4f7b3042012-11-28 21:27:02 +0000884 return ansp - (unsigned char *)header;
885}
886
Simon Kelley4820dce2012-12-18 18:30:30 +0000887#endif
Simon Kelleyb75e9362012-12-07 11:50:41 +0000888
Simon Kelley4f7b3042012-11-28 21:27:02 +0000889
890