blob: 2b0b7d6b052df410804b436df4354432438ab079 [file] [log] [blame]
Simon Kelleyaff33962015-01-31 20:13:40 +00001/* dnsmasq is Copyright (c) 2000-2015 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 Kelleyc50f25a2013-11-21 11:29:27 +000021static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
Simon Kelley4f7b3042012-11-28 21:27:02 +000022{
Simon Kelley376d48c2013-11-13 13:04:30 +000023 struct addrlist *subnet;
Simon Kelley4f7b3042012-11-28 21:27:02 +000024
25 for (subnet = zone->subnet; subnet; subnet = subnet->next)
26 {
Simon Kelley376d48c2013-11-13 13:04:30 +000027 if (!(subnet->flags & ADDRLIST_IPV6))
Simon Kelley4f7b3042012-11-28 21:27:02 +000028 {
Simon Kelley376d48c2013-11-13 13:04:30 +000029 struct in_addr netmask, addr = addr_u->addr.addr4;
30
31 if (!(flag & F_IPV4))
32 continue;
Simon Kelley4f7b3042012-11-28 21:27:02 +000033
Richard Genoud10cfc0d2014-09-17 21:17:39 +010034 netmask.s_addr = htonl(~(in_addr_t)0 << (32 - subnet->prefixlen));
Simon Kelley4f7b3042012-11-28 21:27:02 +000035
Simon Kelley376d48c2013-11-13 13:04:30 +000036 if (is_same_net(addr, subnet->addr.addr.addr4, netmask))
Simon Kelley45dd1fe2012-12-04 20:49:24 +000037 return subnet;
Simon Kelley4f7b3042012-11-28 21:27:02 +000038 }
39#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +000040 else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen))
Simon Kelley45dd1fe2012-12-04 20:49:24 +000041 return subnet;
Simon Kelley4f7b3042012-11-28 21:27:02 +000042#endif
43
44 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +000045 return NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +000046}
47
Simon Kelleyc50f25a2013-11-21 11:29:27 +000048static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
49{
50 /* No zones specified, no filter */
51 if (!zone->subnet)
52 return 1;
53
54 return find_subnet(zone, flag, addr_u) != NULL;
55}
56
Simon Kelleyb485ed92013-10-18 22:00:39 +010057int in_zone(struct auth_zone *zone, char *name, char **cut)
Simon Kelleyb75e9362012-12-07 11:50:41 +000058{
59 size_t namelen = strlen(name);
60 size_t domainlen = strlen(zone->domain);
61
62 if (cut)
63 *cut = NULL;
64
65 if (namelen >= domainlen &&
66 hostname_isequal(zone->domain, &name[namelen - domainlen]))
67 {
68
69 if (namelen == domainlen)
70 return 1;
71
72 if (name[namelen - domainlen - 1] == '.')
73 {
74 if (cut)
75 *cut = &name[namelen - domainlen - 1];
76 return 1;
77 }
78 }
79
80 return 0;
81}
Simon Kelley4f7b3042012-11-28 21:27:02 +000082
83
Simon Kelley19b16892013-10-20 10:19:39 +010084size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query)
Simon Kelley4f7b3042012-11-28 21:27:02 +000085{
86 char *name = daemon->namebuff;
87 unsigned char *p, *ansp;
88 int qtype, qclass;
Simon Kelleyb75e9362012-12-07 11:50:41 +000089 int nameoffset, axfroffset = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +000090 int q, anscount = 0, authcount = 0;
91 struct crec *crecp;
Simon Kelley19b16892013-10-20 10:19:39 +010092 int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +000093 struct auth_zone *zone = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +000094 struct addrlist *subnet = NULL;
Simon Kelleyb75e9362012-12-07 11:50:41 +000095 char *cut;
Simon Kelleye1ff4192012-12-09 17:08:47 +000096 struct mx_srv_record *rec, *move, **up;
97 struct txt_record *txt;
98 struct interface_name *intr;
99 struct naptr *na;
100 struct all_addr addr;
101 struct cname *a;
102
Simon Kelley4f7b3042012-11-28 21:27:02 +0000103 if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
104 return 0;
Simon Kelley6008bdb2013-10-21 21:47:03 +0100105
Simon Kelley4f7b3042012-11-28 21:27:02 +0000106 /* determine end of question section (we put answers there) */
107 if (!(ansp = skip_questions(header, qlen)))
108 return 0; /* bad packet */
109
110 /* now process each question, answers go in RRs after the question */
111 p = (unsigned char *)(header+1);
112
113 for (q = ntohs(header->qdcount); q != 0; q--)
114 {
Simon Kelley8273ea52012-11-29 21:12:33 +0000115 unsigned short flag = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000116 int found = 0;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000117
Simon Kelley4f7b3042012-11-28 21:27:02 +0000118 /* save pointer to name for copying into answers */
119 nameoffset = p - (unsigned char *)header;
120
121 /* now extract name as .-concatenated string into name */
122 if (!extract_name(header, qlen, &p, name, 1, 4))
123 return 0; /* bad packet */
124
125 GETSHORT(qtype, p);
126 GETSHORT(qclass, p);
127
128 if (qclass != C_IN)
Simon Kelleyf8abe0c2012-12-15 11:59:25 +0000129 {
130 auth = 0;
131 continue;
132 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000133
Simon Kelley78c61842015-04-16 15:05:30 +0100134 if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
135 (flag = in_arpa_name_2_addr(name, &addr)) &&
136 !local_query)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000137 {
Simon Kelley78c61842015-04-16 15:05:30 +0100138 for (zone = daemon->auth_zones; zone; zone = zone->next)
139 if ((subnet = find_subnet(zone, flag, &addr)))
140 break;
141
142 if (!zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000143 {
Simon Kelley78c61842015-04-16 15:05:30 +0100144 auth = 0;
145 continue;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000146 }
Simon Kelley78c61842015-04-16 15:05:30 +0100147 else if (qtype == T_SOA)
148 soa = 1, found = 1;
149 else if (qtype == T_NS)
150 ns = 1, found = 1;
151 }
Simon Kelley19b16892013-10-20 10:19:39 +0100152
Simon Kelley78c61842015-04-16 15:05:30 +0100153 if (qtype == T_PTR && flag)
154 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100155 intr = NULL;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000156
Simon Kelley115ac3e2013-05-20 11:28:32 +0100157 if (flag == F_IPV4)
158 for (intr = daemon->int_names; intr; intr = intr->next)
159 {
160 struct addrlist *addrlist;
161
Simon Kelley376d48c2013-11-13 13:04:30 +0000162 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
163 if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100164 break;
165
166 if (addrlist)
167 break;
168 else
169 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
170 intr = intr->next;
171 }
172#ifdef HAVE_IPV6
173 else if (flag == F_IPV6)
174 for (intr = daemon->int_names; intr; intr = intr->next)
175 {
176 struct addrlist *addrlist;
177
Simon Kelley376d48c2013-11-13 13:04:30 +0000178 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
179 if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100180 break;
181
182 if (addrlist)
183 break;
184 else
185 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
186 intr = intr->next;
187 }
188#endif
189
190 if (intr)
191 {
Simon Kelley38440b22015-04-12 21:52:47 +0100192 if (local_query || in_zone(zone, intr->name, NULL))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100193 {
194 found = 1;
Simon Kelley8ab91e92013-10-21 20:50:04 +0100195 log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelley115ac3e2013-05-20 11:28:32 +0100196 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
197 daemon->auth_ttl, NULL,
198 T_PTR, C_IN, "d", intr->name))
199 anscount++;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000200 }
201 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100202
Simon Kelley4f7b3042012-11-28 21:27:02 +0000203 if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
204 do {
205 strcpy(name, cache_get_name(crecp));
206
207 if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
208 {
209 char *p = strchr(name, '.');
210 if (p)
211 *p = 0; /* must be bare name */
212
213 /* add external domain */
Simon Kelley38440b22015-04-12 21:52:47 +0100214 if (zone)
215 {
216 strcat(name, ".");
217 strcat(name, zone->domain);
218 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000219 log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000220 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000221 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
222 daemon->auth_ttl, NULL,
223 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000224 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000225 }
Simon Kelley38440b22015-04-12 21:52:47 +0100226 else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000227 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000228 log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000229 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000230 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
231 daemon->auth_ttl, NULL,
232 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000233 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000234 }
235 else
236 continue;
237
238 } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000239
Simon Kelley10068602014-04-03 21:16:40 +0100240 if (found)
241 nxdomain = 0;
242 else
243 log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000244
245 continue;
246 }
247
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000248 cname_restart:
Simon Kelley78c61842015-04-16 15:05:30 +0100249 if (found)
250 /* NS and SOA .arpa requests have set found above. */
251 cut = NULL;
252 else
Simon Kelley4f7b3042012-11-28 21:27:02 +0000253 {
Simon Kelley78c61842015-04-16 15:05:30 +0100254 for (zone = daemon->auth_zones; zone; zone = zone->next)
255 if (in_zone(zone, name, &cut))
256 break;
257
258 if (!zone)
259 {
260 auth = 0;
261 continue;
262 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000263 }
264
Simon Kelley8273ea52012-11-29 21:12:33 +0000265 for (rec = daemon->mxnames; rec; rec = rec->next)
266 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000267 {
268 nxdomain = 0;
269
270 if (qtype == T_MX)
271 {
272 found = 1;
273 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
274 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
275 NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
276 anscount++;
277 }
278 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000279
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000280 for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
281 if (rec->issrv && hostname_isequal(name, rec->name))
282 {
283 nxdomain = 0;
284
285 if (qtype == T_SRV)
286 {
287 found = 1;
288 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
289 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
290 NULL, T_SRV, C_IN, "sssd",
291 rec->priority, rec->weight, rec->srvport, rec->target))
292
293 anscount++;
294 }
295
296 /* unlink first SRV record found */
297 if (!move)
298 {
299 move = rec;
300 *up = rec->next;
301 }
302 else
303 up = &rec->next;
304 }
305 else
306 up = &rec->next;
307
308 /* put first SRV record back at the end. */
309 if (move)
310 {
311 *up = move;
312 move->next = NULL;
313 }
314
315 for (txt = daemon->rr; txt; txt = txt->next)
316 if (hostname_isequal(name, txt->name))
317 {
318 nxdomain = 0;
319 if (txt->class == qtype)
320 {
321 found = 1;
322 log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
323 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
324 NULL, txt->class, C_IN, "t", txt->len, txt->txt))
325 anscount++;
326 }
327 }
328
329 for (txt = daemon->txt; txt; txt = txt->next)
330 if (txt->class == C_IN && hostname_isequal(name, txt->name))
331 {
332 nxdomain = 0;
333 if (qtype == T_TXT)
334 {
335 found = 1;
336 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
337 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
338 NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
339 anscount++;
340 }
341 }
342
343 for (na = daemon->naptr; na; na = na->next)
344 if (hostname_isequal(name, na->name))
345 {
346 nxdomain = 0;
347 if (qtype == T_NAPTR)
348 {
349 found = 1;
350 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
351 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
352 NULL, T_NAPTR, C_IN, "sszzzd",
353 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
354 anscount++;
355 }
356 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100357
358 if (qtype == T_A)
359 flag = F_IPV4;
360
361#ifdef HAVE_IPV6
362 if (qtype == T_AAAA)
363 flag = F_IPV6;
364#endif
365
Simon Kelley8ab91e92013-10-21 20:50:04 +0100366 for (intr = daemon->int_names; intr; intr = intr->next)
367 if (hostname_isequal(name, intr->name))
368 {
369 struct addrlist *addrlist;
370
Simon Kelley8ab91e92013-10-21 20:50:04 +0100371 nxdomain = 0;
372
373 if (flag)
Simon Kelley376d48c2013-11-13 13:04:30 +0000374 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
375 if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
376 (local_query || filter_zone(zone, flag, &addrlist->addr)))
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100377 {
Simon Kelley47669362014-12-17 12:41:56 +0000378#ifdef HAVE_IPV6
379 if (addrlist->flags & ADDRLIST_REVONLY)
380 continue;
381#endif
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100382 found = 1;
383 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
384 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
385 daemon->auth_ttl, NULL, qtype, C_IN,
386 qtype == T_A ? "4" : "6", &addrlist->addr))
387 anscount++;
388 }
389 }
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000390
391 for (a = daemon->cnames; a; a = a->next)
392 if (hostname_isequal(name, a->alias) )
393 {
394 log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
395 strcpy(name, a->target);
396 if (!strchr(name, '.'))
397 {
398 strcat(name, ".");
399 strcat(name, zone->domain);
400 }
401 found = 1;
402 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
Simon Kelley93bafe62013-10-21 21:19:34 +0100403 daemon->auth_ttl, &nameoffset,
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000404 T_CNAME, C_IN, "d", name))
405 anscount++;
406
407 goto cname_restart;
408 }
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000409
Simon Kelleye1ff4192012-12-09 17:08:47 +0000410 if (!cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000411 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000412 nxdomain = 0;
413
414 if (qtype == T_SOA)
415 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100416 auth = soa = 1; /* inhibits auth section */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000417 found = 1;
418 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
419 }
420 else if (qtype == T_AXFR)
421 {
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000422 struct iname *peers;
423
424 if (peer_addr->sa.sa_family == AF_INET)
425 peer_addr->in.sin_port = 0;
426#ifdef HAVE_IPV6
427 else
Simon Kelley39341552015-01-18 22:11:10 +0000428 {
429 peer_addr->in6.sin6_port = 0;
430 peer_addr->in6.sin6_scope_id = 0;
431 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000432#endif
433
434 for (peers = daemon->auth_peers; peers; peers = peers->next)
435 if (sockaddr_isequal(peer_addr, &peers->addr))
436 break;
437
438 /* Refuse all AXFR unless --auth-sec-servers is set */
439 if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
Simon Kelley49678762012-12-09 18:24:58 +0000440 {
Simon Kelley49678762012-12-09 18:24:58 +0000441 if (peer_addr->sa.sa_family == AF_INET)
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000442 inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000443#ifdef HAVE_IPV6
444 else
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000445 inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000446#endif
447
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000448 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
449 return 0;
Simon Kelley49678762012-12-09 18:24:58 +0000450 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000451
Simon Kelley5f8002f2013-10-21 17:40:18 +0100452 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000453 soa = 1; /* inhibits auth section */
454 ns = 1; /* ensure we include NS records! */
455 axfr = 1;
456 found = 1;
457 axfroffset = nameoffset;
458 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
459 }
460 else if (qtype == T_NS)
461 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100462 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000463 ns = 1; /* inhibits auth section */
464 found = 1;
465 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
466 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000467 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000468
Simon Kelleyb75e9362012-12-07 11:50:41 +0000469 if (!option_bool(OPT_DHCP_FQDN) && cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000470 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000471 *cut = 0; /* remove domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000472
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000473 if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000474 {
475 if (crecp->flags & F_DHCP)
476 do
477 {
478 nxdomain = 0;
Simon Kelley60225f42012-12-28 11:29:01 +0000479 if ((crecp->flags & flag) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000480 (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000481 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000482 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000483 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelleyb75e9362012-12-07 11:50:41 +0000484 *cut = 0; /* remove domain part */
Simon Kelley8273ea52012-11-29 21:12:33 +0000485 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000486 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
487 daemon->auth_ttl, NULL, qtype, C_IN,
488 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000489 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000490 }
491 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
492 }
493
Simon Kelleyb75e9362012-12-07 11:50:41 +0000494 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000495 }
496
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000497 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000498 {
499 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
500 do
501 {
502 nxdomain = 0;
Simon Kelley376d48c2013-11-13 13:04:30 +0000503 if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000504 {
505 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000506 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000507 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
508 daemon->auth_ttl, NULL, qtype, C_IN,
509 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000510 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000511 }
512 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
513 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000514
Simon Kelley4f7b3042012-11-28 21:27:02 +0000515 if (!found)
516 log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
Simon Kelley8273ea52012-11-29 21:12:33 +0000517
Simon Kelley4f7b3042012-11-28 21:27:02 +0000518 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000519
Simon Kelley4f7b3042012-11-28 21:27:02 +0000520 /* Add auth section */
Simon Kelleyaa67fe72013-02-04 21:32:34 +0000521 if (auth && zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000522 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000523 char *authname;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000524 int newoffset, offset = 0;
525
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000526 if (!subnet)
Simon Kelleyb75e9362012-12-07 11:50:41 +0000527 authname = zone->domain;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000528 else
529 {
530 /* handle NS and SOA for PTR records */
Simon Kelleyb75e9362012-12-07 11:50:41 +0000531
532 authname = name;
533
Simon Kelley376d48c2013-11-13 13:04:30 +0000534 if (!(subnet->flags & ADDRLIST_IPV6))
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000535 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000536 in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000537 char *p = name;
538
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100539 if (subnet->prefixlen >= 24)
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000540 p += sprintf(p, "%d.", a & 0xff);
541 a = a >> 8;
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100542 if (subnet->prefixlen >= 16 )
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000543 p += sprintf(p, "%d.", a & 0xff);
544 a = a >> 8;
545 p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
546
547 }
548#ifdef HAVE_IPV6
549 else
550 {
551 char *p = name;
552 int i;
553
554 for (i = subnet->prefixlen-1; i >= 0; i -= 4)
555 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000556 int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3];
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000557 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
558 }
559 p += sprintf(p, "ip6.arpa");
560
561 }
562#endif
563 }
564
565 /* handle NS and SOA in auth section or for explicit queries */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000566 newoffset = ansp - (unsigned char *)header;
567 if (((anscount == 0 && !ns) || soa) &&
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000568 add_resource_record(header, limit, &trunc, 0, &ansp,
569 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
Simon Kelleyb75e9362012-12-07 11:50:41 +0000570 authname, daemon->authserver, daemon->hostmaster,
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000571 daemon->soa_sn, daemon->soa_refresh,
572 daemon->soa_retry, daemon->soa_expiry,
573 daemon->auth_ttl))
574 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000575 offset = newoffset;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000576 if (soa)
577 anscount++;
578 else
579 authcount++;
580 }
Simon Kelleye1ff4192012-12-09 17:08:47 +0000581
582 if (anscount != 0 || ns)
583 {
584 struct name_list *secondary;
585
586 newoffset = ansp - (unsigned char *)header;
587 if (add_resource_record(header, limit, &trunc, -offset, &ansp,
588 daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
589 {
590 if (offset == 0)
591 offset = newoffset;
592 if (ns)
593 anscount++;
594 else
595 authcount++;
596 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000597
Simon Kelleye1ff4192012-12-09 17:08:47 +0000598 if (!subnet)
599 for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
600 if (add_resource_record(header, limit, &trunc, offset, &ansp,
601 daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
602 {
603 if (ns)
604 anscount++;
605 else
606 authcount++;
607 }
608 }
609
Simon Kelleyb75e9362012-12-07 11:50:41 +0000610 if (axfr)
611 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000612 for (rec = daemon->mxnames; rec; rec = rec->next)
613 if (in_zone(zone, rec->name, &cut))
614 {
615 if (cut)
616 *cut = 0;
617
618 if (rec->issrv)
619 {
620 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
621 NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
622 rec->priority, rec->weight, rec->srvport, rec->target))
623
624 anscount++;
625 }
626 else
627 {
628 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
629 NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
630 anscount++;
631 }
632
633 /* restore config data */
634 if (cut)
635 *cut = '.';
636 }
637
638 for (txt = daemon->rr; txt; txt = txt->next)
639 if (in_zone(zone, txt->name, &cut))
640 {
641 if (cut)
642 *cut = 0;
643
644 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
645 NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
646 anscount++;
647
648 /* restore config data */
649 if (cut)
650 *cut = '.';
651 }
652
653 for (txt = daemon->txt; txt; txt = txt->next)
654 if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
655 {
656 if (cut)
657 *cut = 0;
658
659 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
660 NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
661 anscount++;
662
663 /* restore config data */
664 if (cut)
665 *cut = '.';
666 }
667
668 for (na = daemon->naptr; na; na = na->next)
669 if (in_zone(zone, na->name, &cut))
670 {
671 if (cut)
672 *cut = 0;
673
674 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
675 NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
676 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
677 anscount++;
678
679 /* restore config data */
680 if (cut)
681 *cut = '.';
682 }
683
684 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100685 if (in_zone(zone, intr->name, &cut))
Simon Kelleye1ff4192012-12-09 17:08:47 +0000686 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100687 struct addrlist *addrlist;
688
Simon Kelleye1ff4192012-12-09 17:08:47 +0000689 if (cut)
690 *cut = 0;
691
Simon Kelley376d48c2013-11-13 13:04:30 +0000692 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000693 if (!(addrlist->flags & ADDRLIST_IPV6) &&
694 (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100695 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
696 daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
697 anscount++;
698
699#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +0000700 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000701 if ((addrlist->flags & ADDRLIST_IPV6) &&
702 (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100703 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
704 daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
705 anscount++;
706#endif
Simon Kelleye1ff4192012-12-09 17:08:47 +0000707
708 /* restore config data */
709 if (cut)
710 *cut = '.';
711 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100712
Simon Kelleye1ff4192012-12-09 17:08:47 +0000713 for (a = daemon->cnames; a; a = a->next)
714 if (in_zone(zone, a->alias, &cut))
715 {
716 strcpy(name, a->target);
717 if (!strchr(name, '.'))
718 {
719 strcat(name, ".");
720 strcat(name, zone->domain);
721 }
722
723 if (cut)
724 *cut = 0;
725
726 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
727 daemon->auth_ttl, NULL,
728 T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
729 anscount++;
730 }
731
Simon Kelleyb75e9362012-12-07 11:50:41 +0000732 cache_enumerate(1);
733 while ((crecp = cache_enumerate(0)))
734 {
735 if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
736 !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
737 (crecp->flags & F_FORWARD))
738 {
739 if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
740 {
741 char *cache_name = cache_get_name(crecp);
Simon Kelley19b16892013-10-20 10:19:39 +0100742 if (!strchr(cache_name, '.') &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000743 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000744 {
745 qtype = T_A;
746#ifdef HAVE_IPV6
747 if (crecp->flags & F_IPV6)
748 qtype = T_AAAA;
749#endif
750 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
751 daemon->auth_ttl, NULL, qtype, C_IN,
752 (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
753 anscount++;
754 }
755 }
756
757 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
758 {
759 strcpy(name, cache_get_name(crecp));
Simon Kelley19b16892013-10-20 10:19:39 +0100760 if (in_zone(zone, name, &cut) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000761 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000762 {
763 qtype = T_A;
764#ifdef HAVE_IPV6
765 if (crecp->flags & F_IPV6)
766 qtype = T_AAAA;
767#endif
768 if (cut)
Simon Kelleye1ff4192012-12-09 17:08:47 +0000769 *cut = 0;
770
771 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
772 daemon->auth_ttl, NULL, qtype, C_IN,
773 (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
774 anscount++;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000775 }
776 }
777 }
778 }
779
780 /* repeat SOA as last record */
781 if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
782 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
783 daemon->authserver, daemon->hostmaster,
784 daemon->soa_sn, daemon->soa_refresh,
785 daemon->soa_retry, daemon->soa_expiry,
786 daemon->auth_ttl))
787 anscount++;
Simon Kelley4c985da2013-03-22 14:07:38 +0000788
Simon Kelleyb75e9362012-12-07 11:50:41 +0000789 }
Simon Kelley4c985da2013-03-22 14:07:38 +0000790
791 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000792
Simon Kelley4f7b3042012-11-28 21:27:02 +0000793 /* done all questions, set up header and return length of result */
794 /* clear authoritative and truncated flags, set QR flag */
795 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
Simon Kelley93bafe62013-10-21 21:19:34 +0100796
797 if (local_query)
798 {
799 /* set RA flag */
800 header->hb4 |= HB4_RA;
801 }
802 else
803 {
804 /* clear RA flag */
805 header->hb4 &= ~HB4_RA;
806 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000807
808 /* authoritive */
809 if (auth)
810 header->hb3 |= HB3_AA;
811
812 /* truncation */
813 if (trunc)
814 header->hb3 |= HB3_TC;
815
Simon Kelley57310502013-10-21 18:26:20 +0100816 if ((auth || local_query) && nxdomain)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000817 SET_RCODE(header, NXDOMAIN);
818 else
819 SET_RCODE(header, NOERROR); /* no error */
820 header->ancount = htons(anscount);
821 header->nscount = htons(authcount);
Simon Kelleyaa792352012-12-06 19:41:35 +0000822 header->arcount = htons(0);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000823 return ansp - (unsigned char *)header;
824}
825
Simon Kelley4820dce2012-12-18 18:30:30 +0000826#endif
Simon Kelleyb75e9362012-12-07 11:50:41 +0000827
Simon Kelley4f7b3042012-11-28 21:27:02 +0000828
829