]>
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 | { |
0a852541 SK |
343 | unsigned int q, crc = 0xffffffff; |
344 | unsigned char *p1, *p = (unsigned char *)(header+1); | |
345 | ||
346 | for (q = 0; q < ntohs(header->qdcount); q++) | |
347 | { | |
348 | if (!extract_name(header, plen, &p, name, 1)) | |
349 | return crc; /* bad packet */ | |
350 | ||
351 | for (p1 = name; *p1; p1++) | |
352 | { | |
353 | int i = 8; | |
354 | char c = *p1; | |
355 | ||
356 | if (c >= 'A' && c <= 'Z') | |
357 | c += 'a' - 'A'; | |
358 | ||
359 | crc ^= c << 24; | |
360 | while (i--) | |
361 | crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | |
362 | } | |
363 | ||
364 | /* CRC the class and type as well */ | |
365 | for (p1 = p; p1 < p+4; p1++) | |
366 | { | |
367 | int i = 8; | |
368 | crc ^= *p1 << 24; | |
369 | while (i--) | |
370 | crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1; | |
371 | } | |
372 | ||
373 | p += 4; | |
374 | if ((unsigned int)(p - (unsigned char *)header) > plen) | |
375 | return crc; /* bad packet */ | |
376 | } | |
fd9fa481 | 377 | |
fd9fa481 SK |
378 | return crc; |
379 | } | |
380 | ||
381 | ||
382 | int resize_packet(HEADER *header, unsigned int plen, unsigned char *pheader, unsigned int hlen) | |
383 | { | |
384 | unsigned char *ansp = skip_questions(header, plen); | |
385 | ||
386 | if (!ansp) | |
387 | return 0; | |
388 | ||
389 | if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), | |
390 | header, plen))) | |
391 | return 0; | |
392 | ||
36717eee SK |
393 | /* restore pseudoheader */ |
394 | if (pheader && ntohs(header->arcount) == 0) | |
395 | { | |
396 | /* must use memmove, may overlap */ | |
397 | memmove(ansp, pheader, hlen); | |
398 | header->arcount = htons(1); | |
399 | ansp += hlen; | |
400 | } | |
401 | ||
402 | return ansp - (unsigned char *)header; | |
403 | } | |
404 | ||
405 | unsigned char *find_pseudoheader(HEADER *header, unsigned int plen, unsigned int *len, unsigned char **p) | |
406 | { | |
407 | /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. | |
408 | also return length of pseudoheader in *len and pointer to the UDP size in *p */ | |
feba5c1d SK |
409 | |
410 | int i, arcount = ntohs(header->arcount); | |
411 | unsigned char *ansp; | |
412 | unsigned short rdlen, type; | |
413 | ||
414 | if (arcount == 0 || !(ansp = skip_questions(header, plen))) | |
415 | return NULL; | |
416 | ||
fd9fa481 SK |
417 | if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen))) |
418 | return NULL; | |
419 | ||
feba5c1d SK |
420 | for (i = 0; i < arcount; i++) |
421 | { | |
36717eee | 422 | unsigned char *save, *start = ansp; |
feba5c1d SK |
423 | if (!(ansp = skip_name(ansp, header, plen))) |
424 | return NULL; | |
425 | ||
426 | GETSHORT(type, ansp); | |
427 | save = ansp; | |
428 | ansp += 6; /* class, TTL */ | |
429 | GETSHORT(rdlen, ansp); | |
430 | if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) | |
431 | return NULL; | |
36717eee SK |
432 | ansp += rdlen; |
433 | if (type == T_OPT) | |
434 | { | |
435 | if (len) | |
436 | *len = ansp - start; | |
437 | if (p) | |
438 | *p = save; | |
439 | return start; | |
440 | } | |
feba5c1d SK |
441 | } |
442 | ||
443 | return NULL; | |
444 | } | |
445 | ||
446 | ||
9e4abcb5 SK |
447 | /* is addr in the non-globally-routed IP space? */ |
448 | static int private_net(struct all_addr *addrp) | |
449 | { | |
450 | struct in_addr addr = *(struct in_addr *)addrp; | |
451 | if (inet_netof(addr) == 0xA || | |
452 | (inet_netof(addr) >= 0xAC10 && inet_netof(addr) < 0xAC20) || | |
453 | (inet_netof(addr) >> 8) == 0xC0A8) | |
454 | return 1; | |
455 | else | |
456 | return 0; | |
457 | } | |
458 | ||
fd9fa481 SK |
459 | static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr) |
460 | { | |
461 | for (; doctor; doctor = doctor->next) | |
462 | if (is_same_net(doctor->in, *addr, doctor->mask)) | |
463 | { | |
464 | addr->s_addr &= ~doctor->mask.s_addr; | |
465 | addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); | |
466 | /* Since we munged the data, the server it came from is no longer authoritative */ | |
fd9fa481 SK |
467 | header->aa = 0; |
468 | break; | |
469 | } | |
470 | } | |
471 | ||
0a852541 | 472 | static int find_soa(HEADER *header, struct doctor *doctor, unsigned int qlen) |
9e4abcb5 SK |
473 | { |
474 | unsigned char *p; | |
9e4abcb5 | 475 | int qtype, qclass, rdlen; |
fd9fa481 SK |
476 | unsigned long ttl, minttl = ULONG_MAX; |
477 | int i, found_soa = 0; | |
9e4abcb5 | 478 | |
fd9fa481 SK |
479 | /* first move to NS section and find TTL from any SOA section */ |
480 | if (!(p = skip_questions(header, qlen)) || | |
481 | !(p = skip_section(p, ntohs(header->ancount), header, qlen))) | |
482 | return 0; /* bad packet */ | |
9e4abcb5 | 483 | |
9e4abcb5 SK |
484 | for (i=0; i<ntohs(header->nscount); i++) |
485 | { | |
fd9fa481 SK |
486 | if (!(p = skip_name(p, header, qlen))) |
487 | return 0; /* bad packet */ | |
488 | ||
9e4abcb5 SK |
489 | GETSHORT(qtype, p); |
490 | GETSHORT(qclass, p); | |
491 | GETLONG(ttl, p); | |
492 | GETSHORT(rdlen, p); | |
fd9fa481 | 493 | |
9e4abcb5 SK |
494 | if ((qclass == C_IN) && (qtype == T_SOA)) |
495 | { | |
fd9fa481 SK |
496 | found_soa = 1; |
497 | if (ttl < minttl) | |
498 | minttl = ttl; | |
499 | ||
9e4abcb5 | 500 | /* MNAME */ |
fd9fa481 SK |
501 | if (!(p = skip_name(p, header, qlen))) |
502 | return 0; | |
9e4abcb5 | 503 | /* RNAME */ |
fd9fa481 SK |
504 | if (!(p = skip_name(p, header, qlen))) |
505 | return 0; | |
506 | p += 16; /* SERIAL REFRESH RETRY EXPIRE */ | |
507 | ||
9e4abcb5 SK |
508 | GETLONG(ttl, p); /* minTTL */ |
509 | if (ttl < minttl) | |
510 | minttl = ttl; | |
511 | } | |
512 | else | |
513 | p += rdlen; | |
9e4abcb5 | 514 | |
fd9fa481 SK |
515 | if ((unsigned int)(p - (unsigned char *)header) > qlen) |
516 | return 0; /* bad packet */ | |
9e4abcb5 | 517 | } |
fd9fa481 | 518 | |
0a852541 SK |
519 | if (doctor) |
520 | for (i=0; i<ntohs(header->arcount); i++) | |
521 | { | |
522 | if (!(p = skip_name(p, header, qlen))) | |
523 | return 0; /* bad packet */ | |
524 | ||
525 | GETSHORT(qtype, p); | |
526 | GETSHORT(qclass, p); | |
527 | GETLONG(ttl, p); | |
528 | GETSHORT(rdlen, p); | |
529 | ||
530 | if ((qclass == C_IN) && (qtype == T_A)) | |
531 | dns_doctor(header, doctor, (struct in_addr *)p); | |
532 | ||
533 | p += rdlen; | |
534 | ||
535 | if ((unsigned int)(p - (unsigned char *)header) > qlen) | |
536 | return 0; /* bad packet */ | |
537 | } | |
538 | ||
fd9fa481 | 539 | return found_soa ? minttl : 0; |
1cff166d SK |
540 | } |
541 | ||
fd9fa481 SK |
542 | /* Note that the following code can create CNAME chains that don't point to a real record, |
543 | either because of lack of memory, or lack of SOA records. These are treated by the cache code as | |
544 | expired and cleaned out that way. */ | |
545 | void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now, struct daemon *daemon) | |
9e4abcb5 | 546 | { |
fd9fa481 SK |
547 | unsigned char *p, *p1, *endrr; |
548 | int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; | |
0a852541 SK |
549 | unsigned long ttl = 0; |
550 | ||
9e4abcb5 | 551 | cache_start_insert(); |
0a852541 SK |
552 | |
553 | /* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */ | |
554 | if (daemon->doctors) | |
555 | { | |
556 | searched_soa = 1; | |
557 | ttl = find_soa(header, daemon->doctors, qlen); | |
558 | } | |
9e4abcb5 | 559 | |
fd9fa481 SK |
560 | /* go through the questions. */ |
561 | p = (unsigned char *)(header+1); | |
9e4abcb5 | 562 | |
fd9fa481 | 563 | for (i = 0; i<ntohs(header->qdcount); i++) |
9e4abcb5 | 564 | { |
fd9fa481 SK |
565 | int found = 0, cname_count = 5; |
566 | struct crec *cpp = NULL; | |
567 | int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0; | |
0a852541 | 568 | unsigned long cttl = ULONG_MAX, attl; |
fd9fa481 | 569 | |
9e4abcb5 SK |
570 | if (!extract_name(header, qlen, &p, name, 1)) |
571 | return; /* bad packet */ | |
fd9fa481 | 572 | |
9e4abcb5 SK |
573 | GETSHORT(qtype, p); |
574 | GETSHORT(qclass, p); | |
9e4abcb5 SK |
575 | |
576 | if (qclass != C_IN) | |
fd9fa481 | 577 | continue; |
9e4abcb5 | 578 | |
fd9fa481 SK |
579 | /* PTRs: we chase CNAMEs here, since we have no way to |
580 | represent them in the cache. */ | |
581 | if (qtype == T_PTR) | |
582 | { | |
9e4abcb5 SK |
583 | struct all_addr addr; |
584 | int name_encoding = in_arpa_name_2_addr(name, &addr); | |
fd9fa481 SK |
585 | |
586 | if (!name_encoding) | |
587 | continue; | |
588 | ||
589 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 590 | { |
fd9fa481 SK |
591 | cname_loop: |
592 | if (!(p1 = skip_questions(header, qlen))) | |
593 | return; | |
594 | ||
595 | for (j = 0; j<ntohs(header->ancount); j++) | |
596 | { | |
597 | if (!(res = extract_name(header, qlen, &p1, name, 0))) | |
598 | return; /* bad packet */ | |
599 | ||
600 | GETSHORT(aqtype, p1); | |
601 | GETSHORT(aqclass, p1); | |
602 | GETLONG(attl, p1); | |
603 | GETSHORT(ardlen, p1); | |
604 | endrr = p1+ardlen; | |
605 | ||
606 | /* TTL of record is minimum of CNAMES and PTR */ | |
607 | if (attl < cttl) | |
608 | cttl = attl; | |
609 | ||
610 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) | |
611 | { | |
612 | if (!extract_name(header, qlen, &p1, name, 1)) | |
613 | return; | |
614 | ||
615 | if (aqtype == T_CNAME) | |
616 | { | |
617 | if (!cname_count--) | |
618 | return; /* looped CNAMES */ | |
619 | goto cname_loop; | |
620 | } | |
621 | ||
622 | cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE); | |
623 | found = 1; | |
624 | } | |
625 | ||
626 | p1 = endrr; | |
627 | if ((unsigned int)(p1 - (unsigned char *)header) > qlen) | |
628 | return; /* bad packet */ | |
629 | } | |
630 | } | |
631 | ||
632 | if (!found && !(daemon->options & OPT_NO_NEG)) | |
633 | { | |
634 | if (!searched_soa) | |
635 | { | |
636 | searched_soa = 1; | |
0a852541 | 637 | ttl = find_soa(header, NULL, qlen); |
fd9fa481 SK |
638 | } |
639 | if (ttl) | |
640 | cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); | |
9e4abcb5 SK |
641 | } |
642 | } | |
fd9fa481 | 643 | else |
9e4abcb5 | 644 | { |
fd9fa481 SK |
645 | /* everything other than PTR */ |
646 | struct crec *newc; | |
647 | ||
648 | if (qtype == T_A) | |
649 | flags |= F_IPV4; | |
650 | #ifdef HAVE_IPV6 | |
651 | else if (qtype == T_AAAA) | |
652 | flags |= F_IPV6; | |
653 | #endif | |
654 | else | |
655 | continue; | |
656 | ||
657 | if (!(flags & F_NXDOMAIN)) | |
9e4abcb5 | 658 | { |
fd9fa481 SK |
659 | cname_loop1: |
660 | if (!(p1 = skip_questions(header, qlen))) | |
661 | return; | |
9e4abcb5 | 662 | |
fd9fa481 | 663 | for (j = 0; j<ntohs(header->ancount); j++) |
9e4abcb5 | 664 | { |
fd9fa481 SK |
665 | if (!(res = extract_name(header, qlen, &p1, name, 0))) |
666 | return; /* bad packet */ | |
667 | ||
668 | GETSHORT(aqtype, p1); | |
669 | GETSHORT(aqclass, p1); | |
670 | GETLONG(attl, p1); | |
671 | GETSHORT(ardlen, p1); | |
672 | endrr = p1+ardlen; | |
673 | ||
674 | if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) | |
675 | { | |
676 | if (aqtype == T_CNAME) | |
677 | { | |
678 | if (!cname_count--) | |
679 | return; /* looped CNAMES */ | |
680 | newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD); | |
26128d27 | 681 | if (newc && cpp) |
fd9fa481 SK |
682 | { |
683 | cpp->addr.cname.cache = newc; | |
684 | cpp->addr.cname.uid = newc->uid; | |
685 | } | |
9e4abcb5 | 686 | |
fd9fa481 SK |
687 | cpp = newc; |
688 | if (attl < cttl) | |
689 | cttl = attl; | |
690 | ||
691 | if (!extract_name(header, qlen, &p1, name, 1)) | |
692 | return; | |
693 | goto cname_loop1; | |
694 | } | |
695 | else | |
696 | { | |
697 | found = 1; | |
698 | if (aqtype == T_A) | |
699 | dns_doctor(header, daemon->doctors, (struct in_addr *)p1); | |
700 | newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD); | |
26128d27 | 701 | if (newc && cpp) |
fd9fa481 SK |
702 | { |
703 | cpp->addr.cname.cache = newc; | |
704 | cpp->addr.cname.uid = newc->uid; | |
705 | } | |
706 | cpp = NULL; | |
707 | } | |
708 | } | |
709 | ||
710 | p1 = endrr; | |
711 | if ((unsigned int)(p1 - (unsigned char *)header) > qlen) | |
712 | return; /* bad packet */ | |
713 | } | |
714 | } | |
715 | ||
716 | if (!found && !(daemon->options & OPT_NO_NEG)) | |
717 | { | |
718 | if (!searched_soa) | |
1cff166d | 719 | { |
fd9fa481 | 720 | searched_soa = 1; |
0a852541 | 721 | ttl = find_soa(header, NULL, qlen); |
1cff166d | 722 | } |
fd9fa481 SK |
723 | /* If there's no SOA to get the TTL from, but there is a CNAME |
724 | pointing at this, inherit it's TTL */ | |
725 | if (ttl || cpp) | |
9e4abcb5 | 726 | { |
fd9fa481 | 727 | newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags); |
26128d27 | 728 | if (newc && cpp) |
9e4abcb5 | 729 | { |
fd9fa481 SK |
730 | cpp->addr.cname.cache = newc; |
731 | cpp->addr.cname.uid = newc->uid; | |
732 | } | |
9e4abcb5 | 733 | } |
9e4abcb5 | 734 | } |
fd9fa481 | 735 | } |
9e4abcb5 | 736 | } |
fd9fa481 | 737 | |
9e4abcb5 SK |
738 | cache_end_insert(); |
739 | } | |
740 | ||
741 | /* If the packet holds exactly one query | |
742 | return 1 and leave the name from the query in name. */ | |
743 | ||
c1bb8504 | 744 | unsigned short extract_request(HEADER *header,unsigned int qlen, char *name, unsigned short *typep) |
9e4abcb5 SK |
745 | { |
746 | unsigned char *p = (unsigned char *)(header+1); | |
747 | int qtype, qclass; | |
748 | ||
c1bb8504 SK |
749 | if (typep) |
750 | *typep = 0; | |
751 | ||
9e4abcb5 SK |
752 | if (ntohs(header->qdcount) != 1 || header->opcode != QUERY) |
753 | return 0; /* must be exactly one query. */ | |
754 | ||
755 | if (!extract_name(header, qlen, &p, name, 1)) | |
756 | return 0; /* bad packet */ | |
757 | ||
758 | GETSHORT(qtype, p); | |
759 | GETSHORT(qclass, p); | |
760 | ||
0a852541 SK |
761 | if (typep) |
762 | *typep = qtype; | |
763 | ||
9e4abcb5 SK |
764 | if (qclass == C_IN) |
765 | { | |
766 | if (qtype == T_A) | |
767 | return F_IPV4; | |
768 | if (qtype == T_AAAA) | |
769 | return F_IPV6; | |
770 | if (qtype == T_ANY) | |
771 | return F_IPV4 | F_IPV6; | |
772 | } | |
773 | ||
774 | return F_QUERY; | |
775 | } | |
776 | ||
777 | ||
778 | int setup_reply(HEADER *header, unsigned int qlen, | |
779 | struct all_addr *addrp, unsigned short flags, unsigned long ttl) | |
780 | { | |
781 | unsigned char *p = skip_questions(header, qlen); | |
782 | ||
783 | header->qr = 1; /* response */ | |
784 | header->aa = 0; /* authoritive */ | |
785 | header->ra = 1; /* recursion if available */ | |
786 | header->tc = 0; /* not truncated */ | |
787 | header->nscount = htons(0); | |
788 | header->arcount = htons(0); | |
feba5c1d | 789 | header->ancount = htons(0); /* no answers unless changed below */ |
9e4abcb5 SK |
790 | if (flags == F_NEG) |
791 | header->rcode = SERVFAIL; /* couldn't get memory */ | |
44a2a316 | 792 | else if (flags == F_NOERR || flags == F_QUERY) |
9e4abcb5 SK |
793 | header->rcode = NOERROR; /* empty domain */ |
794 | else if (flags == F_NXDOMAIN) | |
795 | header->rcode = NXDOMAIN; | |
796 | else if (p && flags == F_IPV4) | |
797 | { /* we know the address */ | |
798 | header->rcode = NOERROR; | |
799 | header->ancount = htons(1); | |
800 | header->aa = 1; | |
f6b7dc47 | 801 | add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp); |
9e4abcb5 SK |
802 | } |
803 | #ifdef HAVE_IPV6 | |
804 | else if (p && flags == F_IPV6) | |
805 | { | |
806 | header->rcode = NOERROR; | |
807 | header->ancount = htons(1); | |
808 | header->aa = 1; | |
f6b7dc47 | 809 | add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp); |
9e4abcb5 SK |
810 | } |
811 | #endif | |
812 | else /* nowhere to forward to */ | |
813 | header->rcode = REFUSED; | |
814 | ||
815 | return p - (unsigned char *)header; | |
816 | } | |
36717eee SK |
817 | |
818 | /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ | |
f6b7dc47 | 819 | int check_for_local_domain(char *name, time_t now, struct daemon *daemon) |
36717eee SK |
820 | { |
821 | struct crec *crecp; | |
0a852541 SK |
822 | struct mx_srv_record *mx; |
823 | struct txt_record *txt; | |
824 | ||
26128d27 | 825 | if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) && |
36717eee SK |
826 | (crecp->flags & (F_HOSTS | F_DHCP))) |
827 | return 1; | |
828 | ||
f6b7dc47 | 829 | for (mx = daemon->mxnames; mx; mx = mx->next) |
0a852541 | 830 | if (hostname_isequal(name, mx->name)) |
36717eee | 831 | return 1; |
f6b7dc47 | 832 | |
0a852541 SK |
833 | for (txt = daemon->txt; txt; txt = txt->next) |
834 | if (hostname_isequal(name, txt->name)) | |
f6b7dc47 | 835 | return 1; |
0a852541 | 836 | |
36717eee SK |
837 | return 0; |
838 | } | |
9e4abcb5 SK |
839 | |
840 | /* Is the packet a reply with the answer address equal to addr? | |
841 | If so mung is into an NXDOMAIN reply and also put that information | |
842 | in the cache. */ | |
843 | int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name, | |
844 | struct bogus_addr *baddr, time_t now) | |
845 | { | |
846 | unsigned char *p; | |
847 | int i, qtype, qclass, rdlen; | |
848 | unsigned long ttl; | |
849 | struct bogus_addr *baddrp; | |
850 | ||
851 | /* skip over questions */ | |
852 | if (!(p = skip_questions(header, qlen))) | |
853 | return 0; /* bad packet */ | |
854 | ||
855 | for (i=0; i<ntohs(header->ancount); i++) | |
856 | { | |
857 | if (!extract_name(header, qlen, &p, name, 1)) | |
858 | return 0; /* bad packet */ | |
859 | ||
860 | GETSHORT(qtype, p); | |
861 | GETSHORT(qclass, p); | |
862 | GETLONG(ttl, p); | |
863 | GETSHORT(rdlen, p); | |
864 | ||
865 | if (qclass == C_IN && qtype == T_A) | |
866 | for (baddrp = baddr; baddrp; baddrp = baddrp->next) | |
867 | if (memcmp(&baddrp->addr, p, INADDRSZ) == 0) | |
868 | { | |
fd9fa481 SK |
869 | /* Found a bogus address. Insert that info here, since there no SOA record |
870 | to get the ttl from in the normal processing */ | |
9e4abcb5 SK |
871 | cache_start_insert(); |
872 | cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG); | |
873 | cache_end_insert(); | |
874 | ||
875 | return 1; | |
876 | } | |
877 | ||
878 | p += rdlen; | |
879 | } | |
880 | ||
881 | return 0; | |
882 | } | |
883 | ||
f6b7dc47 SK |
884 | static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, |
885 | unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...) | |
886 | { | |
887 | va_list ap; | |
888 | unsigned char *sav, *p = *pp; | |
889 | int j; | |
890 | unsigned short usval; | |
891 | long lval; | |
892 | char *sval; | |
893 | ||
894 | if (truncp && *truncp) | |
895 | return 0; | |
896 | ||
897 | PUTSHORT(nameoffset | 0xc000, p); | |
898 | PUTSHORT(type, p); | |
899 | PUTSHORT(class, p); | |
900 | PUTLONG(ttl, p); /* TTL */ | |
901 | ||
902 | sav = p; /* Save pointer to RDLength field */ | |
903 | PUTSHORT(0, p); /* Placeholder RDLength */ | |
904 | ||
905 | va_start(ap, format); /* make ap point to 1st unamed argument */ | |
906 | ||
907 | for (; *format; format++) | |
908 | switch (*format) | |
909 | { | |
910 | #ifdef HAVE_IPV6 | |
911 | case '6': | |
912 | sval = va_arg(ap, char *); | |
913 | memcpy(p, sval, IN6ADDRSZ); | |
914 | p += IN6ADDRSZ; | |
915 | break; | |
916 | #endif | |
917 | ||
918 | case '4': | |
919 | sval = va_arg(ap, char *); | |
920 | memcpy(p, sval, INADDRSZ); | |
921 | p += INADDRSZ; | |
922 | break; | |
923 | ||
924 | case 's': | |
925 | usval = va_arg(ap, int); | |
926 | PUTSHORT(usval, p); | |
927 | break; | |
928 | ||
929 | case 'l': | |
930 | lval = va_arg(ap, long); | |
931 | PUTLONG(lval, p); | |
932 | break; | |
933 | ||
934 | case 'd': | |
935 | /* get domain-name answer arg and store it in RDATA field */ | |
0a852541 SK |
936 | if (offset) |
937 | *offset = p - (unsigned char *)header; | |
f6b7dc47 SK |
938 | sval = va_arg(ap, char *); |
939 | while (sval && *sval) | |
940 | { | |
941 | unsigned char *cp = p++; | |
942 | for (j = 0; *sval && (*sval != '.'); sval++, j++) | |
943 | *p++ = *sval; | |
944 | *cp = j; | |
945 | if (*sval) | |
946 | sval++; | |
947 | } | |
948 | *p++ = 0; | |
949 | break; | |
950 | ||
951 | case 't': | |
0a852541 | 952 | usval = va_arg(ap, int); |
f6b7dc47 | 953 | sval = va_arg(ap, char *); |
0a852541 SK |
954 | memcpy(p, sval, usval); |
955 | p += usval; | |
f6b7dc47 SK |
956 | break; |
957 | } | |
958 | ||
959 | va_end(ap); /* clean up variable argument pointer */ | |
960 | ||
961 | j = p - sav - 2; | |
962 | PUTSHORT(j, sav); /* Now, store real RDLength */ | |
963 | ||
f6b7dc47 SK |
964 | /* check for overflow of buffer */ |
965 | if (limit && ((unsigned char *)limit - p) < 0) | |
966 | { | |
967 | if (truncp) | |
968 | *truncp = 1; | |
969 | return 0; | |
970 | } | |
971 | ||
972 | *pp = p; | |
973 | return 1; | |
974 | } | |
975 | ||
9e4abcb5 | 976 | /* return zero if we can't answer from cache, or packet size if we can */ |
f6b7dc47 SK |
977 | int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, |
978 | struct in_addr local_addr, struct in_addr local_netmask, time_t now) | |
9e4abcb5 | 979 | { |
3be34541 | 980 | char *name = daemon->namebuff; |
feba5c1d | 981 | unsigned char *p, *ansp, *pheader; |
9e4abcb5 SK |
982 | int qtype, qclass, is_arpa; |
983 | struct all_addr addr; | |
984 | unsigned int nameoffset; | |
feba5c1d SK |
985 | unsigned short flag; |
986 | int qdcount = ntohs(header->qdcount); | |
0a852541 | 987 | int q, ans, anscount = 0, addncount = 0; |
feba5c1d | 988 | int dryrun = 0, sec_reqd = 0; |
9e4abcb5 | 989 | struct crec *crecp; |
f6b7dc47 | 990 | int nxdomain = 0, auth = 1, trunc = 0; |
0a852541 SK |
991 | struct mx_srv_record *rec; |
992 | ||
9e4abcb5 SK |
993 | if (!qdcount || header->opcode != QUERY ) |
994 | return 0; | |
995 | ||
feba5c1d SK |
996 | /* If there is an RFC2671 pseudoheader then it will be overwritten by |
997 | partial replies, so we have to do a dry run to see if we can answer | |
998 | the query. We check to see if the do bit is set, if so we always | |
999 | forward rather than answering from the cache, which doesn't include | |
1000 | security information. */ | |
1001 | ||
36717eee | 1002 | if (find_pseudoheader(header, qlen, NULL, &pheader)) |
feba5c1d SK |
1003 | { |
1004 | unsigned short udpsz, ext_rcode, flags; | |
1005 | unsigned char *psave = pheader; | |
1006 | ||
1007 | GETSHORT(udpsz, pheader); | |
1008 | GETSHORT(ext_rcode, pheader); | |
1009 | GETSHORT(flags, pheader); | |
1010 | ||
1011 | sec_reqd = flags & 0x8000; /* do bit */ | |
1012 | ||
1013 | /* If our client is advertising a larger UDP packet size | |
1014 | than we allow, trim it so that we don't get an overlarge | |
1015 | response from upstream */ | |
1016 | ||
3be34541 SK |
1017 | if (udpsz > daemon->edns_pktsz) |
1018 | PUTSHORT(daemon->edns_pktsz, psave); | |
feba5c1d SK |
1019 | |
1020 | dryrun = 1; | |
1021 | } | |
1022 | ||
0a852541 SK |
1023 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1024 | rec->offset = 0; | |
1025 | ||
feba5c1d | 1026 | rerun: |
9e4abcb5 SK |
1027 | /* determine end of question section (we put answers there) */ |
1028 | if (!(ansp = skip_questions(header, qlen))) | |
1029 | return 0; /* bad packet */ | |
1030 | ||
1031 | /* now process each question, answers go in RRs after the question */ | |
1032 | p = (unsigned char *)(header+1); | |
feba5c1d | 1033 | |
9e4abcb5 SK |
1034 | for (q=0; q<qdcount; q++) |
1035 | { | |
1036 | /* save pointer to name for copying into answers */ | |
1037 | nameoffset = p - (unsigned char *)header; | |
1038 | ||
1039 | /* now extract name as .-concatenated string into name */ | |
1040 | if (!extract_name(header, qlen, &p, name, 1)) | |
1041 | return 0; /* bad packet */ | |
1042 | ||
1043 | /* see if it's w.z.y.z.in-addr.arpa format */ | |
1044 | ||
1045 | is_arpa = in_arpa_name_2_addr(name, &addr); | |
1046 | ||
1047 | GETSHORT(qtype, p); | |
1048 | GETSHORT(qclass, p); | |
1049 | ||
1050 | ans = 0; /* have we answered this question */ | |
1051 | ||
0a852541 | 1052 | if (qtype == T_TXT || qtype == T_ANY) |
9e4abcb5 | 1053 | { |
0a852541 SK |
1054 | struct txt_record *t; |
1055 | for(t = daemon->txt; t ; t = t->next) | |
9e4abcb5 | 1056 | { |
0a852541 SK |
1057 | if (t->class == qclass && hostname_isequal(name, t->name)) |
1058 | { | |
1059 | ans = 1; | |
1060 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0); | |
1061 | if (!dryrun && | |
1062 | add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL, | |
1063 | T_TXT, t->class, "t", t->len, t->txt)) | |
1064 | anscount++; | |
1065 | } | |
9e4abcb5 | 1066 | } |
0a852541 | 1067 | } |
f6b7dc47 | 1068 | |
0a852541 | 1069 | if (qclass == C_IN) |
9e4abcb5 | 1070 | { |
f6b7dc47 | 1071 | if (qtype == T_PTR || qtype == T_ANY) |
c1bb8504 | 1072 | { |
f6b7dc47 SK |
1073 | if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) |
1074 | { | |
1075 | if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr)) | |
1076 | { | |
1077 | /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */ | |
1078 | ans = 1; | |
1079 | nxdomain = 1; | |
1080 | if (!dryrun) | |
1081 | log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0); | |
9e4abcb5 | 1082 | } |
9e4abcb5 | 1083 | } |
f6b7dc47 SK |
1084 | else do |
1085 | { | |
1086 | /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ | |
1087 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1088 | continue; | |
1089 | ||
1090 | if (crecp->flags & F_NEG) | |
feba5c1d | 1091 | { |
f6b7dc47 SK |
1092 | ans = 1; |
1093 | auth = 0; | |
1094 | if (crecp->flags & F_NXDOMAIN) | |
1095 | nxdomain = 1; | |
1096 | if (!dryrun) | |
1097 | log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0); | |
1098 | } | |
1099 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) | |
1100 | { | |
1101 | ans = 1; | |
1102 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1103 | auth = 0; | |
1104 | if (!dryrun) | |
1105 | { | |
1106 | unsigned long ttl; | |
1107 | /* Return 0 ttl for DHCP entries, which might change | |
1108 | before the lease expires. */ | |
1109 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
1110 | ttl = daemon->local_ttl; | |
1111 | else | |
1112 | ttl = crecp->ttd - now; | |
1113 | ||
1114 | log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, | |
1115 | 0, daemon->addn_hosts, crecp->uid); | |
1116 | ||
1117 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, | |
1118 | T_PTR, C_IN, "d", cache_get_name(crecp))) | |
1119 | anscount++; | |
1120 | } | |
1121 | } | |
1122 | } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); | |
1123 | } | |
1124 | ||
1125 | for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) | |
1126 | { | |
1127 | unsigned short type = T_A; | |
1128 | ||
1129 | if (flag == F_IPV6) | |
feba5c1d | 1130 | #ifdef HAVE_IPV6 |
f6b7dc47 | 1131 | type = T_AAAA; |
feba5c1d | 1132 | #else |
f6b7dc47 | 1133 | break; |
feba5c1d | 1134 | #endif |
f6b7dc47 SK |
1135 | |
1136 | if (qtype != type && qtype != T_ANY) | |
1137 | continue; | |
1138 | ||
1139 | cname_restart: | |
1140 | if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME))) | |
1141 | { | |
1142 | int localise = 0; | |
feba5c1d | 1143 | |
f6b7dc47 SK |
1144 | /* See if a putative address is on the network from which we recieved |
1145 | the query, is so we'll filter other answers. */ | |
1146 | if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4) | |
1147 | { | |
1148 | struct crec *save = crecp; | |
1149 | do { | |
1150 | if ((crecp->flags & F_HOSTS) && | |
1151 | is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1152 | { | |
1153 | localise = 1; | |
1154 | break; | |
1155 | } | |
1156 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); | |
1157 | crecp = save; | |
1158 | } | |
1159 | ||
1160 | do | |
9e4abcb5 | 1161 | { |
26128d27 SK |
1162 | /* don't answer wildcard queries with data not from /etc/hosts |
1163 | or DHCP leases */ | |
1164 | if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) | |
1165 | break; | |
1166 | ||
fd9fa481 SK |
1167 | if (crecp->flags & F_CNAME) |
1168 | { | |
fd9fa481 SK |
1169 | if (!dryrun) |
1170 | { | |
fd9fa481 | 1171 | log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid); |
f6b7dc47 SK |
1172 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset, |
1173 | T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache))) | |
1174 | anscount++; | |
fd9fa481 | 1175 | } |
f6b7dc47 | 1176 | |
fd9fa481 SK |
1177 | strcpy(name, cache_get_name(crecp->addr.cname.cache)); |
1178 | goto cname_restart; | |
1179 | } | |
f6b7dc47 | 1180 | |
9e4abcb5 SK |
1181 | if (crecp->flags & F_NEG) |
1182 | { | |
feba5c1d | 1183 | ans = 1; |
f6b7dc47 SK |
1184 | auth = 0; |
1185 | if (crecp->flags & F_NXDOMAIN) | |
1186 | nxdomain = 1; | |
feba5c1d | 1187 | if (!dryrun) |
f6b7dc47 | 1188 | log_query(crecp->flags, name, NULL, 0, NULL, 0); |
9e4abcb5 | 1189 | } |
feba5c1d | 1190 | else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) |
9e4abcb5 | 1191 | { |
f6b7dc47 SK |
1192 | /* If we are returning local answers depending on network, |
1193 | filter here. */ | |
1194 | if (localise && | |
1195 | (crecp->flags & F_HOSTS) && | |
1196 | !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask)) | |
1197 | continue; | |
1198 | ||
1199 | if (!(crecp->flags & (F_HOSTS | F_DHCP))) | |
1200 | auth = 0; | |
1201 | ||
feba5c1d SK |
1202 | ans = 1; |
1203 | if (!dryrun) | |
1204 | { | |
1205 | unsigned long ttl; | |
1206 | ||
1207 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
3be34541 | 1208 | ttl = daemon->local_ttl; |
feba5c1d SK |
1209 | else |
1210 | ttl = crecp->ttd - now; | |
1211 | ||
fd9fa481 SK |
1212 | log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr, |
1213 | 0, daemon->addn_hosts, crecp->uid); | |
feba5c1d | 1214 | |
f6b7dc47 SK |
1215 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN, |
1216 | type == T_A ? "4" : "6", &crecp->addr)) | |
1217 | anscount++; | |
feba5c1d | 1218 | } |
9e4abcb5 | 1219 | } |
f6b7dc47 | 1220 | } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME))); |
9e4abcb5 | 1221 | } |
f6b7dc47 SK |
1222 | } |
1223 | ||
1224 | if (qtype == T_MX || qtype == T_ANY) | |
1225 | { | |
1226 | int found = 0; | |
0a852541 SK |
1227 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1228 | if (!rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1229 | { |
1230 | ans = found = 1; | |
1231 | if (!dryrun) | |
feba5c1d | 1232 | { |
0a852541 | 1233 | int offset; |
f6b7dc47 | 1234 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0); |
0a852541 SK |
1235 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, |
1236 | &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) | |
1237 | { | |
1238 | anscount++; | |
1239 | if (rec->target) | |
1240 | rec->offset = offset; | |
1241 | } | |
feba5c1d | 1242 | } |
f6b7dc47 SK |
1243 | } |
1244 | ||
1245 | if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) && | |
1246 | cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP)) | |
1247 | { | |
1248 | ans = 1; | |
1249 | if (!dryrun) | |
1250 | { | |
1251 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0); | |
1252 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, | |
1253 | T_MX, C_IN, "sd", 1, | |
1254 | (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget)) | |
1255 | anscount++; | |
9e4abcb5 SK |
1256 | } |
1257 | } | |
f6b7dc47 SK |
1258 | } |
1259 | ||
1260 | if (qtype == T_SRV || qtype == T_ANY) | |
1261 | { | |
1262 | int found = 0; | |
9e4abcb5 | 1263 | |
0a852541 SK |
1264 | for (rec = daemon->mxnames; rec; rec = rec->next) |
1265 | if (rec->issrv && hostname_isequal(name, rec->name)) | |
f6b7dc47 SK |
1266 | { |
1267 | found = ans = 1; | |
1268 | if (!dryrun) | |
1269 | { | |
0a852541 | 1270 | int offset; |
f6b7dc47 SK |
1271 | log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0); |
1272 | if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, | |
0a852541 SK |
1273 | &offset, T_SRV, C_IN, "sssd", |
1274 | rec->priority, rec->weight, rec->srvport, rec->target)) | |
1275 | { | |
1276 | anscount++; | |
1277 | if (rec->target) | |
1278 | rec->offset = offset; | |
1279 | } | |
f6b7dc47 SK |
1280 | } |
1281 | } | |
feba5c1d | 1282 | |
f6b7dc47 SK |
1283 | if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) |
1284 | { | |
1285 | ans = 1; | |
1286 | if (!dryrun) | |
1287 | log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0); | |
1288 | } | |
1289 | } | |
1290 | ||
1291 | if (qtype == T_MAILB) | |
1292 | ans = 1, nxdomain = 1; | |
1293 | ||
1294 | if (qtype == T_SOA && (daemon->options & OPT_FILTER)) | |
1295 | { | |
1296 | ans = 1; | |
1297 | if (!dryrun) | |
1298 | log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0); | |
feba5c1d | 1299 | } |
9e4abcb5 | 1300 | } |
f6b7dc47 SK |
1301 | |
1302 | if (!ans) | |
9e4abcb5 | 1303 | return 0; /* failed to answer a question */ |
feba5c1d | 1304 | } |
f6b7dc47 | 1305 | |
feba5c1d SK |
1306 | if (dryrun) |
1307 | { | |
1308 | dryrun = 0; | |
1309 | goto rerun; | |
9e4abcb5 SK |
1310 | } |
1311 | ||
0a852541 SK |
1312 | /* create an additional data section, for stuff in SRV and MX record replies. */ |
1313 | for (rec = daemon->mxnames; rec; rec = rec->next) | |
1314 | if (rec->offset != 0) | |
1315 | { | |
1316 | /* squash dupes */ | |
1317 | struct mx_srv_record *tmp; | |
1318 | for (tmp = rec->next; tmp; tmp = tmp->next) | |
1319 | if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target)) | |
1320 | tmp->offset = 0; | |
1321 | ||
1322 | crecp = NULL; | |
1323 | while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6))) | |
1324 | { | |
1325 | unsigned long ttl; | |
1326 | #ifdef HAVE_IPV6 | |
1327 | int type = crecp->flags & F_IPV4 ? T_A : T_AAAA; | |
1328 | #else | |
1329 | int type = T_A; | |
1330 | #endif | |
1331 | if (crecp->flags & F_NEG) | |
1332 | continue; | |
1333 | ||
1334 | if (crecp->flags & (F_IMMORTAL | F_DHCP)) | |
1335 | ttl = daemon->local_ttl; | |
1336 | else | |
1337 | ttl = crecp->ttd - now; | |
1338 | ||
1339 | if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN, | |
1340 | crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) | |
1341 | addncount++; | |
1342 | } | |
1343 | } | |
1344 | ||
9e4abcb5 SK |
1345 | /* done all questions, set up header and return length of result */ |
1346 | header->qr = 1; /* response */ | |
1347 | header->aa = auth; /* authoritive - only hosts and DHCP derived names. */ | |
1348 | header->ra = 1; /* recursion if available */ | |
f6b7dc47 | 1349 | header->tc = trunc; /* truncation */ |
9e4abcb5 SK |
1350 | if (anscount == 0 && nxdomain) |
1351 | header->rcode = NXDOMAIN; | |
1352 | else | |
1353 | header->rcode = NOERROR; /* no error */ | |
1354 | header->ancount = htons(anscount); | |
1355 | header->nscount = htons(0); | |
0a852541 | 1356 | header->arcount = htons(addncount); |
9e4abcb5 SK |
1357 | return ansp - (unsigned char *)header; |
1358 | } | |
1359 | ||
1360 | ||
1361 | ||
1362 | ||
1363 |