grrr...fix reverse chronological order
[dragonfly.git] / lib / libcr / net / ns_name.c
1 /*
2  * Copyright (c) 1996 by Internet Software Consortium.
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  *
17  * $FreeBSD: src/lib/libc/net/ns_name.c,v 1.2 1999/08/28 00:00:14 peter Exp $
18  * $DragonFly: src/lib/libcr/net/Attic/ns_name.c,v 1.2 2003/06/17 04:26:44 dillon Exp $
19  */
20
21 #include <sys/types.h>
22
23 #include <netinet/in.h>
24 #include <arpa/nameser.h>
25
26 #include <errno.h>
27 #include <resolv.h>
28 #include <string.h>
29
30 /* Data. */
31
32 static char             digits[] = "0123456789";
33
34 /* Forward. */
35
36 static int              special(int);
37 static int              printable(int);
38 static int              dn_find(const u_char *, const u_char *,
39                                 const u_char * const *,
40                                 const u_char * const *);
41
42 /* Public. */
43
44 /*
45  * ns_name_ntop(src, dst, dstsiz)
46  *      Convert an encoded domain name to printable ascii as per RFC1035.
47  * return:
48  *      Number of bytes written to buffer, or -1 (with errno set)
49  * notes:
50  *      The root is returned as "."
51  *      All other domains are returned in non absolute form
52  */
53 int
54 ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
55         const u_char *cp;
56         char *dn, *eom;
57         u_char c;
58         u_int n;
59
60         cp = src;
61         dn = dst;
62         eom = dst + dstsiz;
63
64         while ((n = *cp++) != 0) {
65                 if ((n & NS_CMPRSFLGS) != 0) {
66                         /* Some kind of compression pointer. */
67                         errno = EMSGSIZE;
68                         return (-1);
69                 }
70                 if (dn != dst) {
71                         if (dn >= eom) {
72                                 errno = EMSGSIZE;
73                                 return (-1);
74                         }
75                         *dn++ = '.';
76                 }
77                 if (dn + n >= eom) {
78                         errno = EMSGSIZE;
79                         return (-1);
80                 }
81                 for ((void)NULL; n > 0; n--) {
82                         c = *cp++;
83                         if (special(c)) {
84                                 if (dn + 1 >= eom) {
85                                         errno = EMSGSIZE;
86                                         return (-1);
87                                 }
88                                 *dn++ = '\\';
89                                 *dn++ = (char)c;
90                         } else if (!printable(c)) {
91                                 if (dn + 3 >= eom) {
92                                         errno = EMSGSIZE;
93                                         return (-1);
94                                 }
95                                 *dn++ = '\\';
96                                 *dn++ = digits[c / 100];
97                                 *dn++ = digits[(c % 100) / 10];
98                                 *dn++ = digits[c % 10];
99                         } else {
100                                 if (dn >= eom) {
101                                         errno = EMSGSIZE;
102                                         return (-1);
103                                 }
104                                 *dn++ = (char)c;
105                         }
106                 }
107         }
108         if (dn == dst) {
109                 if (dn >= eom) {
110                         errno = EMSGSIZE;
111                         return (-1);
112                 }
113                 *dn++ = '.';
114         }
115         if (dn >= eom) {
116                 errno = EMSGSIZE;
117                 return (-1);
118         }
119         *dn++ = '\0';
120         return (dn - dst);
121 }
122
123 /*
124  * ns_name_pton(src, dst, dstsiz)
125  *      Convert a ascii string into an encoded domain name as per RFC1035.
126  * return:
127  *      -1 if it fails
128  *      1 if string was fully qualified
129  *      0 is string was not fully qualified
130  * notes:
131  *      Enforces label and domain length limits.
132  */
133
134 int
135 ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
136         u_char *label, *bp, *eom;
137         int c, n, escaped;
138         char *cp;
139
140         escaped = 0;
141         bp = dst;
142         eom = dst + dstsiz;
143         label = bp++;
144
145         while ((c = *src++) != 0) {
146                 if (escaped) {
147                         if ((cp = strchr(digits, c)) != NULL) {
148                                 n = (cp - digits) * 100;
149                                 if ((c = *src++) == 0 ||
150                                     (cp = strchr(digits, c)) == NULL) {
151                                         errno = EMSGSIZE;
152                                         return (-1);
153                                 }
154                                 n += (cp - digits) * 10;
155                                 if ((c = *src++) == 0 ||
156                                     (cp = strchr(digits, c)) == NULL) {
157                                         errno = EMSGSIZE;
158                                         return (-1);
159                                 }
160                                 n += (cp - digits);
161                                 if (n > 255) {
162                                         errno = EMSGSIZE;
163                                         return (-1);
164                                 }
165                                 c = n;
166                         }
167                         escaped = 0;
168                 } else if (c == '\\') {
169                         escaped = 1;
170                         continue;
171                 } else if (c == '.') {
172                         c = (bp - label - 1);
173                         if ((c & NS_CMPRSFLGS) != 0) {  /* Label too big. */
174                                 errno = EMSGSIZE;
175                                 return (-1);
176                         }
177                         if (label >= eom) {
178                                 errno = EMSGSIZE;
179                                 return (-1);
180                         }
181                         *label = c;
182                         /* Fully qualified ? */
183                         if (*src == '\0') {
184                                 if (c != 0) {
185                                         if (bp >= eom) {
186                                                 errno = EMSGSIZE;
187                                                 return (-1);
188                                         }
189                                         *bp++ = '\0';
190                                 }
191                                 if ((bp - dst) > MAXCDNAME) {
192                                         errno = EMSGSIZE;
193                                         return (-1);
194                                 }
195                                 return (1);
196                         }
197                         if (c == 0) {
198                                 errno = EMSGSIZE;
199                                 return (-1);
200                         }
201                         label = bp++;
202                         continue;
203                 }
204                 if (bp >= eom) {
205                         errno = EMSGSIZE;
206                         return (-1);
207                 }
208                 *bp++ = (u_char)c;
209         }
210         c = (bp - label - 1);
211         if ((c & NS_CMPRSFLGS) != 0) {          /* Label too big. */
212                 errno = EMSGSIZE;
213                 return (-1);
214         }
215         if (label >= eom) {
216                 errno = EMSGSIZE;
217                 return (-1);
218         }
219         *label = c;
220         if (c != 0) {
221                 if (bp >= eom) {
222                         errno = EMSGSIZE;
223                         return (-1);
224                 }
225                 *bp++ = 0;
226         }
227         if ((bp - dst) > MAXCDNAME) {   /* src too big */
228                 errno = EMSGSIZE;
229                 return (-1);
230         }
231         return (0);
232 }
233
234 /*
235  * ns_name_unpack(msg, eom, src, dst, dstsiz)
236  *      Unpack a domain name from a message, source may be compressed.
237  * return:
238  *      -1 if it fails, or consumed octets if it succeeds.
239  */
240 int
241 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
242                u_char *dst, size_t dstsiz)
243 {
244         const u_char *srcp, *dstlim;
245         u_char *dstp;
246         int n, c, len, checked;
247
248         len = -1;
249         checked = 0;
250         dstp = dst;
251         srcp = src;
252         dstlim = dst + dstsiz;
253         if (srcp < msg || srcp >= eom) {
254                 errno = EMSGSIZE;
255                 return (-1);
256         }
257         /* Fetch next label in domain name. */
258         while ((n = *srcp++) != 0) {
259                 /* Check for indirection. */
260                 switch (n & NS_CMPRSFLGS) {
261                 case 0:
262                         /* Limit checks. */
263                         if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
264                                 errno = EMSGSIZE;
265                                 return (-1);
266                         }
267                         checked += n + 1;
268                         *dstp++ = n;
269                         memcpy(dstp, srcp, n);
270                         dstp += n;
271                         srcp += n;
272                         break;
273
274                 case NS_CMPRSFLGS:
275                         if (srcp >= eom) {
276                                 errno = EMSGSIZE;
277                                 return (-1);
278                         }
279                         if (len < 0)
280                                 len = srcp - src + 1;
281                         srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
282                         if (srcp < msg || srcp >= eom) {  /* Out of range. */
283                                 errno = EMSGSIZE;
284                                 return (-1);
285                         }
286                         checked += 2;
287                         /*
288                          * Check for loops in the compressed name;
289                          * if we've looked at the whole message,
290                          * there must be a loop.
291                          */
292                         if (checked >= eom - msg) {
293                                 errno = EMSGSIZE;
294                                 return (-1);
295                         }
296                         break;
297
298                 default:
299                         errno = EMSGSIZE;
300                         return (-1);                    /* flag error */
301                 }
302         }
303         *dstp = '\0';
304         if (len < 0)
305                 len = srcp - src;
306         return (len);
307 }
308
309 /*
310  * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
311  *      Pack domain name 'domain' into 'comp_dn'.
312  * return:
313  *      Size of the compressed name, or -1.
314  * notes:
315  *      'dnptrs' is an array of pointers to previous compressed names.
316  *      dnptrs[0] is a pointer to the beginning of the message. The array
317  *      ends with NULL.
318  *      'lastdnptr' is a pointer to the end of the array pointed to
319  *      by 'dnptrs'.
320  * Side effects:
321  *      The list of pointers in dnptrs is updated for labels inserted into
322  *      the message as we compress the name.  If 'dnptr' is NULL, we don't
323  *      try to compress names. If 'lastdnptr' is NULL, we don't update the
324  *      list.
325  */
326 int
327 ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
328              const u_char **dnptrs, const u_char **lastdnptr)
329 {
330         u_char *dstp;
331         const u_char **cpp, **lpp, *eob, *msg;
332         const u_char *srcp;
333         int n, l;
334
335         srcp = src;
336         dstp = dst;
337         eob = dstp + dstsiz;
338         lpp = cpp = NULL;
339         if (dnptrs != NULL) {
340                 if ((msg = *dnptrs++) != NULL) {
341                         for (cpp = dnptrs; *cpp != NULL; cpp++)
342                                 (void)NULL;
343                         lpp = cpp;      /* end of list to search */
344                 }
345         } else
346                 msg = NULL;
347
348         /* make sure the domain we are about to add is legal */
349         l = 0;
350         do {
351                 n = *srcp;
352                 if ((n & NS_CMPRSFLGS) != 0) {
353                         errno = EMSGSIZE;
354                         return (-1);
355                 }
356                 l += n + 1;
357                 if (l > MAXCDNAME) {
358                         errno = EMSGSIZE;
359                         return (-1);
360                 }
361                 srcp += n + 1;
362         } while (n != 0);
363
364         srcp = src;
365         do {
366                 /* Look to see if we can use pointers. */
367                 n = *srcp;
368                 if (n != 0 && msg != NULL) {
369                         l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
370                                     (const u_char * const *)lpp);
371                         if (l >= 0) {
372                                 if (dstp + 1 >= eob) {
373                                         errno = EMSGSIZE;
374                                         return (-1);
375                                 }
376                                 *dstp++ = (l >> 8) | NS_CMPRSFLGS;
377                                 *dstp++ = l % 256;
378                                 return (dstp - dst);
379                         }
380                         /* Not found, save it. */
381                         if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
382                             (dstp - msg) < 0x4000) {
383                                 *cpp++ = dstp;
384                                 *cpp = NULL;
385                         }
386                 }
387                 /* copy label to buffer */
388                 if (n & NS_CMPRSFLGS) {         /* Should not happen. */
389                         errno = EMSGSIZE;
390                         return (-1);
391                 }
392                 if (dstp + 1 + n >= eob) {
393                         errno = EMSGSIZE;
394                         return (-1);
395                 }
396                 memcpy(dstp, srcp, n + 1);
397                 srcp += n + 1;
398                 dstp += n + 1;
399         } while (n != 0);
400
401         if (dstp > eob) {
402                 if (msg != NULL)
403                         *lpp = NULL;
404                 errno = EMSGSIZE;
405                 return (-1);
406         } 
407         return (dstp - dst);
408 }
409
410 /*
411  * ns_name_uncompress(msg, eom, src, dst, dstsiz)
412  *      Expand compressed domain name to presentation format.
413  * return:
414  *      Number of bytes read out of `src', or -1 (with errno set).
415  * note:
416  *      Root domain returns as "." not "".
417  */
418 int
419 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
420                    char *dst, size_t dstsiz)
421 {
422         u_char tmp[NS_MAXCDNAME];
423         int n;
424         
425         if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
426                 return (-1);
427         if (ns_name_ntop(tmp, dst, dstsiz) == -1)
428                 return (-1);
429         return (n);
430 }
431
432 /*
433  * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
434  *      Compress a domain name into wire format, using compression pointers.
435  * return:
436  *      Number of bytes consumed in `dst' or -1 (with errno set).
437  * notes:
438  *      'dnptrs' is an array of pointers to previous compressed names.
439  *      dnptrs[0] is a pointer to the beginning of the message.
440  *      The list ends with NULL.  'lastdnptr' is a pointer to the end of the
441  *      array pointed to by 'dnptrs'. Side effect is to update the list of
442  *      pointers for labels inserted into the message as we compress the name.
443  *      If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
444  *      is NULL, we don't update the list.
445  */
446 int
447 ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
448                  const u_char **dnptrs, const u_char **lastdnptr)
449 {
450         u_char tmp[NS_MAXCDNAME];
451
452         if (ns_name_pton(src, tmp, sizeof tmp) == -1)
453                 return (-1);
454         return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
455 }
456
457 /*
458  * ns_name_skip(ptrptr, eom)
459  *      Advance *ptrptr to skip over the compressed name it points at.
460  * return:
461  *      0 on success, -1 (with errno set) on failure.
462  */
463 int
464 ns_name_skip(const u_char **ptrptr, const u_char *eom) {
465         const u_char *cp;
466         u_int n;
467
468         cp = *ptrptr;
469         while (cp < eom && (n = *cp++) != 0) {
470                 /* Check for indirection. */
471                 switch (n & NS_CMPRSFLGS) {
472                 case 0:                 /* normal case, n == len */
473                         cp += n;
474                         continue;
475                 case NS_CMPRSFLGS:      /* indirection */
476                         cp++;
477                         break;
478                 default:                /* illegal type */
479                         errno = EMSGSIZE;
480                         return (-1);
481                 }
482                 break;
483         }
484         if (cp > eom) {
485                 errno = EMSGSIZE;
486                 return (-1);
487         }
488         *ptrptr = cp;
489         return (0);
490 }
491
492 /* Private. */
493
494 /*
495  * special(ch)
496  *      Thinking in noninternationalized USASCII (per the DNS spec),
497  *      is this characted special ("in need of quoting") ?
498  * return:
499  *      boolean.
500  */
501 static int
502 special(int ch) {
503         switch (ch) {
504         case 0x22: /* '"' */
505         case 0x2E: /* '.' */
506         case 0x3B: /* ';' */
507         case 0x5C: /* '\\' */
508         /* Special modifiers in zone files. */
509         case 0x40: /* '@' */
510         case 0x24: /* '$' */
511                 return (1);
512         default:
513                 return (0);
514         }
515 }
516
517 /*
518  * printable(ch)
519  *      Thinking in noninternationalized USASCII (per the DNS spec),
520  *      is this character visible and not a space when printed ?
521  * return:
522  *      boolean.
523  */
524 static int
525 printable(int ch) {
526         return (ch > 0x20 && ch < 0x7f);
527 }
528
529 /*
530  *      Thinking in noninternationalized USASCII (per the DNS spec),
531  *      convert this character to lower case if it's upper case.
532  */
533 static int
534 mklower(int ch) {
535         if (ch >= 0x41 && ch <= 0x5A)
536                 return (ch + 0x20);
537         return (ch);
538 }
539
540 /*
541  * dn_find(domain, msg, dnptrs, lastdnptr)
542  *      Search for the counted-label name in an array of compressed names.
543  * return:
544  *      offset from msg if found, or -1.
545  * notes:
546  *      dnptrs is the pointer to the first name on the list,
547  *      not the pointer to the start of the message.
548  */
549 static int
550 dn_find(const u_char *domain, const u_char *msg,
551         const u_char * const *dnptrs,
552         const u_char * const *lastdnptr)
553 {
554         const u_char *dn, *cp, *sp;
555         const u_char * const *cpp;
556         u_int n;
557
558         for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
559                 dn = domain;
560                 sp = cp = *cpp;
561                 while ((n = *cp++) != 0) {
562                         /*
563                          * check for indirection
564                          */
565                         switch (n & NS_CMPRSFLGS) {
566                         case 0:                 /* normal case, n == len */
567                                 if (n != *dn++)
568                                         goto next;
569                                 for ((void)NULL; n > 0; n--)
570                                         if (mklower(*dn++) != mklower(*cp++))
571                                                 goto next;
572                                 /* Is next root for both ? */
573                                 if (*dn == '\0' && *cp == '\0')
574                                         return (sp - msg);
575                                 if (*dn)
576                                         continue;
577                                 goto next;
578
579                         case NS_CMPRSFLGS:      /* indirection */
580                                 cp = msg + (((n & 0x3f) << 8) | *cp);
581                                 break;
582
583                         default:        /* illegal type */
584                                 errno = EMSGSIZE;
585                                 return (-1);
586                         }
587                 }
588  next: ;
589         }
590         errno = ENOENT;
591         return (-1);
592 }