root/src/udns/udns_dn.c

Revision 651988747506d25414e2375c0eb1b41f0ca3aaf9, 9.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

Various security fixes.

  • Property mode set to 100644
Line 
1 /* $Id: udns_dn.c,v 1.7 2006/11/28 22:45:20 mjt Exp $
2    domain names manipulation routines
3
4    Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
5    This file is part of UDNS library, an async DNS stub resolver.
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library, in file named COPYING.LGPL; if not,
19    write to the Free Software Foundation, Inc., 59 Temple Place,
20    Suite 330, Boston, MA  02111-1307  USA
21
22  */
23
24 #include <string.h>
25 #include "udns.h"
26
27 unsigned dns_dnlen(dnscc_t *dn) {
28   register dnscc_t *d = dn;
29   unsigned l = 0;
30   while(*d && l < DNS_MAXDN) {
31     l += 1 + *d;
32     d += 1 + *d;
33   }
34   return (unsigned)(d - dn) + 1;
35 }
36
37 unsigned dns_dnlabels(register dnscc_t *dn) {
38   register unsigned l = 0;
39   while(*dn)
40     ++l, dn += 1 + *dn;
41   return l;
42 }
43
44 unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) {
45   register unsigned c;
46   dnscc_t *dn = dn1;
47   for(;;) {
48     if ((c = *dn1++) != *dn2++)
49       return 0;
50     if (!c)
51       return (unsigned)(dn1 - dn);
52     while(c--) {
53       if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2))
54         return 0;
55       ++dn1; ++dn2;
56     }
57   }
58 }
59
60 unsigned
61 dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) {
62   unsigned sdnlen = dns_dnlen(sdn);
63   if (ddnsiz < sdnlen)
64     return 0;
65   memcpy(ddn, sdn, sdnlen);
66   return sdnlen;
67 }
68
69 int
70 dns_ptodn(const char *name, unsigned namelen,
71           dnsc_t *dn, unsigned dnsiz, int *isabs)
72 {
73   dnsc_t *dp;           /* current position in dn (len byte first) */
74   dnsc_t *const de      /* end of dn: last byte that can be filled up */
75       = dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1;
76   dnscc_t *np = (dnscc_t *)name;
77   dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np));
78   dnsc_t *llab;         /* start of last label (llab[-1] will be length) */
79   unsigned c;           /* next input character, or length of last label */
80
81   if (!dnsiz)
82     return 0;
83   dp = llab = dn + 1;
84
85   while(np < ne) {
86
87     if (*np == '.') {   /* label delimiter */
88       c = dp - llab;            /* length of the label */
89       if (!c) {                 /* empty label */
90         if (np == (dnscc_t *)name && np + 1 == ne) {
91           /* special case for root dn, aka `.' */
92           /* ++np;
93              ... incrementing a variable that isn't subsequently looked at */
94           break;
95         }
96         return -1;              /* zero label */
97       }
98       if (c > DNS_MAXLABEL)
99         return -1;              /* label too long */
100       llab[-1] = (dnsc_t)c;     /* update len of last label */
101       llab = ++dp; /* start new label, llab[-1] will be len of it */
102       ++np;
103       continue;
104     }
105
106     /* check whenever we may put out one more byte */
107     if (dp >= de) /* too long? */
108       return dnsiz >= DNS_MAXDN ? -1 : 0;
109     if (*np != '\\') { /* non-escape, simple case */
110       *dp++ = *np++;
111       continue;
112     }
113     /* handle \-style escape */
114     /* note that traditionally, domain names (gethostbyname etc)
115      * used decimal \dd notation, not octal \ooo (RFC1035), so
116      * we're following this tradition here.
117      */
118     if (++np == ne)
119       return -1;                        /* bad escape */
120     else if (*np >= '0' && *np <= '9') { /* decimal number */
121       /* we allow not only exactly 3 digits as per RFC1035,
122        * but also 2 or 1, for better usability. */
123       c = *np++ - '0';
124       if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */
125         c = c * 10 + *np++ - '0';
126         if (np < ne && *np >= '0' && *np <= '9') {
127           c = c * 10 + *np++ - '0';
128           if (c > 255)
129             return -1;                  /* bad escape */
130         }
131       }
132     }
133     else
134       c = *np++;
135     *dp++ = (dnsc_t)c;  /* place next out byte */
136   }
137
138   if ((c = dp - llab) > DNS_MAXLABEL)
139     return -1;                          /* label too long */
140   if ((llab[-1] = (dnsc_t)c) != 0) {
141     if (dp >= de)
142       return -1;
143     *dp++ = 0;
144     if (isabs)
145       *isabs = 0;
146   }
147   else if (isabs)
148     *isabs = 1;
149
150   return dp - dn;
151 }
152
153 dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa";
154
155 dnsc_t *
156 dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) {
157   dnsc_t *p;
158   unsigned n;
159   dnscc_t *s = ((dnscc_t *)addr) + 4;
160   while(--s >= (dnscc_t *)addr) {
161     n = *s;
162     p = dn + 1;
163     if (n > 99) {
164       if (p + 2 > dne) return 0;
165       *p++ = n / 100 + '0';
166       *p++ = (n % 100 / 10) + '0';
167       *p = n % 10 + '0';
168     }
169     else if (n > 9) {
170       if (p + 1 > dne) return 0;
171       *p++ = n / 10 + '0';
172       *p = n % 10 + '0';
173     }
174     else {
175       if (p > dne) return 0;
176       *p = n + '0';
177     }
178     *dn = p - dn;
179     dn = p + 1;
180   }
181   return dn;
182 }
183
184 int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn,
185                dnsc_t *dn, unsigned dnsiz) {
186   dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
187   dnsc_t *p;
188   unsigned l;
189   p = dns_a4todn_(addr, dn, dne);
190   if (!p) return 0;
191   if (!tdn)
192     tdn = dns_inaddr_arpa_dn;
193   l = dns_dnlen(tdn);
194   if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
195   memcpy(p, tdn, l);
196   return (p + l) - dn;
197 }
198
199 int dns_a4ptodn(const struct in_addr *addr, const char *tname,
200                 dnsc_t *dn, unsigned dnsiz) {
201   dnsc_t *p;
202   int r;
203   if (!tname)
204     return dns_a4todn(addr, NULL, dn, dnsiz);
205   p = dns_a4todn_(addr, dn, dn + dnsiz);
206   if (!p) return 0;
207   r = dns_sptodn(tname, p, dnsiz - (p - dn));
208   return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
209 }
210
211 dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa";
212
213 dnsc_t *
214 dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) {
215   unsigned n;
216   dnscc_t *s = ((dnscc_t *)addr) + 16;
217   if (dn + 64 > dne) return 0;
218   while(--s >= (dnscc_t *)addr) {
219     *dn++ = 1;
220     n = *s & 0x0f;
221     *dn++ = n > 9 ? n + 'a' - 10 : n + '0';
222     *dn++ = 1;
223     n = *s >> 4;
224     *dn++ = n > 9 ? n + 'a' - 10 : n + '0';
225   }
226   return dn;
227 }
228
229 int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn,
230                dnsc_t *dn, unsigned dnsiz) {
231   dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
232   dnsc_t *p;
233   unsigned l;
234   p = dns_a6todn_(addr, dn, dne);
235   if (!p) return 0;
236   if (!tdn)
237     tdn = dns_ip6_arpa_dn;
238   l = dns_dnlen(tdn);
239   if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
240   memcpy(p, tdn, l);
241   return (p + l) - dn;
242 }
243
244 int dns_a6ptodn(const struct in6_addr *addr, const char *tname,
245                 dnsc_t *dn, unsigned dnsiz) {
246   dnsc_t *p;
247   int r;
248   if (!tname)
249     return dns_a6todn(addr, NULL, dn, dnsiz);
250   p = dns_a6todn_(addr, dn, dn + dnsiz);
251   if (!p) return 0;
252   r = dns_sptodn(tname, p, dnsiz - (p - dn));
253   return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
254 }
255
256 /* return size of buffer required to convert the dn into asciiz string.
257  * Keep in sync with dns_dntop() below.
258  */
259 unsigned dns_dntop_size(dnscc_t *dn) {
260   unsigned size = 0;            /* the size reqd */
261   dnscc_t *le;                  /* label end */
262
263   while(*dn) {
264     /* *dn is the length of the next label, non-zero */
265     if (size)
266       ++size;           /* for the dot */
267     le = dn + *dn + 1;
268     ++dn;
269     do {
270       switch(*dn) {
271       case '.':
272       case '\\':
273       /* Special modifiers in zone files. */
274       case '"':
275       case ';':
276       case '@':
277       case '$':
278         size += 2;
279         break;
280       default:
281         if (*dn <= 0x20 || *dn >= 0x7f)
282           /* \ddd decimal notation */
283           size += 4;
284         else
285           size += 1;
286       }
287     } while(++dn < le);
288   }
289   size += 1;    /* zero byte at the end - string terminator */
290   return size > DNS_MAXNAME ? 0 : size;
291 }
292
293 /* Convert the dn into asciiz string.
294  * Keep in sync with dns_dntop_size() above.
295  */
296 int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) {
297   char *np = name;                      /* current name ptr */
298   char *const ne = name + namesiz;      /* end of name */
299   dnscc_t *le;          /* label end */
300
301   while(*dn) {
302     /* *dn is the length of the next label, non-zero */
303     if (np != name) {
304       if (np >= ne) goto toolong;
305       *np++ = '.';
306     }
307     le = dn + *dn + 1;
308     ++dn;
309     do {
310       switch(*dn) {
311       case '.':
312       case '\\':
313       /* Special modifiers in zone files. */
314       case '"':
315       case ';':
316       case '@':
317       case '$':
318         if (np + 2 > ne) goto toolong;
319         *np++ = '\\';
320         *np++ = *dn;
321         break;
322       default:
323         if (*dn <= 0x20 || *dn >= 0x7f) {
324           /* \ddd decimal notation */
325           if (np + 4 >= ne) goto toolong;
326           *np++ = '\\';
327           *np++ = '0' + (*dn / 100);
328           *np++ = '0' + ((*dn % 100) / 10);
329           *np++ = '0' + (*dn % 10);
330         }
331         else {
332           if (np >= ne) goto toolong;
333           *np++ = *dn;
334         }
335       }
336     } while(++dn < le);
337   }
338   if (np >= ne) goto toolong;
339   *np++ = '\0';
340   return np - name;
341 toolong:
342   return namesiz >= DNS_MAXNAME ? -1 : 0;
343 }
344
345 #ifdef TEST
346 #include <stdio.h>
347 #include <stdlib.h>
348
349 int main(int argc, char **argv) {
350   int i;
351   int sz;
352   dnsc_t dn[DNS_MAXDN+10];
353   dnsc_t *dl, *dp;
354   int isabs;
355
356   sz = (argc > 1) ? atoi(argv[1]) : 0;
357
358   for(i = 2; i < argc; ++i) {
359     int r = dns_ptodn(argv[i], 0, dn, sz, &isabs);
360     printf("%s: ", argv[i]);
361     if (r < 0) printf("error\n");
362     else if (!r) printf("buffer too small\n");
363     else {
364       printf("len=%d dnlen=%d size=%d name:",
365              r, dns_dnlen(dn), dns_dntop_size(dn));
366       dl = dn;
367       while(*dl) {
368         printf(" %d=", *dl);
369         dp = dl + 1;
370         dl = dp + *dl;
371         while(dp < dl) {
372           if (*dp <= ' ' || *dp >= 0x7f)
373             printf("\\%03d", *dp);
374           else if (*dp == '.' || *dp == '\\')
375             printf("\\%c", *dp);
376           else
377             putchar(*dp);
378           ++dp;
379         }
380       }
381       if (isabs) putchar('.');
382       putchar('\n');
383     }
384   }
385   return 0;
386 }
387
388 #endif /* TEST */
Note: See TracBrowser for help on using the browser.