blob: 6ad051d5e41d80078585d5fa8354f557269dec5d [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
439 /* Refuse all AXFR unless --auth-sec-servers is set */
440 if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
Simon Kelley49678762012-12-09 18:24:58 +0000441 {
Simon Kelley49678762012-12-09 18:24:58 +0000442 if (peer_addr->sa.sa_family == AF_INET)
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000443 inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000444#ifdef HAVE_IPV6
445 else
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000446 inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000447#endif
448
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000449 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
450 return 0;
Simon Kelley49678762012-12-09 18:24:58 +0000451 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000452
Simon Kelley5f8002f2013-10-21 17:40:18 +0100453 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000454 soa = 1; /* inhibits auth section */
455 ns = 1; /* ensure we include NS records! */
456 axfr = 1;
457 found = 1;
458 axfroffset = nameoffset;
459 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
460 }
461 else if (qtype == T_NS)
462 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100463 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000464 ns = 1; /* inhibits auth section */
465 found = 1;
466 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
467 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000468 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000469
Simon Kelleyb75e9362012-12-07 11:50:41 +0000470 if (!option_bool(OPT_DHCP_FQDN) && cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000471 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000472 *cut = 0; /* remove domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000473
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000474 if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000475 {
476 if (crecp->flags & F_DHCP)
477 do
478 {
479 nxdomain = 0;
Simon Kelley60225f42012-12-28 11:29:01 +0000480 if ((crecp->flags & flag) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000481 (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000482 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000483 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000484 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelleyb75e9362012-12-07 11:50:41 +0000485 *cut = 0; /* remove domain part */
Simon Kelley8273ea52012-11-29 21:12:33 +0000486 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000487 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
488 daemon->auth_ttl, NULL, qtype, C_IN,
489 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000490 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000491 }
492 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
493 }
494
Simon Kelleyb75e9362012-12-07 11:50:41 +0000495 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000496 }
497
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000498 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000499 {
500 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
501 do
502 {
503 nxdomain = 0;
Simon Kelley376d48c2013-11-13 13:04:30 +0000504 if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000505 {
506 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000507 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000508 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
509 daemon->auth_ttl, NULL, qtype, C_IN,
510 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000511 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000512 }
513 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
514 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000515
Simon Kelley62f9c0d2017-02-19 23:07:01 +0000516 /* Only supply CNAME if no record for any type is known. */
517 if (nxdomain)
Simon Kelleyb637d782016-12-13 16:44:11 +0000518 {
519 /* Check for possible wildcard match against *.domain
520 return length of match, to get longest.
521 Note that if return length of wildcard section, so
522 we match b.simon to _both_ *.simon and b.simon
523 but return a longer (better) match to b.simon.
524 */
525 for (wclen = 0, candidate = NULL, a = daemon->cnames; a; a = a->next)
526 if (a->alias[0] == '*')
527 {
528 char *test = name;
529
530 while ((test = strchr(test+1, '.')))
531 {
532 if (hostname_isequal(test, &(a->alias[1])))
533 {
534 if (strlen(test) > wclen && !cname_wildcard)
535 {
536 wclen = strlen(test);
537 candidate = a;
538 cname_wildcard = 1;
539 }
540 break;
541 }
542 }
543
544 }
545 else if (hostname_isequal(a->alias, name) && strlen(a->alias) > wclen)
546 {
547 /* Simple case, no wildcard */
548 wclen = strlen(a->alias);
549 candidate = a;
550 }
551
552 if (candidate)
553 {
554 log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
555 strcpy(name, candidate->target);
556 if (!strchr(name, '.'))
557 {
558 strcat(name, ".");
559 strcat(name, zone->domain);
560 }
561 found = 1;
562 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
563 daemon->auth_ttl, &nameoffset,
564 T_CNAME, C_IN, "d", name))
565 anscount++;
566
567 goto cname_restart;
568 }
569
570 log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
571 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000572
Simon Kelley4f7b3042012-11-28 21:27:02 +0000573 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000574
Simon Kelley4f7b3042012-11-28 21:27:02 +0000575 /* Add auth section */
Simon Kelleyaa67fe72013-02-04 21:32:34 +0000576 if (auth && zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000577 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000578 char *authname;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000579 int newoffset, offset = 0;
580
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000581 if (!subnet)
Simon Kelleyb75e9362012-12-07 11:50:41 +0000582 authname = zone->domain;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000583 else
584 {
585 /* handle NS and SOA for PTR records */
Simon Kelleyb75e9362012-12-07 11:50:41 +0000586
587 authname = name;
588
Simon Kelley376d48c2013-11-13 13:04:30 +0000589 if (!(subnet->flags & ADDRLIST_IPV6))
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000590 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000591 in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000592 char *p = name;
593
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100594 if (subnet->prefixlen >= 24)
Rosen Penevcbd29e52017-06-27 22:29:51 +0100595 p += sprintf(p, "%u.", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000596 a = a >> 8;
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100597 if (subnet->prefixlen >= 16 )
Rosen Penevcbd29e52017-06-27 22:29:51 +0100598 p += sprintf(p, "%u.", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000599 a = a >> 8;
Rosen Penevcbd29e52017-06-27 22:29:51 +0100600 p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000601
602 }
603#ifdef HAVE_IPV6
604 else
605 {
606 char *p = name;
607 int i;
608
609 for (i = subnet->prefixlen-1; i >= 0; i -= 4)
610 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000611 int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3];
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000612 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
613 }
614 p += sprintf(p, "ip6.arpa");
615
616 }
617#endif
618 }
619
620 /* handle NS and SOA in auth section or for explicit queries */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000621 newoffset = ansp - (unsigned char *)header;
622 if (((anscount == 0 && !ns) || soa) &&
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000623 add_resource_record(header, limit, &trunc, 0, &ansp,
624 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
Simon Kelleyb75e9362012-12-07 11:50:41 +0000625 authname, daemon->authserver, daemon->hostmaster,
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000626 daemon->soa_sn, daemon->soa_refresh,
627 daemon->soa_retry, daemon->soa_expiry,
628 daemon->auth_ttl))
629 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000630 offset = newoffset;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000631 if (soa)
632 anscount++;
633 else
634 authcount++;
635 }
Simon Kelleye1ff4192012-12-09 17:08:47 +0000636
637 if (anscount != 0 || ns)
638 {
639 struct name_list *secondary;
640
641 newoffset = ansp - (unsigned char *)header;
642 if (add_resource_record(header, limit, &trunc, -offset, &ansp,
643 daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
644 {
645 if (offset == 0)
646 offset = newoffset;
647 if (ns)
648 anscount++;
649 else
650 authcount++;
651 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000652
Simon Kelleye1ff4192012-12-09 17:08:47 +0000653 if (!subnet)
654 for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
655 if (add_resource_record(header, limit, &trunc, offset, &ansp,
656 daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
657 {
658 if (ns)
659 anscount++;
660 else
661 authcount++;
662 }
663 }
664
Simon Kelleyb75e9362012-12-07 11:50:41 +0000665 if (axfr)
666 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000667 for (rec = daemon->mxnames; rec; rec = rec->next)
668 if (in_zone(zone, rec->name, &cut))
669 {
670 if (cut)
671 *cut = 0;
672
673 if (rec->issrv)
674 {
675 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
676 NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
677 rec->priority, rec->weight, rec->srvport, rec->target))
678
679 anscount++;
680 }
681 else
682 {
683 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
684 NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
685 anscount++;
686 }
687
688 /* restore config data */
689 if (cut)
690 *cut = '.';
691 }
692
693 for (txt = daemon->rr; txt; txt = txt->next)
694 if (in_zone(zone, txt->name, &cut))
695 {
696 if (cut)
697 *cut = 0;
698
699 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
700 NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
701 anscount++;
702
703 /* restore config data */
704 if (cut)
705 *cut = '.';
706 }
707
708 for (txt = daemon->txt; txt; txt = txt->next)
709 if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
710 {
711 if (cut)
712 *cut = 0;
713
714 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
715 NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
716 anscount++;
717
718 /* restore config data */
719 if (cut)
720 *cut = '.';
721 }
722
723 for (na = daemon->naptr; na; na = na->next)
724 if (in_zone(zone, na->name, &cut))
725 {
726 if (cut)
727 *cut = 0;
728
729 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
730 NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
731 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
732 anscount++;
733
734 /* restore config data */
735 if (cut)
736 *cut = '.';
737 }
738
739 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100740 if (in_zone(zone, intr->name, &cut))
Simon Kelleye1ff4192012-12-09 17:08:47 +0000741 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100742 struct addrlist *addrlist;
743
Simon Kelleye1ff4192012-12-09 17:08:47 +0000744 if (cut)
745 *cut = 0;
746
Simon Kelley376d48c2013-11-13 13:04:30 +0000747 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000748 if (!(addrlist->flags & ADDRLIST_IPV6) &&
749 (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100750 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
751 daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
752 anscount++;
753
754#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +0000755 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000756 if ((addrlist->flags & ADDRLIST_IPV6) &&
757 (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100758 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
759 daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
760 anscount++;
761#endif
Simon Kelleye1ff4192012-12-09 17:08:47 +0000762
763 /* restore config data */
764 if (cut)
765 *cut = '.';
766 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100767
Simon Kelleye1ff4192012-12-09 17:08:47 +0000768 for (a = daemon->cnames; a; a = a->next)
769 if (in_zone(zone, a->alias, &cut))
770 {
771 strcpy(name, a->target);
772 if (!strchr(name, '.'))
773 {
774 strcat(name, ".");
775 strcat(name, zone->domain);
776 }
777
778 if (cut)
779 *cut = 0;
780
781 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
782 daemon->auth_ttl, NULL,
783 T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
784 anscount++;
785 }
786
Simon Kelleyb75e9362012-12-07 11:50:41 +0000787 cache_enumerate(1);
788 while ((crecp = cache_enumerate(0)))
789 {
790 if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
791 !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
792 (crecp->flags & F_FORWARD))
793 {
794 if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
795 {
796 char *cache_name = cache_get_name(crecp);
Simon Kelley19b16892013-10-20 10:19:39 +0100797 if (!strchr(cache_name, '.') &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000798 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000799 {
800 qtype = T_A;
801#ifdef HAVE_IPV6
802 if (crecp->flags & F_IPV6)
803 qtype = T_AAAA;
804#endif
805 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
806 daemon->auth_ttl, NULL, qtype, C_IN,
807 (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
808 anscount++;
809 }
810 }
811
812 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
813 {
814 strcpy(name, cache_get_name(crecp));
Simon Kelley19b16892013-10-20 10:19:39 +0100815 if (in_zone(zone, name, &cut) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000816 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000817 {
818 qtype = T_A;
819#ifdef HAVE_IPV6
820 if (crecp->flags & F_IPV6)
821 qtype = T_AAAA;
822#endif
823 if (cut)
Simon Kelleye1ff4192012-12-09 17:08:47 +0000824 *cut = 0;
825
826 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
827 daemon->auth_ttl, NULL, qtype, C_IN,
828 (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
829 anscount++;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000830 }
831 }
832 }
833 }
834
835 /* repeat SOA as last record */
836 if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
837 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
838 daemon->authserver, daemon->hostmaster,
839 daemon->soa_sn, daemon->soa_refresh,
840 daemon->soa_retry, daemon->soa_expiry,
841 daemon->auth_ttl))
842 anscount++;
Simon Kelley4c985da2013-03-22 14:07:38 +0000843
Simon Kelleyb75e9362012-12-07 11:50:41 +0000844 }
Simon Kelley4c985da2013-03-22 14:07:38 +0000845
846 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000847
Simon Kelley4f7b3042012-11-28 21:27:02 +0000848 /* done all questions, set up header and return length of result */
849 /* clear authoritative and truncated flags, set QR flag */
850 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
Simon Kelley93bafe62013-10-21 21:19:34 +0100851
852 if (local_query)
853 {
854 /* set RA flag */
855 header->hb4 |= HB4_RA;
856 }
857 else
858 {
859 /* clear RA flag */
860 header->hb4 &= ~HB4_RA;
861 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000862
Josh Soref730c6742017-02-06 16:14:04 +0000863 /* authoritative */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000864 if (auth)
865 header->hb3 |= HB3_AA;
866
867 /* truncation */
868 if (trunc)
869 header->hb3 |= HB3_TC;
870
Simon Kelley57310502013-10-21 18:26:20 +0100871 if ((auth || local_query) && nxdomain)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000872 SET_RCODE(header, NXDOMAIN);
873 else
874 SET_RCODE(header, NOERROR); /* no error */
875 header->ancount = htons(anscount);
876 header->nscount = htons(authcount);
Simon Kelleyaa792352012-12-06 19:41:35 +0000877 header->arcount = htons(0);
Simon Kelleyfa14bec2015-12-20 17:12:16 +0000878
879 /* Advertise our packet size limit in our reply */
880 if (have_pseudoheader)
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000881 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 +0000882
Simon Kelley4f7b3042012-11-28 21:27:02 +0000883 return ansp - (unsigned char *)header;
884}
885
Simon Kelley4820dce2012-12-18 18:30:30 +0000886#endif
Simon Kelleyb75e9362012-12-07 11:50:41 +0000887
Simon Kelley4f7b3042012-11-28 21:27:02 +0000888
889