]>
Commit | Line | Data |
---|---|---|
0a852541 | 1 | /* dnsmasq is Copyright (c) 2000 - 2005 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 | |
5 | the Free Software Foundation; version 2 dated June, 1991. | |
6 | ||
7 | This program is distributed in the hope that it will be useful, | |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | GNU General Public License for more details. | |
11 | */ | |
12 | ||
13 | #include "dnsmasq.h" | |
14 | ||
f6b7dc47 SK |
15 | static int add_resource_record(HEADER *header, char *limit, int *truncp, |
16 | unsigned int nameoffset, unsigned char **pp, | |
17 | unsigned long ttl, int *offset, unsigned short type, | |
18 | unsigned short class, char *format, ...); | |
19 | ||
9e4abcb5 SK |
20 | static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp, |
21 | unsigned char *name, int isExtract) | |
22 | { | |
23 | unsigned char *cp = name, *p = *pp, *p1 = NULL; | |
24 | unsigned int j, l, hops = 0; | |
25 | int retvalue = 1; | |
26 | ||
f6b7dc47 SK |
27 | if (isExtract) |
28 | *cp = 0; | |
29 | ||
9e4abcb5 SK |
30 | while ((l = *p++)) |
31 | { | |
32 | unsigned int label_type = l & 0xc0; | |
33 | if (label_type == 0xc0) /* pointer */ | |
34 | { | |
35 | if (p - (unsigned char *)header + 1u >= plen) | |
36 | return 0; | |
37 | ||
38 | /* get offset */ | |
39 | l = (l&0x3f) << 8; | |
40 | l |= *p++; | |
41 | if (l >= (unsigned int)plen) | |
42 | return 0; | |
43 | ||
44 | if (!p1) /* first jump, save location to go back to */ | |
45 | p1 = p; | |
46 | ||
47 | hops++; /* break malicious infinite loops */ | |
48 | if (hops > 255) | |
49 | return 0; | |
50 | ||
51 | p = l + (unsigned char *)header; | |
52 | } | |
53 | else if (label_type == 0x80) | |
54 | return 0; /* reserved */ | |
55 | else if (label_type == 0x40) | |
56 | { /* ELT */ | |
57 | unsigned int count, digs; | |
58 | ||
59 | if ((l & 0x3f) != 1) | |
60 | return 0; /* we only understand bitstrings */ | |
61 | ||
62 | if (!isExtract) | |
63 | return 0; /* Cannot compare bitsrings */ | |
64 | ||
65 | count = *p++; | |
66 | if (count == 0) | |
67 | count = 256; | |
68 | digs = ((count-1)>>2)+1; | |
69 | ||
70 | /* output is \[x<hex>/siz]. which is digs+9 chars */ | |
71 | if (cp - name + digs + 9 >= MAXDNAME) | |
72 | return 0; | |
73 | if (p - (unsigned char *)header + ((count-1)>>3) + 1u >= plen) | |
74 | return 0; | |
75 | ||
76 | *cp++ = '\\'; | |
77 | *cp++ = '['; | |
78 | *cp++ = 'x'; | |
79 | for (j=0; j<digs; j++) | |
80 | { | |
81 | unsigned int dig; | |
82 | if (j%2 == 0) | |
83 | dig = *p >> 4; | |
84 | else | |
85 | dig = *p++ & 0x0f; | |
86 | ||
87 | *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10; | |
88 | } | |
89 | cp += sprintf(cp, "/%d]", count); | |
90 | /* do this here to overwrite the zero char from sprintf */ | |
91 | *cp++ = '.'; | |
92 | } | |
93 | else | |
94 | { /* label_type = 0 -> label. */ | |
95 | if (cp - name + l + 1 >= MAXDNAME) | |
96 | return 0; | |
97 | if (p - (unsigned char *)header + 1u >= plen) | |
98 | return 0; | |
99 | for(j=0; j<l; j++, p++) | |
100 | if (isExtract) | |
101 | { | |
102 | if (legal_char(*p)) | |
103 | *cp++ = *p; | |
104 | else | |
105 | return 0; | |
106 | } | |
107 | else | |
108 | { | |
109 | unsigned char c1 = *cp, c2 = *p; | |
110 | ||
111 | if (c1 == 0) | |
112 | retvalue = 2; | |
113 | else | |
114 | { | |
115 | cp++; | |
116 | if (c1 >= 'A' && c1 <= 'Z') | |
117 | c1 += 'a' - 'A'; | |
118 | if (c2 >= 'A' && c2 <= 'Z') | |
119 | c2 += 'a' - 'A'; | |
120 | ||
121 | if (c1 != c2) | |
122 | retvalue = 2; | |
123 | } | |
124 | } | |
125 | ||
126 | if (isExtract) | |
127 | *cp++ = '.'; | |
f6b7dc47 SK |
128 | else if (*cp != 0 && *cp++ != '.') |
129 | retvalue = 2; | |
9e4abcb5 SK |
130 | } |
131 | ||
132 | if ((unsigned int)(p - (unsigned char *)header) >= plen) | |
133 | return 0; | |
134 | } | |
135 | ||
136 | if (isExtract) | |
137 | *--cp = 0; /* terminate: lose final period */ | |
f6b7dc47 SK |
138 | else if (*cp != 0) |
139 | retvalue = 2; | |
140 | ||
9e4abcb5 SK |
141 | if (p1) /* we jumped via compression */ |
142 | *pp = p1; | |
143 | else | |
144 | *pp = p; | |
145 | ||
146 | return retvalue; | |
147 | } | |
148 | ||
149 | /* Max size of input string (for IPv6) is 75 chars.) */ | |
150 | #define MAXARPANAME 75 | |
151 | static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp) | |
152 | { | |
153 | int j; | |
154 | char name[MAXARPANAME+1], *cp1; | |
155 | unsigned char *addr = (unsigned char *)addrp; | |
156 | char *lastchunk = NULL, *penchunk = NULL; | |
157 | ||
158 | if (strlen(namein) > MAXARPANAME) | |
159 | return 0; | |
160 | ||
161 | memset(addrp, 0, sizeof(struct all_addr)); | |
162 | ||
163 | /* turn name into a series of asciiz strings */ | |
164 | /* j counts no of labels */ | |
165 | for(j = 1,cp1 = name; *namein; cp1++, namein++) | |
166 | if (*namein == '.') | |
167 | { | |
168 | penchunk = lastchunk; | |
169 | lastchunk = cp1 + 1; | |
170 | *cp1 = 0; | |
171 | j++; | |
172 | } | |
173 | else | |
174 | *cp1 = *namein; | |
175 | ||
176 | *cp1 = 0; | |
177 | ||
178 | if (j<3) | |
179 | return 0; | |
180 | ||
181 | if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr")) | |
182 | { | |
183 | /* IP v4 */ | |
184 | /* address arives as a name of the form | |
185 | www.xxx.yyy.zzz.in-addr.arpa | |
186 | some of the low order address octets might be missing | |
187 | and should be set to zero. */ | |
188 | for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1) | |
189 | { | |
190 | /* check for digits only (weeds out things like | |
191 | 50.0/24.67.28.64.in-addr.arpa which are used | |
192 | as CNAME targets according to RFC 2317 */ | |
193 | char *cp; | |
194 | for (cp = cp1; *cp; cp++) | |
195 | if (!isdigit((int)*cp)) | |
196 | return 0; | |
197 | ||
198 | addr[3] = addr[2]; | |
199 | addr[2] = addr[1]; | |
200 | addr[1] = addr[0]; | |
201 | addr[0] = atoi(cp1); | |
202 | } | |
203 | ||
204 | return F_IPV4; | |
205 | } | |
206 | #ifdef HAVE_IPV6 | |
207 | else if (hostname_isequal(penchunk, "ip6") && | |
208 | (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa"))) | |
209 | { | |
210 | /* IP v6: | |
211 | Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa] | |
212 | or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa] | |
213 | ||
214 | Note that most of these the various reprentations are obsolete and | |
215 | left-over from the many DNS-for-IPv6 wars. We support all the formats | |
216 | that we can since there is no reason not to. | |
217 | */ | |
218 | ||
219 | if (*name == '\\' && *(name+1) == '[' && | |
220 | (*(name+2) == 'x' || *(name+2) == 'X')) | |
221 | { | |
222 | for (j = 0, cp1 = name+3; *cp1 && isxdigit(*cp1) && j < 32; cp1++, j++) | |
223 | { | |
224 | char xdig[2]; | |
225 | xdig[0] = *cp1; | |
226 | xdig[1] = 0; | |
227 | if (j%2) | |
228 | addr[j/2] |= strtol(xdig, NULL, 16); | |
229 | else | |
230 | addr[j/2] = strtol(xdig, NULL, 16) << 4; | |
231 | } | |
232 | ||
233 | if (*cp1 == '/' && j == 32) | |
234 | return F_IPV6; | |
235 | } | |
236 | else | |
237 | { | |
238 | for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1) | |
239 | { | |
240 | if (*(cp1+1) || !isxdigit((int)*cp1)) | |
241 | return 0; | |
242 | ||
243 | for (j = sizeof(struct all_addr)-1; j>0; j--) | |
244 | addr[j] = (addr[j] >> 4) | (addr[j-1] << 4); | |
245 | addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4); | |
246 | } | |
247 | ||
248 | return F_IPV6; | |
249 | } | |
250 | } | |
251 | #endif | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
feba5c1d | 256 | static unsigned char *skip_name(unsigned char *ansp, HEADER *header, unsigned int plen) |
9e4abcb5 | 257 | { |
feba5c1d | 258 | while(1) |
9e4abcb5 | 259 | { |
feba5c1d SK |
260 | unsigned int label_type = (*ansp) & 0xc0; |
261 | ||
262 | if ((unsigned int)(ansp - (unsigned char *)header) >= plen) | |
263 | return NULL; | |
264 | ||
265 | if (label_type == 0xc0) | |
9e4abcb5 | 266 | { |
feba5c1d SK |
267 | /* pointer for compression. */ |
268 | ansp += 2; | |
269 | break; | |
270 | } | |
271 | else if (label_type == 0x80) | |
272 | return NULL; /* reserved */ | |
273 | else if (label_type == 0x40) | |
274 | { | |
275 | /* Extended label type */ | |
276 | unsigned int count; | |
9e4abcb5 | 277 | |
feba5c1d SK |
278 | if (((*ansp++) & 0x3f) != 1) |
279 | return NULL; /* we only understand bitstrings */ | |
9e4abcb5 | 280 | |
feba5c1d SK |
281 | count = *(ansp++); /* Bits in bitstring */ |
282 | ||
283 | if (count == 0) /* count == 0 means 256 bits */ | |
284 | ansp += 32; | |
9e4abcb5 | 285 | else |
feba5c1d SK |
286 | ansp += ((count-1)>>3)+1; |
287 | } | |
288 | else | |
289 | { /* label type == 0 Bottom six bits is length */ | |
290 | unsigned int len = (*ansp++) & 0x3f; | |
291 | if (len == 0) | |
292 | break; /* zero length label marks the end. */ | |
293 | ||
294 | ansp += len; | |
9e4abcb5 | 295 | } |
feba5c1d SK |
296 | } |
297 | ||
298 | return ansp; | |
299 | } | |
300 | ||
301 | static unsigned char *skip_questions(HEADER *header, unsigned int plen) | |
302 | { | |
303 | int q, qdcount = ntohs(header->qdcount); | |
304 | unsigned char *ansp = (unsigned char *)(header+1); | |
305 | ||
306 | for (q = 0; q<qdcount; q++) | |
307 | { | |
308 | if (!(ansp = skip_name(ansp, header, plen))) | |
309 | return NULL; | |
9e4abcb5 SK |
310 | ansp += 4; /* class and type */ |
311 | } | |
312 | if ((unsigned int)(ansp - (unsigned char *)header) > plen) | |
313 | return NULL; | |
314 | ||
315 | return ansp; | |
316 | } | |
317 | ||
fd9fa481 | 318 | static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, unsigned int plen) |
feba5c1d | 319 | { |
fd9fa481 | 320 | int i, rdlen; |
36717eee | 321 | |
fd9fa481 | 322 | for (i = 0; i < count; i++) |
36717eee SK |
323 | { |
324 | if (!(ansp = skip_name(ansp, header, plen))) | |
fd9fa481 | 325 | return NULL; |
36717eee SK |
326 | ansp += 8; /* type, class, TTL */ |
327 | GETSHORT(rdlen, ansp); | |
328 | if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) | |
fd9fa481 | 329 | return NULL; |
36717eee SK |
330 | ansp += rdlen; |
331 | } | |
332 | ||
fd9fa481 SK |
333 | return ansp; |
334 | } | |
335 | ||
0a852541 SK |
336 | /* CRC the question section. This is used to safely detect query |
337 | retransmision and to detect answers to questions we didn't ask, which | |
338 | might be poisoning attacks. Note that we decode the name rather | |
339 | than CRC the raw bytes, since replies might be compressed differently. | |
340 | We ignore case in the names for the same reason. */ | |
341 | unsigned int questions_crc(HEADER *header, unsigned int plen, char *name) | |
fd9fa481 | 342 | { |
91dccd09 SK |
343 | int q; |
344 | unsigned int crc = 0xffffffff; | |
0a852541 SK |
345 | unsigned char *p1, *p = (unsigned char *)(header+1); |
346 | ||
347 | for (q = 0; q < ntohs(header->qdcount); q++) | |
348 | { | |
349 | if (!extract_name(header, plen, &p, name, 1)) | |
350 | return crc; /* bad packet */ | |
351 | ||
352 | for (p1 = name; *p1; p1++) | |
353 | { | |
354 | int i = 8; | |
355 | char c = *p1; | |
356 | ||
357 | if (c >= 'A' && c <= 'Z') | |
358 | c += 'a' - 'A'; | |
359 | ||
360 | crc ^= c << 24; | |
361 | while (i--) | |
362 | crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | |
363 | } | |
364 | ||
365 | /* CRC the class and type as well */ | |
366 | for (p1 = p; p1 < p+4; p1++) | |
367 | { | |
368 | int i = 8; | |
369 | crc ^= *p1 << 24; | |
370 | while (i--) | |
371 | crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | |
372 | } | |
373 | ||
374 | p += 4; | |
375 | if ((unsigned int)(p - (unsigned char *)header) > plen) | |
376 | return crc; /* bad packet */ | |
377 | } | |
fd9fa481 | 378 | |
fd9fa481 SK |
379 | return crc; |
380 | } | |
381 | ||
382 | ||
383 | int resize_packet(HEADER *header, unsigned int plen, unsigned char *pheader, unsigned int hlen) | |
384 | { | |
385 | unsigned char *ansp = skip_questions(header, plen); | |
386 | ||
387 | if (!ansp) | |
388 | return 0; | |
389 | ||
390 | if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), | |
391 | header, plen))) | |
392 | return 0; | |
393 | ||
36717eee SK |
394 | /* restore pseudoheader */ |
395 | if (pheader && ntohs(header->arcount) == 0) | |
396 | { | |
397 | /* must use memmove, may overlap */ | |
398 | memmove(ansp, pheader, hlen); | |
399 | header->arcount = htons(1); | |
400 | ansp += hlen; | |
401 | } | |
402 | ||
403 | return ansp - (unsigned char *)header; | |
404 | } | |
405 | ||
406 | unsigned char *find_pseudoheader(HEADER *header, unsigned int plen, unsigned int *len, unsigned char **p) | |
407 | { | |
408 | /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. | |
409 | also return length of pseudoheader in *len and pointer to the UDP size in *p */ | |
feba5c1d SK |
410 | |
411 | int i, arcount = ntohs(header->arcount); | |
412 | unsigned char *ansp; | |
413 | unsigned short rdlen, type; | |
414 | ||
415 | if (arcount == 0 || !(ansp = skip_questions(header, plen))) | |
416 | return NULL; | |
417 | ||
fd9fa481 SK |
418 | if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen))) |
419 | return NULL; | |
420 | ||
feba5c1d SK |
421 | for (i = 0; i < arcount; i++) |
422 | { | |
36717eee | 423 | unsigned char *save, *start = ansp; |
feba5c1d SK |
424 | if (!(ansp = skip_name(ansp, header, plen))) |
425 | return NULL; | |
426 | ||
427 | GETSHORT(type, ansp); | |
428 | save = ansp; | |
429 | ansp += 6; /* class, TTL */ | |
430 | GETSHORT(rdlen, ansp); | |
431 | if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) | |
432 | return NULL; | |
36717eee SK |
433 | ansp += rdlen; |
434 | if (type == T_OPT) | |
435 | { | |
436 | if (len) | |
437 | *len = ansp - start; | |
438 | if (p) | |
439 | *p = save; | |
440 | return start; | |
441 | } | |
feba5c1d SK |
442 | } |
443 | ||
444 | return NULL; | |
445 | } | |
446 | ||
447 | ||
9e4abcb5 SK |
448 | /* is addr in the non-globally-routed IP space? */ |
449 | static int private_net(struct all_addr *addrp) | |
450 | { | |
451 | struct in_addr addr = *(struct in_addr *)addrp; | |
452 | if (inet_netof(addr) == 0xA || | |
453 | (inet_netof(addr) >= 0xAC10 && inet_netof(addr) < 0xAC20) || | |
454 | (inet_netof(addr) >> 8) == 0xC0A8) | |
455 | return 1; | |
456 | else | |
457 | return 0; | |
458 | } | |
459 | ||
fd9fa481 SK |
460 | static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr) |
461 | { | |
462 | for (; doctor; doctor = doctor->next) | |
463 | if (is_same_net(doctor->in, *addr, doctor->mask)) | |
464 | { | |
465 | addr->s_addr &= ~doctor->mask.s_addr; | |
466 | addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); | |
467 | /* Since we munged the data, the server it came from is no longer authoritative */ | |
fd9fa481 SK |
468 | header->aa = 0; |
469 | break; | |
470 | } | |
471 | } | |
472 | ||
0a852541 | 473 | static int find_soa(HEADER *header, struct doctor *doctor, unsigned int qlen) |
9e4abcb5 SK |
474 | { |
475 | unsigned char *p; | |
9e4abcb5 | 476 | int qtype, qclass, rdlen; |
fd9fa481 SK |
477 | unsigned long ttl, minttl = ULONG_MAX; |
478 | int i, found_soa = 0; | |
9e4abcb5 | 479 | |
fd9fa481 SK |
480 | /* first move to NS section and find TTL from any SOA section */ |
481 | if (!(p = skip_questions(header, qlen)) || | |
482 | !(p = skip_section(p, ntohs(header->ancount), header, qlen))) | |
483 | return 0; /* bad packet */ | |
9e4abcb5 | 484 | |
9e4abcb5 SK |
485 | for (i=0; i<ntohs(header->nscount); i++) |
486 | { | |
fd9fa481 SK |
487 | if (!(p = skip_name(p, header, qlen))) |
488 | return 0; /* bad packet */ | |
489 | ||
9e4abcb5 SK |
490 | GETSHORT(qtype, p); |
491 | GETSHORT(qclass, p); | |
492 | GETLONG(ttl, p); | |
493 | GETSHORT(rdlen, p); | |
fd9fa481 | 494 | |
9e4abcb5 SK |
495 | if ((qclass == C_IN) && (qtype == T_SOA)) |
496 | { | |
fd9fa481 SK |
497 | found_soa = 1; |
498 | if (ttl < minttl) | |
499 | minttl = ttl; | |
500 | ||
9e4abcb5 | 501 | /* MNAME */ |
fd9fa481 SK |
502 | if (!(p = skip_name(p, header, qlen))) |
503 | return 0; | |
9e4abcb5 | 504 | /* RNAME */ |
fd9fa481 SK |
505 | if (!(p = skip_name(p, header, qlen))) |
506 | return 0; | |
507 | p += 16; /* SERIAL REFRESH RETRY EXPIRE */ | |
508 | ||
9e4abcb5 SK |
509 | GETLONG(ttl, p); /* minTTL */ |
510 | if (ttl < minttl) | |
511 | minttl = ttl; | |
512 | } | |
513 | else | |
514 | p += rdlen; | |
9e4abcb5 | 515 | |
fd9fa481 SK |
516 | if ((unsigned int)(p - (unsigned char *)header) > qlen) |
517 | return 0; /* bad packet */ | |
9e4abcb5 | 518 | } |
fd9fa481 | 519 | |
0a852541 SK |
520 | if (doctor) |
521 | for (i=0; i<ntohs(header->arcount); i++) | |
522 | { | |
523 | if (!(p = skip_name(p, header, qlen))) | |
524 | return 0; /* bad packet */ | |
525 | ||
526 | GETSHORT(qtype, p); | |
527 | GETSHORT(qclass, p); | |
528 | GETLONG(ttl, p); | |
529 | GETSHORT(rdlen, p); | |
530 | ||
531 | if ((qclass == C_IN) && (qtype == T_A)) | |
532 | dns_doctor(header, doctor, (struct in_addr *)p); | |
533 | ||
534 | p += rdlen; | |
535 | ||
536 | if ((unsigned int)(p - (unsigned char *)header) > qlen) | |
537 | return 0; /* bad packet */ | |
538 | } | |
539 | ||
fd9fa481 | 540 | return found_soa ? minttl : 0; |
1cff166d SK |
541 | } |
542 | ||
fd9fa481 SK |
543 | /* Note that the following code can create CNAME chains that don't point to a real record, |
544 | either because of lack of memory, or lack of SOA records. These are treated by the cache code as | |
545 | expired and cleaned out that way. */ | |
546 | void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now, struct daemon *daemon) | |
9e4abcb5 | 547 | { |
fd9fa481 SK |
548 | unsigned char *p, *p1, *endrr; |
549 | int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; | |
0a852541 SK |
550 | unsigned long ttl = 0; |
551 | ||
9e4abcb5 | 552 | cache_start_insert(); |
0a852541 SK |
553 | |
554 | /* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */ | |
555 | if (daemon->doctors) | |
556 | { | |
557 | searched_soa = 1; | |
558 | ttl = find_soa(header, daemon->doctors, qlen); | |
559 | } | |
9e4abcb5 | 560 | |
fd9fa481 SK |
561 | /* go through the questions. */ |
562 | p = (unsigned char *)(header+1); | |
9e4abcb5 | 563 | |
fd9fa481 | 564 | for (i = 0; i<ntohs(header->qdcount); i++) |
9e4abcb5 | 565 | { |
fd9fa481 SK |
566 | int found = 0, cname_count = 5; |
567 | struct crec *cpp = NULL; | |
568 | int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0; | |
0a852541 | 569 | unsigned long cttl = ULONG_MAX, attl; |
fd9fa481 | 570 | |
9e4abcb5 SK |
571 | if (!extract_name(header, qlen, &p, name, 1)) |
572 | return; /* bad packet */ | |
fd9fa481 | 573 | |
9e4abcb5 SK |
574 | GETSHORT(qtype, p); |
575 | GETSHORT(qclass, p); | |
9e4abcb5 SK |
576 | |
577 | if (qclass != C_IN) | |
fd9fa481 | 578 | continue; |
9e4abcb5 | 579 | |
fd9fa481 SK |
580 | /* PTRs: we chase CNAMEs here, since we have no way to |
581 | represent them in the cache. */ | |
582 | if (qtype == T_PTR) | |
583 | { | |
9e4abcb5 SK |
584 | struct all_addr addr; |
585 | int name_encoding = in_arpa_name_2_addr(name, &addr); | |
fd9fa481 SK |
586 | |
587 | if (!name_encoding) | |
588 | continue; | |
589 | ||
590 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 591 | { |
fd9fa481 SK |
592 | cname_loop: |
593 | if (!(p1 = skip_questions(header, qlen))) | |
594 | return; | |
595 | ||
596 | for (j = 0; j<ntohs(header->ancount); j++) | |
597 | { | |
598 | if (!(res = extract_name(header, qlen, &p1, name, 0))) | |
599 | return; /* bad packet */ | |
600 | ||
601 | GETSHORT(aqtype, p1); | |
602 | GETSHORT(aqclass, p1); | |
603 | GETLONG(attl, p1); | |
604 | GETSHORT(ardlen, p1); | |
605 | endrr = p1+ardlen; | |
606 | ||
607 | /* TTL of record is minimum of CNAMES and PTR */ | |
608 | if (attl < cttl) | |
609 | cttl = attl; | |
610 | ||
611 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) | |
612 | { | |
613 | if (!extract_name(header, qlen, &p1, name, 1)) | |
614 | return; | |
615 | ||
616 | if (aqtype == T_CNAME) | |
617 | { | |
618 | if (!cname_count--) | |
619 | return; /* looped CNAMES */ | |
620 | goto cname_loop; | |
621 | } | |
622 | ||
623 | cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE); | |
624 | found = 1; | |
625 | } | |
626 | ||
627 | p1 = endrr; | |
628 | if ((unsigned int)(p1 - (unsigned char *)header) > qlen) | |
629 | return; /* bad packet */ | |
630 | } | |
631 | } | |
632 | ||
633 | if (!found && !(daemon->options & OPT_NO_NEG)) | |
634 | { | |
635 | if (!searched_soa) | |
636 | { | |
637 | searched_soa = 1; | |
0a852541 | 638 | ttl = find_soa(header, NULL, qlen); |
fd9fa481 SK |
639 | } |
640 | if (ttl) | |
641 | cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); | |
9e4abcb5 SK |
642 | } |
643 | } | |
fd9fa481 | 644 | else |
9e4abcb5 | 645 | { |
fd9fa481 SK |
646 | /* everything other than PTR */ |
647 | struct crec *newc; | |
648 | ||
649 | if (qtype == T_A) | |
650 | flags |= F_IPV4; | |
651 | #ifdef HAVE_IPV6 | |
652 | else if (qtype == T_AAAA) | |
653 | flags |= F_IPV6; | |
654 | #endif | |
655 | else | |
656 | continue; | |
657 | ||
658 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 659 | { |
fd9fa481 SK |
660 | cname_loop1: |
661 | if (!(p1 = skip_questions(header, qlen))) | |
662 | return; | |
9e4abcb5 | 663 | |
fd9fa481 | 664 | for (j = 0; j<ntohs(header->ancount); j++) |
9e4abcb5 | 665 | { |
fd9fa481 SK |
666 | if (!(res = extract_name(header, qlen, &p1, name, 0))) |
667 | return; /* bad packet */ | |
668 | ||
669 | GETSHORT(aqtype, p1); | |
670 | GETSHORT(aqclass, p1); | |
671 | GETLONG(attl, p1); | |
672 | GETSHORT(ardlen, p1); | |
673 | endrr = p1+ardlen; | |
674 | ||
675 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) | |
676 | { | |
677 | if (aqtype == T_CNAME) | |
678 | { | |
679 | if (!cname_count--) | |
680 | return; /* looped CNAMES */ | |
681 | newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD); | |
26128d27 | 682 | if (newc && cpp) |
fd9fa481 SK |
683 | { |
684 | cpp->addr.cname.cache = newc; | |
685 | cpp->addr.cname.uid = newc->uid; | |
686 | } | |
9e4abcb5 | 687 | |
fd9fa481 SK |
688 | cpp = newc; |
689 | if (attl < cttl) | |
690 | cttl = attl; | |
691 | ||
692 | if (!extract_name(header, qlen, &p1, name, 1)) | |
693 | return; | |
694 | goto cname_loop1; | |
695 | } | |
696 | else | |
697 | { | |
698 | found = 1; | |
699 | if (aqtype == T_A) | |
700 | dns_doctor(header, daemon->doctors, (struct in_addr *)p1); | |
701 | newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD); | |
26128d27 | 702 | if (newc && cpp) |
fd9fa481 SK |
703 | { |
704 | cpp->addr.cname.cache = newc; | |
705 | cpp->addr.cname.uid = newc->uid; | |
706 | } | |
707 | cpp = NULL; | |
708 | } | |
709 | } | |
710 | ||
711 | p1 = endrr; | |
712 | if ((unsigned int)(p1 - (unsigned char *)header) > qlen) | |
713 | return; /* bad packet */ | |
714 | } | |
715 | } | |
716 | ||
717 | if (!found && !(daemon->options & OPT_NO_NEG)) | |
718 | { | |
719 | if (!searched_soa) | |
1cff166d | 720 | { |
fd9fa481 | 721 | searched_soa = 1; |
0a852541 | 722 | ttl = find_soa(header, NULL, qlen); |
1cff166d | 723 | } |
fd9fa481 SK |
724 | /* If there's no SOA to get the TTL from, but there is a CNAME |
725 | pointing at this, inherit it's TTL */ | |
726 | if (ttl || cpp) | |
9e4abcb5 | 727 | { |
fd9fa481 | 728 | newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags); |
26128d27 | 729 | if (newc && cpp) |
9e4abcb5 | 730 | { |
fd9fa481 SK |
731 | cpp->addr.cname.cache = newc; |
732 | cpp->addr.cname.uid = newc->uid; | |
733 | } | |
9e4abcb5 | 734 | } |
9e4abcb5 | 735 | } |
fd9fa481 | 736 | } |
9e4abcb5 | 737 | } |
fd9fa481 | 738 | |
9e4abcb5 SK |
739 | cache_end_insert(); |
740 | } | |
741 | ||
742 | /* If the packet holds exactly one query | |
743 | return 1 and leave the name from the query in name. */ | |
744 | ||
c1bb8504 | 745 | unsigned short extract_request(HEADER *header,unsigned int qlen, char *name, unsigned short *typep) |
9e4abcb5 SK |
746 | { |
747 | unsigned char *p = (unsigned char *)(header+1); | |
748 | int qtype, qclass; | |
749 | ||
c1bb8504 SK |
750 | if (typep) |
751 | *typep = 0; | |
752 | ||
9e4abcb5 SK |
753 | if (ntohs(header->qdcount) != 1 || header->opcode != QUERY) |
754 | return 0; /* must be exactly one query. */ | |
755 | ||
756 | if (!extract_name(header, qlen, &p, name, 1)) | |
757 | return 0; /* bad packet */ | |
758 | ||
759 | GETSHORT(qtype, p); | |
760 | GETSHORT(qclass, p); | |
761 | ||
0a852541 SK |
762 | if (typep) |
763 | *typep = qtype; | |
764 | ||
9e4abcb5 SK |
765 | if (qclass == C_IN) |
766 | { | |
767 | if (qtype == T_A) | |
768 | return F_IPV4; | |
769 | if (qtype == T_AAAA) | |
770 | return F_IPV6; | |
771 | if (qtype == T_ANY) | |
772 | return F_IPV4 | F_IPV6; | |
773 | } | |
774 | ||
775 | return F_QUERY; | |
776 | } | |
777 | ||
778 | ||
779 | int setup_reply(HEADER *header, unsigned int qlen, | |
780 | struct all_addr *addrp, unsigned short flags, unsigned long ttl) | |
781 | { | |
782 | unsigned char *p = skip_questions(header, qlen); | |
783 | ||
784 | header->qr = 1; /* response */ | |
785 | header->aa = 0; /* authoritive */ | |
786 | header->ra = 1; /* recursion if available */ | |
787 | header->tc = 0; /* not truncated */ | |
788 | header->nscount = htons(0); | |
789 | header->arcount = htons(0); | |
feba5c1d | 790 | header->ancount = htons(0); /* no answers unless changed below */ |
9e4abcb5 SK |
791 | if (flags == F_NEG) |
792 | header->rcode = SERVFAIL; /* couldn't get memory */ | |
44a2a316 | 793 | else if (flags == F_NOERR || flags == F_QUERY) |
9e4abcb5 SK |
794 | header->rcode = NOERROR; /* empty domain */ |
795 | else if (flags == F_NXDOMAIN) | |
796 | header->rcode = NXDOMAIN; | |
797 | else if (p && flags == F_IPV4) | |
798 | { /* we know the address */ | |
799 | header->rcode = NOERROR; | |
800 | header->ancount = htons(1); | |
801 | header->aa = 1; | |
f6b7dc47 | 802 | add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp); |
9e4abcb5 SK |
803 | } |
804 | #ifdef HAVE_IPV6 | |
805 | else if (p && flags == F_IPV6) | |
806 | { | |
807 | header->rcode = NOERROR; | |
808 | header->ancount = htons(1); | |
809 | header->aa = 1; | |
f6b7dc47 | 810 | add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp); |
9e4abcb5 SK |
811 | } |
812 | #endif | |
813 | else /* nowhere to forward to */ | |
814 | header->rcode = REFUSED; | |
815 | ||
816 | return p - (unsigned char *)header; | |
817 | } | |
36717eee SK |
818 | |
819 | /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ | |
f6b7dc47 | 820 | int check_for_local_domain(char *name, time_t now, struct daemon *daemon) |
36717eee SK |
821 | { |
822 | struct crec *crecp; | |
0a852541 SK |
823 | struct mx_srv_record *mx; |
824 | struct txt_record *txt; | |
825 | ||
26128d27 | 826 | if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) && |
36717eee SK |
827 | (crecp->flags & (F_HOSTS | F_DHCP))) |
828 | return 1; | |
829 | ||
f6b7dc47 | 830 | for (mx = daemon->mxnames; mx; mx = mx->next) |
0a852541 | 831 | if (hostname_isequal(name, mx->name)) |
36717eee | 832 | return 1; |
f6b7dc47 | 833 | |
0a852541 SK |
834 | for (txt = daemon->txt; txt; txt = txt->next) |
835 | if (hostname_isequal(name, txt->name)) | |
f6b7dc47 | 836 | return 1; |
0a852541 | 837 | |
36717eee SK |
838 | return 0; |
839 | } | |
9e4abcb5 SK |
840 | |
841 | /* Is the packet a reply with the answer address equal to addr? | |
842 | If so mung is into an NXDOMAIN reply and also put that information | |
843 | in the cache. */ | |
844 | int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name, | |
845 | struct bogus_addr *baddr, time_t now) | |
846 | { | |
847 | unsigned char *p; | |
848 | int i, qtype, qclass, rdlen; | |
849 | unsigned long ttl; | |
850 | struct bogus_addr *baddrp; | |
851 | ||
852 | /* skip over questions */ | |
853 | if (!(p = skip_questions(header, qlen))) | |
854 | return 0; /* bad packet */ | |
855 | ||
856 | for (i=0; i<ntohs(header->ancount); i++) | |
857 | { | |
858 | if (!extract_name(header, qlen, &p, name, 1)) | |
859 | return 0; /* bad packet */ | |
860 | ||
861 | GETSHORT(qtype, p); | |
862 | GETSHORT(qclass, p); | |
863 | GETLONG(ttl, p); | |
864 | GETSHORT(rdlen, p); | |
865 | ||
866 | if (qclass == C_IN && qtype == T_A) | |
867 | for (baddrp = baddr; baddrp; baddrp = baddrp->next) | |
868 | if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) | |
869 | { | |
fd9fa481 SK |
870 | /* Found a bogus address. Insert that info here, since there no SOA record |
871 | to get the ttl from in the normal processing */ | |
9e4abcb5 SK |
872 | cache_start_insert(); |
873 | cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG); | |
874 | cache_end_insert(); | |
875 | ||
876 | return 1; | |
877 | } | |
878 | ||
879 | p += rdlen; | |
880 | } | |
881 | ||
882 | return 0; | |
883 | } | |
884 | ||
f6b7dc47 SK |
885 | static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, |
886 | unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...) | |
887 | { | |
888 | va_list ap; | |
889 | unsigned char *sav, *p = *pp; | |
890 | int j; | |
891 | unsigned short usval; | |
892 | long lval; | |
893 | char *sval; | |
894 | ||
895 | if (truncp && *truncp) | |
896 | return 0; | |
897 | ||
898 | PUTSHORT(nameoffset | 0xc000, p); | |
899 | PUTSHORT(type, p); | |
900 | PUTSHORT(class, p); | |
901 | PUTLONG(ttl, p); /* TTL */ | |
902 | ||
903 | sav = p; /* Save pointer to RDLength field */ | |
904 | PUTSHORT(0, p); /* Placeholder RDLength */ | |
905 | ||
906 | va_start(ap, format); /* make ap point to 1st unamed argument */ | |
907 | ||
908 | for (; *format; format++) | |
909 | switch (*format) | |
910 | { | |
911 | #ifdef HAVE_IPV6 | |
912 | case '6': | |
913 | sval = va_arg(ap, char *); | |
914 | memcpy(p, sval, IN6ADDRSZ); | |
915 | p += IN6ADDRSZ; | |
916 | break; | |
917 | #endif | |
918 | ||
919 | case '4': | |
920 | sval = va_arg(ap, char *); | |
921 | memcpy(p, sval, INADDRSZ); | |
922 | p += INADDRSZ; | |
923 | break; | |
924 | ||
925 | case 's': | |
926 | usval = va_arg(ap, int); | |
927 | PUTSHORT(usval, p); | |
928 | break; | |
929 | ||
930 | case 'l': | |
931 | lval = va_arg(ap, long); | |
932 | PUTLONG(lval, p); | |
933 | break; | |
934 | ||
935 | case 'd': | |
936 | /* get domain-name answer arg and store it in RDATA field */ | |
0a852541 SK |
937 | if (offset) |
938 | *offset = p - (unsigned char *)header; | |
f6b7dc47 SK |
939 | sval = va_arg(ap, char *); |
940 | while (sval && *sval) | |
941 | { | |
942 | unsigned char *cp = p++; | |
943 | for (j = 0; *sval && (*sval != '.'); sval++, j++) | |
944 | *p++ = *sval; | |
945 | *cp = j; | |
946 | if (*sval) | |
947 | sval++; | |
948 | } | |
949 | *p++ = 0; | |
950 | break; | |
951 | ||
952 | case 't': | |
0a852541 | 953 | usval = va_arg(ap, int); |
f6b7dc47 | 954 | sval = va_arg(ap, char *); |
0a852541 SK |
955 | memcpy(p, sval, usval); |
956 | p += usval; | |
f6b7dc47 SK |
957 | break; |
958 | } | |
959 | ||
960 | va_end(ap); /* clean up variable argument pointer */ | |
961 | ||
962 | j = p - sav - 2; | |
963 | PUTSHORT(j, sav); /* Now, store real RDLength */ | |
964 | ||
f6b7dc47 SK |
965 | /* check for overflow of buffer */ |
966 | if (limit && ((unsigned char *)limit - p) < 0) | |
967 | { | |
968 | if (truncp) | |
969 | *truncp = 1; | |
970 | return 0; | |
971 | } | |
972 | ||
973 | *pp = p; | |
974 | return 1; | |
975 | } | |
976 | ||
9e4abcb5 | 977 | /* return zero if we can't answer from cache, or packet size if we can */ |
f6b7dc47 SK |
978 | int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, |
979 | struct in_addr local_addr, struct in_addr local_netmask, time_t now) | |
9e4abcb5 | 980 | { |
3be34541 | 981 | char *name = daemon->namebuff; |
feba5c1d | 982 | unsigned char *p, *ansp, *pheader; |
9e4abcb5 SK |
983 | int qtype, qclass, is_arpa; |
984 | struct all_addr addr; | |
985 | unsigned int nameoffset; | |
feba5c1d SK |
986 | unsigned short flag; |
987 | int qdcount = ntohs(header->qdcount); | |
0a852541 | 988 | int q, ans, anscount = 0, addncount = 0; |
feba5c1d | 989 | int dryrun = 0, sec_reqd = 0; |
9e4abcb5 | 990 | struct crec *crecp; |
f6b7dc47 | 991 | int nxdomain = 0, auth = 1, trunc = 0; |
0a852541 SK |
992 | struct mx_srv_record *rec; |
993 | ||
9e4abcb5 SK |
994 | if (!qdcount || header->opcode != QUERY ) |
995 | return 0; | |
996 | ||
feba5c1d SK |
997 | /* If there is an RFC2671 pseudoheader then it will be overwritten by |
998 | partial replies, so we have to do a dry run to see if we can answer | |
999 | the query. We check to see if the do bit is set, if so we always | |
1000 | forward rather than answering from the cache, which doesn't include | |
1001 | security information. */ | |
1002 | ||
36717eee | 1003 | if (find_pseudoheader(header, qlen, NULL, &pheader)) |
feba5c1d SK |
1004 | { |
1005 | unsigned short udpsz, ext_rcode, flags; | |
1006 | unsigned char *psave = pheader; | |
1007 | ||
1008 | GETSHORT(udpsz, pheader); | |
1009 | GETSHORT(ext_rcode, pheader); | |
1010 | GETSHORT(flags, pheader); | |
1011 | ||
1012 | sec_reqd = flags & 0x8000; /* do bit */ | |
1013 | ||
1014 | /* If our client is advertising a larger UDP packet size | |
1015 | than we allow, trim it so that we don't get an overlarge | |
1016 | response from upstream */ | |
1017 | ||
3be34541 SK |
1018 | if (udpsz > daemon->edns_pktsz) |
1019 | PUTSHORT(daemon->edns_pktsz, psave); | |
feba5c1d SK |
1020 | |
1021 | dryrun = 1; | |
1022 | } | |
1023 | ||
0a852541 SK |
1024 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1025 | rec->offset = 0; | |
1026 | ||
feba5c1d | 1027 | rerun: |
9e4abcb5 SK |
1028 | /* determine end of question section (we put answers there) */ |
1029 | if (!(ansp = skip_questions(header, qlen))) | |
1030 | return 0; /* bad packet */ | |
1031 | ||
1032 | /* now process each question, answers go in RRs after the question */ | |
1033 | p = (unsigned char *)(header+1); | |
feba5c1d | 1034 | |
9e4abcb5 SK |
1035 | for (q=0; q<qdcount; q++) |
1036 | { | |
1037 | /* save pointer to name for copying into answers */ | |
1038 | nameoffset = p - (unsigned char *)header; | |
1039 | ||
1040 | /* now extract name as .-concatenated string into name */ | |
1041 | if (!extract_name(header, qlen, &p, name, 1)) | |
1042 | return 0; /* bad packet */ | |
1043 | ||
1044 | /* see if it's w.z.y.z.in-addr.arpa format */ | |
1045 | ||
1046 | is_arpa = in_arpa_name_2_addr(name, &addr); | |
1047 | ||
1048 | GETSHORT(qtype, p); | |
1049 | GETSHORT(qclass, p); | |
1050 | ||
1051 | ans = 0; /* have we answered this question */ | |
1052 | ||
0a852541 | 1053 | if (qtype == T_TXT || qtype == T_ANY) |
9e4abcb5 | 1054 | { |
0a852541 SK |
1055 | struct txt_record *t; |
1056 | for(t = daemon->txt; t ; t = t->next) | |
9e4abcb5 | 1057 | { |
0a852541 SK |
1058 | if (t->class == qclass && hostname_isequal(name, t->name)) |
1059 | { | |
1060 | ans = 1; | |
1061 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0); | |
1062 | if (!dryrun && | |
1063 | add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL, | |
1064 | T_TXT, t->class, "t", t->len, t->txt)) | |
1065 | anscount++; | |
1066 | } | |
9e4abcb5 | 1067 | } |
0a852541 | 1068 | } |
f6b7dc47 | 1069 | |
0a852541 | 1070 | if (qclass == C_IN) |
9e4abcb5 | 1071 | { |
f6b7dc47 | 1072 | if (qtype == T_PTR || qtype == T_ANY) |
c1bb8504 | 1073 | { |
f6b7dc47 SK |
1074 | if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) |
1075 | { | |
1076 | if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr)) | |
1077 | { | |
1078 | /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */ | |
1079 | ans = 1; | |
1080 | nxdomain = 1; | |
1081 | if (!dryrun) | |
1082 | log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0); | |
9e4abcb5 | 1083 | } |
9e4abcb5 | 1084 | } |
f6b7dc47 SK |
1085 | else do |
1086 | { | |
1087 | /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ | |
1088 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1089 | continue; | |
1090 | ||
1091 | if (crecp->flags & F_NEG) | |
feba5c1d | 1092 | { |
f6b7dc47 SK |
1093 | ans = 1; |
1094 | auth = 0; | |
1095 | if (crecp->flags & F_NXDOMAIN) | |
1096 | nxdomain = 1; | |
1097 | if (!dryrun) | |
1098 | log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0); | |
1099 | } | |
1100 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) | |
1101 | { | |
1102 | ans = 1; | |
1103 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1104 | auth = 0; | |
1105 | if (!dryrun) | |
1106 | { | |
1107 | unsigned long ttl; | |
1108 | /* Return 0 ttl for DHCP entries, which might change | |
1109 | before the lease expires. */ | |
1110 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
1111 | ttl = daemon->local_ttl; | |
1112 | else | |
1113 | ttl = crecp->ttd - now; | |
1114 | ||
1115 | log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, | |
1116 | 0, daemon->addn_hosts, crecp->uid); | |
1117 | ||
1118 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, | |
1119 | T_PTR, C_IN, "d", cache_get_name(crecp))) | |
1120 | anscount++; | |
1121 | } | |
1122 | } | |
1123 | } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); | |
1124 | } | |
1125 | ||
1126 | for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) | |
1127 | { | |
1128 | unsigned short type = T_A; | |
1129 | ||
1130 | if (flag == F_IPV6) | |
feba5c1d | 1131 | #ifdef HAVE_IPV6 |
f6b7dc47 | 1132 | type = T_AAAA; |
feba5c1d | 1133 | #else |
f6b7dc47 | 1134 | break; |
feba5c1d | 1135 | #endif |
f6b7dc47 SK |
1136 | |
1137 | if (qtype != type && qtype != T_ANY) | |
1138 | continue; | |
1139 | ||
1140 | cname_restart: | |
1141 | if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME))) | |
1142 | { | |
1143 | int localise = 0; | |
feba5c1d | 1144 | |
f6b7dc47 SK |
1145 | /* See if a putative address is on the network from which we recieved |
1146 | the query, is so we'll filter other answers. */ | |
1147 | if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4) | |
1148 | { | |
1149 | struct crec *save = crecp; | |
1150 | do { | |
1151 | if ((crecp->flags & F_HOSTS) && | |
1152 | is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1153 | { | |
1154 | localise = 1; | |
1155 | break; | |
1156 | } | |
1157 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); | |
1158 | crecp = save; | |
1159 | } | |
1160 | ||
1161 | do | |
9e4abcb5 | 1162 | { |
26128d27 SK |
1163 | /* don't answer wildcard queries with data not from /etc/hosts |
1164 | or DHCP leases */ | |
1165 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1166 | break; | |
1167 | ||
fd9fa481 SK |
1168 | if (crecp->flags & F_CNAME) |
1169 | { | |
fd9fa481 SK |
1170 | if (!dryrun) |
1171 | { | |
fd9fa481 | 1172 | log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid); |
f6b7dc47 SK |
1173 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset, |
1174 | T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache))) | |
1175 | anscount++; | |
fd9fa481 | 1176 | } |
f6b7dc47 | 1177 | |
fd9fa481 SK |
1178 | strcpy(name, cache_get_name(crecp->addr.cname.cache)); |
1179 | goto cname_restart; | |
1180 | } | |
f6b7dc47 | 1181 | |
9e4abcb5 SK |
1182 | if (crecp->flags & F_NEG) |
1183 | { | |
feba5c1d | 1184 | ans = 1; |
f6b7dc47 SK |
1185 | auth = 0; |
1186 | if (crecp->flags & F_NXDOMAIN) | |
1187 | nxdomain = 1; | |
feba5c1d | 1188 | if (!dryrun) |
f6b7dc47 | 1189 | log_query(crecp->flags, name, NULL, 0, NULL, 0); |
9e4abcb5 | 1190 | } |
feba5c1d | 1191 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) |
9e4abcb5 | 1192 | { |
f6b7dc47 SK |
1193 | /* If we are returning local answers depending on network, |
1194 | filter here. */ | |
1195 | if (localise && | |
1196 | (crecp->flags & F_HOSTS) && | |
1197 | !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1198 | continue; | |
1199 | ||
1200 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1201 | auth = 0; | |
1202 | ||
feba5c1d SK |
1203 | ans = 1; |
1204 | if (!dryrun) | |
1205 | { | |
1206 | unsigned long ttl; | |
1207 | ||
1208 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
3be34541 | 1209 | ttl = daemon->local_ttl; |
feba5c1d SK |
1210 | else |
1211 | ttl = crecp->ttd - now; | |
1212 | ||
fd9fa481 SK |
1213 | log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr, |
1214 | 0, daemon->addn_hosts, crecp->uid); | |
feba5c1d | 1215 | |
f6b7dc47 SK |
1216 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN, |
1217 | type == T_A ? "4" : "6", &crecp->addr)) | |
1218 | anscount++; | |
feba5c1d | 1219 | } |
9e4abcb5 | 1220 | } |
f6b7dc47 | 1221 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); |
9e4abcb5 | 1222 | } |
f6b7dc47 SK |
1223 | } |
1224 | ||
1225 | if (qtype == T_MX || qtype == T_ANY) | |
1226 | { | |
1227 | int found = 0; | |
0a852541 SK |
1228 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1229 | if (!rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1230 | { |
1231 | ans = found = 1; | |
1232 | if (!dryrun) | |
feba5c1d | 1233 | { |
0a852541 | 1234 | int offset; |
f6b7dc47 | 1235 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0); |
0a852541 SK |
1236 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
1237 | &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) | |
1238 | { | |
1239 | anscount++; | |
1240 | if (rec->target) | |
1241 | rec->offset = offset; | |
1242 | } | |
feba5c1d | 1243 | } |
f6b7dc47 SK |
1244 | } |
1245 | ||
1246 | if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) && | |
1247 | cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP)) | |
1248 | { | |
1249 | ans = 1; | |
1250 | if (!dryrun) | |
1251 | { | |
1252 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0); | |
1253 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, | |
1254 | T_MX, C_IN, "sd", 1, | |
1255 | (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget)) | |
1256 | anscount++; | |
9e4abcb5 SK |
1257 | } |
1258 | } | |
f6b7dc47 SK |
1259 | } |
1260 | ||
1261 | if (qtype == T_SRV || qtype == T_ANY) | |
1262 | { | |
1263 | int found = 0; | |
9e4abcb5 | 1264 | |
0a852541 SK |
1265 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1266 | if (rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1267 | { |
1268 | found = ans = 1; | |
1269 | if (!dryrun) | |
1270 | { | |
0a852541 | 1271 | int offset; |
f6b7dc47 SK |
1272 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0); |
1273 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, | |
0a852541 SK |
1274 | &offset, T_SRV, C_IN, "sssd", |
1275 | rec->priority, rec->weight, rec->srvport, rec->target)) | |
1276 | { | |
1277 | anscount++; | |
1278 | if (rec->target) | |
1279 | rec->offset = offset; | |
1280 | } | |
f6b7dc47 SK |
1281 | } |
1282 | } | |
feba5c1d | 1283 | |
f6b7dc47 SK |
1284 | if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) |
1285 | { | |
1286 | ans = 1; | |
1287 | if (!dryrun) | |
1288 | log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0); | |
1289 | } | |
1290 | } | |
1291 | ||
1292 | if (qtype == T_MAILB) | |
1293 | ans = 1, nxdomain = 1; | |
1294 | ||
1295 | if (qtype == T_SOA && (daemon->options & OPT_FILTER)) | |
1296 | { | |
1297 | ans = 1; | |
1298 | if (!dryrun) | |
1299 | log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0); | |
feba5c1d | 1300 | } |
9e4abcb5 | 1301 | } |
f6b7dc47 SK |
1302 | |
1303 | if (!ans) | |
9e4abcb5 | 1304 | return 0; /* failed to answer a question */ |
feba5c1d | 1305 | } |
f6b7dc47 | 1306 | |
feba5c1d SK |
1307 | if (dryrun) |
1308 | { | |
1309 | dryrun = 0; | |
1310 | goto rerun; | |
9e4abcb5 SK |
1311 | } |
1312 | ||
0a852541 SK |
1313 | /* create an additional data section, for stuff in SRV and MX record replies. */ |
1314 | for (rec = daemon->mxnames; rec; rec = rec->next) | |
1315 | if (rec->offset != 0) | |
1316 | { | |
1317 | /* squash dupes */ | |
1318 | struct mx_srv_record *tmp; | |
1319 | for (tmp = rec->next; tmp; tmp = tmp->next) | |
1320 | if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target)) | |
1321 | tmp->offset = 0; | |
1322 | ||
1323 | crecp = NULL; | |
1324 | while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6))) | |
1325 | { | |
1326 | unsigned long ttl; | |
1327 | #ifdef HAVE_IPV6 | |
1328 | int type = crecp->flags & F_IPV4 ? T_A : T_AAAA; | |
1329 | #else | |
1330 | int type = T_A; | |
1331 | #endif | |
1332 | if (crecp->flags & F_NEG) | |
1333 | continue; | |
1334 | ||
1335 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
1336 | ttl = daemon->local_ttl; | |
1337 | else | |
1338 | ttl = crecp->ttd - now; | |
1339 | ||
1340 | if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN, | |
1341 | crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) | |
1342 | addncount++; | |
1343 | } | |
1344 | } | |
1345 | ||
9e4abcb5 SK |
1346 | /* done all questions, set up header and return length of result */ |
1347 | header->qr = 1; /* response */ | |
1348 | header->aa = auth; /* authoritive - only hosts and DHCP derived names. */ | |
1349 | header->ra = 1; /* recursion if available */ | |
f6b7dc47 | 1350 | header->tc = trunc; /* truncation */ |
9e4abcb5 SK |
1351 | if (anscount == 0 && nxdomain) |
1352 | header->rcode = NXDOMAIN; | |
1353 | else | |
1354 | header->rcode = NOERROR; /* no error */ | |
1355 | header->ancount = htons(anscount); | |
1356 | header->nscount = htons(0); | |
0a852541 | 1357 | header->arcount = htons(addncount); |
9e4abcb5 SK |
1358 | return ansp - (unsigned char *)header; |
1359 | } | |
1360 | ||
1361 | ||
1362 | ||
1363 | ||
1364 |