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