]>
Commit | Line | Data |
---|---|---|
59546085 | 1 | /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley |
9e4abcb5 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 | |
824af85b SK |
5 | the Free Software Foundation; version 2 dated June, 1991, or |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
9e4abcb5 SK |
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. | |
824af85b | 12 | |
73a08a24 SK |
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/>. | |
9e4abcb5 SK |
15 | */ |
16 | ||
17 | #include "dnsmasq.h" | |
18 | ||
572b41eb | 19 | static int add_resource_record(struct dns_header *header, char *limit, int *truncp, |
f6b7dc47 | 20 | unsigned int nameoffset, unsigned char **pp, |
3d8df260 | 21 | unsigned long ttl, unsigned int *offset, unsigned short type, |
f6b7dc47 SK |
22 | unsigned short class, char *format, ...); |
23 | ||
9009d746 SK |
24 | #define CHECK_LEN(header, pp, plen, len) \ |
25 | ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen)) | |
26 | ||
27 | #define ADD_RDLEN(header, pp, plen, len) \ | |
22b135a1 | 28 | (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1)) |
9009d746 | 29 | |
572b41eb | 30 | static int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, |
9009d746 | 31 | char *name, int isExtract, int extrabytes) |
9e4abcb5 | 32 | { |
3d8df260 | 33 | unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL; |
9e4abcb5 SK |
34 | unsigned int j, l, hops = 0; |
35 | int retvalue = 1; | |
36 | ||
f6b7dc47 SK |
37 | if (isExtract) |
38 | *cp = 0; | |
39 | ||
9009d746 SK |
40 | while (1) |
41 | { | |
42 | unsigned int label_type; | |
43 | ||
44 | if (!CHECK_LEN(header, p, plen, 1)) | |
45 | return 0; | |
46 | ||
47 | if ((l = *p++) == 0) | |
48 | /* end marker */ | |
49 | { | |
50 | /* check that there are the correct no of bytes after the name */ | |
51 | if (!CHECK_LEN(header, p, plen, extrabytes)) | |
52 | return 0; | |
53 | ||
54 | if (isExtract) | |
55 | { | |
56 | if (cp != (unsigned char *)name) | |
57 | cp--; | |
58 | *cp = 0; /* terminate: lose final period */ | |
59 | } | |
60 | else if (*cp != 0) | |
61 | retvalue = 2; | |
62 | ||
63 | if (p1) /* we jumped via compression */ | |
64 | *pp = p1; | |
65 | else | |
66 | *pp = p; | |
67 | ||
68 | return retvalue; | |
69 | } | |
70 | ||
71 | label_type = l & 0xc0; | |
72 | ||
9e4abcb5 SK |
73 | if (label_type == 0xc0) /* pointer */ |
74 | { | |
9009d746 | 75 | if (!CHECK_LEN(header, p, plen, 1)) |
9e4abcb5 SK |
76 | return 0; |
77 | ||
78 | /* get offset */ | |
79 | l = (l&0x3f) << 8; | |
80 | l |= *p++; | |
9e4abcb5 SK |
81 | |
82 | if (!p1) /* first jump, save location to go back to */ | |
83 | p1 = p; | |
84 | ||
85 | hops++; /* break malicious infinite loops */ | |
86 | if (hops > 255) | |
87 | return 0; | |
88 | ||
89 | p = l + (unsigned char *)header; | |
90 | } | |
91 | else if (label_type == 0x80) | |
92 | return 0; /* reserved */ | |
93 | else if (label_type == 0x40) | |
94 | { /* ELT */ | |
95 | unsigned int count, digs; | |
96 | ||
97 | if ((l & 0x3f) != 1) | |
98 | return 0; /* we only understand bitstrings */ | |
99 | ||
100 | if (!isExtract) | |
101 | return 0; /* Cannot compare bitsrings */ | |
102 | ||
103 | count = *p++; | |
104 | if (count == 0) | |
105 | count = 256; | |
106 | digs = ((count-1)>>2)+1; | |
107 | ||
108 | /* output is \[x<hex>/siz]. which is digs+9 chars */ | |
3d8df260 | 109 | if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME) |
9e4abcb5 | 110 | return 0; |
9009d746 | 111 | if (!CHECK_LEN(header, p, plen, (count-1)>>3)) |
9e4abcb5 SK |
112 | return 0; |
113 | ||
114 | *cp++ = '\\'; | |
115 | *cp++ = '['; | |
116 | *cp++ = 'x'; | |
117 | for (j=0; j<digs; j++) | |
118 | { | |
119 | unsigned int dig; | |
120 | if (j%2 == 0) | |
121 | dig = *p >> 4; | |
122 | else | |
123 | dig = *p++ & 0x0f; | |
124 | ||
125 | *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10; | |
126 | } | |
3d8df260 | 127 | cp += sprintf((char *)cp, "/%d]", count); |
9e4abcb5 SK |
128 | /* do this here to overwrite the zero char from sprintf */ |
129 | *cp++ = '.'; | |
130 | } | |
131 | else | |
132 | { /* label_type = 0 -> label. */ | |
3d8df260 | 133 | if (cp - (unsigned char *)name + l + 1 >= MAXDNAME) |
9e4abcb5 | 134 | return 0; |
9009d746 | 135 | if (!CHECK_LEN(header, p, plen, l)) |
9e4abcb5 | 136 | return 0; |
9009d746 | 137 | |
9e4abcb5 SK |
138 | for(j=0; j<l; j++, p++) |
139 | if (isExtract) | |
140 | { | |
1f15b81d SK |
141 | unsigned char c = *p; |
142 | if (isascii(c) && !iscntrl(c) && c != '.') | |
9e4abcb5 SK |
143 | *cp++ = *p; |
144 | else | |
145 | return 0; | |
146 | } | |
147 | else | |
148 | { | |
149 | unsigned char c1 = *cp, c2 = *p; | |
150 | ||
151 | if (c1 == 0) | |
152 | retvalue = 2; | |
153 | else | |
154 | { | |
155 | cp++; | |
156 | if (c1 >= 'A' && c1 <= 'Z') | |
157 | c1 += 'a' - 'A'; | |
158 | if (c2 >= 'A' && c2 <= 'Z') | |
159 | c2 += 'a' - 'A'; | |
160 | ||
161 | if (c1 != c2) | |
162 | retvalue = 2; | |
163 | } | |
164 | } | |
165 | ||
166 | if (isExtract) | |
167 | *cp++ = '.'; | |
f6b7dc47 SK |
168 | else if (*cp != 0 && *cp++ != '.') |
169 | retvalue = 2; | |
9e4abcb5 | 170 | } |
309331f5 | 171 | } |
9e4abcb5 SK |
172 | } |
173 | ||
174 | /* Max size of input string (for IPv6) is 75 chars.) */ | |
175 | #define MAXARPANAME 75 | |
176 | static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp) | |
177 | { | |
178 | int j; | |
179 | char name[MAXARPANAME+1], *cp1; | |
180 | unsigned char *addr = (unsigned char *)addrp; | |
181 | char *lastchunk = NULL, *penchunk = NULL; | |
182 | ||
183 | if (strlen(namein) > MAXARPANAME) | |
184 | return 0; | |
185 | ||
186 | memset(addrp, 0, sizeof(struct all_addr)); | |
187 | ||
188 | /* turn name into a series of asciiz strings */ | |
189 | /* j counts no of labels */ | |
190 | for(j = 1,cp1 = name; *namein; cp1++, namein++) | |
191 | if (*namein == '.') | |
192 | { | |
193 | penchunk = lastchunk; | |
194 | lastchunk = cp1 + 1; | |
195 | *cp1 = 0; | |
196 | j++; | |
197 | } | |
198 | else | |
199 | *cp1 = *namein; | |
200 | ||
201 | *cp1 = 0; | |
202 | ||
203 | if (j<3) | |
204 | return 0; | |
205 | ||
206 | if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr")) | |
207 | { | |
208 | /* IP v4 */ | |
209 | /* address arives as a name of the form | |
210 | www.xxx.yyy.zzz.in-addr.arpa | |
211 | some of the low order address octets might be missing | |
212 | and should be set to zero. */ | |
213 | for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1) | |
214 | { | |
215 | /* check for digits only (weeds out things like | |
216 | 50.0/24.67.28.64.in-addr.arpa which are used | |
217 | as CNAME targets according to RFC 2317 */ | |
218 | char *cp; | |
219 | for (cp = cp1; *cp; cp++) | |
572b41eb | 220 | if (!isdigit((unsigned char)*cp)) |
9e4abcb5 SK |
221 | return 0; |
222 | ||
223 | addr[3] = addr[2]; | |
224 | addr[2] = addr[1]; | |
225 | addr[1] = addr[0]; | |
226 | addr[0] = atoi(cp1); | |
227 | } | |
228 | ||
229 | return F_IPV4; | |
230 | } | |
231 | #ifdef HAVE_IPV6 | |
232 | else if (hostname_isequal(penchunk, "ip6") && | |
233 | (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa"))) | |
234 | { | |
235 | /* IP v6: | |
236 | Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa] | |
237 | or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa] | |
238 | ||
239 | Note that most of these the various reprentations are obsolete and | |
240 | left-over from the many DNS-for-IPv6 wars. We support all the formats | |
241 | that we can since there is no reason not to. | |
242 | */ | |
243 | ||
244 | if (*name == '\\' && *(name+1) == '[' && | |
245 | (*(name+2) == 'x' || *(name+2) == 'X')) | |
246 | { | |
572b41eb | 247 | for (j = 0, cp1 = name+3; *cp1 && isxdigit((unsigned char) *cp1) && j < 32; cp1++, j++) |
9e4abcb5 SK |
248 | { |
249 | char xdig[2]; | |
250 | xdig[0] = *cp1; | |
251 | xdig[1] = 0; | |
252 | if (j%2) | |
253 | addr[j/2] |= strtol(xdig, NULL, 16); | |
254 | else | |
255 | addr[j/2] = strtol(xdig, NULL, 16) << 4; | |
256 | } | |
257 | ||
258 | if (*cp1 == '/' && j == 32) | |
259 | return F_IPV6; | |
260 | } | |
261 | else | |
262 | { | |
263 | for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1) | |
264 | { | |
572b41eb | 265 | if (*(cp1+1) || !isxdigit((unsigned char)*cp1)) |
9e4abcb5 SK |
266 | return 0; |
267 | ||
268 | for (j = sizeof(struct all_addr)-1; j>0; j--) | |
269 | addr[j] = (addr[j] >> 4) | (addr[j-1] << 4); | |
270 | addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4); | |
271 | } | |
272 | ||
273 | return F_IPV6; | |
274 | } | |
275 | } | |
276 | #endif | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
572b41eb | 281 | static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes) |
9e4abcb5 | 282 | { |
feba5c1d | 283 | while(1) |
9e4abcb5 | 284 | { |
9009d746 | 285 | unsigned int label_type; |
feba5c1d | 286 | |
9009d746 | 287 | if (!CHECK_LEN(header, ansp, plen, 1)) |
feba5c1d SK |
288 | return NULL; |
289 | ||
9009d746 SK |
290 | label_type = (*ansp) & 0xc0; |
291 | ||
feba5c1d | 292 | if (label_type == 0xc0) |
9e4abcb5 | 293 | { |
feba5c1d SK |
294 | /* pointer for compression. */ |
295 | ansp += 2; | |
296 | break; | |
297 | } | |
298 | else if (label_type == 0x80) | |
299 | return NULL; /* reserved */ | |
300 | else if (label_type == 0x40) | |
301 | { | |
302 | /* Extended label type */ | |
303 | unsigned int count; | |
9e4abcb5 | 304 | |
9009d746 SK |
305 | if (!CHECK_LEN(header, ansp, plen, 2)) |
306 | return NULL; | |
307 | ||
feba5c1d SK |
308 | if (((*ansp++) & 0x3f) != 1) |
309 | return NULL; /* we only understand bitstrings */ | |
9e4abcb5 | 310 | |
feba5c1d SK |
311 | count = *(ansp++); /* Bits in bitstring */ |
312 | ||
313 | if (count == 0) /* count == 0 means 256 bits */ | |
314 | ansp += 32; | |
9e4abcb5 | 315 | else |
feba5c1d SK |
316 | ansp += ((count-1)>>3)+1; |
317 | } | |
318 | else | |
319 | { /* label type == 0 Bottom six bits is length */ | |
320 | unsigned int len = (*ansp++) & 0x3f; | |
9009d746 SK |
321 | |
322 | if (!ADD_RDLEN(header, ansp, plen, len)) | |
323 | return NULL; | |
324 | ||
feba5c1d SK |
325 | if (len == 0) |
326 | break; /* zero length label marks the end. */ | |
9e4abcb5 | 327 | } |
feba5c1d | 328 | } |
9009d746 SK |
329 | |
330 | if (!CHECK_LEN(header, ansp, plen, extrabytes)) | |
331 | return NULL; | |
feba5c1d SK |
332 | |
333 | return ansp; | |
334 | } | |
335 | ||
572b41eb | 336 | static unsigned char *skip_questions(struct dns_header *header, size_t plen) |
feba5c1d | 337 | { |
5aabfc78 | 338 | int q; |
feba5c1d SK |
339 | unsigned char *ansp = (unsigned char *)(header+1); |
340 | ||
5aabfc78 | 341 | for (q = ntohs(header->qdcount); q != 0; q--) |
feba5c1d | 342 | { |
9009d746 | 343 | if (!(ansp = skip_name(ansp, header, plen, 4))) |
feba5c1d | 344 | return NULL; |
9e4abcb5 SK |
345 | ansp += 4; /* class and type */ |
346 | } | |
9e4abcb5 SK |
347 | |
348 | return ansp; | |
349 | } | |
350 | ||
572b41eb | 351 | static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen) |
feba5c1d | 352 | { |
fd9fa481 | 353 | int i, rdlen; |
36717eee | 354 | |
fd9fa481 | 355 | for (i = 0; i < count; i++) |
36717eee | 356 | { |
9009d746 | 357 | if (!(ansp = skip_name(ansp, header, plen, 10))) |
fd9fa481 | 358 | return NULL; |
36717eee SK |
359 | ansp += 8; /* type, class, TTL */ |
360 | GETSHORT(rdlen, ansp); | |
9009d746 | 361 | if (!ADD_RDLEN(header, ansp, plen, rdlen)) |
fd9fa481 | 362 | return NULL; |
36717eee SK |
363 | } |
364 | ||
fd9fa481 SK |
365 | return ansp; |
366 | } | |
367 | ||
0a852541 SK |
368 | /* CRC the question section. This is used to safely detect query |
369 | retransmision and to detect answers to questions we didn't ask, which | |
370 | might be poisoning attacks. Note that we decode the name rather | |
371 | than CRC the raw bytes, since replies might be compressed differently. | |
832af0ba SK |
372 | We ignore case in the names for the same reason. Return all-ones |
373 | if there is not question section. */ | |
572b41eb | 374 | unsigned int questions_crc(struct dns_header *header, size_t plen, char *name) |
fd9fa481 | 375 | { |
91dccd09 SK |
376 | int q; |
377 | unsigned int crc = 0xffffffff; | |
0a852541 SK |
378 | unsigned char *p1, *p = (unsigned char *)(header+1); |
379 | ||
5aabfc78 | 380 | for (q = ntohs(header->qdcount); q != 0; q--) |
0a852541 | 381 | { |
9009d746 | 382 | if (!extract_name(header, plen, &p, name, 1, 4)) |
0a852541 SK |
383 | return crc; /* bad packet */ |
384 | ||
3d8df260 | 385 | for (p1 = (unsigned char *)name; *p1; p1++) |
0a852541 SK |
386 | { |
387 | int i = 8; | |
388 | char c = *p1; | |
389 | ||
390 | if (c >= 'A' && c <= 'Z') | |
391 | c += 'a' - 'A'; | |
392 | ||
393 | crc ^= c << 24; | |
394 | while (i--) | |
395 | crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | |
396 | } | |
397 | ||
398 | /* CRC the class and type as well */ | |
399 | for (p1 = p; p1 < p+4; p1++) | |
400 | { | |
401 | int i = 8; | |
402 | crc ^= *p1 << 24; | |
403 | while (i--) | |
404 | crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | |
405 | } | |
406 | ||
407 | p += 4; | |
9009d746 | 408 | if (!CHECK_LEN(header, p, plen, 0)) |
0a852541 SK |
409 | return crc; /* bad packet */ |
410 | } | |
fd9fa481 | 411 | |
fd9fa481 SK |
412 | return crc; |
413 | } | |
414 | ||
415 | ||
572b41eb | 416 | size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen) |
fd9fa481 SK |
417 | { |
418 | unsigned char *ansp = skip_questions(header, plen); | |
419 | ||
9009d746 | 420 | /* if packet is malformed, just return as-is. */ |
fd9fa481 | 421 | if (!ansp) |
9009d746 | 422 | return plen; |
fd9fa481 SK |
423 | |
424 | if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), | |
425 | header, plen))) | |
9009d746 | 426 | return plen; |
fd9fa481 | 427 | |
36717eee SK |
428 | /* restore pseudoheader */ |
429 | if (pheader && ntohs(header->arcount) == 0) | |
430 | { | |
431 | /* must use memmove, may overlap */ | |
432 | memmove(ansp, pheader, hlen); | |
433 | header->arcount = htons(1); | |
434 | ansp += hlen; | |
435 | } | |
436 | ||
437 | return ansp - (unsigned char *)header; | |
438 | } | |
439 | ||
572b41eb | 440 | unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign) |
36717eee SK |
441 | { |
442 | /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. | |
832af0ba SK |
443 | also return length of pseudoheader in *len and pointer to the UDP size in *p |
444 | Finally, check to see if a packet is signed. If it is we cannot change a single bit before | |
445 | forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */ | |
feba5c1d SK |
446 | |
447 | int i, arcount = ntohs(header->arcount); | |
832af0ba SK |
448 | unsigned char *ansp = (unsigned char *)(header+1); |
449 | unsigned short rdlen, type, class; | |
450 | unsigned char *ret = NULL; | |
1b7ecd11 SK |
451 | |
452 | if (is_sign) | |
832af0ba | 453 | { |
1b7ecd11 SK |
454 | *is_sign = 0; |
455 | ||
572b41eb | 456 | if (OPCODE(header) == QUERY) |
832af0ba | 457 | { |
5aabfc78 | 458 | for (i = ntohs(header->qdcount); i != 0; i--) |
1b7ecd11 | 459 | { |
9009d746 | 460 | if (!(ansp = skip_name(ansp, header, plen, 4))) |
1b7ecd11 SK |
461 | return NULL; |
462 | ||
463 | GETSHORT(type, ansp); | |
464 | GETSHORT(class, ansp); | |
465 | ||
466 | if (class == C_IN && type == T_TKEY) | |
467 | *is_sign = 1; | |
468 | } | |
832af0ba SK |
469 | } |
470 | } | |
471 | else | |
472 | { | |
473 | if (!(ansp = skip_questions(header, plen))) | |
474 | return NULL; | |
475 | } | |
476 | ||
477 | if (arcount == 0) | |
feba5c1d SK |
478 | return NULL; |
479 | ||
fd9fa481 SK |
480 | if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen))) |
481 | return NULL; | |
832af0ba | 482 | |
feba5c1d SK |
483 | for (i = 0; i < arcount; i++) |
484 | { | |
36717eee | 485 | unsigned char *save, *start = ansp; |
9009d746 | 486 | if (!(ansp = skip_name(ansp, header, plen, 10))) |
feba5c1d SK |
487 | return NULL; |
488 | ||
489 | GETSHORT(type, ansp); | |
490 | save = ansp; | |
832af0ba SK |
491 | GETSHORT(class, ansp); |
492 | ansp += 4; /* TTL */ | |
feba5c1d | 493 | GETSHORT(rdlen, ansp); |
9009d746 | 494 | if (!ADD_RDLEN(header, ansp, plen, rdlen)) |
feba5c1d | 495 | return NULL; |
832af0ba | 496 | if (type == T_OPT) |
36717eee SK |
497 | { |
498 | if (len) | |
499 | *len = ansp - start; | |
500 | if (p) | |
501 | *p = save; | |
832af0ba | 502 | ret = start; |
36717eee | 503 | } |
832af0ba SK |
504 | else if (is_sign && |
505 | i == arcount - 1 && | |
506 | class == C_ANY && | |
507 | (type == T_SIG || type == T_TSIG)) | |
508 | *is_sign = 1; | |
feba5c1d SK |
509 | } |
510 | ||
832af0ba | 511 | return ret; |
feba5c1d | 512 | } |
28866e95 SK |
513 | |
514 | struct macparm { | |
515 | unsigned char *limit; | |
572b41eb | 516 | struct dns_header *header; |
28866e95 SK |
517 | size_t plen; |
518 | union mysockaddr *l3; | |
519 | }; | |
520 | ||
521 | static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) | |
522 | { | |
523 | struct macparm *parm = parmv; | |
524 | int match = 0; | |
525 | unsigned short rdlen; | |
572b41eb | 526 | struct dns_header *header = parm->header; |
28866e95 SK |
527 | unsigned char *lenp, *datap, *p; |
528 | ||
529 | if (family == parm->l3->sa.sa_family) | |
530 | { | |
531 | if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0) | |
532 | match = 1; | |
533 | #ifdef HAVE_IPV6 | |
534 | else | |
535 | if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0) | |
536 | match = 1; | |
537 | #endif | |
538 | } | |
539 | ||
540 | if (!match) | |
541 | return 1; /* continue */ | |
542 | ||
543 | if (ntohs(header->arcount) == 0) | |
544 | { | |
545 | /* We are adding the pseudoheader */ | |
546 | if (!(p = skip_questions(header, parm->plen)) || | |
547 | !(p = skip_section(p, | |
548 | ntohs(header->ancount) + ntohs(header->nscount), | |
549 | header, parm->plen))) | |
550 | return 0; | |
551 | *p++ = 0; /* empty name */ | |
552 | PUTSHORT(T_OPT, p); | |
553 | PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */ | |
554 | PUTLONG(0, p); /* extended RCODE */ | |
555 | lenp = p; | |
556 | PUTSHORT(0, p); /* RDLEN */ | |
557 | rdlen = 0; | |
558 | if (((ssize_t)maclen) > (parm->limit - (p + 4))) | |
559 | return 0; /* Too big */ | |
560 | header->arcount = htons(1); | |
561 | datap = p; | |
562 | } | |
563 | else | |
564 | { | |
565 | int i, is_sign; | |
566 | unsigned short code, len; | |
567 | ||
568 | if (ntohs(header->arcount) != 1 || | |
569 | !(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) || | |
570 | is_sign || | |
571 | (!(p = skip_name(p, header, parm->plen, 10)))) | |
572 | return 0; | |
573 | ||
574 | p += 8; /* skip UDP length and RCODE */ | |
575 | ||
576 | lenp = p; | |
577 | GETSHORT(rdlen, p); | |
578 | if (!CHECK_LEN(header, p, parm->plen, rdlen)) | |
579 | return 0; /* bad packet */ | |
580 | datap = p; | |
581 | ||
582 | /* check if option already there */ | |
583 | for (i = 0; i + 4 < rdlen; i += len + 4) | |
584 | { | |
585 | GETSHORT(code, p); | |
586 | GETSHORT(len, p); | |
587 | if (code == EDNS0_OPTION_MAC) | |
588 | return 0; | |
589 | p += len; | |
590 | } | |
feba5c1d | 591 | |
28866e95 SK |
592 | if (((ssize_t)maclen) > (parm->limit - (p + 4))) |
593 | return 0; /* Too big */ | |
594 | } | |
595 | ||
596 | PUTSHORT(EDNS0_OPTION_MAC, p); | |
597 | PUTSHORT(maclen, p); | |
598 | memcpy(p, mac, maclen); | |
599 | p += maclen; | |
600 | ||
601 | PUTSHORT(p - datap, lenp); | |
602 | parm->plen = p - (unsigned char *)header; | |
603 | ||
604 | return 0; /* done */ | |
605 | } | |
606 | ||
607 | ||
572b41eb | 608 | size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3) |
28866e95 SK |
609 | { |
610 | struct macparm parm; | |
611 | ||
612 | /* Must have an existing pseudoheader as the only ar-record, | |
613 | or have no ar-records. Must also not be signed */ | |
614 | ||
615 | if (ntohs(header->arcount) > 1) | |
616 | return plen; | |
617 | ||
618 | parm.header = header; | |
619 | parm.limit = (unsigned char *)limit; | |
620 | parm.plen = plen; | |
621 | parm.l3 = l3; | |
622 | ||
623 | iface_enumerate(AF_UNSPEC, &parm, filter_mac); | |
feba5c1d | 624 | |
28866e95 SK |
625 | return parm.plen; |
626 | } | |
627 | ||
628 | ||
9e4abcb5 | 629 | /* is addr in the non-globally-routed IP space? */ |
8ef5ada2 | 630 | static int private_net(struct in_addr addr, int ban_localhost) |
9e4abcb5 | 631 | { |
f2621c7f SK |
632 | in_addr_t ip_addr = ntohl(addr.s_addr); |
633 | ||
634 | return | |
8ef5ada2 | 635 | (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ || |
f2621c7f SK |
636 | ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ || |
637 | ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ || | |
638 | ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ || | |
639 | ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ; | |
9e4abcb5 | 640 | } |
fd9fa481 | 641 | |
572b41eb | 642 | static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name) |
824af85b SK |
643 | { |
644 | int i, qtype, qclass, rdlen; | |
824af85b SK |
645 | |
646 | for (i = count; i != 0; i--) | |
647 | { | |
28866e95 | 648 | if (name && option_bool(OPT_LOG)) |
8ef5ada2 SK |
649 | { |
650 | if (!extract_name(header, qlen, &p, name, 1, 10)) | |
651 | return 0; | |
652 | } | |
653 | else if (!(p = skip_name(p, header, qlen, 10))) | |
824af85b SK |
654 | return 0; /* bad packet */ |
655 | ||
656 | GETSHORT(qtype, p); | |
657 | GETSHORT(qclass, p); | |
7de060b0 | 658 | p += 4; /* ttl */ |
824af85b SK |
659 | GETSHORT(rdlen, p); |
660 | ||
8ef5ada2 | 661 | if (qclass == C_IN && qtype == T_A) |
824af85b SK |
662 | { |
663 | struct doctor *doctor; | |
664 | struct in_addr addr; | |
665 | ||
9009d746 SK |
666 | if (!CHECK_LEN(header, p, qlen, INADDRSZ)) |
667 | return 0; | |
8ef5ada2 SK |
668 | |
669 | /* alignment */ | |
824af85b SK |
670 | memcpy(&addr, p, INADDRSZ); |
671 | ||
672 | for (doctor = daemon->doctors; doctor; doctor = doctor->next) | |
73a08a24 SK |
673 | { |
674 | if (doctor->end.s_addr == 0) | |
675 | { | |
676 | if (!is_same_net(doctor->in, addr, doctor->mask)) | |
677 | continue; | |
678 | } | |
679 | else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) || | |
680 | ntohl(doctor->end.s_addr) < ntohl(addr.s_addr)) | |
681 | continue; | |
8ef5ada2 | 682 | |
73a08a24 SK |
683 | addr.s_addr &= ~doctor->mask.s_addr; |
684 | addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); | |
685 | /* Since we munged the data, the server it came from is no longer authoritative */ | |
572b41eb | 686 | header->hb3 &= ~HB3_AA; |
73a08a24 SK |
687 | memcpy(p, &addr, INADDRSZ); |
688 | break; | |
689 | } | |
824af85b | 690 | } |
28866e95 | 691 | else if (qtype == T_TXT && name && option_bool(OPT_LOG)) |
8ef5ada2 SK |
692 | { |
693 | unsigned char *p1 = p; | |
694 | if (!CHECK_LEN(header, p1, qlen, rdlen)) | |
695 | return 0; | |
696 | while ((p1 - p) < rdlen) | |
697 | { | |
698 | unsigned int i, len = *p1; | |
699 | unsigned char *p2 = p1; | |
700 | /* make counted string zero-term and sanitise */ | |
701 | for (i = 0; i < len; i++) | |
702 | if (isprint(*(p2+1))) | |
703 | { | |
704 | *p2 = *(p2+1); | |
705 | p2++; | |
706 | } | |
707 | *p2 = 0; | |
28866e95 | 708 | my_syslog(LOG_INFO, "reply %s is %s", name, p1); |
8ef5ada2 SK |
709 | /* restore */ |
710 | memmove(p1 + 1, p1, len); | |
711 | *p1 = len; | |
712 | p1 += len+1; | |
713 | } | |
714 | } | |
824af85b | 715 | |
9009d746 SK |
716 | if (!ADD_RDLEN(header, p, qlen, rdlen)) |
717 | return 0; /* bad packet */ | |
824af85b SK |
718 | } |
719 | ||
720 | return p; | |
721 | } | |
722 | ||
572b41eb | 723 | static int find_soa(struct dns_header *header, size_t qlen, char *name) |
9e4abcb5 SK |
724 | { |
725 | unsigned char *p; | |
9e4abcb5 | 726 | int qtype, qclass, rdlen; |
fd9fa481 SK |
727 | unsigned long ttl, minttl = ULONG_MAX; |
728 | int i, found_soa = 0; | |
9e4abcb5 | 729 | |
fd9fa481 SK |
730 | /* first move to NS section and find TTL from any SOA section */ |
731 | if (!(p = skip_questions(header, qlen)) || | |
8ef5ada2 | 732 | !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name))) |
824af85b | 733 | return 0; /* bad packet */ |
9e4abcb5 | 734 | |
5aabfc78 | 735 | for (i = ntohs(header->nscount); i != 0; i--) |
9e4abcb5 | 736 | { |
9009d746 | 737 | if (!(p = skip_name(p, header, qlen, 10))) |
fd9fa481 SK |
738 | return 0; /* bad packet */ |
739 | ||
9e4abcb5 SK |
740 | GETSHORT(qtype, p); |
741 | GETSHORT(qclass, p); | |
742 | GETLONG(ttl, p); | |
743 | GETSHORT(rdlen, p); | |
fd9fa481 | 744 | |
9e4abcb5 SK |
745 | if ((qclass == C_IN) && (qtype == T_SOA)) |
746 | { | |
fd9fa481 SK |
747 | found_soa = 1; |
748 | if (ttl < minttl) | |
749 | minttl = ttl; | |
750 | ||
9e4abcb5 | 751 | /* MNAME */ |
9009d746 | 752 | if (!(p = skip_name(p, header, qlen, 0))) |
fd9fa481 | 753 | return 0; |
9e4abcb5 | 754 | /* RNAME */ |
9009d746 | 755 | if (!(p = skip_name(p, header, qlen, 20))) |
fd9fa481 SK |
756 | return 0; |
757 | p += 16; /* SERIAL REFRESH RETRY EXPIRE */ | |
758 | ||
9e4abcb5 SK |
759 | GETLONG(ttl, p); /* minTTL */ |
760 | if (ttl < minttl) | |
761 | minttl = ttl; | |
762 | } | |
9009d746 | 763 | else if (!ADD_RDLEN(header, p, qlen, rdlen)) |
fd9fa481 | 764 | return 0; /* bad packet */ |
9e4abcb5 | 765 | } |
9009d746 | 766 | |
824af85b | 767 | /* rewrite addresses in additioal section too */ |
8ef5ada2 | 768 | if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL)) |
824af85b SK |
769 | return 0; |
770 | ||
771 | if (!found_soa) | |
772 | minttl = daemon->neg_ttl; | |
773 | ||
774 | return minttl; | |
1cff166d SK |
775 | } |
776 | ||
fd9fa481 SK |
777 | /* Note that the following code can create CNAME chains that don't point to a real record, |
778 | either because of lack of memory, or lack of SOA records. These are treated by the cache code as | |
824af85b | 779 | expired and cleaned out that way. |
8ef5ada2 | 780 | Return 1 if we reject an address because it look like part of dns-rebinding attack. */ |
572b41eb | 781 | int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, |
28866e95 | 782 | int is_sign, int check_rebind, int checking_disabled) |
9e4abcb5 | 783 | { |
824af85b | 784 | unsigned char *p, *p1, *endrr, *namep; |
fd9fa481 | 785 | int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; |
0a852541 | 786 | unsigned long ttl = 0; |
5aabfc78 | 787 | struct all_addr addr; |
0a852541 | 788 | |
9e4abcb5 | 789 | cache_start_insert(); |
0a852541 | 790 | |
8ef5ada2 | 791 | /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */ |
28866e95 | 792 | if (daemon->doctors || option_bool(OPT_LOG)) |
0a852541 SK |
793 | { |
794 | searched_soa = 1; | |
8ef5ada2 | 795 | ttl = find_soa(header, qlen, name); |
0a852541 | 796 | } |
9e4abcb5 | 797 | |
fd9fa481 SK |
798 | /* go through the questions. */ |
799 | p = (unsigned char *)(header+1); | |
9e4abcb5 | 800 | |
5aabfc78 | 801 | for (i = ntohs(header->qdcount); i != 0; i--) |
9e4abcb5 | 802 | { |
fd9fa481 SK |
803 | int found = 0, cname_count = 5; |
804 | struct crec *cpp = NULL; | |
572b41eb | 805 | int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0; |
0a852541 | 806 | unsigned long cttl = ULONG_MAX, attl; |
fd9fa481 | 807 | |
824af85b | 808 | namep = p; |
9009d746 | 809 | if (!extract_name(header, qlen, &p, name, 1, 4)) |
824af85b | 810 | return 0; /* bad packet */ |
fd9fa481 | 811 | |
9e4abcb5 SK |
812 | GETSHORT(qtype, p); |
813 | GETSHORT(qclass, p); | |
9e4abcb5 SK |
814 | |
815 | if (qclass != C_IN) | |
fd9fa481 | 816 | continue; |
9e4abcb5 | 817 | |
fd9fa481 SK |
818 | /* PTRs: we chase CNAMEs here, since we have no way to |
819 | represent them in the cache. */ | |
820 | if (qtype == T_PTR) | |
821 | { | |
9e4abcb5 | 822 | int name_encoding = in_arpa_name_2_addr(name, &addr); |
fd9fa481 SK |
823 | |
824 | if (!name_encoding) | |
825 | continue; | |
826 | ||
827 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 828 | { |
fd9fa481 SK |
829 | cname_loop: |
830 | if (!(p1 = skip_questions(header, qlen))) | |
824af85b | 831 | return 0; |
fd9fa481 | 832 | |
5aabfc78 | 833 | for (j = ntohs(header->ancount); j != 0; j--) |
fd9fa481 | 834 | { |
824af85b SK |
835 | unsigned char *tmp = namep; |
836 | /* the loop body overwrites the original name, so get it back here. */ | |
9009d746 SK |
837 | if (!extract_name(header, qlen, &tmp, name, 1, 0) || |
838 | !(res = extract_name(header, qlen, &p1, name, 0, 10))) | |
824af85b | 839 | return 0; /* bad packet */ |
fd9fa481 SK |
840 | |
841 | GETSHORT(aqtype, p1); | |
842 | GETSHORT(aqclass, p1); | |
843 | GETLONG(attl, p1); | |
8ef5ada2 SK |
844 | if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) |
845 | { | |
572b41eb | 846 | (p1) -= 4; |
8ef5ada2 SK |
847 | PUTLONG(daemon->max_ttl, p1); |
848 | } | |
fd9fa481 SK |
849 | GETSHORT(ardlen, p1); |
850 | endrr = p1+ardlen; | |
851 | ||
852 | /* TTL of record is minimum of CNAMES and PTR */ | |
853 | if (attl < cttl) | |
854 | cttl = attl; | |
855 | ||
856 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) | |
857 | { | |
9009d746 | 858 | if (!extract_name(header, qlen, &p1, name, 1, 0)) |
824af85b | 859 | return 0; |
fd9fa481 SK |
860 | |
861 | if (aqtype == T_CNAME) | |
862 | { | |
863 | if (!cname_count--) | |
824af85b | 864 | return 0; /* looped CNAMES */ |
fd9fa481 SK |
865 | goto cname_loop; |
866 | } | |
867 | ||
868 | cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE); | |
869 | found = 1; | |
870 | } | |
871 | ||
872 | p1 = endrr; | |
9009d746 | 873 | if (!CHECK_LEN(header, p1, qlen, 0)) |
824af85b | 874 | return 0; /* bad packet */ |
fd9fa481 SK |
875 | } |
876 | } | |
877 | ||
28866e95 | 878 | if (!found && !option_bool(OPT_NO_NEG)) |
fd9fa481 SK |
879 | { |
880 | if (!searched_soa) | |
881 | { | |
882 | searched_soa = 1; | |
8ef5ada2 | 883 | ttl = find_soa(header, qlen, NULL); |
fd9fa481 SK |
884 | } |
885 | if (ttl) | |
5aabfc78 | 886 | cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); |
9e4abcb5 SK |
887 | } |
888 | } | |
fd9fa481 | 889 | else |
9e4abcb5 | 890 | { |
fd9fa481 SK |
891 | /* everything other than PTR */ |
892 | struct crec *newc; | |
5aabfc78 SK |
893 | int addrlen; |
894 | ||
fd9fa481 | 895 | if (qtype == T_A) |
5aabfc78 SK |
896 | { |
897 | addrlen = INADDRSZ; | |
898 | flags |= F_IPV4; | |
899 | } | |
fd9fa481 SK |
900 | #ifdef HAVE_IPV6 |
901 | else if (qtype == T_AAAA) | |
5aabfc78 SK |
902 | { |
903 | addrlen = IN6ADDRSZ; | |
904 | flags |= F_IPV6; | |
905 | } | |
fd9fa481 SK |
906 | #endif |
907 | else | |
908 | continue; | |
909 | ||
910 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 911 | { |
fd9fa481 SK |
912 | cname_loop1: |
913 | if (!(p1 = skip_questions(header, qlen))) | |
824af85b | 914 | return 0; |
9e4abcb5 | 915 | |
5aabfc78 | 916 | for (j = ntohs(header->ancount); j != 0; j--) |
9e4abcb5 | 917 | { |
9009d746 | 918 | if (!(res = extract_name(header, qlen, &p1, name, 0, 10))) |
824af85b | 919 | return 0; /* bad packet */ |
fd9fa481 SK |
920 | |
921 | GETSHORT(aqtype, p1); | |
922 | GETSHORT(aqclass, p1); | |
923 | GETLONG(attl, p1); | |
8ef5ada2 SK |
924 | if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) |
925 | { | |
572b41eb | 926 | (p1) -= 4; |
8ef5ada2 SK |
927 | PUTLONG(daemon->max_ttl, p1); |
928 | } | |
fd9fa481 SK |
929 | GETSHORT(ardlen, p1); |
930 | endrr = p1+ardlen; | |
931 | ||
932 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) | |
933 | { | |
934 | if (aqtype == T_CNAME) | |
935 | { | |
936 | if (!cname_count--) | |
824af85b | 937 | return 0; /* looped CNAMES */ |
fd9fa481 | 938 | newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD); |
26128d27 | 939 | if (newc && cpp) |
fd9fa481 SK |
940 | { |
941 | cpp->addr.cname.cache = newc; | |
942 | cpp->addr.cname.uid = newc->uid; | |
943 | } | |
9e4abcb5 | 944 | |
fd9fa481 SK |
945 | cpp = newc; |
946 | if (attl < cttl) | |
947 | cttl = attl; | |
948 | ||
9009d746 | 949 | if (!extract_name(header, qlen, &p1, name, 1, 0)) |
824af85b | 950 | return 0; |
fd9fa481 SK |
951 | goto cname_loop1; |
952 | } | |
953 | else | |
954 | { | |
955 | found = 1; | |
9009d746 | 956 | |
5aabfc78 | 957 | /* copy address into aligned storage */ |
9009d746 SK |
958 | if (!CHECK_LEN(header, p1, qlen, addrlen)) |
959 | return 0; /* bad packet */ | |
5aabfc78 | 960 | memcpy(&addr, p1, addrlen); |
824af85b SK |
961 | |
962 | /* check for returned address in private space */ | |
8ef5ada2 | 963 | if (check_rebind && |
824af85b | 964 | (flags & F_IPV4) && |
28866e95 | 965 | private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))) |
824af85b SK |
966 | return 1; |
967 | ||
5aabfc78 | 968 | newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD); |
26128d27 | 969 | if (newc && cpp) |
fd9fa481 SK |
970 | { |
971 | cpp->addr.cname.cache = newc; | |
972 | cpp->addr.cname.uid = newc->uid; | |
973 | } | |
974 | cpp = NULL; | |
975 | } | |
976 | } | |
977 | ||
978 | p1 = endrr; | |
9009d746 | 979 | if (!CHECK_LEN(header, p1, qlen, 0)) |
824af85b | 980 | return 0; /* bad packet */ |
fd9fa481 SK |
981 | } |
982 | } | |
983 | ||
28866e95 | 984 | if (!found && !option_bool(OPT_NO_NEG)) |
fd9fa481 SK |
985 | { |
986 | if (!searched_soa) | |
1cff166d | 987 | { |
fd9fa481 | 988 | searched_soa = 1; |
8ef5ada2 | 989 | ttl = find_soa(header, qlen, NULL); |
1cff166d | 990 | } |
fd9fa481 | 991 | /* If there's no SOA to get the TTL from, but there is a CNAME |
824af85b | 992 | pointing at this, inherit its TTL */ |
fd9fa481 | 993 | if (ttl || cpp) |
9e4abcb5 | 994 | { |
5aabfc78 | 995 | newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags); |
26128d27 | 996 | if (newc && cpp) |
9e4abcb5 | 997 | { |
fd9fa481 SK |
998 | cpp->addr.cname.cache = newc; |
999 | cpp->addr.cname.uid = newc->uid; | |
1000 | } | |
9e4abcb5 | 1001 | } |
9e4abcb5 | 1002 | } |
fd9fa481 | 1003 | } |
9e4abcb5 | 1004 | } |
fd9fa481 | 1005 | |
1023dcbc SK |
1006 | /* Don't put stuff from a truncated packet into the cache. |
1007 | Don't cache replies where DNSSEC validation was turned off, either | |
1008 | the upstream server told us so, or the original query specified it. | |
1009 | Don't cache replies from non-recursive nameservers, since we may get a | |
1010 | reply containing a CNAME but not its target, even though the target | |
1011 | does exist. */ | |
1012 | if (!(header->hb3 & HB3_TC) && | |
1013 | !(header->hb4 & HB4_CD) && | |
1014 | (header->hb4 & HB4_RA) && | |
1015 | !checking_disabled) | |
824af85b SK |
1016 | cache_end_insert(); |
1017 | ||
1018 | return 0; | |
9e4abcb5 SK |
1019 | } |
1020 | ||
1021 | /* If the packet holds exactly one query | |
28866e95 | 1022 | return F_IPV4 or F_IPV6 and leave the name from the query in name */ |
9e4abcb5 | 1023 | |
572b41eb | 1024 | unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep) |
9e4abcb5 SK |
1025 | { |
1026 | unsigned char *p = (unsigned char *)(header+1); | |
1027 | int qtype, qclass; | |
1028 | ||
c1bb8504 SK |
1029 | if (typep) |
1030 | *typep = 0; | |
1031 | ||
572b41eb | 1032 | if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY) |
9e4abcb5 SK |
1033 | return 0; /* must be exactly one query. */ |
1034 | ||
9009d746 | 1035 | if (!extract_name(header, qlen, &p, name, 1, 4)) |
9e4abcb5 SK |
1036 | return 0; /* bad packet */ |
1037 | ||
1038 | GETSHORT(qtype, p); | |
1039 | GETSHORT(qclass, p); | |
1040 | ||
0a852541 SK |
1041 | if (typep) |
1042 | *typep = qtype; | |
1043 | ||
9e4abcb5 SK |
1044 | if (qclass == C_IN) |
1045 | { | |
1046 | if (qtype == T_A) | |
1047 | return F_IPV4; | |
1048 | if (qtype == T_AAAA) | |
1049 | return F_IPV6; | |
1050 | if (qtype == T_ANY) | |
1051 | return F_IPV4 | F_IPV6; | |
1052 | } | |
1053 | ||
1054 | return F_QUERY; | |
1055 | } | |
1056 | ||
1057 | ||
572b41eb | 1058 | size_t setup_reply(struct dns_header *header, size_t qlen, |
28866e95 | 1059 | struct all_addr *addrp, unsigned int flags, unsigned long ttl) |
9e4abcb5 SK |
1060 | { |
1061 | unsigned char *p = skip_questions(header, qlen); | |
1062 | ||
572b41eb SK |
1063 | /* clear authoritative and truncated flags, set QR flag */ |
1064 | header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR; | |
1065 | /* set RA flag */ | |
1066 | header->hb4 |= HB4_RA; | |
1067 | ||
9e4abcb5 SK |
1068 | header->nscount = htons(0); |
1069 | header->arcount = htons(0); | |
feba5c1d | 1070 | header->ancount = htons(0); /* no answers unless changed below */ |
9e4abcb5 | 1071 | if (flags == F_NEG) |
572b41eb | 1072 | SET_RCODE(header, SERVFAIL); /* couldn't get memory */ |
824af85b | 1073 | else if (flags == F_NOERR) |
572b41eb | 1074 | SET_RCODE(header, NOERROR); /* empty domain */ |
9e4abcb5 | 1075 | else if (flags == F_NXDOMAIN) |
572b41eb | 1076 | SET_RCODE(header, NXDOMAIN); |
9e4abcb5 SK |
1077 | else if (p && flags == F_IPV4) |
1078 | { /* we know the address */ | |
572b41eb | 1079 | SET_RCODE(header, NOERROR); |
9e4abcb5 | 1080 | header->ancount = htons(1); |
572b41eb SK |
1081 | header->hb3 |= HB3_AA; |
1082 | add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp); | |
9e4abcb5 SK |
1083 | } |
1084 | #ifdef HAVE_IPV6 | |
1085 | else if (p && flags == F_IPV6) | |
1086 | { | |
572b41eb | 1087 | SET_RCODE(header, NOERROR); |
9e4abcb5 | 1088 | header->ancount = htons(1); |
572b41eb SK |
1089 | header->hb3 |= HB3_AA; |
1090 | add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp); | |
9e4abcb5 SK |
1091 | } |
1092 | #endif | |
1093 | else /* nowhere to forward to */ | |
572b41eb | 1094 | SET_RCODE(header, REFUSED); |
9e4abcb5 SK |
1095 | |
1096 | return p - (unsigned char *)header; | |
1097 | } | |
36717eee SK |
1098 | |
1099 | /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ | |
5aabfc78 | 1100 | int check_for_local_domain(char *name, time_t now) |
36717eee SK |
1101 | { |
1102 | struct crec *crecp; | |
0a852541 SK |
1103 | struct mx_srv_record *mx; |
1104 | struct txt_record *txt; | |
f2621c7f SK |
1105 | struct interface_name *intr; |
1106 | struct ptr_record *ptr; | |
7de060b0 SK |
1107 | struct naptr *naptr; |
1108 | ||
1109 | if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) && | |
36717eee SK |
1110 | (crecp->flags & (F_HOSTS | F_DHCP))) |
1111 | return 1; | |
1112 | ||
7de060b0 SK |
1113 | for (naptr = daemon->naptr; naptr; naptr = naptr->next) |
1114 | if (hostname_isequal(name, naptr->name)) | |
1115 | return 1; | |
1116 | ||
1117 | for (mx = daemon->mxnames; mx; mx = mx->next) | |
0a852541 | 1118 | if (hostname_isequal(name, mx->name)) |
36717eee | 1119 | return 1; |
f6b7dc47 | 1120 | |
0a852541 SK |
1121 | for (txt = daemon->txt; txt; txt = txt->next) |
1122 | if (hostname_isequal(name, txt->name)) | |
f6b7dc47 | 1123 | return 1; |
f2621c7f SK |
1124 | |
1125 | for (intr = daemon->int_names; intr; intr = intr->next) | |
1126 | if (hostname_isequal(name, intr->name)) | |
1127 | return 1; | |
1128 | ||
1129 | for (ptr = daemon->ptr; ptr; ptr = ptr->next) | |
1130 | if (hostname_isequal(name, ptr->name)) | |
1131 | return 1; | |
1132 | ||
36717eee SK |
1133 | return 0; |
1134 | } | |
9e4abcb5 SK |
1135 | |
1136 | /* Is the packet a reply with the answer address equal to addr? | |
1137 | If so mung is into an NXDOMAIN reply and also put that information | |
1138 | in the cache. */ | |
572b41eb | 1139 | int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, |
9e4abcb5 SK |
1140 | struct bogus_addr *baddr, time_t now) |
1141 | { | |
1142 | unsigned char *p; | |
1143 | int i, qtype, qclass, rdlen; | |
1144 | unsigned long ttl; | |
1145 | struct bogus_addr *baddrp; | |
1146 | ||
1147 | /* skip over questions */ | |
1148 | if (!(p = skip_questions(header, qlen))) | |
1149 | return 0; /* bad packet */ | |
1150 | ||
5aabfc78 | 1151 | for (i = ntohs(header->ancount); i != 0; i--) |
9e4abcb5 | 1152 | { |
9009d746 | 1153 | if (!extract_name(header, qlen, &p, name, 1, 10)) |
9e4abcb5 SK |
1154 | return 0; /* bad packet */ |
1155 | ||
1156 | GETSHORT(qtype, p); | |
1157 | GETSHORT(qclass, p); | |
1158 | GETLONG(ttl, p); | |
1159 | GETSHORT(rdlen, p); | |
1160 | ||
1161 | if (qclass == C_IN && qtype == T_A) | |
9009d746 SK |
1162 | { |
1163 | if (!CHECK_LEN(header, p, qlen, INADDRSZ)) | |
1164 | return 0; | |
1165 | ||
1166 | for (baddrp = baddr; baddrp; baddrp = baddrp->next) | |
1167 | if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) | |
1168 | { | |
1169 | /* Found a bogus address. Insert that info here, since there no SOA record | |
1170 | to get the ttl from in the normal processing */ | |
1171 | cache_start_insert(); | |
1172 | cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG); | |
1173 | cache_end_insert(); | |
1174 | ||
1175 | return 1; | |
1176 | } | |
1177 | } | |
9e4abcb5 | 1178 | |
9009d746 SK |
1179 | if (!ADD_RDLEN(header, p, qlen, rdlen)) |
1180 | return 0; | |
9e4abcb5 SK |
1181 | } |
1182 | ||
1183 | return 0; | |
1184 | } | |
1185 | ||
572b41eb | 1186 | static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, |
3d8df260 | 1187 | unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...) |
f6b7dc47 SK |
1188 | { |
1189 | va_list ap; | |
1190 | unsigned char *sav, *p = *pp; | |
1191 | int j; | |
1192 | unsigned short usval; | |
1193 | long lval; | |
1194 | char *sval; | |
1195 | ||
1196 | if (truncp && *truncp) | |
1197 | return 0; | |
1198 | ||
1199 | PUTSHORT(nameoffset | 0xc000, p); | |
1200 | PUTSHORT(type, p); | |
1201 | PUTSHORT(class, p); | |
1202 | PUTLONG(ttl, p); /* TTL */ | |
1203 | ||
1204 | sav = p; /* Save pointer to RDLength field */ | |
1205 | PUTSHORT(0, p); /* Placeholder RDLength */ | |
1206 | ||
1207 | va_start(ap, format); /* make ap point to 1st unamed argument */ | |
1208 | ||
1209 | for (; *format; format++) | |
1210 | switch (*format) | |
1211 | { | |
1212 | #ifdef HAVE_IPV6 | |
1213 | case '6': | |
1214 | sval = va_arg(ap, char *); | |
1215 | memcpy(p, sval, IN6ADDRSZ); | |
1216 | p += IN6ADDRSZ; | |
1217 | break; | |
1218 | #endif | |
1219 | ||
1220 | case '4': | |
1221 | sval = va_arg(ap, char *); | |
1222 | memcpy(p, sval, INADDRSZ); | |
1223 | p += INADDRSZ; | |
1224 | break; | |
1225 | ||
1226 | case 's': | |
1227 | usval = va_arg(ap, int); | |
1228 | PUTSHORT(usval, p); | |
1229 | break; | |
1230 | ||
1231 | case 'l': | |
1232 | lval = va_arg(ap, long); | |
1233 | PUTLONG(lval, p); | |
1234 | break; | |
1235 | ||
1236 | case 'd': | |
1237 | /* get domain-name answer arg and store it in RDATA field */ | |
0a852541 SK |
1238 | if (offset) |
1239 | *offset = p - (unsigned char *)header; | |
3d8df260 | 1240 | p = do_rfc1035_name(p, va_arg(ap, char *)); |
f6b7dc47 SK |
1241 | *p++ = 0; |
1242 | break; | |
3d8df260 | 1243 | |
f6b7dc47 | 1244 | case 't': |
0a852541 | 1245 | usval = va_arg(ap, int); |
f6b7dc47 | 1246 | sval = va_arg(ap, char *); |
0a852541 SK |
1247 | memcpy(p, sval, usval); |
1248 | p += usval; | |
f6b7dc47 | 1249 | break; |
1a6bca81 SK |
1250 | |
1251 | case 'z': | |
1252 | sval = va_arg(ap, char *); | |
1253 | usval = sval ? strlen(sval) : 0; | |
1254 | if (usval > 255) | |
1255 | usval = 255; | |
1256 | *p++ = (unsigned char)usval; | |
1257 | memcpy(p, sval, usval); | |
1258 | p += usval; | |
1259 | break; | |
f6b7dc47 SK |
1260 | } |
1261 | ||
1262 | va_end(ap); /* clean up variable argument pointer */ | |
1263 | ||
1264 | j = p - sav - 2; | |
1265 | PUTSHORT(j, sav); /* Now, store real RDLength */ | |
1266 | ||
f6b7dc47 SK |
1267 | /* check for overflow of buffer */ |
1268 | if (limit && ((unsigned char *)limit - p) < 0) | |
1269 | { | |
1270 | if (truncp) | |
1271 | *truncp = 1; | |
1272 | return 0; | |
1273 | } | |
1274 | ||
1275 | *pp = p; | |
1276 | return 1; | |
1277 | } | |
1278 | ||
9009d746 SK |
1279 | static unsigned long crec_ttl(struct crec *crecp, time_t now) |
1280 | { | |
1281 | /* Return 0 ttl for DHCP entries, which might change | |
1282 | before the lease expires. */ | |
1283 | ||
1284 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
1285 | return daemon->local_ttl; | |
1286 | ||
8ef5ada2 SK |
1287 | /* Return the Max TTL value if it is lower then the actual TTL */ |
1288 | if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl)) | |
1289 | return crecp->ttd - now; | |
1290 | else | |
1291 | return daemon->max_ttl; | |
9009d746 SK |
1292 | } |
1293 | ||
1294 | ||
9e4abcb5 | 1295 | /* return zero if we can't answer from cache, or packet size if we can */ |
572b41eb | 1296 | size_t answer_request(struct dns_header *header, char *limit, size_t qlen, |
cdeda28f | 1297 | struct in_addr local_addr, struct in_addr local_netmask, time_t now) |
9e4abcb5 | 1298 | { |
3be34541 | 1299 | char *name = daemon->namebuff; |
feba5c1d | 1300 | unsigned char *p, *ansp, *pheader; |
832af0ba | 1301 | int qtype, qclass; |
9e4abcb5 SK |
1302 | struct all_addr addr; |
1303 | unsigned int nameoffset; | |
feba5c1d | 1304 | unsigned short flag; |
0a852541 | 1305 | int q, ans, anscount = 0, addncount = 0; |
feba5c1d | 1306 | int dryrun = 0, sec_reqd = 0; |
832af0ba | 1307 | int is_sign; |
9e4abcb5 | 1308 | struct crec *crecp; |
f6b7dc47 | 1309 | int nxdomain = 0, auth = 1, trunc = 0; |
0a852541 SK |
1310 | struct mx_srv_record *rec; |
1311 | ||
feba5c1d SK |
1312 | /* If there is an RFC2671 pseudoheader then it will be overwritten by |
1313 | partial replies, so we have to do a dry run to see if we can answer | |
1314 | the query. We check to see if the do bit is set, if so we always | |
1315 | forward rather than answering from the cache, which doesn't include | |
1316 | security information. */ | |
1317 | ||
832af0ba | 1318 | if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) |
feba5c1d | 1319 | { |
7de060b0 | 1320 | unsigned short udpsz, flags; |
feba5c1d SK |
1321 | unsigned char *psave = pheader; |
1322 | ||
1323 | GETSHORT(udpsz, pheader); | |
7de060b0 | 1324 | pheader += 2; /* ext_rcode */ |
feba5c1d SK |
1325 | GETSHORT(flags, pheader); |
1326 | ||
1327 | sec_reqd = flags & 0x8000; /* do bit */ | |
1328 | ||
1329 | /* If our client is advertising a larger UDP packet size | |
1330 | than we allow, trim it so that we don't get an overlarge | |
1331 | response from upstream */ | |
1332 | ||
832af0ba | 1333 | if (!is_sign && (udpsz > daemon->edns_pktsz)) |
3be34541 | 1334 | PUTSHORT(daemon->edns_pktsz, psave); |
feba5c1d SK |
1335 | |
1336 | dryrun = 1; | |
1337 | } | |
1338 | ||
572b41eb | 1339 | if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) |
832af0ba SK |
1340 | return 0; |
1341 | ||
0a852541 SK |
1342 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1343 | rec->offset = 0; | |
1344 | ||
feba5c1d | 1345 | rerun: |
9e4abcb5 SK |
1346 | /* determine end of question section (we put answers there) */ |
1347 | if (!(ansp = skip_questions(header, qlen))) | |
1348 | return 0; /* bad packet */ | |
1349 | ||
1350 | /* now process each question, answers go in RRs after the question */ | |
1351 | p = (unsigned char *)(header+1); | |
feba5c1d | 1352 | |
5aabfc78 | 1353 | for (q = ntohs(header->qdcount); q != 0; q--) |
9e4abcb5 SK |
1354 | { |
1355 | /* save pointer to name for copying into answers */ | |
1356 | nameoffset = p - (unsigned char *)header; | |
1357 | ||
1358 | /* now extract name as .-concatenated string into name */ | |
9009d746 | 1359 | if (!extract_name(header, qlen, &p, name, 1, 4)) |
9e4abcb5 | 1360 | return 0; /* bad packet */ |
832af0ba | 1361 | |
9e4abcb5 SK |
1362 | GETSHORT(qtype, p); |
1363 | GETSHORT(qclass, p); | |
1364 | ||
1365 | ans = 0; /* have we answered this question */ | |
1366 | ||
0a852541 | 1367 | if (qtype == T_TXT || qtype == T_ANY) |
9e4abcb5 | 1368 | { |
0a852541 SK |
1369 | struct txt_record *t; |
1370 | for(t = daemon->txt; t ; t = t->next) | |
9e4abcb5 | 1371 | { |
0a852541 SK |
1372 | if (t->class == qclass && hostname_isequal(name, t->name)) |
1373 | { | |
1374 | ans = 1; | |
e17fb629 SK |
1375 | if (!dryrun) |
1376 | { | |
28866e95 | 1377 | log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>"); |
e17fb629 SK |
1378 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1379 | daemon->local_ttl, NULL, | |
1380 | T_TXT, t->class, "t", t->len, t->txt)) | |
1381 | anscount++; | |
1382 | ||
1383 | } | |
0a852541 | 1384 | } |
9e4abcb5 | 1385 | } |
0a852541 | 1386 | } |
f6b7dc47 | 1387 | |
0a852541 | 1388 | if (qclass == C_IN) |
9e4abcb5 | 1389 | { |
f6b7dc47 | 1390 | if (qtype == T_PTR || qtype == T_ANY) |
c1bb8504 | 1391 | { |
832af0ba SK |
1392 | /* see if it's w.z.y.z.in-addr.arpa format */ |
1393 | int is_arpa = in_arpa_name_2_addr(name, &addr); | |
1394 | struct ptr_record *ptr; | |
f2621c7f | 1395 | struct interface_name* intr = NULL; |
832af0ba SK |
1396 | |
1397 | for (ptr = daemon->ptr; ptr; ptr = ptr->next) | |
1398 | if (hostname_isequal(name, ptr->name)) | |
1399 | break; | |
1400 | ||
f2621c7f SK |
1401 | if (is_arpa == F_IPV4) |
1402 | for (intr = daemon->int_names; intr; intr = intr->next) | |
5aabfc78 SK |
1403 | { |
1404 | if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr) | |
1405 | break; | |
1406 | else | |
1407 | while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) | |
1408 | intr = intr->next; | |
1409 | } | |
f2621c7f SK |
1410 | |
1411 | if (intr) | |
1412 | { | |
1413 | ans = 1; | |
1414 | if (!dryrun) | |
f6b7dc47 | 1415 | { |
1a6bca81 | 1416 | log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL); |
f2621c7f SK |
1417 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1418 | daemon->local_ttl, NULL, | |
1419 | T_PTR, C_IN, "d", intr->name)) | |
1420 | anscount++; | |
9e4abcb5 | 1421 | } |
9e4abcb5 | 1422 | } |
832af0ba SK |
1423 | else if (ptr) |
1424 | { | |
1425 | ans = 1; | |
1426 | if (!dryrun) | |
1427 | { | |
28866e95 | 1428 | log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>"); |
832af0ba | 1429 | for (ptr = daemon->ptr; ptr; ptr = ptr->next) |
f2621c7f SK |
1430 | if (hostname_isequal(name, ptr->name) && |
1431 | add_resource_record(header, limit, &trunc, nameoffset, &ansp, | |
1432 | daemon->local_ttl, NULL, | |
1433 | T_PTR, C_IN, "d", ptr->ptr)) | |
1434 | anscount++; | |
1435 | ||
1436 | } | |
1437 | } | |
1438 | else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) | |
1439 | do | |
1440 | { | |
1441 | /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ | |
1442 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1443 | continue; | |
1444 | ||
1445 | if (crecp->flags & F_NEG) | |
1446 | { | |
1447 | ans = 1; | |
1448 | auth = 0; | |
1449 | if (crecp->flags & F_NXDOMAIN) | |
1450 | nxdomain = 1; | |
1451 | if (!dryrun) | |
1a6bca81 | 1452 | log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); |
f2621c7f SK |
1453 | } |
1454 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) | |
1455 | { | |
1456 | ans = 1; | |
1457 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1458 | auth = 0; | |
1459 | if (!dryrun) | |
832af0ba | 1460 | { |
1a6bca81 | 1461 | log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, |
7622fc06 | 1462 | record_source(crecp->uid)); |
f2621c7f | 1463 | |
9009d746 SK |
1464 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1465 | crec_ttl(crecp, now), NULL, | |
f2621c7f | 1466 | T_PTR, C_IN, "d", cache_get_name(crecp))) |
832af0ba SK |
1467 | anscount++; |
1468 | } | |
f2621c7f SK |
1469 | } |
1470 | } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); | |
1471 | else if (is_arpa == F_IPV4 && | |
28866e95 | 1472 | option_bool(OPT_BOGUSPRIV) && |
8ef5ada2 | 1473 | private_net(addr.addr.addr4, 1)) |
f2621c7f SK |
1474 | { |
1475 | /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */ | |
1476 | ans = 1; | |
1477 | nxdomain = 1; | |
1478 | if (!dryrun) | |
1479 | log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, | |
1a6bca81 | 1480 | name, &addr, NULL); |
832af0ba | 1481 | } |
f6b7dc47 | 1482 | } |
f2621c7f | 1483 | |
f6b7dc47 SK |
1484 | for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) |
1485 | { | |
1486 | unsigned short type = T_A; | |
f2621c7f | 1487 | |
f6b7dc47 | 1488 | if (flag == F_IPV6) |
feba5c1d | 1489 | #ifdef HAVE_IPV6 |
3d8df260 | 1490 | type = T_AAAA; |
feba5c1d | 1491 | #else |
3d8df260 | 1492 | break; |
feba5c1d | 1493 | #endif |
f6b7dc47 SK |
1494 | |
1495 | if (qtype != type && qtype != T_ANY) | |
1496 | continue; | |
1497 | ||
316e2730 SK |
1498 | /* Check for "A for A" queries; be rather conservative |
1499 | about what looks like dotted-quad. */ | |
1500 | if (qtype == T_A) | |
3d8df260 | 1501 | { |
316e2730 SK |
1502 | char *cp; |
1503 | unsigned int i, a; | |
1504 | int x; | |
1505 | ||
1506 | for (cp = name, i = 0, a = 0; *cp; i++) | |
3d8df260 | 1507 | { |
572b41eb | 1508 | if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255) |
316e2730 SK |
1509 | { |
1510 | i = 5; | |
1511 | break; | |
1512 | } | |
1513 | ||
1514 | a = (a << 8) + x; | |
1515 | ||
1516 | if (*cp == '.') | |
1517 | cp++; | |
1518 | } | |
1519 | ||
1520 | if (i == 4) | |
1521 | { | |
1522 | ans = 1; | |
1523 | if (!dryrun) | |
1524 | { | |
1525 | addr.addr.addr4.s_addr = htonl(a); | |
1526 | log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL); | |
1527 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, | |
1528 | daemon->local_ttl, NULL, type, C_IN, "4", &addr)) | |
1529 | anscount++; | |
1530 | } | |
1531 | continue; | |
3d8df260 | 1532 | } |
3d8df260 SK |
1533 | } |
1534 | ||
f2621c7f SK |
1535 | /* interface name stuff */ |
1536 | if (qtype == T_A) | |
1537 | { | |
1538 | struct interface_name *intr; | |
1539 | ||
1540 | for (intr = daemon->int_names; intr; intr = intr->next) | |
1541 | if (hostname_isequal(name, intr->name)) | |
1542 | break; | |
1543 | ||
1544 | if (intr) | |
1545 | { | |
1546 | ans = 1; | |
1547 | if (!dryrun) | |
1548 | { | |
5aabfc78 | 1549 | if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1) |
1a6bca81 | 1550 | log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL); |
f2621c7f SK |
1551 | else |
1552 | { | |
1a6bca81 | 1553 | log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL); |
f2621c7f SK |
1554 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1555 | daemon->local_ttl, NULL, type, C_IN, "4", &addr)) | |
1556 | anscount++; | |
1557 | } | |
1558 | } | |
1559 | continue; | |
1560 | } | |
1561 | } | |
1562 | ||
f6b7dc47 SK |
1563 | cname_restart: |
1564 | if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME))) | |
1565 | { | |
1566 | int localise = 0; | |
feba5c1d | 1567 | |
f6b7dc47 SK |
1568 | /* See if a putative address is on the network from which we recieved |
1569 | the query, is so we'll filter other answers. */ | |
28866e95 | 1570 | if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4) |
f6b7dc47 SK |
1571 | { |
1572 | struct crec *save = crecp; | |
1573 | do { | |
1574 | if ((crecp->flags & F_HOSTS) && | |
1575 | is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1576 | { | |
1577 | localise = 1; | |
1578 | break; | |
1579 | } | |
1580 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); | |
1581 | crecp = save; | |
1582 | } | |
1583 | ||
1584 | do | |
9e4abcb5 | 1585 | { |
26128d27 SK |
1586 | /* don't answer wildcard queries with data not from /etc/hosts |
1587 | or DHCP leases */ | |
1588 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1589 | break; | |
1590 | ||
fd9fa481 SK |
1591 | if (crecp->flags & F_CNAME) |
1592 | { | |
fd9fa481 SK |
1593 | if (!dryrun) |
1594 | { | |
7622fc06 | 1595 | log_query(crecp->flags, name, NULL, record_source(crecp->uid)); |
9009d746 SK |
1596 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1597 | crec_ttl(crecp, now), &nameoffset, | |
f6b7dc47 SK |
1598 | T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache))) |
1599 | anscount++; | |
fd9fa481 | 1600 | } |
f6b7dc47 | 1601 | |
fd9fa481 SK |
1602 | strcpy(name, cache_get_name(crecp->addr.cname.cache)); |
1603 | goto cname_restart; | |
1604 | } | |
f6b7dc47 | 1605 | |
9e4abcb5 SK |
1606 | if (crecp->flags & F_NEG) |
1607 | { | |
feba5c1d | 1608 | ans = 1; |
f6b7dc47 SK |
1609 | auth = 0; |
1610 | if (crecp->flags & F_NXDOMAIN) | |
1611 | nxdomain = 1; | |
feba5c1d | 1612 | if (!dryrun) |
1a6bca81 | 1613 | log_query(crecp->flags, name, NULL, NULL); |
9e4abcb5 | 1614 | } |
feba5c1d | 1615 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) |
9e4abcb5 | 1616 | { |
f6b7dc47 SK |
1617 | /* If we are returning local answers depending on network, |
1618 | filter here. */ | |
1619 | if (localise && | |
1620 | (crecp->flags & F_HOSTS) && | |
1621 | !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1622 | continue; | |
1623 | ||
1624 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1625 | auth = 0; | |
1626 | ||
feba5c1d SK |
1627 | ans = 1; |
1628 | if (!dryrun) | |
1629 | { | |
fd9fa481 | 1630 | log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr, |
7622fc06 | 1631 | record_source(crecp->uid)); |
feba5c1d | 1632 | |
9009d746 SK |
1633 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1634 | crec_ttl(crecp, now), NULL, type, C_IN, | |
f6b7dc47 SK |
1635 | type == T_A ? "4" : "6", &crecp->addr)) |
1636 | anscount++; | |
feba5c1d | 1637 | } |
9e4abcb5 | 1638 | } |
f6b7dc47 | 1639 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); |
9e4abcb5 | 1640 | } |
f6b7dc47 SK |
1641 | } |
1642 | ||
1643 | if (qtype == T_MX || qtype == T_ANY) | |
1644 | { | |
1645 | int found = 0; | |
0a852541 SK |
1646 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1647 | if (!rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1648 | { |
1649 | ans = found = 1; | |
1650 | if (!dryrun) | |
feba5c1d | 1651 | { |
3d8df260 | 1652 | unsigned int offset; |
28866e95 | 1653 | log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>"); |
0a852541 SK |
1654 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
1655 | &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) | |
1656 | { | |
1657 | anscount++; | |
1658 | if (rec->target) | |
1659 | rec->offset = offset; | |
1660 | } | |
feba5c1d | 1661 | } |
f6b7dc47 SK |
1662 | } |
1663 | ||
28866e95 | 1664 | if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && |
f6b7dc47 SK |
1665 | cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP)) |
1666 | { | |
1667 | ans = 1; | |
1668 | if (!dryrun) | |
1669 | { | |
28866e95 | 1670 | log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>"); |
f6b7dc47 SK |
1671 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, |
1672 | T_MX, C_IN, "sd", 1, | |
28866e95 | 1673 | option_bool(OPT_SELFMX) ? name : daemon->mxtarget)) |
f6b7dc47 | 1674 | anscount++; |
9e4abcb5 SK |
1675 | } |
1676 | } | |
f6b7dc47 SK |
1677 | } |
1678 | ||
1679 | if (qtype == T_SRV || qtype == T_ANY) | |
1680 | { | |
1681 | int found = 0; | |
28866e95 SK |
1682 | struct mx_srv_record *move = NULL, **up = &daemon->mxnames; |
1683 | ||
0a852541 SK |
1684 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1685 | if (rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1686 | { |
1687 | found = ans = 1; | |
1688 | if (!dryrun) | |
1689 | { | |
3d8df260 | 1690 | unsigned int offset; |
28866e95 | 1691 | log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>"); |
f6b7dc47 | 1692 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
0a852541 SK |
1693 | &offset, T_SRV, C_IN, "sssd", |
1694 | rec->priority, rec->weight, rec->srvport, rec->target)) | |
1695 | { | |
1696 | anscount++; | |
1697 | if (rec->target) | |
1698 | rec->offset = offset; | |
1699 | } | |
f6b7dc47 | 1700 | } |
28866e95 SK |
1701 | |
1702 | /* unlink first SRV record found */ | |
1703 | if (!move) | |
1704 | { | |
1705 | move = rec; | |
1706 | *up = rec->next; | |
1707 | } | |
1708 | else | |
1709 | up = &rec->next; | |
f6b7dc47 | 1710 | } |
28866e95 SK |
1711 | else |
1712 | up = &rec->next; | |
1713 | ||
1714 | /* put first SRV record back at the end. */ | |
1715 | if (move) | |
1716 | { | |
1717 | *up = move; | |
1718 | move->next = NULL; | |
1719 | } | |
feba5c1d | 1720 | |
28866e95 | 1721 | if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) |
f6b7dc47 SK |
1722 | { |
1723 | ans = 1; | |
1724 | if (!dryrun) | |
1a6bca81 | 1725 | log_query(F_CONFIG | F_NEG, name, NULL, NULL); |
f6b7dc47 SK |
1726 | } |
1727 | } | |
1a6bca81 SK |
1728 | |
1729 | if (qtype == T_NAPTR || qtype == T_ANY) | |
1730 | { | |
1731 | struct naptr *na; | |
1732 | for (na = daemon->naptr; na; na = na->next) | |
1733 | if (hostname_isequal(name, na->name)) | |
1734 | { | |
1735 | ans = 1; | |
1736 | if (!dryrun) | |
1737 | { | |
28866e95 | 1738 | log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>"); |
1a6bca81 SK |
1739 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
1740 | NULL, T_NAPTR, C_IN, "sszzzd", | |
1741 | na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) | |
1742 | anscount++; | |
1743 | } | |
1744 | } | |
1745 | } | |
f6b7dc47 SK |
1746 | |
1747 | if (qtype == T_MAILB) | |
1748 | ans = 1, nxdomain = 1; | |
1749 | ||
28866e95 | 1750 | if (qtype == T_SOA && option_bool(OPT_FILTER)) |
f6b7dc47 SK |
1751 | { |
1752 | ans = 1; | |
1753 | if (!dryrun) | |
1a6bca81 | 1754 | log_query(F_CONFIG | F_NEG, name, &addr, NULL); |
feba5c1d | 1755 | } |
9e4abcb5 | 1756 | } |
f6b7dc47 SK |
1757 | |
1758 | if (!ans) | |
9e4abcb5 | 1759 | return 0; /* failed to answer a question */ |
feba5c1d | 1760 | } |
f6b7dc47 | 1761 | |
feba5c1d SK |
1762 | if (dryrun) |
1763 | { | |
1764 | dryrun = 0; | |
1765 | goto rerun; | |
9e4abcb5 SK |
1766 | } |
1767 | ||
0a852541 SK |
1768 | /* create an additional data section, for stuff in SRV and MX record replies. */ |
1769 | for (rec = daemon->mxnames; rec; rec = rec->next) | |
1770 | if (rec->offset != 0) | |
1771 | { | |
1772 | /* squash dupes */ | |
1773 | struct mx_srv_record *tmp; | |
1774 | for (tmp = rec->next; tmp; tmp = tmp->next) | |
1775 | if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target)) | |
1776 | tmp->offset = 0; | |
1777 | ||
1778 | crecp = NULL; | |
1779 | while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6))) | |
1780 | { | |
0a852541 SK |
1781 | #ifdef HAVE_IPV6 |
1782 | int type = crecp->flags & F_IPV4 ? T_A : T_AAAA; | |
1783 | #else | |
1784 | int type = T_A; | |
1785 | #endif | |
1786 | if (crecp->flags & F_NEG) | |
1787 | continue; | |
1788 | ||
9009d746 SK |
1789 | if (add_resource_record(header, limit, NULL, rec->offset, &ansp, |
1790 | crec_ttl(crecp, now), NULL, type, C_IN, | |
0a852541 SK |
1791 | crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) |
1792 | addncount++; | |
1793 | } | |
1794 | } | |
1795 | ||
9e4abcb5 | 1796 | /* done all questions, set up header and return length of result */ |
572b41eb SK |
1797 | /* clear authoritative and truncated flags, set QR flag */ |
1798 | header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR; | |
1799 | /* set RA flag */ | |
1800 | header->hb4 |= HB4_RA; | |
1801 | ||
1802 | /* authoritive - only hosts and DHCP derived names. */ | |
1803 | if (auth) | |
1804 | header->hb3 |= HB3_AA; | |
1805 | ||
1806 | /* truncation */ | |
1807 | if (trunc) | |
1808 | header->hb3 |= HB3_TC; | |
1809 | ||
9e4abcb5 | 1810 | if (anscount == 0 && nxdomain) |
572b41eb | 1811 | SET_RCODE(header, NXDOMAIN); |
9e4abcb5 | 1812 | else |
572b41eb | 1813 | SET_RCODE(header, NOERROR); /* no error */ |
9e4abcb5 SK |
1814 | header->ancount = htons(anscount); |
1815 | header->nscount = htons(0); | |
0a852541 | 1816 | header->arcount = htons(addncount); |
9e4abcb5 SK |
1817 | return ansp - (unsigned char *)header; |
1818 | } | |
1819 | ||
1820 | ||
1821 | ||
1822 | ||
1823 |