]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/nameser/ns_name.c
libc: move rfork_thread(3) to libsys
[FreeBSD/FreeBSD.git] / lib / libc / nameser / ns_name.c
1 /*-
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (c) 1996,1999 by Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include "port_before.h"
21
22 #include <sys/types.h>
23
24 #include <netinet/in.h>
25 #include <arpa/nameser.h>
26
27 #include <errno.h>
28 #include <resolv.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <limits.h>
33
34 #include "port_after.h"
35
36 #ifdef SPRINTF_CHAR
37 # define SPRINTF(x) strlen(sprintf/**/x)
38 #else
39 # define SPRINTF(x) ((size_t)sprintf x)
40 #endif
41
42 #define NS_TYPE_ELT                     0x40 /*%< EDNS0 extended label type */
43 #define DNS_LABELTYPE_BITSTRING         0x41
44
45 /* Data. */
46
47 static const char       digits[] = "0123456789";
48
49 static const char digitvalue[256] = {
50         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
51         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
52         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
53          0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
54         -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
55         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
56         -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
57         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
58         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
59         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
60         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
61         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
62         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
63         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
64         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
65         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
66 };
67
68 /* Forward. */
69
70 static int              special(int);
71 static int              printable(int);
72 static int              dn_find(const u_char *, const u_char *,
73                                 const u_char * const *,
74                                 const u_char * const *);
75 static int              encode_bitsring(const char **, const char *,
76                                         unsigned char **, unsigned char **,
77                                         unsigned const char *);
78 static int              labellen(const u_char *);
79 static int              decode_bitstring(const unsigned char **,
80                                          char *, const char *);
81
82 /* Public. */
83
84 /*%
85  *      Convert an encoded domain name to printable ascii as per RFC1035.
86
87  * return:
88  *\li   Number of bytes written to buffer, or -1 (with errno set)
89  *
90  * notes:
91  *\li   The root is returned as "."
92  *\li   All other domains are returned in non absolute form
93  */
94 int
95 ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
96 {
97         const u_char *cp;
98         char *dn, *eom;
99         u_char c;
100         u_int n;
101         int l;
102
103         cp = src;
104         dn = dst;
105         eom = dst + dstsiz;
106
107         while ((n = *cp++) != 0) {
108                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
109                         /* Some kind of compression pointer. */
110                         errno = EMSGSIZE;
111                         return (-1);
112                 }
113                 if (dn != dst) {
114                         if (dn >= eom) {
115                                 errno = EMSGSIZE;
116                                 return (-1);
117                         }
118                         *dn++ = '.';
119                 }
120                 if ((l = labellen(cp - 1)) < 0) {
121                         errno = EMSGSIZE; /*%< XXX */
122                         return (-1);
123                 }
124                 if (dn + l >= eom) {
125                         errno = EMSGSIZE;
126                         return (-1);
127                 }
128                 if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
129                         int m;
130
131                         if (n != DNS_LABELTYPE_BITSTRING) {
132                                 /* XXX: labellen should reject this case */
133                                 errno = EINVAL;
134                                 return (-1);
135                         }
136                         if ((m = decode_bitstring(&cp, dn, eom)) < 0)
137                         {
138                                 errno = EMSGSIZE;
139                                 return (-1);
140                         }
141                         dn += m; 
142                         continue;
143                 }
144                 for ((void)NULL; l > 0; l--) {
145                         c = *cp++;
146                         if (special(c)) {
147                                 if (dn + 1 >= eom) {
148                                         errno = EMSGSIZE;
149                                         return (-1);
150                                 }
151                                 *dn++ = '\\';
152                                 *dn++ = (char)c;
153                         } else if (!printable(c)) {
154                                 if (dn + 3 >= eom) {
155                                         errno = EMSGSIZE;
156                                         return (-1);
157                                 }
158                                 *dn++ = '\\';
159                                 *dn++ = digits[c / 100];
160                                 *dn++ = digits[(c % 100) / 10];
161                                 *dn++ = digits[c % 10];
162                         } else {
163                                 if (dn >= eom) {
164                                         errno = EMSGSIZE;
165                                         return (-1);
166                                 }
167                                 *dn++ = (char)c;
168                         }
169                 }
170         }
171         if (dn == dst) {
172                 if (dn >= eom) {
173                         errno = EMSGSIZE;
174                         return (-1);
175                 }
176                 *dn++ = '.';
177         }
178         if (dn >= eom) {
179                 errno = EMSGSIZE;
180                 return (-1);
181         }
182         *dn++ = '\0';
183         return (dn - dst);
184 }
185
186 /*%
187  *      Convert a ascii string into an encoded domain name as per RFC1035.
188  *
189  * return:
190  *
191  *\li   -1 if it fails
192  *\li   1 if string was fully qualified
193  *\li   0 is string was not fully qualified
194  *
195  * notes:
196  *\li   Enforces label and domain length limits.
197  */
198 int
199 ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
200         return (ns_name_pton2(src, dst, dstsiz, NULL));
201 }
202
203 /*
204  * ns_name_pton2(src, dst, dstsiz, *dstlen)
205  *      Convert a ascii string into an encoded domain name as per RFC1035.
206  * return:
207  *      -1 if it fails
208  *      1 if string was fully qualified
209  *      0 is string was not fully qualified
210  * side effects:
211  *      fills in *dstlen (if non-NULL)
212  * notes:
213  *      Enforces label and domain length limits.
214  */
215 int
216 ns_name_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen) {
217         u_char *label, *bp, *eom;
218         int c, n, escaped, e = 0;
219         char *cp;
220
221         escaped = 0;
222         bp = dst;
223         eom = dst + dstsiz;
224         label = bp++;
225
226         while ((c = *src++) != 0) {
227                 if (escaped) {
228                         if (c == '[') { /*%< start a bit string label */
229                                 if ((cp = strchr(src, ']')) == NULL) {
230                                         errno = EINVAL; /*%< ??? */
231                                         return (-1);
232                                 }
233                                 if ((e = encode_bitsring(&src, cp + 2,
234                                                          &label, &bp, eom))
235                                     != 0) {
236                                         errno = e;
237                                         return (-1);
238                                 }
239                                 escaped = 0;
240                                 label = bp++;
241                                 if ((c = *src++) == 0)
242                                         goto done;
243                                 else if (c != '.') {
244                                         errno = EINVAL;
245                                         return  (-1);
246                                 }
247                                 continue;
248                         }
249                         else if ((cp = strchr(digits, c)) != NULL) {
250                                 n = (cp - digits) * 100;
251                                 if ((c = *src++) == 0 ||
252                                     (cp = strchr(digits, c)) == NULL) {
253                                         errno = EMSGSIZE;
254                                         return (-1);
255                                 }
256                                 n += (cp - digits) * 10;
257                                 if ((c = *src++) == 0 ||
258                                     (cp = strchr(digits, c)) == NULL) {
259                                         errno = EMSGSIZE;
260                                         return (-1);
261                                 }
262                                 n += (cp - digits);
263                                 if (n > 255) {
264                                         errno = EMSGSIZE;
265                                         return (-1);
266                                 }
267                                 c = n;
268                         }
269                         escaped = 0;
270                 } else if (c == '\\') {
271                         escaped = 1;
272                         continue;
273                 } else if (c == '.') {
274                         c = (bp - label - 1);
275                         if ((c & NS_CMPRSFLGS) != 0) {  /*%< Label too big. */
276                                 errno = EMSGSIZE;
277                                 return (-1);
278                         }
279                         if (label >= eom) {
280                                 errno = EMSGSIZE;
281                                 return (-1);
282                         }
283                         *label = c;
284                         /* Fully qualified ? */
285                         if (*src == '\0') {
286                                 if (c != 0) {
287                                         if (bp >= eom) {
288                                                 errno = EMSGSIZE;
289                                                 return (-1);
290                                         }
291                                         *bp++ = '\0';
292                                 }
293                                 if ((bp - dst) > MAXCDNAME) {
294                                         errno = EMSGSIZE;
295                                         return (-1);
296                                 }
297                                 if (dstlen != NULL)
298                                         *dstlen = (bp - dst);
299                                 return (1);
300                         }
301                         if (c == 0 || *src == '.') {
302                                 errno = EMSGSIZE;
303                                 return (-1);
304                         }
305                         label = bp++;
306                         continue;
307                 }
308                 if (bp >= eom) {
309                         errno = EMSGSIZE;
310                         return (-1);
311                 }
312                 *bp++ = (u_char)c;
313         }
314         c = (bp - label - 1);
315         if ((c & NS_CMPRSFLGS) != 0) {          /*%< Label too big. */
316                 errno = EMSGSIZE;
317                 return (-1);
318         }
319   done:
320         if (label >= eom) {
321                 errno = EMSGSIZE;
322                 return (-1);
323         }
324         *label = c;
325         if (c != 0) {
326                 if (bp >= eom) {
327                         errno = EMSGSIZE;
328                         return (-1);
329                 }
330                 *bp++ = 0;
331         }
332         if ((bp - dst) > MAXCDNAME) {   /*%< src too big */
333                 errno = EMSGSIZE;
334                 return (-1);
335         }
336         if (dstlen != NULL)
337                 *dstlen = (bp - dst);
338         return (0);
339 }
340
341 /*%
342  *      Convert a network strings labels into all lowercase.
343  *
344  * return:
345  *\li   Number of bytes written to buffer, or -1 (with errno set)
346  *
347  * notes:
348  *\li   Enforces label and domain length limits.
349  */
350
351 int
352 ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
353 {
354         const u_char *cp;
355         u_char *dn, *eom;
356         u_char c;
357         u_int n;
358         int l;
359
360         cp = src;
361         dn = dst;
362         eom = dst + dstsiz;
363
364         if (dn >= eom) {
365                 errno = EMSGSIZE;
366                 return (-1);
367         }
368         while ((n = *cp++) != 0) {
369                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
370                         /* Some kind of compression pointer. */
371                         errno = EMSGSIZE;
372                         return (-1);
373                 }
374                 *dn++ = n;
375                 if ((l = labellen(cp - 1)) < 0) {
376                         errno = EMSGSIZE;
377                         return (-1);
378                 }
379                 if (dn + l >= eom) {
380                         errno = EMSGSIZE;
381                         return (-1);
382                 }
383                 for ((void)NULL; l > 0; l--) {
384                         c = *cp++;
385                         if (isascii(c) && isupper(c))
386                                 *dn++ = tolower(c);
387                         else
388                                 *dn++ = c;
389                 }
390         }
391         *dn++ = '\0';
392         return (dn - dst);
393 }
394
395 /*%
396  *      Unpack a domain name from a message, source may be compressed.
397  *
398  * return:
399  *\li   -1 if it fails, or consumed octets if it succeeds.
400  */
401 int
402 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
403                u_char *dst, size_t dstsiz)
404 {
405         return (ns_name_unpack2(msg, eom, src, dst, dstsiz, NULL));
406 }
407
408 /*
409  * ns_name_unpack2(msg, eom, src, dst, dstsiz, *dstlen)
410  *      Unpack a domain name from a message, source may be compressed.
411  * return:
412  *      -1 if it fails, or consumed octets if it succeeds.
413  * side effect:
414  *      fills in *dstlen (if non-NULL).
415  */
416 int
417 ns_name_unpack2(const u_char *msg, const u_char *eom, const u_char *src,
418                 u_char *dst, size_t dstsiz, size_t *dstlen)
419 {
420         const u_char *srcp, *dstlim;
421         u_char *dstp;
422         int n, len, checked, l;
423
424         len = -1;
425         checked = 0;
426         dstp = dst;
427         srcp = src;
428         dstlim = dst + dstsiz;
429         if (srcp < msg || srcp >= eom) {
430                 errno = EMSGSIZE;
431                 return (-1);
432         }
433         /* Fetch next label in domain name. */
434         while ((n = *srcp++) != 0) {
435                 /* Check for indirection. */
436                 switch (n & NS_CMPRSFLGS) {
437                 case 0:
438                 case NS_TYPE_ELT:
439                         /* Limit checks. */
440                         if ((l = labellen(srcp - 1)) < 0) {
441                                 errno = EMSGSIZE;
442                                 return (-1);
443                         }
444                         if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
445                                 errno = EMSGSIZE;
446                                 return (-1);
447                         }
448                         checked += l + 1;
449                         *dstp++ = n;
450                         memcpy(dstp, srcp, l);
451                         dstp += l;
452                         srcp += l;
453                         break;
454
455                 case NS_CMPRSFLGS:
456                         if (srcp >= eom) {
457                                 errno = EMSGSIZE;
458                                 return (-1);
459                         }
460                         if (len < 0)
461                                 len = srcp - src + 1;
462                         l = ((n & 0x3f) << 8) | (*srcp & 0xff);
463                         if (l >= eom - msg) {  /*%< Out of range. */
464                                 errno = EMSGSIZE;
465                                 return (-1);
466                         }
467                         srcp = msg + l;
468                         checked += 2;
469                         /*
470                          * Check for loops in the compressed name;
471                          * if we've looked at the whole message,
472                          * there must be a loop.
473                          */
474                         if (checked >= eom - msg) {
475                                 errno = EMSGSIZE;
476                                 return (-1);
477                         }
478                         break;
479
480                 default:
481                         errno = EMSGSIZE;
482                         return (-1);                    /*%< flag error */
483                 }
484         }
485         *dstp++ = 0;
486         if (dstlen != NULL)
487                 *dstlen = dstp - dst;
488         if (len < 0)
489                 len = srcp - src;
490         return (len);
491 }
492
493 /*%
494  *      Pack domain name 'domain' into 'comp_dn'.
495  *
496  * return:
497  *\li   Size of the compressed name, or -1.
498  *
499  * notes:
500  *\li   'dnptrs' is an array of pointers to previous compressed names.
501  *\li   dnptrs[0] is a pointer to the beginning of the message. The array
502  *      ends with NULL.
503  *\li   'lastdnptr' is a pointer to the end of the array pointed to
504  *      by 'dnptrs'.
505  *
506  * Side effects:
507  *\li   The list of pointers in dnptrs is updated for labels inserted into
508  *      the message as we compress the name.  If 'dnptr' is NULL, we don't
509  *      try to compress names. If 'lastdnptr' is NULL, we don't update the
510  *      list.
511  */
512 int
513 ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
514              const u_char **dnptrs, const u_char **lastdnptr)
515 {
516         u_char *dstp;
517         const u_char **cpp, **lpp, *eob, *msg;
518         const u_char *srcp;
519         int n, l, first = 1;
520
521         srcp = src;
522         dstp = dst;
523         eob = dstp + dstsiz;
524         lpp = cpp = NULL;
525         if (dnptrs != NULL) {
526                 if ((msg = *dnptrs++) != NULL) {
527                         for (cpp = dnptrs; *cpp != NULL; cpp++)
528                                 (void)NULL;
529                         lpp = cpp;      /*%< end of list to search */
530                 }
531         } else
532                 msg = NULL;
533
534         /* make sure the domain we are about to add is legal */
535         l = 0;
536         do {
537                 int l0;
538
539                 n = *srcp;
540                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
541                         errno = EMSGSIZE;
542                         return (-1);
543                 }
544                 if ((l0 = labellen(srcp)) < 0) {
545                         errno = EINVAL;
546                         return (-1);
547                 }
548                 l += l0 + 1;
549                 if (l > MAXCDNAME) {
550                         errno = EMSGSIZE;
551                         return (-1);
552                 }
553                 srcp += l0 + 1;
554         } while (n != 0);
555
556         /* from here on we need to reset compression pointer array on error */
557         srcp = src;
558         do {
559                 /* Look to see if we can use pointers. */
560                 n = *srcp;
561                 if (n != 0 && msg != NULL) {
562                         l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
563                                     (const u_char * const *)lpp);
564                         if (l >= 0) {
565                                 if (dstp + 1 >= eob) {
566                                         goto cleanup;
567                                 }
568                                 *dstp++ = (l >> 8) | NS_CMPRSFLGS;
569                                 *dstp++ = l % 256;
570                                 return (dstp - dst);
571                         }
572                         /* Not found, save it. */
573                         if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
574                             (dstp - msg) < 0x4000 && first) {
575                                 *cpp++ = dstp;
576                                 *cpp = NULL;
577                                 first = 0;
578                         }
579                 }
580                 /* copy label to buffer */
581                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
582                         /* Should not happen. */
583                         goto cleanup;
584                 }
585                 n = labellen(srcp);
586                 if (dstp + 1 + n >= eob) {
587                         goto cleanup;
588                 }
589                 memcpy(dstp, srcp, n + 1);
590                 srcp += n + 1;
591                 dstp += n + 1;
592         } while (n != 0);
593
594         if (dstp > eob) {
595 cleanup:
596                 if (msg != NULL)
597                         *lpp = NULL;
598                 errno = EMSGSIZE;
599                 return (-1);
600         } 
601         return (dstp - dst);
602 }
603
604 /*%
605  *      Expand compressed domain name to presentation format.
606  *
607  * return:
608  *\li   Number of bytes read out of `src', or -1 (with errno set).
609  *
610  * note:
611  *\li   Root domain returns as "." not "".
612  */
613 int
614 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
615                    char *dst, size_t dstsiz)
616 {
617         u_char tmp[NS_MAXCDNAME];
618         int n;
619         
620         if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
621                 return (-1);
622         if (ns_name_ntop(tmp, dst, dstsiz) == -1)
623                 return (-1);
624         return (n);
625 }
626
627 /*%
628  *      Compress a domain name into wire format, using compression pointers.
629  *
630  * return:
631  *\li   Number of bytes consumed in `dst' or -1 (with errno set).
632  *
633  * notes:
634  *\li   'dnptrs' is an array of pointers to previous compressed names.
635  *\li   dnptrs[0] is a pointer to the beginning of the message.
636  *\li   The list ends with NULL.  'lastdnptr' is a pointer to the end of the
637  *      array pointed to by 'dnptrs'. Side effect is to update the list of
638  *      pointers for labels inserted into the message as we compress the name.
639  *\li   If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
640  *      is NULL, we don't update the list.
641  */
642 int
643 ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
644                  const u_char **dnptrs, const u_char **lastdnptr)
645 {
646         u_char tmp[NS_MAXCDNAME];
647
648         if (ns_name_pton(src, tmp, sizeof tmp) == -1)
649                 return (-1);
650         return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
651 }
652
653 /*%
654  * Reset dnptrs so that there are no active references to pointers at or
655  * after src.
656  */
657 void
658 ns_name_rollback(const u_char *src, const u_char **dnptrs,
659                  const u_char **lastdnptr)
660 {
661         while (dnptrs < lastdnptr && *dnptrs != NULL) {
662                 if (*dnptrs >= src) {
663                         *dnptrs = NULL;
664                         break;
665                 }
666                 dnptrs++;
667         }
668 }
669
670 /*%
671  *      Advance *ptrptr to skip over the compressed name it points at.
672  *
673  * return:
674  *\li   0 on success, -1 (with errno set) on failure.
675  */
676 int
677 ns_name_skip(const u_char **ptrptr, const u_char *eom)
678 {
679         const u_char *cp;
680         u_int n;
681         int l = 0;
682
683         cp = *ptrptr;
684         while (cp < eom && (n = *cp++) != 0) {
685                 /* Check for indirection. */
686                 switch (n & NS_CMPRSFLGS) {
687                 case 0:                 /*%< normal case, n == len */
688                         cp += n;
689                         continue;
690                 case NS_TYPE_ELT: /*%< EDNS0 extended label */
691                         if (cp < eom && (l = labellen(cp - 1)) < 0) {
692                                 errno = EMSGSIZE; /*%< XXX */
693                                 return (-1);
694                         }
695                         cp += l;
696                         continue;
697                 case NS_CMPRSFLGS:      /*%< indirection */
698                         cp++;
699                         break;
700                 default:                /*%< illegal type */
701                         errno = EMSGSIZE;
702                         return (-1);
703                 }
704                 break;
705         }
706         if (cp > eom) {
707                 errno = EMSGSIZE;
708                 return (-1);
709         }
710         *ptrptr = cp;
711         return (0);
712 }
713
714 /* Find the number of octets an nname takes up, including the root label.
715  * (This is basically ns_name_skip() without compression-pointer support.)
716  * ((NOTE: can only return zero if passed-in namesiz argument is zero.))
717  */
718 ssize_t
719 ns_name_length(ns_nname_ct nname, size_t namesiz) {
720         ns_nname_ct orig = nname;
721         u_int n;
722
723         while (namesiz-- > 0 && (n = *nname++) != 0) {
724                 if ((n & NS_CMPRSFLGS) != 0) {
725                         errno = EISDIR;
726                         return (-1);
727                 }
728                 if (n > namesiz) {
729                         errno = EMSGSIZE;
730                         return (-1);
731                 }
732                 nname += n;
733                 namesiz -= n;
734         }
735         return (nname - orig);
736 }
737
738 /* Compare two nname's for equality.  Return -1 on error (setting errno).
739  */
740 int
741 ns_name_eq(ns_nname_ct a, size_t as, ns_nname_ct b, size_t bs) {
742         ns_nname_ct ae = a + as, be = b + bs;
743         int ac, bc;
744
745         while (ac = *a, bc = *b, ac != 0 && bc != 0) {
746                 if ((ac & NS_CMPRSFLGS) != 0 || (bc & NS_CMPRSFLGS) != 0) {
747                         errno = EISDIR;
748                         return (-1);
749                 }
750                 if (a + ac >= ae || b + bc >= be) {
751                         errno = EMSGSIZE;
752                         return (-1);
753                 }
754                 if (ac != bc || strncasecmp((const char *) ++a,
755                                             (const char *) ++b, ac) != 0)
756                         return (0);
757                 a += ac, b += bc;
758         }
759         return (ac == 0 && bc == 0);
760 }
761
762 /* Is domain "A" owned by (at or below) domain "B"?
763  */
764 int
765 ns_name_owned(ns_namemap_ct a, int an, ns_namemap_ct b, int bn) {
766         /* If A is shorter, it cannot be owned by B. */
767         if (an < bn)
768                 return (0);
769
770         /* If they are unequal before the length of the shorter, A cannot... */
771         while (bn > 0) {
772                 if (a->len != b->len ||
773                     strncasecmp((const char *) a->base,
774                                 (const char *) b->base, a->len) != 0)
775                         return (0);
776                 a++, an--;
777                 b++, bn--;
778         }
779
780         /* A might be longer or not, but either way, B owns it. */
781         return (1);
782 }
783
784 /* Build an array of <base,len> tuples from an nname, top-down order.
785  * Return the number of tuples (labels) thus discovered.
786  */
787 int
788 ns_name_map(ns_nname_ct nname, size_t namelen, ns_namemap_t map, int mapsize) {
789         u_int n;
790         int l;
791
792         n = *nname++;
793         namelen--;
794
795         /* Root zone? */
796         if (n == 0) {
797                 /* Extra data follows name? */
798                 if (namelen > 0) {
799                         errno = EMSGSIZE;
800                         return (-1);
801                 }
802                 return (0);
803         }
804
805         /* Compression pointer? */
806         if ((n & NS_CMPRSFLGS) != 0) {
807                 errno = EISDIR;
808                 return (-1);
809         }
810
811         /* Label too long? */
812         if (n > namelen) {
813                 errno = EMSGSIZE;
814                 return (-1);
815         }
816
817         /* Recurse to get rest of name done first. */
818         l = ns_name_map(nname + n, namelen - n, map, mapsize);
819         if (l < 0)
820                 return (-1);
821
822         /* Too many labels? */
823         if (l >= mapsize) {
824                 errno = ENAMETOOLONG;
825                 return (-1);
826         }
827
828         /* We're on our way back up-stack, store current map data. */
829         map[l].base = nname;
830         map[l].len = n;
831         return (l + 1);
832 }
833
834 /* Count the labels in a domain name.  Root counts, so COM. has two.  This
835  * is to make the result comparable to the result of ns_name_map().
836  */
837 int
838 ns_name_labels(ns_nname_ct nname, size_t namesiz) {
839         int ret = 0;
840         u_int n;
841
842         while (namesiz-- > 0 && (n = *nname++) != 0) {
843                 if ((n & NS_CMPRSFLGS) != 0) {
844                         errno = EISDIR;
845                         return (-1);
846                 }
847                 if (n > namesiz) {
848                         errno = EMSGSIZE;
849                         return (-1);
850                 }
851                 nname += n;
852                 namesiz -= n;
853                 ret++;
854         }
855         return (ret + 1);
856 }
857
858 /* Private. */
859
860 /*%
861  *      Thinking in noninternationalized USASCII (per the DNS spec),
862  *      is this characted special ("in need of quoting") ?
863  *
864  * return:
865  *\li   boolean.
866  */
867 static int
868 special(int ch) {
869         switch (ch) {
870         case 0x22: /*%< '"' */
871         case 0x2E: /*%< '.' */
872         case 0x3B: /*%< ';' */
873         case 0x5C: /*%< '\\' */
874         case 0x28: /*%< '(' */
875         case 0x29: /*%< ')' */
876         /* Special modifiers in zone files. */
877         case 0x40: /*%< '@' */
878         case 0x24: /*%< '$' */
879                 return (1);
880         default:
881                 return (0);
882         }
883 }
884
885 /*%
886  *      Thinking in noninternationalized USASCII (per the DNS spec),
887  *      is this character visible and not a space when printed ?
888  *
889  * return:
890  *\li   boolean.
891  */
892 static int
893 printable(int ch) {
894         return (ch > 0x20 && ch < 0x7f);
895 }
896
897 /*%
898  *      Thinking in noninternationalized USASCII (per the DNS spec),
899  *      convert this character to lower case if it's upper case.
900  */
901 static int
902 mklower(int ch) {
903         if (ch >= 0x41 && ch <= 0x5A)
904                 return (ch + 0x20);
905         return (ch);
906 }
907
908 /*%
909  *      Search for the counted-label name in an array of compressed names.
910  *
911  * return:
912  *\li   offset from msg if found, or -1.
913  *
914  * notes:
915  *\li   dnptrs is the pointer to the first name on the list,
916  *\li   not the pointer to the start of the message.
917  */
918 static int
919 dn_find(const u_char *domain, const u_char *msg,
920         const u_char * const *dnptrs,
921         const u_char * const *lastdnptr)
922 {
923         const u_char *dn, *cp, *sp;
924         const u_char * const *cpp;
925         u_int n;
926
927         for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
928                 sp = *cpp;
929                 /*
930                  * terminate search on:
931                  * root label
932                  * compression pointer
933                  * unusable offset
934                  */
935                 while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
936                        (sp - msg) < 0x4000) {
937                         dn = domain;
938                         cp = sp;
939                         while ((n = *cp++) != 0) {
940                                 /*
941                                  * check for indirection
942                                  */
943                                 switch (n & NS_CMPRSFLGS) {
944                                 case 0:         /*%< normal case, n == len */
945                                         n = labellen(cp - 1); /*%< XXX */
946                                         if (n != *dn++)
947                                                 goto next;
948
949                                         for ((void)NULL; n > 0; n--)
950                                                 if (mklower(*dn++) !=
951                                                     mklower(*cp++))
952                                                         goto next;
953                                         /* Is next root for both ? */
954                                         if (*dn == '\0' && *cp == '\0')
955                                                 return (sp - msg);
956                                         if (*dn)
957                                                 continue;
958                                         goto next;
959                                 case NS_CMPRSFLGS:      /*%< indirection */
960                                         cp = msg + (((n & 0x3f) << 8) | *cp);
961                                         break;
962
963                                 default:        /*%< illegal type */
964                                         errno = EMSGSIZE;
965                                         return (-1);
966                                 }
967                         }
968  next: ;
969                         sp += *sp + 1;
970                 }
971         }
972         errno = ENOENT;
973         return (-1);
974 }
975
976 static int
977 decode_bitstring(const unsigned char **cpp, char *dn, const char *eom)
978 {
979         const unsigned char *cp = *cpp;
980         char *beg = dn, tc;
981         int b, blen, plen, i;
982
983         if ((blen = (*cp & 0xff)) == 0)
984                 blen = 256;
985         plen = (blen + 3) / 4;
986         plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
987         if (dn + plen >= eom)
988                 return (-1);
989
990         cp++;
991         i = SPRINTF((dn, "\\[x"));
992         if (i < 0)
993                 return (-1);
994         dn += i;
995         for (b = blen; b > 7; b -= 8, cp++) {
996                 i = SPRINTF((dn, "%02x", *cp & 0xff));
997                 if (i < 0)
998                         return (-1);
999                 dn += i;
1000         }
1001         if (b > 4) {
1002                 tc = *cp++;
1003                 i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b))));
1004                 if (i < 0)
1005                         return (-1);
1006                 dn += i;
1007         } else if (b > 0) {
1008                 tc = *cp++;
1009                 i = SPRINTF((dn, "%1x",
1010                                ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); 
1011                 if (i < 0)
1012                         return (-1);
1013                 dn += i;
1014         }
1015         i = SPRINTF((dn, "/%d]", blen));
1016         if (i < 0)
1017                 return (-1);
1018         dn += i;
1019
1020         *cpp = cp;
1021         return (dn - beg);
1022 }
1023
1024 static int
1025 encode_bitsring(const char **bp, const char *end, unsigned char **labelp,
1026                 unsigned char ** dst, unsigned const char *eom)
1027 {
1028         int afterslash = 0;
1029         const char *cp = *bp;
1030         unsigned char *tp;
1031         char c;
1032         const char *beg_blen;
1033         char *end_blen = NULL;
1034         int value = 0, count = 0, tbcount = 0, blen = 0;
1035
1036         beg_blen = end_blen = NULL;
1037
1038         /* a bitstring must contain at least 2 characters */
1039         if (end - cp < 2)
1040                 return (EINVAL);
1041
1042         /* XXX: currently, only hex strings are supported */
1043         if (*cp++ != 'x')
1044                 return (EINVAL);
1045         if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */
1046                 return (EINVAL);
1047
1048         for (tp = *dst + 1; cp < end && tp < eom; cp++) {
1049                 switch((c = *cp)) {
1050                 case ']':       /*%< end of the bitstring */
1051                         if (afterslash) {
1052                                 if (beg_blen == NULL)
1053                                         return (EINVAL);
1054                                 blen = (int)strtol(beg_blen, &end_blen, 10);
1055                                 if (*end_blen != ']')
1056                                         return (EINVAL);
1057                         }
1058                         if (count)
1059                                 *tp++ = ((value << 4) & 0xff);
1060                         cp++;   /*%< skip ']' */
1061                         goto done;
1062                 case '/':
1063                         afterslash = 1;
1064                         break;
1065                 default:
1066                         if (afterslash) {
1067                                 if (!isdigit(c&0xff))
1068                                         return (EINVAL);
1069                                 if (beg_blen == NULL) {
1070                                         
1071                                         if (c == '0') {
1072                                                 /* blen never begings with 0 */
1073                                                 return (EINVAL);
1074                                         }
1075                                         beg_blen = cp;
1076                                 }
1077                         } else {
1078                                 if (!isxdigit(c&0xff))
1079                                         return (EINVAL);
1080                                 value <<= 4;
1081                                 value += digitvalue[(int)c];
1082                                 count += 4;
1083                                 tbcount += 4;
1084                                 if (tbcount > 256)
1085                                         return (EINVAL);
1086                                 if (count == 8) {
1087                                         *tp++ = value;
1088                                         count = 0;
1089                                 }
1090                         }
1091                         break;
1092                 }
1093         }
1094   done:
1095         if (cp >= end || tp >= eom)
1096                 return (EMSGSIZE);
1097
1098         /*
1099          * bit length validation:
1100          * If a <length> is present, the number of digits in the <bit-data>
1101          * MUST be just sufficient to contain the number of bits specified
1102          * by the <length>. If there are insignificant bits in a final
1103          * hexadecimal or octal digit, they MUST be zero.
1104          * RFC2673, Section 3.2.
1105          */
1106         if (blen > 0) {
1107                 int traillen;
1108
1109                 if (((blen + 3) & ~3) != tbcount)
1110                         return (EINVAL);
1111                 traillen = tbcount - blen; /*%< between 0 and 3 */
1112                 if (((value << (8 - traillen)) & 0xff) != 0)
1113                         return (EINVAL);
1114         }
1115         else
1116                 blen = tbcount;
1117         if (blen == 256)
1118                 blen = 0;
1119
1120         /* encode the type and the significant bit fields */
1121         **labelp = DNS_LABELTYPE_BITSTRING;
1122         **dst = blen;
1123
1124         *bp = cp;
1125         *dst = tp;
1126
1127         return (0);
1128 }
1129
1130 static int
1131 labellen(const u_char *lp)
1132 {
1133         int bitlen;
1134         u_char l = *lp;
1135
1136         if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
1137                 /* should be avoided by the caller */
1138                 return (-1);
1139         }
1140
1141         if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
1142                 if (l == DNS_LABELTYPE_BITSTRING) {
1143                         if ((bitlen = *(lp + 1)) == 0)
1144                                 bitlen = 256;
1145                         return ((bitlen + 7 ) / 8 + 1);
1146                 }
1147                 return (-1);    /*%< unknown ELT */
1148         }
1149         return (l);
1150 }
1151
1152 /*! \file */