1/*	$NetBSD: ns_print.c,v 1.5 2004/11/07 02:19:49 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
23static const char rcsid[] = "Id: ns_print.c,v 1.3.2.1.4.5 2004/07/28 20:16:45 marka Exp";
24#else
25__RCSID("$NetBSD: ns_print.c,v 1.5 2004/11/07 02:19:49 christos Exp $");
26#endif
27#endif
28
29/* Import. */
30
31#include <sys/types.h>
32#include <sys/socket.h>
33
34#include <netinet/in.h>
35#include "arpa_nameser.h"
36#include <arpa/inet.h>
37
38#include <isc/assertions.h>
39#include <isc/dst.h>
40#include <errno.h>
41#ifdef ANDROID_CHANGES
42#include "resolv_private.h"
43#else
44#include <resolv.h>
45#endif
46#include <string.h>
47#include <ctype.h>
48#include <assert.h>
49
50#ifdef SPRINTF_CHAR
51# define SPRINTF(x) strlen(sprintf/**/x)
52#else
53# define SPRINTF(x) ((size_t)sprintf x)
54#endif
55
56#ifndef MIN
57#define	MIN(x,y)	((x)<(y)?(x):(y))
58#endif
59
60/* Forward. */
61
62static size_t	prune_origin(const char *name, const char *origin);
63static int	charstr(const u_char *rdata, const u_char *edata,
64			char **buf, size_t *buflen);
65static int	addname(const u_char *msg, size_t msglen,
66			const u_char **p, const char *origin,
67			char **buf, size_t *buflen);
68static void	addlen(size_t len, char **buf, size_t *buflen);
69static int	addstr(const char *src, size_t len,
70		       char **buf, size_t *buflen);
71static int	addtab(size_t len, size_t target, int spaced,
72		       char **buf, size_t *buflen);
73
74/* Macros. */
75
76#define	T(x) \
77	do { \
78		if ((x) < 0) \
79			return (-1); \
80	} while (/*CONSTCOND*/0)
81
82/* Public. */
83
84/*
85 * int
86 * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen)
87 *	Convert an RR to presentation format.
88 * return:
89 *	Number of characters written to buf, or -1 (check errno).
90 */
91int
92ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
93	    const char *name_ctx, const char *origin,
94	    char *buf, size_t buflen)
95{
96	int n;
97
98	n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle),
99			 ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr),
100			 ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr),
101			 name_ctx, origin, buf, buflen);
102	return (n);
103}
104
105/*
106 * int
107 * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen,
108 *	       name_ctx, origin, buf, buflen)
109 *	Convert the fields of an RR into presentation format.
110 * return:
111 *	Number of characters written to buf, or -1 (check errno).
112 */
113int
114ns_sprintrrf(const u_char *msg, size_t msglen,
115	    const char *name, ns_class class, ns_type type,
116	    u_long ttl, const u_char *rdata, size_t rdlen,
117	    const char *name_ctx, const char *origin,
118	    char *buf, size_t buflen)
119{
120	const char *obuf = buf;
121	const u_char *edata = rdata + rdlen;
122	int spaced = 0;
123
124	const char *comment;
125	char tmp[100];
126	int len, x;
127
128	/*
129	 * Owner.
130	 */
131	if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) {
132		T(addstr("\t\t\t", (size_t)3, &buf, &buflen));
133	} else {
134		len = prune_origin(name, origin);
135		if (*name == '\0') {
136			goto root;
137		} else if (len == 0) {
138			T(addstr("@\t\t\t", (size_t)4, &buf, &buflen));
139		} else {
140			T(addstr(name, (size_t)len, &buf, &buflen));
141			/* Origin not used or not root, and no trailing dot? */
142			if (((origin == NULL || origin[0] == '\0') ||
143			    (origin[0] != '.' && origin[1] != '\0' &&
144			    name[len] == '\0')) && name[len - 1] != '.') {
145 root:
146				T(addstr(".", (size_t)1, &buf, &buflen));
147				len++;
148			}
149			T(spaced = addtab((size_t)len, 24, spaced, &buf, &buflen));
150		}
151	}
152
153	/*
154	 * TTL, Class, Type.
155	 */
156	T(x = ns_format_ttl(ttl, buf, buflen));
157	addlen((size_t)x, &buf, &buflen);
158	len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
159	T(addstr(tmp, (size_t)len, &buf, &buflen));
160	T(spaced = addtab((size_t)(x + len), (size_t)16, spaced, &buf, &buflen));
161
162	/*
163	 * RData.
164	 */
165	switch (type) {
166	case ns_t_a:
167		if (rdlen != (size_t)NS_INADDRSZ)
168			goto formerr;
169		(void) inet_ntop(AF_INET, rdata, buf, buflen);
170		addlen(strlen(buf), &buf, &buflen);
171		break;
172
173	case ns_t_cname:
174	case ns_t_mb:
175	case ns_t_mg:
176	case ns_t_mr:
177	case ns_t_ns:
178	case ns_t_ptr:
179	case ns_t_dname:
180		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
181		break;
182
183	case ns_t_hinfo:
184	case ns_t_isdn:
185		/* First word. */
186		T(len = charstr(rdata, edata, &buf, &buflen));
187		if (len == 0)
188			goto formerr;
189		rdata += len;
190		T(addstr(" ", (size_t)1, &buf, &buflen));
191
192
193		/* Second word, optional in ISDN records. */
194		if (type == ns_t_isdn && rdata == edata)
195			break;
196
197		T(len = charstr(rdata, edata, &buf, &buflen));
198		if (len == 0)
199			goto formerr;
200		rdata += len;
201		break;
202
203	case ns_t_soa: {
204		u_long t;
205
206		/* Server name. */
207		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
208		T(addstr(" ", (size_t)1, &buf, &buflen));
209
210		/* Administrator name. */
211		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
212		T(addstr(" (\n", (size_t)3, &buf, &buflen));
213		spaced = 0;
214
215		if ((edata - rdata) != 5*NS_INT32SZ)
216			goto formerr;
217
218		/* Serial number. */
219		t = ns_get32(rdata);  rdata += NS_INT32SZ;
220		T(addstr("\t\t\t\t\t", (size_t)5, &buf, &buflen));
221		len = SPRINTF((tmp, "%lu", t));
222		T(addstr(tmp, (size_t)len, &buf, &buflen));
223		T(spaced = addtab((size_t)len, (size_t)16, spaced, &buf, &buflen));
224		T(addstr("; serial\n", (size_t)9, &buf, &buflen));
225		spaced = 0;
226
227		/* Refresh interval. */
228		t = ns_get32(rdata);  rdata += NS_INT32SZ;
229		T(addstr("\t\t\t\t\t", (size_t)5, &buf, &buflen));
230		T(len = ns_format_ttl(t, buf, buflen));
231		addlen((size_t)len, &buf, &buflen);
232		T(spaced = addtab((size_t)len, (size_t)16, spaced, &buf, &buflen));
233		T(addstr("; refresh\n", (size_t)10, &buf, &buflen));
234		spaced = 0;
235
236		/* Retry interval. */
237		t = ns_get32(rdata);  rdata += NS_INT32SZ;
238		T(addstr("\t\t\t\t\t", (size_t)5, &buf, &buflen));
239		T(len = ns_format_ttl(t, buf, buflen));
240		addlen((size_t)len, &buf, &buflen);
241		T(spaced = addtab((size_t)len, (size_t)16, spaced, &buf, &buflen));
242		T(addstr("; retry\n", (size_t)8, &buf, &buflen));
243		spaced = 0;
244
245		/* Expiry. */
246		t = ns_get32(rdata);  rdata += NS_INT32SZ;
247		T(addstr("\t\t\t\t\t", (size_t)5, &buf, &buflen));
248		T(len = ns_format_ttl(t, buf, buflen));
249		addlen((size_t)len, &buf, &buflen);
250		T(spaced = addtab((size_t)len, (size_t)16, spaced, &buf, &buflen));
251		T(addstr("; expiry\n", (size_t)9, &buf, &buflen));
252		spaced = 0;
253
254		/* Minimum TTL. */
255		t = ns_get32(rdata);  rdata += NS_INT32SZ;
256		T(addstr("\t\t\t\t\t", (size_t)5, &buf, &buflen));
257		T(len = ns_format_ttl(t, buf, buflen));
258		addlen((size_t)len, &buf, &buflen);
259		T(addstr(" )", (size_t)2, &buf, &buflen));
260		T(spaced = addtab((size_t)len, (size_t)16, spaced, &buf, &buflen));
261		T(addstr("; minimum\n", (size_t)10, &buf, &buflen));
262
263		break;
264	    }
265
266	case ns_t_mx:
267	case ns_t_afsdb:
268	case ns_t_rt: {
269		u_int t;
270
271		if (rdlen < (size_t)NS_INT16SZ)
272			goto formerr;
273
274		/* Priority. */
275		t = ns_get16(rdata);
276		rdata += NS_INT16SZ;
277		len = SPRINTF((tmp, "%u ", t));
278		T(addstr(tmp, (size_t)len, &buf, &buflen));
279
280		/* Target. */
281		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
282
283		break;
284	    }
285
286	case ns_t_px: {
287		u_int t;
288
289		if (rdlen < (size_t)NS_INT16SZ)
290			goto formerr;
291
292		/* Priority. */
293		t = ns_get16(rdata);
294		rdata += NS_INT16SZ;
295		len = SPRINTF((tmp, "%u ", t));
296		T(addstr(tmp, (size_t)len, &buf, &buflen));
297
298		/* Name1. */
299		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
300		T(addstr(" ", (size_t)1, &buf, &buflen));
301
302		/* Name2. */
303		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
304
305		break;
306	    }
307
308	case ns_t_x25:
309		T(len = charstr(rdata, edata, &buf, &buflen));
310		if (len == 0)
311			goto formerr;
312		rdata += len;
313		break;
314
315	case ns_t_txt:
316		while (rdata < edata) {
317			T(len = charstr(rdata, edata, &buf, &buflen));
318			if (len == 0)
319				goto formerr;
320			rdata += len;
321			if (rdata < edata)
322				T(addstr(" ", (size_t)1, &buf, &buflen));
323		}
324		break;
325
326	case ns_t_nsap: {
327		char t[2+255*3];
328
329		(void) inet_nsap_ntoa((int)rdlen, rdata, t);
330		T(addstr(t, strlen(t), &buf, &buflen));
331		break;
332	    }
333
334	case ns_t_aaaa:
335		if (rdlen != (size_t)NS_IN6ADDRSZ)
336			goto formerr;
337		(void) inet_ntop(AF_INET6, rdata, buf, buflen);
338		addlen(strlen(buf), &buf, &buflen);
339		break;
340
341	case ns_t_loc: {
342		char t[255];
343
344		/* XXX protocol format checking? */
345		(void) loc_ntoa(rdata, t);
346		T(addstr(t, strlen(t), &buf, &buflen));
347		break;
348	    }
349
350	case ns_t_naptr: {
351		u_int order, preference;
352		char t[50];
353
354		if (rdlen < 2U*NS_INT16SZ)
355			goto formerr;
356
357		/* Order, Precedence. */
358		order = ns_get16(rdata);	rdata += NS_INT16SZ;
359		preference = ns_get16(rdata);	rdata += NS_INT16SZ;
360		len = SPRINTF((t, "%u %u ", order, preference));
361		T(addstr(t, (size_t)len, &buf, &buflen));
362
363		/* Flags. */
364		T(len = charstr(rdata, edata, &buf, &buflen));
365		if (len == 0)
366			goto formerr;
367		rdata += len;
368		T(addstr(" ", (size_t)1, &buf, &buflen));
369
370		/* Service. */
371		T(len = charstr(rdata, edata, &buf, &buflen));
372		if (len == 0)
373			goto formerr;
374		rdata += len;
375		T(addstr(" ", (size_t)1, &buf, &buflen));
376
377		/* Regexp. */
378		T(len = charstr(rdata, edata, &buf, &buflen));
379		if (len < 0)
380			return (-1);
381		if (len == 0)
382			goto formerr;
383		rdata += len;
384		T(addstr(" ", (size_t)1, &buf, &buflen));
385
386		/* Server. */
387		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
388		break;
389	    }
390
391	case ns_t_srv: {
392		u_int priority, weight, port;
393		char t[50];
394
395		if (rdlen < 3U*NS_INT16SZ)
396			goto formerr;
397
398		/* Priority, Weight, Port. */
399		priority = ns_get16(rdata);  rdata += NS_INT16SZ;
400		weight   = ns_get16(rdata);  rdata += NS_INT16SZ;
401		port     = ns_get16(rdata);  rdata += NS_INT16SZ;
402		len = SPRINTF((t, "%u %u %u ", priority, weight, port));
403		T(addstr(t, (size_t)len, &buf, &buflen));
404
405		/* Server. */
406		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
407		break;
408	    }
409
410	case ns_t_minfo:
411	case ns_t_rp:
412		/* Name1. */
413		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
414		T(addstr(" ", (size_t)1, &buf, &buflen));
415
416		/* Name2. */
417		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
418
419		break;
420
421	case ns_t_wks: {
422		int n, lcnt;
423
424		if (rdlen < 1U + NS_INT32SZ)
425			goto formerr;
426
427		/* Address. */
428		(void) inet_ntop(AF_INET, rdata, buf, buflen);
429		addlen(strlen(buf), &buf, &buflen);
430		rdata += NS_INADDRSZ;
431
432		/* Protocol. */
433		len = SPRINTF((tmp, " %u ( ", *rdata));
434		T(addstr(tmp, (size_t)len, &buf, &buflen));
435		rdata += NS_INT8SZ;
436
437		/* Bit map. */
438		n = 0;
439		lcnt = 0;
440		while (rdata < edata) {
441			u_int c = *rdata++;
442			do {
443				if (c & 0200) {
444					if (lcnt == 0) {
445						T(addstr("\n\t\t\t\t", (size_t)5,
446							 &buf, &buflen));
447						lcnt = 10;
448						spaced = 0;
449					}
450					len = SPRINTF((tmp, "%d ", n));
451					T(addstr(tmp, (size_t)len, &buf, &buflen));
452					lcnt--;
453				}
454				c <<= 1;
455			} while (++n & 07);
456		}
457		T(addstr(")", (size_t)1, &buf, &buflen));
458
459		break;
460	    }
461
462	case ns_t_key: {
463		char base64_key[NS_MD5RSA_MAX_BASE64];
464		u_int keyflags, protocol, algorithm, key_id;
465		const char *leader;
466		int n;
467
468		if (rdlen < 0U + NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
469			goto formerr;
470
471		/* Key flags, Protocol, Algorithm. */
472#ifndef _LIBC
473		key_id = dst_s_dns_key_id(rdata, edata-rdata);
474#else
475		key_id = 0;
476#endif
477		keyflags = ns_get16(rdata);  rdata += NS_INT16SZ;
478		protocol = *rdata++;
479		algorithm = *rdata++;
480		len = SPRINTF((tmp, "0x%04x %u %u",
481			       keyflags, protocol, algorithm));
482		T(addstr(tmp, (size_t)len, &buf, &buflen));
483
484		/* Public key data. */
485		len = b64_ntop(rdata, (size_t)(edata - rdata),
486			       base64_key, sizeof base64_key);
487		if (len < 0)
488			goto formerr;
489		if (len > 15) {
490			T(addstr(" (", (size_t)2, &buf, &buflen));
491			leader = "\n\t\t";
492			spaced = 0;
493		} else
494			leader = " ";
495		for (n = 0; n < len; n += 48) {
496			T(addstr(leader, strlen(leader), &buf, &buflen));
497			T(addstr(base64_key + n, (size_t)MIN(len - n, 48),
498				 &buf, &buflen));
499		}
500		if (len > 15)
501			T(addstr(" )", (size_t)2, &buf, &buflen));
502		n = SPRINTF((tmp, " ; key_tag= %u", key_id));
503		T(addstr(tmp, (size_t)n, &buf, &buflen));
504
505		break;
506	    }
507
508	case ns_t_sig: {
509		char base64_key[NS_MD5RSA_MAX_BASE64];
510		u_int typ, algorithm, labels, footprint;
511		const char *leader;
512		u_long t;
513		int n;
514
515		if (rdlen < 22U)
516			goto formerr;
517
518		/* Type covered, Algorithm, Label count, Original TTL. */
519	        typ = ns_get16(rdata);  rdata += NS_INT16SZ;
520		algorithm = *rdata++;
521		labels = *rdata++;
522		t = ns_get32(rdata);  rdata += NS_INT32SZ;
523		len = SPRINTF((tmp, "%s %d %d %lu ",
524			       p_type((int)typ), algorithm, labels, t));
525		T(addstr(tmp, (size_t)len, &buf, &buflen));
526		if (labels > (u_int)dn_count_labels(name))
527			goto formerr;
528
529		/* Signature expiry. */
530		t = ns_get32(rdata);  rdata += NS_INT32SZ;
531		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
532		T(addstr(tmp, (size_t)len, &buf, &buflen));
533
534		/* Time signed. */
535		t = ns_get32(rdata);  rdata += NS_INT32SZ;
536		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
537		T(addstr(tmp, (size_t)len, &buf, &buflen));
538
539		/* Signature Footprint. */
540		footprint = ns_get16(rdata);  rdata += NS_INT16SZ;
541		len = SPRINTF((tmp, "%u ", footprint));
542		T(addstr(tmp, (size_t)len, &buf, &buflen));
543
544		/* Signer's name. */
545		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
546
547		/* Signature. */
548		len = b64_ntop(rdata, (size_t)(edata - rdata),
549			       base64_key, sizeof base64_key);
550		if (len > 15) {
551			T(addstr(" (", (size_t)2, &buf, &buflen));
552			leader = "\n\t\t";
553			spaced = 0;
554		} else
555			leader = " ";
556		if (len < 0)
557			goto formerr;
558		for (n = 0; n < len; n += 48) {
559			T(addstr(leader, strlen(leader), &buf, &buflen));
560			T(addstr(base64_key + n, (size_t)MIN(len - n, 48),
561				 &buf, &buflen));
562		}
563		if (len > 15)
564			T(addstr(" )", (size_t)2, &buf, &buflen));
565		break;
566	    }
567
568	case ns_t_nxt: {
569		int n, c;
570
571		/* Next domain name. */
572		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
573
574		/* Type bit map. */
575		n = edata - rdata;
576		for (c = 0; c < n*8; c++)
577			if (NS_NXT_BIT_ISSET(c, rdata)) {
578				len = SPRINTF((tmp, " %s", p_type(c)));
579				T(addstr(tmp, (size_t)len, &buf, &buflen));
580			}
581		break;
582	    }
583
584	case ns_t_cert: {
585		u_int c_type, key_tag, alg;
586		int n;
587		unsigned int siz;
588		char base64_cert[8192], tmp1[40];
589		const char *leader;
590
591		c_type  = ns_get16(rdata); rdata += NS_INT16SZ;
592		key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
593		alg = (u_int) *rdata++;
594
595		len = SPRINTF((tmp1, "%d %d %d ", c_type, key_tag, alg));
596		T(addstr(tmp1, (size_t)len, &buf, &buflen));
597		siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
598		if (siz > sizeof(base64_cert) * 3/4) {
599			const char *str = "record too long to print";
600			T(addstr(str, strlen(str), &buf, &buflen));
601		}
602		else {
603			len = b64_ntop(rdata, (size_t)(edata-rdata),
604			    base64_cert, siz);
605
606			if (len < 0)
607				goto formerr;
608			else if (len > 15) {
609				T(addstr(" (", (size_t)2, &buf, &buflen));
610				leader = "\n\t\t";
611				spaced = 0;
612			}
613			else
614				leader = " ";
615
616			for (n = 0; n < len; n += 48) {
617				T(addstr(leader, strlen(leader),
618					 &buf, &buflen));
619				T(addstr(base64_cert + n, (size_t)MIN(len - n, 48),
620					 &buf, &buflen));
621			}
622			if (len > 15)
623				T(addstr(" )", (size_t)2, &buf, &buflen));
624		}
625		break;
626	    }
627
628	case ns_t_tkey: {
629		/* KJD - need to complete this */
630		u_long t;
631		int mode, err, keysize;
632
633		/* Algorithm name. */
634		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
635		T(addstr(" ", (size_t)1, &buf, &buflen));
636
637		/* Inception. */
638		t = ns_get32(rdata);  rdata += NS_INT32SZ;
639		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
640		T(addstr(tmp, (size_t)len, &buf, &buflen));
641
642		/* Experation. */
643		t = ns_get32(rdata);  rdata += NS_INT32SZ;
644		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
645		T(addstr(tmp, (size_t)len, &buf, &buflen));
646
647		/* Mode , Error, Key Size. */
648		/* Priority, Weight, Port. */
649		mode = ns_get16(rdata);  rdata += NS_INT16SZ;
650		err  = ns_get16(rdata);  rdata += NS_INT16SZ;
651		keysize  = ns_get16(rdata);  rdata += NS_INT16SZ;
652		len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
653		T(addstr(tmp, (size_t)len, &buf, &buflen));
654
655		/* XXX need to dump key, print otherdata length & other data */
656		break;
657	    }
658
659	case ns_t_tsig: {
660		/* BEW - need to complete this */
661		int n;
662
663		T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
664		T(addstr(" ", (size_t)1, &buf, &buflen));
665		rdata += 8; /* time */
666		n = ns_get16(rdata); rdata += INT16SZ;
667		rdata += n; /* sig */
668		n = ns_get16(rdata); rdata += INT16SZ; /* original id */
669		sprintf(buf, "%d", ns_get16(rdata));
670		rdata += INT16SZ;
671		addlen(strlen(buf), &buf, &buflen);
672		break;
673	    }
674
675	case ns_t_a6: {
676		struct in6_addr a;
677		int pbyte, pbit;
678
679		/* prefix length */
680		if (rdlen == 0U) goto formerr;
681		len = SPRINTF((tmp, "%d ", *rdata));
682		T(addstr(tmp, (size_t)len, &buf, &buflen));
683		pbit = *rdata;
684		if (pbit > 128) goto formerr;
685		pbyte = (pbit & ~7) / 8;
686		rdata++;
687
688		/* address suffix: provided only when prefix len != 128 */
689		if (pbit < 128) {
690			if (rdata + pbyte >= edata) goto formerr;
691			memset(&a, 0, sizeof(a));
692			memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
693			(void) inet_ntop(AF_INET6, &a, buf, buflen);
694			addlen(strlen(buf), &buf, &buflen);
695			rdata += sizeof(a) - pbyte;
696		}
697
698		/* prefix name: provided only when prefix len > 0 */
699		if (pbit == 0)
700			break;
701		if (rdata >= edata) goto formerr;
702		T(addstr(" ", (size_t)1, &buf, &buflen));
703		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
704
705		break;
706	    }
707
708	case ns_t_opt: {
709		len = SPRINTF((tmp, "%u bytes", class));
710		T(addstr(tmp, (size_t)len, &buf, &buflen));
711		break;
712	    }
713
714	default:
715		comment = "unknown RR type";
716		goto hexify;
717	}
718	return (buf - obuf);
719 formerr:
720	comment = "RR format error";
721 hexify: {
722	int n, m;
723	char *p;
724
725	len = SPRINTF((tmp, "\\# %tu%s\t; %s", edata - rdata,
726		       rdlen != 0 ? " (" : "", comment));
727	T(addstr(tmp, (size_t)len, &buf, &buflen));
728	while (rdata < edata) {
729		p = tmp;
730		p += SPRINTF((p, "\n\t"));
731		spaced = 0;
732		n = MIN(16, edata - rdata);
733		for (m = 0; m < n; m++)
734			p += SPRINTF((p, "%02x ", rdata[m]));
735		T(addstr(tmp, (size_t)(p - tmp), &buf, &buflen));
736		if (n < 16) {
737			T(addstr(")", (size_t)1, &buf, &buflen));
738			T(addtab((size_t)(p - tmp + 1), (size_t)48, spaced, &buf, &buflen));
739		}
740		p = tmp;
741		p += SPRINTF((p, "; "));
742		for (m = 0; m < n; m++)
743			*p++ = (isascii(rdata[m]) && isprint(rdata[m]))
744				? rdata[m]
745				: '.';
746		T(addstr(tmp, (size_t)(p - tmp), &buf, &buflen));
747		rdata += n;
748	}
749	return (buf - obuf);
750    }
751}
752
753/* Private. */
754
755/*
756 * size_t
757 * prune_origin(name, origin)
758 *	Find out if the name is at or under the current origin.
759 * return:
760 *	Number of characters in name before start of origin,
761 *	or length of name if origin does not match.
762 * notes:
763 *	This function should share code with samedomain().
764 */
765static size_t
766prune_origin(const char *name, const char *origin) {
767	const char *oname = name;
768
769	while (*name != '\0') {
770		if (origin != NULL && ns_samename(name, origin) == 1)
771			return (name - oname - (name > oname));
772		while (*name != '\0') {
773			if (*name == '\\') {
774				name++;
775				/* XXX need to handle \nnn form. */
776				if (*name == '\0')
777					break;
778			} else if (*name == '.') {
779				name++;
780				break;
781			}
782			name++;
783		}
784	}
785	return (name - oname);
786}
787
788/*
789 * int
790 * charstr(rdata, edata, buf, buflen)
791 *	Format a <character-string> into the presentation buffer.
792 * return:
793 *	Number of rdata octets consumed
794 *	0 for protocol format error
795 *	-1 for output buffer error
796 * side effects:
797 *	buffer is advanced on success.
798 */
799static int
800charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
801	const u_char *odata = rdata;
802	size_t save_buflen = *buflen;
803	char *save_buf = *buf;
804
805	if (addstr("\"", (size_t)1, buf, buflen) < 0)
806		goto enospc;
807	if (rdata < edata) {
808		int n = *rdata;
809
810		if (rdata + 1 + n <= edata) {
811			rdata++;
812			while (n-- > 0) {
813				if (strchr("\n\"\\", *rdata) != NULL)
814					if (addstr("\\", (size_t)1, buf, buflen) < 0)
815						goto enospc;
816				if (addstr((const char *)rdata, (size_t)1,
817					   buf, buflen) < 0)
818					goto enospc;
819				rdata++;
820			}
821		}
822	}
823	if (addstr("\"", (size_t)1, buf, buflen) < 0)
824		goto enospc;
825	return (rdata - odata);
826 enospc:
827	errno = ENOSPC;
828	*buf = save_buf;
829	*buflen = save_buflen;
830	return (-1);
831}
832
833static int
834addname(const u_char *msg, size_t msglen,
835	const u_char **pp, const char *origin,
836	char **buf, size_t *buflen)
837{
838	size_t newlen, save_buflen = *buflen;
839	char *save_buf = *buf;
840	int n;
841
842	n = dn_expand(msg, msg + msglen, *pp, *buf, (int)*buflen);
843	if (n < 0)
844		goto enospc;	/* Guess. */
845	newlen = prune_origin(*buf, origin);
846	if (**buf == '\0') {
847		goto root;
848	} else if (newlen == 0U) {
849		/* Use "@" instead of name. */
850		if (newlen + 2 > *buflen)
851			goto enospc;        /* No room for "@\0". */
852		(*buf)[newlen++] = '@';
853		(*buf)[newlen] = '\0';
854	} else {
855		if (((origin == NULL || origin[0] == '\0') ||
856		    (origin[0] != '.' && origin[1] != '\0' &&
857		    (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
858			/* No trailing dot. */
859 root:
860			if (newlen + 2 > *buflen)
861				goto enospc;	/* No room for ".\0". */
862			(*buf)[newlen++] = '.';
863			(*buf)[newlen] = '\0';
864		}
865	}
866	*pp += n;
867	addlen(newlen, buf, buflen);
868	**buf = '\0';
869	return (newlen);
870 enospc:
871	errno = ENOSPC;
872	*buf = save_buf;
873	*buflen = save_buflen;
874	return (-1);
875}
876
877static void
878addlen(size_t len, char **buf, size_t *buflen) {
879	assert(len <= *buflen);
880	*buf += len;
881	*buflen -= len;
882}
883
884static int
885addstr(const char *src, size_t len, char **buf, size_t *buflen) {
886	if (len >= *buflen) {
887		errno = ENOSPC;
888		return (-1);
889	}
890	memcpy(*buf, src, len);
891	addlen(len, buf, buflen);
892	**buf = '\0';
893	return (0);
894}
895
896static int
897addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
898	size_t save_buflen = *buflen;
899	char *save_buf = *buf;
900	int t;
901
902	if (spaced || len >= target - 1) {
903		T(addstr("  ", (size_t)2, buf, buflen));
904		spaced = 1;
905	} else {
906		for (t = (target - len - 1) / 8; t >= 0; t--)
907			if (addstr("\t", (size_t)1, buf, buflen) < 0) {
908				*buflen = save_buflen;
909				*buf = save_buf;
910				return (-1);
911			}
912		spaced = 0;
913	}
914	return (spaced);
915}
916