9d409f3d95368b5d5fe88ed0c577606ab17f3f7a
[dragonfly.git] / lib / libc / nameser / ns_name.c
1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1996,1999 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
18 #ifndef lint
19 static const char rcsid[] = "$Id: ns_name.c,v 1.10 2005/04/27 04:56:40 sra Exp $";
20 #endif
21
22 #include "port_before.h"
23
24 #include <sys/types.h>
25
26 #include <netinet/in.h>
27 #include <arpa/nameser.h>
28
29 #include <errno.h>
30 #include <resolv.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <limits.h>
35
36 #include "port_after.h"
37
38 #ifdef SPRINTF_CHAR
39 # define SPRINTF(x) strlen(sprintf/**/x)
40 #else
41 # define SPRINTF(x) ((size_t)sprintf x)
42 #endif
43
44 #define NS_TYPE_ELT                     0x40 /*%< EDNS0 extended label type */
45 #define DNS_LABELTYPE_BITSTRING         0x41
46
47 /* Data. */
48
49 static const char       digits[] = "0123456789";
50
51 static const char digitvalue[256] = {
52         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
53         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
54         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
55          0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
56         -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
57         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
58         -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
59         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
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,
66         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
67         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
68 };
69
70 /* Forward. */
71
72 static int              special(int);
73 static int              printable(int);
74 static int              dn_find(const u_char *, const u_char *,
75                                 const u_char * const *,
76                                 const u_char * const *);
77 static int              encode_bitsring(const char **, const char *,
78                                         unsigned char **, unsigned char **,
79                                         unsigned const char *);
80 static int              labellen(const u_char *);
81 static int              decode_bitstring(const unsigned char **,
82                                          char *, const char *);
83
84 /* Public. */
85
86 /*%
87  *      Convert an encoded domain name to printable ascii as per RFC1035.
88
89  * return:
90  *\li   Number of bytes written to buffer, or -1 (with errno set)
91  *
92  * notes:
93  *\li   The root is returned as "."
94  *\li   All other domains are returned in non absolute form
95  */
96 int
97 ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
98 {
99         const u_char *cp;
100         char *dn, *eom;
101         u_char c;
102         u_int n;
103         int l;
104
105         cp = src;
106         dn = dst;
107         eom = dst + dstsiz;
108
109         while ((n = *cp++) != 0) {
110                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
111                         /* Some kind of compression pointer. */
112                         errno = EMSGSIZE;
113                         return (-1);
114                 }
115                 if (dn != dst) {
116                         if (dn >= eom) {
117                                 errno = EMSGSIZE;
118                                 return (-1);
119                         }
120                         *dn++ = '.';
121                 }
122                 if ((l = labellen(cp - 1)) < 0) {
123                         errno = EMSGSIZE; /*%< XXX */
124                         return(-1);
125                 }
126                 if (dn + l >= eom) {
127                         errno = EMSGSIZE;
128                         return (-1);
129                 }
130                 if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
131                         int m;
132
133                         if (n != DNS_LABELTYPE_BITSTRING) {
134                                 /* XXX: labellen should reject this case */
135                                 errno = EINVAL;
136                                 return(-1);
137                         }
138                         if ((m = decode_bitstring(&cp, dn, eom)) < 0)
139                         {
140                                 errno = EMSGSIZE;
141                                 return(-1);
142                         }
143                         dn += m; 
144                         continue;
145                 }
146                 for ((void)NULL; l > 0; l--) {
147                         c = *cp++;
148                         if (special(c)) {
149                                 if (dn + 1 >= eom) {
150                                         errno = EMSGSIZE;
151                                         return (-1);
152                                 }
153                                 *dn++ = '\\';
154                                 *dn++ = (char)c;
155                         } else if (!printable(c)) {
156                                 if (dn + 3 >= eom) {
157                                         errno = EMSGSIZE;
158                                         return (-1);
159                                 }
160                                 *dn++ = '\\';
161                                 *dn++ = digits[c / 100];
162                                 *dn++ = digits[(c % 100) / 10];
163                                 *dn++ = digits[c % 10];
164                         } else {
165                                 if (dn >= eom) {
166                                         errno = EMSGSIZE;
167                                         return (-1);
168                                 }
169                                 *dn++ = (char)c;
170                         }
171                 }
172         }
173         if (dn == dst) {
174                 if (dn >= eom) {
175                         errno = EMSGSIZE;
176                         return (-1);
177                 }
178                 *dn++ = '.';
179         }
180         if (dn >= eom) {
181                 errno = EMSGSIZE;
182                 return (-1);
183         }
184         *dn++ = '\0';
185         return (dn - dst);
186 }
187
188 /*%
189  *      Convert a ascii string into an encoded domain name as per RFC1035.
190  *
191  * return:
192  *
193  *\li   -1 if it fails
194  *\li   1 if string was fully qualified
195  *\li   0 is string was not fully qualified
196  *
197  * notes:
198  *\li   Enforces label and domain length limits.
199  */
200
201 int
202 ns_name_pton(const char *src, u_char *dst, size_t dstsiz)
203 {
204         u_char *label, *bp, *eom;
205         int c, n, escaped, e = 0;
206         char *cp;
207
208         escaped = 0;
209         bp = dst;
210         eom = dst + dstsiz;
211         label = bp++;
212
213         while ((c = *src++) != 0) {
214                 if (escaped) {
215                         if (c == '[') { /*%< start a bit string label */
216                                 if ((cp = strchr(src, ']')) == NULL) {
217                                         errno = EINVAL; /*%< ??? */
218                                         return(-1);
219                                 }
220                                 if ((e = encode_bitsring(&src, cp + 2,
221                                                          &label, &bp, eom))
222                                     != 0) {
223                                         errno = e;
224                                         return(-1);
225                                 }
226                                 escaped = 0;
227                                 label = bp++;
228                                 if ((c = *src++) == 0)
229                                         goto done;
230                                 else if (c != '.') {
231                                         errno = EINVAL;
232                                         return(-1);
233                                 }
234                                 continue;
235                         }
236                         else if ((cp = strchr(digits, c)) != NULL) {
237                                 n = (cp - digits) * 100;
238                                 if ((c = *src++) == 0 ||
239                                     (cp = strchr(digits, c)) == NULL) {
240                                         errno = EMSGSIZE;
241                                         return (-1);
242                                 }
243                                 n += (cp - digits) * 10;
244                                 if ((c = *src++) == 0 ||
245                                     (cp = strchr(digits, c)) == NULL) {
246                                         errno = EMSGSIZE;
247                                         return (-1);
248                                 }
249                                 n += (cp - digits);
250                                 if (n > 255) {
251                                         errno = EMSGSIZE;
252                                         return (-1);
253                                 }
254                                 c = n;
255                         }
256                         escaped = 0;
257                 } else if (c == '\\') {
258                         escaped = 1;
259                         continue;
260                 } else if (c == '.') {
261                         c = (bp - label - 1);
262                         if ((c & NS_CMPRSFLGS) != 0) {  /*%< Label too big. */
263                                 errno = EMSGSIZE;
264                                 return (-1);
265                         }
266                         if (label >= eom) {
267                                 errno = EMSGSIZE;
268                                 return (-1);
269                         }
270                         *label = c;
271                         /* Fully qualified ? */
272                         if (*src == '\0') {
273                                 if (c != 0) {
274                                         if (bp >= eom) {
275                                                 errno = EMSGSIZE;
276                                                 return (-1);
277                                         }
278                                         *bp++ = '\0';
279                                 }
280                                 if ((bp - dst) > MAXCDNAME) {
281                                         errno = EMSGSIZE;
282                                         return (-1);
283                                 }
284                                 return (1);
285                         }
286                         if (c == 0 || *src == '.') {
287                                 errno = EMSGSIZE;
288                                 return (-1);
289                         }
290                         label = bp++;
291                         continue;
292                 }
293                 if (bp >= eom) {
294                         errno = EMSGSIZE;
295                         return (-1);
296                 }
297                 *bp++ = (u_char)c;
298         }
299         c = (bp - label - 1);
300         if ((c & NS_CMPRSFLGS) != 0) {          /*%< Label too big. */
301                 errno = EMSGSIZE;
302                 return (-1);
303         }
304   done:
305         if (label >= eom) {
306                 errno = EMSGSIZE;
307                 return (-1);
308         }
309         *label = c;
310         if (c != 0) {
311                 if (bp >= eom) {
312                         errno = EMSGSIZE;
313                         return (-1);
314                 }
315                 *bp++ = 0;
316         }
317         if ((bp - dst) > MAXCDNAME) {   /*%< src too big */
318                 errno = EMSGSIZE;
319                 return (-1);
320         }
321         return (0);
322 }
323
324 /*%
325  *      Convert a network strings labels into all lowercase.
326  *
327  * return:
328  *\li   Number of bytes written to buffer, or -1 (with errno set)
329  *
330  * notes:
331  *\li   Enforces label and domain length limits.
332  */
333
334 int
335 ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
336 {
337         const u_char *cp;
338         u_char *dn, *eom;
339         u_char c;
340         u_int n;
341         int l;
342
343         cp = src;
344         dn = dst;
345         eom = dst + dstsiz;
346
347         if (dn >= eom) {
348                 errno = EMSGSIZE;
349                 return (-1);
350         }
351         while ((n = *cp++) != 0) {
352                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
353                         /* Some kind of compression pointer. */
354                         errno = EMSGSIZE;
355                         return (-1);
356                 }
357                 *dn++ = n;
358                 if ((l = labellen(cp - 1)) < 0) {
359                         errno = EMSGSIZE;
360                         return (-1);
361                 }
362                 if (dn + l >= eom) {
363                         errno = EMSGSIZE;
364                         return (-1);
365                 }
366                 for ((void)NULL; l > 0; l--) {
367                         c = *cp++;
368                         if (isupper(c))
369                                 *dn++ = tolower(c);
370                         else
371                                 *dn++ = c;
372                 }
373         }
374         *dn++ = '\0';
375         return (dn - dst);
376 }
377
378 /*%
379  *      Unpack a domain name from a message, source may be compressed.
380  *
381  * return:
382  *\li   -1 if it fails, or consumed octets if it succeeds.
383  */
384 int
385 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
386                u_char *dst, size_t dstsiz)
387 {
388         const u_char *srcp, *dstlim;
389         u_char *dstp;
390         int n, len, checked, l;
391
392         len = -1;
393         checked = 0;
394         dstp = dst;
395         srcp = src;
396         dstlim = dst + dstsiz;
397         if (srcp < msg || srcp >= eom) {
398                 errno = EMSGSIZE;
399                 return (-1);
400         }
401         /* Fetch next label in domain name. */
402         while ((n = *srcp++) != 0) {
403                 /* Check for indirection. */
404                 switch (n & NS_CMPRSFLGS) {
405                 case 0:
406                 case NS_TYPE_ELT:
407                         /* Limit checks. */
408                         if ((l = labellen(srcp - 1)) < 0) {
409                                 errno = EMSGSIZE;
410                                 return(-1);
411                         }
412                         if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
413                                 errno = EMSGSIZE;
414                                 return (-1);
415                         }
416                         checked += l + 1;
417                         *dstp++ = n;
418                         memcpy(dstp, srcp, l);
419                         dstp += l;
420                         srcp += l;
421                         break;
422
423                 case NS_CMPRSFLGS:
424                         if (srcp >= eom) {
425                                 errno = EMSGSIZE;
426                                 return (-1);
427                         }
428                         if (len < 0)
429                                 len = srcp - src + 1;
430                         srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
431                         if (srcp < msg || srcp >= eom) {  /*%< Out of range. */
432                                 errno = EMSGSIZE;
433                                 return (-1);
434                         }
435                         checked += 2;
436                         /*
437                          * Check for loops in the compressed name;
438                          * if we've looked at the whole message,
439                          * there must be a loop.
440                          */
441                         if (checked >= eom - msg) {
442                                 errno = EMSGSIZE;
443                                 return (-1);
444                         }
445                         break;
446
447                 default:
448                         errno = EMSGSIZE;
449                         return (-1);                    /*%< flag error */
450                 }
451         }
452         *dstp = '\0';
453         if (len < 0)
454                 len = srcp - src;
455         return (len);
456 }
457
458 /*%
459  *      Pack domain name 'domain' into 'comp_dn'.
460  *
461  * return:
462  *\li   Size of the compressed name, or -1.
463  *
464  * notes:
465  *\li   'dnptrs' is an array of pointers to previous compressed names.
466  *\li   dnptrs[0] is a pointer to the beginning of the message. The array
467  *      ends with NULL.
468  *\li   'lastdnptr' is a pointer to the end of the array pointed to
469  *      by 'dnptrs'.
470  *
471  * Side effects:
472  *\li   The list of pointers in dnptrs is updated for labels inserted into
473  *      the message as we compress the name.  If 'dnptr' is NULL, we don't
474  *      try to compress names. If 'lastdnptr' is NULL, we don't update the
475  *      list.
476  */
477 int
478 ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
479              const u_char **dnptrs, const u_char **lastdnptr)
480 {
481         u_char *dstp;
482         const u_char **cpp, **lpp, *eob, *msg;
483         const u_char *srcp;
484         int n, l, first = 1;
485
486         srcp = src;
487         dstp = dst;
488         eob = dstp + dstsiz;
489         lpp = cpp = NULL;
490         if (dnptrs != NULL) {
491                 if ((msg = *dnptrs++) != NULL) {
492                         for (cpp = dnptrs; *cpp != NULL; cpp++)
493                                 (void)NULL;
494                         lpp = cpp;      /*%< end of list to search */
495                 }
496         } else
497                 msg = NULL;
498
499         /* make sure the domain we are about to add is legal */
500         l = 0;
501         do {
502                 int l0;
503
504                 n = *srcp;
505                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
506                         errno = EMSGSIZE;
507                         return (-1);
508                 }
509                 if ((l0 = labellen(srcp)) < 0) {
510                         errno = EINVAL;
511                         return(-1);
512                 }
513                 l += l0 + 1;
514                 if (l > MAXCDNAME) {
515                         errno = EMSGSIZE;
516                         return (-1);
517                 }
518                 srcp += l0 + 1;
519         } while (n != 0);
520
521         /* from here on we need to reset compression pointer array on error */
522         srcp = src;
523         do {
524                 /* Look to see if we can use pointers. */
525                 n = *srcp;
526                 if (n != 0 && msg != NULL) {
527                         l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
528                                     (const u_char * const *)lpp);
529                         if (l >= 0) {
530                                 if (dstp + 1 >= eob) {
531                                         goto cleanup;
532                                 }
533                                 *dstp++ = (l >> 8) | NS_CMPRSFLGS;
534                                 *dstp++ = l % 256;
535                                 return (dstp - dst);
536                         }
537                         /* Not found, save it. */
538                         if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
539                             (dstp - msg) < 0x4000 && first) {
540                                 *cpp++ = dstp;
541                                 *cpp = NULL;
542                                 first = 0;
543                         }
544                 }
545                 /* copy label to buffer */
546                 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
547                         /* Should not happen. */
548                         goto cleanup;
549                 }
550                 n = labellen(srcp);
551                 if (dstp + 1 + n >= eob) {
552                         goto cleanup;
553                 }
554                 memcpy(dstp, srcp, n + 1);
555                 srcp += n + 1;
556                 dstp += n + 1;
557         } while (n != 0);
558
559         if (dstp > eob) {
560 cleanup:
561                 if (msg != NULL)
562                         *lpp = NULL;
563                 errno = EMSGSIZE;
564                 return (-1);
565         } 
566         return (dstp - dst);
567 }
568
569 /*%
570  *      Expand compressed domain name to presentation format.
571  *
572  * return:
573  *\li   Number of bytes read out of `src', or -1 (with errno set).
574  *
575  * note:
576  *\li   Root domain returns as "." not "".
577  */
578 int
579 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
580                    char *dst, size_t dstsiz)
581 {
582         u_char tmp[NS_MAXCDNAME];
583         int n;
584         
585         if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
586                 return (-1);
587         if (ns_name_ntop(tmp, dst, dstsiz) == -1)
588                 return (-1);
589         return (n);
590 }
591
592 /*%
593  *      Compress a domain name into wire format, using compression pointers.
594  *
595  * return:
596  *\li   Number of bytes consumed in `dst' or -1 (with errno set).
597  *
598  * notes:
599  *\li   'dnptrs' is an array of pointers to previous compressed names.
600  *\li   dnptrs[0] is a pointer to the beginning of the message.
601  *\li   The list ends with NULL.  'lastdnptr' is a pointer to the end of the
602  *      array pointed to by 'dnptrs'. Side effect is to update the list of
603  *      pointers for labels inserted into the message as we compress the name.
604  *\li   If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
605  *      is NULL, we don't update the list.
606  */
607 int
608 ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
609                  const u_char **dnptrs, const u_char **lastdnptr)
610 {
611         u_char tmp[NS_MAXCDNAME];
612
613         if (ns_name_pton(src, tmp, sizeof tmp) == -1)
614                 return (-1);
615         return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
616 }
617
618 /*%
619  * Reset dnptrs so that there are no active references to pointers at or
620  * after src.
621  */
622 void
623 ns_name_rollback(const u_char *src, const u_char **dnptrs,
624                  const u_char **lastdnptr)
625 {
626         while (dnptrs < lastdnptr && *dnptrs != NULL) {
627                 if (*dnptrs >= src) {
628                         *dnptrs = NULL;
629                         break;
630                 }
631                 dnptrs++;
632         }
633 }
634
635 /*%
636  *      Advance *ptrptr to skip over the compressed name it points at.
637  *
638  * return:
639  *\li   0 on success, -1 (with errno set) on failure.
640  */
641 int
642 ns_name_skip(const u_char **ptrptr, const u_char *eom)
643 {
644         const u_char *cp;
645         u_int n;
646         int l;
647
648         cp = *ptrptr;
649         while (cp < eom && (n = *cp++) != 0) {
650                 /* Check for indirection. */
651                 switch (n & NS_CMPRSFLGS) {
652                 case 0:                 /*%< normal case, n == len */
653                         cp += n;
654                         continue;
655                 case NS_TYPE_ELT: /*%< EDNS0 extended label */
656                         if ((l = labellen(cp - 1)) < 0) {
657                                 errno = EMSGSIZE; /*%< XXX */
658                                 return(-1);
659                         }
660                         cp += l;
661                         continue;
662                 case NS_CMPRSFLGS:      /*%< indirection */
663                         cp++;
664                         break;
665                 default:                /*%< illegal type */
666                         errno = EMSGSIZE;
667                         return (-1);
668                 }
669                 break;
670         }
671         if (cp > eom) {
672                 errno = EMSGSIZE;
673                 return (-1);
674         }
675         *ptrptr = cp;
676         return (0);
677 }
678
679 /* Private. */
680
681 /*%
682  *      Thinking in noninternationalized USASCII (per the DNS spec),
683  *      is this characted special ("in need of quoting") ?
684  *
685  * return:
686  *\li   boolean.
687  */
688 static int
689 special(int ch) {
690         switch (ch) {
691         case 0x22: /*%< '"' */
692         case 0x2E: /*%< '.' */
693         case 0x3B: /*%< ';' */
694         case 0x5C: /*%< '\\' */
695         case 0x28: /*%< '(' */
696         case 0x29: /*%< ')' */
697         /* Special modifiers in zone files. */
698         case 0x40: /*%< '@' */
699         case 0x24: /*%< '$' */
700                 return (1);
701         default:
702                 return (0);
703         }
704 }
705
706 /*%
707  *      Thinking in noninternationalized USASCII (per the DNS spec),
708  *      is this character visible and not a space when printed ?
709  *
710  * return:
711  *\li   boolean.
712  */
713 static int
714 printable(int ch) {
715         return (ch > 0x20 && ch < 0x7f);
716 }
717
718 /*%
719  *      Thinking in noninternationalized USASCII (per the DNS spec),
720  *      convert this character to lower case if it's upper case.
721  */
722 static int
723 mklower(int ch) {
724         if (ch >= 0x41 && ch <= 0x5A)
725                 return (ch + 0x20);
726         return (ch);
727 }
728
729 /*%
730  *      Search for the counted-label name in an array of compressed names.
731  *
732  * return:
733  *\li   offset from msg if found, or -1.
734  *
735  * notes:
736  *\li   dnptrs is the pointer to the first name on the list,
737  *\li   not the pointer to the start of the message.
738  */
739 static int
740 dn_find(const u_char *domain, const u_char *msg,
741         const u_char * const *dnptrs,
742         const u_char * const *lastdnptr)
743 {
744         const u_char *dn, *cp, *sp;
745         const u_char * const *cpp;
746         u_int n;
747
748         for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
749                 sp = *cpp;
750                 /*
751                  * terminate search on:
752                  * root label
753                  * compression pointer
754                  * unusable offset
755                  */
756                 while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
757                        (sp - msg) < 0x4000) {
758                         dn = domain;
759                         cp = sp;
760                         while ((n = *cp++) != 0) {
761                                 /*
762                                  * check for indirection
763                                  */
764                                 switch (n & NS_CMPRSFLGS) {
765                                 case 0:         /*%< normal case, n == len */
766                                         n = labellen(cp - 1); /*%< XXX */
767                                         if (n != *dn++)
768                                                 goto next;
769
770                                         for ((void)NULL; n > 0; n--)
771                                                 if (mklower(*dn++) !=
772                                                     mklower(*cp++))
773                                                         goto next;
774                                         /* Is next root for both ? */
775                                         if (*dn == '\0' && *cp == '\0')
776                                                 return (sp - msg);
777                                         if (*dn)
778                                                 continue;
779                                         goto next;
780                                 case NS_CMPRSFLGS:      /*%< indirection */
781                                         cp = msg + (((n & 0x3f) << 8) | *cp);
782                                         break;
783
784                                 default:        /*%< illegal type */
785                                         errno = EMSGSIZE;
786                                         return (-1);
787                                 }
788                         }
789  next: ;
790                         sp += *sp + 1;
791                 }
792         }
793         errno = ENOENT;
794         return (-1);
795 }
796
797 static int
798 decode_bitstring(const unsigned char **cpp, char *dn, const char *eom)
799 {
800         const unsigned char *cp = *cpp;
801         char *beg = dn, tc;
802         int b, blen, plen, i;
803
804         if ((blen = (*cp & 0xff)) == 0)
805                 blen = 256;
806         plen = (blen + 3) / 4;
807         plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
808         if (dn + plen >= eom)
809                 return(-1);
810
811         cp++;
812         i = SPRINTF((dn, "\\[x"));
813         if (i < 0)
814                 return (-1);
815         dn += i;
816         for (b = blen; b > 7; b -= 8, cp++) {
817                 i = SPRINTF((dn, "%02x", *cp & 0xff));
818                 if (i < 0)
819                         return (-1);
820                 dn += i;
821         }
822         if (b > 4) {
823                 tc = *cp++;
824                 i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b))));
825                 if (i < 0)
826                         return (-1);
827                 dn += i;
828         } else if (b > 0) {
829                 tc = *cp++;
830                 i = SPRINTF((dn, "%1x",
831                                ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); 
832                 if (i < 0)
833                         return (-1);
834                 dn += i;
835         }
836         i = SPRINTF((dn, "/%d]", blen));
837         if (i < 0)
838                 return (-1);
839         dn += i;
840
841         *cpp = cp;
842         return(dn - beg);
843 }
844
845 static int
846 encode_bitsring(const char **bp, const char *end, unsigned char **labelp,
847                 unsigned char ** dst, unsigned const char *eom)
848 {
849         int afterslash = 0;
850         const char *cp = *bp;
851         unsigned char *tp;
852         char c;
853         const char *beg_blen;
854         char *end_blen = NULL;
855         int value = 0, count = 0, tbcount = 0, blen = 0;
856
857         beg_blen = end_blen = NULL;
858
859         /* a bitstring must contain at least 2 characters */
860         if (end - cp < 2)
861                 return(EINVAL);
862
863         /* XXX: currently, only hex strings are supported */
864         if (*cp++ != 'x')
865                 return(EINVAL);
866         if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */
867                 return(EINVAL);
868
869         for (tp = *dst + 1; cp < end && tp < eom; cp++) {
870                 switch((c = *cp)) {
871                 case ']':       /*%< end of the bitstring */
872                         if (afterslash) {
873                                 if (beg_blen == NULL)
874                                         return(EINVAL);
875                                 blen = (int)strtol(beg_blen, &end_blen, 10);
876                                 if (*end_blen != ']')
877                                         return(EINVAL);
878                         }
879                         if (count)
880                                 *tp++ = ((value << 4) & 0xff);
881                         cp++;   /*%< skip ']' */
882                         goto done;
883                 case '/':
884                         afterslash = 1;
885                         break;
886                 default:
887                         if (afterslash) {
888                                 if (!isdigit(c&0xff))
889                                         return(EINVAL);
890                                 if (beg_blen == NULL) {
891                                         
892                                         if (c == '0') {
893                                                 /* blen never begings with 0 */
894                                                 return(EINVAL);
895                                         }
896                                         beg_blen = cp;
897                                 }
898                         } else {
899                                 if (!isxdigit(c&0xff))
900                                         return(EINVAL);
901                                 value <<= 4;
902                                 value += digitvalue[(int)c];
903                                 count += 4;
904                                 tbcount += 4;
905                                 if (tbcount > 256)
906                                         return(EINVAL);
907                                 if (count == 8) {
908                                         *tp++ = value;
909                                         count = 0;
910                                 }
911                         }
912                         break;
913                 }
914         }
915   done:
916         if (cp >= end || tp >= eom)
917                 return(EMSGSIZE);
918
919         /*
920          * bit length validation:
921          * If a <length> is present, the number of digits in the <bit-data>
922          * MUST be just sufficient to contain the number of bits specified
923          * by the <length>. If there are insignificant bits in a final
924          * hexadecimal or octal digit, they MUST be zero.
925          * RFC2673, Section 3.2.
926          */
927         if (blen > 0) {
928                 int traillen;
929
930                 if (((blen + 3) & ~3) != tbcount)
931                         return(EINVAL);
932                 traillen = tbcount - blen; /*%< between 0 and 3 */
933                 if (((value << (8 - traillen)) & 0xff) != 0)
934                         return(EINVAL);
935         }
936         else
937                 blen = tbcount;
938         if (blen == 256)
939                 blen = 0;
940
941         /* encode the type and the significant bit fields */
942         **labelp = DNS_LABELTYPE_BITSTRING;
943         **dst = blen;
944
945         *bp = cp;
946         *dst = tp;
947
948         return(0);
949 }
950
951 static int
952 labellen(const u_char *lp)
953 {
954         int bitlen;
955         u_char l = *lp;
956
957         if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
958                 /* should be avoided by the caller */
959                 return(-1);
960         }
961
962         if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
963                 if (l == DNS_LABELTYPE_BITSTRING) {
964                         if ((bitlen = *(lp + 1)) == 0)
965                                 bitlen = 256;
966                         return((bitlen + 7 ) / 8 + 1);
967                 }
968                 return(-1);     /*%< unknwon ELT */
969         }
970         return(l);
971 }
972
973 /*! \file */