1 /* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
2
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
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
8 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.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16 /**
17 * This file is modified from dnsmasq/src/rfc1035.c.
18 */
19
20 #include "rfc1035.h"
21
22 #define CHECK_LEN(header, pp, plen, len) \
23 ((size_t)((pp) - (unsigned char *) (header) + (len)) <= (plen))
24
25 #define ADD_RDLEN(header, pp, plen, len) \
26 (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
27
28 int
extract_name(struct dns_header * header,size_t plen,unsigned char ** pp,char * name,int isExtract,int extrabytes)29 extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
30 char *name, int isExtract, int extrabytes)
31 {
32 unsigned char *cp = (unsigned char *) name, *p = *pp, *p1 = NULL;
33 unsigned int j, l, hops = 0;
34 int retvalue = 1;
35
36 if (isExtract) {
37 *cp = 0;
38 }
39 while (1) {
40 unsigned int label_type;
41
42 if (!CHECK_LEN(header, p, plen, 1)) {
43 return 0;
44 }
45 if ((l = *p++) == 0) {
46 /* end marker */
47 /* check that there are the correct no of bytes after the name */
48 if (!CHECK_LEN(header, p, plen, extrabytes)) {
49 return 0;
50 }
51 if (isExtract) {
52 if (cp != (unsigned char *) name) {
53 cp--;
54 }
55 *cp = 0; /* terminate: lose final period */
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
69 if (label_type == 0xc0) { /* pointer */
70 if (!CHECK_LEN(header, p, plen, 1)) {
71 return 0;
72 }
73
74 /* get offset */
75 l = (l & 0x3f) << 8;
76 l |= *p++;
77
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 } else if (label_type == 0x80) {
87 return 0; /* reserved */
88 } else if (label_type == 0x40) { /* ELT */
89 unsigned int count, digs;
90
91 if ((l & 0x3f) != 1) {
92 return 0; /* we only understand bitstrings */
93 }
94 if (!isExtract) {
95 return 0; /* Cannot compare bitsrings */
96 }
97 count = *p++;
98 if (count == 0) {
99 count = 256;
100 }
101 digs = ((count - 1) >> 2) + 1;
102
103 /* output is \[x<hex>/siz]. which is digs+9 chars */
104 if (cp - (unsigned char *) name + digs + 9 >= MAXDNAME) {
105 return 0;
106 }
107
108 if (!CHECK_LEN(header, p, plen, (count - 1) >> 3)) {
109 return 0;
110 }
111
112 *cp++ = '\\';
113 *cp++ = '[';
114 *cp++ = 'x';
115 for (j = 0; j < digs; j++) {
116 unsigned int dig;
117 if (j % 2 == 0) {
118 dig = *p >> 4;
119 } else {
120 dig = *p++ & 0x0f;
121 }
122 *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
123 }
124 cp += sprintf((char *) cp, "/%d]", count);
125
126 /* do this here to overwrite the zero char from sprintf */
127 *cp++ = '.';
128 } else { /* label_type = 0 -> label. */
129 if (cp - (unsigned char *) name + l + 1 >= MAXDNAME) {
130 return 0;
131 }
132 if (!CHECK_LEN(header, p, plen, l)) {
133 return 0;
134 }
135 for (j = 0; j < l; j++, p++) {
136 if (isExtract) {
137 unsigned char c = *p;
138 if (isascii(c) && !iscntrl(c) && c != '.') {
139 *cp++ = *p;
140 } else {
141 return 0;
142 }
143 } else {
144 unsigned char c1 = *cp, c2 = *p;
145
146 if (c1 == 0) {
147 retvalue = 2;
148 } else {
149 cp++;
150 if (c1 >= 'A' && c1 <= 'Z') {
151 c1 += 'a' - 'A';
152 }
153 if (c2 >= 'A' && c2 <= 'Z') {
154 c2 += 'a' - 'A';
155 }
156 if (c1 != c2) {
157 retvalue = 2;
158 }
159 }
160 }
161 }
162 if (isExtract) {
163 *cp++ = '.';
164 } else if (*cp != 0 && *cp++ != '.') {
165 retvalue = 2;
166 }
167 }
168 }
169 }
170
171 /* Hash the question section. This is used to safely detect query
172 retransmision and to detect answers to questions we didn't ask, which
173 might be poisoning attacks. Note that we decode the name rather
174 than hash the raw bytes, since replies might be compressed differently.
175 We ignore case in the names for the same reason. Return all-ones
176 if there is not question section. */
177 int
questions_hash(uint64_t * hash,struct dns_header * header,size_t plen,char * name,const unsigned char key[crypto_shorthash_KEYBYTES])178 questions_hash(uint64_t *hash, struct dns_header *header, size_t plen,
179 char *name, const unsigned char key[crypto_shorthash_KEYBYTES])
180 {
181 unsigned char *p = (unsigned char *) (header + 1);
182 size_t name_len;
183
184 if (ntohs(header->qdcount) != 1 ||
185 !extract_name(header, plen, &p, name, 1, 4) ||
186 (name_len = strlen(name)) > MAXDNAME) {
187 return -1;
188 }
189 crypto_shorthash((unsigned char *) hash, (const unsigned char *) name, name_len, key);
190
191 return 0;
192 }
193
194 static unsigned char *
skip_name(unsigned char * ansp,struct dns_header * header,size_t plen,int extrabytes)195 skip_name(unsigned char *ansp, struct dns_header *header, size_t plen,
196 int extrabytes)
197 {
198 while (1) {
199 unsigned int label_type;
200
201 if (!CHECK_LEN(header, ansp, plen, 1)) {
202 return NULL;
203 }
204 label_type = (*ansp) & 0xc0;
205
206 if (label_type == 0xc0) {
207 /* pointer for compression. */
208 ansp += 2;
209 break;
210 } else if (label_type == 0x80) {
211 return NULL; /* reserved */
212 } else if (label_type == 0x40) {
213 /* Extended label type */
214 unsigned int count;
215
216 if (!CHECK_LEN(header, ansp, plen, 2)) {
217 return NULL;
218 }
219 if (((*ansp++) & 0x3f) != 1) {
220 return NULL; /* we only understand bitstrings */
221 }
222 count = *(ansp++); /* Bits in bitstring */
223
224 if (count == 0) { /* count == 0 means 256 bits */
225 ansp += 32;
226 } else {
227 ansp += ((count - 1) >> 3) + 1;
228 }
229 } else { /* label type == 0 Bottom six bits is length */
230 unsigned int len = (*ansp++) & 0x3f;
231
232 if (!ADD_RDLEN(header, ansp, plen, len)) {
233 return NULL;
234 }
235 if (len == 0) {
236 break; /* zero length label marks the end. */
237 }
238 }
239 }
240
241 if (!CHECK_LEN(header, ansp, plen, extrabytes)) {
242 return NULL;
243 }
244 return ansp;
245 }
246
247 unsigned char *
skip_questions(struct dns_header * header,size_t plen)248 skip_questions(struct dns_header *header, size_t plen)
249 {
250 int q;
251 unsigned char *ansp = (unsigned char *) (header + 1);
252
253 for (q = ntohs(header->qdcount); q != 0; q--) {
254 if (!(ansp = skip_name(ansp, header, plen, 4))) {
255 return NULL;
256 }
257 ansp += 4; /* class and type */
258 }
259 return ansp;
260 }
261
262 unsigned char *
do_rfc1035_name(unsigned char * p,char * sval)263 do_rfc1035_name(unsigned char *p, char *sval)
264 {
265 int j;
266
267 while (sval && *sval) {
268 unsigned char *cp = p++;
269 for (j = 0; *sval && (*sval != '.'); sval++, j++) {
270 *p++ = *sval;
271 }
272 *cp = j;
273 if (*sval) {
274 sval++;
275 }
276 }
277 return p;
278 }
279
280 int
add_resource_record(struct dns_header * header,unsigned int nameoffset,size_t plen,unsigned char ** pp,unsigned long ttl,unsigned int * offset,unsigned short type,unsigned short class,char * format,...)281 add_resource_record(struct dns_header *header, unsigned int nameoffset, size_t plen,
282 unsigned char **pp, unsigned long ttl, unsigned int *offset,
283 unsigned short type, unsigned short class, char *format,
284 ...)
285 {
286 va_list ap;
287 unsigned char *sav, *p = *pp;
288 int j;
289 unsigned short usval;
290 long lval;
291 char *sval;
292
293 if (!CHECK_LEN(header, p, plen, 12)) {
294 return 0;
295 }
296 PUTSHORT(nameoffset | 0xc000, p);
297 PUTSHORT(type, p);
298 PUTSHORT(class, p);
299 PUTLONG(ttl, p); /* TTL */
300
301 sav = p; /* Save pointer to RDLength field */
302 PUTSHORT(0, p); /* Placeholder RDLength */
303
304 va_start(ap, format); /* make ap point to 1st unamed argument */
305
306 for (; *format; format++)
307 switch (*format) {
308 #ifdef HAVE_IPV6
309 case '6':
310 if (!CHECK_LEN(header, p, plen, IN6ADDRSZ)) {
311 return 0;
312 }
313 sval = va_arg(ap, char *);
314 memcpy(p, sval, IN6ADDRSZ);
315 p += IN6ADDRSZ;
316 break;
317 #endif
318
319 case '4':
320 if (!CHECK_LEN(header, p, plen, INADDRSZ)) {
321 return 0;
322 }
323 sval = va_arg(ap, char *);
324 memcpy(p, sval, INADDRSZ);
325 p += INADDRSZ;
326 break;
327
328 case 's':
329 if (!CHECK_LEN(header, p, plen, 2)) {
330 return 0;
331 }
332 usval = va_arg(ap, int);
333 PUTSHORT(usval, p);
334 break;
335
336 case 'l':
337 if (!CHECK_LEN(header, p, plen, 4)) {
338 return 0;
339 }
340 lval = va_arg(ap, long);
341 PUTLONG(lval, p);
342 break;
343
344 case 'd':
345 /* get domain-name answer arg and store it in RDATA field */
346 if (offset) {
347 *offset = p - (unsigned char *) header;
348 }
349 p = do_rfc1035_name(p, va_arg(ap, char *));
350 *p++ = 0;
351 break;
352
353 case 't':
354 usval = va_arg(ap, int);
355 sval = va_arg(ap, char *);
356 if (!CHECK_LEN(header, p, plen, usval)) {
357 return 0;
358 }
359 if (usval != 0) {
360 memcpy(p, sval, usval);
361 }
362 p += usval;
363 break;
364
365 case 'z':
366 sval = va_arg(ap, char *);
367 usval = sval ? strlen(sval) : 0;
368 if (usval > 255) {
369 usval = 255;
370 }
371 if (!CHECK_LEN(header, p, plen, (1 + usval))) {
372 return 0;
373 }
374 *p++ = (unsigned char) usval;
375 memcpy(p, sval, usval);
376 p += usval;
377 break;
378 }
379
380 va_end(ap); /* clean up variable argument pointer */
381
382 j = p - sav - 2;
383 if (!CHECK_LEN(header, sav, plen, 2)) {
384 return 0;
385 }
386 PUTSHORT(j, sav); /* Now, store real RDLength */
387
388 *pp = p;
389
390 return 1;
391 }
392