blob: c0b6c174768003b3990891b00ea58d0b1444642e [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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};
Simon Kelleyed4c0762013-10-08 20:46:34 +0100516
517static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
518 int optno, unsigned char *opt, size_t optlen)
519{
520 unsigned char *lenp, *datap, *p;
521 int rdlen;
522
523 if (ntohs(header->arcount) == 0)
524 {
525 /* We are adding the pseudoheader */
526 if (!(p = skip_questions(header, plen)) ||
527 !(p = skip_section(p,
528 ntohs(header->ancount) + ntohs(header->nscount),
529 header, plen)))
530 return plen;
531 *p++ = 0; /* empty name */
532 PUTSHORT(T_OPT, p);
533 PUTSHORT(daemon->edns_pktsz, p); /* max packet length */
534 PUTLONG(0, p); /* extended RCODE */
535 lenp = p;
536 PUTSHORT(0, p); /* RDLEN */
537 rdlen = 0;
538 if (((ssize_t)optlen) > (limit - (p + 4)))
539 return plen; /* Too big */
540 header->arcount = htons(1);
541 datap = p;
542 }
543 else
544 {
545 int i, is_sign;
546 unsigned short code, len;
547
548 if (ntohs(header->arcount) != 1 ||
549 !(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
550 is_sign ||
551 (!(p = skip_name(p, header, plen, 10))))
552 return plen;
553
554 p += 8; /* skip UDP length and RCODE */
555
556 lenp = p;
557 GETSHORT(rdlen, p);
558 if (!CHECK_LEN(header, p, plen, rdlen))
559 return plen; /* bad packet */
560 datap = p;
561
562 /* check if option already there */
563 for (i = 0; i + 4 < rdlen; i += len + 4)
564 {
565 GETSHORT(code, p);
566 GETSHORT(len, p);
567 if (code == optno)
568 return plen;
569 p += len;
570 }
571
572 if (((ssize_t)optlen) > (limit - (p + 4)))
573 return plen; /* Too big */
574 }
575
576 PUTSHORT(optno, p);
577 PUTSHORT(optlen, p);
578 memcpy(p, opt, optlen);
579 p += optlen;
580
581 PUTSHORT(p - datap, lenp);
582 return p - (unsigned char *)header;
583
584}
Simon Kelley28866e92011-02-14 20:19:14 +0000585
586static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
587{
588 struct macparm *parm = parmv;
589 int match = 0;
Simon Kelleyed4c0762013-10-08 20:46:34 +0100590
Simon Kelley28866e92011-02-14 20:19:14 +0000591 if (family == parm->l3->sa.sa_family)
592 {
593 if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
594 match = 1;
595#ifdef HAVE_IPV6
596 else
597 if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
598 match = 1;
599#endif
600 }
601
602 if (!match)
603 return 1; /* continue */
Simon Kelley28866e92011-02-14 20:19:14 +0000604
Simon Kelleyed4c0762013-10-08 20:46:34 +0100605 parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen);
Simon Kelley28866e92011-02-14 20:19:14 +0000606
607 return 0; /* done */
608}
609
Simon Kelley572b41e2011-02-18 18:11:18 +0000610size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
Simon Kelley28866e92011-02-14 20:19:14 +0000611{
612 struct macparm parm;
613
614/* Must have an existing pseudoheader as the only ar-record,
615 or have no ar-records. Must also not be signed */
616
617 if (ntohs(header->arcount) > 1)
618 return plen;
619
620 parm.header = header;
621 parm.limit = (unsigned char *)limit;
622 parm.plen = plen;
623 parm.l3 = l3;
624
625 iface_enumerate(AF_UNSPEC, &parm, filter_mac);
626
627 return parm.plen;
628}
629
Simon Kelleyed4c0762013-10-08 20:46:34 +0100630struct subnet_opt {
631 u16 family;
632 u8 source_netmask, scope_netmask;
633#ifdef HAVE_IPV6
634 u8 addr[IN6ADDRSZ];
635#else
636 u8 addr[INADDRSZ];
637#endif
638};
639
640size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
641{
642 /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
643
644 int len;
645 void *addrp;
646
Simon Kelleyed4c0762013-10-08 20:46:34 +0100647#ifdef HAVE_IPV6
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100648 if (source->sa.sa_family == AF_INET6)
Simon Kelleyed4c0762013-10-08 20:46:34 +0100649 {
650 opt->family = htons(2);
651 opt->source_netmask = daemon->addr6_netmask;
652 addrp = &source->in6.sin6_addr;
653 }
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100654 else
Simon Kelleyed4c0762013-10-08 20:46:34 +0100655#endif
Simon Kelley24b5a5d2013-10-11 15:19:28 +0100656 {
657 opt->family = htons(1);
658 opt->source_netmask = daemon->addr4_netmask;
659 addrp = &source->in.sin_addr;
660 }
Simon Kelleyed4c0762013-10-08 20:46:34 +0100661
662 opt->scope_netmask = 0;
663 len = 0;
664
665 if (opt->source_netmask != 0)
666 {
667 len = ((opt->source_netmask - 1) >> 3) + 1;
668 memcpy(opt->addr, addrp, len);
669 if (opt->source_netmask & 7)
670 opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
671 }
672
673 return len + 4;
674}
675
676size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source)
677{
678 /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
679
680 int len;
681 struct subnet_opt opt;
682
683 len = calc_subnet_opt(&opt, source);
684 return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len);
685}
686
687int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
688{
689 /* Section 9.2, Check that subnet option in reply matches. */
690
691
692 int len, calc_len;
693 struct subnet_opt opt;
694 unsigned char *p;
695 int code, i, rdlen;
696
697 calc_len = calc_subnet_opt(&opt, peer);
698
699 if (!(p = skip_name(pseudoheader, header, plen, 10)))
700 return 1;
701
702 p += 8; /* skip UDP length and RCODE */
703
704 GETSHORT(rdlen, p);
705 if (!CHECK_LEN(header, p, plen, rdlen))
706 return 1; /* bad packet */
707
708 /* check if option there */
709 for (i = 0; i + 4 < rdlen; i += len + 4)
710 {
711 GETSHORT(code, p);
712 GETSHORT(len, p);
713 if (code == EDNS0_OPTION_CLIENT_SUBNET)
714 {
715 /* make sure this doesn't mismatch. */
716 opt.scope_netmask = p[3];
717 if (len != calc_len || memcmp(p, &opt, len) != 0)
718 return 0;
719 }
720 p += len;
721 }
722
723 return 1;
724}
725
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000726/* is addr in the non-globally-routed IP space? */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100727static int private_net(struct in_addr addr, int ban_localhost)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000728{
Simon Kelleyf2621c72007-04-29 19:47:21 +0100729 in_addr_t ip_addr = ntohl(addr.s_addr);
730
731 return
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100732 (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
Simon Kelleyf2621c72007-04-29 19:47:21 +0100733 ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
734 ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
735 ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
736 ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000737}
Simon Kelley1cff1662004-03-12 08:12:58 +0000738
Simon Kelley572b41e2011-02-18 18:11:18 +0000739static 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 +0000740{
741 int i, qtype, qclass, rdlen;
Simon Kelley824af852008-02-12 20:43:05 +0000742
743 for (i = count; i != 0; i--)
744 {
Simon Kelley28866e92011-02-14 20:19:14 +0000745 if (name && option_bool(OPT_LOG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100746 {
747 if (!extract_name(header, qlen, &p, name, 1, 10))
748 return 0;
749 }
750 else if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000751 return 0; /* bad packet */
752
753 GETSHORT(qtype, p);
754 GETSHORT(qclass, p);
Simon Kelley7de060b2011-08-26 17:24:52 +0100755 p += 4; /* ttl */
Simon Kelley824af852008-02-12 20:43:05 +0000756 GETSHORT(rdlen, p);
757
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100758 if (qclass == C_IN && qtype == T_A)
Simon Kelley824af852008-02-12 20:43:05 +0000759 {
760 struct doctor *doctor;
761 struct in_addr addr;
762
Simon Kelley9009d742008-11-14 20:04:27 +0000763 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
764 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100765
766 /* alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000767 memcpy(&addr, p, INADDRSZ);
768
769 for (doctor = daemon->doctors; doctor; doctor = doctor->next)
Simon Kelley73a08a22009-02-05 20:28:08 +0000770 {
771 if (doctor->end.s_addr == 0)
772 {
773 if (!is_same_net(doctor->in, addr, doctor->mask))
774 continue;
775 }
776 else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
777 ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
778 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100779
Simon Kelley73a08a22009-02-05 20:28:08 +0000780 addr.s_addr &= ~doctor->mask.s_addr;
781 addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
782 /* Since we munged the data, the server it came from is no longer authoritative */
Simon Kelley572b41e2011-02-18 18:11:18 +0000783 header->hb3 &= ~HB3_AA;
Simon Kelley73a08a22009-02-05 20:28:08 +0000784 memcpy(p, &addr, INADDRSZ);
785 break;
786 }
Simon Kelley824af852008-02-12 20:43:05 +0000787 }
Simon Kelley28866e92011-02-14 20:19:14 +0000788 else if (qtype == T_TXT && name && option_bool(OPT_LOG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100789 {
790 unsigned char *p1 = p;
791 if (!CHECK_LEN(header, p1, qlen, rdlen))
792 return 0;
793 while ((p1 - p) < rdlen)
794 {
795 unsigned int i, len = *p1;
796 unsigned char *p2 = p1;
797 /* make counted string zero-term and sanitise */
798 for (i = 0; i < len; i++)
Simon Kelley231d0612012-04-27 13:50:45 +0100799 {
800 if (!isprint((int)*(p2+1)))
801 break;
802
803 *p2 = *(p2+1);
804 p2++;
805 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100806 *p2 = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000807 my_syslog(LOG_INFO, "reply %s is %s", name, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100808 /* restore */
Simon Kelley231d0612012-04-27 13:50:45 +0100809 memmove(p1 + 1, p1, i);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100810 *p1 = len;
811 p1 += len+1;
812 }
813 }
Simon Kelley824af852008-02-12 20:43:05 +0000814
Simon Kelley9009d742008-11-14 20:04:27 +0000815 if (!ADD_RDLEN(header, p, qlen, rdlen))
816 return 0; /* bad packet */
Simon Kelley824af852008-02-12 20:43:05 +0000817 }
818
819 return p;
820}
821
Simon Kelley572b41e2011-02-18 18:11:18 +0000822static int find_soa(struct dns_header *header, size_t qlen, char *name)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000823{
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100824 unsigned char *p;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000825 int qtype, qclass, rdlen;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100826 unsigned long ttl, minttl = ULONG_MAX;
827 int i, found_soa = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000828
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100829 /* first move to NS section and find TTL from any SOA section */
830 if (!(p = skip_questions(header, qlen)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100831 !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
Simon Kelley824af852008-02-12 20:43:05 +0000832 return 0; /* bad packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000833
Simon Kelley5aabfc72007-08-29 11:24:47 +0100834 for (i = ntohs(header->nscount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000835 {
Simon Kelley9009d742008-11-14 20:04:27 +0000836 if (!(p = skip_name(p, header, qlen, 10)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100837 return 0; /* bad packet */
838
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000839 GETSHORT(qtype, p);
840 GETSHORT(qclass, p);
841 GETLONG(ttl, p);
842 GETSHORT(rdlen, p);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100843
844 if ((qclass == C_IN) && (qtype == T_SOA))
845 {
846 found_soa = 1;
847 if (ttl < minttl)
848 minttl = ttl;
849
850 /* MNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000851 if (!(p = skip_name(p, header, qlen, 0)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100852 return 0;
853 /* RNAME */
Simon Kelley9009d742008-11-14 20:04:27 +0000854 if (!(p = skip_name(p, header, qlen, 20)))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100855 return 0;
856 p += 16; /* SERIAL REFRESH RETRY EXPIRE */
857
858 GETLONG(ttl, p); /* minTTL */
859 if (ttl < minttl)
860 minttl = ttl;
861 }
Simon Kelley9009d742008-11-14 20:04:27 +0000862 else if (!ADD_RDLEN(header, p, qlen, rdlen))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100863 return 0; /* bad packet */
864 }
Simon Kelley9009d742008-11-14 20:04:27 +0000865
Simon Kelley824af852008-02-12 20:43:05 +0000866 /* rewrite addresses in additioal section too */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100867 if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
Simon Kelley824af852008-02-12 20:43:05 +0000868 return 0;
869
870 if (!found_soa)
871 minttl = daemon->neg_ttl;
872
873 return minttl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100874}
875
876/* Note that the following code can create CNAME chains that don't point to a real record,
877 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 +0000878 expired and cleaned out that way.
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100879 Return 1 if we reject an address because it look like part of dns-rebinding attack. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000880int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000881 char **ipsets, int is_sign, int check_rebind, int checking_disabled)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100882{
Simon Kelley824af852008-02-12 20:43:05 +0000883 unsigned char *p, *p1, *endrr, *namep;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100884 int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000885 unsigned long ttl = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100886 struct all_addr addr;
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000887#ifdef HAVE_IPSET
888 char **ipsets_cur;
889#else
890 (void)ipsets; /* unused */
891#endif
892
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100893 cache_start_insert();
Simon Kelley0a852542005-03-23 20:28:59 +0000894
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100895 /* 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 +0000896 if (daemon->doctors || option_bool(OPT_LOG))
Simon Kelley0a852542005-03-23 20:28:59 +0000897 {
898 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100899 ttl = find_soa(header, qlen, name);
Simon Kelley0a852542005-03-23 20:28:59 +0000900 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100901
902 /* go through the questions. */
903 p = (unsigned char *)(header+1);
904
Simon Kelley5aabfc72007-08-29 11:24:47 +0100905 for (i = ntohs(header->qdcount); i != 0; i--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100906 {
907 int found = 0, cname_count = 5;
908 struct crec *cpp = NULL;
Simon Kelley572b41e2011-02-18 18:11:18 +0000909 int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000910 unsigned long cttl = ULONG_MAX, attl;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100911
Simon Kelley824af852008-02-12 20:43:05 +0000912 namep = p;
Simon Kelley9009d742008-11-14 20:04:27 +0000913 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley824af852008-02-12 20:43:05 +0000914 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100915
916 GETSHORT(qtype, p);
917 GETSHORT(qclass, p);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000918
919 if (qclass != C_IN)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100920 continue;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000921
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100922 /* PTRs: we chase CNAMEs here, since we have no way to
923 represent them in the cache. */
924 if (qtype == T_PTR)
925 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000926 int name_encoding = in_arpa_name_2_addr(name, &addr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100927
928 if (!name_encoding)
929 continue;
930
931 if (!(flags & F_NXDOMAIN))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000932 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100933 cname_loop:
934 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +0000935 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100936
Simon Kelley5aabfc72007-08-29 11:24:47 +0100937 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100938 {
Simon Kelley824af852008-02-12 20:43:05 +0000939 unsigned char *tmp = namep;
940 /* the loop body overwrites the original name, so get it back here. */
Simon Kelley9009d742008-11-14 20:04:27 +0000941 if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
942 !(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +0000943 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100944
945 GETSHORT(aqtype, p1);
946 GETSHORT(aqclass, p1);
947 GETLONG(attl, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100948 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
949 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000950 (p1) -= 4;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100951 PUTLONG(daemon->max_ttl, p1);
952 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100953 GETSHORT(ardlen, p1);
954 endrr = p1+ardlen;
955
956 /* TTL of record is minimum of CNAMES and PTR */
957 if (attl < cttl)
958 cttl = attl;
959
960 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
961 {
Simon Kelley9009d742008-11-14 20:04:27 +0000962 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000963 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100964
965 if (aqtype == T_CNAME)
966 {
967 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +0000968 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100969 goto cname_loop;
970 }
971
972 cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
973 found = 1;
974 }
975
976 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +0000977 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +0000978 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100979 }
980 }
981
Simon Kelley28866e92011-02-14 20:19:14 +0000982 if (!found && !option_bool(OPT_NO_NEG))
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100983 {
984 if (!searched_soa)
985 {
986 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100987 ttl = find_soa(header, qlen, NULL);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100988 }
989 if (ttl)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100990 cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000991 }
992 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100993 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000994 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100995 /* everything other than PTR */
996 struct crec *newc;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100997 int addrlen;
998
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100999 if (qtype == T_A)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001000 {
1001 addrlen = INADDRSZ;
1002 flags |= F_IPV4;
1003 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001004#ifdef HAVE_IPV6
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001005 else if (qtype == T_AAAA)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001006 {
1007 addrlen = IN6ADDRSZ;
1008 flags |= F_IPV6;
1009 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001010#endif
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001011 else
1012 continue;
1013
1014 if (!(flags & F_NXDOMAIN))
1015 {
1016 cname_loop1:
1017 if (!(p1 = skip_questions(header, qlen)))
Simon Kelley824af852008-02-12 20:43:05 +00001018 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001019
Simon Kelley5aabfc72007-08-29 11:24:47 +01001020 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001021 {
Simon Kelley9009d742008-11-14 20:04:27 +00001022 if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
Simon Kelley824af852008-02-12 20:43:05 +00001023 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001024
1025 GETSHORT(aqtype, p1);
1026 GETSHORT(aqclass, p1);
1027 GETLONG(attl, p1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001028 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
1029 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001030 (p1) -= 4;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001031 PUTLONG(daemon->max_ttl, p1);
1032 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001033 GETSHORT(ardlen, p1);
1034 endrr = p1+ardlen;
1035
1036 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001037 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001038 if (aqtype == T_CNAME)
1039 {
1040 if (!cname_count--)
Simon Kelley824af852008-02-12 20:43:05 +00001041 return 0; /* looped CNAMES */
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001042 newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
Simon Kelley7b4ad2e2012-04-04 14:05:35 +01001043 if (newc)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001044 {
Simon Kelleyd56a6042013-10-11 14:39:03 +01001045 newc->addr.cname.target.cache = NULL;
Simon Kelley7b4ad2e2012-04-04 14:05:35 +01001046 if (cpp)
1047 {
Simon Kelleyd56a6042013-10-11 14:39:03 +01001048 cpp->addr.cname.target.cache = newc;
Simon Kelley7b4ad2e2012-04-04 14:05:35 +01001049 cpp->addr.cname.uid = newc->uid;
1050 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001051 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001052
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001053 cpp = newc;
1054 if (attl < cttl)
1055 cttl = attl;
1056
Simon Kelley9009d742008-11-14 20:04:27 +00001057 if (!extract_name(header, qlen, &p1, name, 1, 0))
Simon Kelley824af852008-02-12 20:43:05 +00001058 return 0;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001059 goto cname_loop1;
1060 }
1061 else
1062 {
1063 found = 1;
Simon Kelley9009d742008-11-14 20:04:27 +00001064
Simon Kelley5aabfc72007-08-29 11:24:47 +01001065 /* copy address into aligned storage */
Simon Kelley9009d742008-11-14 20:04:27 +00001066 if (!CHECK_LEN(header, p1, qlen, addrlen))
1067 return 0; /* bad packet */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001068 memcpy(&addr, p1, addrlen);
Simon Kelley824af852008-02-12 20:43:05 +00001069
1070 /* check for returned address in private space */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001071 if (check_rebind &&
Simon Kelley824af852008-02-12 20:43:05 +00001072 (flags & F_IPV4) &&
Simon Kelley28866e92011-02-14 20:19:14 +00001073 private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
Simon Kelley824af852008-02-12 20:43:05 +00001074 return 1;
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00001075
1076#ifdef HAVE_IPSET
1077 if (ipsets && (flags & (F_IPV4 | F_IPV6)))
1078 {
1079 ipsets_cur = ipsets;
1080 while (*ipsets_cur)
1081 add_to_ipset(*ipsets_cur++, &addr, flags, 0);
1082 }
1083#endif
Simon Kelley824af852008-02-12 20:43:05 +00001084
Simon Kelley5aabfc72007-08-29 11:24:47 +01001085 newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
Simon Kelley26128d22004-11-14 16:43:54 +00001086 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001087 {
Simon Kelleyd56a6042013-10-11 14:39:03 +01001088 cpp->addr.cname.target.cache = newc;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001089 cpp->addr.cname.uid = newc->uid;
1090 }
1091 cpp = NULL;
1092 }
1093 }
1094
1095 p1 = endrr;
Simon Kelley9009d742008-11-14 20:04:27 +00001096 if (!CHECK_LEN(header, p1, qlen, 0))
Simon Kelley824af852008-02-12 20:43:05 +00001097 return 0; /* bad packet */
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001098 }
1099 }
1100
Simon Kelley28866e92011-02-14 20:19:14 +00001101 if (!found && !option_bool(OPT_NO_NEG))
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001102 {
1103 if (!searched_soa)
1104 {
1105 searched_soa = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001106 ttl = find_soa(header, qlen, NULL);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001107 }
1108 /* If there's no SOA to get the TTL from, but there is a CNAME
Simon Kelley824af852008-02-12 20:43:05 +00001109 pointing at this, inherit its TTL */
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001110 if (ttl || cpp)
1111 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001112 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
Simon Kelley26128d22004-11-14 16:43:54 +00001113 if (newc && cpp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001114 {
Simon Kelleyd56a6042013-10-11 14:39:03 +01001115 cpp->addr.cname.target.cache = newc;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001116 cpp->addr.cname.uid = newc->uid;
1117 }
1118 }
1119 }
1120 }
1121 }
1122
Simon Kelley1023dcb2012-04-09 18:00:08 +01001123 /* Don't put stuff from a truncated packet into the cache.
1124 Don't cache replies where DNSSEC validation was turned off, either
1125 the upstream server told us so, or the original query specified it.
1126 Don't cache replies from non-recursive nameservers, since we may get a
1127 reply containing a CNAME but not its target, even though the target
1128 does exist. */
1129 if (!(header->hb3 & HB3_TC) &&
1130 !(header->hb4 & HB4_CD) &&
1131 (header->hb4 & HB4_RA) &&
1132 !checking_disabled)
Simon Kelley824af852008-02-12 20:43:05 +00001133 cache_end_insert();
1134
1135 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001136}
1137
1138/* If the packet holds exactly one query
Simon Kelley28866e92011-02-14 20:19:14 +00001139 return F_IPV4 or F_IPV6 and leave the name from the query in name */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001140
Simon Kelley572b41e2011-02-18 18:11:18 +00001141unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001142{
1143 unsigned char *p = (unsigned char *)(header+1);
1144 int qtype, qclass;
1145
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001146 if (typep)
1147 *typep = 0;
1148
Simon Kelley572b41e2011-02-18 18:11:18 +00001149 if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001150 return 0; /* must be exactly one query. */
1151
Simon Kelley9009d742008-11-14 20:04:27 +00001152 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001153 return 0; /* bad packet */
1154
1155 GETSHORT(qtype, p);
1156 GETSHORT(qclass, p);
1157
Simon Kelley0a852542005-03-23 20:28:59 +00001158 if (typep)
1159 *typep = qtype;
1160
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001161 if (qclass == C_IN)
1162 {
1163 if (qtype == T_A)
1164 return F_IPV4;
1165 if (qtype == T_AAAA)
1166 return F_IPV6;
1167 if (qtype == T_ANY)
1168 return F_IPV4 | F_IPV6;
1169 }
1170
1171 return F_QUERY;
1172}
1173
1174
Simon Kelley572b41e2011-02-18 18:11:18 +00001175size_t setup_reply(struct dns_header *header, size_t qlen,
Simon Kelley28866e92011-02-14 20:19:14 +00001176 struct all_addr *addrp, unsigned int flags, unsigned long ttl)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001177{
1178 unsigned char *p = skip_questions(header, qlen);
1179
Simon Kelley572b41e2011-02-18 18:11:18 +00001180 /* clear authoritative and truncated flags, set QR flag */
1181 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
1182 /* set RA flag */
1183 header->hb4 |= HB4_RA;
1184
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001185 header->nscount = htons(0);
1186 header->arcount = htons(0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001187 header->ancount = htons(0); /* no answers unless changed below */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001188 if (flags == F_NEG)
Simon Kelley572b41e2011-02-18 18:11:18 +00001189 SET_RCODE(header, SERVFAIL); /* couldn't get memory */
Simon Kelley824af852008-02-12 20:43:05 +00001190 else if (flags == F_NOERR)
Simon Kelley572b41e2011-02-18 18:11:18 +00001191 SET_RCODE(header, NOERROR); /* empty domain */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001192 else if (flags == F_NXDOMAIN)
Simon Kelley572b41e2011-02-18 18:11:18 +00001193 SET_RCODE(header, NXDOMAIN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001194 else if (p && flags == F_IPV4)
1195 { /* we know the address */
Simon Kelley572b41e2011-02-18 18:11:18 +00001196 SET_RCODE(header, NOERROR);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001197 header->ancount = htons(1);
Simon Kelley572b41e2011-02-18 18:11:18 +00001198 header->hb3 |= HB3_AA;
1199 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 +00001200 }
1201#ifdef HAVE_IPV6
1202 else if (p && flags == F_IPV6)
1203 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001204 SET_RCODE(header, NOERROR);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001205 header->ancount = htons(1);
Simon Kelley572b41e2011-02-18 18:11:18 +00001206 header->hb3 |= HB3_AA;
1207 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 +00001208 }
1209#endif
1210 else /* nowhere to forward to */
Simon Kelley572b41e2011-02-18 18:11:18 +00001211 SET_RCODE(header, REFUSED);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001212
1213 return p - (unsigned char *)header;
1214}
Simon Kelley36717ee2004-09-20 19:20:58 +01001215
1216/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001217int check_for_local_domain(char *name, time_t now)
Simon Kelley36717ee2004-09-20 19:20:58 +01001218{
1219 struct crec *crecp;
Simon Kelley0a852542005-03-23 20:28:59 +00001220 struct mx_srv_record *mx;
1221 struct txt_record *txt;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001222 struct interface_name *intr;
1223 struct ptr_record *ptr;
Simon Kelley7de060b2011-08-26 17:24:52 +01001224 struct naptr *naptr;
1225
1226 if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +01001227 (crecp->flags & (F_HOSTS | F_DHCP)))
1228 return 1;
1229
Simon Kelley7de060b2011-08-26 17:24:52 +01001230 for (naptr = daemon->naptr; naptr; naptr = naptr->next)
1231 if (hostname_isequal(name, naptr->name))
1232 return 1;
1233
1234 for (mx = daemon->mxnames; mx; mx = mx->next)
Simon Kelley0a852542005-03-23 20:28:59 +00001235 if (hostname_isequal(name, mx->name))
Simon Kelley36717ee2004-09-20 19:20:58 +01001236 return 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001237
Simon Kelley0a852542005-03-23 20:28:59 +00001238 for (txt = daemon->txt; txt; txt = txt->next)
1239 if (hostname_isequal(name, txt->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001240 return 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001241
1242 for (intr = daemon->int_names; intr; intr = intr->next)
1243 if (hostname_isequal(name, intr->name))
1244 return 1;
1245
1246 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1247 if (hostname_isequal(name, ptr->name))
1248 return 1;
1249
Simon Kelley36717ee2004-09-20 19:20:58 +01001250 return 0;
1251}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001252
1253/* Is the packet a reply with the answer address equal to addr?
1254 If so mung is into an NXDOMAIN reply and also put that information
1255 in the cache. */
Simon Kelley572b41e2011-02-18 18:11:18 +00001256int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001257 struct bogus_addr *baddr, time_t now)
1258{
1259 unsigned char *p;
1260 int i, qtype, qclass, rdlen;
1261 unsigned long ttl;
1262 struct bogus_addr *baddrp;
1263
1264 /* skip over questions */
1265 if (!(p = skip_questions(header, qlen)))
1266 return 0; /* bad packet */
1267
Simon Kelley5aabfc72007-08-29 11:24:47 +01001268 for (i = ntohs(header->ancount); i != 0; i--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001269 {
Simon Kelley9009d742008-11-14 20:04:27 +00001270 if (!extract_name(header, qlen, &p, name, 1, 10))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001271 return 0; /* bad packet */
1272
1273 GETSHORT(qtype, p);
1274 GETSHORT(qclass, p);
1275 GETLONG(ttl, p);
1276 GETSHORT(rdlen, p);
1277
1278 if (qclass == C_IN && qtype == T_A)
Simon Kelley9009d742008-11-14 20:04:27 +00001279 {
1280 if (!CHECK_LEN(header, p, qlen, INADDRSZ))
1281 return 0;
1282
1283 for (baddrp = baddr; baddrp; baddrp = baddrp->next)
1284 if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
1285 {
1286 /* Found a bogus address. Insert that info here, since there no SOA record
1287 to get the ttl from in the normal processing */
1288 cache_start_insert();
1289 cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
1290 cache_end_insert();
1291
1292 return 1;
1293 }
1294 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001295
Simon Kelley9009d742008-11-14 20:04:27 +00001296 if (!ADD_RDLEN(header, p, qlen, rdlen))
1297 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001298 }
1299
1300 return 0;
1301}
1302
Simon Kelleyb75e9362012-12-07 11:50:41 +00001303int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
Simon Kelleye1ff4192012-12-09 17:08:47 +00001304 unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001305{
1306 va_list ap;
1307 unsigned char *sav, *p = *pp;
1308 int j;
1309 unsigned short usval;
1310 long lval;
1311 char *sval;
1312
1313 if (truncp && *truncp)
1314 return 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001315
1316 va_start(ap, format); /* make ap point to 1st unamed argument */
1317
Simon Kelleyb75e9362012-12-07 11:50:41 +00001318 if (nameoffset > 0)
Simon Kelley4f7b3042012-11-28 21:27:02 +00001319 {
1320 PUTSHORT(nameoffset | 0xc000, p);
1321 }
1322 else
1323 {
Simon Kelleye1ff4192012-12-09 17:08:47 +00001324 char *name = va_arg(ap, char *);
1325 if (name)
1326 p = do_rfc1035_name(p, name);
Simon Kelleyb75e9362012-12-07 11:50:41 +00001327 if (nameoffset < 0)
1328 {
1329 PUTSHORT(-nameoffset | 0xc000, p);
1330 }
1331 else
1332 *p++ = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001333 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001334
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001335 PUTSHORT(type, p);
1336 PUTSHORT(class, p);
1337 PUTLONG(ttl, p); /* TTL */
1338
1339 sav = p; /* Save pointer to RDLength field */
1340 PUTSHORT(0, p); /* Placeholder RDLength */
1341
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001342 for (; *format; format++)
1343 switch (*format)
1344 {
1345#ifdef HAVE_IPV6
1346 case '6':
1347 sval = va_arg(ap, char *);
1348 memcpy(p, sval, IN6ADDRSZ);
1349 p += IN6ADDRSZ;
1350 break;
1351#endif
1352
1353 case '4':
1354 sval = va_arg(ap, char *);
1355 memcpy(p, sval, INADDRSZ);
1356 p += INADDRSZ;
1357 break;
1358
1359 case 's':
1360 usval = va_arg(ap, int);
1361 PUTSHORT(usval, p);
1362 break;
1363
1364 case 'l':
1365 lval = va_arg(ap, long);
1366 PUTLONG(lval, p);
1367 break;
1368
1369 case 'd':
1370 /* get domain-name answer arg and store it in RDATA field */
Simon Kelley0a852542005-03-23 20:28:59 +00001371 if (offset)
1372 *offset = p - (unsigned char *)header;
Simon Kelley3d8df262005-08-29 12:19:27 +01001373 p = do_rfc1035_name(p, va_arg(ap, char *));
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001374 *p++ = 0;
1375 break;
Simon Kelley3d8df262005-08-29 12:19:27 +01001376
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001377 case 't':
Simon Kelley0a852542005-03-23 20:28:59 +00001378 usval = va_arg(ap, int);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001379 sval = va_arg(ap, char *);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01001380 if (usval != 0)
1381 memcpy(p, sval, usval);
Simon Kelley0a852542005-03-23 20:28:59 +00001382 p += usval;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001383 break;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001384
1385 case 'z':
1386 sval = va_arg(ap, char *);
1387 usval = sval ? strlen(sval) : 0;
1388 if (usval > 255)
1389 usval = 255;
1390 *p++ = (unsigned char)usval;
1391 memcpy(p, sval, usval);
1392 p += usval;
1393 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001394 }
1395
1396 va_end(ap); /* clean up variable argument pointer */
1397
1398 j = p - sav - 2;
1399 PUTSHORT(j, sav); /* Now, store real RDLength */
1400
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001401 /* check for overflow of buffer */
1402 if (limit && ((unsigned char *)limit - p) < 0)
1403 {
1404 if (truncp)
1405 *truncp = 1;
1406 return 0;
1407 }
1408
1409 *pp = p;
1410 return 1;
1411}
1412
Simon Kelley9009d742008-11-14 20:04:27 +00001413static unsigned long crec_ttl(struct crec *crecp, time_t now)
1414{
1415 /* Return 0 ttl for DHCP entries, which might change
1416 before the lease expires. */
1417
1418 if (crecp->flags & (F_IMMORTAL | F_DHCP))
1419 return daemon->local_ttl;
1420
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001421 /* Return the Max TTL value if it is lower then the actual TTL */
1422 if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
1423 return crecp->ttd - now;
1424 else
1425 return daemon->max_ttl;
Simon Kelley9009d742008-11-14 20:04:27 +00001426}
1427
1428
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001429/* return zero if we can't answer from cache, or packet size if we can */
Simon Kelley572b41e2011-02-18 18:11:18 +00001430size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
Simon Kelleycdeda282006-03-16 20:16:06 +00001431 struct in_addr local_addr, struct in_addr local_netmask, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001432{
Simon Kelley3be34542004-09-11 19:12:13 +01001433 char *name = daemon->namebuff;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001434 unsigned char *p, *ansp, *pheader;
Simon Kelley832af0b2007-01-21 20:01:28 +00001435 int qtype, qclass;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001436 struct all_addr addr;
Simon Kelleyb75e9362012-12-07 11:50:41 +00001437 int nameoffset;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001438 unsigned short flag;
Simon Kelley0a852542005-03-23 20:28:59 +00001439 int q, ans, anscount = 0, addncount = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001440 int dryrun = 0, sec_reqd = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001441 int is_sign;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001442 struct crec *crecp;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001443 int nxdomain = 0, auth = 1, trunc = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001444 struct mx_srv_record *rec;
1445
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001446 /* If there is an RFC2671 pseudoheader then it will be overwritten by
1447 partial replies, so we have to do a dry run to see if we can answer
1448 the query. We check to see if the do bit is set, if so we always
1449 forward rather than answering from the cache, which doesn't include
1450 security information. */
1451
Simon Kelley832af0b2007-01-21 20:01:28 +00001452 if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001453 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001454 unsigned short udpsz, flags;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001455 unsigned char *psave = pheader;
1456
1457 GETSHORT(udpsz, pheader);
Simon Kelley7de060b2011-08-26 17:24:52 +01001458 pheader += 2; /* ext_rcode */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001459 GETSHORT(flags, pheader);
1460
1461 sec_reqd = flags & 0x8000; /* do bit */
1462
1463 /* If our client is advertising a larger UDP packet size
1464 than we allow, trim it so that we don't get an overlarge
1465 response from upstream */
1466
Simon Kelley832af0b2007-01-21 20:01:28 +00001467 if (!is_sign && (udpsz > daemon->edns_pktsz))
Simon Kelley3be34542004-09-11 19:12:13 +01001468 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001469
1470 dryrun = 1;
1471 }
1472
Simon Kelley572b41e2011-02-18 18:11:18 +00001473 if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
Simon Kelley832af0b2007-01-21 20:01:28 +00001474 return 0;
1475
Simon Kelley0a852542005-03-23 20:28:59 +00001476 for (rec = daemon->mxnames; rec; rec = rec->next)
1477 rec->offset = 0;
1478
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001479 rerun:
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001480 /* determine end of question section (we put answers there) */
1481 if (!(ansp = skip_questions(header, qlen)))
1482 return 0; /* bad packet */
1483
1484 /* now process each question, answers go in RRs after the question */
1485 p = (unsigned char *)(header+1);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001486
Simon Kelley5aabfc72007-08-29 11:24:47 +01001487 for (q = ntohs(header->qdcount); q != 0; q--)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001488 {
1489 /* save pointer to name for copying into answers */
1490 nameoffset = p - (unsigned char *)header;
1491
1492 /* now extract name as .-concatenated string into name */
Simon Kelley9009d742008-11-14 20:04:27 +00001493 if (!extract_name(header, qlen, &p, name, 1, 4))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001494 return 0; /* bad packet */
Simon Kelley832af0b2007-01-21 20:01:28 +00001495
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001496 GETSHORT(qtype, p);
1497 GETSHORT(qclass, p);
1498
1499 ans = 0; /* have we answered this question */
1500
Simon Kelley0a852542005-03-23 20:28:59 +00001501 if (qtype == T_TXT || qtype == T_ANY)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001502 {
Simon Kelley0a852542005-03-23 20:28:59 +00001503 struct txt_record *t;
1504 for(t = daemon->txt; t ; t = t->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001505 {
Simon Kelley0a852542005-03-23 20:28:59 +00001506 if (t->class == qclass && hostname_isequal(name, t->name))
1507 {
1508 ans = 1;
Simon Kelleye17fb622006-01-14 20:33:46 +00001509 if (!dryrun)
1510 {
Simon Kelley28866e92011-02-14 20:19:14 +00001511 log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
Simon Kelleye17fb622006-01-14 20:33:46 +00001512 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1513 daemon->local_ttl, NULL,
1514 T_TXT, t->class, "t", t->len, t->txt))
1515 anscount++;
1516
1517 }
Simon Kelley0a852542005-03-23 20:28:59 +00001518 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001519 }
Simon Kelley0a852542005-03-23 20:28:59 +00001520 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001521
Simon Kelley0a852542005-03-23 20:28:59 +00001522 if (qclass == C_IN)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001523 {
Simon Kelley9f7f3b12012-05-28 21:39:57 +01001524 struct txt_record *t;
1525
1526 for (t = daemon->rr; t; t = t->next)
1527 if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
1528 {
1529 ans = 1;
1530 if (!dryrun)
1531 {
1532 log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
1533 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1534 daemon->local_ttl, NULL,
1535 t->class, C_IN, "t", t->len, t->txt))
1536 anscount ++;
1537 }
1538 }
1539
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001540 if (qtype == T_PTR || qtype == T_ANY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +01001541 {
Simon Kelley832af0b2007-01-21 20:01:28 +00001542 /* see if it's w.z.y.z.in-addr.arpa format */
1543 int is_arpa = in_arpa_name_2_addr(name, &addr);
1544 struct ptr_record *ptr;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001545 struct interface_name* intr = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00001546
1547 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
1548 if (hostname_isequal(name, ptr->name))
1549 break;
1550
Simon Kelleyf2621c72007-04-29 19:47:21 +01001551 if (is_arpa == F_IPV4)
1552 for (intr = daemon->int_names; intr; intr = intr->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001553 {
Simon Kelley115ac3e2013-05-20 11:28:32 +01001554 struct addrlist *addrlist;
1555
1556 for (addrlist = intr->addr4; addrlist; addrlist = addrlist->next)
1557 if (addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
1558 break;
1559
1560 if (addrlist)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001561 break;
1562 else
1563 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
1564 intr = intr->next;
1565 }
Simon Kelley115ac3e2013-05-20 11:28:32 +01001566#ifdef HAVE_IPV6
1567 else if (is_arpa == F_IPV6)
1568 for (intr = daemon->int_names; intr; intr = intr->next)
1569 {
1570 struct addrlist *addrlist;
1571
1572 for (addrlist = intr->addr6; addrlist; addrlist = addrlist->next)
1573 if (IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
1574 break;
1575
1576 if (addrlist)
1577 break;
1578 else
1579 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
1580 intr = intr->next;
1581 }
1582#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001583
1584 if (intr)
1585 {
1586 ans = 1;
1587 if (!dryrun)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001588 {
Simon Kelley115ac3e2013-05-20 11:28:32 +01001589 log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001590 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1591 daemon->local_ttl, NULL,
1592 T_PTR, C_IN, "d", intr->name))
1593 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001594 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001595 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001596 else if (ptr)
1597 {
1598 ans = 1;
1599 if (!dryrun)
1600 {
Simon Kelley28866e92011-02-14 20:19:14 +00001601 log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
Simon Kelley832af0b2007-01-21 20:01:28 +00001602 for (ptr = daemon->ptr; ptr; ptr = ptr->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001603 if (hostname_isequal(name, ptr->name) &&
1604 add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1605 daemon->local_ttl, NULL,
1606 T_PTR, C_IN, "d", ptr->ptr))
1607 anscount++;
1608
Simon Kelley832af0b2007-01-21 20:01:28 +00001609 }
1610 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001611 else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
1612 do
1613 {
1614 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
1615 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
1616 continue;
1617
1618 if (crecp->flags & F_NEG)
1619 {
1620 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001621 auth = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001622 if (crecp->flags & F_NXDOMAIN)
1623 nxdomain = 1;
1624 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001625 log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001626 }
1627 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
1628 {
1629 ans = 1;
1630 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1631 auth = 0;
1632 if (!dryrun)
1633 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001634 log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001635 record_source(crecp->uid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001636
Simon Kelley9009d742008-11-14 20:04:27 +00001637 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1638 crec_ttl(crecp, now), NULL,
Simon Kelleyf2621c72007-04-29 19:47:21 +01001639 T_PTR, C_IN, "d", cache_get_name(crecp)))
1640 anscount++;
1641 }
1642 }
1643 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
Simon Kelley2bb73af2013-04-24 17:38:19 +01001644 else if (is_rev_synth(is_arpa, &addr, name))
1645 {
1646 ans = 1;
1647 if (!dryrun)
1648 {
1649 log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
1650
1651 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1652 daemon->local_ttl, NULL,
1653 T_PTR, C_IN, "d", name))
1654 anscount++;
1655 }
1656 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001657 else if (is_arpa == F_IPV4 &&
Simon Kelley28866e92011-02-14 20:19:14 +00001658 option_bool(OPT_BOGUSPRIV) &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001659 private_net(addr.addr.addr4, 1))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001660 {
1661 /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
1662 ans = 1;
1663 nxdomain = 1;
1664 if (!dryrun)
1665 log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
Simon Kelley1a6bca82008-07-11 11:11:42 +01001666 name, &addr, NULL);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001667 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001668 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001669
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001670 for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
1671 {
1672 unsigned short type = T_A;
Simon Kelley115ac3e2013-05-20 11:28:32 +01001673 struct interface_name *intr;
1674
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001675 if (flag == F_IPV6)
1676#ifdef HAVE_IPV6
Simon Kelley3d8df262005-08-29 12:19:27 +01001677 type = T_AAAA;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001678#else
Simon Kelley3d8df262005-08-29 12:19:27 +01001679 break;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001680#endif
1681
1682 if (qtype != type && qtype != T_ANY)
1683 continue;
1684
Simon Kelley316e2732010-01-22 20:16:09 +00001685 /* Check for "A for A" queries; be rather conservative
1686 about what looks like dotted-quad. */
1687 if (qtype == T_A)
Simon Kelley3d8df262005-08-29 12:19:27 +01001688 {
Simon Kelley316e2732010-01-22 20:16:09 +00001689 char *cp;
1690 unsigned int i, a;
1691 int x;
1692
1693 for (cp = name, i = 0, a = 0; *cp; i++)
Simon Kelley3d8df262005-08-29 12:19:27 +01001694 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001695 if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)
Simon Kelley316e2732010-01-22 20:16:09 +00001696 {
1697 i = 5;
1698 break;
1699 }
1700
1701 a = (a << 8) + x;
1702
1703 if (*cp == '.')
1704 cp++;
Simon Kelley3d8df262005-08-29 12:19:27 +01001705 }
Simon Kelley316e2732010-01-22 20:16:09 +00001706
1707 if (i == 4)
1708 {
1709 ans = 1;
1710 if (!dryrun)
1711 {
1712 addr.addr.addr4.s_addr = htonl(a);
1713 log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
1714 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1715 daemon->local_ttl, NULL, type, C_IN, "4", &addr))
1716 anscount++;
1717 }
1718 continue;
1719 }
Simon Kelley3d8df262005-08-29 12:19:27 +01001720 }
1721
Simon Kelleyf2621c72007-04-29 19:47:21 +01001722 /* interface name stuff */
Simon Kelleyd56a6042013-10-11 14:39:03 +01001723 intname_restart:
Simon Kelley115ac3e2013-05-20 11:28:32 +01001724 for (intr = daemon->int_names; intr; intr = intr->next)
1725 if (hostname_isequal(name, intr->name))
1726 break;
1727
1728 if (intr)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001729 {
Simon Kelley115ac3e2013-05-20 11:28:32 +01001730 struct addrlist *addrlist;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001731
Simon Kelley115ac3e2013-05-20 11:28:32 +01001732 enumerate_interfaces(0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001733
Simon Kelley115ac3e2013-05-20 11:28:32 +01001734 addrlist = intr->addr4;
1735#ifdef HAVE_IPV6
1736 if (type == T_AAAA)
1737 addrlist = intr->addr6;
1738#endif
1739 ans = 1;
1740 if (!dryrun)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001741 {
Simon Kelley115ac3e2013-05-20 11:28:32 +01001742 if (!addrlist)
1743 log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL);
1744 else
1745 for (; addrlist; addrlist = addrlist->next)
1746 {
1747 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
1748 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1749 daemon->local_ttl, NULL, type, C_IN,
1750 type == T_A ? "4" : "6", &addrlist->addr))
1751 anscount++;
1752 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001753 }
Simon Kelley115ac3e2013-05-20 11:28:32 +01001754 continue;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001755 }
1756
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001757 cname_restart:
1758 if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
1759 {
1760 int localise = 0;
1761
1762 /* See if a putative address is on the network from which we recieved
1763 the query, is so we'll filter other answers. */
Simon Kelley28866e92011-02-14 20:19:14 +00001764 if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001765 {
1766 struct crec *save = crecp;
1767 do {
1768 if ((crecp->flags & F_HOSTS) &&
1769 is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1770 {
1771 localise = 1;
1772 break;
1773 }
1774 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
1775 crecp = save;
1776 }
1777
1778 do
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001779 {
Simon Kelley26128d22004-11-14 16:43:54 +00001780 /* don't answer wildcard queries with data not from /etc/hosts
1781 or DHCP leases */
Simon Kelley3bb51da2013-10-14 14:20:34 +01001782 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
Simon Kelley26128d22004-11-14 16:43:54 +00001783 break;
1784
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001785 if (crecp->flags & F_CNAME)
1786 {
Simon Kelleyd56a6042013-10-11 14:39:03 +01001787 char *cname_target = cache_get_cname_target(crecp);
1788
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001789 if (!dryrun)
1790 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001791 log_query(crecp->flags, name, NULL, record_source(crecp->uid));
Simon Kelley9009d742008-11-14 20:04:27 +00001792 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1793 crec_ttl(crecp, now), &nameoffset,
Simon Kelleyd56a6042013-10-11 14:39:03 +01001794 T_CNAME, C_IN, "d", cname_target))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001795 anscount++;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001796 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001797
Simon Kelleyd56a6042013-10-11 14:39:03 +01001798 strcpy(name, cname_target);
1799 /* check if target interface_name */
1800 if (crecp->addr.cname.uid == -1)
1801 goto intname_restart;
1802 else
1803 goto cname_restart;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001804 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001805
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001806 if (crecp->flags & F_NEG)
1807 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001808 ans = 1;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001809 auth = 0;
1810 if (crecp->flags & F_NXDOMAIN)
1811 nxdomain = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001812 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001813 log_query(crecp->flags, name, NULL, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001814 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001815 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001816 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001817 /* If we are returning local answers depending on network,
1818 filter here. */
1819 if (localise &&
1820 (crecp->flags & F_HOSTS) &&
1821 !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
1822 continue;
1823
1824 if (!(crecp->flags & (F_HOSTS | F_DHCP)))
1825 auth = 0;
1826
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001827 ans = 1;
1828 if (!dryrun)
1829 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001830 log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
Simon Kelley7622fc02009-06-04 20:32:05 +01001831 record_source(crecp->uid));
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001832
Simon Kelley9009d742008-11-14 20:04:27 +00001833 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1834 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001835 type == T_A ? "4" : "6", &crecp->addr))
1836 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001837 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001838 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001839 } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001840 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01001841 else if (is_name_synthetic(flag, name, &addr))
1842 {
1843 ans = 1;
1844 if (!dryrun)
1845 {
1846 log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
1847 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1848 daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
1849 anscount++;
1850 }
1851 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001852 }
Simon Kelleyd1c759c2012-04-16 17:26:19 +01001853
1854 if (qtype == T_CNAME || qtype == T_ANY)
1855 {
1856 if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
1857 (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP))))
1858 {
1859 ans = 1;
1860 if (!dryrun)
1861 {
1862 log_query(crecp->flags, name, NULL, record_source(crecp->uid));
1863 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
1864 crec_ttl(crecp, now), &nameoffset,
Simon Kelleyd56a6042013-10-11 14:39:03 +01001865 T_CNAME, C_IN, "d", cache_get_cname_target(crecp)))
Simon Kelleyd1c759c2012-04-16 17:26:19 +01001866 anscount++;
1867 }
1868 }
1869 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001870
1871 if (qtype == T_MX || qtype == T_ANY)
1872 {
1873 int found = 0;
Simon Kelley0a852542005-03-23 20:28:59 +00001874 for (rec = daemon->mxnames; rec; rec = rec->next)
1875 if (!rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001876 {
1877 ans = found = 1;
1878 if (!dryrun)
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001879 {
Simon Kelleye1ff4192012-12-09 17:08:47 +00001880 int offset;
Simon Kelley28866e92011-02-14 20:19:14 +00001881 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
Simon Kelley0a852542005-03-23 20:28:59 +00001882 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1883 &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
1884 {
1885 anscount++;
1886 if (rec->target)
1887 rec->offset = offset;
1888 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001889 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001890 }
1891
Simon Kelley28866e92011-02-14 20:19:14 +00001892 if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001893 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
1894 {
1895 ans = 1;
1896 if (!dryrun)
1897 {
Simon Kelley28866e92011-02-14 20:19:14 +00001898 log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001899 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
1900 T_MX, C_IN, "sd", 1,
Simon Kelley28866e92011-02-14 20:19:14 +00001901 option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001902 anscount++;
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001903 }
1904 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001905 }
1906
1907 if (qtype == T_SRV || qtype == T_ANY)
1908 {
1909 int found = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001910 struct mx_srv_record *move = NULL, **up = &daemon->mxnames;
1911
Simon Kelley0a852542005-03-23 20:28:59 +00001912 for (rec = daemon->mxnames; rec; rec = rec->next)
1913 if (rec->issrv && hostname_isequal(name, rec->name))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001914 {
1915 found = ans = 1;
1916 if (!dryrun)
1917 {
Simon Kelleye1ff4192012-12-09 17:08:47 +00001918 int offset;
Simon Kelley28866e92011-02-14 20:19:14 +00001919 log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001920 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
Simon Kelley0a852542005-03-23 20:28:59 +00001921 &offset, T_SRV, C_IN, "sssd",
1922 rec->priority, rec->weight, rec->srvport, rec->target))
1923 {
1924 anscount++;
1925 if (rec->target)
1926 rec->offset = offset;
1927 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001928 }
Simon Kelley28866e92011-02-14 20:19:14 +00001929
1930 /* unlink first SRV record found */
1931 if (!move)
1932 {
1933 move = rec;
1934 *up = rec->next;
1935 }
1936 else
1937 up = &rec->next;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001938 }
Simon Kelley28866e92011-02-14 20:19:14 +00001939 else
1940 up = &rec->next;
1941
1942 /* put first SRV record back at the end. */
1943 if (move)
1944 {
1945 *up = move;
1946 move->next = NULL;
1947 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001948
Simon Kelley28866e92011-02-14 20:19:14 +00001949 if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001950 {
1951 ans = 1;
1952 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001953 log_query(F_CONFIG | F_NEG, name, NULL, NULL);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001954 }
1955 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001956
1957 if (qtype == T_NAPTR || qtype == T_ANY)
1958 {
1959 struct naptr *na;
1960 for (na = daemon->naptr; na; na = na->next)
1961 if (hostname_isequal(name, na->name))
1962 {
1963 ans = 1;
1964 if (!dryrun)
1965 {
Simon Kelley28866e92011-02-14 20:19:14 +00001966 log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
Simon Kelley1a6bca82008-07-11 11:11:42 +01001967 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
1968 NULL, T_NAPTR, C_IN, "sszzzd",
1969 na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
1970 anscount++;
1971 }
1972 }
1973 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001974
1975 if (qtype == T_MAILB)
1976 ans = 1, nxdomain = 1;
1977
Simon Kelley28866e92011-02-14 20:19:14 +00001978 if (qtype == T_SOA && option_bool(OPT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001979 {
1980 ans = 1;
1981 if (!dryrun)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001982 log_query(F_CONFIG | F_NEG, name, &addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001983 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001984 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001985
1986 if (!ans)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001987 return 0; /* failed to answer a question */
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001988 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001989
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001990 if (dryrun)
1991 {
1992 dryrun = 0;
1993 goto rerun;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001994 }
1995
Simon Kelley0a852542005-03-23 20:28:59 +00001996 /* create an additional data section, for stuff in SRV and MX record replies. */
1997 for (rec = daemon->mxnames; rec; rec = rec->next)
1998 if (rec->offset != 0)
1999 {
2000 /* squash dupes */
2001 struct mx_srv_record *tmp;
2002 for (tmp = rec->next; tmp; tmp = tmp->next)
2003 if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
2004 tmp->offset = 0;
2005
2006 crecp = NULL;
2007 while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
2008 {
Simon Kelley0a852542005-03-23 20:28:59 +00002009#ifdef HAVE_IPV6
2010 int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
2011#else
2012 int type = T_A;
2013#endif
2014 if (crecp->flags & F_NEG)
2015 continue;
2016
Simon Kelley9009d742008-11-14 20:04:27 +00002017 if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
2018 crec_ttl(crecp, now), NULL, type, C_IN,
Simon Kelley0a852542005-03-23 20:28:59 +00002019 crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
2020 addncount++;
2021 }
2022 }
2023
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002024 /* done all questions, set up header and return length of result */
Simon Kelley572b41e2011-02-18 18:11:18 +00002025 /* clear authoritative and truncated flags, set QR flag */
2026 header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
2027 /* set RA flag */
2028 header->hb4 |= HB4_RA;
2029
2030 /* authoritive - only hosts and DHCP derived names. */
2031 if (auth)
2032 header->hb3 |= HB3_AA;
2033
2034 /* truncation */
2035 if (trunc)
2036 header->hb3 |= HB3_TC;
2037
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002038 if (anscount == 0 && nxdomain)
Simon Kelley572b41e2011-02-18 18:11:18 +00002039 SET_RCODE(header, NXDOMAIN);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002040 else
Simon Kelley572b41e2011-02-18 18:11:18 +00002041 SET_RCODE(header, NOERROR); /* no error */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002042 header->ancount = htons(anscount);
2043 header->nscount = htons(0);
Simon Kelley0a852542005-03-23 20:28:59 +00002044 header->arcount = htons(addncount);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002045 return ansp - (unsigned char *)header;
2046}
2047