blob: f03b6df7859669be466c664e9091a4a8f4f3cc3b [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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 Kelley572b41e2011-02-18 18:11:18 +000019static int add_resource_record(struct dns_header *header, char *limit, int *truncp,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000020 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 Kelley572b41e2011-02-18 18:11:18 +000030static int extract_name(struct dns_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 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100141 unsigned char c = *p;
142 if (isascii(c) && !iscntrl(c) && c != '.')
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000143 *cp++ = *p;
144 else
145 return 0;
146 }
147 else
148 {
149 unsigned char c1 = *cp, c2 = *p;
150
151 if (c1 == 0)
152 retvalue = 2;
153 else
154 {
155 cp++;
156 if (c1 >= 'A' && c1 <= 'Z')
157 c1 += 'a' - 'A';
158 if (c2 >= 'A' && c2 <= 'Z')
159 c2 += 'a' - 'A';
160
161 if (c1 != c2)
162 retvalue = 2;
163 }
164 }
165
166 if (isExtract)
167 *cp++ = '.';
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000168 else if (*cp != 0 && *cp++ != '.')
169 retvalue = 2;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000170 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000171 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000172}
173
174/* Max size of input string (for IPv6) is 75 chars.) */
175#define MAXARPANAME 75
176static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
177{
178 int j;
179 char name[MAXARPANAME+1], *cp1;
180 unsigned char *addr = (unsigned char *)addrp;
181 char *lastchunk = NULL, *penchunk = NULL;
182
183 if (strlen(namein) > MAXARPANAME)
184 return 0;
185
186 memset(addrp, 0, sizeof(struct all_addr));
187
188 /* turn name into a series of asciiz strings */
189 /* j counts no of labels */
190 for(j = 1,cp1 = name; *namein; cp1++, namein++)
191 if (*namein == '.')
192 {
193 penchunk = lastchunk;
194 lastchunk = cp1 + 1;
195 *cp1 = 0;
196 j++;
197 }
198 else
199 *cp1 = *namein;
200
201 *cp1 = 0;
202
203 if (j<3)
204 return 0;
205
206 if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))
207 {
208 /* IP v4 */
209 /* address arives as a name of the form
210 www.xxx.yyy.zzz.in-addr.arpa
211 some of the low order address octets might be missing
212 and should be set to zero. */
213 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
214 {
215 /* check for digits only (weeds out things like
216 50.0/24.67.28.64.in-addr.arpa which are used
217 as CNAME targets according to RFC 2317 */
218 char *cp;
219 for (cp = cp1; *cp; cp++)
Simon Kelley572b41e2011-02-18 18:11:18 +0000220 if (!isdigit((unsigned char)*cp))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000221 return 0;
222
223 addr[3] = addr[2];
224 addr[2] = addr[1];
225 addr[1] = addr[0];
226 addr[0] = atoi(cp1);
227 }
228
229 return F_IPV4;
230 }
231#ifdef HAVE_IPV6
232 else if (hostname_isequal(penchunk, "ip6") &&
233 (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
234 {
235 /* IP v6:
236 Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
237 or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
238
239 Note that most of these the various reprentations are obsolete and
240 left-over from the many DNS-for-IPv6 wars. We support all the formats
241 that we can since there is no reason not to.
242 */
243
244 if (*name == '\\' && *(name+1) == '[' &&
245 (*(name+2) == 'x' || *(name+2) == 'X'))
246 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000247 for (j = 0, cp1 = name+3; *cp1 && isxdigit((unsigned char) *cp1) && j < 32; cp1++, j++)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000248 {
249 char xdig[2];
250 xdig[0] = *cp1;
251 xdig[1] = 0;
252 if (j%2)
253 addr[j/2] |= strtol(xdig, NULL, 16);
254 else
255 addr[j/2] = strtol(xdig, NULL, 16) << 4;
256 }
257
258 if (*cp1 == '/' && j == 32)
259 return F_IPV6;
260 }
261 else
262 {
263 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
264 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000265 if (*(cp1+1) || !isxdigit((unsigned char)*cp1))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000266 return 0;
267
268 for (j = sizeof(struct all_addr)-1; j>0; j--)
269 addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);
270 addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
271 }
272
273 return F_IPV6;
274 }
275 }
276#endif
277
278 return 0;
279}
280
Simon Kelley572b41e2011-02-18 18:11:18 +0000281static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100282{
283 while(1)
284 {
Simon Kelley9009d742008-11-14 20:04:27 +0000285 unsigned int label_type;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100286
Simon Kelley9009d742008-11-14 20:04:27 +0000287 if (!CHECK_LEN(header, ansp, plen, 1))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100288 return NULL;
289
Simon Kelley9009d742008-11-14 20:04:27 +0000290 label_type = (*ansp) & 0xc0;
291
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100292 if (label_type == 0xc0)
293 {
294 /* pointer for compression. */
295 ansp += 2;
296 break;
297 }
298 else if (label_type == 0x80)
299 return NULL; /* reserved */
300 else if (label_type == 0x40)
301 {
302 /* Extended label type */
303 unsigned int count;
304
Simon Kelley9009d742008-11-14 20:04:27 +0000305 if (!CHECK_LEN(header, ansp, plen, 2))
306 return NULL;
307
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100308 if (((*ansp++) & 0x3f) != 1)
309 return NULL; /* we only understand bitstrings */
310
311 count = *(ansp++); /* Bits in bitstring */
312
313 if (count == 0) /* count == 0 means 256 bits */
314 ansp += 32;
315 else
316 ansp += ((count-1)>>3)+1;
317 }
318 else
319 { /* label type == 0 Bottom six bits is length */
320 unsigned int len = (*ansp++) & 0x3f;
Simon Kelley9009d742008-11-14 20:04:27 +0000321
322 if (!ADD_RDLEN(header, ansp, plen, len))
323 return NULL;
324
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100325 if (len == 0)
326 break; /* zero length label marks the end. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100327 }
328 }
Simon Kelley9009d742008-11-14 20:04:27 +0000329
330 if (!CHECK_LEN(header, ansp, plen, extrabytes))
331 return NULL;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100332
333 return ansp;
334}
335
Simon Kelley572b41e2011-02-18 18:11:18 +0000336static unsigned char *skip_questions(struct dns_header *header, size_t plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000337{
Simon Kelley5aabfc72007-08-29 11:24:47 +0100338 int q;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000339 unsigned char *ansp = (unsigned char *)(header+1);
340
Simon Kelley5aabfc72007-08-29 11:24:47 +0100341 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000342 {
Simon Kelley9009d742008-11-14 20:04:27 +0000343 if (!(ansp = skip_name(ansp, header, plen, 4)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100344 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000345 ansp += 4; /* class and type */
346 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000347
348 return ansp;
349}
350
Simon Kelley572b41e2011-02-18 18:11:18 +0000351static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100352{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100353 int i, rdlen;
Simon Kelley36717ee2004-09-20 19:20:58 +0100354
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100355 for (i = 0; i < count; i++)
Simon Kelley36717ee2004-09-20 19:20:58 +0100356 {
Simon Kelley9009d742008-11-14 20:04:27 +0000357 if (!(ansp = skip_name(ansp, header, plen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100358 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100359 ansp += 8; /* type, class, TTL */
360 GETSHORT(rdlen, ansp);
Simon Kelley9009d742008-11-14 20:04:27 +0000361 if (!ADD_RDLEN(header, ansp, plen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100362 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100363 }
364
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100365 return ansp;
366}
367
Simon Kelley0a852542005-03-23 20:28:59 +0000368/* CRC the question section. This is used to safely detect query
369 retransmision and to detect answers to questions we didn't ask, which
370 might be poisoning attacks. Note that we decode the name rather
371 than CRC the raw bytes, since replies might be compressed differently.
Simon Kelley832af0b2007-01-21 20:01:28 +0000372 We ignore case in the names for the same reason. Return all-ones
373 if there is not question section. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000374unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100375{
Simon Kelley91dccd02005-03-31 17:48:32 +0100376 int q;
377 unsigned int crc = 0xffffffff;
Simon Kelley0a852542005-03-23 20:28:59 +0000378 unsigned char *p1, *p = (unsigned char *)(header+1);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100379
Simon Kelley5aabfc72007-08-29 11:24:47 +0100380 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley0a852542005-03-23 20:28:59 +0000381 {
Simon Kelley9009d742008-11-14 20:04:27 +0000382 if (!extract_name(header, plen, &p, name, 1, 4))
Simon Kelley0a852542005-03-23 20:28:59 +0000383 return crc; /* bad packet */
384
Simon Kelley3d8df262005-08-29 12:19:27 +0100385 for (p1 = (unsigned char *)name; *p1; p1++)
Simon Kelley0a852542005-03-23 20:28:59 +0000386 {
387 int i = 8;
388 char c = *p1;
389
390 if (c >= 'A' && c <= 'Z')
391 c += 'a' - 'A';
392
393 crc ^= c << 24;
394 while (i--)
395 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
396 }
397
398 /* CRC the class and type as well */
399 for (p1 = p; p1 < p+4; p1++)
400 {
401 int i = 8;
402 crc ^= *p1 << 24;
403 while (i--)
404 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
405 }
406
407 p += 4;
Simon Kelley9009d742008-11-14 20:04:27 +0000408 if (!CHECK_LEN(header, p, plen, 0))
Simon Kelley0a852542005-03-23 20:28:59 +0000409 return crc; /* bad packet */
410 }
411
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100412 return crc;
413}
414
415
Simon Kelley572b41e2011-02-18 18:11:18 +0000416size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100417{
418 unsigned char *ansp = skip_questions(header, plen);
419
Simon Kelley9009d742008-11-14 20:04:27 +0000420 /* if packet is malformed, just return as-is. */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100421 if (!ansp)
Simon Kelley9009d742008-11-14 20:04:27 +0000422 return plen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100423
424 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
425 header, plen)))
Simon Kelley9009d742008-11-14 20:04:27 +0000426 return plen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100427
Simon Kelley36717ee2004-09-20 19:20:58 +0100428 /* restore pseudoheader */
429 if (pheader && ntohs(header->arcount) == 0)
430 {
431 /* must use memmove, may overlap */
432 memmove(ansp, pheader, hlen);
433 header->arcount = htons(1);
434 ansp += hlen;
435 }
436
437 return ansp - (unsigned char *)header;
438}
439
Simon Kelley572b41e2011-02-18 18:11:18 +0000440unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
Simon Kelley36717ee2004-09-20 19:20:58 +0100441{
442 /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
Simon Kelley832af0b2007-01-21 20:01:28 +0000443 also return length of pseudoheader in *len and pointer to the UDP size in *p
444 Finally, check to see if a packet is signed. If it is we cannot change a single bit before
445 forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100446
447 int i, arcount = ntohs(header->arcount);
Simon Kelley832af0b2007-01-21 20:01:28 +0000448 unsigned char *ansp = (unsigned char *)(header+1);
449 unsigned short rdlen, type, class;
450 unsigned char *ret = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000451
452 if (is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000453 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000454 *is_sign = 0;
455
Simon Kelley572b41e2011-02-18 18:11:18 +0000456 if (OPCODE(header) == QUERY)
Simon Kelley832af0b2007-01-21 20:01:28 +0000457 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100458 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000459 {
Simon Kelley9009d742008-11-14 20:04:27 +0000460 if (!(ansp = skip_name(ansp, header, plen, 4)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000461 return NULL;
462
463 GETSHORT(type, ansp);
464 GETSHORT(class, ansp);
465
466 if (class == C_IN && type == T_TKEY)
467 *is_sign = 1;
468 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000469 }
470 }
471 else
472 {
473 if (!(ansp = skip_questions(header, plen)))
474 return NULL;
475 }
476
477 if (arcount == 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100478 return NULL;
479
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100480 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
481 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000482
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100483 for (i = 0; i < arcount; i++)
484 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100485 unsigned char *save, *start = ansp;
Simon Kelley9009d742008-11-14 20:04:27 +0000486 if (!(ansp = skip_name(ansp, header, plen, 10)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100487 return NULL;
488
489 GETSHORT(type, ansp);
490 save = ansp;
Simon Kelley832af0b2007-01-21 20:01:28 +0000491 GETSHORT(class, ansp);
492 ansp += 4; /* TTL */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100493 GETSHORT(rdlen, ansp);
Simon Kelley9009d742008-11-14 20:04:27 +0000494 if (!ADD_RDLEN(header, ansp, plen, rdlen))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100495 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000496 if (type == T_OPT)
Simon Kelley36717ee2004-09-20 19:20:58 +0100497 {
498 if (len)
499 *len = ansp - start;
500 if (p)
501 *p = save;
Simon Kelley832af0b2007-01-21 20:01:28 +0000502 ret = start;
Simon Kelley36717ee2004-09-20 19:20:58 +0100503 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000504 else if (is_sign &&
505 i == arcount - 1 &&
506 class == C_ANY &&
507 (type == T_SIG || type == T_TSIG))
508 *is_sign = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100509 }
510
Simon Kelley832af0b2007-01-21 20:01:28 +0000511 return ret;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100512}
Simon Kelley28866e92011-02-14 20:19:14 +0000513
514struct macparm {
515 unsigned char *limit;
Simon Kelley572b41e2011-02-18 18:11:18 +0000516 struct dns_header *header;
Simon Kelley28866e92011-02-14 20:19:14 +0000517 size_t plen;
518 union mysockaddr *l3;
519};
520
521static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
522{
523 struct macparm *parm = parmv;
524 int match = 0;
525 unsigned short rdlen;
Simon Kelley572b41e2011-02-18 18:11:18 +0000526 struct dns_header *header = parm->header;
Simon Kelley28866e92011-02-14 20:19:14 +0000527 unsigned char *lenp, *datap, *p;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100528
Simon Kelley28866e92011-02-14 20:19:14 +0000529 if (family == parm->l3->sa.sa_family)
530 {
531 if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
532 match = 1;
533#ifdef HAVE_IPV6
534 else
535 if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
536 match = 1;
537#endif
538 }
539
540 if (!match)
541 return 1; /* continue */
542
543 if (ntohs(header->arcount) == 0)
544 {
545 /* We are adding the pseudoheader */
546 if (!(p = skip_questions(header, parm->plen)) ||
547 !(p = skip_section(p,
548 ntohs(header->ancount) + ntohs(header->nscount),
549 header, parm->plen)))
550 return 0;
551 *p++ = 0; /* empty name */
552 PUTSHORT(T_OPT, p);
553 PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */
554 PUTLONG(0, p); /* extended RCODE */
555 lenp = p;
556 PUTSHORT(0, p); /* RDLEN */
557 rdlen = 0;
558 if (((ssize_t)maclen) > (parm->limit - (p + 4)))
559 return 0; /* Too big */
560 header->arcount = htons(1);
561 datap = p;
562 }
563 else
564 {
565 int i, is_sign;
566 unsigned short code, len;
567
568 if (ntohs(header->arcount) != 1 ||
569 !(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) ||
570 is_sign ||
571 (!(p = skip_name(p, header, parm->plen, 10))))
572 return 0;
573
574 p += 8; /* skip UDP length and RCODE */
575
576 lenp = p;
577 GETSHORT(rdlen, p);
578 if (!CHECK_LEN(header, p, parm->plen, rdlen))
579 return 0; /* bad packet */
580 datap = p;
581
582 /* check if option already there */
583 for (i = 0; i + 4 < rdlen; i += len + 4)
584 {
585 GETSHORT(code, p);
586 GETSHORT(len, p);
587 if (code == EDNS0_OPTION_MAC)
588 return 0;
589 p += len;
590 }
591
592 if (((ssize_t)maclen) > (parm->limit - (p + 4)))
593 return 0; /* Too big */
594 }
595
596 PUTSHORT(EDNS0_OPTION_MAC, p);
597 PUTSHORT(maclen, p);
598 memcpy(p, mac, maclen);
599 p += maclen;
600
601 PUTSHORT(p - datap, lenp);
602 parm->plen = p - (unsigned char *)header;
603
604 return 0; /* done */
605}
606
607
Simon Kelley572b41e2011-02-18 18:11:18 +0000608size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
Simon Kelley28866e92011-02-14 20:19:14 +0000609{
610 struct macparm parm;
611
612/* Must have an existing pseudoheader as the only ar-record,
613 or have no ar-records. Must also not be signed */
614
615 if (ntohs(header->arcount) > 1)
616 return plen;
617
618 parm.header = header;
619 parm.limit = (unsigned char *)limit;
620 parm.plen = plen;
621 parm.l3 = l3;
622
623 iface_enumerate(AF_UNSPEC, &parm, filter_mac);
624
625 return parm.plen;
626}
627
628
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000629/* is addr in the non-globally-routed IP space? */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100630static int private_net(struct in_addr addr, int ban_localhost)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000631{
Simon Kelleyf2621c72007-04-29 19:47:21 +0100632 in_addr_t ip_addr = ntohl(addr.s_addr);
633
634 return
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100635 (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
Simon Kelleyf2621c72007-04-29 19:47:21 +0100636 ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
637 ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
638 ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
639 ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000640}
Simon Kelley1cff1662004-03-12 08:12:58 +0000641
Simon Kelley572b41e2011-02-18 18:11:18 +0000642static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
Simon Kelley824af852008-02-12 20:43:05 +0000643{
644 int i, qtype, qclass, rdlen;
Simon Kelley824af852008-02-12 20:43:05 +0000645
646 for (i = count; i != 0; i--)
647 {
Simon Kelley28866e92011-02-14 20:19:14 +0000648 if (name && option_bool(OPT_LOG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100649 {
650 if (!extract_name(header, qlen, &p, name, 1, 10))
651 return 0;
652 }
653 else if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000654 return 0; /* bad packet */
655
656 GETSHORT(qtype, p);
657 GETSHORT(qclass, p);
Simon Kelley7de060b2011-08-26 17:24:52 +0100658 p += 4; /* ttl */
Simon Kelley824af852008-02-12 20:43:05 +0000659 GETSHORT(rdlen, p);
660
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100661 if (qclass == C_IN && qtype == T_A)
Simon Kelley824af852008-02-12 20:43:05 +0000662 {
663 struct doctor *doctor;
664 struct in_addr addr;
665
Simon Kelley9009d742008-11-14 20:04:27 +0000666 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
667 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100668
669 /* alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000670 memcpy(&addr, p, INADDRSZ);
671
672 for (doctor = daemon->doctors; doctor; doctor = doctor->next)
Simon Kelley73a08a22009-02-05 20:28:08 +0000673 {
674 if (doctor->end.s_addr == 0)
675 {
676 if (!is_same_net(doctor->in, addr, doctor->mask))
677 continue;
678 }
679 else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
680 ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
681 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100682
Simon Kelley73a08a22009-02-05 20:28:08 +0000683 addr.s_addr &= ~doctor->mask.s_addr;
684 addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
685 /* Since we munged the data, the server it came from is no longer authoritative */
Simon Kelley572b41e2011-02-18 18:11:18 +0000686 header->hb3 &= ~HB3_AA;
Simon Kelley73a08a22009-02-05 20:28:08 +0000687 memcpy(p, &addr, INADDRSZ);
688 break;
689 }
Simon Kelley824af852008-02-12 20:43:05 +0000690 }
Simon Kelley28866e92011-02-14 20:19:14 +0000691 else if (qtype == T_TXT && name && option_bool(OPT_LOG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100692 {
693 unsigned char *p1 = p;
694 if (!CHECK_LEN(header, p1, qlen, rdlen))
695 return 0;
696 while ((p1 - p) < rdlen)
697 {
698 unsigned int i, len = *p1;
699 unsigned char *p2 = p1;
700 /* make counted string zero-term and sanitise */
701 for (i = 0; i < len; i++)
702 if (isprint(*(p2+1)))
703 {
704 *p2 = *(p2+1);
705 p2++;
706 }
707 *p2 = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000708 my_syslog(LOG_INFO, "reply %s is %s", name, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100709 /* restore */
710 memmove(p1 + 1, p1, len);
711 *p1 = len;
712 p1 += len+1;
713 }
714 }
Simon Kelley824af852008-02-12 20:43:05 +0000715
Simon Kelley9009d742008-11-14 20:04:27 +0000716 if (!ADD_RDLEN(header, p, qlen, rdlen))
717 return 0; /* bad packet */
Simon Kelley824af852008-02-12 20:43:05 +0000718 }
719
720 return p;
721}
722
Simon Kelley572b41e2011-02-18 18:11:18 +0000723static int find_soa(struct dns_header *header, size_t qlen, char *name)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000724{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100725 unsigned char *p;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000726 int qtype, qclass, rdlen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100727 unsigned long ttl, minttl = ULONG_MAX;
728 int i, found_soa = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000729
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100730 /* first move to NS section and find TTL from any SOA section */
731 if (!(p = skip_questions(header, qlen)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100732 !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
Simon Kelley824af852008-02-12 20:43:05 +0000733 return 0; /* bad packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000734
Simon Kelley5aabfc72007-08-29 11:24:47 +0100735 for (i = ntohs(header->nscount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000736 {
Simon Kelley9009d742008-11-14 20:04:27 +0000737 if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100738 return 0; /* bad packet */
739
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000740 GETSHORT(qtype, p);
741 GETSHORT(qclass, p);
742 GETLONG(ttl, p);
743 GETSHORT(rdlen, p);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100744
745 if ((qclass == C_IN) && (qtype == T_SOA))
746 {
747 found_soa = 1;
748 if (ttl < minttl)
749 minttl = ttl;
750
751 /* MNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000752 if (!(p = skip_name(p, header, qlen, 0)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100753 return 0;
754 /* RNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000755 if (!(p = skip_name(p, header, qlen, 20)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100756 return 0;
757 p += 16; /* SERIAL REFRESH RETRY EXPIRE */
758
759 GETLONG(ttl, p); /* minTTL */
760 if (ttl < minttl)
761 minttl = ttl;
762 }
Simon Kelley9009d742008-11-14 20:04:27 +0000763 else if (!ADD_RDLEN(header, p, qlen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100764 return 0; /* bad packet */
765 }
Simon Kelley9009d742008-11-14 20:04:27 +0000766
Simon Kelley824af852008-02-12 20:43:05 +0000767 /* rewrite addresses in additioal section too */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100768 if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
Simon Kelley824af852008-02-12 20:43:05 +0000769 return 0;
770
771 if (!found_soa)
772 minttl = daemon->neg_ttl;
773
774 return minttl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100775}
776
777/* Note that the following code can create CNAME chains that don't point to a real record,
778 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 +0000779 expired and cleaned out that way.
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100780 Return 1 if we reject an address because it look like part of dns-rebinding attack. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000781int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
Simon Kelley28866e92011-02-14 20:19:14 +0000782 int is_sign, int check_rebind, int checking_disabled)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100783{
Simon Kelley824af852008-02-12 20:43:05 +0000784 unsigned char *p, *p1, *endrr, *namep;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100785 int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000786 unsigned long ttl = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100787 struct all_addr addr;
Simon Kelley0a852542005-03-23 20:28:59 +0000788
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100789 cache_start_insert();
Simon Kelley0a852542005-03-23 20:28:59 +0000790
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100791 /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
Simon Kelley28866e92011-02-14 20:19:14 +0000792 if (daemon->doctors || option_bool(OPT_LOG))
Simon Kelley0a852542005-03-23 20:28:59 +0000793 {
794 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100795 ttl = find_soa(header, qlen, name);
Simon Kelley0a852542005-03-23 20:28:59 +0000796 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100797
798 /* go through the questions. */
799 p = (unsigned char *)(header+1);
800
Simon Kelley5aabfc72007-08-29 11:24:47 +0100801 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100802 {
803 int found = 0, cname_count = 5;
804 struct crec *cpp = NULL;
Simon Kelley572b41e2011-02-18 18:11:18 +0000805 int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000806 unsigned long cttl = ULONG_MAX, attl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100807
Simon Kelley824af852008-02-12 20:43:05 +0000808 namep = p;
Simon Kelley9009d742008-11-14 20:04:27 +0000809 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley824af852008-02-12 20:43:05 +0000810 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100811
812 GETSHORT(qtype, p);
813 GETSHORT(qclass, p);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000814
815 if (qclass != C_IN)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100816 continue;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000817
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100818 /* PTRs: we chase CNAMEs here, since we have no way to
819 represent them in the cache. */
820 if (qtype == T_PTR)
821 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000822 int name_encoding = in_arpa_name_2_addr(name, &addr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100823
824 if (!name_encoding)
825 continue;
826
827 if (!(flags & F_NXDOMAIN))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000828 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100829 cname_loop:
830 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000831 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100832
Simon Kelley5aabfc72007-08-29 11:24:47 +0100833 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100834 {
Simon Kelley824af852008-02-12 20:43:05 +0000835 unsigned char *tmp = namep;
836 /* the loop body overwrites the original name, so get it back here. */
Simon Kelley9009d742008-11-14 20:04:27 +0000837 if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
838 !(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000839 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100840
841 GETSHORT(aqtype, p1);
842 GETSHORT(aqclass, p1);
843 GETLONG(attl, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100844 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
845 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000846 (p1) -= 4;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100847 PUTLONG(daemon->max_ttl, p1);
848 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100849 GETSHORT(ardlen, p1);
850 endrr = p1+ardlen;
851
852 /* TTL of record is minimum of CNAMES and PTR */
853 if (attl < cttl)
854 cttl = attl;
855
856 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
857 {
Simon Kelley9009d742008-11-14 20:04:27 +0000858 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000859 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100860
861 if (aqtype == T_CNAME)
862 {
863 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000864 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100865 goto cname_loop;
866 }
867
868 cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
869 found = 1;
870 }
871
872 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000873 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000874 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100875 }
876 }
877
Simon Kelley28866e92011-02-14 20:19:14 +0000878 if (!found && !option_bool(OPT_NO_NEG))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100879 {
880 if (!searched_soa)
881 {
882 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100883 ttl = find_soa(header, qlen, NULL);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100884 }
885 if (ttl)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100886 cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000887 }
888 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100889 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000890 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100891 /* everything other than PTR */
892 struct crec *newc;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100893 int addrlen;
894
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100895 if (qtype == T_A)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100896 {
897 addrlen = INADDRSZ;
898 flags |= F_IPV4;
899 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000900#ifdef HAVE_IPV6
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100901 else if (qtype == T_AAAA)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100902 {
903 addrlen = IN6ADDRSZ;
904 flags |= F_IPV6;
905 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000906#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100907 else
908 continue;
909
910 if (!(flags & F_NXDOMAIN))
911 {
912 cname_loop1:
913 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000914 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100915
Simon Kelley5aabfc72007-08-29 11:24:47 +0100916 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000917 {
Simon Kelley9009d742008-11-14 20:04:27 +0000918 if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000919 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100920
921 GETSHORT(aqtype, p1);
922 GETSHORT(aqclass, p1);
923 GETLONG(attl, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100924 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
925 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000926 (p1) -= 4;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100927 PUTLONG(daemon->max_ttl, p1);
928 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100929 GETSHORT(ardlen, p1);
930 endrr = p1+ardlen;
931
932 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000933 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100934 if (aqtype == T_CNAME)
935 {
936 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000937 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100938 newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000939 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100940 {
941 cpp->addr.cname.cache = newc;
942 cpp->addr.cname.uid = newc->uid;
943 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000944
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100945 cpp = newc;
946 if (attl < cttl)
947 cttl = attl;
948
Simon Kelley9009d742008-11-14 20:04:27 +0000949 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000950 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100951 goto cname_loop1;
952 }
953 else
954 {
955 found = 1;
Simon Kelley9009d742008-11-14 20:04:27 +0000956
Simon Kelley5aabfc72007-08-29 11:24:47 +0100957 /* copy address into aligned storage */
Simon Kelley9009d742008-11-14 20:04:27 +0000958 if (!CHECK_LEN(header, p1, qlen, addrlen))
959 return 0; /* bad packet */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100960 memcpy(&addr, p1, addrlen);
Simon Kelley824af852008-02-12 20:43:05 +0000961
962 /* check for returned address in private space */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100963 if (check_rebind &&
Simon Kelley824af852008-02-12 20:43:05 +0000964 (flags & F_IPV4) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000965 private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
Simon Kelley824af852008-02-12 20:43:05 +0000966 return 1;
967
Simon Kelley5aabfc72007-08-29 11:24:47 +0100968 newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000969 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100970 {
971 cpp->addr.cname.cache = newc;
972 cpp->addr.cname.uid = newc->uid;
973 }
974 cpp = NULL;
975 }
976 }
977
978 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000979 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000980 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100981 }
982 }
983
Simon Kelley28866e92011-02-14 20:19:14 +0000984 if (!found && !option_bool(OPT_NO_NEG))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100985 {
986 if (!searched_soa)
987 {
988 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100989 ttl = find_soa(header, qlen, NULL);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100990 }
991 /* If there's no SOA to get the TTL from, but there is a CNAME
Simon Kelley824af852008-02-12 20:43:05 +0000992 pointing at this, inherit its TTL */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100993 if (ttl || cpp)
994 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100995 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
Simon Kelley26128d22004-11-14 16:43:54 +0000996 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100997 {
998 cpp->addr.cname.cache = newc;
999 cpp->addr.cname.uid = newc->uid;
1000 }
1001 }
1002 }
1003 }
1004 }
1005
Simon Kelley28866e92011-02-14 20:19:14 +00001006 /* Don't put stuff from a truncated packet into the cache,
1007 also don't cache replies where DNSSEC validation was turned off, either
1008 the upstream server told us so, or the original query specified it. */
Simon Kelley572b41e2011-02-18 18:11:18 +00001009 if (!(header->hb3 & HB3_TC) && !(header->hb4 & HB4_CD) && !checking_disabled)
Simon Kelley824af852008-02-12 20:43:05 +00001010 cache_end_insert();
1011
1012 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001013}
1014
1015/* If the packet holds exactly one query
Simon Kelley28866e92011-02-14 20:19:14 +00001016 return F_IPV4 or F_IPV6 and leave the name from the query in name */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001017
Simon Kelley572b41e2011-02-18 18:11:18 +00001018unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001019{
1020 unsigned char *p = (unsigned char *)(header+1);
1021 int qtype, qclass;
1022
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001023 if (typep)
1024 *typep = 0;
1025
Simon Kelley572b41e2011-02-18 18:11:18 +00001026 if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001027 return 0; /* must be exactly one query. */
1028
Simon Kelley9009d742008-11-14 20:04:27 +00001029 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001030 return 0; /* bad packet */
1031
1032 GETSHORT(qtype, p);
1033 GETSHORT(qclass, p);
1034
Simon Kelley0a852542005-03-23 20:28:59 +00001035 if (typep)
1036 *typep = qtype;
1037
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001038 if (qclass == C_IN)
1039 {
1040 if (qtype == T_A)
1041 return F_IPV4;
1042 if (qtype == T_AAAA)
1043 return F_IPV6;
1044 if (qtype == T_ANY)
1045 return F_IPV4 | F_IPV6;
1046 }
1047
1048 return F_QUERY;
1049}
1050
1051
Simon Kelley572b41e2011-02-18 18:11:18 +00001052size_t setup_reply(struct dns_header *header, size_t qlen,
Simon Kelley28866e92011-02-14 20:19:14 +00001053 struct all_addr *addrp, unsigned int flags, unsigned long ttl)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001054{
1055 unsigned char *p = skip_questions(header, qlen);
1056
Simon Kelley572b41e2011-02-18 18:11:18 +00001057 /* clear authoritative and truncated flags, set QR flag */
1058 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
1059 /* set RA flag */
1060 header->hb4 |= HB4_RA;
1061
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001062 header->nscount = htons(0);
1063 header->arcount = htons(0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001064 header->ancount = htons(0); /* no answers unless changed below */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001065 if (flags == F_NEG)
Simon Kelley572b41e2011-02-18 18:11:18 +00001066 SET_RCODE(header, SERVFAIL); /* couldn't get memory */
Simon Kelley824af852008-02-12 20:43:05 +00001067 else if (flags == F_NOERR)
Simon Kelley572b41e2011-02-18 18:11:18 +00001068 SET_RCODE(header, NOERROR); /* empty domain */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001069 else if (flags == F_NXDOMAIN)
Simon Kelley572b41e2011-02-18 18:11:18 +00001070 SET_RCODE(header, NXDOMAIN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001071 else if (p && flags == F_IPV4)
1072 { /* we know the address */
Simon Kelley572b41e2011-02-18 18:11:18 +00001073 SET_RCODE(header, NOERROR);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001074 header->ancount = htons(1);
Simon Kelley572b41e2011-02-18 18:11:18 +00001075 header->hb3 |= HB3_AA;
1076 add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001077 }
1078#ifdef HAVE_IPV6
1079 else if (p && flags == F_IPV6)
1080 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001081 SET_RCODE(header, NOERROR);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001082 header->ancount = htons(1);
Simon Kelley572b41e2011-02-18 18:11:18 +00001083 header->hb3 |= HB3_AA;
1084 add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001085 }
1086#endif
1087 else /* nowhere to forward to */
Simon Kelley572b41e2011-02-18 18:11:18 +00001088 SET_RCODE(header, REFUSED);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001089
1090 return p - (unsigned char *)header;
1091}
Simon Kelley36717ee2004-09-20 19:20:58 +01001092
1093/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001094int check_for_local_domain(char *name, time_t now)
Simon Kelley36717ee2004-09-20 19:20:58 +01001095{
1096 struct crec *crecp;
Simon Kelley0a852542005-03-23 20:28:59 +00001097 struct mx_srv_record *mx;
1098 struct txt_record *txt;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001099 struct interface_name *intr;
1100 struct ptr_record *ptr;
Simon Kelley7de060b2011-08-26 17:24:52 +01001101 struct naptr *naptr;
1102
1103 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +01001104 (crecp->flags & (F_HOSTS | F_DHCP)))
1105 return 1;
1106
Simon Kelley7de060b2011-08-26 17:24:52 +01001107 for (naptr = daemon->naptr; naptr; naptr = naptr->next)
1108 if (hostname_isequal(name, naptr->name))
1109 return 1;
1110
1111 for (mx = daemon->mxnames; mx; mx = mx->next)
Simon Kelley0a852542005-03-23 20:28:59 +00001112 if (hostname_isequal(name, mx->name))
Simon Kelley36717ee2004-09-20 19:20:58 +01001113 return 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001114
Simon Kelley0a852542005-03-23 20:28:59 +00001115 for (txt = daemon->txt; txt; txt = txt->next)
1116 if (hostname_isequal(name, txt->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001117 return 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001118
1119 for (intr = daemon->int_names; intr; intr = intr->next)
1120 if (hostname_isequal(name, intr->name))
1121 return 1;
1122
1123 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1124 if (hostname_isequal(name, ptr->name))
1125 return 1;
1126
Simon Kelley36717ee2004-09-20 19:20:58 +01001127 return 0;
1128}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001129
1130/* Is the packet a reply with the answer address equal to addr?
1131 If so mung is into an NXDOMAIN reply and also put that information
1132 in the cache. */
Simon Kelley572b41e2011-02-18 18:11:18 +00001133int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001134 struct bogus_addr *baddr, time_t now)
1135{
1136 unsigned char *p;
1137 int i, qtype, qclass, rdlen;
1138 unsigned long ttl;
1139 struct bogus_addr *baddrp;
1140
1141 /* skip over questions */
1142 if (!(p = skip_questions(header, qlen)))
1143 return 0; /* bad packet */
1144
Simon Kelley5aabfc72007-08-29 11:24:47 +01001145 for (i = ntohs(header->ancount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001146 {
Simon Kelley9009d742008-11-14 20:04:27 +00001147 if (!extract_name(header, qlen, &p, name, 1, 10))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001148 return 0; /* bad packet */
1149
1150 GETSHORT(qtype, p);
1151 GETSHORT(qclass, p);
1152 GETLONG(ttl, p);
1153 GETSHORT(rdlen, p);
1154
1155 if (qclass == C_IN && qtype == T_A)
Simon Kelley9009d742008-11-14 20:04:27 +00001156 {
1157 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
1158 return 0;
1159
1160 for (baddrp = baddr; baddrp; baddrp = baddrp->next)
1161 if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
1162 {
1163 /* Found a bogus address. Insert that info here, since there no SOA record
1164 to get the ttl from in the normal processing */
1165 cache_start_insert();
1166 cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
1167 cache_end_insert();
1168
1169 return 1;
1170 }
1171 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001172
Simon Kelley9009d742008-11-14 20:04:27 +00001173 if (!ADD_RDLEN(header, p, qlen, rdlen))
1174 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001175 }
1176
1177 return 0;
1178}
1179
Simon Kelley572b41e2011-02-18 18:11:18 +00001180static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
Simon Kelley3d8df262005-08-29 12:19:27 +01001181 unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001182{
1183 va_list ap;
1184 unsigned char *sav, *p = *pp;
1185 int j;
1186 unsigned short usval;
1187 long lval;
1188 char *sval;
1189
1190 if (truncp && *truncp)
1191 return 0;
1192
1193 PUTSHORT(nameoffset | 0xc000, p);
1194 PUTSHORT(type, p);
1195 PUTSHORT(class, p);
1196 PUTLONG(ttl, p); /* TTL */
1197
1198 sav = p; /* Save pointer to RDLength field */
1199 PUTSHORT(0, p); /* Placeholder RDLength */
1200
1201 va_start(ap, format); /* make ap point to 1st unamed argument */
1202
1203 for (; *format; format++)
1204 switch (*format)
1205 {
1206#ifdef HAVE_IPV6
1207 case '6':
1208 sval = va_arg(ap, char *);
1209 memcpy(p, sval, IN6ADDRSZ);
1210 p += IN6ADDRSZ;
1211 break;
1212#endif
1213
1214 case '4':
1215 sval = va_arg(ap, char *);
1216 memcpy(p, sval, INADDRSZ);
1217 p += INADDRSZ;
1218 break;
1219
1220 case 's':
1221 usval = va_arg(ap, int);
1222 PUTSHORT(usval, p);
1223 break;
1224
1225 case 'l':
1226 lval = va_arg(ap, long);
1227 PUTLONG(lval, p);
1228 break;
1229
1230 case 'd':
1231 /* get domain-name answer arg and store it in RDATA field */
Simon Kelley0a852542005-03-23 20:28:59 +00001232 if (offset)
1233 *offset = p - (unsigned char *)header;
Simon Kelley3d8df262005-08-29 12:19:27 +01001234 p = do_rfc1035_name(p, va_arg(ap, char *));
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001235 *p++ = 0;
1236 break;
Simon Kelley3d8df262005-08-29 12:19:27 +01001237
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001238 case 't':
Simon Kelley0a852542005-03-23 20:28:59 +00001239 usval = va_arg(ap, int);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001240 sval = va_arg(ap, char *);
Simon Kelley0a852542005-03-23 20:28:59 +00001241 memcpy(p, sval, usval);
1242 p += usval;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001243 break;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001244
1245 case 'z':
1246 sval = va_arg(ap, char *);
1247 usval = sval ? strlen(sval) : 0;
1248 if (usval > 255)
1249 usval = 255;
1250 *p++ = (unsigned char)usval;
1251 memcpy(p, sval, usval);
1252 p += usval;
1253 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001254 }
1255
1256 va_end(ap); /* clean up variable argument pointer */
1257
1258 j = p - sav - 2;
1259 PUTSHORT(j, sav); /* Now, store real RDLength */
1260
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001261 /* check for overflow of buffer */
1262 if (limit && ((unsigned char *)limit - p) < 0)
1263 {
1264 if (truncp)
1265 *truncp = 1;
1266 return 0;
1267 }
1268
1269 *pp = p;
1270 return 1;
1271}
1272
Simon Kelley9009d742008-11-14 20:04:27 +00001273static unsigned long crec_ttl(struct crec *crecp, time_t now)
1274{
1275 /* Return 0 ttl for DHCP entries, which might change
1276 before the lease expires. */
1277
1278 if (crecp->flags & (F_IMMORTAL | F_DHCP))
1279 return daemon->local_ttl;
1280
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001281 /* Return the Max TTL value if it is lower then the actual TTL */
1282 if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
1283 return crecp->ttd - now;
1284 else
1285 return daemon->max_ttl;
Simon Kelley9009d742008-11-14 20:04:27 +00001286}
1287
1288
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001289/* return zero if we can't answer from cache, or packet size if we can */
Simon Kelley572b41e2011-02-18 18:11:18 +00001290size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
Simon Kelleycdeda282006-03-16 20:16:06 +00001291 struct in_addr local_addr, struct in_addr local_netmask, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001292{
Simon Kelley3be34542004-09-11 19:12:13 +01001293 char *name = daemon->namebuff;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001294 unsigned char *p, *ansp, *pheader;
Simon Kelley832af0b2007-01-21 20:01:28 +00001295 int qtype, qclass;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001296 struct all_addr addr;
1297 unsigned int nameoffset;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001298 unsigned short flag;
Simon Kelley0a852542005-03-23 20:28:59 +00001299 int q, ans, anscount = 0, addncount = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001300 int dryrun = 0, sec_reqd = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001301 int is_sign;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001302 struct crec *crecp;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001303 int nxdomain = 0, auth = 1, trunc = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001304 struct mx_srv_record *rec;
1305
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001306 /* If there is an RFC2671 pseudoheader then it will be overwritten by
1307 partial replies, so we have to do a dry run to see if we can answer
1308 the query. We check to see if the do bit is set, if so we always
1309 forward rather than answering from the cache, which doesn't include
1310 security information. */
1311
Simon Kelley832af0b2007-01-21 20:01:28 +00001312 if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001313 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001314 unsigned short udpsz, flags;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001315 unsigned char *psave = pheader;
1316
1317 GETSHORT(udpsz, pheader);
Simon Kelley7de060b2011-08-26 17:24:52 +01001318 pheader += 2; /* ext_rcode */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001319 GETSHORT(flags, pheader);
1320
1321 sec_reqd = flags & 0x8000; /* do bit */
1322
1323 /* If our client is advertising a larger UDP packet size
1324 than we allow, trim it so that we don't get an overlarge
1325 response from upstream */
1326
Simon Kelley832af0b2007-01-21 20:01:28 +00001327 if (!is_sign && (udpsz > daemon->edns_pktsz))
Simon Kelley3be34542004-09-11 19:12:13 +01001328 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001329
1330 dryrun = 1;
1331 }
1332
Simon Kelley572b41e2011-02-18 18:11:18 +00001333 if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
Simon Kelley832af0b2007-01-21 20:01:28 +00001334 return 0;
1335
Simon Kelley0a852542005-03-23 20:28:59 +00001336 for (rec = daemon->mxnames; rec; rec = rec->next)
1337 rec->offset = 0;
1338
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001339 rerun:
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001340 /* determine end of question section (we put answers there) */
1341 if (!(ansp = skip_questions(header, qlen)))
1342 return 0; /* bad packet */
1343
1344 /* now process each question, answers go in RRs after the question */
1345 p = (unsigned char *)(header+1);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001346
Simon Kelley5aabfc72007-08-29 11:24:47 +01001347 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001348 {
1349 /* save pointer to name for copying into answers */
1350 nameoffset = p - (unsigned char *)header;
1351
1352 /* now extract name as .-concatenated string into name */
Simon Kelley9009d742008-11-14 20:04:27 +00001353 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001354 return 0; /* bad packet */
Simon Kelley832af0b2007-01-21 20:01:28 +00001355
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001356 GETSHORT(qtype, p);
1357 GETSHORT(qclass, p);
1358
1359 ans = 0; /* have we answered this question */
1360
Simon Kelley0a852542005-03-23 20:28:59 +00001361 if (qtype == T_TXT || qtype == T_ANY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001362 {
Simon Kelley0a852542005-03-23 20:28:59 +00001363 struct txt_record *t;
1364 for(t = daemon->txt; t ; t = t->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001365 {
Simon Kelley0a852542005-03-23 20:28:59 +00001366 if (t->class == qclass && hostname_isequal(name, t->name))
1367 {
1368 ans = 1;
Simon Kelleye17fb622006-01-14 20:33:46 +00001369 if (!dryrun)
1370 {
Simon Kelley28866e92011-02-14 20:19:14 +00001371 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
Simon Kelleye17fb622006-01-14 20:33:46 +00001372 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1373 daemon->local_ttl, NULL,
1374 T_TXT, t->class, "t", t->len, t->txt))
1375 anscount++;
1376
1377 }
Simon Kelley0a852542005-03-23 20:28:59 +00001378 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001379 }
Simon Kelley0a852542005-03-23 20:28:59 +00001380 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001381
Simon Kelley0a852542005-03-23 20:28:59 +00001382 if (qclass == C_IN)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001383 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001384 if (qtype == T_PTR || qtype == T_ANY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001385 {
Simon Kelley832af0b2007-01-21 20:01:28 +00001386 /* see if it's w.z.y.z.in-addr.arpa format */
1387 int is_arpa = in_arpa_name_2_addr(name, &addr);
1388 struct ptr_record *ptr;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001389 struct interface_name* intr = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00001390
1391 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1392 if (hostname_isequal(name, ptr->name))
1393 break;
1394
Simon Kelleyf2621c72007-04-29 19:47:21 +01001395 if (is_arpa == F_IPV4)
1396 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001397 {
1398 if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
1399 break;
1400 else
1401 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
1402 intr = intr->next;
1403 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001404
1405 if (intr)
1406 {
1407 ans = 1;
1408 if (!dryrun)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001409 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001410 log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001411 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1412 daemon->local_ttl, NULL,
1413 T_PTR, C_IN, "d", intr->name))
1414 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001415 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001416 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001417 else if (ptr)
1418 {
1419 ans = 1;
1420 if (!dryrun)
1421 {
Simon Kelley28866e92011-02-14 20:19:14 +00001422 log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
Simon Kelley832af0b2007-01-21 20:01:28 +00001423 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001424 if (hostname_isequal(name, ptr->name) &&
1425 add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1426 daemon->local_ttl, NULL,
1427 T_PTR, C_IN, "d", ptr->ptr))
1428 anscount++;
1429
Simon Kelley832af0b2007-01-21 20:01:28 +00001430 }
1431 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001432 else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
1433 do
1434 {
1435 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
1436 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1437 continue;
1438
1439 if (crecp->flags & F_NEG)
1440 {
1441 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001442 auth = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001443 if (crecp->flags & F_NXDOMAIN)
1444 nxdomain = 1;
1445 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001446 log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001447 }
1448 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
1449 {
1450 ans = 1;
1451 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1452 auth = 0;
1453 if (!dryrun)
1454 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001455 log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001456 record_source(crecp->uid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001457
Simon Kelley9009d742008-11-14 20:04:27 +00001458 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1459 crec_ttl(crecp, now), NULL,
Simon Kelleyf2621c72007-04-29 19:47:21 +01001460 T_PTR, C_IN, "d", cache_get_name(crecp)))
1461 anscount++;
1462 }
1463 }
1464 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
1465 else if (is_arpa == F_IPV4 &&
Simon Kelley28866e92011-02-14 20:19:14 +00001466 option_bool(OPT_BOGUSPRIV) &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001467 private_net(addr.addr.addr4, 1))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001468 {
1469 /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
1470 ans = 1;
1471 nxdomain = 1;
1472 if (!dryrun)
1473 log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
Simon Kelley1a6bca82008-07-11 11:11:42 +01001474 name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001475 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001476 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001477
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001478 for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
1479 {
1480 unsigned short type = T_A;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001481
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001482 if (flag == F_IPV6)
1483#ifdef HAVE_IPV6
Simon Kelley3d8df262005-08-29 12:19:27 +01001484 type = T_AAAA;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001485#else
Simon Kelley3d8df262005-08-29 12:19:27 +01001486 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001487#endif
1488
1489 if (qtype != type && qtype != T_ANY)
1490 continue;
1491
Simon Kelley316e2732010-01-22 20:16:09 +00001492 /* Check for "A for A" queries; be rather conservative
1493 about what looks like dotted-quad. */
1494 if (qtype == T_A)
Simon Kelley3d8df262005-08-29 12:19:27 +01001495 {
Simon Kelley316e2732010-01-22 20:16:09 +00001496 char *cp;
1497 unsigned int i, a;
1498 int x;
1499
1500 for (cp = name, i = 0, a = 0; *cp; i++)
Simon Kelley3d8df262005-08-29 12:19:27 +01001501 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001502 if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)
Simon Kelley316e2732010-01-22 20:16:09 +00001503 {
1504 i = 5;
1505 break;
1506 }
1507
1508 a = (a << 8) + x;
1509
1510 if (*cp == '.')
1511 cp++;
Simon Kelley3d8df262005-08-29 12:19:27 +01001512 }
Simon Kelley316e2732010-01-22 20:16:09 +00001513
1514 if (i == 4)
1515 {
1516 ans = 1;
1517 if (!dryrun)
1518 {
1519 addr.addr.addr4.s_addr = htonl(a);
1520 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
1521 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1522 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1523 anscount++;
1524 }
1525 continue;
1526 }
Simon Kelley3d8df262005-08-29 12:19:27 +01001527 }
1528
Simon Kelleyf2621c72007-04-29 19:47:21 +01001529 /* interface name stuff */
1530 if (qtype == T_A)
1531 {
1532 struct interface_name *intr;
1533
1534 for (intr = daemon->int_names; intr; intr = intr->next)
1535 if (hostname_isequal(name, intr->name))
1536 break;
1537
1538 if (intr)
1539 {
1540 ans = 1;
1541 if (!dryrun)
1542 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001543 if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001544 log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001545 else
1546 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001547 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001548 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1549 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1550 anscount++;
1551 }
1552 }
1553 continue;
1554 }
1555 }
1556
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001557 cname_restart:
1558 if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
1559 {
1560 int localise = 0;
1561
1562 /* See if a putative address is on the network from which we recieved
1563 the query, is so we'll filter other answers. */
Simon Kelley28866e92011-02-14 20:19:14 +00001564 if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001565 {
1566 struct crec *save = crecp;
1567 do {
1568 if ((crecp->flags & F_HOSTS) &&
1569 is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1570 {
1571 localise = 1;
1572 break;
1573 }
1574 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
1575 crecp = save;
1576 }
1577
1578 do
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001579 {
Simon Kelley26128d22004-11-14 16:43:54 +00001580 /* don't answer wildcard queries with data not from /etc/hosts
1581 or DHCP leases */
1582 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1583 break;
1584
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001585 if (crecp->flags & F_CNAME)
1586 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001587 if (!dryrun)
1588 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001589 log_query(crecp->flags, name, NULL, record_source(crecp->uid));
Simon Kelley9009d742008-11-14 20:04:27 +00001590 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1591 crec_ttl(crecp, now), &nameoffset,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001592 T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
1593 anscount++;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001594 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001595
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001596 strcpy(name, cache_get_name(crecp->addr.cname.cache));
1597 goto cname_restart;
1598 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001599
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001600 if (crecp->flags & F_NEG)
1601 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001602 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001603 auth = 0;
1604 if (crecp->flags & F_NXDOMAIN)
1605 nxdomain = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001606 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001607 log_query(crecp->flags, name, NULL, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001608 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001609 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001610 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001611 /* If we are returning local answers depending on network,
1612 filter here. */
1613 if (localise &&
1614 (crecp->flags & F_HOSTS) &&
1615 !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1616 continue;
1617
1618 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1619 auth = 0;
1620
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001621 ans = 1;
1622 if (!dryrun)
1623 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001624 log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001625 record_source(crecp->uid));
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001626
Simon Kelley9009d742008-11-14 20:04:27 +00001627 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1628 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001629 type == T_A ? "4" : "6", &crecp->addr))
1630 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001631 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001632 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001633 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001634 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001635 }
1636
1637 if (qtype == T_MX || qtype == T_ANY)
1638 {
1639 int found = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001640 for (rec = daemon->mxnames; rec; rec = rec->next)
1641 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001642 {
1643 ans = found = 1;
1644 if (!dryrun)
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001645 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001646 unsigned int offset;
Simon Kelley28866e92011-02-14 20:19:14 +00001647 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
Simon Kelley0a852542005-03-23 20:28:59 +00001648 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1649 &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
1650 {
1651 anscount++;
1652 if (rec->target)
1653 rec->offset = offset;
1654 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001655 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001656 }
1657
Simon Kelley28866e92011-02-14 20:19:14 +00001658 if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001659 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
1660 {
1661 ans = 1;
1662 if (!dryrun)
1663 {
Simon Kelley28866e92011-02-14 20:19:14 +00001664 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001665 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
1666 T_MX, C_IN, "sd", 1,
Simon Kelley28866e92011-02-14 20:19:14 +00001667 option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001668 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001669 }
1670 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001671 }
1672
1673 if (qtype == T_SRV || qtype == T_ANY)
1674 {
1675 int found = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001676 struct mx_srv_record *move = NULL, **up = &daemon->mxnames;
1677
Simon Kelley0a852542005-03-23 20:28:59 +00001678 for (rec = daemon->mxnames; rec; rec = rec->next)
1679 if (rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001680 {
1681 found = ans = 1;
1682 if (!dryrun)
1683 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001684 unsigned int offset;
Simon Kelley28866e92011-02-14 20:19:14 +00001685 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001686 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
Simon Kelley0a852542005-03-23 20:28:59 +00001687 &offset, T_SRV, C_IN, "sssd",
1688 rec->priority, rec->weight, rec->srvport, rec->target))
1689 {
1690 anscount++;
1691 if (rec->target)
1692 rec->offset = offset;
1693 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001694 }
Simon Kelley28866e92011-02-14 20:19:14 +00001695
1696 /* unlink first SRV record found */
1697 if (!move)
1698 {
1699 move = rec;
1700 *up = rec->next;
1701 }
1702 else
1703 up = &rec->next;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001704 }
Simon Kelley28866e92011-02-14 20:19:14 +00001705 else
1706 up = &rec->next;
1707
1708 /* put first SRV record back at the end. */
1709 if (move)
1710 {
1711 *up = move;
1712 move->next = NULL;
1713 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001714
Simon Kelley28866e92011-02-14 20:19:14 +00001715 if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001716 {
1717 ans = 1;
1718 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001719 log_query(F_CONFIG | F_NEG, name, NULL, NULL);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001720 }
1721 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001722
1723 if (qtype == T_NAPTR || qtype == T_ANY)
1724 {
1725 struct naptr *na;
1726 for (na = daemon->naptr; na; na = na->next)
1727 if (hostname_isequal(name, na->name))
1728 {
1729 ans = 1;
1730 if (!dryrun)
1731 {
Simon Kelley28866e92011-02-14 20:19:14 +00001732 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
Simon Kelley1a6bca82008-07-11 11:11:42 +01001733 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1734 NULL, T_NAPTR, C_IN, "sszzzd",
1735 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
1736 anscount++;
1737 }
1738 }
1739 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001740
1741 if (qtype == T_MAILB)
1742 ans = 1, nxdomain = 1;
1743
Simon Kelley28866e92011-02-14 20:19:14 +00001744 if (qtype == T_SOA && option_bool(OPT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001745 {
1746 ans = 1;
1747 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001748 log_query(F_CONFIG | F_NEG, name, &addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001749 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001750 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001751
1752 if (!ans)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001753 return 0; /* failed to answer a question */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001754 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001755
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001756 if (dryrun)
1757 {
1758 dryrun = 0;
1759 goto rerun;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001760 }
1761
Simon Kelley0a852542005-03-23 20:28:59 +00001762 /* create an additional data section, for stuff in SRV and MX record replies. */
1763 for (rec = daemon->mxnames; rec; rec = rec->next)
1764 if (rec->offset != 0)
1765 {
1766 /* squash dupes */
1767 struct mx_srv_record *tmp;
1768 for (tmp = rec->next; tmp; tmp = tmp->next)
1769 if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
1770 tmp->offset = 0;
1771
1772 crecp = NULL;
1773 while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
1774 {
Simon Kelley0a852542005-03-23 20:28:59 +00001775#ifdef HAVE_IPV6
1776 int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
1777#else
1778 int type = T_A;
1779#endif
1780 if (crecp->flags & F_NEG)
1781 continue;
1782
Simon Kelley9009d742008-11-14 20:04:27 +00001783 if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
1784 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelley0a852542005-03-23 20:28:59 +00001785 crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
1786 addncount++;
1787 }
1788 }
1789
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001790 /* done all questions, set up header and return length of result */
Simon Kelley572b41e2011-02-18 18:11:18 +00001791 /* clear authoritative and truncated flags, set QR flag */
1792 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
1793 /* set RA flag */
1794 header->hb4 |= HB4_RA;
1795
1796 /* authoritive - only hosts and DHCP derived names. */
1797 if (auth)
1798 header->hb3 |= HB3_AA;
1799
1800 /* truncation */
1801 if (trunc)
1802 header->hb3 |= HB3_TC;
1803
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001804 if (anscount == 0 && nxdomain)
Simon Kelley572b41e2011-02-18 18:11:18 +00001805 SET_RCODE(header, NXDOMAIN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001806 else
Simon Kelley572b41e2011-02-18 18:11:18 +00001807 SET_RCODE(header, NOERROR); /* no error */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001808 header->ancount = htons(anscount);
1809 header->nscount = htons(0);
Simon Kelley0a852542005-03-23 20:28:59 +00001810 header->arcount = htons(addncount);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001811 return ansp - (unsigned char *)header;
1812}
1813
1814
1815
1816
1817