1 /* $NetBSD: ns_name.c,v 1.9 2012/03/13 21:13:39 christos Exp $ */
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1996,1999 by Internet Software Consortium.
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.
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.
21 static const char rcsid
[] = "$Id: ns_name.c,v 1.11 2009/01/23 19:59:16 each Exp $";
24 #include "port_before.h"
26 #include <sys/types.h>
28 #include <netinet/in.h>
29 #include <arpa/nameser.h>
39 #include "port_after.h"
42 # define SPRINTF(x) ((int)strlen(sprintf/**/x))
44 # define SPRINTF(x) (sprintf x)
47 #define NS_TYPE_ELT 0x40 /*%< EDNS0 extended label type */
48 #define DNS_LABELTYPE_BITSTRING 0x41
52 static const char digits
[] = "0123456789";
54 static const char digitvalue
[256] = {
55 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
56 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
57 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
58 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/
59 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
60 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
61 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
62 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
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,
68 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
70 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
75 static int special(int);
76 static int printable(int);
77 static int dn_find(const u_char
*, const u_char
*,
78 const u_char
* const *,
79 const u_char
* const *);
80 static int encode_bitsring(const char **, const char *,
81 unsigned char **, unsigned char **,
82 unsigned const char *);
83 static int labellen(const u_char
*);
84 static int decode_bitstring(const unsigned char **,
85 char *, const char *);
90 * Convert an encoded domain name to printable ascii as per RFC1035.
93 *\li Number of bytes written to buffer, or -1 (with errno set)
96 *\li The root is returned as "."
97 *\li All other domains are returned in non absolute form
100 ns_name_ntop(const u_char
*src
, char *dst
, size_t dstsiz
)
112 while ((n
= *cp
++) != 0) {
113 if ((n
& NS_CMPRSFLGS
) == NS_CMPRSFLGS
) {
114 /* Some kind of compression pointer. */
125 if ((l
= labellen(cp
- 1)) < 0) {
126 errno
= EMSGSIZE
; /*%< XXX */
133 if ((n
& NS_CMPRSFLGS
) == NS_TYPE_ELT
) {
136 if (n
!= DNS_LABELTYPE_BITSTRING
) {
137 /* XXX: labellen should reject this case */
141 if ((m
= decode_bitstring(&cp
, dn
, eom
)) < 0)
158 } else if (!printable(c
)) {
164 *dn
++ = digits
[c
/ 100];
165 *dn
++ = digits
[(c
% 100) / 10];
166 *dn
++ = digits
[c
% 10];
188 assert(INT_MIN
<= (dn
- dst
) && (dn
- dst
) <= INT_MAX
);
189 return (int)(dn
- dst
);
193 * Convert a ascii string into an encoded domain name as per RFC1035.
198 *\li 1 if string was fully qualified
199 *\li 0 is string was not fully qualified
202 *\li Enforces label and domain length limits.
205 ns_name_pton(const char *src
, u_char
*dst
, size_t dstsiz
) {
206 return (ns_name_pton2(src
, dst
, dstsiz
, NULL
));
210 * ns_name_pton2(src, dst, dstsiz, *dstlen)
211 * Convert a ascii string into an encoded domain name as per RFC1035.
214 * 1 if string was fully qualified
215 * 0 is string was not fully qualified
217 * fills in *dstlen (if non-NULL)
219 * Enforces label and domain length limits.
222 ns_name_pton2(const char *src
, u_char
*dst
, size_t dstsiz
, size_t *dstlen
) {
223 u_char
*label
, *bp
, *eom
;
224 int c
, n
, escaped
, e
= 0;
232 while ((c
= *src
++) != 0) {
234 if (c
== '[') { /*%< start a bit string label */
235 if ((cp
= strchr(src
, ']')) == NULL
) {
236 errno
= EINVAL
; /*%< ??? */
239 if ((e
= encode_bitsring(&src
, cp
+ 2,
247 if ((c
= *src
++) == 0)
255 else if ((cp
= strchr(digits
, c
)) != NULL
) {
256 n
= (int)(cp
- digits
) * 100;
257 if ((c
= *src
++) == 0 ||
258 (cp
= strchr(digits
, c
)) == NULL
) {
262 n
+= (int)(cp
- digits
) * 10;
263 if ((c
= *src
++) == 0 ||
264 (cp
= strchr(digits
, c
)) == NULL
) {
268 n
+= (int)(cp
- digits
);
276 } else if (c
== '\\') {
279 } else if (c
== '.') {
280 c
= (int)(bp
- label
- 1);
281 if ((c
& NS_CMPRSFLGS
) != 0) { /*%< Label too big. */
290 /* Fully qualified ? */
299 if ((bp
- dst
) > MAXCDNAME
) {
304 *dstlen
= (bp
- dst
);
307 if (c
== 0 || *src
== '.') {
320 c
= (int)(bp
- label
- 1);
321 if ((c
& NS_CMPRSFLGS
) != 0) { /*%< Label too big. */
338 if ((bp
- dst
) > MAXCDNAME
) { /*%< src too big */
343 *dstlen
= (bp
- dst
);
348 * Convert a network strings labels into all lowercase.
351 *\li Number of bytes written to buffer, or -1 (with errno set)
354 *\li Enforces label and domain length limits.
358 ns_name_ntol(const u_char
*src
, u_char
*dst
, size_t dstsiz
)
374 while ((n
= *cp
++) != 0) {
375 if ((n
& NS_CMPRSFLGS
) == NS_CMPRSFLGS
) {
376 /* Some kind of compression pointer. */
381 if ((l
= labellen(cp
- 1)) < 0) {
391 if (isascii(c
) && isupper(c
))
398 assert(INT_MIN
<= (dn
- dst
) && (dn
- dst
) <= INT_MAX
);
399 return (int)(dn
- dst
);
403 * Unpack a domain name from a message, source may be compressed.
406 *\li -1 if it fails, or consumed octets if it succeeds.
409 ns_name_unpack(const u_char
*msg
, const u_char
*eom
, const u_char
*src
,
410 u_char
*dst
, size_t dstsiz
)
412 return (ns_name_unpack2(msg
, eom
, src
, dst
, dstsiz
, NULL
));
416 * ns_name_unpack2(msg, eom, src, dst, dstsiz, *dstlen)
417 * Unpack a domain name from a message, source may be compressed.
419 * -1 if it fails, or consumed octets if it succeeds.
421 * fills in *dstlen (if non-NULL).
424 ns_name_unpack2(const u_char
*msg
, const u_char
*eom
, const u_char
*src
,
425 u_char
*dst
, size_t dstsiz
, size_t *dstlen
)
427 const u_char
*srcp
, *dstlim
;
429 int n
, len
, checked
, l
;
435 dstlim
= dst
+ dstsiz
;
436 if (srcp
< msg
|| srcp
>= eom
) {
440 /* Fetch next label in domain name. */
441 while ((n
= *srcp
++) != 0) {
442 /* Check for indirection. */
443 switch (n
& NS_CMPRSFLGS
) {
447 if ((l
= labellen(srcp
- 1)) < 0) {
451 if (dstp
+ l
+ 1 >= dstlim
|| srcp
+ l
>= eom
) {
457 memcpy(dstp
, srcp
, (size_t)l
);
468 assert(INT_MIN
<= (srcp
- src
+ 1) && (srcp
- src
+ 1) <= INT_MAX
);
469 len
= (int)(srcp
- src
+ 1);
471 srcp
= msg
+ (((n
& 0x3f) << 8) | (*srcp
& 0xff));
472 if (srcp
< msg
|| srcp
>= eom
) { /*%< Out of range. */
478 * Check for loops in the compressed name;
479 * if we've looked at the whole message,
480 * there must be a loop.
482 if (checked
>= eom
- msg
) {
490 return (-1); /*%< flag error */
495 *dstlen
= dstp
- dst
;
497 assert(INT_MIN
<= (srcp
- src
) && (srcp
- src
) <= INT_MAX
);
498 len
= (int)(srcp
- src
);
504 * Pack domain name 'domain' into 'comp_dn'.
507 *\li Size of the compressed name, or -1.
510 *\li 'dnptrs' is an array of pointers to previous compressed names.
511 *\li dnptrs[0] is a pointer to the beginning of the message. The array
513 *\li 'lastdnptr' is a pointer to the end of the array pointed to
517 *\li The list of pointers in dnptrs is updated for labels inserted into
518 * the message as we compress the name. If 'dnptr' is NULL, we don't
519 * try to compress names. If 'lastdnptr' is NULL, we don't update the
523 ns_name_pack(const u_char
*src
, u_char
*dst
, int dstsiz
,
524 const u_char
**dnptrs
, const u_char
**lastdnptr
)
527 const u_char
**cpp
, **lpp
, *eob
, *msg
;
535 if (dnptrs
!= NULL
) {
536 if ((msg
= *dnptrs
++) != NULL
) {
537 for (cpp
= dnptrs
; *cpp
!= NULL
; cpp
++)
539 lpp
= cpp
; /*%< end of list to search */
544 /* make sure the domain we are about to add is legal */
550 if ((n
& NS_CMPRSFLGS
) == NS_CMPRSFLGS
) {
554 if ((l0
= labellen(srcp
)) < 0) {
566 /* from here on we need to reset compression pointer array on error */
569 /* Look to see if we can use pointers. */
571 if (n
!= 0 && msg
!= NULL
) {
572 l
= dn_find(srcp
, msg
, (const u_char
* const *)dnptrs
,
573 (const u_char
* const *)lpp
);
575 if (dstp
+ 1 >= eob
) {
578 *dstp
++ = ((u_int32_t
)l
>> 8) | NS_CMPRSFLGS
;
580 assert(INT_MIN
<= (dstp
- dst
) && (dstp
- dst
) <= INT_MAX
);
581 return (int)(dstp
- dst
);
583 /* Not found, save it. */
584 if (lastdnptr
!= NULL
&& cpp
< lastdnptr
- 1 &&
585 (dstp
- msg
) < 0x4000 && first
) {
591 /* copy label to buffer */
592 if ((n
& NS_CMPRSFLGS
) == NS_CMPRSFLGS
) {
593 /* Should not happen. */
597 if (dstp
+ 1 + n
>= eob
) {
600 memcpy(dstp
, srcp
, (size_t)(n
+ 1));
612 assert(INT_MIN
<= (dstp
- dst
) && (dstp
- dst
) <= INT_MAX
);
613 return (int)(dstp
- dst
);
617 * Expand compressed domain name to presentation format.
620 *\li Number of bytes read out of `src', or -1 (with errno set).
623 *\li Root domain returns as "." not "".
626 ns_name_uncompress(const u_char
*msg
, const u_char
*eom
, const u_char
*src
,
627 char *dst
, size_t dstsiz
)
629 u_char tmp
[NS_MAXCDNAME
];
632 if ((n
= ns_name_unpack(msg
, eom
, src
, tmp
, sizeof tmp
)) == -1)
634 if (ns_name_ntop(tmp
, dst
, dstsiz
) == -1)
640 * Compress a domain name into wire format, using compression pointers.
643 *\li Number of bytes consumed in `dst' or -1 (with errno set).
646 *\li 'dnptrs' is an array of pointers to previous compressed names.
647 *\li dnptrs[0] is a pointer to the beginning of the message.
648 *\li The list ends with NULL. 'lastdnptr' is a pointer to the end of the
649 * array pointed to by 'dnptrs'. Side effect is to update the list of
650 * pointers for labels inserted into the message as we compress the name.
651 *\li If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
652 * is NULL, we don't update the list.
655 ns_name_compress(const char *src
, u_char
*dst
, size_t dstsiz
,
656 const u_char
**dnptrs
, const u_char
**lastdnptr
)
658 u_char tmp
[NS_MAXCDNAME
];
660 if (ns_name_pton(src
, tmp
, sizeof tmp
) == -1)
662 return (ns_name_pack(tmp
, dst
, (int)dstsiz
, dnptrs
, lastdnptr
));
666 * Reset dnptrs so that there are no active references to pointers at or
670 ns_name_rollback(const u_char
*src
, const u_char
**dnptrs
,
671 const u_char
**lastdnptr
)
673 while (dnptrs
< lastdnptr
&& *dnptrs
!= NULL
) {
674 if (*dnptrs
>= src
) {
683 * Advance *ptrptr to skip over the compressed name it points at.
686 *\li 0 on success, -1 (with errno set) on failure.
689 ns_name_skip(const u_char
**ptrptr
, const u_char
*eom
)
696 while (cp
< eom
&& (n
= *cp
++) != 0) {
697 /* Check for indirection. */
698 switch (n
& NS_CMPRSFLGS
) {
699 case 0: /*%< normal case, n == len */
702 case NS_TYPE_ELT
: /*%< EDNS0 extended label */
703 if ((l
= labellen(cp
- 1)) < 0) {
704 errno
= EMSGSIZE
; /*%< XXX */
709 case NS_CMPRSFLGS
: /*%< indirection */
712 default: /*%< illegal type */
726 /* Find the number of octets an nname takes up, including the root label.
727 * (This is basically ns_name_skip() without compression-pointer support.)
728 * ((NOTE: can only return zero if passed-in namesiz argument is zero.))
731 ns_name_length(ns_nname_ct nname
, size_t namesiz
) {
732 ns_nname_ct orig
= nname
;
735 while (namesiz
-- > 0 && (n
= *nname
++) != 0) {
736 if ((n
& NS_CMPRSFLGS
) != 0) {
747 return (nname
- orig
);
750 /* Compare two nname's for equality. Return -1 on error (setting errno).
753 ns_name_eq(ns_nname_ct a
, size_t as
, ns_nname_ct b
, size_t bs
) {
754 ns_nname_ct ae
= a
+ as
, be
= b
+ bs
;
757 while (ac
= *a
, bc
= *b
, ac
!= 0 && bc
!= 0) {
758 if ((ac
& NS_CMPRSFLGS
) != 0 || (bc
& NS_CMPRSFLGS
) != 0) {
762 if (a
+ ac
>= ae
|| b
+ bc
>= be
) {
766 if (ac
!= bc
|| strncasecmp((const char *) ++a
,
772 return (ac
== 0 && bc
== 0);
775 /* Is domain "A" owned by (at or below) domain "B"?
778 ns_name_owned(ns_namemap_ct a
, int an
, ns_namemap_ct b
, int bn
) {
779 /* If A is shorter, it cannot be owned by B. */
783 /* If they are unequal before the length of the shorter, A cannot... */
785 if (a
->len
!= b
->len
||
786 strncasecmp((const char *) a
->base
,
787 (const char *) b
->base
, (size_t)a
->len
) != 0)
793 /* A might be longer or not, but either way, B owns it. */
797 /* Build an array of <base,len> tuples from an nname, top-down order.
798 * Return the number of tuples (labels) thus discovered.
801 ns_name_map(ns_nname_ct nname
, size_t namelen
, ns_namemap_t map
, int mapsize
) {
810 /* Extra data follows name? */
818 /* Compression pointer? */
819 if ((n
& NS_CMPRSFLGS
) != 0) {
824 /* Label too long? */
830 /* Recurse to get rest of name done first. */
831 l
= ns_name_map(nname
+ n
, namelen
- n
, map
, mapsize
);
835 /* Too many labels? */
837 errno
= ENAMETOOLONG
;
841 /* We're on our way back up-stack, store current map data. */
847 /* Count the labels in a domain name. Root counts, so COM. has two. This
848 * is to make the result comparable to the result of ns_name_map().
851 ns_name_labels(ns_nname_ct nname
, size_t namesiz
) {
855 while (namesiz
-- > 0 && (n
= *nname
++) != 0) {
856 if ((n
& NS_CMPRSFLGS
) != 0) {
874 * Thinking in noninternationalized USASCII (per the DNS spec),
875 * is this characted special ("in need of quoting") ?
883 case 0x22: /*%< '"' */
884 case 0x2E: /*%< '.' */
885 case 0x3B: /*%< ';' */
886 case 0x5C: /*%< '\\' */
887 case 0x28: /*%< '(' */
888 case 0x29: /*%< ')' */
889 /* Special modifiers in zone files. */
890 case 0x40: /*%< '@' */
891 case 0x24: /*%< '$' */
899 * Thinking in noninternationalized USASCII (per the DNS spec),
900 * is this character visible and not a space when printed ?
907 return (ch
> 0x20 && ch
< 0x7f);
911 * Thinking in noninternationalized USASCII (per the DNS spec),
912 * convert this character to lower case if it's upper case.
916 if (ch
>= 0x41 && ch
<= 0x5A)
922 * Search for the counted-label name in an array of compressed names.
925 *\li offset from msg if found, or -1.
928 *\li dnptrs is the pointer to the first name on the list,
929 *\li not the pointer to the start of the message.
932 dn_find(const u_char
*domain
, const u_char
*msg
,
933 const u_char
* const *dnptrs
,
934 const u_char
* const *lastdnptr
)
936 const u_char
*dn
, *cp
, *sp
;
937 const u_char
* const *cpp
;
940 for (cpp
= dnptrs
; cpp
< lastdnptr
; cpp
++) {
943 * terminate search on:
945 * compression pointer
948 while (*sp
!= 0 && (*sp
& NS_CMPRSFLGS
) == 0 &&
949 (sp
- msg
) < 0x4000) {
952 while ((n
= *cp
++) != 0) {
954 * check for indirection
956 switch (n
& NS_CMPRSFLGS
) {
957 case 0: /*%< normal case, n == len */
958 n
= labellen(cp
- 1); /*%< XXX */
963 if (mklower(*dn
++) !=
966 /* Is next root for both ? */
967 if (*dn
== '\0' && *cp
== '\0') {
968 assert(INT_MIN
<= (sp
- msg
) && (sp
- msg
) <= INT_MAX
);
969 return (int)(sp
- msg
);
974 case NS_CMPRSFLGS
: /*%< indirection */
975 cp
= msg
+ (((n
& 0x3f) << 8) | *cp
);
978 default: /*%< illegal type */
992 decode_bitstring(const unsigned char **cpp
, char *dn
, const char *eom
)
994 const unsigned char *cp
= *cpp
;
996 int b
, blen
, plen
, i
;
998 if ((blen
= (*cp
& 0xff)) == 0)
1000 plen
= (blen
+ 3) / 4;
1001 plen
+= (int)sizeof("\\[x/]") + (blen
> 99 ? 3 : (blen
> 9) ? 2 : 1);
1002 if (dn
+ plen
>= eom
)
1006 i
= SPRINTF((dn
, "\\[x"));
1010 for (b
= blen
; b
> 7; b
-= 8, cp
++) {
1011 i
= SPRINTF((dn
, "%02x", *cp
& 0xff));
1018 i
= SPRINTF((dn
, "%02x", tc
& (0xff << (8 - b
))));
1024 i
= SPRINTF((dn
, "%1x",
1025 (((u_int32_t
)tc
>> 4) & 0x0f) & (0x0f << (4 - b
))));
1030 i
= SPRINTF((dn
, "/%d]", blen
));
1036 assert(INT_MIN
<= (dn
- beg
) && (dn
- beg
) <= INT_MAX
);
1037 return (int)(dn
- beg
);
1041 encode_bitsring(const char **bp
, const char *end
, unsigned char **labelp
,
1042 unsigned char ** dst
, unsigned const char *eom
)
1045 const char *cp
= *bp
;
1048 const char *beg_blen
;
1049 char *end_blen
= NULL
;
1050 int value
= 0, count
= 0, tbcount
= 0, blen
= 0;
1052 beg_blen
= end_blen
= NULL
;
1054 /* a bitstring must contain at least 2 characters */
1058 /* XXX: currently, only hex strings are supported */
1061 if (!isxdigit((*cp
) & 0xff)) /*%< reject '\[x/BLEN]' */
1064 for (tp
= *dst
+ 1; cp
< end
&& tp
< eom
; cp
++) {
1066 case ']': /*%< end of the bitstring */
1068 if (beg_blen
== NULL
)
1070 blen
= (int)strtol(beg_blen
, &end_blen
, 10);
1071 if (*end_blen
!= ']')
1075 *tp
++ = ((value
<< 4) & 0xff);
1076 cp
++; /*%< skip ']' */
1083 if (!isdigit(c
&0xff))
1085 if (beg_blen
== NULL
) {
1088 /* blen never begings with 0 */
1094 if (!isxdigit(c
&0xff))
1097 value
+= digitvalue
[(int)c
];
1111 if (cp
>= end
|| tp
>= eom
)
1115 * bit length validation:
1116 * If a <length> is present, the number of digits in the <bit-data>
1117 * MUST be just sufficient to contain the number of bits specified
1118 * by the <length>. If there are insignificant bits in a final
1119 * hexadecimal or octal digit, they MUST be zero.
1120 * RFC2673, Section 3.2.
1125 if (((blen
+ 3) & ~3) != tbcount
)
1127 traillen
= tbcount
- blen
; /*%< between 0 and 3 */
1128 if (((value
<< (8 - traillen
)) & 0xff) != 0)
1136 /* encode the type and the significant bit fields */
1137 **labelp
= DNS_LABELTYPE_BITSTRING
;
1147 labellen(const u_char
*lp
)
1152 if ((l
& NS_CMPRSFLGS
) == NS_CMPRSFLGS
) {
1153 /* should be avoided by the caller */
1157 if ((l
& NS_CMPRSFLGS
) == NS_TYPE_ELT
) {
1158 if (l
== DNS_LABELTYPE_BITSTRING
) {
1159 if ((bitlen
= *(lp
+ 1)) == 0)
1161 return ((bitlen
+ 7 ) / 8 + 1);
1163 return (-1); /*%< unknwon ELT */