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