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