blob: 932199b52c1d74705dc9f6d5164984f381958800 [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 Kelleyf6b7dc42005-01-23 12:06:08 +000019
Simon Kelley9009d742008-11-14 20:04:27 +000020#define CHECK_LEN(header, pp, plen, len) \
21 ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
22
23#define ADD_RDLEN(header, pp, plen, len) \
Simon Kelley22b135a2012-03-01 19:54:50 +000024 (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
Simon Kelley9009d742008-11-14 20:04:27 +000025
Simon Kelley4f7b3042012-11-28 21:27:02 +000026int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
27 char *name, int isExtract, int extrabytes)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000028{
Simon Kelley3d8df262005-08-29 12:19:27 +010029 unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000030 unsigned int j, l, hops = 0;
31 int retvalue = 1;
32
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000033 if (isExtract)
34 *cp = 0;
35
Simon Kelley9009d742008-11-14 20:04:27 +000036 while (1)
37 {
38 unsigned int label_type;
39
40 if (!CHECK_LEN(header, p, plen, 1))
41 return 0;
42
43 if ((l = *p++) == 0)
44 /* end marker */
45 {
46 /* check that there are the correct no of bytes after the name */
47 if (!CHECK_LEN(header, p, plen, extrabytes))
48 return 0;
49
50 if (isExtract)
51 {
52 if (cp != (unsigned char *)name)
53 cp--;
54 *cp = 0; /* terminate: lose final period */
55 }
56 else if (*cp != 0)
57 retvalue = 2;
58
59 if (p1) /* we jumped via compression */
60 *pp = p1;
61 else
62 *pp = p;
63
64 return retvalue;
65 }
66
67 label_type = l & 0xc0;
68
Simon Kelley9e4abcb2004-01-22 19:47:41 +000069 if (label_type == 0xc0) /* pointer */
70 {
Simon Kelley9009d742008-11-14 20:04:27 +000071 if (!CHECK_LEN(header, p, plen, 1))
Simon Kelley9e4abcb2004-01-22 19:47:41 +000072 return 0;
73
74 /* get offset */
75 l = (l&0x3f) << 8;
76 l |= *p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000077
78 if (!p1) /* first jump, save location to go back to */
79 p1 = p;
80
81 hops++; /* break malicious infinite loops */
82 if (hops > 255)
83 return 0;
84
85 p = l + (unsigned char *)header;
86 }
87 else if (label_type == 0x80)
88 return 0; /* reserved */
89 else if (label_type == 0x40)
90 { /* ELT */
91 unsigned int count, digs;
92
93 if ((l & 0x3f) != 1)
94 return 0; /* we only understand bitstrings */
95
96 if (!isExtract)
97 return 0; /* Cannot compare bitsrings */
98
99 count = *p++;
100 if (count == 0)
101 count = 256;
102 digs = ((count-1)>>2)+1;
103
104 /* output is \[x<hex>/siz]. which is digs+9 chars */
Simon Kelley3d8df262005-08-29 12:19:27 +0100105 if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000106 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000107 if (!CHECK_LEN(header, p, plen, (count-1)>>3))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000108 return 0;
109
110 *cp++ = '\\';
111 *cp++ = '[';
112 *cp++ = 'x';
113 for (j=0; j<digs; j++)
114 {
115 unsigned int dig;
116 if (j%2 == 0)
117 dig = *p >> 4;
118 else
119 dig = *p++ & 0x0f;
120
121 *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
122 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100123 cp += sprintf((char *)cp, "/%d]", count);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000124 /* do this here to overwrite the zero char from sprintf */
125 *cp++ = '.';
126 }
127 else
128 { /* label_type = 0 -> label. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100129 if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000130 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000131 if (!CHECK_LEN(header, p, plen, l))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000132 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000133
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000134 for(j=0; j<l; j++, p++)
135 if (isExtract)
136 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100137 unsigned char c = *p;
138 if (isascii(c) && !iscntrl(c) && c != '.')
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000139 *cp++ = *p;
140 else
141 return 0;
142 }
143 else
144 {
145 unsigned char c1 = *cp, c2 = *p;
146
147 if (c1 == 0)
148 retvalue = 2;
149 else
150 {
151 cp++;
152 if (c1 >= 'A' && c1 <= 'Z')
153 c1 += 'a' - 'A';
154 if (c2 >= 'A' && c2 <= 'Z')
155 c2 += 'a' - 'A';
156
157 if (c1 != c2)
158 retvalue = 2;
159 }
160 }
161
162 if (isExtract)
163 *cp++ = '.';
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000164 else if (*cp != 0 && *cp++ != '.')
165 retvalue = 2;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000166 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000167 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000168}
169
170/* Max size of input string (for IPv6) is 75 chars.) */
171#define MAXARPANAME 75
Simon Kelley4f7b3042012-11-28 21:27:02 +0000172int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000173{
174 int j;
175 char name[MAXARPANAME+1], *cp1;
176 unsigned char *addr = (unsigned char *)addrp;
177 char *lastchunk = NULL, *penchunk = NULL;
178
179 if (strlen(namein) > MAXARPANAME)
180 return 0;
181
182 memset(addrp, 0, sizeof(struct all_addr));
183
184 /* turn name into a series of asciiz strings */
185 /* j counts no of labels */
186 for(j = 1,cp1 = name; *namein; cp1++, namein++)
187 if (*namein == '.')
188 {
189 penchunk = lastchunk;
190 lastchunk = cp1 + 1;
191 *cp1 = 0;
192 j++;
193 }
194 else
195 *cp1 = *namein;
196
197 *cp1 = 0;
198
199 if (j<3)
200 return 0;
201
202 if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))
203 {
204 /* IP v4 */
205 /* address arives as a name of the form
206 www.xxx.yyy.zzz.in-addr.arpa
207 some of the low order address octets might be missing
208 and should be set to zero. */
209 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
210 {
211 /* check for digits only (weeds out things like
212 50.0/24.67.28.64.in-addr.arpa which are used
213 as CNAME targets according to RFC 2317 */
214 char *cp;
215 for (cp = cp1; *cp; cp++)
Simon Kelley572b41e2011-02-18 18:11:18 +0000216 if (!isdigit((unsigned char)*cp))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000217 return 0;
218
219 addr[3] = addr[2];
220 addr[2] = addr[1];
221 addr[1] = addr[0];
222 addr[0] = atoi(cp1);
223 }
224
225 return F_IPV4;
226 }
227#ifdef HAVE_IPV6
228 else if (hostname_isequal(penchunk, "ip6") &&
229 (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
230 {
231 /* IP v6:
232 Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
233 or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
234
235 Note that most of these the various reprentations are obsolete and
236 left-over from the many DNS-for-IPv6 wars. We support all the formats
237 that we can since there is no reason not to.
238 */
239
240 if (*name == '\\' && *(name+1) == '[' &&
241 (*(name+2) == 'x' || *(name+2) == 'X'))
242 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000243 for (j = 0, cp1 = name+3; *cp1 && isxdigit((unsigned char) *cp1) && j < 32; cp1++, j++)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000244 {
245 char xdig[2];
246 xdig[0] = *cp1;
247 xdig[1] = 0;
248 if (j%2)
249 addr[j/2] |= strtol(xdig, NULL, 16);
250 else
251 addr[j/2] = strtol(xdig, NULL, 16) << 4;
252 }
253
254 if (*cp1 == '/' && j == 32)
255 return F_IPV6;
256 }
257 else
258 {
259 for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
260 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000261 if (*(cp1+1) || !isxdigit((unsigned char)*cp1))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000262 return 0;
263
264 for (j = sizeof(struct all_addr)-1; j>0; j--)
265 addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);
266 addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
267 }
268
269 return F_IPV6;
270 }
271 }
272#endif
273
274 return 0;
275}
276
Simon Kelley572b41e2011-02-18 18:11:18 +0000277static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100278{
279 while(1)
280 {
Simon Kelley9009d742008-11-14 20:04:27 +0000281 unsigned int label_type;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100282
Simon Kelley9009d742008-11-14 20:04:27 +0000283 if (!CHECK_LEN(header, ansp, plen, 1))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100284 return NULL;
285
Simon Kelley9009d742008-11-14 20:04:27 +0000286 label_type = (*ansp) & 0xc0;
287
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100288 if (label_type == 0xc0)
289 {
290 /* pointer for compression. */
291 ansp += 2;
292 break;
293 }
294 else if (label_type == 0x80)
295 return NULL; /* reserved */
296 else if (label_type == 0x40)
297 {
298 /* Extended label type */
299 unsigned int count;
300
Simon Kelley9009d742008-11-14 20:04:27 +0000301 if (!CHECK_LEN(header, ansp, plen, 2))
302 return NULL;
303
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100304 if (((*ansp++) & 0x3f) != 1)
305 return NULL; /* we only understand bitstrings */
306
307 count = *(ansp++); /* Bits in bitstring */
308
309 if (count == 0) /* count == 0 means 256 bits */
310 ansp += 32;
311 else
312 ansp += ((count-1)>>3)+1;
313 }
314 else
315 { /* label type == 0 Bottom six bits is length */
316 unsigned int len = (*ansp++) & 0x3f;
Simon Kelley9009d742008-11-14 20:04:27 +0000317
318 if (!ADD_RDLEN(header, ansp, plen, len))
319 return NULL;
320
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100321 if (len == 0)
322 break; /* zero length label marks the end. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100323 }
324 }
Simon Kelley9009d742008-11-14 20:04:27 +0000325
326 if (!CHECK_LEN(header, ansp, plen, extrabytes))
327 return NULL;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100328
329 return ansp;
330}
331
Simon Kelley4f7b3042012-11-28 21:27:02 +0000332unsigned char *skip_questions(struct dns_header *header, size_t plen)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000333{
Simon Kelley5aabfc72007-08-29 11:24:47 +0100334 int q;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000335 unsigned char *ansp = (unsigned char *)(header+1);
336
Simon Kelley5aabfc72007-08-29 11:24:47 +0100337 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000338 {
Simon Kelley9009d742008-11-14 20:04:27 +0000339 if (!(ansp = skip_name(ansp, header, plen, 4)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100340 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000341 ansp += 4; /* class and type */
342 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000343
344 return ansp;
345}
346
Simon Kelley572b41e2011-02-18 18:11:18 +0000347static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100348{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100349 int i, rdlen;
Simon Kelley36717ee2004-09-20 19:20:58 +0100350
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100351 for (i = 0; i < count; i++)
Simon Kelley36717ee2004-09-20 19:20:58 +0100352 {
Simon Kelley9009d742008-11-14 20:04:27 +0000353 if (!(ansp = skip_name(ansp, header, plen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100354 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100355 ansp += 8; /* type, class, TTL */
356 GETSHORT(rdlen, ansp);
Simon Kelley9009d742008-11-14 20:04:27 +0000357 if (!ADD_RDLEN(header, ansp, plen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100358 return NULL;
Simon Kelley36717ee2004-09-20 19:20:58 +0100359 }
360
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100361 return ansp;
362}
363
Simon Kelley0a852542005-03-23 20:28:59 +0000364/* CRC the question section. This is used to safely detect query
365 retransmision and to detect answers to questions we didn't ask, which
366 might be poisoning attacks. Note that we decode the name rather
367 than CRC the raw bytes, since replies might be compressed differently.
Simon Kelley832af0b2007-01-21 20:01:28 +0000368 We ignore case in the names for the same reason. Return all-ones
369 if there is not question section. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000370unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100371{
Simon Kelley91dccd02005-03-31 17:48:32 +0100372 int q;
373 unsigned int crc = 0xffffffff;
Simon Kelley0a852542005-03-23 20:28:59 +0000374 unsigned char *p1, *p = (unsigned char *)(header+1);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100375
Simon Kelley5aabfc72007-08-29 11:24:47 +0100376 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley0a852542005-03-23 20:28:59 +0000377 {
Simon Kelley9009d742008-11-14 20:04:27 +0000378 if (!extract_name(header, plen, &p, name, 1, 4))
Simon Kelley0a852542005-03-23 20:28:59 +0000379 return crc; /* bad packet */
380
Simon Kelley3d8df262005-08-29 12:19:27 +0100381 for (p1 = (unsigned char *)name; *p1; p1++)
Simon Kelley0a852542005-03-23 20:28:59 +0000382 {
383 int i = 8;
384 char c = *p1;
385
386 if (c >= 'A' && c <= 'Z')
387 c += 'a' - 'A';
388
389 crc ^= c << 24;
390 while (i--)
391 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
392 }
393
394 /* CRC the class and type as well */
395 for (p1 = p; p1 < p+4; p1++)
396 {
397 int i = 8;
398 crc ^= *p1 << 24;
399 while (i--)
400 crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
401 }
402
403 p += 4;
Simon Kelley9009d742008-11-14 20:04:27 +0000404 if (!CHECK_LEN(header, p, plen, 0))
Simon Kelley0a852542005-03-23 20:28:59 +0000405 return crc; /* bad packet */
406 }
407
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100408 return crc;
409}
410
411
Simon Kelley572b41e2011-02-18 18:11:18 +0000412size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100413{
414 unsigned char *ansp = skip_questions(header, plen);
415
Simon Kelley9009d742008-11-14 20:04:27 +0000416 /* if packet is malformed, just return as-is. */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100417 if (!ansp)
Simon Kelley9009d742008-11-14 20:04:27 +0000418 return plen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100419
420 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
421 header, plen)))
Simon Kelley9009d742008-11-14 20:04:27 +0000422 return plen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100423
Simon Kelley36717ee2004-09-20 19:20:58 +0100424 /* restore pseudoheader */
425 if (pheader && ntohs(header->arcount) == 0)
426 {
427 /* must use memmove, may overlap */
428 memmove(ansp, pheader, hlen);
429 header->arcount = htons(1);
430 ansp += hlen;
431 }
432
433 return ansp - (unsigned char *)header;
434}
435
Simon Kelley572b41e2011-02-18 18:11:18 +0000436unsigned 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 +0100437{
438 /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
Simon Kelley832af0b2007-01-21 20:01:28 +0000439 also return length of pseudoheader in *len and pointer to the UDP size in *p
440 Finally, check to see if a packet is signed. If it is we cannot change a single bit before
441 forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100442
443 int i, arcount = ntohs(header->arcount);
Simon Kelley832af0b2007-01-21 20:01:28 +0000444 unsigned char *ansp = (unsigned char *)(header+1);
445 unsigned short rdlen, type, class;
446 unsigned char *ret = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000447
448 if (is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000449 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000450 *is_sign = 0;
451
Simon Kelley572b41e2011-02-18 18:11:18 +0000452 if (OPCODE(header) == QUERY)
Simon Kelley832af0b2007-01-21 20:01:28 +0000453 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100454 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000455 {
Simon Kelley9009d742008-11-14 20:04:27 +0000456 if (!(ansp = skip_name(ansp, header, plen, 4)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000457 return NULL;
458
459 GETSHORT(type, ansp);
460 GETSHORT(class, ansp);
461
462 if (class == C_IN && type == T_TKEY)
463 *is_sign = 1;
464 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000465 }
466 }
467 else
468 {
469 if (!(ansp = skip_questions(header, plen)))
470 return NULL;
471 }
472
473 if (arcount == 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100474 return NULL;
475
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100476 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
477 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000478
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100479 for (i = 0; i < arcount; i++)
480 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100481 unsigned char *save, *start = ansp;
Simon Kelley9009d742008-11-14 20:04:27 +0000482 if (!(ansp = skip_name(ansp, header, plen, 10)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100483 return NULL;
484
485 GETSHORT(type, ansp);
486 save = ansp;
Simon Kelley832af0b2007-01-21 20:01:28 +0000487 GETSHORT(class, ansp);
488 ansp += 4; /* TTL */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100489 GETSHORT(rdlen, ansp);
Simon Kelley9009d742008-11-14 20:04:27 +0000490 if (!ADD_RDLEN(header, ansp, plen, rdlen))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100491 return NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000492 if (type == T_OPT)
Simon Kelley36717ee2004-09-20 19:20:58 +0100493 {
494 if (len)
495 *len = ansp - start;
496 if (p)
497 *p = save;
Simon Kelley832af0b2007-01-21 20:01:28 +0000498 ret = start;
Simon Kelley36717ee2004-09-20 19:20:58 +0100499 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000500 else if (is_sign &&
501 i == arcount - 1 &&
502 class == C_ANY &&
503 (type == T_SIG || type == T_TSIG))
504 *is_sign = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100505 }
506
Simon Kelley832af0b2007-01-21 20:01:28 +0000507 return ret;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100508}
Simon Kelley28866e92011-02-14 20:19:14 +0000509
510struct macparm {
511 unsigned char *limit;
Simon Kelley572b41e2011-02-18 18:11:18 +0000512 struct dns_header *header;
Simon Kelley28866e92011-02-14 20:19:14 +0000513 size_t plen;
514 union mysockaddr *l3;
515};
516
517static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
518{
519 struct macparm *parm = parmv;
520 int match = 0;
521 unsigned short rdlen;
Simon Kelley572b41e2011-02-18 18:11:18 +0000522 struct dns_header *header = parm->header;
Simon Kelley28866e92011-02-14 20:19:14 +0000523 unsigned char *lenp, *datap, *p;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100524
Simon Kelley28866e92011-02-14 20:19:14 +0000525 if (family == parm->l3->sa.sa_family)
526 {
527 if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
528 match = 1;
529#ifdef HAVE_IPV6
530 else
531 if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
532 match = 1;
533#endif
534 }
535
536 if (!match)
537 return 1; /* continue */
538
539 if (ntohs(header->arcount) == 0)
540 {
541 /* We are adding the pseudoheader */
542 if (!(p = skip_questions(header, parm->plen)) ||
543 !(p = skip_section(p,
544 ntohs(header->ancount) + ntohs(header->nscount),
545 header, parm->plen)))
546 return 0;
547 *p++ = 0; /* empty name */
548 PUTSHORT(T_OPT, p);
549 PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */
550 PUTLONG(0, p); /* extended RCODE */
551 lenp = p;
552 PUTSHORT(0, p); /* RDLEN */
553 rdlen = 0;
554 if (((ssize_t)maclen) > (parm->limit - (p + 4)))
555 return 0; /* Too big */
556 header->arcount = htons(1);
557 datap = p;
558 }
559 else
560 {
561 int i, is_sign;
562 unsigned short code, len;
563
564 if (ntohs(header->arcount) != 1 ||
565 !(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) ||
566 is_sign ||
567 (!(p = skip_name(p, header, parm->plen, 10))))
568 return 0;
569
570 p += 8; /* skip UDP length and RCODE */
571
572 lenp = p;
573 GETSHORT(rdlen, p);
574 if (!CHECK_LEN(header, p, parm->plen, rdlen))
575 return 0; /* bad packet */
576 datap = p;
577
578 /* check if option already there */
579 for (i = 0; i + 4 < rdlen; i += len + 4)
580 {
581 GETSHORT(code, p);
582 GETSHORT(len, p);
583 if (code == EDNS0_OPTION_MAC)
584 return 0;
585 p += len;
586 }
587
588 if (((ssize_t)maclen) > (parm->limit - (p + 4)))
589 return 0; /* Too big */
590 }
591
592 PUTSHORT(EDNS0_OPTION_MAC, p);
593 PUTSHORT(maclen, p);
594 memcpy(p, mac, maclen);
595 p += maclen;
596
597 PUTSHORT(p - datap, lenp);
598 parm->plen = p - (unsigned char *)header;
599
600 return 0; /* done */
601}
602
603
Simon Kelley572b41e2011-02-18 18:11:18 +0000604size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
Simon Kelley28866e92011-02-14 20:19:14 +0000605{
606 struct macparm parm;
607
608/* Must have an existing pseudoheader as the only ar-record,
609 or have no ar-records. Must also not be signed */
610
611 if (ntohs(header->arcount) > 1)
612 return plen;
613
614 parm.header = header;
615 parm.limit = (unsigned char *)limit;
616 parm.plen = plen;
617 parm.l3 = l3;
618
619 iface_enumerate(AF_UNSPEC, &parm, filter_mac);
620
621 return parm.plen;
622}
623
624
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000625/* is addr in the non-globally-routed IP space? */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100626static int private_net(struct in_addr addr, int ban_localhost)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000627{
Simon Kelleyf2621c72007-04-29 19:47:21 +0100628 in_addr_t ip_addr = ntohl(addr.s_addr);
629
630 return
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100631 (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
Simon Kelleyf2621c72007-04-29 19:47:21 +0100632 ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
633 ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
634 ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
635 ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000636}
Simon Kelley1cff1662004-03-12 08:12:58 +0000637
Simon Kelley572b41e2011-02-18 18:11:18 +0000638static 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 +0000639{
640 int i, qtype, qclass, rdlen;
Simon Kelley824af852008-02-12 20:43:05 +0000641
642 for (i = count; i != 0; i--)
643 {
Simon Kelley28866e92011-02-14 20:19:14 +0000644 if (name && option_bool(OPT_LOG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100645 {
646 if (!extract_name(header, qlen, &p, name, 1, 10))
647 return 0;
648 }
649 else if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000650 return 0; /* bad packet */
651
652 GETSHORT(qtype, p);
653 GETSHORT(qclass, p);
Simon Kelley7de060b2011-08-26 17:24:52 +0100654 p += 4; /* ttl */
Simon Kelley824af852008-02-12 20:43:05 +0000655 GETSHORT(rdlen, p);
656
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100657 if (qclass == C_IN && qtype == T_A)
Simon Kelley824af852008-02-12 20:43:05 +0000658 {
659 struct doctor *doctor;
660 struct in_addr addr;
661
Simon Kelley9009d742008-11-14 20:04:27 +0000662 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
663 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100664
665 /* alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000666 memcpy(&addr, p, INADDRSZ);
667
668 for (doctor = daemon->doctors; doctor; doctor = doctor->next)
Simon Kelley73a08a22009-02-05 20:28:08 +0000669 {
670 if (doctor->end.s_addr == 0)
671 {
672 if (!is_same_net(doctor->in, addr, doctor->mask))
673 continue;
674 }
675 else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
676 ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
677 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100678
Simon Kelley73a08a22009-02-05 20:28:08 +0000679 addr.s_addr &= ~doctor->mask.s_addr;
680 addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
681 /* Since we munged the data, the server it came from is no longer authoritative */
Simon Kelley572b41e2011-02-18 18:11:18 +0000682 header->hb3 &= ~HB3_AA;
Simon Kelley73a08a22009-02-05 20:28:08 +0000683 memcpy(p, &addr, INADDRSZ);
684 break;
685 }
Simon Kelley824af852008-02-12 20:43:05 +0000686 }
Simon Kelley28866e92011-02-14 20:19:14 +0000687 else if (qtype == T_TXT && name && option_bool(OPT_LOG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100688 {
689 unsigned char *p1 = p;
690 if (!CHECK_LEN(header, p1, qlen, rdlen))
691 return 0;
692 while ((p1 - p) < rdlen)
693 {
694 unsigned int i, len = *p1;
695 unsigned char *p2 = p1;
696 /* make counted string zero-term and sanitise */
697 for (i = 0; i < len; i++)
Simon Kelley231d0612012-04-27 13:50:45 +0100698 {
699 if (!isprint((int)*(p2+1)))
700 break;
701
702 *p2 = *(p2+1);
703 p2++;
704 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100705 *p2 = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000706 my_syslog(LOG_INFO, "reply %s is %s", name, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100707 /* restore */
Simon Kelley231d0612012-04-27 13:50:45 +0100708 memmove(p1 + 1, p1, i);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100709 *p1 = len;
710 p1 += len+1;
711 }
712 }
Simon Kelley824af852008-02-12 20:43:05 +0000713
Simon Kelley9009d742008-11-14 20:04:27 +0000714 if (!ADD_RDLEN(header, p, qlen, rdlen))
715 return 0; /* bad packet */
Simon Kelley824af852008-02-12 20:43:05 +0000716 }
717
718 return p;
719}
720
Simon Kelley572b41e2011-02-18 18:11:18 +0000721static int find_soa(struct dns_header *header, size_t qlen, char *name)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000722{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100723 unsigned char *p;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000724 int qtype, qclass, rdlen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100725 unsigned long ttl, minttl = ULONG_MAX;
726 int i, found_soa = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000727
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100728 /* first move to NS section and find TTL from any SOA section */
729 if (!(p = skip_questions(header, qlen)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100730 !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
Simon Kelley824af852008-02-12 20:43:05 +0000731 return 0; /* bad packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000732
Simon Kelley5aabfc72007-08-29 11:24:47 +0100733 for (i = ntohs(header->nscount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000734 {
Simon Kelley9009d742008-11-14 20:04:27 +0000735 if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100736 return 0; /* bad packet */
737
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000738 GETSHORT(qtype, p);
739 GETSHORT(qclass, p);
740 GETLONG(ttl, p);
741 GETSHORT(rdlen, p);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100742
743 if ((qclass == C_IN) && (qtype == T_SOA))
744 {
745 found_soa = 1;
746 if (ttl < minttl)
747 minttl = ttl;
748
749 /* MNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000750 if (!(p = skip_name(p, header, qlen, 0)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100751 return 0;
752 /* RNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000753 if (!(p = skip_name(p, header, qlen, 20)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100754 return 0;
755 p += 16; /* SERIAL REFRESH RETRY EXPIRE */
756
757 GETLONG(ttl, p); /* minTTL */
758 if (ttl < minttl)
759 minttl = ttl;
760 }
Simon Kelley9009d742008-11-14 20:04:27 +0000761 else if (!ADD_RDLEN(header, p, qlen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100762 return 0; /* bad packet */
763 }
Simon Kelley9009d742008-11-14 20:04:27 +0000764
Simon Kelley824af852008-02-12 20:43:05 +0000765 /* rewrite addresses in additioal section too */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100766 if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
Simon Kelley824af852008-02-12 20:43:05 +0000767 return 0;
768
769 if (!found_soa)
770 minttl = daemon->neg_ttl;
771
772 return minttl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100773}
774
775/* Note that the following code can create CNAME chains that don't point to a real record,
776 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 +0000777 expired and cleaned out that way.
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100778 Return 1 if we reject an address because it look like part of dns-rebinding attack. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000779int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
Simon Kelley28866e92011-02-14 20:19:14 +0000780 int is_sign, int check_rebind, int checking_disabled)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100781{
Simon Kelley824af852008-02-12 20:43:05 +0000782 unsigned char *p, *p1, *endrr, *namep;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100783 int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000784 unsigned long ttl = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100785 struct all_addr addr;
Simon Kelley0a852542005-03-23 20:28:59 +0000786
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100787 cache_start_insert();
Simon Kelley0a852542005-03-23 20:28:59 +0000788
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100789 /* 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 +0000790 if (daemon->doctors || option_bool(OPT_LOG))
Simon Kelley0a852542005-03-23 20:28:59 +0000791 {
792 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100793 ttl = find_soa(header, qlen, name);
Simon Kelley0a852542005-03-23 20:28:59 +0000794 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100795
796 /* go through the questions. */
797 p = (unsigned char *)(header+1);
798
Simon Kelley5aabfc72007-08-29 11:24:47 +0100799 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100800 {
801 int found = 0, cname_count = 5;
802 struct crec *cpp = NULL;
Simon Kelley572b41e2011-02-18 18:11:18 +0000803 int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000804 unsigned long cttl = ULONG_MAX, attl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100805
Simon Kelley824af852008-02-12 20:43:05 +0000806 namep = p;
Simon Kelley9009d742008-11-14 20:04:27 +0000807 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley824af852008-02-12 20:43:05 +0000808 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100809
810 GETSHORT(qtype, p);
811 GETSHORT(qclass, p);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000812
813 if (qclass != C_IN)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100814 continue;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000815
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100816 /* PTRs: we chase CNAMEs here, since we have no way to
817 represent them in the cache. */
818 if (qtype == T_PTR)
819 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000820 int name_encoding = in_arpa_name_2_addr(name, &addr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100821
822 if (!name_encoding)
823 continue;
824
825 if (!(flags & F_NXDOMAIN))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000826 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100827 cname_loop:
828 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000829 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100830
Simon Kelley5aabfc72007-08-29 11:24:47 +0100831 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100832 {
Simon Kelley824af852008-02-12 20:43:05 +0000833 unsigned char *tmp = namep;
834 /* the loop body overwrites the original name, so get it back here. */
Simon Kelley9009d742008-11-14 20:04:27 +0000835 if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
836 !(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000837 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100838
839 GETSHORT(aqtype, p1);
840 GETSHORT(aqclass, p1);
841 GETLONG(attl, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100842 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
843 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000844 (p1) -= 4;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100845 PUTLONG(daemon->max_ttl, p1);
846 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100847 GETSHORT(ardlen, p1);
848 endrr = p1+ardlen;
849
850 /* TTL of record is minimum of CNAMES and PTR */
851 if (attl < cttl)
852 cttl = attl;
853
854 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
855 {
Simon Kelley9009d742008-11-14 20:04:27 +0000856 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000857 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100858
859 if (aqtype == T_CNAME)
860 {
861 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000862 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100863 goto cname_loop;
864 }
865
866 cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
867 found = 1;
868 }
869
870 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000871 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000872 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100873 }
874 }
875
Simon Kelley28866e92011-02-14 20:19:14 +0000876 if (!found && !option_bool(OPT_NO_NEG))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100877 {
878 if (!searched_soa)
879 {
880 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100881 ttl = find_soa(header, qlen, NULL);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100882 }
883 if (ttl)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100884 cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000885 }
886 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100887 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000888 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100889 /* everything other than PTR */
890 struct crec *newc;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100891 int addrlen;
892
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100893 if (qtype == T_A)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100894 {
895 addrlen = INADDRSZ;
896 flags |= F_IPV4;
897 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000898#ifdef HAVE_IPV6
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100899 else if (qtype == T_AAAA)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100900 {
901 addrlen = IN6ADDRSZ;
902 flags |= F_IPV6;
903 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000904#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100905 else
906 continue;
907
908 if (!(flags & F_NXDOMAIN))
909 {
910 cname_loop1:
911 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000912 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100913
Simon Kelley5aabfc72007-08-29 11:24:47 +0100914 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000915 {
Simon Kelley9009d742008-11-14 20:04:27 +0000916 if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000917 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100918
919 GETSHORT(aqtype, p1);
920 GETSHORT(aqclass, p1);
921 GETLONG(attl, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100922 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
923 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000924 (p1) -= 4;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100925 PUTLONG(daemon->max_ttl, p1);
926 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100927 GETSHORT(ardlen, p1);
928 endrr = p1+ardlen;
929
930 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000931 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100932 if (aqtype == T_CNAME)
933 {
934 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000935 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100936 newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
Simon Kelley7b4ad2e2012-04-04 14:05:35 +0100937 if (newc)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100938 {
Simon Kelley7b4ad2e2012-04-04 14:05:35 +0100939 newc->addr.cname.cache = NULL;
940 if (cpp)
941 {
942 cpp->addr.cname.cache = newc;
943 cpp->addr.cname.uid = newc->uid;
944 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100945 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000946
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100947 cpp = newc;
948 if (attl < cttl)
949 cttl = attl;
950
Simon Kelley9009d742008-11-14 20:04:27 +0000951 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000952 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100953 goto cname_loop1;
954 }
955 else
956 {
957 found = 1;
Simon Kelley9009d742008-11-14 20:04:27 +0000958
Simon Kelley5aabfc72007-08-29 11:24:47 +0100959 /* copy address into aligned storage */
Simon Kelley9009d742008-11-14 20:04:27 +0000960 if (!CHECK_LEN(header, p1, qlen, addrlen))
961 return 0; /* bad packet */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100962 memcpy(&addr, p1, addrlen);
Simon Kelley824af852008-02-12 20:43:05 +0000963
964 /* check for returned address in private space */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100965 if (check_rebind &&
Simon Kelley824af852008-02-12 20:43:05 +0000966 (flags & F_IPV4) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000967 private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
Simon Kelley824af852008-02-12 20:43:05 +0000968 return 1;
969
Simon Kelley5aabfc72007-08-29 11:24:47 +0100970 newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +0000971 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100972 {
973 cpp->addr.cname.cache = newc;
974 cpp->addr.cname.uid = newc->uid;
975 }
976 cpp = NULL;
977 }
978 }
979
980 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000981 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000982 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100983 }
984 }
985
Simon Kelley28866e92011-02-14 20:19:14 +0000986 if (!found && !option_bool(OPT_NO_NEG))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100987 {
988 if (!searched_soa)
989 {
990 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100991 ttl = find_soa(header, qlen, NULL);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100992 }
993 /* If there's no SOA to get the TTL from, but there is a CNAME
Simon Kelley824af852008-02-12 20:43:05 +0000994 pointing at this, inherit its TTL */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100995 if (ttl || cpp)
996 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100997 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
Simon Kelley26128d22004-11-14 16:43:54 +0000998 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100999 {
1000 cpp->addr.cname.cache = newc;
1001 cpp->addr.cname.uid = newc->uid;
1002 }
1003 }
1004 }
1005 }
1006 }
1007
Simon Kelley1023dcb2012-04-09 18:00:08 +01001008 /* Don't put stuff from a truncated packet into the cache.
1009 Don't cache replies where DNSSEC validation was turned off, either
1010 the upstream server told us so, or the original query specified it.
1011 Don't cache replies from non-recursive nameservers, since we may get a
1012 reply containing a CNAME but not its target, even though the target
1013 does exist. */
1014 if (!(header->hb3 & HB3_TC) &&
1015 !(header->hb4 & HB4_CD) &&
1016 (header->hb4 & HB4_RA) &&
1017 !checking_disabled)
Simon Kelley824af852008-02-12 20:43:05 +00001018 cache_end_insert();
1019
1020 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001021}
1022
1023/* If the packet holds exactly one query
Simon Kelley28866e92011-02-14 20:19:14 +00001024 return F_IPV4 or F_IPV6 and leave the name from the query in name */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001025
Simon Kelley572b41e2011-02-18 18:11:18 +00001026unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001027{
1028 unsigned char *p = (unsigned char *)(header+1);
1029 int qtype, qclass;
1030
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001031 if (typep)
1032 *typep = 0;
1033
Simon Kelley572b41e2011-02-18 18:11:18 +00001034 if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001035 return 0; /* must be exactly one query. */
1036
Simon Kelley9009d742008-11-14 20:04:27 +00001037 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001038 return 0; /* bad packet */
1039
1040 GETSHORT(qtype, p);
1041 GETSHORT(qclass, p);
1042
Simon Kelley0a852542005-03-23 20:28:59 +00001043 if (typep)
1044 *typep = qtype;
1045
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001046 if (qclass == C_IN)
1047 {
1048 if (qtype == T_A)
1049 return F_IPV4;
1050 if (qtype == T_AAAA)
1051 return F_IPV6;
1052 if (qtype == T_ANY)
1053 return F_IPV4 | F_IPV6;
1054 }
1055
1056 return F_QUERY;
1057}
1058
1059
Simon Kelley572b41e2011-02-18 18:11:18 +00001060size_t setup_reply(struct dns_header *header, size_t qlen,
Simon Kelley28866e92011-02-14 20:19:14 +00001061 struct all_addr *addrp, unsigned int flags, unsigned long ttl)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001062{
1063 unsigned char *p = skip_questions(header, qlen);
1064
Simon Kelley572b41e2011-02-18 18:11:18 +00001065 /* clear authoritative and truncated flags, set QR flag */
1066 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
1067 /* set RA flag */
1068 header->hb4 |= HB4_RA;
1069
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001070 header->nscount = htons(0);
1071 header->arcount = htons(0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001072 header->ancount = htons(0); /* no answers unless changed below */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001073 if (flags == F_NEG)
Simon Kelley572b41e2011-02-18 18:11:18 +00001074 SET_RCODE(header, SERVFAIL); /* couldn't get memory */
Simon Kelley824af852008-02-12 20:43:05 +00001075 else if (flags == F_NOERR)
Simon Kelley572b41e2011-02-18 18:11:18 +00001076 SET_RCODE(header, NOERROR); /* empty domain */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001077 else if (flags == F_NXDOMAIN)
Simon Kelley572b41e2011-02-18 18:11:18 +00001078 SET_RCODE(header, NXDOMAIN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001079 else if (p && flags == F_IPV4)
1080 { /* we know the address */
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_A, C_IN, "4", addrp);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001085 }
1086#ifdef HAVE_IPV6
1087 else if (p && flags == F_IPV6)
1088 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001089 SET_RCODE(header, NOERROR);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001090 header->ancount = htons(1);
Simon Kelley572b41e2011-02-18 18:11:18 +00001091 header->hb3 |= HB3_AA;
1092 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 +00001093 }
1094#endif
1095 else /* nowhere to forward to */
Simon Kelley572b41e2011-02-18 18:11:18 +00001096 SET_RCODE(header, REFUSED);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001097
1098 return p - (unsigned char *)header;
1099}
Simon Kelley36717ee2004-09-20 19:20:58 +01001100
1101/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001102int check_for_local_domain(char *name, time_t now)
Simon Kelley36717ee2004-09-20 19:20:58 +01001103{
1104 struct crec *crecp;
Simon Kelley0a852542005-03-23 20:28:59 +00001105 struct mx_srv_record *mx;
1106 struct txt_record *txt;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001107 struct interface_name *intr;
1108 struct ptr_record *ptr;
Simon Kelley7de060b2011-08-26 17:24:52 +01001109 struct naptr *naptr;
1110
1111 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +01001112 (crecp->flags & (F_HOSTS | F_DHCP)))
1113 return 1;
1114
Simon Kelley7de060b2011-08-26 17:24:52 +01001115 for (naptr = daemon->naptr; naptr; naptr = naptr->next)
1116 if (hostname_isequal(name, naptr->name))
1117 return 1;
1118
1119 for (mx = daemon->mxnames; mx; mx = mx->next)
Simon Kelley0a852542005-03-23 20:28:59 +00001120 if (hostname_isequal(name, mx->name))
Simon Kelley36717ee2004-09-20 19:20:58 +01001121 return 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001122
Simon Kelley0a852542005-03-23 20:28:59 +00001123 for (txt = daemon->txt; txt; txt = txt->next)
1124 if (hostname_isequal(name, txt->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001125 return 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001126
1127 for (intr = daemon->int_names; intr; intr = intr->next)
1128 if (hostname_isequal(name, intr->name))
1129 return 1;
1130
1131 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1132 if (hostname_isequal(name, ptr->name))
1133 return 1;
1134
Simon Kelley36717ee2004-09-20 19:20:58 +01001135 return 0;
1136}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001137
1138/* Is the packet a reply with the answer address equal to addr?
1139 If so mung is into an NXDOMAIN reply and also put that information
1140 in the cache. */
Simon Kelley572b41e2011-02-18 18:11:18 +00001141int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001142 struct bogus_addr *baddr, time_t now)
1143{
1144 unsigned char *p;
1145 int i, qtype, qclass, rdlen;
1146 unsigned long ttl;
1147 struct bogus_addr *baddrp;
1148
1149 /* skip over questions */
1150 if (!(p = skip_questions(header, qlen)))
1151 return 0; /* bad packet */
1152
Simon Kelley5aabfc72007-08-29 11:24:47 +01001153 for (i = ntohs(header->ancount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001154 {
Simon Kelley9009d742008-11-14 20:04:27 +00001155 if (!extract_name(header, qlen, &p, name, 1, 10))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001156 return 0; /* bad packet */
1157
1158 GETSHORT(qtype, p);
1159 GETSHORT(qclass, p);
1160 GETLONG(ttl, p);
1161 GETSHORT(rdlen, p);
1162
1163 if (qclass == C_IN && qtype == T_A)
Simon Kelley9009d742008-11-14 20:04:27 +00001164 {
1165 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
1166 return 0;
1167
1168 for (baddrp = baddr; baddrp; baddrp = baddrp->next)
1169 if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
1170 {
1171 /* Found a bogus address. Insert that info here, since there no SOA record
1172 to get the ttl from in the normal processing */
1173 cache_start_insert();
1174 cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
1175 cache_end_insert();
1176
1177 return 1;
1178 }
1179 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001180
Simon Kelley9009d742008-11-14 20:04:27 +00001181 if (!ADD_RDLEN(header, p, qlen, rdlen))
1182 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001183 }
1184
1185 return 0;
1186}
1187
Simon Kelleyb75e9362012-12-07 11:50:41 +00001188int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
Simon Kelleye1ff4192012-12-09 17:08:47 +00001189 unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001190{
1191 va_list ap;
1192 unsigned char *sav, *p = *pp;
1193 int j;
1194 unsigned short usval;
1195 long lval;
1196 char *sval;
1197
1198 if (truncp && *truncp)
1199 return 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001200
1201 va_start(ap, format); /* make ap point to 1st unamed argument */
1202
Simon Kelleyb75e9362012-12-07 11:50:41 +00001203 if (nameoffset > 0)
Simon Kelley4f7b3042012-11-28 21:27:02 +00001204 {
1205 PUTSHORT(nameoffset | 0xc000, p);
1206 }
1207 else
1208 {
Simon Kelleye1ff4192012-12-09 17:08:47 +00001209 char *name = va_arg(ap, char *);
1210 if (name)
1211 p = do_rfc1035_name(p, name);
Simon Kelleyb75e9362012-12-07 11:50:41 +00001212 if (nameoffset < 0)
1213 {
1214 PUTSHORT(-nameoffset | 0xc000, p);
1215 }
1216 else
1217 *p++ = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001218 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001219
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001220 PUTSHORT(type, p);
1221 PUTSHORT(class, p);
1222 PUTLONG(ttl, p); /* TTL */
1223
1224 sav = p; /* Save pointer to RDLength field */
1225 PUTSHORT(0, p); /* Placeholder RDLength */
1226
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001227 for (; *format; format++)
1228 switch (*format)
1229 {
1230#ifdef HAVE_IPV6
1231 case '6':
1232 sval = va_arg(ap, char *);
1233 memcpy(p, sval, IN6ADDRSZ);
1234 p += IN6ADDRSZ;
1235 break;
1236#endif
1237
1238 case '4':
1239 sval = va_arg(ap, char *);
1240 memcpy(p, sval, INADDRSZ);
1241 p += INADDRSZ;
1242 break;
1243
1244 case 's':
1245 usval = va_arg(ap, int);
1246 PUTSHORT(usval, p);
1247 break;
1248
1249 case 'l':
1250 lval = va_arg(ap, long);
1251 PUTLONG(lval, p);
1252 break;
1253
1254 case 'd':
1255 /* get domain-name answer arg and store it in RDATA field */
Simon Kelley0a852542005-03-23 20:28:59 +00001256 if (offset)
1257 *offset = p - (unsigned char *)header;
Simon Kelley3d8df262005-08-29 12:19:27 +01001258 p = do_rfc1035_name(p, va_arg(ap, char *));
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001259 *p++ = 0;
1260 break;
Simon Kelley3d8df262005-08-29 12:19:27 +01001261
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001262 case 't':
Simon Kelley0a852542005-03-23 20:28:59 +00001263 usval = va_arg(ap, int);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001264 sval = va_arg(ap, char *);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01001265 if (usval != 0)
1266 memcpy(p, sval, usval);
Simon Kelley0a852542005-03-23 20:28:59 +00001267 p += usval;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001268 break;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001269
1270 case 'z':
1271 sval = va_arg(ap, char *);
1272 usval = sval ? strlen(sval) : 0;
1273 if (usval > 255)
1274 usval = 255;
1275 *p++ = (unsigned char)usval;
1276 memcpy(p, sval, usval);
1277 p += usval;
1278 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001279 }
1280
1281 va_end(ap); /* clean up variable argument pointer */
1282
1283 j = p - sav - 2;
1284 PUTSHORT(j, sav); /* Now, store real RDLength */
1285
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001286 /* check for overflow of buffer */
1287 if (limit && ((unsigned char *)limit - p) < 0)
1288 {
1289 if (truncp)
1290 *truncp = 1;
1291 return 0;
1292 }
1293
1294 *pp = p;
1295 return 1;
1296}
1297
Simon Kelley9009d742008-11-14 20:04:27 +00001298static unsigned long crec_ttl(struct crec *crecp, time_t now)
1299{
1300 /* Return 0 ttl for DHCP entries, which might change
1301 before the lease expires. */
1302
1303 if (crecp->flags & (F_IMMORTAL | F_DHCP))
1304 return daemon->local_ttl;
1305
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001306 /* Return the Max TTL value if it is lower then the actual TTL */
1307 if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
1308 return crecp->ttd - now;
1309 else
1310 return daemon->max_ttl;
Simon Kelley9009d742008-11-14 20:04:27 +00001311}
1312
1313
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001314/* return zero if we can't answer from cache, or packet size if we can */
Simon Kelley572b41e2011-02-18 18:11:18 +00001315size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
Simon Kelleycdeda282006-03-16 20:16:06 +00001316 struct in_addr local_addr, struct in_addr local_netmask, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001317{
Simon Kelley3be34542004-09-11 19:12:13 +01001318 char *name = daemon->namebuff;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001319 unsigned char *p, *ansp, *pheader;
Simon Kelley832af0b2007-01-21 20:01:28 +00001320 int qtype, qclass;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001321 struct all_addr addr;
Simon Kelleyb75e9362012-12-07 11:50:41 +00001322 int nameoffset;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001323 unsigned short flag;
Simon Kelley0a852542005-03-23 20:28:59 +00001324 int q, ans, anscount = 0, addncount = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001325 int dryrun = 0, sec_reqd = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001326 int is_sign;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001327 struct crec *crecp;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001328 int nxdomain = 0, auth = 1, trunc = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001329 struct mx_srv_record *rec;
1330
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001331 /* If there is an RFC2671 pseudoheader then it will be overwritten by
1332 partial replies, so we have to do a dry run to see if we can answer
1333 the query. We check to see if the do bit is set, if so we always
1334 forward rather than answering from the cache, which doesn't include
1335 security information. */
1336
Simon Kelley832af0b2007-01-21 20:01:28 +00001337 if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001338 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001339 unsigned short udpsz, flags;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001340 unsigned char *psave = pheader;
1341
1342 GETSHORT(udpsz, pheader);
Simon Kelley7de060b2011-08-26 17:24:52 +01001343 pheader += 2; /* ext_rcode */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001344 GETSHORT(flags, pheader);
1345
1346 sec_reqd = flags & 0x8000; /* do bit */
1347
1348 /* If our client is advertising a larger UDP packet size
1349 than we allow, trim it so that we don't get an overlarge
1350 response from upstream */
1351
Simon Kelley832af0b2007-01-21 20:01:28 +00001352 if (!is_sign && (udpsz > daemon->edns_pktsz))
Simon Kelley3be34542004-09-11 19:12:13 +01001353 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001354
1355 dryrun = 1;
1356 }
1357
Simon Kelley572b41e2011-02-18 18:11:18 +00001358 if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
Simon Kelley832af0b2007-01-21 20:01:28 +00001359 return 0;
1360
Simon Kelley0a852542005-03-23 20:28:59 +00001361 for (rec = daemon->mxnames; rec; rec = rec->next)
1362 rec->offset = 0;
1363
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001364 rerun:
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001365 /* determine end of question section (we put answers there) */
1366 if (!(ansp = skip_questions(header, qlen)))
1367 return 0; /* bad packet */
1368
1369 /* now process each question, answers go in RRs after the question */
1370 p = (unsigned char *)(header+1);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001371
Simon Kelley5aabfc72007-08-29 11:24:47 +01001372 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001373 {
1374 /* save pointer to name for copying into answers */
1375 nameoffset = p - (unsigned char *)header;
1376
1377 /* now extract name as .-concatenated string into name */
Simon Kelley9009d742008-11-14 20:04:27 +00001378 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001379 return 0; /* bad packet */
Simon Kelley832af0b2007-01-21 20:01:28 +00001380
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001381 GETSHORT(qtype, p);
1382 GETSHORT(qclass, p);
1383
1384 ans = 0; /* have we answered this question */
1385
Simon Kelley0a852542005-03-23 20:28:59 +00001386 if (qtype == T_TXT || qtype == T_ANY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001387 {
Simon Kelley0a852542005-03-23 20:28:59 +00001388 struct txt_record *t;
1389 for(t = daemon->txt; t ; t = t->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001390 {
Simon Kelley0a852542005-03-23 20:28:59 +00001391 if (t->class == qclass && hostname_isequal(name, t->name))
1392 {
1393 ans = 1;
Simon Kelleye17fb622006-01-14 20:33:46 +00001394 if (!dryrun)
1395 {
Simon Kelley28866e92011-02-14 20:19:14 +00001396 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
Simon Kelleye17fb622006-01-14 20:33:46 +00001397 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1398 daemon->local_ttl, NULL,
1399 T_TXT, t->class, "t", t->len, t->txt))
1400 anscount++;
1401
1402 }
Simon Kelley0a852542005-03-23 20:28:59 +00001403 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001404 }
Simon Kelley0a852542005-03-23 20:28:59 +00001405 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001406
Simon Kelley0a852542005-03-23 20:28:59 +00001407 if (qclass == C_IN)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001408 {
Simon Kelley9f7f3b12012-05-28 21:39:57 +01001409 struct txt_record *t;
1410
1411 for (t = daemon->rr; t; t = t->next)
1412 if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
1413 {
1414 ans = 1;
1415 if (!dryrun)
1416 {
1417 log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
1418 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1419 daemon->local_ttl, NULL,
1420 t->class, C_IN, "t", t->len, t->txt))
1421 anscount ++;
1422 }
1423 }
1424
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001425 if (qtype == T_PTR || qtype == T_ANY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001426 {
Simon Kelley832af0b2007-01-21 20:01:28 +00001427 /* see if it's w.z.y.z.in-addr.arpa format */
1428 int is_arpa = in_arpa_name_2_addr(name, &addr);
1429 struct ptr_record *ptr;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001430 struct interface_name* intr = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00001431
1432 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1433 if (hostname_isequal(name, ptr->name))
1434 break;
1435
Simon Kelleyf2621c72007-04-29 19:47:21 +01001436 if (is_arpa == F_IPV4)
1437 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001438 {
1439 if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
1440 break;
1441 else
1442 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
1443 intr = intr->next;
1444 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001445
1446 if (intr)
1447 {
1448 ans = 1;
1449 if (!dryrun)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001450 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001451 log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001452 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1453 daemon->local_ttl, NULL,
1454 T_PTR, C_IN, "d", intr->name))
1455 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001456 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001457 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001458 else if (ptr)
1459 {
1460 ans = 1;
1461 if (!dryrun)
1462 {
Simon Kelley28866e92011-02-14 20:19:14 +00001463 log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
Simon Kelley832af0b2007-01-21 20:01:28 +00001464 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001465 if (hostname_isequal(name, ptr->name) &&
1466 add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1467 daemon->local_ttl, NULL,
1468 T_PTR, C_IN, "d", ptr->ptr))
1469 anscount++;
1470
Simon Kelley832af0b2007-01-21 20:01:28 +00001471 }
1472 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001473 else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
1474 do
1475 {
1476 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
1477 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1478 continue;
1479
1480 if (crecp->flags & F_NEG)
1481 {
1482 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001483 auth = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001484 if (crecp->flags & F_NXDOMAIN)
1485 nxdomain = 1;
1486 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001487 log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001488 }
1489 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
1490 {
1491 ans = 1;
1492 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1493 auth = 0;
1494 if (!dryrun)
1495 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001496 log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001497 record_source(crecp->uid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001498
Simon Kelley9009d742008-11-14 20:04:27 +00001499 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1500 crec_ttl(crecp, now), NULL,
Simon Kelleyf2621c72007-04-29 19:47:21 +01001501 T_PTR, C_IN, "d", cache_get_name(crecp)))
1502 anscount++;
1503 }
1504 }
1505 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
1506 else if (is_arpa == F_IPV4 &&
Simon Kelley28866e92011-02-14 20:19:14 +00001507 option_bool(OPT_BOGUSPRIV) &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001508 private_net(addr.addr.addr4, 1))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001509 {
1510 /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
1511 ans = 1;
1512 nxdomain = 1;
1513 if (!dryrun)
1514 log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
Simon Kelley1a6bca82008-07-11 11:11:42 +01001515 name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001516 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001517 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001518
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001519 for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
1520 {
1521 unsigned short type = T_A;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001522
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001523 if (flag == F_IPV6)
1524#ifdef HAVE_IPV6
Simon Kelley3d8df262005-08-29 12:19:27 +01001525 type = T_AAAA;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001526#else
Simon Kelley3d8df262005-08-29 12:19:27 +01001527 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001528#endif
1529
1530 if (qtype != type && qtype != T_ANY)
1531 continue;
1532
Simon Kelley316e2732010-01-22 20:16:09 +00001533 /* Check for "A for A" queries; be rather conservative
1534 about what looks like dotted-quad. */
1535 if (qtype == T_A)
Simon Kelley3d8df262005-08-29 12:19:27 +01001536 {
Simon Kelley316e2732010-01-22 20:16:09 +00001537 char *cp;
1538 unsigned int i, a;
1539 int x;
1540
1541 for (cp = name, i = 0, a = 0; *cp; i++)
Simon Kelley3d8df262005-08-29 12:19:27 +01001542 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001543 if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)
Simon Kelley316e2732010-01-22 20:16:09 +00001544 {
1545 i = 5;
1546 break;
1547 }
1548
1549 a = (a << 8) + x;
1550
1551 if (*cp == '.')
1552 cp++;
Simon Kelley3d8df262005-08-29 12:19:27 +01001553 }
Simon Kelley316e2732010-01-22 20:16:09 +00001554
1555 if (i == 4)
1556 {
1557 ans = 1;
1558 if (!dryrun)
1559 {
1560 addr.addr.addr4.s_addr = htonl(a);
1561 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
1562 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1563 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1564 anscount++;
1565 }
1566 continue;
1567 }
Simon Kelley3d8df262005-08-29 12:19:27 +01001568 }
1569
Simon Kelleyf2621c72007-04-29 19:47:21 +01001570 /* interface name stuff */
1571 if (qtype == T_A)
1572 {
1573 struct interface_name *intr;
1574
1575 for (intr = daemon->int_names; intr; intr = intr->next)
1576 if (hostname_isequal(name, intr->name))
1577 break;
1578
1579 if (intr)
1580 {
1581 ans = 1;
1582 if (!dryrun)
1583 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001584 if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001585 log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001586 else
1587 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001588 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001589 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1590 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1591 anscount++;
1592 }
1593 }
1594 continue;
1595 }
1596 }
1597
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001598 cname_restart:
1599 if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
1600 {
1601 int localise = 0;
1602
1603 /* See if a putative address is on the network from which we recieved
1604 the query, is so we'll filter other answers. */
Simon Kelley28866e92011-02-14 20:19:14 +00001605 if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001606 {
1607 struct crec *save = crecp;
1608 do {
1609 if ((crecp->flags & F_HOSTS) &&
1610 is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1611 {
1612 localise = 1;
1613 break;
1614 }
1615 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
1616 crecp = save;
1617 }
1618
1619 do
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001620 {
Simon Kelley26128d22004-11-14 16:43:54 +00001621 /* don't answer wildcard queries with data not from /etc/hosts
1622 or DHCP leases */
1623 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1624 break;
1625
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001626 if (crecp->flags & F_CNAME)
1627 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001628 if (!dryrun)
1629 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001630 log_query(crecp->flags, name, NULL, record_source(crecp->uid));
Simon Kelley9009d742008-11-14 20:04:27 +00001631 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1632 crec_ttl(crecp, now), &nameoffset,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001633 T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
1634 anscount++;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001635 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001636
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001637 strcpy(name, cache_get_name(crecp->addr.cname.cache));
1638 goto cname_restart;
1639 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001640
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001641 if (crecp->flags & F_NEG)
1642 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001643 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001644 auth = 0;
1645 if (crecp->flags & F_NXDOMAIN)
1646 nxdomain = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001647 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001648 log_query(crecp->flags, name, NULL, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001649 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001650 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001651 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001652 /* If we are returning local answers depending on network,
1653 filter here. */
1654 if (localise &&
1655 (crecp->flags & F_HOSTS) &&
1656 !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1657 continue;
1658
1659 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1660 auth = 0;
1661
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001662 ans = 1;
1663 if (!dryrun)
1664 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001665 log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001666 record_source(crecp->uid));
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001667
Simon Kelley9009d742008-11-14 20:04:27 +00001668 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1669 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001670 type == T_A ? "4" : "6", &crecp->addr))
1671 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001672 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001673 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001674 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001675 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001676 }
Simon Kelleyd1c759c2012-04-16 17:26:19 +01001677
1678 if (qtype == T_CNAME || qtype == T_ANY)
1679 {
1680 if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
1681 (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP))))
1682 {
1683 ans = 1;
1684 if (!dryrun)
1685 {
1686 log_query(crecp->flags, name, NULL, record_source(crecp->uid));
1687 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1688 crec_ttl(crecp, now), &nameoffset,
1689 T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
1690 anscount++;
1691 }
1692 }
1693 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001694
1695 if (qtype == T_MX || qtype == T_ANY)
1696 {
1697 int found = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001698 for (rec = daemon->mxnames; rec; rec = rec->next)
1699 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001700 {
1701 ans = found = 1;
1702 if (!dryrun)
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001703 {
Simon Kelleye1ff4192012-12-09 17:08:47 +00001704 int offset;
Simon Kelley28866e92011-02-14 20:19:14 +00001705 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
Simon Kelley0a852542005-03-23 20:28:59 +00001706 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1707 &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
1708 {
1709 anscount++;
1710 if (rec->target)
1711 rec->offset = offset;
1712 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001713 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001714 }
1715
Simon Kelley28866e92011-02-14 20:19:14 +00001716 if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001717 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
1718 {
1719 ans = 1;
1720 if (!dryrun)
1721 {
Simon Kelley28866e92011-02-14 20:19:14 +00001722 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001723 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
1724 T_MX, C_IN, "sd", 1,
Simon Kelley28866e92011-02-14 20:19:14 +00001725 option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001726 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001727 }
1728 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001729 }
1730
1731 if (qtype == T_SRV || qtype == T_ANY)
1732 {
1733 int found = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001734 struct mx_srv_record *move = NULL, **up = &daemon->mxnames;
1735
Simon Kelley0a852542005-03-23 20:28:59 +00001736 for (rec = daemon->mxnames; rec; rec = rec->next)
1737 if (rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001738 {
1739 found = ans = 1;
1740 if (!dryrun)
1741 {
Simon Kelleye1ff4192012-12-09 17:08:47 +00001742 int offset;
Simon Kelley28866e92011-02-14 20:19:14 +00001743 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001744 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
Simon Kelley0a852542005-03-23 20:28:59 +00001745 &offset, T_SRV, C_IN, "sssd",
1746 rec->priority, rec->weight, rec->srvport, rec->target))
1747 {
1748 anscount++;
1749 if (rec->target)
1750 rec->offset = offset;
1751 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001752 }
Simon Kelley28866e92011-02-14 20:19:14 +00001753
1754 /* unlink first SRV record found */
1755 if (!move)
1756 {
1757 move = rec;
1758 *up = rec->next;
1759 }
1760 else
1761 up = &rec->next;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001762 }
Simon Kelley28866e92011-02-14 20:19:14 +00001763 else
1764 up = &rec->next;
1765
1766 /* put first SRV record back at the end. */
1767 if (move)
1768 {
1769 *up = move;
1770 move->next = NULL;
1771 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001772
Simon Kelley28866e92011-02-14 20:19:14 +00001773 if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001774 {
1775 ans = 1;
1776 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001777 log_query(F_CONFIG | F_NEG, name, NULL, NULL);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001778 }
1779 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001780
1781 if (qtype == T_NAPTR || qtype == T_ANY)
1782 {
1783 struct naptr *na;
1784 for (na = daemon->naptr; na; na = na->next)
1785 if (hostname_isequal(name, na->name))
1786 {
1787 ans = 1;
1788 if (!dryrun)
1789 {
Simon Kelley28866e92011-02-14 20:19:14 +00001790 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
Simon Kelley1a6bca82008-07-11 11:11:42 +01001791 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1792 NULL, T_NAPTR, C_IN, "sszzzd",
1793 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
1794 anscount++;
1795 }
1796 }
1797 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001798
1799 if (qtype == T_MAILB)
1800 ans = 1, nxdomain = 1;
1801
Simon Kelley28866e92011-02-14 20:19:14 +00001802 if (qtype == T_SOA && option_bool(OPT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001803 {
1804 ans = 1;
1805 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001806 log_query(F_CONFIG | F_NEG, name, &addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001807 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001808 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001809
1810 if (!ans)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001811 return 0; /* failed to answer a question */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001812 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001813
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001814 if (dryrun)
1815 {
1816 dryrun = 0;
1817 goto rerun;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001818 }
1819
Simon Kelley0a852542005-03-23 20:28:59 +00001820 /* create an additional data section, for stuff in SRV and MX record replies. */
1821 for (rec = daemon->mxnames; rec; rec = rec->next)
1822 if (rec->offset != 0)
1823 {
1824 /* squash dupes */
1825 struct mx_srv_record *tmp;
1826 for (tmp = rec->next; tmp; tmp = tmp->next)
1827 if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
1828 tmp->offset = 0;
1829
1830 crecp = NULL;
1831 while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
1832 {
Simon Kelley0a852542005-03-23 20:28:59 +00001833#ifdef HAVE_IPV6
1834 int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
1835#else
1836 int type = T_A;
1837#endif
1838 if (crecp->flags & F_NEG)
1839 continue;
1840
Simon Kelley9009d742008-11-14 20:04:27 +00001841 if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
1842 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelley0a852542005-03-23 20:28:59 +00001843 crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
1844 addncount++;
1845 }
1846 }
1847
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001848 /* done all questions, set up header and return length of result */
Simon Kelley572b41e2011-02-18 18:11:18 +00001849 /* clear authoritative and truncated flags, set QR flag */
1850 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
1851 /* set RA flag */
1852 header->hb4 |= HB4_RA;
1853
1854 /* authoritive - only hosts and DHCP derived names. */
1855 if (auth)
1856 header->hb3 |= HB3_AA;
1857
1858 /* truncation */
1859 if (trunc)
1860 header->hb3 |= HB3_TC;
1861
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001862 if (anscount == 0 && nxdomain)
Simon Kelley572b41e2011-02-18 18:11:18 +00001863 SET_RCODE(header, NXDOMAIN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001864 else
Simon Kelley572b41e2011-02-18 18:11:18 +00001865 SET_RCODE(header, NOERROR); /* no error */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001866 header->ancount = htons(anscount);
1867 header->nscount = htons(0);
Simon Kelley0a852542005-03-23 20:28:59 +00001868 header->arcount = htons(addncount);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001869 return ansp - (unsigned char *)header;
1870}
1871