blob: 6293c9e4941275e72f46f354ecedb31f198565ed [file] [log] [blame]
Simon Kelley824af852008-02-12 20:43:05 +00001/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
17#include "dnsmasq.h"
18
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000019static int add_resource_record(HEADER *header, char *limit, int *truncp,
20 unsigned int nameoffset, unsigned char **pp,
Simon Kelley3d8df262005-08-29 12:19:27 +010021 unsigned long ttl, unsigned int *offset, unsigned short type,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000022 unsigned short class, char *format, ...);
23
Simon Kelleycdeda282006-03-16 20:16:06 +000024static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
Simon Kelley3d8df262005-08-29 12:19:27 +010025 char *name, int isExtract)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000026{
Simon Kelley3d8df262005-08-29 12:19:27 +010027 unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000028 unsigned int j, l, hops = 0;
29 int retvalue = 1;
30
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000031 if (isExtract)
32 *cp = 0;
33
Simon Kelley9e4abcb2004-01-22 19:47:41 +000034 while ((l = *p++))
35 {
36 unsigned int label_type = l & 0xc0;
37 if (label_type == 0xc0) /* pointer */
38 {
Simon Kelley824af852008-02-12 20:43:05 +000039 if ((size_t)(p - (unsigned char *)header) >= plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000040 return 0;
41
42 /* get offset */
43 l = (l&0x3f) << 8;
44 l |= *p++;
Simon Kelleycdeda282006-03-16 20:16:06 +000045 if (l >= plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000046 return 0;
47
48 if (!p1) /* first jump, save location to go back to */
49 p1 = p;
50
51 hops++; /* break malicious infinite loops */
52 if (hops > 255)
53 return 0;
54
55 p = l + (unsigned char *)header;
56 }
57 else if (label_type == 0x80)
58 return 0; /* reserved */
59 else if (label_type == 0x40)
60 { /* ELT */
61 unsigned int count, digs;
62
63 if ((l & 0x3f) != 1)
64 return 0; /* we only understand bitstrings */
65
66 if (!isExtract)
67 return 0; /* Cannot compare bitsrings */
68
69 count = *p++;
70 if (count == 0)
71 count = 256;
72 digs = ((count-1)>>2)+1;
73
74 /* output is \[x<hex>/siz]. which is digs+9 chars */
Simon Kelley3d8df262005-08-29 12:19:27 +010075 if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000076 return 0;
Simon Kelley824af852008-02-12 20:43:05 +000077 if ((size_t)(p - (unsigned char *)header + ((count-1)>>3)) >= plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000078 return 0;
79
80 *cp++ = '\\';
81 *cp++ = '[';
82 *cp++ = 'x';
83 for (j=0; j<digs; j++)
84 {
85 unsigned int dig;
86 if (j%2 == 0)
87 dig = *p >> 4;
88 else
89 dig = *p++ & 0x0f;
90
91 *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
92 }
Simon Kelley3d8df262005-08-29 12:19:27 +010093 cp += sprintf((char *)cp, "/%d]", count);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000094 /* do this here to overwrite the zero char from sprintf */
95 *cp++ = '.';
96 }
97 else
98 { /* label_type = 0 -> label. */
Simon Kelley3d8df262005-08-29 12:19:27 +010099 if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000100 return 0;
Simon Kelley824af852008-02-12 20:43:05 +0000101 if ((size_t)(p - (unsigned char *)header) >= plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000102 return 0;
103 for(j=0; j<l; j++, p++)
104 if (isExtract)
105 {
106 if (legal_char(*p))
107 *cp++ = *p;
108 else
109 return 0;
110 }
111 else
112 {
113 unsigned char c1 = *cp, c2 = *p;
114
115 if (c1 == 0)
116 retvalue = 2;
117 else
118 {
119 cp++;
120 if (c1 >= 'A' && c1 <= 'Z')
121 c1 += 'a' - 'A';
122 if (c2 >= 'A' && c2 <= 'Z')
123 c2 += 'a' - 'A';
124
125 if (c1 != c2)
126 retvalue = 2;
127 }
128 }
129
130 if (isExtract)
131 *cp++ = '.';
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000132 else if (*cp != 0 && *cp++ != '.')
133 retvalue = 2;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000134 }
135
136 if ((unsigned int)(p - (unsigned char *)header) >= plen)
137 return 0;
138 }
139
140 if (isExtract)
Simon Kelley309331f2006-04-22 15:05:01 +0100141 {
142 if (cp != (unsigned char *)name)
143 cp--;
144 *cp = 0; /* terminate: lose final period */
145 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000146 else if (*cp != 0)
147 retvalue = 2;
148
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000149 if (p1) /* we jumped via compression */
150 *pp = p1;
151 else
152 *pp = p;
153
154 return retvalue;
155}
156
157/* Max size of input string (for IPv6) is 75 chars.) */
158#define MAXARPANAME 75
159static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
160{
161 int j;
162 char name[MAXARPANAME+1], *cp1;
163 unsigned char *addr = (unsigned char *)addrp;
164 char *lastchunk = NULL, *penchunk = NULL;
165
166 if (strlen(namein) > MAXARPANAME)
167 return 0;
168
169 memset(addrp, 0, sizeof(struct all_addr));
170
171 /* turn name into a series of asciiz strings */
172 /* j counts no of labels */
173 for(j = 1,cp1 = name; *namein; cp1++, namein++)
174 if (*namein == '.')
175 {
176 penchunk = lastchunk;
177 lastchunk = cp1 + 1;
178 *cp1 = 0;
179 j++;
180 }
181 else
182 *cp1 = *namein;
183
184 *cp1 = 0;
185
186 if (j<3)
187 return 0;
188
189 if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))
190 {
191 /* IP v4 */
192 /* address arives as a name of the form
193 www.xxx.yyy.zzz.in-addr.arpa
194 some of the low order address octets might be missing
195 and should be set to zero. */
196 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
197 {
198 /* check for digits only (weeds out things like
199 50.0/24.67.28.64.in-addr.arpa which are used
200 as CNAME targets according to RFC 2317 */
201 char *cp;
202 for (cp = cp1; *cp; cp++)
203 if (!isdigit((int)*cp))
204 return 0;
205
206 addr[3] = addr[2];
207 addr[2] = addr[1];
208 addr[1] = addr[0];
209 addr[0] = atoi(cp1);
210 }
211
212 return F_IPV4;
213 }
214#ifdef HAVE_IPV6
215 else if (hostname_isequal(penchunk, "ip6") &&
216 (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
217 {
218 /* IP v6:
219 Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
220 or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
221
222 Note that most of these the various reprentations are obsolete and
223 left-over from the many DNS-for-IPv6 wars. We support all the formats
224 that we can since there is no reason not to.
225 */
226
227 if (*name == '\\' && *(name+1) == '[' &&
228 (*(name+2) == 'x' || *(name+2) == 'X'))
229 {
Simon Kelley9e038942008-05-30 20:06:34 +0100230 for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000231 {
232 char xdig[2];
233 xdig[0] = *cp1;
234 xdig[1] = 0;
235 if (j%2)
236 addr[j/2] |= strtol(xdig, NULL, 16);
237 else
238 addr[j/2] = strtol(xdig, NULL, 16) << 4;
239 }
240
241 if (*cp1 == '/' && j == 32)
242 return F_IPV6;
243 }
244 else
245 {
246 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
247 {
248 if (*(cp1+1) || !isxdigit((int)*cp1))
249 return 0;
250
251 for (j = sizeof(struct all_addr)-1; j>0; j--)
252 addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);
253 addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
254 }
255
256 return F_IPV6;
257 }
258 }
259#endif
260
261 return 0;
262}
263
Simon Kelleycdeda282006-03-16 20:16:06 +0000264static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100265{
266 while(1)
267 {
268 unsigned int label_type = (*ansp) & 0xc0;
269
270 if ((unsigned int)(ansp - (unsigned char *)header) >= plen)
271 return NULL;
272
273 if (label_type == 0xc0)
274 {
275 /* pointer for compression. */
276 ansp += 2;
277 break;
278 }
279 else if (label_type == 0x80)
280 return NULL; /* reserved */
281 else if (label_type == 0x40)
282 {
283 /* Extended label type */
284 unsigned int count;
285
286 if (((*ansp++) & 0x3f) != 1)
287 return NULL; /* we only understand bitstrings */
288
289 count = *(ansp++); /* Bits in bitstring */
290
291 if (count == 0) /* count == 0 means 256 bits */
292 ansp += 32;
293 else
294 ansp += ((count-1)>>3)+1;
295 }
296 else
297 { /* label type == 0 Bottom six bits is length */
298 unsigned int len = (*ansp++) & 0x3f;
299 if (len == 0)
300 break; /* zero length label marks the end. */
301
302 ansp += len;
303 }
304 }
305
306 return ansp;
307}
308
Simon Kelleycdeda282006-03-16 20:16:06 +0000309static unsigned char *skip_questions(HEADER *header, size_t plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000310{
Simon Kelley5aabfc72007-08-29 11:24:47 +0100311 int q;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000312 unsigned char *ansp = (unsigned char *)(header+1);
313
Simon Kelley5aabfc72007-08-29 11:24:47 +0100314 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000315 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100316 if (!(ansp = skip_name(ansp, header, plen)))
317 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000318 ansp += 4; /* class and type */
319 }
320 if ((unsigned int)(ansp - (unsigned char *)header) > plen)
321 return NULL;
322
323 return ansp;
324}
325
Simon Kelleycdeda282006-03-16 20:16:06 +0000326static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, size_t plen)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100327{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100328 int i, rdlen;
Simon Kelley36717ee2004-09-20 19:20:58 +0100329
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100330 for (i = 0; i < count; i++)
Simon Kelley36717ee2004-09-20 19:20:58 +0100331 {
332 if (!(ansp = skip_name(ansp, header, plen)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100333 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100334 ansp += 8; /* type, class, TTL */
335 GETSHORT(rdlen, ansp);
336 if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100337 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100338 ansp += rdlen;
339 }
340
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100341 return ansp;
342}
343
Simon Kelley0a852542005-03-23 20:28:59 +0000344/* CRC the question section. This is used to safely detect query
345 retransmision and to detect answers to questions we didn't ask, which
346 might be poisoning attacks. Note that we decode the name rather
347 than CRC the raw bytes, since replies might be compressed differently.
Simon Kelley832af0b2007-01-21 20:01:28 +0000348 We ignore case in the names for the same reason. Return all-ones
349 if there is not question section. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000350unsigned int questions_crc(HEADER *header, size_t plen, char *name)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100351{
Simon Kelley91dccd02005-03-31 17:48:32 +0100352 int q;
353 unsigned int crc = 0xffffffff;
Simon Kelley0a852542005-03-23 20:28:59 +0000354 unsigned char *p1, *p = (unsigned char *)(header+1);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100355
Simon Kelley5aabfc72007-08-29 11:24:47 +0100356 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley0a852542005-03-23 20:28:59 +0000357 {
358 if (!extract_name(header, plen, &p, name, 1))
359 return crc; /* bad packet */
360
Simon Kelley3d8df262005-08-29 12:19:27 +0100361 for (p1 = (unsigned char *)name; *p1; p1++)
Simon Kelley0a852542005-03-23 20:28:59 +0000362 {
363 int i = 8;
364 char c = *p1;
365
366 if (c >= 'A' && c <= 'Z')
367 c += 'a' - 'A';
368
369 crc ^= c << 24;
370 while (i--)
371 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
372 }
373
374 /* CRC the class and type as well */
375 for (p1 = p; p1 < p+4; p1++)
376 {
377 int i = 8;
378 crc ^= *p1 << 24;
379 while (i--)
380 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
381 }
382
383 p += 4;
384 if ((unsigned int)(p - (unsigned char *)header) > plen)
385 return crc; /* bad packet */
386 }
387
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100388 return crc;
389}
390
391
Simon Kelleycdeda282006-03-16 20:16:06 +0000392size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t hlen)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100393{
394 unsigned char *ansp = skip_questions(header, plen);
395
396 if (!ansp)
397 return 0;
398
399 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
400 header, plen)))
401 return 0;
402
Simon Kelley36717ee2004-09-20 19:20:58 +0100403 /* restore pseudoheader */
404 if (pheader && ntohs(header->arcount) == 0)
405 {
406 /* must use memmove, may overlap */
407 memmove(ansp, pheader, hlen);
408 header->arcount = htons(1);
409 ansp += hlen;
410 }
411
412 return ansp - (unsigned char *)header;
413}
414
Simon Kelley832af0b2007-01-21 20:01:28 +0000415unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
Simon Kelley36717ee2004-09-20 19:20:58 +0100416{
417 /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
Simon Kelley832af0b2007-01-21 20:01:28 +0000418 also return length of pseudoheader in *len and pointer to the UDP size in *p
419 Finally, check to see if a packet is signed. If it is we cannot change a single bit before
420 forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100421
422 int i, arcount = ntohs(header->arcount);
Simon Kelley832af0b2007-01-21 20:01:28 +0000423 unsigned char *ansp = (unsigned char *)(header+1);
424 unsigned short rdlen, type, class;
425 unsigned char *ret = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000426
427 if (is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000428 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000429 *is_sign = 0;
430
431 if (header->opcode == QUERY)
Simon Kelley832af0b2007-01-21 20:01:28 +0000432 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100433 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000434 {
435 if (!(ansp = skip_name(ansp, header, plen)))
436 return NULL;
437
438 GETSHORT(type, ansp);
439 GETSHORT(class, ansp);
440
441 if (class == C_IN && type == T_TKEY)
442 *is_sign = 1;
443 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000444 }
445 }
446 else
447 {
448 if (!(ansp = skip_questions(header, plen)))
449 return NULL;
450 }
451
452 if (arcount == 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100453 return NULL;
454
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100455 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
456 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000457
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100458 for (i = 0; i < arcount; i++)
459 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100460 unsigned char *save, *start = ansp;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100461 if (!(ansp = skip_name(ansp, header, plen)))
462 return NULL;
463
464 GETSHORT(type, ansp);
465 save = ansp;
Simon Kelley832af0b2007-01-21 20:01:28 +0000466 GETSHORT(class, ansp);
467 ansp += 4; /* TTL */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100468 GETSHORT(rdlen, ansp);
Simon Kelleycdeda282006-03-16 20:16:06 +0000469 if ((size_t)(ansp + rdlen - (unsigned char *)header) > plen)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100470 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000471 ansp += rdlen;
472 if (type == T_OPT)
Simon Kelley36717ee2004-09-20 19:20:58 +0100473 {
474 if (len)
475 *len = ansp - start;
476 if (p)
477 *p = save;
Simon Kelley832af0b2007-01-21 20:01:28 +0000478 ret = start;
Simon Kelley36717ee2004-09-20 19:20:58 +0100479 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000480 else if (is_sign &&
481 i == arcount - 1 &&
482 class == C_ANY &&
483 (type == T_SIG || type == T_TSIG))
484 *is_sign = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100485 }
486
Simon Kelley832af0b2007-01-21 20:01:28 +0000487 return ret;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100488}
489
490
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000491/* is addr in the non-globally-routed IP space? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100492static int private_net(struct in_addr addr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000493{
Simon Kelleyf2621c72007-04-29 19:47:21 +0100494 in_addr_t ip_addr = ntohl(addr.s_addr);
495
496 return
497 ((ip_addr & 0xFF000000) == 0x7F000000) /* 127.0.0.0/8 (loopback) */ ||
498 ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
499 ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
500 ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
501 ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000502}
Simon Kelley1cff1662004-03-12 08:12:58 +0000503
Simon Kelley824af852008-02-12 20:43:05 +0000504static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
505{
506 int i, qtype, qclass, rdlen;
507 unsigned long ttl;
508
509 for (i = count; i != 0; i--)
510 {
511 if (!(p = skip_name(p, header, qlen)))
512 return 0; /* bad packet */
513
514 GETSHORT(qtype, p);
515 GETSHORT(qclass, p);
516 GETLONG(ttl, p);
517 GETSHORT(rdlen, p);
518
519 if ((qclass == C_IN) && (qtype == T_A))
520 {
521 struct doctor *doctor;
522 struct in_addr addr;
523
524 /* alignment */
525 memcpy(&addr, p, INADDRSZ);
526
527 for (doctor = daemon->doctors; doctor; doctor = doctor->next)
528 if (is_same_net(doctor->in, addr, doctor->mask))
529 {
530 addr.s_addr &= ~doctor->mask.s_addr;
531 addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
532 /* Since we munged the data, the server it came from is no longer authoritative */
533 header->aa = 0;
534 memcpy(p, &addr, INADDRSZ);
535 break;
536 }
537 }
538
539 p += rdlen;
540
541 if ((size_t)(p - (unsigned char *)header) > qlen)
542 return 0; /* bad packet */
543 }
544
545 return p;
546}
547
Simon Kelley5aabfc72007-08-29 11:24:47 +0100548static int find_soa(HEADER *header, size_t qlen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000549{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100550 unsigned char *p;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000551 int qtype, qclass, rdlen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100552 unsigned long ttl, minttl = ULONG_MAX;
553 int i, found_soa = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000554
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100555 /* first move to NS section and find TTL from any SOA section */
556 if (!(p = skip_questions(header, qlen)) ||
Simon Kelley824af852008-02-12 20:43:05 +0000557 !(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
558 return 0; /* bad packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000559
Simon Kelley5aabfc72007-08-29 11:24:47 +0100560 for (i = ntohs(header->nscount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000561 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100562 if (!(p = skip_name(p, header, qlen)))
563 return 0; /* bad packet */
564
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000565 GETSHORT(qtype, p);
566 GETSHORT(qclass, p);
567 GETLONG(ttl, p);
568 GETSHORT(rdlen, p);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100569
570 if ((qclass == C_IN) && (qtype == T_SOA))
571 {
572 found_soa = 1;
573 if (ttl < minttl)
574 minttl = ttl;
575
576 /* MNAME */
577 if (!(p = skip_name(p, header, qlen)))
578 return 0;
579 /* RNAME */
580 if (!(p = skip_name(p, header, qlen)))
581 return 0;
582 p += 16; /* SERIAL REFRESH RETRY EXPIRE */
583
584 GETLONG(ttl, p); /* minTTL */
585 if (ttl < minttl)
586 minttl = ttl;
587 }
588 else
589 p += rdlen;
590
Simon Kelleycdeda282006-03-16 20:16:06 +0000591 if ((size_t)(p - (unsigned char *)header) > qlen)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100592 return 0; /* bad packet */
593 }
594
Simon Kelley824af852008-02-12 20:43:05 +0000595 /* rewrite addresses in additioal section too */
596 if (!do_doctor(p, ntohs(header->arcount), header, qlen))
597 return 0;
598
599 if (!found_soa)
600 minttl = daemon->neg_ttl;
601
602 return minttl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100603}
604
605/* Note that the following code can create CNAME chains that don't point to a real record,
606 either because of lack of memory, or lack of SOA records. These are treated by the cache code as
Simon Kelley824af852008-02-12 20:43:05 +0000607 expired and cleaned out that way.
608 Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
609int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100610{
Simon Kelley824af852008-02-12 20:43:05 +0000611 unsigned char *p, *p1, *endrr, *namep;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100612 int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000613 unsigned long ttl = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100614 struct all_addr addr;
Simon Kelley0a852542005-03-23 20:28:59 +0000615
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100616 cache_start_insert();
Simon Kelley0a852542005-03-23 20:28:59 +0000617
618 /* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
619 if (daemon->doctors)
620 {
621 searched_soa = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100622 ttl = find_soa(header, qlen);
Simon Kelley0a852542005-03-23 20:28:59 +0000623 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100624
625 /* go through the questions. */
626 p = (unsigned char *)(header+1);
627
Simon Kelley5aabfc72007-08-29 11:24:47 +0100628 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100629 {
630 int found = 0, cname_count = 5;
631 struct crec *cpp = NULL;
632 int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000633 unsigned long cttl = ULONG_MAX, attl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100634
Simon Kelley824af852008-02-12 20:43:05 +0000635 namep = p;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100636 if (!extract_name(header, qlen, &p, name, 1))
Simon Kelley824af852008-02-12 20:43:05 +0000637 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100638
639 GETSHORT(qtype, p);
640 GETSHORT(qclass, p);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000641
642 if (qclass != C_IN)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100643 continue;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000644
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100645 /* PTRs: we chase CNAMEs here, since we have no way to
646 represent them in the cache. */
647 if (qtype == T_PTR)
648 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000649 int name_encoding = in_arpa_name_2_addr(name, &addr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100650
651 if (!name_encoding)
652 continue;
653
654 if (!(flags & F_NXDOMAIN))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000655 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100656 cname_loop:
657 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000658 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100659
Simon Kelley5aabfc72007-08-29 11:24:47 +0100660 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100661 {
Simon Kelley824af852008-02-12 20:43:05 +0000662 unsigned char *tmp = namep;
663 /* the loop body overwrites the original name, so get it back here. */
664 if (!extract_name(header, qlen, &tmp, name, 1) ||
665 !(res = extract_name(header, qlen, &p1, name, 0)))
666 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100667
668 GETSHORT(aqtype, p1);
669 GETSHORT(aqclass, p1);
670 GETLONG(attl, p1);
671 GETSHORT(ardlen, p1);
672 endrr = p1+ardlen;
673
674 /* TTL of record is minimum of CNAMES and PTR */
675 if (attl < cttl)
676 cttl = attl;
677
678 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
679 {
680 if (!extract_name(header, qlen, &p1, name, 1))
Simon Kelley824af852008-02-12 20:43:05 +0000681 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100682
683 if (aqtype == T_CNAME)
684 {
685 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000686 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100687 goto cname_loop;
688 }
689
690 cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
691 found = 1;
692 }
693
694 p1 = endrr;
Simon Kelleycdeda282006-03-16 20:16:06 +0000695 if ((size_t)(p1 - (unsigned char *)header) > qlen)
Simon Kelley824af852008-02-12 20:43:05 +0000696 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100697 }
698 }
699
700 if (!found && !(daemon->options & OPT_NO_NEG))
701 {
702 if (!searched_soa)
703 {
704 searched_soa = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100705 ttl = find_soa(header, qlen);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100706 }
707 if (ttl)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100708 cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000709 }
710 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100711 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000712 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100713 /* everything other than PTR */
714 struct crec *newc;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100715 int addrlen;
716
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100717 if (qtype == T_A)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100718 {
719 addrlen = INADDRSZ;
720 flags |= F_IPV4;
721 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000722#ifdef HAVE_IPV6
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100723 else if (qtype == T_AAAA)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100724 {
725 addrlen = IN6ADDRSZ;
726 flags |= F_IPV6;
727 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000728#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100729 else
730 continue;
731
732 if (!(flags & F_NXDOMAIN))
733 {
734 cname_loop1:
735 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000736 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100737
Simon Kelley5aabfc72007-08-29 11:24:47 +0100738 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000739 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100740 if (!(res = extract_name(header, qlen, &p1, name, 0)))
Simon Kelley824af852008-02-12 20:43:05 +0000741 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100742
743 GETSHORT(aqtype, p1);
744 GETSHORT(aqclass, p1);
745 GETLONG(attl, p1);
746 GETSHORT(ardlen, p1);
747 endrr = p1+ardlen;
748
749 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000750 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100751 if (aqtype == T_CNAME)
752 {
753 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000754 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100755 newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000756 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100757 {
758 cpp->addr.cname.cache = newc;
759 cpp->addr.cname.uid = newc->uid;
760 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000761
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100762 cpp = newc;
763 if (attl < cttl)
764 cttl = attl;
765
766 if (!extract_name(header, qlen, &p1, name, 1))
Simon Kelley824af852008-02-12 20:43:05 +0000767 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100768 goto cname_loop1;
769 }
770 else
771 {
772 found = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100773 /* copy address into aligned storage */
774 memcpy(&addr, p1, addrlen);
Simon Kelley824af852008-02-12 20:43:05 +0000775
776 /* check for returned address in private space */
777 if ((daemon->options & OPT_NO_REBIND) &&
778 (flags & F_IPV4) &&
779 private_net(addr.addr.addr4))
780 return 1;
781
Simon Kelley5aabfc72007-08-29 11:24:47 +0100782 newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000783 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100784 {
785 cpp->addr.cname.cache = newc;
786 cpp->addr.cname.uid = newc->uid;
787 }
788 cpp = NULL;
789 }
790 }
791
792 p1 = endrr;
Simon Kelleycdeda282006-03-16 20:16:06 +0000793 if ((size_t)(p1 - (unsigned char *)header) > qlen)
Simon Kelley824af852008-02-12 20:43:05 +0000794 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100795 }
796 }
797
798 if (!found && !(daemon->options & OPT_NO_NEG))
799 {
800 if (!searched_soa)
801 {
802 searched_soa = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100803 ttl = find_soa(header, qlen);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100804 }
805 /* If there's no SOA to get the TTL from, but there is a CNAME
Simon Kelley824af852008-02-12 20:43:05 +0000806 pointing at this, inherit its TTL */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100807 if (ttl || cpp)
808 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100809 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
Simon Kelley26128d22004-11-14 16:43:54 +0000810 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100811 {
812 cpp->addr.cname.cache = newc;
813 cpp->addr.cname.uid = newc->uid;
814 }
815 }
816 }
817 }
818 }
819
Simon Kelley824af852008-02-12 20:43:05 +0000820 /* Don't put stuff from a truncated packet into the cache, but do everything else */
821 if (!header->tc)
822 cache_end_insert();
823
824 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000825}
826
827/* If the packet holds exactly one query
Simon Kelley832af0b2007-01-21 20:01:28 +0000828 return F_IPV4 or F_IPV6 and leave the name from the query in name.
829 Abuse F_BIGNAME to indicate an NS query - yuck. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000830
Simon Kelleycdeda282006-03-16 20:16:06 +0000831unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000832{
833 unsigned char *p = (unsigned char *)(header+1);
834 int qtype, qclass;
835
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100836 if (typep)
837 *typep = 0;
838
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000839 if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
840 return 0; /* must be exactly one query. */
841
842 if (!extract_name(header, qlen, &p, name, 1))
843 return 0; /* bad packet */
844
845 GETSHORT(qtype, p);
846 GETSHORT(qclass, p);
847
Simon Kelley0a852542005-03-23 20:28:59 +0000848 if (typep)
849 *typep = qtype;
850
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000851 if (qclass == C_IN)
852 {
853 if (qtype == T_A)
854 return F_IPV4;
855 if (qtype == T_AAAA)
856 return F_IPV6;
857 if (qtype == T_ANY)
858 return F_IPV4 | F_IPV6;
Simon Kelley832af0b2007-01-21 20:01:28 +0000859 if (qtype == T_NS || qtype == T_SOA)
860 return F_QUERY | F_BIGNAME;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000861 }
862
863 return F_QUERY;
864}
865
866
Simon Kelleycdeda282006-03-16 20:16:06 +0000867size_t setup_reply(HEADER *header, size_t qlen,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000868 struct all_addr *addrp, unsigned short flags, unsigned long ttl)
869{
870 unsigned char *p = skip_questions(header, qlen);
871
872 header->qr = 1; /* response */
873 header->aa = 0; /* authoritive */
874 header->ra = 1; /* recursion if available */
875 header->tc = 0; /* not truncated */
876 header->nscount = htons(0);
877 header->arcount = htons(0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100878 header->ancount = htons(0); /* no answers unless changed below */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000879 if (flags == F_NEG)
880 header->rcode = SERVFAIL; /* couldn't get memory */
Simon Kelley824af852008-02-12 20:43:05 +0000881 else if (flags == F_NOERR)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000882 header->rcode = NOERROR; /* empty domain */
883 else if (flags == F_NXDOMAIN)
884 header->rcode = NXDOMAIN;
885 else if (p && flags == F_IPV4)
886 { /* we know the address */
887 header->rcode = NOERROR;
888 header->ancount = htons(1);
889 header->aa = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000890 add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000891 }
892#ifdef HAVE_IPV6
893 else if (p && flags == F_IPV6)
894 {
895 header->rcode = NOERROR;
896 header->ancount = htons(1);
897 header->aa = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000898 add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000899 }
900#endif
901 else /* nowhere to forward to */
902 header->rcode = REFUSED;
903
904 return p - (unsigned char *)header;
905}
Simon Kelley36717ee2004-09-20 19:20:58 +0100906
907/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100908int check_for_local_domain(char *name, time_t now)
Simon Kelley36717ee2004-09-20 19:20:58 +0100909{
910 struct crec *crecp;
Simon Kelley0a852542005-03-23 20:28:59 +0000911 struct mx_srv_record *mx;
912 struct txt_record *txt;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100913 struct interface_name *intr;
914 struct ptr_record *ptr;
915
Simon Kelley26128d22004-11-14 16:43:54 +0000916 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100917 (crecp->flags & (F_HOSTS | F_DHCP)))
918 return 1;
919
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000920 for (mx = daemon->mxnames; mx; mx = mx->next)
Simon Kelley0a852542005-03-23 20:28:59 +0000921 if (hostname_isequal(name, mx->name))
Simon Kelley36717ee2004-09-20 19:20:58 +0100922 return 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000923
Simon Kelley0a852542005-03-23 20:28:59 +0000924 for (txt = daemon->txt; txt; txt = txt->next)
925 if (hostname_isequal(name, txt->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000926 return 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100927
928 for (intr = daemon->int_names; intr; intr = intr->next)
929 if (hostname_isequal(name, intr->name))
930 return 1;
931
932 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
933 if (hostname_isequal(name, ptr->name))
934 return 1;
935
Simon Kelley36717ee2004-09-20 19:20:58 +0100936 return 0;
937}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000938
939/* Is the packet a reply with the answer address equal to addr?
940 If so mung is into an NXDOMAIN reply and also put that information
941 in the cache. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000942int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000943 struct bogus_addr *baddr, time_t now)
944{
945 unsigned char *p;
946 int i, qtype, qclass, rdlen;
947 unsigned long ttl;
948 struct bogus_addr *baddrp;
949
950 /* skip over questions */
951 if (!(p = skip_questions(header, qlen)))
952 return 0; /* bad packet */
953
Simon Kelley5aabfc72007-08-29 11:24:47 +0100954 for (i = ntohs(header->ancount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000955 {
956 if (!extract_name(header, qlen, &p, name, 1))
957 return 0; /* bad packet */
958
959 GETSHORT(qtype, p);
960 GETSHORT(qclass, p);
961 GETLONG(ttl, p);
962 GETSHORT(rdlen, p);
963
964 if (qclass == C_IN && qtype == T_A)
965 for (baddrp = baddr; baddrp; baddrp = baddrp->next)
966 if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
967 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100968 /* Found a bogus address. Insert that info here, since there no SOA record
969 to get the ttl from in the normal processing */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000970 cache_start_insert();
971 cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
972 cache_end_insert();
973
974 return 1;
975 }
976
977 p += rdlen;
978 }
979
980 return 0;
981}
982
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000983static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
Simon Kelley3d8df262005-08-29 12:19:27 +0100984 unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000985{
986 va_list ap;
987 unsigned char *sav, *p = *pp;
988 int j;
989 unsigned short usval;
990 long lval;
991 char *sval;
992
993 if (truncp && *truncp)
994 return 0;
995
996 PUTSHORT(nameoffset | 0xc000, p);
997 PUTSHORT(type, p);
998 PUTSHORT(class, p);
999 PUTLONG(ttl, p); /* TTL */
1000
1001 sav = p; /* Save pointer to RDLength field */
1002 PUTSHORT(0, p); /* Placeholder RDLength */
1003
1004 va_start(ap, format); /* make ap point to 1st unamed argument */
1005
1006 for (; *format; format++)
1007 switch (*format)
1008 {
1009#ifdef HAVE_IPV6
1010 case '6':
1011 sval = va_arg(ap, char *);
1012 memcpy(p, sval, IN6ADDRSZ);
1013 p += IN6ADDRSZ;
1014 break;
1015#endif
1016
1017 case '4':
1018 sval = va_arg(ap, char *);
1019 memcpy(p, sval, INADDRSZ);
1020 p += INADDRSZ;
1021 break;
1022
1023 case 's':
1024 usval = va_arg(ap, int);
1025 PUTSHORT(usval, p);
1026 break;
1027
1028 case 'l':
1029 lval = va_arg(ap, long);
1030 PUTLONG(lval, p);
1031 break;
1032
1033 case 'd':
1034 /* get domain-name answer arg and store it in RDATA field */
Simon Kelley0a852542005-03-23 20:28:59 +00001035 if (offset)
1036 *offset = p - (unsigned char *)header;
Simon Kelley3d8df262005-08-29 12:19:27 +01001037 p = do_rfc1035_name(p, va_arg(ap, char *));
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001038 *p++ = 0;
1039 break;
Simon Kelley3d8df262005-08-29 12:19:27 +01001040
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001041 case 't':
Simon Kelley0a852542005-03-23 20:28:59 +00001042 usval = va_arg(ap, int);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001043 sval = va_arg(ap, char *);
Simon Kelley0a852542005-03-23 20:28:59 +00001044 memcpy(p, sval, usval);
1045 p += usval;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001046 break;
1047 }
1048
1049 va_end(ap); /* clean up variable argument pointer */
1050
1051 j = p - sav - 2;
1052 PUTSHORT(j, sav); /* Now, store real RDLength */
1053
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001054 /* check for overflow of buffer */
1055 if (limit && ((unsigned char *)limit - p) < 0)
1056 {
1057 if (truncp)
1058 *truncp = 1;
1059 return 0;
1060 }
1061
1062 *pp = p;
1063 return 1;
1064}
1065
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001066/* return zero if we can't answer from cache, or packet size if we can */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001067size_t answer_request(HEADER *header, char *limit, size_t qlen,
Simon Kelleycdeda282006-03-16 20:16:06 +00001068 struct in_addr local_addr, struct in_addr local_netmask, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001069{
Simon Kelley3be34542004-09-11 19:12:13 +01001070 char *name = daemon->namebuff;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001071 unsigned char *p, *ansp, *pheader;
Simon Kelley832af0b2007-01-21 20:01:28 +00001072 int qtype, qclass;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001073 struct all_addr addr;
1074 unsigned int nameoffset;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001075 unsigned short flag;
Simon Kelley0a852542005-03-23 20:28:59 +00001076 int q, ans, anscount = 0, addncount = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001077 int dryrun = 0, sec_reqd = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001078 int is_sign;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001079 struct crec *crecp;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001080 int nxdomain = 0, auth = 1, trunc = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001081 struct mx_srv_record *rec;
1082
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001083 /* If there is an RFC2671 pseudoheader then it will be overwritten by
1084 partial replies, so we have to do a dry run to see if we can answer
1085 the query. We check to see if the do bit is set, if so we always
1086 forward rather than answering from the cache, which doesn't include
1087 security information. */
1088
Simon Kelley832af0b2007-01-21 20:01:28 +00001089 if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001090 {
1091 unsigned short udpsz, ext_rcode, flags;
1092 unsigned char *psave = pheader;
1093
1094 GETSHORT(udpsz, pheader);
1095 GETSHORT(ext_rcode, pheader);
1096 GETSHORT(flags, pheader);
1097
1098 sec_reqd = flags & 0x8000; /* do bit */
1099
1100 /* If our client is advertising a larger UDP packet size
1101 than we allow, trim it so that we don't get an overlarge
1102 response from upstream */
1103
Simon Kelley832af0b2007-01-21 20:01:28 +00001104 if (!is_sign && (udpsz > daemon->edns_pktsz))
Simon Kelley3be34542004-09-11 19:12:13 +01001105 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001106
1107 dryrun = 1;
1108 }
1109
Simon Kelley5aabfc72007-08-29 11:24:47 +01001110 if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
Simon Kelley832af0b2007-01-21 20:01:28 +00001111 return 0;
1112
Simon Kelley0a852542005-03-23 20:28:59 +00001113 for (rec = daemon->mxnames; rec; rec = rec->next)
1114 rec->offset = 0;
1115
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001116 rerun:
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001117 /* determine end of question section (we put answers there) */
1118 if (!(ansp = skip_questions(header, qlen)))
1119 return 0; /* bad packet */
1120
1121 /* now process each question, answers go in RRs after the question */
1122 p = (unsigned char *)(header+1);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001123
Simon Kelley5aabfc72007-08-29 11:24:47 +01001124 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001125 {
1126 /* save pointer to name for copying into answers */
1127 nameoffset = p - (unsigned char *)header;
1128
1129 /* now extract name as .-concatenated string into name */
1130 if (!extract_name(header, qlen, &p, name, 1))
1131 return 0; /* bad packet */
Simon Kelley832af0b2007-01-21 20:01:28 +00001132
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001133 GETSHORT(qtype, p);
1134 GETSHORT(qclass, p);
1135
1136 ans = 0; /* have we answered this question */
1137
Simon Kelley0a852542005-03-23 20:28:59 +00001138 if (qtype == T_TXT || qtype == T_ANY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001139 {
Simon Kelley0a852542005-03-23 20:28:59 +00001140 struct txt_record *t;
1141 for(t = daemon->txt; t ; t = t->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001142 {
Simon Kelley0a852542005-03-23 20:28:59 +00001143 if (t->class == qclass && hostname_isequal(name, t->name))
1144 {
1145 ans = 1;
Simon Kelleye17fb622006-01-14 20:33:46 +00001146 if (!dryrun)
1147 {
1148 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
1149 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1150 daemon->local_ttl, NULL,
1151 T_TXT, t->class, "t", t->len, t->txt))
1152 anscount++;
1153
1154 }
Simon Kelley0a852542005-03-23 20:28:59 +00001155 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001156 }
Simon Kelley0a852542005-03-23 20:28:59 +00001157 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001158
Simon Kelley0a852542005-03-23 20:28:59 +00001159 if (qclass == C_IN)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001160 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001161 if (qtype == T_PTR || qtype == T_ANY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001162 {
Simon Kelley832af0b2007-01-21 20:01:28 +00001163 /* see if it's w.z.y.z.in-addr.arpa format */
1164 int is_arpa = in_arpa_name_2_addr(name, &addr);
1165 struct ptr_record *ptr;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001166 struct interface_name* intr = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00001167
1168 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1169 if (hostname_isequal(name, ptr->name))
1170 break;
1171
Simon Kelleyf2621c72007-04-29 19:47:21 +01001172 if (is_arpa == F_IPV4)
1173 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001174 {
1175 if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
1176 break;
1177 else
1178 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
1179 intr = intr->next;
1180 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001181
1182 if (intr)
1183 {
1184 ans = 1;
1185 if (!dryrun)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001186 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001187 log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, 0, NULL, 0);
1188 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1189 daemon->local_ttl, NULL,
1190 T_PTR, C_IN, "d", intr->name))
1191 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001192 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001193 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001194 else if (ptr)
1195 {
1196 ans = 1;
1197 if (!dryrun)
1198 {
1199 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_BIGNAME, name, NULL, 0, NULL, 0);
1200 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001201 if (hostname_isequal(name, ptr->name) &&
1202 add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1203 daemon->local_ttl, NULL,
1204 T_PTR, C_IN, "d", ptr->ptr))
1205 anscount++;
1206
Simon Kelley832af0b2007-01-21 20:01:28 +00001207 }
1208 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001209 else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
1210 do
1211 {
1212 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
1213 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1214 continue;
1215
1216 if (crecp->flags & F_NEG)
1217 {
1218 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001219 auth = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001220 if (crecp->flags & F_NXDOMAIN)
1221 nxdomain = 1;
1222 if (!dryrun)
1223 log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
1224 }
1225 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
1226 {
1227 ans = 1;
1228 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1229 auth = 0;
1230 if (!dryrun)
1231 {
1232 unsigned long ttl;
1233 /* Return 0 ttl for DHCP entries, which might change
1234 before the lease expires. */
1235 if (crecp->flags & (F_IMMORTAL | F_DHCP))
1236 ttl = daemon->local_ttl;
1237 else
1238 ttl = crecp->ttd - now;
1239
1240 log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
1241 0, daemon->addn_hosts, crecp->uid);
1242
1243 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL,
1244 T_PTR, C_IN, "d", cache_get_name(crecp)))
1245 anscount++;
1246 }
1247 }
1248 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
1249 else if (is_arpa == F_IPV4 &&
1250 (daemon->options & OPT_BOGUSPRIV) &&
1251 private_net(addr.addr.addr4))
1252 {
1253 /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
1254 ans = 1;
1255 nxdomain = 1;
1256 if (!dryrun)
1257 log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
1258 name, &addr, 0, NULL, 0);
1259 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001260 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001261
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001262 for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
1263 {
1264 unsigned short type = T_A;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001265
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001266 if (flag == F_IPV6)
1267#ifdef HAVE_IPV6
Simon Kelley3d8df262005-08-29 12:19:27 +01001268 type = T_AAAA;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001269#else
Simon Kelley3d8df262005-08-29 12:19:27 +01001270 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001271#endif
1272
1273 if (qtype != type && qtype != T_ANY)
1274 continue;
1275
Simon Kelleyf2621c72007-04-29 19:47:21 +01001276 /* Check for "A for A" queries */
Simon Kelley3d8df262005-08-29 12:19:27 +01001277 if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1)
1278 {
1279 ans = 1;
1280 if (!dryrun)
1281 {
1282 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, 0, NULL, 0);
1283 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1284 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1285 anscount++;
1286 }
1287 continue;
1288 }
1289
Simon Kelleyf2621c72007-04-29 19:47:21 +01001290 /* interface name stuff */
1291 if (qtype == T_A)
1292 {
1293 struct interface_name *intr;
1294
1295 for (intr = daemon->int_names; intr; intr = intr->next)
1296 if (hostname_isequal(name, intr->name))
1297 break;
1298
1299 if (intr)
1300 {
1301 ans = 1;
1302 if (!dryrun)
1303 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001304 if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001305 log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, 0, NULL, 0);
1306 else
1307 {
1308 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, 0, NULL, 0);
1309 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1310 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1311 anscount++;
1312 }
1313 }
1314 continue;
1315 }
1316 }
1317
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001318 cname_restart:
1319 if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
1320 {
1321 int localise = 0;
1322
1323 /* See if a putative address is on the network from which we recieved
1324 the query, is so we'll filter other answers. */
1325 if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
1326 {
1327 struct crec *save = crecp;
1328 do {
1329 if ((crecp->flags & F_HOSTS) &&
1330 is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1331 {
1332 localise = 1;
1333 break;
1334 }
1335 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
1336 crecp = save;
1337 }
1338
1339 do
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001340 {
Simon Kelley26128d22004-11-14 16:43:54 +00001341 /* don't answer wildcard queries with data not from /etc/hosts
1342 or DHCP leases */
1343 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1344 break;
1345
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001346 if (crecp->flags & F_CNAME)
1347 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001348 if (!dryrun)
1349 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001350 log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001351 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset,
1352 T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
1353 anscount++;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001354 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001355
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001356 strcpy(name, cache_get_name(crecp->addr.cname.cache));
1357 goto cname_restart;
1358 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001359
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001360 if (crecp->flags & F_NEG)
1361 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001362 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001363 auth = 0;
1364 if (crecp->flags & F_NXDOMAIN)
1365 nxdomain = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001366 if (!dryrun)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001367 log_query(crecp->flags, name, NULL, 0, NULL, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001368 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001369 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001370 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001371 /* If we are returning local answers depending on network,
1372 filter here. */
1373 if (localise &&
1374 (crecp->flags & F_HOSTS) &&
1375 !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1376 continue;
1377
1378 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1379 auth = 0;
1380
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001381 ans = 1;
1382 if (!dryrun)
1383 {
1384 unsigned long ttl;
1385
1386 if (crecp->flags & (F_IMMORTAL | F_DHCP))
Simon Kelley3be34542004-09-11 19:12:13 +01001387 ttl = daemon->local_ttl;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001388 else
1389 ttl = crecp->ttd - now;
1390
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001391 log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
1392 0, daemon->addn_hosts, crecp->uid);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001393
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001394 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN,
1395 type == T_A ? "4" : "6", &crecp->addr))
1396 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001397 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001398 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001399 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001400 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001401 }
1402
1403 if (qtype == T_MX || qtype == T_ANY)
1404 {
1405 int found = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001406 for (rec = daemon->mxnames; rec; rec = rec->next)
1407 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001408 {
1409 ans = found = 1;
1410 if (!dryrun)
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001411 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001412 unsigned int offset;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001413 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
Simon Kelley0a852542005-03-23 20:28:59 +00001414 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1415 &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
1416 {
1417 anscount++;
1418 if (rec->target)
1419 rec->offset = offset;
1420 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001421 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001422 }
1423
1424 if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
1425 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
1426 {
1427 ans = 1;
1428 if (!dryrun)
1429 {
1430 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
1431 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
1432 T_MX, C_IN, "sd", 1,
1433 (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
1434 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001435 }
1436 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001437 }
1438
1439 if (qtype == T_SRV || qtype == T_ANY)
1440 {
1441 int found = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001442
Simon Kelley0a852542005-03-23 20:28:59 +00001443 for (rec = daemon->mxnames; rec; rec = rec->next)
1444 if (rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001445 {
1446 found = ans = 1;
1447 if (!dryrun)
1448 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001449 unsigned int offset;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001450 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0);
1451 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
Simon Kelley0a852542005-03-23 20:28:59 +00001452 &offset, T_SRV, C_IN, "sssd",
1453 rec->priority, rec->weight, rec->srvport, rec->target))
1454 {
1455 anscount++;
1456 if (rec->target)
1457 rec->offset = offset;
1458 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001459 }
1460 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001461
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001462 if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
1463 {
1464 ans = 1;
1465 if (!dryrun)
1466 log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0);
1467 }
1468 }
1469
1470 if (qtype == T_MAILB)
1471 ans = 1, nxdomain = 1;
1472
1473 if (qtype == T_SOA && (daemon->options & OPT_FILTER))
1474 {
1475 ans = 1;
1476 if (!dryrun)
1477 log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001478 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001479 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001480
1481 if (!ans)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001482 return 0; /* failed to answer a question */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001483 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001484
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001485 if (dryrun)
1486 {
1487 dryrun = 0;
1488 goto rerun;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001489 }
1490
Simon Kelley0a852542005-03-23 20:28:59 +00001491 /* create an additional data section, for stuff in SRV and MX record replies. */
1492 for (rec = daemon->mxnames; rec; rec = rec->next)
1493 if (rec->offset != 0)
1494 {
1495 /* squash dupes */
1496 struct mx_srv_record *tmp;
1497 for (tmp = rec->next; tmp; tmp = tmp->next)
1498 if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
1499 tmp->offset = 0;
1500
1501 crecp = NULL;
1502 while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
1503 {
1504 unsigned long ttl;
1505#ifdef HAVE_IPV6
1506 int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
1507#else
1508 int type = T_A;
1509#endif
1510 if (crecp->flags & F_NEG)
1511 continue;
1512
1513 if (crecp->flags & (F_IMMORTAL | F_DHCP))
1514 ttl = daemon->local_ttl;
1515 else
1516 ttl = crecp->ttd - now;
1517
1518 if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN,
1519 crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
1520 addncount++;
1521 }
1522 }
1523
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001524 /* done all questions, set up header and return length of result */
1525 header->qr = 1; /* response */
1526 header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
1527 header->ra = 1; /* recursion if available */
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001528 header->tc = trunc; /* truncation */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001529 if (anscount == 0 && nxdomain)
1530 header->rcode = NXDOMAIN;
1531 else
1532 header->rcode = NOERROR; /* no error */
1533 header->ancount = htons(anscount);
1534 header->nscount = htons(0);
Simon Kelley0a852542005-03-23 20:28:59 +00001535 header->arcount = htons(addncount);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001536 return ansp - (unsigned char *)header;
1537}
1538
1539
1540
1541
1542