]>
Commit | Line | Data |
---|---|---|
316e2730 | 1 | /* dnsmasq is Copyright (c) 2000-2010 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 | ||
f6b7dc47 SK |
19 | static int add_resource_record(HEADER *header, char *limit, int *truncp, |
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) \ | |
73a08a24 | 28 | (!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1) |
9009d746 | 29 | |
cdeda28f | 30 | static int extract_name(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++) | |
220 | if (!isdigit((int)*cp)) | |
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 | { | |
9e038946 | 247 | for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *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 | { | |
265 | if (*(cp1+1) || !isxdigit((int)*cp1)) | |
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 | ||
9009d746 | 281 | static unsigned char *skip_name(unsigned char *ansp, 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 | ||
cdeda28f | 336 | static unsigned char *skip_questions(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 | ||
cdeda28f | 351 | static unsigned char *skip_section(unsigned char *ansp, int count, 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. */ | |
cdeda28f | 374 | unsigned int questions_crc(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 | ||
cdeda28f | 416 | size_t resize_packet(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 | ||
832af0ba | 440 | unsigned char *find_pseudoheader(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 | ||
456 | if (header->opcode == 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 SK |
512 | } |
513 | ||
514 | ||
9e4abcb5 | 515 | /* is addr in the non-globally-routed IP space? */ |
8ef5ada2 | 516 | static int private_net(struct in_addr addr, int ban_localhost) |
9e4abcb5 | 517 | { |
f2621c7f SK |
518 | in_addr_t ip_addr = ntohl(addr.s_addr); |
519 | ||
520 | return | |
8ef5ada2 | 521 | (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ || |
f2621c7f SK |
522 | ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ || |
523 | ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ || | |
524 | ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ || | |
525 | ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ; | |
9e4abcb5 | 526 | } |
fd9fa481 | 527 | |
8ef5ada2 | 528 | static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen, char *name) |
824af85b SK |
529 | { |
530 | int i, qtype, qclass, rdlen; | |
531 | unsigned long ttl; | |
532 | ||
533 | for (i = count; i != 0; i--) | |
534 | { | |
8ef5ada2 SK |
535 | if (name && (daemon->options & OPT_LOG)) |
536 | { | |
537 | if (!extract_name(header, qlen, &p, name, 1, 10)) | |
538 | return 0; | |
539 | } | |
540 | else if (!(p = skip_name(p, header, qlen, 10))) | |
824af85b SK |
541 | return 0; /* bad packet */ |
542 | ||
543 | GETSHORT(qtype, p); | |
544 | GETSHORT(qclass, p); | |
545 | GETLONG(ttl, p); | |
546 | GETSHORT(rdlen, p); | |
547 | ||
8ef5ada2 | 548 | if (qclass == C_IN && qtype == T_A) |
824af85b SK |
549 | { |
550 | struct doctor *doctor; | |
551 | struct in_addr addr; | |
552 | ||
9009d746 SK |
553 | if (!CHECK_LEN(header, p, qlen, INADDRSZ)) |
554 | return 0; | |
8ef5ada2 SK |
555 | |
556 | /* alignment */ | |
824af85b SK |
557 | memcpy(&addr, p, INADDRSZ); |
558 | ||
559 | for (doctor = daemon->doctors; doctor; doctor = doctor->next) | |
73a08a24 SK |
560 | { |
561 | if (doctor->end.s_addr == 0) | |
562 | { | |
563 | if (!is_same_net(doctor->in, addr, doctor->mask)) | |
564 | continue; | |
565 | } | |
566 | else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) || | |
567 | ntohl(doctor->end.s_addr) < ntohl(addr.s_addr)) | |
568 | continue; | |
8ef5ada2 | 569 | |
73a08a24 SK |
570 | addr.s_addr &= ~doctor->mask.s_addr; |
571 | addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); | |
572 | /* Since we munged the data, the server it came from is no longer authoritative */ | |
573 | header->aa = 0; | |
574 | memcpy(p, &addr, INADDRSZ); | |
575 | break; | |
576 | } | |
824af85b | 577 | } |
8ef5ada2 SK |
578 | else if (qtype == T_TXT && name && (daemon->options & OPT_LOG)) |
579 | { | |
580 | unsigned char *p1 = p; | |
581 | if (!CHECK_LEN(header, p1, qlen, rdlen)) | |
582 | return 0; | |
583 | while ((p1 - p) < rdlen) | |
584 | { | |
585 | unsigned int i, len = *p1; | |
586 | unsigned char *p2 = p1; | |
587 | /* make counted string zero-term and sanitise */ | |
588 | for (i = 0; i < len; i++) | |
589 | if (isprint(*(p2+1))) | |
590 | { | |
591 | *p2 = *(p2+1); | |
592 | p2++; | |
593 | } | |
594 | *p2 = 0; | |
595 | my_syslog(LOG_DEBUG, "reply %s is %s", name, p1); | |
596 | /* restore */ | |
597 | memmove(p1 + 1, p1, len); | |
598 | *p1 = len; | |
599 | p1 += len+1; | |
600 | } | |
601 | } | |
824af85b | 602 | |
9009d746 SK |
603 | if (!ADD_RDLEN(header, p, qlen, rdlen)) |
604 | return 0; /* bad packet */ | |
824af85b SK |
605 | } |
606 | ||
607 | return p; | |
608 | } | |
609 | ||
8ef5ada2 | 610 | static int find_soa(HEADER *header, size_t qlen, char *name) |
9e4abcb5 SK |
611 | { |
612 | unsigned char *p; | |
9e4abcb5 | 613 | int qtype, qclass, rdlen; |
fd9fa481 SK |
614 | unsigned long ttl, minttl = ULONG_MAX; |
615 | int i, found_soa = 0; | |
9e4abcb5 | 616 | |
fd9fa481 SK |
617 | /* first move to NS section and find TTL from any SOA section */ |
618 | if (!(p = skip_questions(header, qlen)) || | |
8ef5ada2 | 619 | !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name))) |
824af85b | 620 | return 0; /* bad packet */ |
9e4abcb5 | 621 | |
5aabfc78 | 622 | for (i = ntohs(header->nscount); i != 0; i--) |
9e4abcb5 | 623 | { |
9009d746 | 624 | if (!(p = skip_name(p, header, qlen, 10))) |
fd9fa481 SK |
625 | return 0; /* bad packet */ |
626 | ||
9e4abcb5 SK |
627 | GETSHORT(qtype, p); |
628 | GETSHORT(qclass, p); | |
629 | GETLONG(ttl, p); | |
630 | GETSHORT(rdlen, p); | |
fd9fa481 | 631 | |
9e4abcb5 SK |
632 | if ((qclass == C_IN) && (qtype == T_SOA)) |
633 | { | |
fd9fa481 SK |
634 | found_soa = 1; |
635 | if (ttl < minttl) | |
636 | minttl = ttl; | |
637 | ||
9e4abcb5 | 638 | /* MNAME */ |
9009d746 | 639 | if (!(p = skip_name(p, header, qlen, 0))) |
fd9fa481 | 640 | return 0; |
9e4abcb5 | 641 | /* RNAME */ |
9009d746 | 642 | if (!(p = skip_name(p, header, qlen, 20))) |
fd9fa481 SK |
643 | return 0; |
644 | p += 16; /* SERIAL REFRESH RETRY EXPIRE */ | |
645 | ||
9e4abcb5 SK |
646 | GETLONG(ttl, p); /* minTTL */ |
647 | if (ttl < minttl) | |
648 | minttl = ttl; | |
649 | } | |
9009d746 | 650 | else if (!ADD_RDLEN(header, p, qlen, rdlen)) |
fd9fa481 | 651 | return 0; /* bad packet */ |
9e4abcb5 | 652 | } |
9009d746 | 653 | |
824af85b | 654 | /* rewrite addresses in additioal section too */ |
8ef5ada2 | 655 | if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL)) |
824af85b SK |
656 | return 0; |
657 | ||
658 | if (!found_soa) | |
659 | minttl = daemon->neg_ttl; | |
660 | ||
661 | return minttl; | |
1cff166d SK |
662 | } |
663 | ||
fd9fa481 SK |
664 | /* Note that the following code can create CNAME chains that don't point to a real record, |
665 | either because of lack of memory, or lack of SOA records. These are treated by the cache code as | |
824af85b | 666 | expired and cleaned out that way. |
8ef5ada2 SK |
667 | Return 1 if we reject an address because it look like part of dns-rebinding attack. */ |
668 | int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int is_sign, int check_rebind) | |
9e4abcb5 | 669 | { |
824af85b | 670 | unsigned char *p, *p1, *endrr, *namep; |
fd9fa481 | 671 | int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; |
0a852541 | 672 | unsigned long ttl = 0; |
5aabfc78 | 673 | struct all_addr addr; |
0a852541 | 674 | |
9e4abcb5 | 675 | cache_start_insert(); |
0a852541 | 676 | |
8ef5ada2 SK |
677 | /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */ |
678 | if (daemon->doctors || (daemon->options & OPT_LOG)) | |
0a852541 SK |
679 | { |
680 | searched_soa = 1; | |
8ef5ada2 | 681 | ttl = find_soa(header, qlen, name); |
0a852541 | 682 | } |
9e4abcb5 | 683 | |
fd9fa481 SK |
684 | /* go through the questions. */ |
685 | p = (unsigned char *)(header+1); | |
9e4abcb5 | 686 | |
5aabfc78 | 687 | for (i = ntohs(header->qdcount); i != 0; i--) |
9e4abcb5 | 688 | { |
fd9fa481 SK |
689 | int found = 0, cname_count = 5; |
690 | struct crec *cpp = NULL; | |
691 | int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0; | |
0a852541 | 692 | unsigned long cttl = ULONG_MAX, attl; |
fd9fa481 | 693 | |
824af85b | 694 | namep = p; |
9009d746 | 695 | if (!extract_name(header, qlen, &p, name, 1, 4)) |
824af85b | 696 | return 0; /* bad packet */ |
fd9fa481 | 697 | |
9e4abcb5 SK |
698 | GETSHORT(qtype, p); |
699 | GETSHORT(qclass, p); | |
9e4abcb5 SK |
700 | |
701 | if (qclass != C_IN) | |
fd9fa481 | 702 | continue; |
9e4abcb5 | 703 | |
fd9fa481 SK |
704 | /* PTRs: we chase CNAMEs here, since we have no way to |
705 | represent them in the cache. */ | |
706 | if (qtype == T_PTR) | |
707 | { | |
9e4abcb5 | 708 | int name_encoding = in_arpa_name_2_addr(name, &addr); |
fd9fa481 SK |
709 | |
710 | if (!name_encoding) | |
711 | continue; | |
712 | ||
713 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 714 | { |
fd9fa481 SK |
715 | cname_loop: |
716 | if (!(p1 = skip_questions(header, qlen))) | |
824af85b | 717 | return 0; |
fd9fa481 | 718 | |
5aabfc78 | 719 | for (j = ntohs(header->ancount); j != 0; j--) |
fd9fa481 | 720 | { |
824af85b SK |
721 | unsigned char *tmp = namep; |
722 | /* the loop body overwrites the original name, so get it back here. */ | |
9009d746 SK |
723 | if (!extract_name(header, qlen, &tmp, name, 1, 0) || |
724 | !(res = extract_name(header, qlen, &p1, name, 0, 10))) | |
824af85b | 725 | return 0; /* bad packet */ |
fd9fa481 SK |
726 | |
727 | GETSHORT(aqtype, p1); | |
728 | GETSHORT(aqclass, p1); | |
729 | GETLONG(attl, p1); | |
8ef5ada2 SK |
730 | if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) |
731 | { | |
732 | (p1) -= NS_INT32SZ; | |
733 | PUTLONG(daemon->max_ttl, p1); | |
734 | } | |
fd9fa481 SK |
735 | GETSHORT(ardlen, p1); |
736 | endrr = p1+ardlen; | |
737 | ||
738 | /* TTL of record is minimum of CNAMES and PTR */ | |
739 | if (attl < cttl) | |
740 | cttl = attl; | |
741 | ||
742 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) | |
743 | { | |
9009d746 | 744 | if (!extract_name(header, qlen, &p1, name, 1, 0)) |
824af85b | 745 | return 0; |
fd9fa481 SK |
746 | |
747 | if (aqtype == T_CNAME) | |
748 | { | |
749 | if (!cname_count--) | |
824af85b | 750 | return 0; /* looped CNAMES */ |
fd9fa481 SK |
751 | goto cname_loop; |
752 | } | |
753 | ||
754 | cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE); | |
755 | found = 1; | |
756 | } | |
757 | ||
758 | p1 = endrr; | |
9009d746 | 759 | if (!CHECK_LEN(header, p1, qlen, 0)) |
824af85b | 760 | return 0; /* bad packet */ |
fd9fa481 SK |
761 | } |
762 | } | |
763 | ||
764 | if (!found && !(daemon->options & OPT_NO_NEG)) | |
765 | { | |
766 | if (!searched_soa) | |
767 | { | |
768 | searched_soa = 1; | |
8ef5ada2 | 769 | ttl = find_soa(header, qlen, NULL); |
fd9fa481 SK |
770 | } |
771 | if (ttl) | |
5aabfc78 | 772 | cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); |
9e4abcb5 SK |
773 | } |
774 | } | |
fd9fa481 | 775 | else |
9e4abcb5 | 776 | { |
fd9fa481 SK |
777 | /* everything other than PTR */ |
778 | struct crec *newc; | |
5aabfc78 SK |
779 | int addrlen; |
780 | ||
fd9fa481 | 781 | if (qtype == T_A) |
5aabfc78 SK |
782 | { |
783 | addrlen = INADDRSZ; | |
784 | flags |= F_IPV4; | |
785 | } | |
fd9fa481 SK |
786 | #ifdef HAVE_IPV6 |
787 | else if (qtype == T_AAAA) | |
5aabfc78 SK |
788 | { |
789 | addrlen = IN6ADDRSZ; | |
790 | flags |= F_IPV6; | |
791 | } | |
fd9fa481 SK |
792 | #endif |
793 | else | |
794 | continue; | |
795 | ||
796 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 797 | { |
fd9fa481 SK |
798 | cname_loop1: |
799 | if (!(p1 = skip_questions(header, qlen))) | |
824af85b | 800 | return 0; |
9e4abcb5 | 801 | |
5aabfc78 | 802 | for (j = ntohs(header->ancount); j != 0; j--) |
9e4abcb5 | 803 | { |
9009d746 | 804 | if (!(res = extract_name(header, qlen, &p1, name, 0, 10))) |
824af85b | 805 | return 0; /* bad packet */ |
fd9fa481 SK |
806 | |
807 | GETSHORT(aqtype, p1); | |
808 | GETSHORT(aqclass, p1); | |
809 | GETLONG(attl, p1); | |
8ef5ada2 SK |
810 | if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign) |
811 | { | |
812 | (p1) -= NS_INT32SZ; | |
813 | PUTLONG(daemon->max_ttl, p1); | |
814 | } | |
fd9fa481 SK |
815 | GETSHORT(ardlen, p1); |
816 | endrr = p1+ardlen; | |
817 | ||
818 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) | |
819 | { | |
820 | if (aqtype == T_CNAME) | |
821 | { | |
822 | if (!cname_count--) | |
824af85b | 823 | return 0; /* looped CNAMES */ |
fd9fa481 | 824 | newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD); |
26128d27 | 825 | if (newc && cpp) |
fd9fa481 SK |
826 | { |
827 | cpp->addr.cname.cache = newc; | |
828 | cpp->addr.cname.uid = newc->uid; | |
829 | } | |
9e4abcb5 | 830 | |
fd9fa481 SK |
831 | cpp = newc; |
832 | if (attl < cttl) | |
833 | cttl = attl; | |
834 | ||
9009d746 | 835 | if (!extract_name(header, qlen, &p1, name, 1, 0)) |
824af85b | 836 | return 0; |
fd9fa481 SK |
837 | goto cname_loop1; |
838 | } | |
839 | else | |
840 | { | |
841 | found = 1; | |
9009d746 | 842 | |
5aabfc78 | 843 | /* copy address into aligned storage */ |
9009d746 SK |
844 | if (!CHECK_LEN(header, p1, qlen, addrlen)) |
845 | return 0; /* bad packet */ | |
5aabfc78 | 846 | memcpy(&addr, p1, addrlen); |
824af85b SK |
847 | |
848 | /* check for returned address in private space */ | |
8ef5ada2 | 849 | if (check_rebind && |
824af85b | 850 | (flags & F_IPV4) && |
8ef5ada2 | 851 | private_net(addr.addr.addr4, !(daemon->options & OPT_LOCAL_REBIND))) |
824af85b SK |
852 | return 1; |
853 | ||
5aabfc78 | 854 | newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD); |
26128d27 | 855 | if (newc && cpp) |
fd9fa481 SK |
856 | { |
857 | cpp->addr.cname.cache = newc; | |
858 | cpp->addr.cname.uid = newc->uid; | |
859 | } | |
860 | cpp = NULL; | |
861 | } | |
862 | } | |
863 | ||
864 | p1 = endrr; | |
9009d746 | 865 | if (!CHECK_LEN(header, p1, qlen, 0)) |
824af85b | 866 | return 0; /* bad packet */ |
fd9fa481 SK |
867 | } |
868 | } | |
869 | ||
870 | if (!found && !(daemon->options & OPT_NO_NEG)) | |
871 | { | |
872 | if (!searched_soa) | |
1cff166d | 873 | { |
fd9fa481 | 874 | searched_soa = 1; |
8ef5ada2 | 875 | ttl = find_soa(header, qlen, NULL); |
1cff166d | 876 | } |
fd9fa481 | 877 | /* If there's no SOA to get the TTL from, but there is a CNAME |
824af85b | 878 | pointing at this, inherit its TTL */ |
fd9fa481 | 879 | if (ttl || cpp) |
9e4abcb5 | 880 | { |
5aabfc78 | 881 | newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags); |
26128d27 | 882 | if (newc && cpp) |
9e4abcb5 | 883 | { |
fd9fa481 SK |
884 | cpp->addr.cname.cache = newc; |
885 | cpp->addr.cname.uid = newc->uid; | |
886 | } | |
9e4abcb5 | 887 | } |
9e4abcb5 | 888 | } |
fd9fa481 | 889 | } |
9e4abcb5 | 890 | } |
fd9fa481 | 891 | |
824af85b SK |
892 | /* Don't put stuff from a truncated packet into the cache, but do everything else */ |
893 | if (!header->tc) | |
894 | cache_end_insert(); | |
895 | ||
896 | return 0; | |
9e4abcb5 SK |
897 | } |
898 | ||
899 | /* If the packet holds exactly one query | |
832af0ba SK |
900 | return F_IPV4 or F_IPV6 and leave the name from the query in name. |
901 | Abuse F_BIGNAME to indicate an NS query - yuck. */ | |
9e4abcb5 | 902 | |
cdeda28f | 903 | unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep) |
9e4abcb5 SK |
904 | { |
905 | unsigned char *p = (unsigned char *)(header+1); | |
906 | int qtype, qclass; | |
907 | ||
c1bb8504 SK |
908 | if (typep) |
909 | *typep = 0; | |
910 | ||
9e4abcb5 SK |
911 | if (ntohs(header->qdcount) != 1 || header->opcode != QUERY) |
912 | return 0; /* must be exactly one query. */ | |
913 | ||
9009d746 | 914 | if (!extract_name(header, qlen, &p, name, 1, 4)) |
9e4abcb5 SK |
915 | return 0; /* bad packet */ |
916 | ||
917 | GETSHORT(qtype, p); | |
918 | GETSHORT(qclass, p); | |
919 | ||
0a852541 SK |
920 | if (typep) |
921 | *typep = qtype; | |
922 | ||
9e4abcb5 SK |
923 | if (qclass == C_IN) |
924 | { | |
925 | if (qtype == T_A) | |
926 | return F_IPV4; | |
927 | if (qtype == T_AAAA) | |
928 | return F_IPV6; | |
929 | if (qtype == T_ANY) | |
930 | return F_IPV4 | F_IPV6; | |
832af0ba SK |
931 | if (qtype == T_NS || qtype == T_SOA) |
932 | return F_QUERY | F_BIGNAME; | |
9e4abcb5 SK |
933 | } |
934 | ||
935 | return F_QUERY; | |
936 | } | |
937 | ||
938 | ||
cdeda28f | 939 | size_t setup_reply(HEADER *header, size_t qlen, |
9e4abcb5 SK |
940 | struct all_addr *addrp, unsigned short flags, unsigned long ttl) |
941 | { | |
942 | unsigned char *p = skip_questions(header, qlen); | |
943 | ||
944 | header->qr = 1; /* response */ | |
945 | header->aa = 0; /* authoritive */ | |
946 | header->ra = 1; /* recursion if available */ | |
947 | header->tc = 0; /* not truncated */ | |
948 | header->nscount = htons(0); | |
949 | header->arcount = htons(0); | |
feba5c1d | 950 | header->ancount = htons(0); /* no answers unless changed below */ |
9e4abcb5 SK |
951 | if (flags == F_NEG) |
952 | header->rcode = SERVFAIL; /* couldn't get memory */ | |
824af85b | 953 | else if (flags == F_NOERR) |
9e4abcb5 SK |
954 | header->rcode = NOERROR; /* empty domain */ |
955 | else if (flags == F_NXDOMAIN) | |
956 | header->rcode = NXDOMAIN; | |
957 | else if (p && flags == F_IPV4) | |
958 | { /* we know the address */ | |
959 | header->rcode = NOERROR; | |
960 | header->ancount = htons(1); | |
961 | header->aa = 1; | |
f6b7dc47 | 962 | add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp); |
9e4abcb5 SK |
963 | } |
964 | #ifdef HAVE_IPV6 | |
965 | else if (p && flags == F_IPV6) | |
966 | { | |
967 | header->rcode = NOERROR; | |
968 | header->ancount = htons(1); | |
969 | header->aa = 1; | |
f6b7dc47 | 970 | add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp); |
9e4abcb5 SK |
971 | } |
972 | #endif | |
973 | else /* nowhere to forward to */ | |
974 | header->rcode = REFUSED; | |
975 | ||
976 | return p - (unsigned char *)header; | |
977 | } | |
36717eee SK |
978 | |
979 | /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ | |
5aabfc78 | 980 | int check_for_local_domain(char *name, time_t now) |
36717eee SK |
981 | { |
982 | struct crec *crecp; | |
0a852541 SK |
983 | struct mx_srv_record *mx; |
984 | struct txt_record *txt; | |
f2621c7f SK |
985 | struct interface_name *intr; |
986 | struct ptr_record *ptr; | |
987 | ||
26128d27 | 988 | if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) && |
36717eee SK |
989 | (crecp->flags & (F_HOSTS | F_DHCP))) |
990 | return 1; | |
991 | ||
f6b7dc47 | 992 | for (mx = daemon->mxnames; mx; mx = mx->next) |
0a852541 | 993 | if (hostname_isequal(name, mx->name)) |
36717eee | 994 | return 1; |
f6b7dc47 | 995 | |
0a852541 SK |
996 | for (txt = daemon->txt; txt; txt = txt->next) |
997 | if (hostname_isequal(name, txt->name)) | |
f6b7dc47 | 998 | return 1; |
f2621c7f SK |
999 | |
1000 | for (intr = daemon->int_names; intr; intr = intr->next) | |
1001 | if (hostname_isequal(name, intr->name)) | |
1002 | return 1; | |
1003 | ||
1004 | for (ptr = daemon->ptr; ptr; ptr = ptr->next) | |
1005 | if (hostname_isequal(name, ptr->name)) | |
1006 | return 1; | |
1007 | ||
36717eee SK |
1008 | return 0; |
1009 | } | |
9e4abcb5 SK |
1010 | |
1011 | /* Is the packet a reply with the answer address equal to addr? | |
1012 | If so mung is into an NXDOMAIN reply and also put that information | |
1013 | in the cache. */ | |
cdeda28f | 1014 | int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name, |
9e4abcb5 SK |
1015 | struct bogus_addr *baddr, time_t now) |
1016 | { | |
1017 | unsigned char *p; | |
1018 | int i, qtype, qclass, rdlen; | |
1019 | unsigned long ttl; | |
1020 | struct bogus_addr *baddrp; | |
1021 | ||
1022 | /* skip over questions */ | |
1023 | if (!(p = skip_questions(header, qlen))) | |
1024 | return 0; /* bad packet */ | |
1025 | ||
5aabfc78 | 1026 | for (i = ntohs(header->ancount); i != 0; i--) |
9e4abcb5 | 1027 | { |
9009d746 | 1028 | if (!extract_name(header, qlen, &p, name, 1, 10)) |
9e4abcb5 SK |
1029 | return 0; /* bad packet */ |
1030 | ||
1031 | GETSHORT(qtype, p); | |
1032 | GETSHORT(qclass, p); | |
1033 | GETLONG(ttl, p); | |
1034 | GETSHORT(rdlen, p); | |
1035 | ||
1036 | if (qclass == C_IN && qtype == T_A) | |
9009d746 SK |
1037 | { |
1038 | if (!CHECK_LEN(header, p, qlen, INADDRSZ)) | |
1039 | return 0; | |
1040 | ||
1041 | for (baddrp = baddr; baddrp; baddrp = baddrp->next) | |
1042 | if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) | |
1043 | { | |
1044 | /* Found a bogus address. Insert that info here, since there no SOA record | |
1045 | to get the ttl from in the normal processing */ | |
1046 | cache_start_insert(); | |
1047 | cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG); | |
1048 | cache_end_insert(); | |
1049 | ||
1050 | return 1; | |
1051 | } | |
1052 | } | |
9e4abcb5 | 1053 | |
9009d746 SK |
1054 | if (!ADD_RDLEN(header, p, qlen, rdlen)) |
1055 | return 0; | |
9e4abcb5 SK |
1056 | } |
1057 | ||
1058 | return 0; | |
1059 | } | |
1060 | ||
f6b7dc47 | 1061 | static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, |
3d8df260 | 1062 | unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...) |
f6b7dc47 SK |
1063 | { |
1064 | va_list ap; | |
1065 | unsigned char *sav, *p = *pp; | |
1066 | int j; | |
1067 | unsigned short usval; | |
1068 | long lval; | |
1069 | char *sval; | |
1070 | ||
1071 | if (truncp && *truncp) | |
1072 | return 0; | |
1073 | ||
1074 | PUTSHORT(nameoffset | 0xc000, p); | |
1075 | PUTSHORT(type, p); | |
1076 | PUTSHORT(class, p); | |
1077 | PUTLONG(ttl, p); /* TTL */ | |
1078 | ||
1079 | sav = p; /* Save pointer to RDLength field */ | |
1080 | PUTSHORT(0, p); /* Placeholder RDLength */ | |
1081 | ||
1082 | va_start(ap, format); /* make ap point to 1st unamed argument */ | |
1083 | ||
1084 | for (; *format; format++) | |
1085 | switch (*format) | |
1086 | { | |
1087 | #ifdef HAVE_IPV6 | |
1088 | case '6': | |
1089 | sval = va_arg(ap, char *); | |
1090 | memcpy(p, sval, IN6ADDRSZ); | |
1091 | p += IN6ADDRSZ; | |
1092 | break; | |
1093 | #endif | |
1094 | ||
1095 | case '4': | |
1096 | sval = va_arg(ap, char *); | |
1097 | memcpy(p, sval, INADDRSZ); | |
1098 | p += INADDRSZ; | |
1099 | break; | |
1100 | ||
1101 | case 's': | |
1102 | usval = va_arg(ap, int); | |
1103 | PUTSHORT(usval, p); | |
1104 | break; | |
1105 | ||
1106 | case 'l': | |
1107 | lval = va_arg(ap, long); | |
1108 | PUTLONG(lval, p); | |
1109 | break; | |
1110 | ||
1111 | case 'd': | |
1112 | /* get domain-name answer arg and store it in RDATA field */ | |
0a852541 SK |
1113 | if (offset) |
1114 | *offset = p - (unsigned char *)header; | |
3d8df260 | 1115 | p = do_rfc1035_name(p, va_arg(ap, char *)); |
f6b7dc47 SK |
1116 | *p++ = 0; |
1117 | break; | |
3d8df260 | 1118 | |
f6b7dc47 | 1119 | case 't': |
0a852541 | 1120 | usval = va_arg(ap, int); |
f6b7dc47 | 1121 | sval = va_arg(ap, char *); |
0a852541 SK |
1122 | memcpy(p, sval, usval); |
1123 | p += usval; | |
f6b7dc47 | 1124 | break; |
1a6bca81 SK |
1125 | |
1126 | case 'z': | |
1127 | sval = va_arg(ap, char *); | |
1128 | usval = sval ? strlen(sval) : 0; | |
1129 | if (usval > 255) | |
1130 | usval = 255; | |
1131 | *p++ = (unsigned char)usval; | |
1132 | memcpy(p, sval, usval); | |
1133 | p += usval; | |
1134 | break; | |
f6b7dc47 SK |
1135 | } |
1136 | ||
1137 | va_end(ap); /* clean up variable argument pointer */ | |
1138 | ||
1139 | j = p - sav - 2; | |
1140 | PUTSHORT(j, sav); /* Now, store real RDLength */ | |
1141 | ||
f6b7dc47 SK |
1142 | /* check for overflow of buffer */ |
1143 | if (limit && ((unsigned char *)limit - p) < 0) | |
1144 | { | |
1145 | if (truncp) | |
1146 | *truncp = 1; | |
1147 | return 0; | |
1148 | } | |
1149 | ||
1150 | *pp = p; | |
1151 | return 1; | |
1152 | } | |
1153 | ||
9009d746 SK |
1154 | static unsigned long crec_ttl(struct crec *crecp, time_t now) |
1155 | { | |
1156 | /* Return 0 ttl for DHCP entries, which might change | |
1157 | before the lease expires. */ | |
1158 | ||
1159 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
1160 | return daemon->local_ttl; | |
1161 | ||
8ef5ada2 SK |
1162 | /* Return the Max TTL value if it is lower then the actual TTL */ |
1163 | if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl)) | |
1164 | return crecp->ttd - now; | |
1165 | else | |
1166 | return daemon->max_ttl; | |
9009d746 SK |
1167 | } |
1168 | ||
1169 | ||
9e4abcb5 | 1170 | /* return zero if we can't answer from cache, or packet size if we can */ |
5aabfc78 | 1171 | size_t answer_request(HEADER *header, char *limit, size_t qlen, |
cdeda28f | 1172 | struct in_addr local_addr, struct in_addr local_netmask, time_t now) |
9e4abcb5 | 1173 | { |
3be34541 | 1174 | char *name = daemon->namebuff; |
feba5c1d | 1175 | unsigned char *p, *ansp, *pheader; |
832af0ba | 1176 | int qtype, qclass; |
9e4abcb5 SK |
1177 | struct all_addr addr; |
1178 | unsigned int nameoffset; | |
feba5c1d | 1179 | unsigned short flag; |
0a852541 | 1180 | int q, ans, anscount = 0, addncount = 0; |
feba5c1d | 1181 | int dryrun = 0, sec_reqd = 0; |
832af0ba | 1182 | int is_sign; |
9e4abcb5 | 1183 | struct crec *crecp; |
f6b7dc47 | 1184 | int nxdomain = 0, auth = 1, trunc = 0; |
0a852541 SK |
1185 | struct mx_srv_record *rec; |
1186 | ||
feba5c1d SK |
1187 | /* If there is an RFC2671 pseudoheader then it will be overwritten by |
1188 | partial replies, so we have to do a dry run to see if we can answer | |
1189 | the query. We check to see if the do bit is set, if so we always | |
1190 | forward rather than answering from the cache, which doesn't include | |
1191 | security information. */ | |
1192 | ||
832af0ba | 1193 | if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) |
feba5c1d SK |
1194 | { |
1195 | unsigned short udpsz, ext_rcode, flags; | |
1196 | unsigned char *psave = pheader; | |
1197 | ||
1198 | GETSHORT(udpsz, pheader); | |
1199 | GETSHORT(ext_rcode, pheader); | |
1200 | GETSHORT(flags, pheader); | |
1201 | ||
1202 | sec_reqd = flags & 0x8000; /* do bit */ | |
1203 | ||
1204 | /* If our client is advertising a larger UDP packet size | |
1205 | than we allow, trim it so that we don't get an overlarge | |
1206 | response from upstream */ | |
1207 | ||
832af0ba | 1208 | if (!is_sign && (udpsz > daemon->edns_pktsz)) |
3be34541 | 1209 | PUTSHORT(daemon->edns_pktsz, psave); |
feba5c1d SK |
1210 | |
1211 | dryrun = 1; | |
1212 | } | |
1213 | ||
5aabfc78 | 1214 | if (ntohs(header->qdcount) == 0 || header->opcode != QUERY ) |
832af0ba SK |
1215 | return 0; |
1216 | ||
0a852541 SK |
1217 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1218 | rec->offset = 0; | |
1219 | ||
feba5c1d | 1220 | rerun: |
9e4abcb5 SK |
1221 | /* determine end of question section (we put answers there) */ |
1222 | if (!(ansp = skip_questions(header, qlen))) | |
1223 | return 0; /* bad packet */ | |
1224 | ||
1225 | /* now process each question, answers go in RRs after the question */ | |
1226 | p = (unsigned char *)(header+1); | |
feba5c1d | 1227 | |
5aabfc78 | 1228 | for (q = ntohs(header->qdcount); q != 0; q--) |
9e4abcb5 SK |
1229 | { |
1230 | /* save pointer to name for copying into answers */ | |
1231 | nameoffset = p - (unsigned char *)header; | |
1232 | ||
1233 | /* now extract name as .-concatenated string into name */ | |
9009d746 | 1234 | if (!extract_name(header, qlen, &p, name, 1, 4)) |
9e4abcb5 | 1235 | return 0; /* bad packet */ |
832af0ba | 1236 | |
9e4abcb5 SK |
1237 | GETSHORT(qtype, p); |
1238 | GETSHORT(qclass, p); | |
1239 | ||
1240 | ans = 0; /* have we answered this question */ | |
1241 | ||
0a852541 | 1242 | if (qtype == T_TXT || qtype == T_ANY) |
9e4abcb5 | 1243 | { |
0a852541 SK |
1244 | struct txt_record *t; |
1245 | for(t = daemon->txt; t ; t = t->next) | |
9e4abcb5 | 1246 | { |
0a852541 SK |
1247 | if (t->class == qclass && hostname_isequal(name, t->name)) |
1248 | { | |
1249 | ans = 1; | |
e17fb629 SK |
1250 | if (!dryrun) |
1251 | { | |
1a6bca81 | 1252 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>"); |
e17fb629 SK |
1253 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1254 | daemon->local_ttl, NULL, | |
1255 | T_TXT, t->class, "t", t->len, t->txt)) | |
1256 | anscount++; | |
1257 | ||
1258 | } | |
0a852541 | 1259 | } |
9e4abcb5 | 1260 | } |
0a852541 | 1261 | } |
f6b7dc47 | 1262 | |
0a852541 | 1263 | if (qclass == C_IN) |
9e4abcb5 | 1264 | { |
f6b7dc47 | 1265 | if (qtype == T_PTR || qtype == T_ANY) |
c1bb8504 | 1266 | { |
832af0ba SK |
1267 | /* see if it's w.z.y.z.in-addr.arpa format */ |
1268 | int is_arpa = in_arpa_name_2_addr(name, &addr); | |
1269 | struct ptr_record *ptr; | |
f2621c7f | 1270 | struct interface_name* intr = NULL; |
832af0ba SK |
1271 | |
1272 | for (ptr = daemon->ptr; ptr; ptr = ptr->next) | |
1273 | if (hostname_isequal(name, ptr->name)) | |
1274 | break; | |
1275 | ||
f2621c7f SK |
1276 | if (is_arpa == F_IPV4) |
1277 | for (intr = daemon->int_names; intr; intr = intr->next) | |
5aabfc78 SK |
1278 | { |
1279 | if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr) | |
1280 | break; | |
1281 | else | |
1282 | while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) | |
1283 | intr = intr->next; | |
1284 | } | |
f2621c7f SK |
1285 | |
1286 | if (intr) | |
1287 | { | |
1288 | ans = 1; | |
1289 | if (!dryrun) | |
f6b7dc47 | 1290 | { |
1a6bca81 | 1291 | log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL); |
f2621c7f SK |
1292 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1293 | daemon->local_ttl, NULL, | |
1294 | T_PTR, C_IN, "d", intr->name)) | |
1295 | anscount++; | |
9e4abcb5 | 1296 | } |
9e4abcb5 | 1297 | } |
832af0ba SK |
1298 | else if (ptr) |
1299 | { | |
1300 | ans = 1; | |
1301 | if (!dryrun) | |
1302 | { | |
1a6bca81 | 1303 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>"); |
832af0ba | 1304 | for (ptr = daemon->ptr; ptr; ptr = ptr->next) |
f2621c7f SK |
1305 | if (hostname_isequal(name, ptr->name) && |
1306 | add_resource_record(header, limit, &trunc, nameoffset, &ansp, | |
1307 | daemon->local_ttl, NULL, | |
1308 | T_PTR, C_IN, "d", ptr->ptr)) | |
1309 | anscount++; | |
1310 | ||
1311 | } | |
1312 | } | |
1313 | else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) | |
1314 | do | |
1315 | { | |
1316 | /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ | |
1317 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1318 | continue; | |
1319 | ||
1320 | if (crecp->flags & F_NEG) | |
1321 | { | |
1322 | ans = 1; | |
1323 | auth = 0; | |
1324 | if (crecp->flags & F_NXDOMAIN) | |
1325 | nxdomain = 1; | |
1326 | if (!dryrun) | |
1a6bca81 | 1327 | log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); |
f2621c7f SK |
1328 | } |
1329 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) | |
1330 | { | |
1331 | ans = 1; | |
1332 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1333 | auth = 0; | |
1334 | if (!dryrun) | |
832af0ba | 1335 | { |
1a6bca81 | 1336 | log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, |
7622fc06 | 1337 | record_source(crecp->uid)); |
f2621c7f | 1338 | |
9009d746 SK |
1339 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1340 | crec_ttl(crecp, now), NULL, | |
f2621c7f | 1341 | T_PTR, C_IN, "d", cache_get_name(crecp))) |
832af0ba SK |
1342 | anscount++; |
1343 | } | |
f2621c7f SK |
1344 | } |
1345 | } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); | |
1346 | else if (is_arpa == F_IPV4 && | |
1347 | (daemon->options & OPT_BOGUSPRIV) && | |
8ef5ada2 | 1348 | private_net(addr.addr.addr4, 1)) |
f2621c7f SK |
1349 | { |
1350 | /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */ | |
1351 | ans = 1; | |
1352 | nxdomain = 1; | |
1353 | if (!dryrun) | |
1354 | log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, | |
1a6bca81 | 1355 | name, &addr, NULL); |
832af0ba | 1356 | } |
f6b7dc47 | 1357 | } |
f2621c7f | 1358 | |
f6b7dc47 SK |
1359 | for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) |
1360 | { | |
1361 | unsigned short type = T_A; | |
f2621c7f | 1362 | |
f6b7dc47 | 1363 | if (flag == F_IPV6) |
feba5c1d | 1364 | #ifdef HAVE_IPV6 |
3d8df260 | 1365 | type = T_AAAA; |
feba5c1d | 1366 | #else |
3d8df260 | 1367 | break; |
feba5c1d | 1368 | #endif |
f6b7dc47 SK |
1369 | |
1370 | if (qtype != type && qtype != T_ANY) | |
1371 | continue; | |
1372 | ||
316e2730 SK |
1373 | /* Check for "A for A" queries; be rather conservative |
1374 | about what looks like dotted-quad. */ | |
1375 | if (qtype == T_A) | |
3d8df260 | 1376 | { |
316e2730 SK |
1377 | char *cp; |
1378 | unsigned int i, a; | |
1379 | int x; | |
1380 | ||
1381 | for (cp = name, i = 0, a = 0; *cp; i++) | |
3d8df260 | 1382 | { |
316e2730 SK |
1383 | if (!isdigit(*cp) || (x = strtol(cp, &cp, 10)) > 255) |
1384 | { | |
1385 | i = 5; | |
1386 | break; | |
1387 | } | |
1388 | ||
1389 | a = (a << 8) + x; | |
1390 | ||
1391 | if (*cp == '.') | |
1392 | cp++; | |
1393 | } | |
1394 | ||
1395 | if (i == 4) | |
1396 | { | |
1397 | ans = 1; | |
1398 | if (!dryrun) | |
1399 | { | |
1400 | addr.addr.addr4.s_addr = htonl(a); | |
1401 | log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL); | |
1402 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, | |
1403 | daemon->local_ttl, NULL, type, C_IN, "4", &addr)) | |
1404 | anscount++; | |
1405 | } | |
1406 | continue; | |
3d8df260 | 1407 | } |
3d8df260 SK |
1408 | } |
1409 | ||
f2621c7f SK |
1410 | /* interface name stuff */ |
1411 | if (qtype == T_A) | |
1412 | { | |
1413 | struct interface_name *intr; | |
1414 | ||
1415 | for (intr = daemon->int_names; intr; intr = intr->next) | |
1416 | if (hostname_isequal(name, intr->name)) | |
1417 | break; | |
1418 | ||
1419 | if (intr) | |
1420 | { | |
1421 | ans = 1; | |
1422 | if (!dryrun) | |
1423 | { | |
5aabfc78 | 1424 | if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1) |
1a6bca81 | 1425 | log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL); |
f2621c7f SK |
1426 | else |
1427 | { | |
1a6bca81 | 1428 | log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL); |
f2621c7f SK |
1429 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1430 | daemon->local_ttl, NULL, type, C_IN, "4", &addr)) | |
1431 | anscount++; | |
1432 | } | |
1433 | } | |
1434 | continue; | |
1435 | } | |
1436 | } | |
1437 | ||
f6b7dc47 SK |
1438 | cname_restart: |
1439 | if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME))) | |
1440 | { | |
1441 | int localise = 0; | |
feba5c1d | 1442 | |
f6b7dc47 SK |
1443 | /* See if a putative address is on the network from which we recieved |
1444 | the query, is so we'll filter other answers. */ | |
1445 | if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4) | |
1446 | { | |
1447 | struct crec *save = crecp; | |
1448 | do { | |
1449 | if ((crecp->flags & F_HOSTS) && | |
1450 | is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1451 | { | |
1452 | localise = 1; | |
1453 | break; | |
1454 | } | |
1455 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); | |
1456 | crecp = save; | |
1457 | } | |
1458 | ||
1459 | do | |
9e4abcb5 | 1460 | { |
26128d27 SK |
1461 | /* don't answer wildcard queries with data not from /etc/hosts |
1462 | or DHCP leases */ | |
1463 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1464 | break; | |
1465 | ||
fd9fa481 SK |
1466 | if (crecp->flags & F_CNAME) |
1467 | { | |
fd9fa481 SK |
1468 | if (!dryrun) |
1469 | { | |
7622fc06 | 1470 | log_query(crecp->flags, name, NULL, record_source(crecp->uid)); |
9009d746 SK |
1471 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1472 | crec_ttl(crecp, now), &nameoffset, | |
f6b7dc47 SK |
1473 | T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache))) |
1474 | anscount++; | |
fd9fa481 | 1475 | } |
f6b7dc47 | 1476 | |
fd9fa481 SK |
1477 | strcpy(name, cache_get_name(crecp->addr.cname.cache)); |
1478 | goto cname_restart; | |
1479 | } | |
f6b7dc47 | 1480 | |
9e4abcb5 SK |
1481 | if (crecp->flags & F_NEG) |
1482 | { | |
feba5c1d | 1483 | ans = 1; |
f6b7dc47 SK |
1484 | auth = 0; |
1485 | if (crecp->flags & F_NXDOMAIN) | |
1486 | nxdomain = 1; | |
feba5c1d | 1487 | if (!dryrun) |
1a6bca81 | 1488 | log_query(crecp->flags, name, NULL, NULL); |
9e4abcb5 | 1489 | } |
feba5c1d | 1490 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) |
9e4abcb5 | 1491 | { |
f6b7dc47 SK |
1492 | /* If we are returning local answers depending on network, |
1493 | filter here. */ | |
1494 | if (localise && | |
1495 | (crecp->flags & F_HOSTS) && | |
1496 | !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1497 | continue; | |
1498 | ||
1499 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1500 | auth = 0; | |
1501 | ||
feba5c1d SK |
1502 | ans = 1; |
1503 | if (!dryrun) | |
1504 | { | |
fd9fa481 | 1505 | log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr, |
7622fc06 | 1506 | record_source(crecp->uid)); |
feba5c1d | 1507 | |
9009d746 SK |
1508 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, |
1509 | crec_ttl(crecp, now), NULL, type, C_IN, | |
f6b7dc47 SK |
1510 | type == T_A ? "4" : "6", &crecp->addr)) |
1511 | anscount++; | |
feba5c1d | 1512 | } |
9e4abcb5 | 1513 | } |
f6b7dc47 | 1514 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); |
9e4abcb5 | 1515 | } |
f6b7dc47 SK |
1516 | } |
1517 | ||
1518 | if (qtype == T_MX || qtype == T_ANY) | |
1519 | { | |
1520 | int found = 0; | |
0a852541 SK |
1521 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1522 | if (!rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1523 | { |
1524 | ans = found = 1; | |
1525 | if (!dryrun) | |
feba5c1d | 1526 | { |
3d8df260 | 1527 | unsigned int offset; |
1a6bca81 | 1528 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>"); |
0a852541 SK |
1529 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
1530 | &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) | |
1531 | { | |
1532 | anscount++; | |
1533 | if (rec->target) | |
1534 | rec->offset = offset; | |
1535 | } | |
feba5c1d | 1536 | } |
f6b7dc47 SK |
1537 | } |
1538 | ||
1539 | if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) && | |
1540 | cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP)) | |
1541 | { | |
1542 | ans = 1; | |
1543 | if (!dryrun) | |
1544 | { | |
1a6bca81 | 1545 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>"); |
f6b7dc47 SK |
1546 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, |
1547 | T_MX, C_IN, "sd", 1, | |
1548 | (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget)) | |
1549 | anscount++; | |
9e4abcb5 SK |
1550 | } |
1551 | } | |
f6b7dc47 SK |
1552 | } |
1553 | ||
1554 | if (qtype == T_SRV || qtype == T_ANY) | |
1555 | { | |
1556 | int found = 0; | |
9e4abcb5 | 1557 | |
0a852541 SK |
1558 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1559 | if (rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1560 | { |
1561 | found = ans = 1; | |
1562 | if (!dryrun) | |
1563 | { | |
3d8df260 | 1564 | unsigned int offset; |
1a6bca81 | 1565 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>"); |
f6b7dc47 | 1566 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
0a852541 SK |
1567 | &offset, T_SRV, C_IN, "sssd", |
1568 | rec->priority, rec->weight, rec->srvport, rec->target)) | |
1569 | { | |
1570 | anscount++; | |
1571 | if (rec->target) | |
1572 | rec->offset = offset; | |
1573 | } | |
f6b7dc47 SK |
1574 | } |
1575 | } | |
feba5c1d | 1576 | |
f6b7dc47 SK |
1577 | if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) |
1578 | { | |
1579 | ans = 1; | |
1580 | if (!dryrun) | |
1a6bca81 | 1581 | log_query(F_CONFIG | F_NEG, name, NULL, NULL); |
f6b7dc47 SK |
1582 | } |
1583 | } | |
1a6bca81 SK |
1584 | |
1585 | if (qtype == T_NAPTR || qtype == T_ANY) | |
1586 | { | |
1587 | struct naptr *na; | |
1588 | for (na = daemon->naptr; na; na = na->next) | |
1589 | if (hostname_isequal(name, na->name)) | |
1590 | { | |
1591 | ans = 1; | |
1592 | if (!dryrun) | |
1593 | { | |
1594 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>"); | |
1595 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, | |
1596 | NULL, T_NAPTR, C_IN, "sszzzd", | |
1597 | na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) | |
1598 | anscount++; | |
1599 | } | |
1600 | } | |
1601 | } | |
f6b7dc47 SK |
1602 | |
1603 | if (qtype == T_MAILB) | |
1604 | ans = 1, nxdomain = 1; | |
1605 | ||
1606 | if (qtype == T_SOA && (daemon->options & OPT_FILTER)) | |
1607 | { | |
1608 | ans = 1; | |
1609 | if (!dryrun) | |
1a6bca81 | 1610 | log_query(F_CONFIG | F_NEG, name, &addr, NULL); |
feba5c1d | 1611 | } |
9e4abcb5 | 1612 | } |
f6b7dc47 SK |
1613 | |
1614 | if (!ans) | |
9e4abcb5 | 1615 | return 0; /* failed to answer a question */ |
feba5c1d | 1616 | } |
f6b7dc47 | 1617 | |
feba5c1d SK |
1618 | if (dryrun) |
1619 | { | |
1620 | dryrun = 0; | |
1621 | goto rerun; | |
9e4abcb5 SK |
1622 | } |
1623 | ||
0a852541 SK |
1624 | /* create an additional data section, for stuff in SRV and MX record replies. */ |
1625 | for (rec = daemon->mxnames; rec; rec = rec->next) | |
1626 | if (rec->offset != 0) | |
1627 | { | |
1628 | /* squash dupes */ | |
1629 | struct mx_srv_record *tmp; | |
1630 | for (tmp = rec->next; tmp; tmp = tmp->next) | |
1631 | if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target)) | |
1632 | tmp->offset = 0; | |
1633 | ||
1634 | crecp = NULL; | |
1635 | while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6))) | |
1636 | { | |
0a852541 SK |
1637 | #ifdef HAVE_IPV6 |
1638 | int type = crecp->flags & F_IPV4 ? T_A : T_AAAA; | |
1639 | #else | |
1640 | int type = T_A; | |
1641 | #endif | |
1642 | if (crecp->flags & F_NEG) | |
1643 | continue; | |
1644 | ||
9009d746 SK |
1645 | if (add_resource_record(header, limit, NULL, rec->offset, &ansp, |
1646 | crec_ttl(crecp, now), NULL, type, C_IN, | |
0a852541 SK |
1647 | crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) |
1648 | addncount++; | |
1649 | } | |
1650 | } | |
1651 | ||
9e4abcb5 SK |
1652 | /* done all questions, set up header and return length of result */ |
1653 | header->qr = 1; /* response */ | |
1654 | header->aa = auth; /* authoritive - only hosts and DHCP derived names. */ | |
1655 | header->ra = 1; /* recursion if available */ | |
f6b7dc47 | 1656 | header->tc = trunc; /* truncation */ |
9e4abcb5 SK |
1657 | if (anscount == 0 && nxdomain) |
1658 | header->rcode = NXDOMAIN; | |
1659 | else | |
1660 | header->rcode = NOERROR; /* no error */ | |
1661 | header->ancount = htons(anscount); | |
1662 | header->nscount = htons(0); | |
0a852541 | 1663 | header->arcount = htons(addncount); |
9e4abcb5 SK |
1664 | return ansp - (unsigned char *)header; |
1665 | } | |
1666 | ||
1667 | ||
1668 | ||
1669 | ||
1670 |