blob: 15721e52793f793aff9b8e5a91c8c79a88de91d6 [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 Kelley4f7b3042012-11-28 21:27:02 +0000134 if (qtype == T_PTR)
135 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000136 if (!(flag = in_arpa_name_2_addr(name, &addr)))
137 continue;
138
Simon Kelley19b16892013-10-20 10:19:39 +0100139 if (!local_query)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000140 {
Simon Kelley19b16892013-10-20 10:19:39 +0100141 for (zone = daemon->auth_zones; zone; zone = zone->next)
Simon Kelleyc50f25a2013-11-21 11:29:27 +0000142 if ((subnet = find_subnet(zone, flag, &addr)))
Simon Kelley19b16892013-10-20 10:19:39 +0100143 break;
144
145 if (!zone)
146 {
147 auth = 0;
148 continue;
149 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000150 }
Simon Kelley19b16892013-10-20 10:19:39 +0100151
Simon Kelley115ac3e2013-05-20 11:28:32 +0100152 intr = NULL;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000153
Simon Kelley115ac3e2013-05-20 11:28:32 +0100154 if (flag == F_IPV4)
155 for (intr = daemon->int_names; intr; intr = intr->next)
156 {
157 struct addrlist *addrlist;
158
Simon Kelley376d48c2013-11-13 13:04:30 +0000159 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
160 if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100161 break;
162
163 if (addrlist)
164 break;
165 else
166 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
167 intr = intr->next;
168 }
169#ifdef HAVE_IPV6
170 else if (flag == F_IPV6)
171 for (intr = daemon->int_names; intr; intr = intr->next)
172 {
173 struct addrlist *addrlist;
174
Simon Kelley376d48c2013-11-13 13:04:30 +0000175 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
176 if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100177 break;
178
179 if (addrlist)
180 break;
181 else
182 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
183 intr = intr->next;
184 }
185#endif
186
187 if (intr)
188 {
189 if (in_zone(zone, intr->name, NULL))
190 {
191 found = 1;
Simon Kelley8ab91e92013-10-21 20:50:04 +0100192 log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelley115ac3e2013-05-20 11:28:32 +0100193 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
194 daemon->auth_ttl, NULL,
195 T_PTR, C_IN, "d", intr->name))
196 anscount++;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000197 }
198 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100199
Simon Kelley4f7b3042012-11-28 21:27:02 +0000200 if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
201 do {
202 strcpy(name, cache_get_name(crecp));
203
204 if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
205 {
206 char *p = strchr(name, '.');
207 if (p)
208 *p = 0; /* must be bare name */
209
210 /* add external domain */
211 strcat(name, ".");
212 strcat(name, zone->domain);
213 log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000214 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000215 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
216 daemon->auth_ttl, NULL,
217 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000218 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000219 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000220 else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000221 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000222 log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000223 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000224 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
225 daemon->auth_ttl, NULL,
226 T_PTR, C_IN, "d", name))
Simon Kelley8273ea52012-11-29 21:12:33 +0000227 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000228 }
229 else
230 continue;
231
232 } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000233
Simon Kelley10068602014-04-03 21:16:40 +0100234 if (found)
235 nxdomain = 0;
236 else
237 log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000238
239 continue;
240 }
241
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000242 cname_restart:
Simon Kelley4f7b3042012-11-28 21:27:02 +0000243 for (zone = daemon->auth_zones; zone; zone = zone->next)
Simon Kelleyb75e9362012-12-07 11:50:41 +0000244 if (in_zone(zone, name, &cut))
245 break;
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000246
247 if (!zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000248 {
249 auth = 0;
250 continue;
251 }
252
Simon Kelley8273ea52012-11-29 21:12:33 +0000253 for (rec = daemon->mxnames; rec; rec = rec->next)
254 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000255 {
256 nxdomain = 0;
257
258 if (qtype == T_MX)
259 {
260 found = 1;
261 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
262 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
263 NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
264 anscount++;
265 }
266 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000267
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000268 for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
269 if (rec->issrv && hostname_isequal(name, rec->name))
270 {
271 nxdomain = 0;
272
273 if (qtype == T_SRV)
274 {
275 found = 1;
276 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
277 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
278 NULL, T_SRV, C_IN, "sssd",
279 rec->priority, rec->weight, rec->srvport, rec->target))
280
281 anscount++;
282 }
283
284 /* unlink first SRV record found */
285 if (!move)
286 {
287 move = rec;
288 *up = rec->next;
289 }
290 else
291 up = &rec->next;
292 }
293 else
294 up = &rec->next;
295
296 /* put first SRV record back at the end. */
297 if (move)
298 {
299 *up = move;
300 move->next = NULL;
301 }
302
303 for (txt = daemon->rr; txt; txt = txt->next)
304 if (hostname_isequal(name, txt->name))
305 {
306 nxdomain = 0;
307 if (txt->class == qtype)
308 {
309 found = 1;
310 log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
311 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
312 NULL, txt->class, C_IN, "t", txt->len, txt->txt))
313 anscount++;
314 }
315 }
316
317 for (txt = daemon->txt; txt; txt = txt->next)
318 if (txt->class == C_IN && hostname_isequal(name, txt->name))
319 {
320 nxdomain = 0;
321 if (qtype == T_TXT)
322 {
323 found = 1;
324 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
325 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
326 NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
327 anscount++;
328 }
329 }
330
331 for (na = daemon->naptr; na; na = na->next)
332 if (hostname_isequal(name, na->name))
333 {
334 nxdomain = 0;
335 if (qtype == T_NAPTR)
336 {
337 found = 1;
338 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
339 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
340 NULL, T_NAPTR, C_IN, "sszzzd",
341 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
342 anscount++;
343 }
344 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100345
346 if (qtype == T_A)
347 flag = F_IPV4;
348
349#ifdef HAVE_IPV6
350 if (qtype == T_AAAA)
351 flag = F_IPV6;
352#endif
353
Simon Kelley8ab91e92013-10-21 20:50:04 +0100354 for (intr = daemon->int_names; intr; intr = intr->next)
355 if (hostname_isequal(name, intr->name))
356 {
357 struct addrlist *addrlist;
358
Simon Kelley8ab91e92013-10-21 20:50:04 +0100359 nxdomain = 0;
360
361 if (flag)
Simon Kelley376d48c2013-11-13 13:04:30 +0000362 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
363 if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
364 (local_query || filter_zone(zone, flag, &addrlist->addr)))
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100365 {
Simon Kelley47669362014-12-17 12:41:56 +0000366#ifdef HAVE_IPV6
367 if (addrlist->flags & ADDRLIST_REVONLY)
368 continue;
369#endif
Simon Kelleyfb63dd12013-10-21 18:19:35 +0100370 found = 1;
371 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
372 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
373 daemon->auth_ttl, NULL, qtype, C_IN,
374 qtype == T_A ? "4" : "6", &addrlist->addr))
375 anscount++;
376 }
377 }
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000378
379 for (a = daemon->cnames; a; a = a->next)
380 if (hostname_isequal(name, a->alias) )
381 {
382 log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
383 strcpy(name, a->target);
384 if (!strchr(name, '.'))
385 {
386 strcat(name, ".");
387 strcat(name, zone->domain);
388 }
389 found = 1;
390 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
Simon Kelley93bafe62013-10-21 21:19:34 +0100391 daemon->auth_ttl, &nameoffset,
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000392 T_CNAME, C_IN, "d", name))
393 anscount++;
394
395 goto cname_restart;
396 }
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000397
Simon Kelleye1ff4192012-12-09 17:08:47 +0000398 if (!cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000399 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000400 nxdomain = 0;
401
402 if (qtype == T_SOA)
403 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100404 auth = soa = 1; /* inhibits auth section */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000405 found = 1;
406 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
407 }
408 else if (qtype == T_AXFR)
409 {
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000410 struct iname *peers;
411
412 if (peer_addr->sa.sa_family == AF_INET)
413 peer_addr->in.sin_port = 0;
414#ifdef HAVE_IPV6
415 else
Simon Kelley39341552015-01-18 22:11:10 +0000416 {
417 peer_addr->in6.sin6_port = 0;
418 peer_addr->in6.sin6_scope_id = 0;
419 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000420#endif
421
422 for (peers = daemon->auth_peers; peers; peers = peers->next)
423 if (sockaddr_isequal(peer_addr, &peers->addr))
424 break;
425
426 /* Refuse all AXFR unless --auth-sec-servers is set */
427 if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
Simon Kelley49678762012-12-09 18:24:58 +0000428 {
Simon Kelley49678762012-12-09 18:24:58 +0000429 if (peer_addr->sa.sa_family == AF_INET)
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000430 inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000431#ifdef HAVE_IPV6
432 else
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000433 inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley49678762012-12-09 18:24:58 +0000434#endif
435
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000436 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
437 return 0;
Simon Kelley49678762012-12-09 18:24:58 +0000438 }
Simon Kelleyc6cb7402013-01-07 21:55:54 +0000439
Simon Kelley5f8002f2013-10-21 17:40:18 +0100440 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000441 soa = 1; /* inhibits auth section */
442 ns = 1; /* ensure we include NS records! */
443 axfr = 1;
444 found = 1;
445 axfroffset = nameoffset;
446 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
447 }
448 else if (qtype == T_NS)
449 {
Simon Kelley5f8002f2013-10-21 17:40:18 +0100450 auth = 1;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000451 ns = 1; /* inhibits auth section */
452 found = 1;
453 log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
454 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000455 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000456
Simon Kelleyb75e9362012-12-07 11:50:41 +0000457 if (!option_bool(OPT_DHCP_FQDN) && cut)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000458 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000459 *cut = 0; /* remove domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000460
Simon Kelley5c0bd5b2012-12-01 16:42:47 +0000461 if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000462 {
463 if (crecp->flags & F_DHCP)
464 do
465 {
466 nxdomain = 0;
Simon Kelley60225f42012-12-28 11:29:01 +0000467 if ((crecp->flags & flag) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000468 (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000469 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000470 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000471 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelleyb75e9362012-12-07 11:50:41 +0000472 *cut = 0; /* remove domain part */
Simon Kelley8273ea52012-11-29 21:12:33 +0000473 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000474 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
475 daemon->auth_ttl, NULL, qtype, C_IN,
476 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000477 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000478 }
479 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
480 }
481
Simon Kelleyb75e9362012-12-07 11:50:41 +0000482 *cut = '.'; /* restore domain part */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000483 }
484
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000485 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000486 {
487 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
488 do
489 {
490 nxdomain = 0;
Simon Kelley376d48c2013-11-13 13:04:30 +0000491 if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
Simon Kelley4f7b3042012-11-28 21:27:02 +0000492 {
493 log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
Simon Kelley8273ea52012-11-29 21:12:33 +0000494 found = 1;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000495 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
496 daemon->auth_ttl, NULL, qtype, C_IN,
497 qtype == T_A ? "4" : "6", &crecp->addr))
Simon Kelley8273ea52012-11-29 21:12:33 +0000498 anscount++;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000499 }
500 } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
501 }
Simon Kelley8273ea52012-11-29 21:12:33 +0000502
Simon Kelley4f7b3042012-11-28 21:27:02 +0000503 if (!found)
504 log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
Simon Kelley8273ea52012-11-29 21:12:33 +0000505
Simon Kelley4f7b3042012-11-28 21:27:02 +0000506 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000507
Simon Kelley4f7b3042012-11-28 21:27:02 +0000508 /* Add auth section */
Simon Kelleyaa67fe72013-02-04 21:32:34 +0000509 if (auth && zone)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000510 {
Simon Kelleyb75e9362012-12-07 11:50:41 +0000511 char *authname;
Simon Kelleye1ff4192012-12-09 17:08:47 +0000512 int newoffset, offset = 0;
513
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000514 if (!subnet)
Simon Kelleyb75e9362012-12-07 11:50:41 +0000515 authname = zone->domain;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000516 else
517 {
518 /* handle NS and SOA for PTR records */
Simon Kelleyb75e9362012-12-07 11:50:41 +0000519
520 authname = name;
521
Simon Kelley376d48c2013-11-13 13:04:30 +0000522 if (!(subnet->flags & ADDRLIST_IPV6))
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000523 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000524 in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000525 char *p = name;
526
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100527 if (subnet->prefixlen >= 24)
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000528 p += sprintf(p, "%d.", a & 0xff);
529 a = a >> 8;
Simon Kelleybaa80ae2013-05-29 16:32:07 +0100530 if (subnet->prefixlen >= 16 )
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000531 p += sprintf(p, "%d.", a & 0xff);
532 a = a >> 8;
533 p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
534
535 }
536#ifdef HAVE_IPV6
537 else
538 {
539 char *p = name;
540 int i;
541
542 for (i = subnet->prefixlen-1; i >= 0; i -= 4)
543 {
Simon Kelley376d48c2013-11-13 13:04:30 +0000544 int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3];
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000545 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
546 }
547 p += sprintf(p, "ip6.arpa");
548
549 }
550#endif
551 }
552
553 /* handle NS and SOA in auth section or for explicit queries */
Simon Kelleye1ff4192012-12-09 17:08:47 +0000554 newoffset = ansp - (unsigned char *)header;
555 if (((anscount == 0 && !ns) || soa) &&
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000556 add_resource_record(header, limit, &trunc, 0, &ansp,
557 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
Simon Kelleyb75e9362012-12-07 11:50:41 +0000558 authname, daemon->authserver, daemon->hostmaster,
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000559 daemon->soa_sn, daemon->soa_refresh,
560 daemon->soa_retry, daemon->soa_expiry,
561 daemon->auth_ttl))
562 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000563 offset = newoffset;
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000564 if (soa)
565 anscount++;
566 else
567 authcount++;
568 }
Simon Kelleye1ff4192012-12-09 17:08:47 +0000569
570 if (anscount != 0 || ns)
571 {
572 struct name_list *secondary;
573
574 newoffset = ansp - (unsigned char *)header;
575 if (add_resource_record(header, limit, &trunc, -offset, &ansp,
576 daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
577 {
578 if (offset == 0)
579 offset = newoffset;
580 if (ns)
581 anscount++;
582 else
583 authcount++;
584 }
Simon Kelleyb75e9362012-12-07 11:50:41 +0000585
Simon Kelleye1ff4192012-12-09 17:08:47 +0000586 if (!subnet)
587 for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
588 if (add_resource_record(header, limit, &trunc, offset, &ansp,
589 daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
590 {
591 if (ns)
592 anscount++;
593 else
594 authcount++;
595 }
596 }
597
Simon Kelleyb75e9362012-12-07 11:50:41 +0000598 if (axfr)
599 {
Simon Kelleye1ff4192012-12-09 17:08:47 +0000600 for (rec = daemon->mxnames; rec; rec = rec->next)
601 if (in_zone(zone, rec->name, &cut))
602 {
603 if (cut)
604 *cut = 0;
605
606 if (rec->issrv)
607 {
608 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
609 NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
610 rec->priority, rec->weight, rec->srvport, rec->target))
611
612 anscount++;
613 }
614 else
615 {
616 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
617 NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
618 anscount++;
619 }
620
621 /* restore config data */
622 if (cut)
623 *cut = '.';
624 }
625
626 for (txt = daemon->rr; txt; txt = txt->next)
627 if (in_zone(zone, txt->name, &cut))
628 {
629 if (cut)
630 *cut = 0;
631
632 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
633 NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
634 anscount++;
635
636 /* restore config data */
637 if (cut)
638 *cut = '.';
639 }
640
641 for (txt = daemon->txt; txt; txt = txt->next)
642 if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
643 {
644 if (cut)
645 *cut = 0;
646
647 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
648 NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
649 anscount++;
650
651 /* restore config data */
652 if (cut)
653 *cut = '.';
654 }
655
656 for (na = daemon->naptr; na; na = na->next)
657 if (in_zone(zone, na->name, &cut))
658 {
659 if (cut)
660 *cut = 0;
661
662 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
663 NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
664 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
665 anscount++;
666
667 /* restore config data */
668 if (cut)
669 *cut = '.';
670 }
671
672 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley115ac3e2013-05-20 11:28:32 +0100673 if (in_zone(zone, intr->name, &cut))
Simon Kelleye1ff4192012-12-09 17:08:47 +0000674 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100675 struct addrlist *addrlist;
676
Simon Kelleye1ff4192012-12-09 17:08:47 +0000677 if (cut)
678 *cut = 0;
679
Simon Kelley376d48c2013-11-13 13:04:30 +0000680 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000681 if (!(addrlist->flags & ADDRLIST_IPV6) &&
682 (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100683 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
684 daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
685 anscount++;
686
687#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +0000688 for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
Simon Kelley587ad4f2013-11-15 15:47:51 +0000689 if ((addrlist->flags & ADDRLIST_IPV6) &&
690 (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
Simon Kelley115ac3e2013-05-20 11:28:32 +0100691 add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
692 daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
693 anscount++;
694#endif
Simon Kelleye1ff4192012-12-09 17:08:47 +0000695
696 /* restore config data */
697 if (cut)
698 *cut = '.';
699 }
Simon Kelley115ac3e2013-05-20 11:28:32 +0100700
Simon Kelleye1ff4192012-12-09 17:08:47 +0000701 for (a = daemon->cnames; a; a = a->next)
702 if (in_zone(zone, a->alias, &cut))
703 {
704 strcpy(name, a->target);
705 if (!strchr(name, '.'))
706 {
707 strcat(name, ".");
708 strcat(name, zone->domain);
709 }
710
711 if (cut)
712 *cut = 0;
713
714 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
715 daemon->auth_ttl, NULL,
716 T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
717 anscount++;
718 }
719
Simon Kelleyb75e9362012-12-07 11:50:41 +0000720 cache_enumerate(1);
721 while ((crecp = cache_enumerate(0)))
722 {
723 if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
724 !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
725 (crecp->flags & F_FORWARD))
726 {
727 if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
728 {
729 char *cache_name = cache_get_name(crecp);
Simon Kelley19b16892013-10-20 10:19:39 +0100730 if (!strchr(cache_name, '.') &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000731 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000732 {
733 qtype = T_A;
734#ifdef HAVE_IPV6
735 if (crecp->flags & F_IPV6)
736 qtype = T_AAAA;
737#endif
738 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
739 daemon->auth_ttl, NULL, qtype, C_IN,
740 (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
741 anscount++;
742 }
743 }
744
745 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
746 {
747 strcpy(name, cache_get_name(crecp));
Simon Kelley19b16892013-10-20 10:19:39 +0100748 if (in_zone(zone, name, &cut) &&
Simon Kelley376d48c2013-11-13 13:04:30 +0000749 (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
Simon Kelleyb75e9362012-12-07 11:50:41 +0000750 {
751 qtype = T_A;
752#ifdef HAVE_IPV6
753 if (crecp->flags & F_IPV6)
754 qtype = T_AAAA;
755#endif
756 if (cut)
Simon Kelleye1ff4192012-12-09 17:08:47 +0000757 *cut = 0;
758
759 if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
760 daemon->auth_ttl, NULL, qtype, C_IN,
761 (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
762 anscount++;
Simon Kelleyb75e9362012-12-07 11:50:41 +0000763 }
764 }
765 }
766 }
767
768 /* repeat SOA as last record */
769 if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
770 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
771 daemon->authserver, daemon->hostmaster,
772 daemon->soa_sn, daemon->soa_refresh,
773 daemon->soa_retry, daemon->soa_expiry,
774 daemon->auth_ttl))
775 anscount++;
Simon Kelley4c985da2013-03-22 14:07:38 +0000776
Simon Kelleyb75e9362012-12-07 11:50:41 +0000777 }
Simon Kelley4c985da2013-03-22 14:07:38 +0000778
779 }
Simon Kelley45dd1fe2012-12-04 20:49:24 +0000780
Simon Kelley4f7b3042012-11-28 21:27:02 +0000781 /* done all questions, set up header and return length of result */
782 /* clear authoritative and truncated flags, set QR flag */
783 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
Simon Kelley93bafe62013-10-21 21:19:34 +0100784
785 if (local_query)
786 {
787 /* set RA flag */
788 header->hb4 |= HB4_RA;
789 }
790 else
791 {
792 /* clear RA flag */
793 header->hb4 &= ~HB4_RA;
794 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000795
796 /* authoritive */
797 if (auth)
798 header->hb3 |= HB3_AA;
799
800 /* truncation */
801 if (trunc)
802 header->hb3 |= HB3_TC;
803
Simon Kelley57310502013-10-21 18:26:20 +0100804 if ((auth || local_query) && nxdomain)
Simon Kelley4f7b3042012-11-28 21:27:02 +0000805 SET_RCODE(header, NXDOMAIN);
806 else
807 SET_RCODE(header, NOERROR); /* no error */
808 header->ancount = htons(anscount);
809 header->nscount = htons(authcount);
Simon Kelleyaa792352012-12-06 19:41:35 +0000810 header->arcount = htons(0);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000811 return ansp - (unsigned char *)header;
812}
813
Simon Kelley4820dce2012-12-18 18:30:30 +0000814#endif
Simon Kelleyb75e9362012-12-07 11:50:41 +0000815
Simon Kelley4f7b3042012-11-28 21:27:02 +0000816
817