blob: 6a819bbff7c7e2e7951fd45c4ae00ea98b52d44e [file] [log] [blame]
Simon Kelley73a08a22009-02-05 20:28:08 +00001/* dnsmasq is Copyright (c) 2000-2009 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
Simon Kelley73a08a22009-02-05 20:28:08 +000013 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 Kelley9009d742008-11-14 20:04:27 +000024#define CHECK_LEN(header, pp, plen, len) \
25 ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
26
27#define ADD_RDLEN(header, pp, plen, len) \
Simon Kelley73a08a22009-02-05 20:28:08 +000028 (!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1)
Simon Kelley9009d742008-11-14 20:04:27 +000029
Simon Kelleycdeda282006-03-16 20:16:06 +000030static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
Simon Kelley9009d742008-11-14 20:04:27 +000031 char *name, int isExtract, int extrabytes)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000032{
Simon Kelley3d8df262005-08-29 12:19:27 +010033 unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000034 unsigned int j, l, hops = 0;
35 int retvalue = 1;
36
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000037 if (isExtract)
38 *cp = 0;
39
Simon Kelley9009d742008-11-14 20:04:27 +000040 while (1)
41 {
42 unsigned int label_type;
43
44 if (!CHECK_LEN(header, p, plen, 1))
45 return 0;
46
47 if ((l = *p++) == 0)
48 /* end marker */
49 {
50 /* check that there are the correct no of bytes after the name */
51 if (!CHECK_LEN(header, p, plen, extrabytes))
52 return 0;
53
54 if (isExtract)
55 {
56 if (cp != (unsigned char *)name)
57 cp--;
58 *cp = 0; /* terminate: lose final period */
59 }
60 else if (*cp != 0)
61 retvalue = 2;
62
63 if (p1) /* we jumped via compression */
64 *pp = p1;
65 else
66 *pp = p;
67
68 return retvalue;
69 }
70
71 label_type = l & 0xc0;
72
Simon Kelley9e4abcb2004-01-22 19:47:41 +000073 if (label_type == 0xc0) /* pointer */
74 {
Simon Kelley9009d742008-11-14 20:04:27 +000075 if (!CHECK_LEN(header, p, plen, 1))
Simon Kelley9e4abcb2004-01-22 19:47:41 +000076 return 0;
77
78 /* get offset */
79 l = (l&0x3f) << 8;
80 l |= *p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000081
82 if (!p1) /* first jump, save location to go back to */
83 p1 = p;
84
85 hops++; /* break malicious infinite loops */
86 if (hops > 255)
87 return 0;
88
89 p = l + (unsigned char *)header;
90 }
91 else if (label_type == 0x80)
92 return 0; /* reserved */
93 else if (label_type == 0x40)
94 { /* ELT */
95 unsigned int count, digs;
96
97 if ((l & 0x3f) != 1)
98 return 0; /* we only understand bitstrings */
99
100 if (!isExtract)
101 return 0; /* Cannot compare bitsrings */
102
103 count = *p++;
104 if (count == 0)
105 count = 256;
106 digs = ((count-1)>>2)+1;
107
108 /* output is \[x<hex>/siz]. which is digs+9 chars */
Simon Kelley3d8df262005-08-29 12:19:27 +0100109 if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000110 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000111 if (!CHECK_LEN(header, p, plen, (count-1)>>3))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000112 return 0;
113
114 *cp++ = '\\';
115 *cp++ = '[';
116 *cp++ = 'x';
117 for (j=0; j<digs; j++)
118 {
119 unsigned int dig;
120 if (j%2 == 0)
121 dig = *p >> 4;
122 else
123 dig = *p++ & 0x0f;
124
125 *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
126 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100127 cp += sprintf((char *)cp, "/%d]", count);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000128 /* do this here to overwrite the zero char from sprintf */
129 *cp++ = '.';
130 }
131 else
132 { /* label_type = 0 -> label. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100133 if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000134 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000135 if (!CHECK_LEN(header, p, plen, l))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000136 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000137
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000138 for(j=0; j<l; j++, p++)
139 if (isExtract)
140 {
141 if (legal_char(*p))
142 *cp++ = *p;
143 else
144 return 0;
145 }
146 else
147 {
148 unsigned char c1 = *cp, c2 = *p;
149
150 if (c1 == 0)
151 retvalue = 2;
152 else
153 {
154 cp++;
155 if (c1 >= 'A' && c1 <= 'Z')
156 c1 += 'a' - 'A';
157 if (c2 >= 'A' && c2 <= 'Z')
158 c2 += 'a' - 'A';
159
160 if (c1 != c2)
161 retvalue = 2;
162 }
163 }
164
165 if (isExtract)
166 *cp++ = '.';
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000167 else if (*cp != 0 && *cp++ != '.')
168 retvalue = 2;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000169 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000170 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000171}
172
173/* Max size of input string (for IPv6) is 75 chars.) */
174#define MAXARPANAME 75
175static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
176{
177 int j;
178 char name[MAXARPANAME+1], *cp1;
179 unsigned char *addr = (unsigned char *)addrp;
180 char *lastchunk = NULL, *penchunk = NULL;
181
182 if (strlen(namein) > MAXARPANAME)
183 return 0;
184
185 memset(addrp, 0, sizeof(struct all_addr));
186
187 /* turn name into a series of asciiz strings */
188 /* j counts no of labels */
189 for(j = 1,cp1 = name; *namein; cp1++, namein++)
190 if (*namein == '.')
191 {
192 penchunk = lastchunk;
193 lastchunk = cp1 + 1;
194 *cp1 = 0;
195 j++;
196 }
197 else
198 *cp1 = *namein;
199
200 *cp1 = 0;
201
202 if (j<3)
203 return 0;
204
205 if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))
206 {
207 /* IP v4 */
208 /* address arives as a name of the form
209 www.xxx.yyy.zzz.in-addr.arpa
210 some of the low order address octets might be missing
211 and should be set to zero. */
212 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
213 {
214 /* check for digits only (weeds out things like
215 50.0/24.67.28.64.in-addr.arpa which are used
216 as CNAME targets according to RFC 2317 */
217 char *cp;
218 for (cp = cp1; *cp; cp++)
219 if (!isdigit((int)*cp))
220 return 0;
221
222 addr[3] = addr[2];
223 addr[2] = addr[1];
224 addr[1] = addr[0];
225 addr[0] = atoi(cp1);
226 }
227
228 return F_IPV4;
229 }
230#ifdef HAVE_IPV6
231 else if (hostname_isequal(penchunk, "ip6") &&
232 (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
233 {
234 /* IP v6:
235 Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
236 or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
237
238 Note that most of these the various reprentations are obsolete and
239 left-over from the many DNS-for-IPv6 wars. We support all the formats
240 that we can since there is no reason not to.
241 */
242
243 if (*name == '\\' && *(name+1) == '[' &&
244 (*(name+2) == 'x' || *(name+2) == 'X'))
245 {
Simon Kelley9e038942008-05-30 20:06:34 +0100246 for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000247 {
248 char xdig[2];
249 xdig[0] = *cp1;
250 xdig[1] = 0;
251 if (j%2)
252 addr[j/2] |= strtol(xdig, NULL, 16);
253 else
254 addr[j/2] = strtol(xdig, NULL, 16) << 4;
255 }
256
257 if (*cp1 == '/' && j == 32)
258 return F_IPV6;
259 }
260 else
261 {
262 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
263 {
264 if (*(cp1+1) || !isxdigit((int)*cp1))
265 return 0;
266
267 for (j = sizeof(struct all_addr)-1; j>0; j--)
268 addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);
269 addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
270 }
271
272 return F_IPV6;
273 }
274 }
275#endif
276
277 return 0;
278}
279
Simon Kelley9009d742008-11-14 20:04:27 +0000280static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen, int extrabytes)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100281{
282 while(1)
283 {
Simon Kelley9009d742008-11-14 20:04:27 +0000284 unsigned int label_type;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100285
Simon Kelley9009d742008-11-14 20:04:27 +0000286 if (!CHECK_LEN(header, ansp, plen, 1))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100287 return NULL;
288
Simon Kelley9009d742008-11-14 20:04:27 +0000289 label_type = (*ansp) & 0xc0;
290
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100291 if (label_type == 0xc0)
292 {
293 /* pointer for compression. */
294 ansp += 2;
295 break;
296 }
297 else if (label_type == 0x80)
298 return NULL; /* reserved */
299 else if (label_type == 0x40)
300 {
301 /* Extended label type */
302 unsigned int count;
303
Simon Kelley9009d742008-11-14 20:04:27 +0000304 if (!CHECK_LEN(header, ansp, plen, 2))
305 return NULL;
306
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100307 if (((*ansp++) & 0x3f) != 1)
308 return NULL; /* we only understand bitstrings */
309
310 count = *(ansp++); /* Bits in bitstring */
311
312 if (count == 0) /* count == 0 means 256 bits */
313 ansp += 32;
314 else
315 ansp += ((count-1)>>3)+1;
316 }
317 else
318 { /* label type == 0 Bottom six bits is length */
319 unsigned int len = (*ansp++) & 0x3f;
Simon Kelley9009d742008-11-14 20:04:27 +0000320
321 if (!ADD_RDLEN(header, ansp, plen, len))
322 return NULL;
323
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100324 if (len == 0)
325 break; /* zero length label marks the end. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100326 }
327 }
Simon Kelley9009d742008-11-14 20:04:27 +0000328
329 if (!CHECK_LEN(header, ansp, plen, extrabytes))
330 return NULL;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100331
332 return ansp;
333}
334
Simon Kelleycdeda282006-03-16 20:16:06 +0000335static unsigned char *skip_questions(HEADER *header, size_t plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000336{
Simon Kelley5aabfc72007-08-29 11:24:47 +0100337 int q;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000338 unsigned char *ansp = (unsigned char *)(header+1);
339
Simon Kelley5aabfc72007-08-29 11:24:47 +0100340 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000341 {
Simon Kelley9009d742008-11-14 20:04:27 +0000342 if (!(ansp = skip_name(ansp, header, plen, 4)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100343 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000344 ansp += 4; /* class and type */
345 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000346
347 return ansp;
348}
349
Simon Kelleycdeda282006-03-16 20:16:06 +0000350static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, size_t plen)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100351{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100352 int i, rdlen;
Simon Kelley36717ee2004-09-20 19:20:58 +0100353
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100354 for (i = 0; i < count; i++)
Simon Kelley36717ee2004-09-20 19:20:58 +0100355 {
Simon Kelley9009d742008-11-14 20:04:27 +0000356 if (!(ansp = skip_name(ansp, header, plen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100357 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100358 ansp += 8; /* type, class, TTL */
359 GETSHORT(rdlen, ansp);
Simon Kelley9009d742008-11-14 20:04:27 +0000360 if (!ADD_RDLEN(header, ansp, plen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100361 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100362 }
363
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100364 return ansp;
365}
366
Simon Kelley0a852542005-03-23 20:28:59 +0000367/* CRC the question section. This is used to safely detect query
368 retransmision and to detect answers to questions we didn't ask, which
369 might be poisoning attacks. Note that we decode the name rather
370 than CRC the raw bytes, since replies might be compressed differently.
Simon Kelley832af0b2007-01-21 20:01:28 +0000371 We ignore case in the names for the same reason. Return all-ones
372 if there is not question section. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000373unsigned int questions_crc(HEADER *header, size_t plen, char *name)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100374{
Simon Kelley91dccd02005-03-31 17:48:32 +0100375 int q;
376 unsigned int crc = 0xffffffff;
Simon Kelley0a852542005-03-23 20:28:59 +0000377 unsigned char *p1, *p = (unsigned char *)(header+1);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100378
Simon Kelley5aabfc72007-08-29 11:24:47 +0100379 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley0a852542005-03-23 20:28:59 +0000380 {
Simon Kelley9009d742008-11-14 20:04:27 +0000381 if (!extract_name(header, plen, &p, name, 1, 4))
Simon Kelley0a852542005-03-23 20:28:59 +0000382 return crc; /* bad packet */
383
Simon Kelley3d8df262005-08-29 12:19:27 +0100384 for (p1 = (unsigned char *)name; *p1; p1++)
Simon Kelley0a852542005-03-23 20:28:59 +0000385 {
386 int i = 8;
387 char c = *p1;
388
389 if (c >= 'A' && c <= 'Z')
390 c += 'a' - 'A';
391
392 crc ^= c << 24;
393 while (i--)
394 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
395 }
396
397 /* CRC the class and type as well */
398 for (p1 = p; p1 < p+4; p1++)
399 {
400 int i = 8;
401 crc ^= *p1 << 24;
402 while (i--)
403 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
404 }
405
406 p += 4;
Simon Kelley9009d742008-11-14 20:04:27 +0000407 if (!CHECK_LEN(header, p, plen, 0))
Simon Kelley0a852542005-03-23 20:28:59 +0000408 return crc; /* bad packet */
409 }
410
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100411 return crc;
412}
413
414
Simon Kelleycdeda282006-03-16 20:16:06 +0000415size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t hlen)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100416{
417 unsigned char *ansp = skip_questions(header, plen);
418
Simon Kelley9009d742008-11-14 20:04:27 +0000419 /* if packet is malformed, just return as-is. */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100420 if (!ansp)
Simon Kelley9009d742008-11-14 20:04:27 +0000421 return plen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100422
423 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
424 header, plen)))
Simon Kelley9009d742008-11-14 20:04:27 +0000425 return plen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100426
Simon Kelley36717ee2004-09-20 19:20:58 +0100427 /* restore pseudoheader */
428 if (pheader && ntohs(header->arcount) == 0)
429 {
430 /* must use memmove, may overlap */
431 memmove(ansp, pheader, hlen);
432 header->arcount = htons(1);
433 ansp += hlen;
434 }
435
436 return ansp - (unsigned char *)header;
437}
438
Simon Kelley832af0b2007-01-21 20:01:28 +0000439unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
Simon Kelley36717ee2004-09-20 19:20:58 +0100440{
441 /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
Simon Kelley832af0b2007-01-21 20:01:28 +0000442 also return length of pseudoheader in *len and pointer to the UDP size in *p
443 Finally, check to see if a packet is signed. If it is we cannot change a single bit before
444 forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100445
446 int i, arcount = ntohs(header->arcount);
Simon Kelley832af0b2007-01-21 20:01:28 +0000447 unsigned char *ansp = (unsigned char *)(header+1);
448 unsigned short rdlen, type, class;
449 unsigned char *ret = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000450
451 if (is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000452 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000453 *is_sign = 0;
454
455 if (header->opcode == QUERY)
Simon Kelley832af0b2007-01-21 20:01:28 +0000456 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100457 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000458 {
Simon Kelley9009d742008-11-14 20:04:27 +0000459 if (!(ansp = skip_name(ansp, header, plen, 4)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000460 return NULL;
461
462 GETSHORT(type, ansp);
463 GETSHORT(class, ansp);
464
465 if (class == C_IN && type == T_TKEY)
466 *is_sign = 1;
467 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000468 }
469 }
470 else
471 {
472 if (!(ansp = skip_questions(header, plen)))
473 return NULL;
474 }
475
476 if (arcount == 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100477 return NULL;
478
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100479 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
480 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000481
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100482 for (i = 0; i < arcount; i++)
483 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100484 unsigned char *save, *start = ansp;
Simon Kelley9009d742008-11-14 20:04:27 +0000485 if (!(ansp = skip_name(ansp, header, plen, 10)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100486 return NULL;
487
488 GETSHORT(type, ansp);
489 save = ansp;
Simon Kelley832af0b2007-01-21 20:01:28 +0000490 GETSHORT(class, ansp);
491 ansp += 4; /* TTL */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100492 GETSHORT(rdlen, ansp);
Simon Kelley9009d742008-11-14 20:04:27 +0000493 if (!ADD_RDLEN(header, ansp, plen, rdlen))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100494 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000495 if (type == T_OPT)
Simon Kelley36717ee2004-09-20 19:20:58 +0100496 {
497 if (len)
498 *len = ansp - start;
499 if (p)
500 *p = save;
Simon Kelley832af0b2007-01-21 20:01:28 +0000501 ret = start;
Simon Kelley36717ee2004-09-20 19:20:58 +0100502 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000503 else if (is_sign &&
504 i == arcount - 1 &&
505 class == C_ANY &&
506 (type == T_SIG || type == T_TSIG))
507 *is_sign = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100508 }
509
Simon Kelley832af0b2007-01-21 20:01:28 +0000510 return ret;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100511}
512
513
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000514/* is addr in the non-globally-routed IP space? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100515static int private_net(struct in_addr addr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000516{
Simon Kelleyf2621c72007-04-29 19:47:21 +0100517 in_addr_t ip_addr = ntohl(addr.s_addr);
518
519 return
520 ((ip_addr & 0xFF000000) == 0x7F000000) /* 127.0.0.0/8 (loopback) */ ||
521 ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
522 ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
523 ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
524 ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000525}
Simon Kelley1cff1662004-03-12 08:12:58 +0000526
Simon Kelley824af852008-02-12 20:43:05 +0000527static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
528{
529 int i, qtype, qclass, rdlen;
530 unsigned long ttl;
531
532 for (i = count; i != 0; i--)
533 {
Simon Kelley9009d742008-11-14 20:04:27 +0000534 if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000535 return 0; /* bad packet */
536
537 GETSHORT(qtype, p);
538 GETSHORT(qclass, p);
539 GETLONG(ttl, p);
540 GETSHORT(rdlen, p);
541
542 if ((qclass == C_IN) && (qtype == T_A))
543 {
544 struct doctor *doctor;
545 struct in_addr addr;
546
Simon Kelley9009d742008-11-14 20:04:27 +0000547 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
548 return 0;
549
550 /* alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000551 memcpy(&addr, p, INADDRSZ);
552
553 for (doctor = daemon->doctors; doctor; doctor = doctor->next)
Simon Kelley73a08a22009-02-05 20:28:08 +0000554 {
555 if (doctor->end.s_addr == 0)
556 {
557 if (!is_same_net(doctor->in, addr, doctor->mask))
558 continue;
559 }
560 else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
561 ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
562 continue;
563
564 addr.s_addr &= ~doctor->mask.s_addr;
565 addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
566 /* Since we munged the data, the server it came from is no longer authoritative */
567 header->aa = 0;
568 memcpy(p, &addr, INADDRSZ);
569 break;
570 }
Simon Kelley824af852008-02-12 20:43:05 +0000571 }
572
Simon Kelley9009d742008-11-14 20:04:27 +0000573 if (!ADD_RDLEN(header, p, qlen, rdlen))
574 return 0; /* bad packet */
Simon Kelley824af852008-02-12 20:43:05 +0000575 }
576
577 return p;
578}
579
Simon Kelley5aabfc72007-08-29 11:24:47 +0100580static int find_soa(HEADER *header, size_t qlen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000581{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100582 unsigned char *p;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000583 int qtype, qclass, rdlen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100584 unsigned long ttl, minttl = ULONG_MAX;
585 int i, found_soa = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000586
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100587 /* first move to NS section and find TTL from any SOA section */
588 if (!(p = skip_questions(header, qlen)) ||
Simon Kelley824af852008-02-12 20:43:05 +0000589 !(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
590 return 0; /* bad packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000591
Simon Kelley5aabfc72007-08-29 11:24:47 +0100592 for (i = ntohs(header->nscount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000593 {
Simon Kelley9009d742008-11-14 20:04:27 +0000594 if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100595 return 0; /* bad packet */
596
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000597 GETSHORT(qtype, p);
598 GETSHORT(qclass, p);
599 GETLONG(ttl, p);
600 GETSHORT(rdlen, p);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100601
602 if ((qclass == C_IN) && (qtype == T_SOA))
603 {
604 found_soa = 1;
605 if (ttl < minttl)
606 minttl = ttl;
607
608 /* MNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000609 if (!(p = skip_name(p, header, qlen, 0)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100610 return 0;
611 /* RNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000612 if (!(p = skip_name(p, header, qlen, 20)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100613 return 0;
614 p += 16; /* SERIAL REFRESH RETRY EXPIRE */
615
616 GETLONG(ttl, p); /* minTTL */
617 if (ttl < minttl)
618 minttl = ttl;
619 }
Simon Kelley9009d742008-11-14 20:04:27 +0000620 else if (!ADD_RDLEN(header, p, qlen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100621 return 0; /* bad packet */
622 }
Simon Kelley9009d742008-11-14 20:04:27 +0000623
Simon Kelley824af852008-02-12 20:43:05 +0000624 /* rewrite addresses in additioal section too */
625 if (!do_doctor(p, ntohs(header->arcount), header, qlen))
626 return 0;
627
628 if (!found_soa)
629 minttl = daemon->neg_ttl;
630
631 return minttl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100632}
633
634/* Note that the following code can create CNAME chains that don't point to a real record,
635 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 +0000636 expired and cleaned out that way.
637 Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
638int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100639{
Simon Kelley824af852008-02-12 20:43:05 +0000640 unsigned char *p, *p1, *endrr, *namep;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100641 int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000642 unsigned long ttl = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100643 struct all_addr addr;
Simon Kelley0a852542005-03-23 20:28:59 +0000644
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100645 cache_start_insert();
Simon Kelley0a852542005-03-23 20:28:59 +0000646
647 /* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
648 if (daemon->doctors)
649 {
650 searched_soa = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100651 ttl = find_soa(header, qlen);
Simon Kelley0a852542005-03-23 20:28:59 +0000652 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100653
654 /* go through the questions. */
655 p = (unsigned char *)(header+1);
656
Simon Kelley5aabfc72007-08-29 11:24:47 +0100657 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100658 {
659 int found = 0, cname_count = 5;
660 struct crec *cpp = NULL;
661 int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000662 unsigned long cttl = ULONG_MAX, attl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100663
Simon Kelley824af852008-02-12 20:43:05 +0000664 namep = p;
Simon Kelley9009d742008-11-14 20:04:27 +0000665 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley824af852008-02-12 20:43:05 +0000666 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100667
668 GETSHORT(qtype, p);
669 GETSHORT(qclass, p);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000670
671 if (qclass != C_IN)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100672 continue;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000673
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100674 /* PTRs: we chase CNAMEs here, since we have no way to
675 represent them in the cache. */
676 if (qtype == T_PTR)
677 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000678 int name_encoding = in_arpa_name_2_addr(name, &addr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100679
680 if (!name_encoding)
681 continue;
682
683 if (!(flags & F_NXDOMAIN))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000684 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100685 cname_loop:
686 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000687 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100688
Simon Kelley5aabfc72007-08-29 11:24:47 +0100689 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100690 {
Simon Kelley824af852008-02-12 20:43:05 +0000691 unsigned char *tmp = namep;
692 /* the loop body overwrites the original name, so get it back here. */
Simon Kelley9009d742008-11-14 20:04:27 +0000693 if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
694 !(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000695 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100696
697 GETSHORT(aqtype, p1);
698 GETSHORT(aqclass, p1);
699 GETLONG(attl, p1);
700 GETSHORT(ardlen, p1);
701 endrr = p1+ardlen;
702
703 /* TTL of record is minimum of CNAMES and PTR */
704 if (attl < cttl)
705 cttl = attl;
706
707 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
708 {
Simon Kelley9009d742008-11-14 20:04:27 +0000709 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000710 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100711
712 if (aqtype == T_CNAME)
713 {
714 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000715 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100716 goto cname_loop;
717 }
718
719 cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
720 found = 1;
721 }
722
723 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000724 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000725 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100726 }
727 }
728
729 if (!found && !(daemon->options & OPT_NO_NEG))
730 {
731 if (!searched_soa)
732 {
733 searched_soa = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100734 ttl = find_soa(header, qlen);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100735 }
736 if (ttl)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100737 cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000738 }
739 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100740 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000741 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100742 /* everything other than PTR */
743 struct crec *newc;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100744 int addrlen;
745
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100746 if (qtype == T_A)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100747 {
748 addrlen = INADDRSZ;
749 flags |= F_IPV4;
750 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000751#ifdef HAVE_IPV6
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100752 else if (qtype == T_AAAA)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100753 {
754 addrlen = IN6ADDRSZ;
755 flags |= F_IPV6;
756 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000757#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100758 else
759 continue;
760
761 if (!(flags & F_NXDOMAIN))
762 {
763 cname_loop1:
764 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000765 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100766
Simon Kelley5aabfc72007-08-29 11:24:47 +0100767 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000768 {
Simon Kelley9009d742008-11-14 20:04:27 +0000769 if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000770 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100771
772 GETSHORT(aqtype, p1);
773 GETSHORT(aqclass, p1);
774 GETLONG(attl, p1);
775 GETSHORT(ardlen, p1);
776 endrr = p1+ardlen;
777
778 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000779 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100780 if (aqtype == T_CNAME)
781 {
782 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000783 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100784 newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000785 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100786 {
787 cpp->addr.cname.cache = newc;
788 cpp->addr.cname.uid = newc->uid;
789 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000790
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100791 cpp = newc;
792 if (attl < cttl)
793 cttl = attl;
794
Simon Kelley9009d742008-11-14 20:04:27 +0000795 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000796 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100797 goto cname_loop1;
798 }
799 else
800 {
801 found = 1;
Simon Kelley9009d742008-11-14 20:04:27 +0000802
Simon Kelley5aabfc72007-08-29 11:24:47 +0100803 /* copy address into aligned storage */
Simon Kelley9009d742008-11-14 20:04:27 +0000804 if (!CHECK_LEN(header, p1, qlen, addrlen))
805 return 0; /* bad packet */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100806 memcpy(&addr, p1, addrlen);
Simon Kelley824af852008-02-12 20:43:05 +0000807
808 /* check for returned address in private space */
809 if ((daemon->options & OPT_NO_REBIND) &&
810 (flags & F_IPV4) &&
811 private_net(addr.addr.addr4))
812 return 1;
813
Simon Kelley5aabfc72007-08-29 11:24:47 +0100814 newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000815 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100816 {
817 cpp->addr.cname.cache = newc;
818 cpp->addr.cname.uid = newc->uid;
819 }
820 cpp = NULL;
821 }
822 }
823
824 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000825 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000826 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100827 }
828 }
829
830 if (!found && !(daemon->options & OPT_NO_NEG))
831 {
832 if (!searched_soa)
833 {
834 searched_soa = 1;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100835 ttl = find_soa(header, qlen);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100836 }
837 /* If there's no SOA to get the TTL from, but there is a CNAME
Simon Kelley824af852008-02-12 20:43:05 +0000838 pointing at this, inherit its TTL */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100839 if (ttl || cpp)
840 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100841 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
Simon Kelley26128d22004-11-14 16:43:54 +0000842 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100843 {
844 cpp->addr.cname.cache = newc;
845 cpp->addr.cname.uid = newc->uid;
846 }
847 }
848 }
849 }
850 }
851
Simon Kelley824af852008-02-12 20:43:05 +0000852 /* Don't put stuff from a truncated packet into the cache, but do everything else */
853 if (!header->tc)
854 cache_end_insert();
855
856 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000857}
858
859/* If the packet holds exactly one query
Simon Kelley832af0b2007-01-21 20:01:28 +0000860 return F_IPV4 or F_IPV6 and leave the name from the query in name.
861 Abuse F_BIGNAME to indicate an NS query - yuck. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000862
Simon Kelleycdeda282006-03-16 20:16:06 +0000863unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000864{
865 unsigned char *p = (unsigned char *)(header+1);
866 int qtype, qclass;
867
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100868 if (typep)
869 *typep = 0;
870
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000871 if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
872 return 0; /* must be exactly one query. */
873
Simon Kelley9009d742008-11-14 20:04:27 +0000874 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000875 return 0; /* bad packet */
876
877 GETSHORT(qtype, p);
878 GETSHORT(qclass, p);
879
Simon Kelley0a852542005-03-23 20:28:59 +0000880 if (typep)
881 *typep = qtype;
882
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000883 if (qclass == C_IN)
884 {
885 if (qtype == T_A)
886 return F_IPV4;
887 if (qtype == T_AAAA)
888 return F_IPV6;
889 if (qtype == T_ANY)
890 return F_IPV4 | F_IPV6;
Simon Kelley832af0b2007-01-21 20:01:28 +0000891 if (qtype == T_NS || qtype == T_SOA)
892 return F_QUERY | F_BIGNAME;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000893 }
894
895 return F_QUERY;
896}
897
898
Simon Kelleycdeda282006-03-16 20:16:06 +0000899size_t setup_reply(HEADER *header, size_t qlen,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000900 struct all_addr *addrp, unsigned short flags, unsigned long ttl)
901{
902 unsigned char *p = skip_questions(header, qlen);
903
904 header->qr = 1; /* response */
905 header->aa = 0; /* authoritive */
906 header->ra = 1; /* recursion if available */
907 header->tc = 0; /* not truncated */
908 header->nscount = htons(0);
909 header->arcount = htons(0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100910 header->ancount = htons(0); /* no answers unless changed below */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000911 if (flags == F_NEG)
912 header->rcode = SERVFAIL; /* couldn't get memory */
Simon Kelley824af852008-02-12 20:43:05 +0000913 else if (flags == F_NOERR)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000914 header->rcode = NOERROR; /* empty domain */
915 else if (flags == F_NXDOMAIN)
916 header->rcode = NXDOMAIN;
917 else if (p && flags == F_IPV4)
918 { /* we know the address */
919 header->rcode = NOERROR;
920 header->ancount = htons(1);
921 header->aa = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000922 add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000923 }
924#ifdef HAVE_IPV6
925 else if (p && flags == F_IPV6)
926 {
927 header->rcode = NOERROR;
928 header->ancount = htons(1);
929 header->aa = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000930 add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000931 }
932#endif
933 else /* nowhere to forward to */
934 header->rcode = REFUSED;
935
936 return p - (unsigned char *)header;
937}
Simon Kelley36717ee2004-09-20 19:20:58 +0100938
939/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100940int check_for_local_domain(char *name, time_t now)
Simon Kelley36717ee2004-09-20 19:20:58 +0100941{
942 struct crec *crecp;
Simon Kelley0a852542005-03-23 20:28:59 +0000943 struct mx_srv_record *mx;
944 struct txt_record *txt;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100945 struct interface_name *intr;
946 struct ptr_record *ptr;
947
Simon Kelley26128d22004-11-14 16:43:54 +0000948 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100949 (crecp->flags & (F_HOSTS | F_DHCP)))
950 return 1;
951
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000952 for (mx = daemon->mxnames; mx; mx = mx->next)
Simon Kelley0a852542005-03-23 20:28:59 +0000953 if (hostname_isequal(name, mx->name))
Simon Kelley36717ee2004-09-20 19:20:58 +0100954 return 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000955
Simon Kelley0a852542005-03-23 20:28:59 +0000956 for (txt = daemon->txt; txt; txt = txt->next)
957 if (hostname_isequal(name, txt->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000958 return 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100959
960 for (intr = daemon->int_names; intr; intr = intr->next)
961 if (hostname_isequal(name, intr->name))
962 return 1;
963
964 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
965 if (hostname_isequal(name, ptr->name))
966 return 1;
967
Simon Kelley36717ee2004-09-20 19:20:58 +0100968 return 0;
969}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000970
971/* Is the packet a reply with the answer address equal to addr?
972 If so mung is into an NXDOMAIN reply and also put that information
973 in the cache. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000974int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000975 struct bogus_addr *baddr, time_t now)
976{
977 unsigned char *p;
978 int i, qtype, qclass, rdlen;
979 unsigned long ttl;
980 struct bogus_addr *baddrp;
981
982 /* skip over questions */
983 if (!(p = skip_questions(header, qlen)))
984 return 0; /* bad packet */
985
Simon Kelley5aabfc72007-08-29 11:24:47 +0100986 for (i = ntohs(header->ancount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000987 {
Simon Kelley9009d742008-11-14 20:04:27 +0000988 if (!extract_name(header, qlen, &p, name, 1, 10))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000989 return 0; /* bad packet */
990
991 GETSHORT(qtype, p);
992 GETSHORT(qclass, p);
993 GETLONG(ttl, p);
994 GETSHORT(rdlen, p);
995
996 if (qclass == C_IN && qtype == T_A)
Simon Kelley9009d742008-11-14 20:04:27 +0000997 {
998 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
999 return 0;
1000
1001 for (baddrp = baddr; baddrp; baddrp = baddrp->next)
1002 if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
1003 {
1004 /* Found a bogus address. Insert that info here, since there no SOA record
1005 to get the ttl from in the normal processing */
1006 cache_start_insert();
1007 cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
1008 cache_end_insert();
1009
1010 return 1;
1011 }
1012 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001013
Simon Kelley9009d742008-11-14 20:04:27 +00001014 if (!ADD_RDLEN(header, p, qlen, rdlen))
1015 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001016 }
1017
1018 return 0;
1019}
1020
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001021static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
Simon Kelley3d8df262005-08-29 12:19:27 +01001022 unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001023{
1024 va_list ap;
1025 unsigned char *sav, *p = *pp;
1026 int j;
1027 unsigned short usval;
1028 long lval;
1029 char *sval;
1030
1031 if (truncp && *truncp)
1032 return 0;
1033
1034 PUTSHORT(nameoffset | 0xc000, p);
1035 PUTSHORT(type, p);
1036 PUTSHORT(class, p);
1037 PUTLONG(ttl, p); /* TTL */
1038
1039 sav = p; /* Save pointer to RDLength field */
1040 PUTSHORT(0, p); /* Placeholder RDLength */
1041
1042 va_start(ap, format); /* make ap point to 1st unamed argument */
1043
1044 for (; *format; format++)
1045 switch (*format)
1046 {
1047#ifdef HAVE_IPV6
1048 case '6':
1049 sval = va_arg(ap, char *);
1050 memcpy(p, sval, IN6ADDRSZ);
1051 p += IN6ADDRSZ;
1052 break;
1053#endif
1054
1055 case '4':
1056 sval = va_arg(ap, char *);
1057 memcpy(p, sval, INADDRSZ);
1058 p += INADDRSZ;
1059 break;
1060
1061 case 's':
1062 usval = va_arg(ap, int);
1063 PUTSHORT(usval, p);
1064 break;
1065
1066 case 'l':
1067 lval = va_arg(ap, long);
1068 PUTLONG(lval, p);
1069 break;
1070
1071 case 'd':
1072 /* get domain-name answer arg and store it in RDATA field */
Simon Kelley0a852542005-03-23 20:28:59 +00001073 if (offset)
1074 *offset = p - (unsigned char *)header;
Simon Kelley3d8df262005-08-29 12:19:27 +01001075 p = do_rfc1035_name(p, va_arg(ap, char *));
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001076 *p++ = 0;
1077 break;
Simon Kelley3d8df262005-08-29 12:19:27 +01001078
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001079 case 't':
Simon Kelley0a852542005-03-23 20:28:59 +00001080 usval = va_arg(ap, int);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001081 sval = va_arg(ap, char *);
Simon Kelley0a852542005-03-23 20:28:59 +00001082 memcpy(p, sval, usval);
1083 p += usval;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001084 break;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001085
1086 case 'z':
1087 sval = va_arg(ap, char *);
1088 usval = sval ? strlen(sval) : 0;
1089 if (usval > 255)
1090 usval = 255;
1091 *p++ = (unsigned char)usval;
1092 memcpy(p, sval, usval);
1093 p += usval;
1094 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001095 }
1096
1097 va_end(ap); /* clean up variable argument pointer */
1098
1099 j = p - sav - 2;
1100 PUTSHORT(j, sav); /* Now, store real RDLength */
1101
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001102 /* check for overflow of buffer */
1103 if (limit && ((unsigned char *)limit - p) < 0)
1104 {
1105 if (truncp)
1106 *truncp = 1;
1107 return 0;
1108 }
1109
1110 *pp = p;
1111 return 1;
1112}
1113
Simon Kelley9009d742008-11-14 20:04:27 +00001114static unsigned long crec_ttl(struct crec *crecp, time_t now)
1115{
1116 /* Return 0 ttl for DHCP entries, which might change
1117 before the lease expires. */
1118
1119 if (crecp->flags & (F_IMMORTAL | F_DHCP))
1120 return daemon->local_ttl;
1121
1122 return crecp->ttd - now;
1123}
1124
1125
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001126/* return zero if we can't answer from cache, or packet size if we can */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001127size_t answer_request(HEADER *header, char *limit, size_t qlen,
Simon Kelleycdeda282006-03-16 20:16:06 +00001128 struct in_addr local_addr, struct in_addr local_netmask, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001129{
Simon Kelley3be34542004-09-11 19:12:13 +01001130 char *name = daemon->namebuff;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001131 unsigned char *p, *ansp, *pheader;
Simon Kelley832af0b2007-01-21 20:01:28 +00001132 int qtype, qclass;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001133 struct all_addr addr;
1134 unsigned int nameoffset;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001135 unsigned short flag;
Simon Kelley0a852542005-03-23 20:28:59 +00001136 int q, ans, anscount = 0, addncount = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001137 int dryrun = 0, sec_reqd = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001138 int is_sign;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001139 struct crec *crecp;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001140 int nxdomain = 0, auth = 1, trunc = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001141 struct mx_srv_record *rec;
1142
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001143 /* If there is an RFC2671 pseudoheader then it will be overwritten by
1144 partial replies, so we have to do a dry run to see if we can answer
1145 the query. We check to see if the do bit is set, if so we always
1146 forward rather than answering from the cache, which doesn't include
1147 security information. */
1148
Simon Kelley832af0b2007-01-21 20:01:28 +00001149 if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001150 {
1151 unsigned short udpsz, ext_rcode, flags;
1152 unsigned char *psave = pheader;
1153
1154 GETSHORT(udpsz, pheader);
1155 GETSHORT(ext_rcode, pheader);
1156 GETSHORT(flags, pheader);
1157
1158 sec_reqd = flags & 0x8000; /* do bit */
1159
1160 /* If our client is advertising a larger UDP packet size
1161 than we allow, trim it so that we don't get an overlarge
1162 response from upstream */
1163
Simon Kelley832af0b2007-01-21 20:01:28 +00001164 if (!is_sign && (udpsz > daemon->edns_pktsz))
Simon Kelley3be34542004-09-11 19:12:13 +01001165 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001166
1167 dryrun = 1;
1168 }
1169
Simon Kelley5aabfc72007-08-29 11:24:47 +01001170 if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
Simon Kelley832af0b2007-01-21 20:01:28 +00001171 return 0;
1172
Simon Kelley0a852542005-03-23 20:28:59 +00001173 for (rec = daemon->mxnames; rec; rec = rec->next)
1174 rec->offset = 0;
1175
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001176 rerun:
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001177 /* determine end of question section (we put answers there) */
1178 if (!(ansp = skip_questions(header, qlen)))
1179 return 0; /* bad packet */
1180
1181 /* now process each question, answers go in RRs after the question */
1182 p = (unsigned char *)(header+1);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001183
Simon Kelley5aabfc72007-08-29 11:24:47 +01001184 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001185 {
1186 /* save pointer to name for copying into answers */
1187 nameoffset = p - (unsigned char *)header;
1188
1189 /* now extract name as .-concatenated string into name */
Simon Kelley9009d742008-11-14 20:04:27 +00001190 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001191 return 0; /* bad packet */
Simon Kelley832af0b2007-01-21 20:01:28 +00001192
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001193 GETSHORT(qtype, p);
1194 GETSHORT(qclass, p);
1195
1196 ans = 0; /* have we answered this question */
1197
Simon Kelley0a852542005-03-23 20:28:59 +00001198 if (qtype == T_TXT || qtype == T_ANY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001199 {
Simon Kelley0a852542005-03-23 20:28:59 +00001200 struct txt_record *t;
1201 for(t = daemon->txt; t ; t = t->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001202 {
Simon Kelley0a852542005-03-23 20:28:59 +00001203 if (t->class == qclass && hostname_isequal(name, t->name))
1204 {
1205 ans = 1;
Simon Kelleye17fb622006-01-14 20:33:46 +00001206 if (!dryrun)
1207 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001208 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>");
Simon Kelleye17fb622006-01-14 20:33:46 +00001209 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1210 daemon->local_ttl, NULL,
1211 T_TXT, t->class, "t", t->len, t->txt))
1212 anscount++;
1213
1214 }
Simon Kelley0a852542005-03-23 20:28:59 +00001215 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001216 }
Simon Kelley0a852542005-03-23 20:28:59 +00001217 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001218
Simon Kelley0a852542005-03-23 20:28:59 +00001219 if (qclass == C_IN)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001220 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001221 if (qtype == T_PTR || qtype == T_ANY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001222 {
Simon Kelley832af0b2007-01-21 20:01:28 +00001223 /* see if it's w.z.y.z.in-addr.arpa format */
1224 int is_arpa = in_arpa_name_2_addr(name, &addr);
1225 struct ptr_record *ptr;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001226 struct interface_name* intr = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00001227
1228 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1229 if (hostname_isequal(name, ptr->name))
1230 break;
1231
Simon Kelleyf2621c72007-04-29 19:47:21 +01001232 if (is_arpa == F_IPV4)
1233 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001234 {
1235 if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
1236 break;
1237 else
1238 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
1239 intr = intr->next;
1240 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001241
1242 if (intr)
1243 {
1244 ans = 1;
1245 if (!dryrun)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001246 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001247 log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001248 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1249 daemon->local_ttl, NULL,
1250 T_PTR, C_IN, "d", intr->name))
1251 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001252 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001253 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001254 else if (ptr)
1255 {
1256 ans = 1;
1257 if (!dryrun)
1258 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001259 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>");
Simon Kelley832af0b2007-01-21 20:01:28 +00001260 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001261 if (hostname_isequal(name, ptr->name) &&
1262 add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1263 daemon->local_ttl, NULL,
1264 T_PTR, C_IN, "d", ptr->ptr))
1265 anscount++;
1266
Simon Kelley832af0b2007-01-21 20:01:28 +00001267 }
1268 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001269 else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
1270 do
1271 {
1272 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
1273 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1274 continue;
1275
1276 if (crecp->flags & F_NEG)
1277 {
1278 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001279 auth = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001280 if (crecp->flags & F_NXDOMAIN)
1281 nxdomain = 1;
1282 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001283 log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001284 }
1285 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
1286 {
1287 ans = 1;
1288 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1289 auth = 0;
1290 if (!dryrun)
1291 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001292 log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001293 record_source(crecp->uid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001294
Simon Kelley9009d742008-11-14 20:04:27 +00001295 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1296 crec_ttl(crecp, now), NULL,
Simon Kelleyf2621c72007-04-29 19:47:21 +01001297 T_PTR, C_IN, "d", cache_get_name(crecp)))
1298 anscount++;
1299 }
1300 }
1301 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
1302 else if (is_arpa == F_IPV4 &&
1303 (daemon->options & OPT_BOGUSPRIV) &&
1304 private_net(addr.addr.addr4))
1305 {
1306 /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
1307 ans = 1;
1308 nxdomain = 1;
1309 if (!dryrun)
1310 log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
Simon Kelley1a6bca82008-07-11 11:11:42 +01001311 name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001312 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001313 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001314
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001315 for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
1316 {
1317 unsigned short type = T_A;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001318
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001319 if (flag == F_IPV6)
1320#ifdef HAVE_IPV6
Simon Kelley3d8df262005-08-29 12:19:27 +01001321 type = T_AAAA;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001322#else
Simon Kelley3d8df262005-08-29 12:19:27 +01001323 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001324#endif
1325
1326 if (qtype != type && qtype != T_ANY)
1327 continue;
1328
Simon Kelleyf2621c72007-04-29 19:47:21 +01001329 /* Check for "A for A" queries */
Simon Kelley3d8df262005-08-29 12:19:27 +01001330 if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1)
1331 {
1332 ans = 1;
1333 if (!dryrun)
1334 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001335 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +01001336 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1337 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1338 anscount++;
1339 }
1340 continue;
1341 }
1342
Simon Kelleyf2621c72007-04-29 19:47:21 +01001343 /* interface name stuff */
1344 if (qtype == T_A)
1345 {
1346 struct interface_name *intr;
1347
1348 for (intr = daemon->int_names; intr; intr = intr->next)
1349 if (hostname_isequal(name, intr->name))
1350 break;
1351
1352 if (intr)
1353 {
1354 ans = 1;
1355 if (!dryrun)
1356 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001357 if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001358 log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001359 else
1360 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001361 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001362 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1363 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1364 anscount++;
1365 }
1366 }
1367 continue;
1368 }
1369 }
1370
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001371 cname_restart:
1372 if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
1373 {
1374 int localise = 0;
1375
1376 /* See if a putative address is on the network from which we recieved
1377 the query, is so we'll filter other answers. */
1378 if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
1379 {
1380 struct crec *save = crecp;
1381 do {
1382 if ((crecp->flags & F_HOSTS) &&
1383 is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1384 {
1385 localise = 1;
1386 break;
1387 }
1388 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
1389 crecp = save;
1390 }
1391
1392 do
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001393 {
Simon Kelley26128d22004-11-14 16:43:54 +00001394 /* don't answer wildcard queries with data not from /etc/hosts
1395 or DHCP leases */
1396 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1397 break;
1398
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001399 if (crecp->flags & F_CNAME)
1400 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001401 if (!dryrun)
1402 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001403 log_query(crecp->flags, name, NULL, record_source(crecp->uid));
Simon Kelley9009d742008-11-14 20:04:27 +00001404 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1405 crec_ttl(crecp, now), &nameoffset,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001406 T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
1407 anscount++;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001408 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001409
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001410 strcpy(name, cache_get_name(crecp->addr.cname.cache));
1411 goto cname_restart;
1412 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001413
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001414 if (crecp->flags & F_NEG)
1415 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001416 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001417 auth = 0;
1418 if (crecp->flags & F_NXDOMAIN)
1419 nxdomain = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001420 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001421 log_query(crecp->flags, name, NULL, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001422 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001423 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001424 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001425 /* If we are returning local answers depending on network,
1426 filter here. */
1427 if (localise &&
1428 (crecp->flags & F_HOSTS) &&
1429 !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1430 continue;
1431
1432 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1433 auth = 0;
1434
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001435 ans = 1;
1436 if (!dryrun)
1437 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001438 log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001439 record_source(crecp->uid));
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001440
Simon Kelley9009d742008-11-14 20:04:27 +00001441 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1442 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001443 type == T_A ? "4" : "6", &crecp->addr))
1444 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001445 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001446 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001447 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001448 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001449 }
1450
1451 if (qtype == T_MX || qtype == T_ANY)
1452 {
1453 int found = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001454 for (rec = daemon->mxnames; rec; rec = rec->next)
1455 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001456 {
1457 ans = found = 1;
1458 if (!dryrun)
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001459 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001460 unsigned int offset;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001461 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
Simon Kelley0a852542005-03-23 20:28:59 +00001462 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1463 &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
1464 {
1465 anscount++;
1466 if (rec->target)
1467 rec->offset = offset;
1468 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001469 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001470 }
1471
1472 if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
1473 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
1474 {
1475 ans = 1;
1476 if (!dryrun)
1477 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001478 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001479 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
1480 T_MX, C_IN, "sd", 1,
1481 (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
1482 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001483 }
1484 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001485 }
1486
1487 if (qtype == T_SRV || qtype == T_ANY)
1488 {
1489 int found = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001490
Simon Kelley0a852542005-03-23 20:28:59 +00001491 for (rec = daemon->mxnames; rec; rec = rec->next)
1492 if (rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001493 {
1494 found = ans = 1;
1495 if (!dryrun)
1496 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001497 unsigned int offset;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001498 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001499 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
Simon Kelley0a852542005-03-23 20:28:59 +00001500 &offset, T_SRV, C_IN, "sssd",
1501 rec->priority, rec->weight, rec->srvport, rec->target))
1502 {
1503 anscount++;
1504 if (rec->target)
1505 rec->offset = offset;
1506 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001507 }
1508 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001509
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001510 if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
1511 {
1512 ans = 1;
1513 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001514 log_query(F_CONFIG | F_NEG, name, NULL, NULL);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001515 }
1516 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001517
1518 if (qtype == T_NAPTR || qtype == T_ANY)
1519 {
1520 struct naptr *na;
1521 for (na = daemon->naptr; na; na = na->next)
1522 if (hostname_isequal(name, na->name))
1523 {
1524 ans = 1;
1525 if (!dryrun)
1526 {
1527 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>");
1528 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1529 NULL, T_NAPTR, C_IN, "sszzzd",
1530 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
1531 anscount++;
1532 }
1533 }
1534 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001535
1536 if (qtype == T_MAILB)
1537 ans = 1, nxdomain = 1;
1538
1539 if (qtype == T_SOA && (daemon->options & OPT_FILTER))
1540 {
1541 ans = 1;
1542 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001543 log_query(F_CONFIG | F_NEG, name, &addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001544 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001545 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001546
1547 if (!ans)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001548 return 0; /* failed to answer a question */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001549 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001550
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001551 if (dryrun)
1552 {
1553 dryrun = 0;
1554 goto rerun;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001555 }
1556
Simon Kelley0a852542005-03-23 20:28:59 +00001557 /* create an additional data section, for stuff in SRV and MX record replies. */
1558 for (rec = daemon->mxnames; rec; rec = rec->next)
1559 if (rec->offset != 0)
1560 {
1561 /* squash dupes */
1562 struct mx_srv_record *tmp;
1563 for (tmp = rec->next; tmp; tmp = tmp->next)
1564 if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
1565 tmp->offset = 0;
1566
1567 crecp = NULL;
1568 while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
1569 {
Simon Kelley0a852542005-03-23 20:28:59 +00001570#ifdef HAVE_IPV6
1571 int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
1572#else
1573 int type = T_A;
1574#endif
1575 if (crecp->flags & F_NEG)
1576 continue;
1577
Simon Kelley9009d742008-11-14 20:04:27 +00001578 if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
1579 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelley0a852542005-03-23 20:28:59 +00001580 crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
1581 addncount++;
1582 }
1583 }
1584
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001585 /* done all questions, set up header and return length of result */
1586 header->qr = 1; /* response */
1587 header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
1588 header->ra = 1; /* recursion if available */
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001589 header->tc = trunc; /* truncation */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001590 if (anscount == 0 && nxdomain)
1591 header->rcode = NXDOMAIN;
1592 else
1593 header->rcode = NOERROR; /* no error */
1594 header->ancount = htons(anscount);
1595 header->nscount = htons(0);
Simon Kelley0a852542005-03-23 20:28:59 +00001596 header->arcount = htons(addncount);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001597 return ansp - (unsigned char *)header;
1598}
1599
1600
1601
1602
1603