about summary refs log tree commit diff
path: root/resolv/ns_name.c
diff options
context:
space:
mode:
Diffstat (limited to 'resolv/ns_name.c')
-rw-r--r--resolv/ns_name.c589
1 files changed, 408 insertions, 181 deletions
diff --git a/resolv/ns_name.c b/resolv/ns_name.c
index ed361915d8..adf64bbd9a 100644
--- a/resolv/ns_name.c
+++ b/resolv/ns_name.c
@@ -1,18 +1,18 @@
 /*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 1996,1999 by Internet Software Consortium.
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #if !defined(_LIBC) && !defined(lint)
@@ -24,16 +24,41 @@ static const char rcsid[] = "$BINDId: ns_name.c,v 8.15 2000/03/30 22:53:46 vixie
 #include <netinet/in.h>
 #include <arpa/nameser.h>
 
-#include <ctype.h>
 #include <errno.h>
 #include <resolv.h>
 #include <string.h>
 #include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+
+# define SPRINTF(x) ((size_t)sprintf x)
+
+#define NS_TYPE_ELT			0x40 /*%< EDNS0 extended label type */
+#define DNS_LABELTYPE_BITSTRING		0x41
 
 /* Data. */
 
 static const char	digits[] = "0123456789";
 
+static const char digitvalue[256] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
+         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
+};
+
 /* Forward. */
 
 static int		special(int);
@@ -41,31 +66,40 @@ static int		printable(int);
 static int		dn_find(const u_char *, const u_char *,
 				const u_char * const *,
 				const u_char * const *);
+static int		encode_bitstring(const char **, const char *,
+					 unsigned char **, unsigned char **,
+					 unsigned const char *);
+static int		labellen(const u_char *);
+static int		decode_bitstring(const unsigned char **,
+					 char *, const char *);
 
 /* Public. */
 
-/*
- * ns_name_ntop(src, dst, dstsiz)
+/*%
  *	Convert an encoded domain name to printable ascii as per RFC1035.
+
  * return:
- *	Number of bytes written to buffer, or -1 (with errno set)
+ *\li	Number of bytes written to buffer, or -1 (with errno set)
+ *
  * notes:
- *	The root is returned as "."
- *	All other domains are returned in non absolute form
+ *\li	The root is returned as "."
+ *\li	All other domains are returned in non absolute form
  */
 int
-ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
+ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
+{
 	const u_char *cp;
 	char *dn, *eom;
 	u_char c;
 	u_int n;
+	int l;
 
 	cp = src;
 	dn = dst;
 	eom = dst + dstsiz;
 
 	while ((n = *cp++) != 0) {
-		if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {
+		if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
 			/* Some kind of compression pointer. */
 			__set_errno (EMSGSIZE);
 			return (-1);
@@ -77,34 +111,31 @@ ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
 			}
 			*dn++ = '.';
 		}
+		if ((l = labellen(cp - 1)) < 0) {
+			__set_errno (EMSGSIZE);
+			return(-1);
+		}
+		if (dn + l >= eom) {
+			__set_errno (EMSGSIZE);
+			return (-1);
+		}
+		if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+			int m;
 
-		if (n == 0x41) {
-			n = *cp++ / 8;
-			if (dn + n * 2 + 4 >= eom) {
-				__set_errno (EMSGSIZE);
-				return (-1);
+			if (n != DNS_LABELTYPE_BITSTRING) {
+				/* XXX: labellen should reject this case */
+				__set_errno (EINVAL);
+				return(-1);
 			}
-			*dn++ = '\\';
-			*dn++ = '[';
-			*dn++ = 'x';
-
-			while (n-- > 0) {
-				c = *cp++;
-				unsigned u = c >> 4;
-				*dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
-				u = c & 0xf;
-				*dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
+			if ((m = decode_bitstring(&cp, dn, eom)) < 0)
+			{
+				__set_errno (EMSGSIZE);
+				return(-1);
 			}
-
-			*dn++ = ']';
+			dn += m;
 			continue;
 		}
-
-		if (dn + n >= eom) {
-			__set_errno (EMSGSIZE);
-			return (-1);
-		}
-		for ((void)NULL; n > 0; n--) {
+		for ((void)NULL; l > 0; l--) {
 			c = *cp++;
 			if (special(c)) {
 				if (dn + 1 >= eom) {
@@ -146,22 +177,26 @@ ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
 	return (dn - dst);
 }
 libresolv_hidden_def (ns_name_ntop)
+strong_alias (ns_name_ntop, __ns_name_ntop)
 
-/*
- * ns_name_pton(src, dst, dstsiz)
+/*%
  *	Convert a ascii string into an encoded domain name as per RFC1035.
+ *
  * return:
- *	-1 if it fails
- *	1 if string was fully qualified
- *	0 is string was not fully qualified
+ *
+ *\li	-1 if it fails
+ *\li	1 if string was fully qualified
+ *\li	0 is string was not fully qualified
+ *
  * notes:
- *	Enforces label and domain length limits.
+ *\li	Enforces label and domain length limits.
  */
 
 int
-ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
+ns_name_pton(const char *src, u_char *dst, size_t dstsiz)
+{
 	u_char *label, *bp, *eom;
-	int c, n, escaped;
+	int c, n, escaped, e = 0;
 	char *cp;
 
 	escaped = 0;
@@ -171,7 +206,28 @@ ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
 
 	while ((c = *src++) != 0) {
 		if (escaped) {
-			if ((cp = strchr(digits, c)) != NULL) {
+			if (c == '[') { /*%< start a bit string label */
+				if ((cp = strchr(src, ']')) == NULL) {
+					__set_errno (EINVAL);
+					return(-1);
+				}
+				if ((e = encode_bitstring(&src, cp + 2,
+							  &label, &bp, eom))
+				    != 0) {
+					__set_errno (e);
+					return(-1);
+				}
+				escaped = 0;
+				label = bp++;
+				if ((c = *src++) == 0)
+					goto done;
+				else if (c != '.') {
+					__set_errno (EINVAL);
+					return(-1);
+				}
+				continue;
+			}
+			else if ((cp = strchr(digits, c)) != NULL) {
 				n = (cp - digits) * 100;
 				if ((c = *src++) == 0 ||
 				    (cp = strchr(digits, c)) == NULL) {
@@ -190,41 +246,6 @@ ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
 					return (-1);
 				}
 				c = n;
-			} else if (c == '[' && label == bp - 1 && *src == 'x') {
-				/* Theoretically we would have to handle \[o
-				   as well but we do not since we do not need
-				   it internally.  */
-				*label = 0x41;
-				label = bp++;
-				++src;
-				while (isxdigit (*src)) {
-					n = *src > '9' ? *src - 'a' + 10 : *src - '0';
-					++src;
-					if (! isxdigit(*src)) {
-						__set_errno (EMSGSIZE);
-						return (-1);
-					}
-					n <<= 4;
-					n += *src > '9' ? *src - 'a' + 10 : *src - '0';
-					if (bp + 1 >= eom) {
-						__set_errno (EMSGSIZE);
-						return (-1);
-					}
-					*bp++ = n;
-					++src;
-				}
-				*label = (bp - label - 1) * 8;
-				if (*src++ != ']' || *src++ != '.') {
-					__set_errno (EMSGSIZE);
-					return (-1);
-				}
-				escaped = 0;
-				label = bp++;
-				if (bp >= eom) {
-					__set_errno (EMSGSIZE);
-					return (-1);
-				}
-				continue;
 			}
 			escaped = 0;
 		} else if (c == '\\') {
@@ -232,7 +253,7 @@ ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
 			continue;
 		} else if (c == '.') {
 			c = (bp - label - 1);
-			if ((c & NS_CMPRSFLGS) != 0) {	/* Label too big. */
+			if ((c & NS_CMPRSFLGS) != 0) {	/*%< Label too big. */
 				__set_errno (EMSGSIZE);
 				return (-1);
 			}
@@ -270,10 +291,11 @@ ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
 		*bp++ = (u_char)c;
 	}
 	c = (bp - label - 1);
-	if ((c & NS_CMPRSFLGS) != 0) {		/* Label too big. */
+	if ((c & NS_CMPRSFLGS) != 0) {		/*%< Label too big. */
 		__set_errno (EMSGSIZE);
 		return (-1);
 	}
+  done:
 	if (label >= eom) {
 		__set_errno (EMSGSIZE);
 		return (-1);
@@ -286,45 +308,57 @@ ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
 		}
 		*bp++ = 0;
 	}
-	if ((bp - dst) > MAXCDNAME) {	/* src too big */
+	if ((bp - dst) > MAXCDNAME) {	/*%< src too big */
 		__set_errno (EMSGSIZE);
 		return (-1);
 	}
 	return (0);
 }
+libresolv_hidden_def (ns_name_pton)
 
-/*
- * ns_name_ntol(src, dst, dstsiz)
+/*%
  *	Convert a network strings labels into all lowercase.
+ *
  * return:
- *	Number of bytes written to buffer, or -1 (with errno set)
+ *\li	Number of bytes written to buffer, or -1 (with errno set)
+ *
  * notes:
- *	Enforces label and domain length limits.
+ *\li	Enforces label and domain length limits.
  */
 
 int
-ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
+ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
+{
 	const u_char *cp;
 	u_char *dn, *eom;
 	u_char c;
 	u_int n;
+	int l;
 
 	cp = src;
 	dn = dst;
 	eom = dst + dstsiz;
 
+	if (dn >= eom) {
+		__set_errno (EMSGSIZE);
+		return (-1);
+	}
 	while ((n = *cp++) != 0) {
-		if ((n & NS_CMPRSFLGS) != 0) {
+		if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
 			/* Some kind of compression pointer. */
 			__set_errno (EMSGSIZE);
 			return (-1);
 		}
 		*dn++ = n;
-		if (dn + n >= eom) {
+		if ((l = labellen(cp - 1)) < 0) {
 			__set_errno (EMSGSIZE);
 			return (-1);
 		}
-		for ((void)NULL; n > 0; n--) {
+		if (dn + l >= eom) {
+			__set_errno (EMSGSIZE);
+			return (-1);
+		}
+		for ((void)NULL; l > 0; l--) {
 			c = *cp++;
 			if (isupper(c))
 				*dn++ = tolower(c);
@@ -336,11 +370,11 @@ ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
 	return (dn - dst);
 }
 
-/*
- * ns_name_unpack(msg, eom, src, dst, dstsiz)
+/*%
  *	Unpack a domain name from a message, source may be compressed.
+ *
  * return:
- *	-1 if it fails, or consumed octets if it succeeds.
+ *\li	-1 if it fails, or consumed octets if it succeeds.
  */
 int
 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
@@ -348,7 +382,7 @@ ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
 {
 	const u_char *srcp, *dstlim;
 	u_char *dstp;
-	int n, len, checked;
+	int n, len, checked, l;
 
 	len = -1;
 	checked = 0;
@@ -363,29 +397,22 @@ ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
 	while ((n = *srcp++) != 0) {
 		/* Check for indirection. */
 		switch (n & NS_CMPRSFLGS) {
-		case 0x40:
-			if (n == 0x41) {
-				if (dstp + 1 >= dstlim) {
-					__set_errno (EMSGSIZE);
-					return (-1);
-			  	}
-				*dstp++ = 0x41;
-				n = *srcp++ / 8;
-				++checked;
-			} else {
-				__set_errno (EMSGSIZE);
-				return (-1);		/* flag error */
-			}
-			/* FALLTHROUGH */
 		case 0:
+		case NS_TYPE_ELT:
 			/* Limit checks. */
-			if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
+			if ((l = labellen(srcp - 1)) < 0) {
+				__set_errno (EMSGSIZE);
+				return(-1);
+			}
+			if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
 				__set_errno (EMSGSIZE);
 				return (-1);
 			}
-			checked += n + 1;
-			dstp = mempcpy(dstp, srcp - 1, n + 1);
-			srcp += n;
+			checked += l + 1;
+			*dstp++ = n;
+			memcpy(dstp, srcp, l);
+			dstp += l;
+			srcp += l;
 			break;
 
 		case NS_CMPRSFLGS:
@@ -396,7 +423,7 @@ ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
 			if (len < 0)
 				len = srcp - src + 1;
 			srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
-			if (srcp < msg || srcp >= eom) {  /* Out of range. */
+			if (srcp < msg || srcp >= eom) {  /*%< Out of range. */
 				__set_errno (EMSGSIZE);
 				return (-1);
 			}
@@ -414,7 +441,7 @@ ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
 
 		default:
 			__set_errno (EMSGSIZE);
-			return (-1);			/* flag error */
+			return (-1);			/*%< flag error */
 		}
 	}
 	*dstp = '\0';
@@ -423,20 +450,23 @@ ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
 	return (len);
 }
 libresolv_hidden_def (ns_name_unpack)
+strong_alias (ns_name_unpack, __ns_name_unpack)
 
-/*
- * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
+/*%
  *	Pack domain name 'domain' into 'comp_dn'.
+ *
  * return:
- *	Size of the compressed name, or -1.
+ *\li	Size of the compressed name, or -1.
+ *
  * notes:
- *	'dnptrs' is an array of pointers to previous compressed names.
- *	dnptrs[0] is a pointer to the beginning of the message. The array
+ *\li	'dnptrs' is an array of pointers to previous compressed names.
+ *\li	dnptrs[0] is a pointer to the beginning of the message. The array
  *	ends with NULL.
- *	'lastdnptr' is a pointer to the end of the array pointed to
+ *\li	'lastdnptr' is a pointer to the end of the array pointed to
  *	by 'dnptrs'.
+ *
  * Side effects:
- *	The list of pointers in dnptrs is updated for labels inserted into
+ *\li	The list of pointers in dnptrs is updated for labels inserted into
  *	the message as we compress the name.  If 'dnptr' is NULL, we don't
  *	try to compress names. If 'lastdnptr' is NULL, we don't update the
  *	list.
@@ -458,7 +488,7 @@ ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
 		if ((msg = *dnptrs++) != NULL) {
 			for (cpp = dnptrs; *cpp != NULL; cpp++)
 				(void)NULL;
-			lpp = cpp;	/* end of list to search */
+			lpp = cpp;	/*%< end of list to search */
 		}
 	} else
 		msg = NULL;
@@ -466,19 +496,23 @@ ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
 	/* make sure the domain we are about to add is legal */
 	l = 0;
 	do {
+		int l0;
+
 		n = *srcp;
-		if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {
+		if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
 			__set_errno (EMSGSIZE);
 			return (-1);
 		}
-		if (n == 0x41)
-			n = *++srcp / 8;
-		l += n + 1;
+		if ((l0 = labellen(srcp)) < 0) {
+			__set_errno (EINVAL);
+			return(-1);
+		}
+		l += l0 + 1;
 		if (l > MAXCDNAME) {
 			__set_errno (EMSGSIZE);
 			return (-1);
 		}
-		srcp += n + 1;
+		srcp += l0 + 1;
 	} while (n != 0);
 
 	/* from here on we need to reset compression pointer array on error */
@@ -486,7 +520,7 @@ ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
 	do {
 		/* Look to see if we can use pointers. */
 		n = *srcp;
-		if (n != 0 && n != 0x41 && msg != NULL) {
+		if (n != 0 && msg != NULL) {
 			l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
 				    (const u_char * const *)lpp);
 			if (l >= 0) {
@@ -506,15 +540,11 @@ ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
 			}
 		}
 		/* copy label to buffer */
-		if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {		/* Should not happen. */
+		if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+			/* Should not happen. */
 			goto cleanup;
 		}
-		if (n == 0x41) {
-			n = *++srcp / 8;
-			if (dstp + 1 >= eob)
-				goto cleanup;
-			*dstp++ = 0x41;
-		}
+		n = labellen(srcp);
 		if (dstp + 1 + n >= eob) {
 			goto cleanup;
 		}
@@ -532,14 +562,16 @@ cleanup:
 	}
 	return (dstp - dst);
 }
+libresolv_hidden_def (ns_name_pack)
 
-/*
- * ns_name_uncompress(msg, eom, src, dst, dstsiz)
+/*%
  *	Expand compressed domain name to presentation format.
+ *
  * return:
- *	Number of bytes read out of `src', or -1 (with errno set).
+ *\li	Number of bytes read out of `src', or -1 (with errno set).
+ *
  * note:
- *	Root domain returns as "." not "".
+ *\li	Root domain returns as "." not "".
  */
 int
 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
@@ -554,19 +586,21 @@ ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
 		return (-1);
 	return (n);
 }
+libresolv_hidden_def (ns_name_uncompress)
 
-/*
- * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
+/*%
  *	Compress a domain name into wire format, using compression pointers.
+ *
  * return:
- *	Number of bytes consumed in `dst' or -1 (with errno set).
+ *\li	Number of bytes consumed in `dst' or -1 (with errno set).
+ *
  * notes:
- *	'dnptrs' is an array of pointers to previous compressed names.
- *	dnptrs[0] is a pointer to the beginning of the message.
- *	The list ends with NULL.  'lastdnptr' is a pointer to the end of the
+ *\li	'dnptrs' is an array of pointers to previous compressed names.
+ *\li	dnptrs[0] is a pointer to the beginning of the message.
+ *\li	The list ends with NULL.  'lastdnptr' is a pointer to the end of the
  *	array pointed to by 'dnptrs'. Side effect is to update the list of
  *	pointers for labels inserted into the message as we compress the name.
- *	If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
+ *\li	If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
  *	is NULL, we don't update the list.
  */
 int
@@ -579,8 +613,9 @@ ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
 		return (-1);
 	return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
 }
+libresolv_hidden_def (ns_name_compress)
 
-/*
+/*%
  * Reset dnptrs so that there are no active references to pointers at or
  * after src.
  */
@@ -597,28 +632,37 @@ ns_name_rollback(const u_char *src, const u_char **dnptrs,
 	}
 }
 
-/*
- * ns_name_skip(ptrptr, eom)
+/*%
  *	Advance *ptrptr to skip over the compressed name it points at.
+ *
  * return:
- *	0 on success, -1 (with errno set) on failure.
+ *\li	0 on success, -1 (with errno set) on failure.
  */
 int
-ns_name_skip(const u_char **ptrptr, const u_char *eom) {
+ns_name_skip(const u_char **ptrptr, const u_char *eom)
+{
 	const u_char *cp;
 	u_int n;
+	int l;
 
 	cp = *ptrptr;
 	while (cp < eom && (n = *cp++) != 0) {
 		/* Check for indirection. */
 		switch (n & NS_CMPRSFLGS) {
-		case 0:			/* normal case, n == len */
+		case 0:			/*%< normal case, n == len */
 			cp += n;
 			continue;
-		case NS_CMPRSFLGS:	/* indirection */
+		case NS_TYPE_ELT: /*%< EDNS0 extended label */
+			if ((l = labellen(cp - 1)) < 0) {
+				__set_errno (EMSGSIZE);
+				return(-1);
+			}
+			cp += l;
+			continue;
+		case NS_CMPRSFLGS:	/*%< indirection */
 			cp++;
 			break;
-		default:		/* illegal type */
+		default:		/*%< illegal type */
 			__set_errno (EMSGSIZE);
 			return (-1);
 		}
@@ -631,45 +675,48 @@ ns_name_skip(const u_char **ptrptr, const u_char *eom) {
 	*ptrptr = cp;
 	return (0);
 }
+libresolv_hidden_def (ns_name_skip)
 
 /* Private. */
 
-/*
- * special(ch)
+/*%
  *	Thinking in noninternationalized USASCII (per the DNS spec),
  *	is this characted special ("in need of quoting") ?
+ *
  * return:
- *	boolean.
+ *\li	boolean.
  */
 static int
 special(int ch) {
 	switch (ch) {
-	case 0x22: /* '"' */
-	case 0x2E: /* '.' */
-	case 0x3B: /* ';' */
-	case 0x5C: /* '\\' */
+	case 0x22: /*%< '"' */
+	case 0x2E: /*%< '.' */
+	case 0x3B: /*%< ';' */
+	case 0x5C: /*%< '\\' */
+	case 0x28: /*%< '(' */
+	case 0x29: /*%< ')' */
 	/* Special modifiers in zone files. */
-	case 0x40: /* '@' */
-	case 0x24: /* '$' */
+	case 0x40: /*%< '@' */
+	case 0x24: /*%< '$' */
 		return (1);
 	default:
 		return (0);
 	}
 }
 
-/*
- * printable(ch)
+/*%
  *	Thinking in noninternationalized USASCII (per the DNS spec),
  *	is this character visible and not a space when printed ?
+ *
  * return:
- *	boolean.
+ *\li	boolean.
  */
 static int
 printable(int ch) {
 	return (ch > 0x20 && ch < 0x7f);
 }
 
-/*
+/*%
  *	Thinking in noninternationalized USASCII (per the DNS spec),
  *	convert this character to lower case if it's upper case.
  */
@@ -680,14 +727,15 @@ mklower(int ch) {
 	return (ch);
 }
 
-/*
- * dn_find(domain, msg, dnptrs, lastdnptr)
+/*%
  *	Search for the counted-label name in an array of compressed names.
+ *
  * return:
- *	offset from msg if found, or -1.
+ *\li	offset from msg if found, or -1.
+ *
  * notes:
- *	dnptrs is the pointer to the first name on the list,
- *	not the pointer to the start of the message.
+ *\li	dnptrs is the pointer to the first name on the list,
+ *\li	not the pointer to the start of the message.
  */
 static int
 dn_find(const u_char *domain, const u_char *msg,
@@ -715,9 +763,11 @@ dn_find(const u_char *domain, const u_char *msg,
 				 * check for indirection
 				 */
 				switch (n & NS_CMPRSFLGS) {
-				case 0:		/* normal case, n == len */
+				case 0:		/*%< normal case, n == len */
+					n = labellen(cp - 1); /*%< XXX */
 					if (n != *dn++)
 						goto next;
+
 					for ((void)NULL; n > 0; n--)
 						if (mklower(*dn++) !=
 						    mklower(*cp++))
@@ -728,20 +778,197 @@ dn_find(const u_char *domain, const u_char *msg,
 					if (*dn)
 						continue;
 					goto next;
-
-				case NS_CMPRSFLGS:	/* indirection */
+				case NS_CMPRSFLGS:	/*%< indirection */
 					cp = msg + (((n & 0x3f) << 8) | *cp);
 					break;
 
-				default:	/* illegal type */
+				default:	/*%< illegal type */
 					__set_errno (EMSGSIZE);
 					return (-1);
 				}
 			}
- next:
+  next: ;
 			sp += *sp + 1;
 		}
 	}
 	__set_errno (ENOENT);
 	return (-1);
 }
+
+static int
+decode_bitstring(const unsigned char **cpp, char *dn, const char *eom)
+{
+	const unsigned char *cp = *cpp;
+	char *beg = dn, tc;
+	int b, blen, plen, i;
+
+	if ((blen = (*cp & 0xff)) == 0)
+		blen = 256;
+	plen = (blen + 3) / 4;
+	plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
+	if (dn + plen >= eom)
+		return(-1);
+
+	cp++;
+	i = SPRINTF((dn, "\\[x"));
+	if (i < 0)
+		return (-1);
+	dn += i;
+	for (b = blen; b > 7; b -= 8, cp++) {
+		i = SPRINTF((dn, "%02x", *cp & 0xff));
+		if (i < 0)
+			return (-1);
+		dn += i;
+	}
+	if (b > 4) {
+		tc = *cp++;
+		i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b))));
+		if (i < 0)
+			return (-1);
+		dn += i;
+	} else if (b > 0) {
+		tc = *cp++;
+		i = SPRINTF((dn, "%1x",
+			       ((tc >> 4) & 0x0f) & (0x0f << (4 - b))));
+		if (i < 0)
+			return (-1);
+		dn += i;
+	}
+	i = SPRINTF((dn, "/%d]", blen));
+	if (i < 0)
+		return (-1);
+	dn += i;
+
+	*cpp = cp;
+	return(dn - beg);
+}
+
+static int
+encode_bitstring(const char **bp, const char *end, unsigned char **labelp,
+		 unsigned char ** dst, unsigned const char *eom)
+{
+	int afterslash = 0;
+	const char *cp = *bp;
+	unsigned char *tp;
+	char c;
+	const char *beg_blen;
+	char *end_blen = NULL;
+	int value = 0, count = 0, tbcount = 0, blen = 0;
+
+	beg_blen = end_blen = NULL;
+
+	/* a bitstring must contain at least 2 characters */
+	if (end - cp < 2)
+		return(EINVAL);
+
+	/* XXX: currently, only hex strings are supported */
+	if (*cp++ != 'x')
+		return(EINVAL);
+	if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */
+		return(EINVAL);
+
+	for (tp = *dst + 1; cp < end && tp < eom; cp++) {
+		switch((c = *cp)) {
+		case ']':	/*%< end of the bitstring */
+			if (afterslash) {
+				if (beg_blen == NULL)
+					return(EINVAL);
+				blen = (int)strtol(beg_blen, &end_blen, 10);
+				if (*end_blen != ']')
+					return(EINVAL);
+			}
+			if (count)
+				*tp++ = ((value << 4) & 0xff);
+			cp++;	/*%< skip ']' */
+			goto done;
+		case '/':
+			afterslash = 1;
+			break;
+		default:
+			if (afterslash) {
+				if (!isdigit(c&0xff))
+					return(EINVAL);
+				if (beg_blen == NULL) {
+
+					if (c == '0') {
+						/* blen never begings with 0 */
+						return(EINVAL);
+					}
+					beg_blen = cp;
+				}
+			} else {
+				if (!isxdigit(c&0xff))
+					return(EINVAL);
+				value <<= 4;
+				value += digitvalue[(int)c];
+				count += 4;
+				tbcount += 4;
+				if (tbcount > 256)
+					return(EINVAL);
+				if (count == 8) {
+					*tp++ = value;
+					count = 0;
+				}
+			}
+			break;
+		}
+	}
+  done:
+	if (cp >= end || tp >= eom)
+		return(EMSGSIZE);
+
+	/*
+	 * bit length validation:
+	 * If a <length> is present, the number of digits in the <bit-data>
+	 * MUST be just sufficient to contain the number of bits specified
+	 * by the <length>. If there are insignificant bits in a final
+	 * hexadecimal or octal digit, they MUST be zero.
+	 * RFC2673, Section 3.2.
+	 */
+	if (blen > 0) {
+		int traillen;
+
+		if (((blen + 3) & ~3) != tbcount)
+			return(EINVAL);
+		traillen = tbcount - blen; /*%< between 0 and 3 */
+		if (((value << (8 - traillen)) & 0xff) != 0)
+			return(EINVAL);
+	}
+	else
+		blen = tbcount;
+	if (blen == 256)
+		blen = 0;
+
+	/* encode the type and the significant bit fields */
+	**labelp = DNS_LABELTYPE_BITSTRING;
+	**dst = blen;
+
+	*bp = cp;
+	*dst = tp;
+
+	return(0);
+}
+
+static int
+labellen(const u_char *lp)
+{
+	int bitlen;
+	u_char l = *lp;
+
+	if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+		/* should be avoided by the caller */
+		return(-1);
+	}
+
+	if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+		if (l == DNS_LABELTYPE_BITSTRING) {
+			if ((bitlen = *(lp + 1)) == 0)
+				bitlen = 256;
+			return((bitlen + 7 ) / 8 + 1);
+		}
+		return(-1);	/*%< unknwon ELT */
+	}
+	return(l);
+}
+
+/*! \file */