]>
Commit | Line | Data |
---|---|---|
c47e3ba4 | 1 | /* dnsmasq is Copyright (c) 2000-2014 Simon Kelley |
4f7b304f SK |
2 | |
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 | ||
4820dce9 | 19 | #ifdef HAVE_AUTH |
b75e9363 | 20 | |
c50f25a3 | 21 | static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u) |
4f7b304f | 22 | { |
376d48c7 | 23 | struct addrlist *subnet; |
4f7b304f SK |
24 | |
25 | for (subnet = zone->subnet; subnet; subnet = subnet->next) | |
26 | { | |
376d48c7 | 27 | if (!(subnet->flags & ADDRLIST_IPV6)) |
4f7b304f | 28 | { |
376d48c7 SK |
29 | struct in_addr netmask, addr = addr_u->addr.addr4; |
30 | ||
31 | if (!(flag & F_IPV4)) | |
32 | continue; | |
4f7b304f | 33 | |
376d48c7 | 34 | netmask.s_addr = htonl(~((1 << (32 - subnet->prefixlen)) - 1)); |
4f7b304f | 35 | |
376d48c7 | 36 | if (is_same_net(addr, subnet->addr.addr.addr4, netmask)) |
45dd1fec | 37 | return subnet; |
4f7b304f SK |
38 | } |
39 | #ifdef HAVE_IPV6 | |
376d48c7 | 40 | else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen)) |
45dd1fec | 41 | return subnet; |
4f7b304f SK |
42 | #endif |
43 | ||
44 | } | |
45dd1fec | 45 | return NULL; |
4f7b304f SK |
46 | } |
47 | ||
c50f25a3 SK |
48 | static 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 | ||
b485ed97 | 57 | int in_zone(struct auth_zone *zone, char *name, char **cut) |
b75e9363 SK |
58 | { |
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 | } | |
4f7b304f SK |
82 | |
83 | ||
19b16891 | 84 | size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query) |
4f7b304f SK |
85 | { |
86 | char *name = daemon->namebuff; | |
87 | unsigned char *p, *ansp; | |
88 | int qtype, qclass; | |
b75e9363 | 89 | int nameoffset, axfroffset = 0; |
4f7b304f SK |
90 | int q, anscount = 0, authcount = 0; |
91 | struct crec *crecp; | |
19b16891 | 92 | int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0; |
4f7b304f | 93 | struct auth_zone *zone = NULL; |
376d48c7 | 94 | struct addrlist *subnet = NULL; |
b75e9363 | 95 | char *cut; |
e1ff419c SK |
96 | 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 | ||
4f7b304f SK |
103 | if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) |
104 | return 0; | |
6008bdbb | 105 | |
4f7b304f SK |
106 | /* 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 | { | |
8273ea5a | 115 | unsigned short flag = 0; |
4f7b304f | 116 | int found = 0; |
e1ff419c | 117 | |
4f7b304f SK |
118 | /* 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) | |
f8abe0c5 SK |
129 | { |
130 | auth = 0; | |
131 | continue; | |
132 | } | |
b75e9363 | 133 | |
4f7b304f SK |
134 | if (qtype == T_PTR) |
135 | { | |
4f7b304f SK |
136 | if (!(flag = in_arpa_name_2_addr(name, &addr))) |
137 | continue; | |
138 | ||
19b16891 | 139 | if (!local_query) |
4f7b304f | 140 | { |
19b16891 | 141 | for (zone = daemon->auth_zones; zone; zone = zone->next) |
c50f25a3 | 142 | if ((subnet = find_subnet(zone, flag, &addr))) |
19b16891 SK |
143 | break; |
144 | ||
145 | if (!zone) | |
146 | { | |
147 | auth = 0; | |
148 | continue; | |
149 | } | |
4f7b304f | 150 | } |
19b16891 | 151 | |
115ac3e4 SK |
152 | intr = NULL; |
153 | ||
86e3b9a0 | 154 | if (flag == F_IPV4) |
115ac3e4 SK |
155 | for (intr = daemon->int_names; intr; intr = intr->next) |
156 | { | |
157 | struct addrlist *addrlist; | |
158 | ||
376d48c7 SK |
159 | 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) | |
86e3b9a0 | 161 | break; |
115ac3e4 SK |
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 | ||
376d48c7 SK |
175 | 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)) | |
115ac3e4 SK |
177 | 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; | |
8ab91e9f | 192 | log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL); |
115ac3e4 SK |
193 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
194 | daemon->auth_ttl, NULL, | |
195 | T_PTR, C_IN, "d", intr->name)) | |
196 | anscount++; | |
86e3b9a0 SK |
197 | } |
198 | } | |
115ac3e4 | 199 | |
4f7b304f SK |
200 | 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)); | |
8273ea5a | 214 | found = 1; |
4f7b304f SK |
215 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
216 | daemon->auth_ttl, NULL, | |
217 | T_PTR, C_IN, "d", name)) | |
8273ea5a | 218 | anscount++; |
4f7b304f | 219 | } |
b75e9363 | 220 | else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL)) |
4f7b304f | 221 | { |
4f7b304f | 222 | log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid)); |
8273ea5a | 223 | found = 1; |
4f7b304f SK |
224 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
225 | daemon->auth_ttl, NULL, | |
226 | T_PTR, C_IN, "d", name)) | |
8273ea5a | 227 | anscount++; |
4f7b304f SK |
228 | } |
229 | else | |
230 | continue; | |
231 | ||
232 | } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag))); | |
86e3b9a0 | 233 | |
10068600 SK |
234 | if (found) |
235 | nxdomain = 0; | |
236 | else | |
237 | log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL); | |
4f7b304f SK |
238 | |
239 | continue; | |
240 | } | |
241 | ||
5c0bd5b1 | 242 | cname_restart: |
4f7b304f | 243 | for (zone = daemon->auth_zones; zone; zone = zone->next) |
b75e9363 SK |
244 | if (in_zone(zone, name, &cut)) |
245 | break; | |
86e3b9a0 SK |
246 | |
247 | if (!zone) | |
4f7b304f SK |
248 | { |
249 | auth = 0; | |
250 | continue; | |
251 | } | |
252 | ||
8273ea5a SK |
253 | for (rec = daemon->mxnames; rec; rec = rec->next) |
254 | if (!rec->issrv && hostname_isequal(name, rec->name)) | |
86e3b9a0 SK |
255 | { |
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 | } | |
267 | ||
268 | 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)) | |
4f7b304f | 280 | |
86e3b9a0 SK |
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) | |
4f7b304f | 298 | { |
86e3b9a0 SK |
299 | *up = move; |
300 | move->next = NULL; | |
8273ea5a | 301 | } |
86e3b9a0 SK |
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 | } | |
8273ea5a | 316 | |
86e3b9a0 SK |
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 | } | |
115ac3e4 SK |
345 | |
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 | ||
8ab91e9f SK |
354 | for (intr = daemon->int_names; intr; intr = intr->next) |
355 | if (hostname_isequal(name, intr->name)) | |
356 | { | |
357 | struct addrlist *addrlist; | |
358 | ||
8ab91e9f SK |
359 | nxdomain = 0; |
360 | ||
361 | if (flag) | |
376d48c7 SK |
362 | 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))) | |
fb63dd13 SK |
365 | { |
366 | found = 1; | |
367 | log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL); | |
368 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, | |
369 | daemon->auth_ttl, NULL, qtype, C_IN, | |
370 | qtype == T_A ? "4" : "6", &addrlist->addr)) | |
371 | anscount++; | |
372 | } | |
373 | } | |
5c0bd5b1 SK |
374 | |
375 | for (a = daemon->cnames; a; a = a->next) | |
376 | if (hostname_isequal(name, a->alias) ) | |
377 | { | |
378 | log_query(F_CONFIG | F_CNAME, name, NULL, NULL); | |
379 | strcpy(name, a->target); | |
380 | if (!strchr(name, '.')) | |
381 | { | |
382 | strcat(name, "."); | |
383 | strcat(name, zone->domain); | |
384 | } | |
385 | found = 1; | |
386 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, | |
93bafe61 | 387 | daemon->auth_ttl, &nameoffset, |
5c0bd5b1 SK |
388 | T_CNAME, C_IN, "d", name)) |
389 | anscount++; | |
390 | ||
391 | goto cname_restart; | |
392 | } | |
86e3b9a0 | 393 | |
e1ff419c | 394 | if (!cut) |
8273ea5a | 395 | { |
e1ff419c SK |
396 | nxdomain = 0; |
397 | ||
398 | if (qtype == T_SOA) | |
399 | { | |
5f8002fc | 400 | auth = soa = 1; /* inhibits auth section */ |
e1ff419c SK |
401 | found = 1; |
402 | log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>"); | |
403 | } | |
404 | else if (qtype == T_AXFR) | |
405 | { | |
c6cb7407 SK |
406 | struct iname *peers; |
407 | ||
408 | if (peer_addr->sa.sa_family == AF_INET) | |
409 | peer_addr->in.sin_port = 0; | |
410 | #ifdef HAVE_IPV6 | |
411 | else | |
412 | peer_addr->in6.sin6_port = 0; | |
413 | #endif | |
414 | ||
415 | for (peers = daemon->auth_peers; peers; peers = peers->next) | |
416 | if (sockaddr_isequal(peer_addr, &peers->addr)) | |
417 | break; | |
418 | ||
419 | /* Refuse all AXFR unless --auth-sec-servers is set */ | |
420 | if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server) | |
49678767 | 421 | { |
49678767 | 422 | if (peer_addr->sa.sa_family == AF_INET) |
c6cb7407 | 423 | inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN); |
49678767 SK |
424 | #ifdef HAVE_IPV6 |
425 | else | |
c6cb7407 | 426 | inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN); |
49678767 SK |
427 | #endif |
428 | ||
c6cb7407 SK |
429 | my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff); |
430 | return 0; | |
49678767 | 431 | } |
c6cb7407 | 432 | |
5f8002fc | 433 | auth = 1; |
e1ff419c SK |
434 | soa = 1; /* inhibits auth section */ |
435 | ns = 1; /* ensure we include NS records! */ | |
436 | axfr = 1; | |
437 | found = 1; | |
438 | axfroffset = nameoffset; | |
439 | log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>"); | |
440 | } | |
441 | else if (qtype == T_NS) | |
442 | { | |
5f8002fc | 443 | auth = 1; |
e1ff419c SK |
444 | ns = 1; /* inhibits auth section */ |
445 | found = 1; | |
446 | log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>"); | |
447 | } | |
4f7b304f | 448 | } |
8273ea5a | 449 | |
b75e9363 | 450 | if (!option_bool(OPT_DHCP_FQDN) && cut) |
4f7b304f | 451 | { |
b75e9363 | 452 | *cut = 0; /* remove domain part */ |
4f7b304f | 453 | |
5c0bd5b1 | 454 | if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6))) |
4f7b304f SK |
455 | { |
456 | if (crecp->flags & F_DHCP) | |
457 | do | |
458 | { | |
459 | nxdomain = 0; | |
60225f4e | 460 | if ((crecp->flags & flag) && |
376d48c7 | 461 | (local_query || filter_zone(zone, flag, &(crecp->addr.addr)))) |
4f7b304f | 462 | { |
b75e9363 | 463 | *cut = '.'; /* restore domain part */ |
4f7b304f | 464 | log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid)); |
b75e9363 | 465 | *cut = 0; /* remove domain part */ |
8273ea5a | 466 | found = 1; |
4f7b304f SK |
467 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
468 | daemon->auth_ttl, NULL, qtype, C_IN, | |
469 | qtype == T_A ? "4" : "6", &crecp->addr)) | |
8273ea5a | 470 | anscount++; |
4f7b304f SK |
471 | } |
472 | } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6))); | |
473 | } | |
474 | ||
b75e9363 | 475 | *cut = '.'; /* restore domain part */ |
4f7b304f SK |
476 | } |
477 | ||
86e3b9a0 | 478 | if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6))) |
4f7b304f SK |
479 | { |
480 | if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN)))) | |
481 | do | |
482 | { | |
483 | nxdomain = 0; | |
376d48c7 | 484 | if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr)))) |
4f7b304f SK |
485 | { |
486 | log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid)); | |
8273ea5a | 487 | found = 1; |
4f7b304f SK |
488 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
489 | daemon->auth_ttl, NULL, qtype, C_IN, | |
490 | qtype == T_A ? "4" : "6", &crecp->addr)) | |
8273ea5a | 491 | anscount++; |
4f7b304f SK |
492 | } |
493 | } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6))); | |
494 | } | |
8273ea5a | 495 | |
4f7b304f SK |
496 | if (!found) |
497 | log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL); | |
8273ea5a | 498 | |
4f7b304f | 499 | } |
45dd1fec | 500 | |
4f7b304f | 501 | /* Add auth section */ |
aa67fe7a | 502 | if (auth && zone) |
4f7b304f | 503 | { |
b75e9363 | 504 | char *authname; |
e1ff419c SK |
505 | int newoffset, offset = 0; |
506 | ||
45dd1fec | 507 | if (!subnet) |
b75e9363 | 508 | authname = zone->domain; |
45dd1fec SK |
509 | else |
510 | { | |
511 | /* handle NS and SOA for PTR records */ | |
b75e9363 SK |
512 | |
513 | authname = name; | |
514 | ||
376d48c7 | 515 | if (!(subnet->flags & ADDRLIST_IPV6)) |
45dd1fec | 516 | { |
376d48c7 | 517 | in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8; |
45dd1fec SK |
518 | char *p = name; |
519 | ||
baa80ae5 | 520 | if (subnet->prefixlen >= 24) |
45dd1fec SK |
521 | p += sprintf(p, "%d.", a & 0xff); |
522 | a = a >> 8; | |
baa80ae5 | 523 | if (subnet->prefixlen >= 16 ) |
45dd1fec SK |
524 | p += sprintf(p, "%d.", a & 0xff); |
525 | a = a >> 8; | |
526 | p += sprintf(p, "%d.in-addr.arpa", a & 0xff); | |
527 | ||
528 | } | |
529 | #ifdef HAVE_IPV6 | |
530 | else | |
531 | { | |
532 | char *p = name; | |
533 | int i; | |
534 | ||
535 | for (i = subnet->prefixlen-1; i >= 0; i -= 4) | |
536 | { | |
376d48c7 | 537 | int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3]; |
45dd1fec SK |
538 | p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); |
539 | } | |
540 | p += sprintf(p, "ip6.arpa"); | |
541 | ||
542 | } | |
543 | #endif | |
544 | } | |
545 | ||
546 | /* handle NS and SOA in auth section or for explicit queries */ | |
e1ff419c SK |
547 | newoffset = ansp - (unsigned char *)header; |
548 | if (((anscount == 0 && !ns) || soa) && | |
45dd1fec SK |
549 | add_resource_record(header, limit, &trunc, 0, &ansp, |
550 | daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll", | |
b75e9363 | 551 | authname, daemon->authserver, daemon->hostmaster, |
45dd1fec SK |
552 | daemon->soa_sn, daemon->soa_refresh, |
553 | daemon->soa_retry, daemon->soa_expiry, | |
554 | daemon->auth_ttl)) | |
555 | { | |
e1ff419c | 556 | offset = newoffset; |
45dd1fec SK |
557 | if (soa) |
558 | anscount++; | |
559 | else | |
560 | authcount++; | |
561 | } | |
e1ff419c SK |
562 | |
563 | if (anscount != 0 || ns) | |
564 | { | |
565 | struct name_list *secondary; | |
566 | ||
567 | newoffset = ansp - (unsigned char *)header; | |
568 | if (add_resource_record(header, limit, &trunc, -offset, &ansp, | |
569 | daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver)) | |
570 | { | |
571 | if (offset == 0) | |
572 | offset = newoffset; | |
573 | if (ns) | |
574 | anscount++; | |
575 | else | |
576 | authcount++; | |
577 | } | |
b75e9363 | 578 | |
e1ff419c SK |
579 | if (!subnet) |
580 | for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next) | |
581 | if (add_resource_record(header, limit, &trunc, offset, &ansp, | |
582 | daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name)) | |
583 | { | |
584 | if (ns) | |
585 | anscount++; | |
586 | else | |
587 | authcount++; | |
588 | } | |
589 | } | |
590 | ||
b75e9363 SK |
591 | if (axfr) |
592 | { | |
e1ff419c SK |
593 | for (rec = daemon->mxnames; rec; rec = rec->next) |
594 | if (in_zone(zone, rec->name, &cut)) | |
595 | { | |
596 | if (cut) | |
597 | *cut = 0; | |
598 | ||
599 | if (rec->issrv) | |
600 | { | |
601 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, | |
602 | NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL, | |
603 | rec->priority, rec->weight, rec->srvport, rec->target)) | |
604 | ||
605 | anscount++; | |
606 | } | |
607 | else | |
608 | { | |
609 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, | |
610 | NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target)) | |
611 | anscount++; | |
612 | } | |
613 | ||
614 | /* restore config data */ | |
615 | if (cut) | |
616 | *cut = '.'; | |
617 | } | |
618 | ||
619 | for (txt = daemon->rr; txt; txt = txt->next) | |
620 | if (in_zone(zone, txt->name, &cut)) | |
621 | { | |
622 | if (cut) | |
623 | *cut = 0; | |
624 | ||
625 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, | |
626 | NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt)) | |
627 | anscount++; | |
628 | ||
629 | /* restore config data */ | |
630 | if (cut) | |
631 | *cut = '.'; | |
632 | } | |
633 | ||
634 | for (txt = daemon->txt; txt; txt = txt->next) | |
635 | if (txt->class == C_IN && in_zone(zone, txt->name, &cut)) | |
636 | { | |
637 | if (cut) | |
638 | *cut = 0; | |
639 | ||
640 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, | |
641 | NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt)) | |
642 | anscount++; | |
643 | ||
644 | /* restore config data */ | |
645 | if (cut) | |
646 | *cut = '.'; | |
647 | } | |
648 | ||
649 | for (na = daemon->naptr; na; na = na->next) | |
650 | if (in_zone(zone, na->name, &cut)) | |
651 | { | |
652 | if (cut) | |
653 | *cut = 0; | |
654 | ||
655 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, | |
656 | NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL, | |
657 | na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) | |
658 | anscount++; | |
659 | ||
660 | /* restore config data */ | |
661 | if (cut) | |
662 | *cut = '.'; | |
663 | } | |
664 | ||
665 | for (intr = daemon->int_names; intr; intr = intr->next) | |
115ac3e4 | 666 | if (in_zone(zone, intr->name, &cut)) |
e1ff419c | 667 | { |
115ac3e4 SK |
668 | struct addrlist *addrlist; |
669 | ||
e1ff419c SK |
670 | if (cut) |
671 | *cut = 0; | |
672 | ||
376d48c7 | 673 | for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) |
587ad4f2 SK |
674 | if (!(addrlist->flags & ADDRLIST_IPV6) && |
675 | (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) && | |
115ac3e4 SK |
676 | add_resource_record(header, limit, &trunc, -axfroffset, &ansp, |
677 | daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr)) | |
678 | anscount++; | |
679 | ||
680 | #ifdef HAVE_IPV6 | |
376d48c7 | 681 | for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) |
587ad4f2 SK |
682 | if ((addrlist->flags & ADDRLIST_IPV6) && |
683 | (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) && | |
115ac3e4 SK |
684 | add_resource_record(header, limit, &trunc, -axfroffset, &ansp, |
685 | daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr)) | |
686 | anscount++; | |
687 | #endif | |
e1ff419c SK |
688 | |
689 | /* restore config data */ | |
690 | if (cut) | |
691 | *cut = '.'; | |
692 | } | |
115ac3e4 | 693 | |
e1ff419c SK |
694 | for (a = daemon->cnames; a; a = a->next) |
695 | if (in_zone(zone, a->alias, &cut)) | |
696 | { | |
697 | strcpy(name, a->target); | |
698 | if (!strchr(name, '.')) | |
699 | { | |
700 | strcat(name, "."); | |
701 | strcat(name, zone->domain); | |
702 | } | |
703 | ||
704 | if (cut) | |
705 | *cut = 0; | |
706 | ||
707 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, | |
708 | daemon->auth_ttl, NULL, | |
709 | T_CNAME, C_IN, "d", cut ? a->alias : NULL, name)) | |
710 | anscount++; | |
711 | } | |
712 | ||
b75e9363 SK |
713 | cache_enumerate(1); |
714 | while ((crecp = cache_enumerate(0))) | |
715 | { | |
716 | if ((crecp->flags & (F_IPV4 | F_IPV6)) && | |
717 | !(crecp->flags & (F_NEG | F_NXDOMAIN)) && | |
718 | (crecp->flags & F_FORWARD)) | |
719 | { | |
720 | if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN)) | |
721 | { | |
722 | char *cache_name = cache_get_name(crecp); | |
19b16891 | 723 | if (!strchr(cache_name, '.') && |
376d48c7 | 724 | (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))) |
b75e9363 SK |
725 | { |
726 | qtype = T_A; | |
727 | #ifdef HAVE_IPV6 | |
728 | if (crecp->flags & F_IPV6) | |
729 | qtype = T_AAAA; | |
730 | #endif | |
731 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, | |
732 | daemon->auth_ttl, NULL, qtype, C_IN, | |
733 | (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr)) | |
734 | anscount++; | |
735 | } | |
736 | } | |
737 | ||
738 | if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN)))) | |
739 | { | |
740 | strcpy(name, cache_get_name(crecp)); | |
19b16891 | 741 | if (in_zone(zone, name, &cut) && |
376d48c7 | 742 | (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))) |
b75e9363 SK |
743 | { |
744 | qtype = T_A; | |
745 | #ifdef HAVE_IPV6 | |
746 | if (crecp->flags & F_IPV6) | |
747 | qtype = T_AAAA; | |
748 | #endif | |
749 | if (cut) | |
e1ff419c SK |
750 | *cut = 0; |
751 | ||
752 | if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, | |
753 | daemon->auth_ttl, NULL, qtype, C_IN, | |
754 | (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr)) | |
755 | anscount++; | |
b75e9363 SK |
756 | } |
757 | } | |
758 | } | |
759 | } | |
760 | ||
761 | /* repeat SOA as last record */ | |
762 | if (add_resource_record(header, limit, &trunc, axfroffset, &ansp, | |
763 | daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll", | |
764 | daemon->authserver, daemon->hostmaster, | |
765 | daemon->soa_sn, daemon->soa_refresh, | |
766 | daemon->soa_retry, daemon->soa_expiry, | |
767 | daemon->auth_ttl)) | |
768 | anscount++; | |
4c985dac | 769 | |
b75e9363 | 770 | } |
4c985dac SK |
771 | |
772 | } | |
45dd1fec | 773 | |
4f7b304f SK |
774 | /* done all questions, set up header and return length of result */ |
775 | /* clear authoritative and truncated flags, set QR flag */ | |
776 | header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR; | |
93bafe61 SK |
777 | |
778 | if (local_query) | |
779 | { | |
780 | /* set RA flag */ | |
781 | header->hb4 |= HB4_RA; | |
782 | } | |
783 | else | |
784 | { | |
785 | /* clear RA flag */ | |
786 | header->hb4 &= ~HB4_RA; | |
787 | } | |
4f7b304f SK |
788 | |
789 | /* authoritive */ | |
790 | if (auth) | |
791 | header->hb3 |= HB3_AA; | |
792 | ||
793 | /* truncation */ | |
794 | if (trunc) | |
795 | header->hb3 |= HB3_TC; | |
796 | ||
57310500 | 797 | if ((auth || local_query) && nxdomain) |
4f7b304f SK |
798 | SET_RCODE(header, NXDOMAIN); |
799 | else | |
800 | SET_RCODE(header, NOERROR); /* no error */ | |
801 | header->ancount = htons(anscount); | |
802 | header->nscount = htons(authcount); | |
aa792351 | 803 | header->arcount = htons(0); |
4f7b304f SK |
804 | return ansp - (unsigned char *)header; |
805 | } | |
806 | ||
4820dce9 | 807 | #endif |
b75e9363 | 808 | |
4f7b304f SK |
809 | |
810 |