root/src/udns/udns_dn.c

Revision eafba4c159b528b48fd4bb166e310408712691bb, 9.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 months ago)

Upgrade libudns 0.4

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