root/src/udns/udns_dn.c

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

refs #283

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