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