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