Add test case for bug 20263
[glibc.git] / resolv / ns_name.c
blob65e7fc80791a6e2310c8ff267448167ebc6ed1a7
1 /*
2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1996,1999 by Internet Software Consortium.
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.
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.
18 #include <sys/types.h>
20 #include <netinet/in.h>
21 #include <arpa/nameser.h>
23 #include <errno.h>
24 #include <resolv.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <limits.h>
30 # define SPRINTF(x) ((size_t)sprintf x)
32 #define NS_TYPE_ELT 0x40 /*%< EDNS0 extended label type */
33 #define DNS_LABELTYPE_BITSTRING 0x41
35 /* Data. */
37 static const char digits[] = "0123456789";
39 static const char digitvalue[256] = {
40 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
41 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
42 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
43 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/
44 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
45 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
46 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
47 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
49 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
50 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
51 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
52 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
55 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
58 /* Forward. */
60 static int special(int);
61 static int printable(int);
62 static int dn_find(const u_char *, const u_char *,
63 const u_char * const *,
64 const u_char * const *);
65 static int encode_bitstring(const char **, const char *,
66 unsigned char **, unsigned char **,
67 unsigned const char *);
68 static int labellen(const u_char *);
69 static int decode_bitstring(const unsigned char **,
70 char *, const char *);
72 /* Public. */
74 /*%
75 * Convert an encoded domain name to printable ascii as per RFC1035.
77 * return:
78 *\li Number of bytes written to buffer, or -1 (with errno set)
80 * notes:
81 *\li The root is returned as "."
82 *\li All other domains are returned in non absolute form
84 int
85 ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
87 const u_char *cp;
88 char *dn, *eom;
89 u_char c;
90 u_int n;
91 int l;
93 cp = src;
94 dn = dst;
95 eom = dst + dstsiz;
97 while ((n = *cp++) != 0) {
98 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
99 /* Some kind of compression pointer. */
100 __set_errno (EMSGSIZE);
101 return (-1);
103 if (dn != dst) {
104 if (dn >= eom) {
105 __set_errno (EMSGSIZE);
106 return (-1);
108 *dn++ = '.';
110 if ((l = labellen(cp - 1)) < 0) {
111 __set_errno (EMSGSIZE);
112 return(-1);
114 if (dn + l >= eom) {
115 __set_errno (EMSGSIZE);
116 return (-1);
118 if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
119 int m;
121 if (n != DNS_LABELTYPE_BITSTRING) {
122 /* XXX: labellen should reject this case */
123 __set_errno (EINVAL);
124 return(-1);
126 if ((m = decode_bitstring(&cp, dn, eom)) < 0)
128 __set_errno (EMSGSIZE);
129 return(-1);
131 dn += m;
132 continue;
134 for ((void)NULL; l > 0; l--) {
135 c = *cp++;
136 if (special(c)) {
137 if (dn + 1 >= eom) {
138 __set_errno (EMSGSIZE);
139 return (-1);
141 *dn++ = '\\';
142 *dn++ = (char)c;
143 } else if (!printable(c)) {
144 if (dn + 3 >= eom) {
145 __set_errno (EMSGSIZE);
146 return (-1);
148 *dn++ = '\\';
149 *dn++ = digits[c / 100];
150 *dn++ = digits[(c % 100) / 10];
151 *dn++ = digits[c % 10];
152 } else {
153 if (dn >= eom) {
154 __set_errno (EMSGSIZE);
155 return (-1);
157 *dn++ = (char)c;
161 if (dn == dst) {
162 if (dn >= eom) {
163 __set_errno (EMSGSIZE);
164 return (-1);
166 *dn++ = '.';
168 if (dn >= eom) {
169 __set_errno (EMSGSIZE);
170 return (-1);
172 *dn++ = '\0';
173 return (dn - dst);
175 libresolv_hidden_def (ns_name_ntop)
176 strong_alias (ns_name_ntop, __ns_name_ntop)
179 * Convert an ascii string into an encoded domain name as per RFC1035.
181 * return:
183 *\li -1 if it fails
184 *\li 1 if string was fully qualified
185 *\li 0 is string was not fully qualified
187 * notes:
188 *\li Enforces label and domain length limits.
192 ns_name_pton(const char *src, u_char *dst, size_t dstsiz)
194 u_char *label, *bp, *eom;
195 int c, n, escaped, e = 0;
196 char *cp;
198 escaped = 0;
199 bp = dst;
200 eom = dst + dstsiz;
201 label = bp++;
203 while ((c = *src++) != 0) {
204 if (escaped) {
205 if (c == '[') { /*%< start a bit string label */
206 if ((cp = strchr(src, ']')) == NULL) {
207 __set_errno (EINVAL);
208 return(-1);
210 if ((e = encode_bitstring(&src, cp + 2,
211 &label, &bp, eom))
212 != 0) {
213 __set_errno (e);
214 return(-1);
216 escaped = 0;
217 label = bp++;
218 if ((c = *src++) == 0)
219 goto done;
220 else if (c != '.') {
221 __set_errno (EINVAL);
222 return(-1);
224 continue;
226 else if ((cp = strchr(digits, c)) != NULL) {
227 n = (cp - digits) * 100;
228 if ((c = *src++) == 0 ||
229 (cp = strchr(digits, c)) == NULL) {
230 __set_errno (EMSGSIZE);
231 return (-1);
233 n += (cp - digits) * 10;
234 if ((c = *src++) == 0 ||
235 (cp = strchr(digits, c)) == NULL) {
236 __set_errno (EMSGSIZE);
237 return (-1);
239 n += (cp - digits);
240 if (n > 255) {
241 __set_errno (EMSGSIZE);
242 return (-1);
244 c = n;
246 escaped = 0;
247 } else if (c == '\\') {
248 escaped = 1;
249 continue;
250 } else if (c == '.') {
251 c = (bp - label - 1);
252 if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */
253 __set_errno (EMSGSIZE);
254 return (-1);
256 if (label >= eom) {
257 __set_errno (EMSGSIZE);
258 return (-1);
260 *label = c;
261 /* Fully qualified ? */
262 if (*src == '\0') {
263 if (c != 0) {
264 if (bp >= eom) {
265 __set_errno (EMSGSIZE);
266 return (-1);
268 *bp++ = '\0';
270 if ((bp - dst) > MAXCDNAME) {
271 __set_errno (EMSGSIZE);
272 return (-1);
274 return (1);
276 if (c == 0 || *src == '.') {
277 __set_errno (EMSGSIZE);
278 return (-1);
280 label = bp++;
281 continue;
283 if (bp >= eom) {
284 __set_errno (EMSGSIZE);
285 return (-1);
287 *bp++ = (u_char)c;
289 c = (bp - label - 1);
290 if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */
291 __set_errno (EMSGSIZE);
292 return (-1);
294 done:
295 if (label >= eom) {
296 __set_errno (EMSGSIZE);
297 return (-1);
299 *label = c;
300 if (c != 0) {
301 if (bp >= eom) {
302 __set_errno (EMSGSIZE);
303 return (-1);
305 *bp++ = 0;
307 if ((bp - dst) > MAXCDNAME) { /*%< src too big */
308 __set_errno (EMSGSIZE);
309 return (-1);
311 return (0);
313 libresolv_hidden_def (ns_name_pton)
316 * Convert a network strings labels into all lowercase.
318 * return:
319 *\li Number of bytes written to buffer, or -1 (with errno set)
321 * notes:
322 *\li Enforces label and domain length limits.
326 ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
328 const u_char *cp;
329 u_char *dn, *eom;
330 u_char c;
331 u_int n;
332 int l;
334 cp = src;
335 dn = dst;
336 eom = dst + dstsiz;
338 if (dn >= eom) {
339 __set_errno (EMSGSIZE);
340 return (-1);
342 while ((n = *cp++) != 0) {
343 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
344 /* Some kind of compression pointer. */
345 __set_errno (EMSGSIZE);
346 return (-1);
348 *dn++ = n;
349 if ((l = labellen(cp - 1)) < 0) {
350 __set_errno (EMSGSIZE);
351 return (-1);
353 if (dn + l >= eom) {
354 __set_errno (EMSGSIZE);
355 return (-1);
357 for ((void)NULL; l > 0; l--) {
358 c = *cp++;
359 if (isupper(c))
360 *dn++ = tolower(c);
361 else
362 *dn++ = c;
365 *dn++ = '\0';
366 return (dn - dst);
370 * Unpack a domain name from a message, source may be compressed.
372 * return:
373 *\li -1 if it fails, or consumed octets if it succeeds.
376 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
377 u_char *dst, size_t dstsiz)
379 const u_char *srcp, *dstlim;
380 u_char *dstp;
381 int n, len, checked, l;
383 len = -1;
384 checked = 0;
385 dstp = dst;
386 srcp = src;
387 dstlim = dst + dstsiz;
388 if (srcp < msg || srcp >= eom) {
389 __set_errno (EMSGSIZE);
390 return (-1);
392 /* Fetch next label in domain name. */
393 while ((n = *srcp++) != 0) {
394 /* Check for indirection. */
395 switch (n & NS_CMPRSFLGS) {
396 case 0:
397 case NS_TYPE_ELT:
398 /* Limit checks. */
399 if ((l = labellen(srcp - 1)) < 0) {
400 __set_errno (EMSGSIZE);
401 return(-1);
403 if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
404 __set_errno (EMSGSIZE);
405 return (-1);
407 checked += l + 1;
408 *dstp++ = n;
409 memcpy(dstp, srcp, l);
410 dstp += l;
411 srcp += l;
412 break;
414 case NS_CMPRSFLGS:
415 if (srcp >= eom) {
416 __set_errno (EMSGSIZE);
417 return (-1);
419 if (len < 0)
420 len = srcp - src + 1;
421 srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
422 if (srcp < msg || srcp >= eom) { /*%< Out of range. */
423 __set_errno (EMSGSIZE);
424 return (-1);
426 checked += 2;
428 * Check for loops in the compressed name;
429 * if we've looked at the whole message,
430 * there must be a loop.
432 if (checked >= eom - msg) {
433 __set_errno (EMSGSIZE);
434 return (-1);
436 break;
438 default:
439 __set_errno (EMSGSIZE);
440 return (-1); /*%< flag error */
443 *dstp = '\0';
444 if (len < 0)
445 len = srcp - src;
446 return (len);
448 libresolv_hidden_def (ns_name_unpack)
449 strong_alias (ns_name_unpack, __ns_name_unpack)
452 * Pack domain name 'domain' into 'comp_dn'.
454 * return:
455 *\li Size of the compressed name, or -1.
457 * notes:
458 *\li 'dnptrs' is an array of pointers to previous compressed names.
459 *\li dnptrs[0] is a pointer to the beginning of the message. The array
460 * ends with NULL.
461 *\li 'lastdnptr' is a pointer to the end of the array pointed to
462 * by 'dnptrs'.
464 * Side effects:
465 *\li The list of pointers in dnptrs is updated for labels inserted into
466 * the message as we compress the name. If 'dnptr' is NULL, we don't
467 * try to compress names. If 'lastdnptr' is NULL, we don't update the
468 * list.
471 ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
472 const u_char **dnptrs, const u_char **lastdnptr)
474 u_char *dstp;
475 const u_char **cpp, **lpp, *eob, *msg;
476 const u_char *srcp;
477 int n, l, first = 1;
479 srcp = src;
480 dstp = dst;
481 eob = dstp + dstsiz;
482 lpp = cpp = NULL;
483 if (dnptrs != NULL) {
484 if ((msg = *dnptrs++) != NULL) {
485 for (cpp = dnptrs; *cpp != NULL; cpp++)
486 (void)NULL;
487 lpp = cpp; /*%< end of list to search */
489 } else
490 msg = NULL;
492 /* make sure the domain we are about to add is legal */
493 l = 0;
494 do {
495 int l0;
497 n = *srcp;
498 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
499 __set_errno (EMSGSIZE);
500 return (-1);
502 if ((l0 = labellen(srcp)) < 0) {
503 __set_errno (EINVAL);
504 return(-1);
506 l += l0 + 1;
507 if (l > MAXCDNAME) {
508 __set_errno (EMSGSIZE);
509 return (-1);
511 srcp += l0 + 1;
512 } while (n != 0);
514 /* from here on we need to reset compression pointer array on error */
515 srcp = src;
516 do {
517 /* Look to see if we can use pointers. */
518 n = *srcp;
519 if (n != 0 && msg != NULL) {
520 l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
521 (const u_char * const *)lpp);
522 if (l >= 0) {
523 if (dstp + 1 >= eob) {
524 goto cleanup;
526 *dstp++ = (l >> 8) | NS_CMPRSFLGS;
527 *dstp++ = l % 256;
528 return (dstp - dst);
530 /* Not found, save it. */
531 if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
532 (dstp - msg) < 0x4000 && first) {
533 *cpp++ = dstp;
534 *cpp = NULL;
535 first = 0;
538 /* copy label to buffer */
539 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
540 /* Should not happen. */
541 goto cleanup;
543 n = labellen(srcp);
544 if (dstp + 1 + n >= eob) {
545 goto cleanup;
547 memcpy(dstp, srcp, n + 1);
548 srcp += n + 1;
549 dstp += n + 1;
550 } while (n != 0);
552 if (dstp > eob) {
553 cleanup:
554 if (msg != NULL)
555 *lpp = NULL;
556 __set_errno (EMSGSIZE);
557 return (-1);
559 return (dstp - dst);
561 libresolv_hidden_def (ns_name_pack)
564 * Expand compressed domain name to presentation format.
566 * return:
567 *\li Number of bytes read out of `src', or -1 (with errno set).
569 * note:
570 *\li Root domain returns as "." not "".
573 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
574 char *dst, size_t dstsiz)
576 u_char tmp[NS_MAXCDNAME];
577 int n;
579 if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
580 return (-1);
581 if (ns_name_ntop(tmp, dst, dstsiz) == -1)
582 return (-1);
583 return (n);
585 libresolv_hidden_def (ns_name_uncompress)
588 * Compress a domain name into wire format, using compression pointers.
590 * return:
591 *\li Number of bytes consumed in `dst' or -1 (with errno set).
593 * notes:
594 *\li 'dnptrs' is an array of pointers to previous compressed names.
595 *\li dnptrs[0] is a pointer to the beginning of the message.
596 *\li The list ends with NULL. 'lastdnptr' is a pointer to the end of the
597 * array pointed to by 'dnptrs'. Side effect is to update the list of
598 * pointers for labels inserted into the message as we compress the name.
599 *\li If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
600 * is NULL, we don't update the list.
603 ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
604 const u_char **dnptrs, const u_char **lastdnptr)
606 u_char tmp[NS_MAXCDNAME];
608 if (ns_name_pton(src, tmp, sizeof tmp) == -1)
609 return (-1);
610 return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
612 libresolv_hidden_def (ns_name_compress)
615 * Reset dnptrs so that there are no active references to pointers at or
616 * after src.
618 void
619 ns_name_rollback(const u_char *src, const u_char **dnptrs,
620 const u_char **lastdnptr)
622 while (dnptrs < lastdnptr && *dnptrs != NULL) {
623 if (*dnptrs >= src) {
624 *dnptrs = NULL;
625 break;
627 dnptrs++;
632 * Advance *ptrptr to skip over the compressed name it points at.
634 * return:
635 *\li 0 on success, -1 (with errno set) on failure.
638 ns_name_skip(const u_char **ptrptr, const u_char *eom)
640 const u_char *cp;
641 u_int n;
642 int l;
644 cp = *ptrptr;
645 while (cp < eom && (n = *cp++) != 0) {
646 /* Check for indirection. */
647 switch (n & NS_CMPRSFLGS) {
648 case 0: /*%< normal case, n == len */
649 cp += n;
650 continue;
651 case NS_TYPE_ELT: /*%< EDNS0 extended label */
652 if ((l = labellen(cp - 1)) < 0) {
653 __set_errno (EMSGSIZE);
654 return(-1);
656 cp += l;
657 continue;
658 case NS_CMPRSFLGS: /*%< indirection */
659 cp++;
660 break;
661 default: /*%< illegal type */
662 __set_errno (EMSGSIZE);
663 return (-1);
665 break;
667 if (cp > eom) {
668 __set_errno (EMSGSIZE);
669 return (-1);
671 *ptrptr = cp;
672 return (0);
674 libresolv_hidden_def (ns_name_skip)
676 /* Private. */
679 * Thinking in noninternationalized USASCII (per the DNS spec),
680 * is this character special ("in need of quoting") ?
682 * return:
683 *\li boolean.
685 static int
686 special(int ch) {
687 switch (ch) {
688 case 0x22: /*%< '"' */
689 case 0x2E: /*%< '.' */
690 case 0x3B: /*%< ';' */
691 case 0x5C: /*%< '\\' */
692 case 0x28: /*%< '(' */
693 case 0x29: /*%< ')' */
694 /* Special modifiers in zone files. */
695 case 0x40: /*%< '@' */
696 case 0x24: /*%< '$' */
697 return (1);
698 default:
699 return (0);
704 * Thinking in noninternationalized USASCII (per the DNS spec),
705 * is this character visible and not a space when printed ?
707 * return:
708 *\li boolean.
710 static int
711 printable(int ch) {
712 return (ch > 0x20 && ch < 0x7f);
716 * Thinking in noninternationalized USASCII (per the DNS spec),
717 * convert this character to lower case if it's upper case.
719 static int
720 mklower(int ch) {
721 if (ch >= 0x41 && ch <= 0x5A)
722 return (ch + 0x20);
723 return (ch);
727 * Search for the counted-label name in an array of compressed names.
729 * return:
730 *\li offset from msg if found, or -1.
732 * notes:
733 *\li dnptrs is the pointer to the first name on the list,
734 *\li not the pointer to the start of the message.
736 static int
737 dn_find(const u_char *domain, const u_char *msg,
738 const u_char * const *dnptrs,
739 const u_char * const *lastdnptr)
741 const u_char *dn, *cp, *sp;
742 const u_char * const *cpp;
743 u_int n;
745 for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
746 sp = *cpp;
748 * terminate search on:
749 * root label
750 * compression pointer
751 * unusable offset
753 while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
754 (sp - msg) < 0x4000) {
755 dn = domain;
756 cp = sp;
757 while ((n = *cp++) != 0) {
759 * check for indirection
761 switch (n & NS_CMPRSFLGS) {
762 case 0: /*%< normal case, n == len */
763 n = labellen(cp - 1); /*%< XXX */
764 if (n != *dn++)
765 goto next;
767 for ((void)NULL; n > 0; n--)
768 if (mklower(*dn++) !=
769 mklower(*cp++))
770 goto next;
771 /* Is next root for both ? */
772 if (*dn == '\0' && *cp == '\0')
773 return (sp - msg);
774 if (*dn)
775 continue;
776 goto next;
777 case NS_CMPRSFLGS: /*%< indirection */
778 cp = msg + (((n & 0x3f) << 8) | *cp);
779 break;
781 default: /*%< illegal type */
782 __set_errno (EMSGSIZE);
783 return (-1);
786 next: ;
787 sp += *sp + 1;
790 __set_errno (ENOENT);
791 return (-1);
794 static int
795 decode_bitstring(const unsigned char **cpp, char *dn, const char *eom)
797 const unsigned char *cp = *cpp;
798 char *beg = dn, tc;
799 int b, blen, plen, i;
801 if ((blen = (*cp & 0xff)) == 0)
802 blen = 256;
803 plen = (blen + 3) / 4;
804 plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
805 if (dn + plen >= eom)
806 return(-1);
808 cp++;
809 i = SPRINTF((dn, "\\[x"));
810 if (i < 0)
811 return (-1);
812 dn += i;
813 for (b = blen; b > 7; b -= 8, cp++) {
814 i = SPRINTF((dn, "%02x", *cp & 0xff));
815 if (i < 0)
816 return (-1);
817 dn += i;
819 if (b > 4) {
820 tc = *cp++;
821 i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b))));
822 if (i < 0)
823 return (-1);
824 dn += i;
825 } else if (b > 0) {
826 tc = *cp++;
827 i = SPRINTF((dn, "%1x",
828 ((tc >> 4) & 0x0f) & (0x0f << (4 - b))));
829 if (i < 0)
830 return (-1);
831 dn += i;
833 i = SPRINTF((dn, "/%d]", blen));
834 if (i < 0)
835 return (-1);
836 dn += i;
838 *cpp = cp;
839 return(dn - beg);
842 static int
843 encode_bitstring(const char **bp, const char *end, unsigned char **labelp,
844 unsigned char ** dst, unsigned const char *eom)
846 int afterslash = 0;
847 const char *cp = *bp;
848 unsigned char *tp;
849 char c;
850 const char *beg_blen;
851 char *end_blen = NULL;
852 int value = 0, count = 0, tbcount = 0, blen = 0;
854 beg_blen = end_blen = NULL;
856 /* a bitstring must contain at least 2 characters */
857 if (end - cp < 2)
858 return(EINVAL);
860 /* XXX: currently, only hex strings are supported */
861 if (*cp++ != 'x')
862 return(EINVAL);
863 if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */
864 return(EINVAL);
866 for (tp = *dst + 1; cp < end && tp < eom; cp++) {
867 switch((c = *cp)) {
868 case ']': /*%< end of the bitstring */
869 if (afterslash) {
870 if (beg_blen == NULL)
871 return(EINVAL);
872 blen = (int)strtol(beg_blen, &end_blen, 10);
873 if (*end_blen != ']')
874 return(EINVAL);
876 if (count)
877 *tp++ = ((value << 4) & 0xff);
878 cp++; /*%< skip ']' */
879 goto done;
880 case '/':
881 afterslash = 1;
882 break;
883 default:
884 if (afterslash) {
885 if (!isdigit(c&0xff))
886 return(EINVAL);
887 if (beg_blen == NULL) {
889 if (c == '0') {
890 /* blen never begings with 0 */
891 return(EINVAL);
893 beg_blen = cp;
895 } else {
896 if (!isxdigit(c&0xff))
897 return(EINVAL);
898 value <<= 4;
899 value += digitvalue[(int)c];
900 count += 4;
901 tbcount += 4;
902 if (tbcount > 256)
903 return(EINVAL);
904 if (count == 8) {
905 *tp++ = value;
906 count = 0;
909 break;
912 done:
913 if (cp >= end || tp >= eom)
914 return(EMSGSIZE);
917 * bit length validation:
918 * If a <length> is present, the number of digits in the <bit-data>
919 * MUST be just sufficient to contain the number of bits specified
920 * by the <length>. If there are insignificant bits in a final
921 * hexadecimal or octal digit, they MUST be zero.
922 * RFC2673, Section 3.2.
924 if (blen > 0) {
925 int traillen;
927 if (((blen + 3) & ~3) != tbcount)
928 return(EINVAL);
929 traillen = tbcount - blen; /*%< between 0 and 3 */
930 if (((value << (8 - traillen)) & 0xff) != 0)
931 return(EINVAL);
933 else
934 blen = tbcount;
935 if (blen == 256)
936 blen = 0;
938 /* encode the type and the significant bit fields */
939 **labelp = DNS_LABELTYPE_BITSTRING;
940 **dst = blen;
942 *bp = cp;
943 *dst = tp;
945 return(0);
948 static int
949 labellen(const u_char *lp)
951 int bitlen;
952 u_char l = *lp;
954 if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
955 /* should be avoided by the caller */
956 return(-1);
959 if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
960 if (l == DNS_LABELTYPE_BITSTRING) {
961 if ((bitlen = *(lp + 1)) == 0)
962 bitlen = 256;
963 return((bitlen + 7 ) / 8 + 1);
965 return(-1); /*%< unknwon ELT */
967 return(l);
970 /*! \file */