• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$NetBSD: ns_print.c,v 1.11 2012/03/13 21:13:39 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_print.c,v 1.12 2009/03/03 05:29:58 each Exp";
24 #else
25 __RCSID("$NetBSD: ns_print.c,v 1.11 2012/03/13 21:13:39 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 <assert.h>
41 #include <errno.h>
42 #ifdef ANDROID_CHANGES
43 #include "resolv_private.h"
44 #else
45 #include <resolv.h>
46 #endif
47 #include <stddef.h>
48 #include <string.h>
49 #include <ctype.h>
50 
51 #ifdef SPRINTF_CHAR
52 # define SPRINTF(x) ((int)strlen(sprintf/**/x))
53 #else
54 # define SPRINTF(x) (sprintf x)
55 #endif
56 
57 #ifndef MIN
58 #define	MIN(x,y)	((x)<(y)?(x):(y))
59 #endif
60 
61 /* Forward. */
62 
63 static size_t	prune_origin(const char *name, const char *origin);
64 static int	charstr(const u_char *rdata, const u_char *edata,
65 			char **buf, size_t *buflen);
66 static int	addname(const u_char *msg, size_t msglen,
67 			const u_char **p, const char *origin,
68 			char **buf, size_t *buflen);
69 static void	addlen(size_t len, char **buf, size_t *buflen);
70 static int	addstr(const char *src, size_t len,
71 		       char **buf, size_t *buflen);
72 static int	addtab(size_t len, size_t target, int spaced,
73 		       char **buf, size_t *buflen);
74 
75 /* Macros. */
76 
77 #define	T(x) \
78 	do { \
79 		if ((x) < 0) \
80 			return (-1); \
81 	} while (/*CONSTCOND*/0)
82 
83 static const char base32hex[] =
84         "0123456789ABCDEFGHIJKLMNOPQRSTUV=0123456789abcdefghijklmnopqrstuv";
85 /* Public. */
86 
87 /*
88  *	Convert an RR to presentation format.
89  *
90  * return:
91  *	Number of characters written to buf, or -1 (check errno).
92  */
93 int
ns_sprintrr(const ns_msg * handle,const ns_rr * rr,const char * name_ctx,const char * origin,char * buf,size_t buflen)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  *	Convert the fields of an RR into presentation format.
109  *
110  * return:
111  *	Number of characters written to buf, or -1 (check errno).
112  */
113 int
ns_sprintrrf(const u_char * msg,size_t msglen,const char * name,ns_class class,ns_type type,u_long ttl,const u_char * rdata,size_t rdlen,const char * name_ctx,const char * origin,char * buf,size_t buflen)114 ns_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 = (int)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, (socklen_t)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 	case ns_t_kx: {
270 		u_int t;
271 
272 		if (rdlen < (size_t)NS_INT16SZ)
273 			goto formerr;
274 
275 		/* Priority. */
276 		t = ns_get16(rdata);
277 		rdata += NS_INT16SZ;
278 		len = SPRINTF((tmp, "%u ", t));
279 		T(addstr(tmp, (size_t)len, &buf, &buflen));
280 
281 		/* Target. */
282 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
283 
284 		break;
285 	    }
286 
287 	case ns_t_px: {
288 		u_int t;
289 
290 		if (rdlen < (size_t)NS_INT16SZ)
291 			goto formerr;
292 
293 		/* Priority. */
294 		t = ns_get16(rdata);
295 		rdata += NS_INT16SZ;
296 		len = SPRINTF((tmp, "%u ", t));
297 		T(addstr(tmp, (size_t)len, &buf, &buflen));
298 
299 		/* Name1. */
300 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
301 		T(addstr(" ", (size_t)1, &buf, &buflen));
302 
303 		/* Name2. */
304 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
305 
306 		break;
307 	    }
308 
309 	case ns_t_x25:
310 		T(len = charstr(rdata, edata, &buf, &buflen));
311 		if (len == 0)
312 			goto formerr;
313 		rdata += len;
314 		break;
315 
316 	case ns_t_txt:
317 	case ns_t_spf:
318 		while (rdata < edata) {
319 			T(len = charstr(rdata, edata, &buf, &buflen));
320 			if (len == 0)
321 				goto formerr;
322 			rdata += len;
323 			if (rdata < edata)
324 				T(addstr(" ", (size_t)1, &buf, &buflen));
325 		}
326 		break;
327 
328 	case ns_t_nsap: {
329 		char t[2+255*3];
330 
331 		(void) inet_nsap_ntoa((int)rdlen, rdata, t);
332 		T(addstr(t, strlen(t), &buf, &buflen));
333 		break;
334 	    }
335 
336 	case ns_t_aaaa:
337 		if (rdlen != (size_t)NS_IN6ADDRSZ)
338 			goto formerr;
339 		(void) inet_ntop(AF_INET6, rdata, buf, (socklen_t)buflen);
340 		addlen(strlen(buf), &buf, &buflen);
341 		break;
342 
343 	case ns_t_loc: {
344 		char t[255];
345 
346 		/* XXX protocol format checking? */
347 		(void) loc_ntoa(rdata, t);
348 		T(addstr(t, strlen(t), &buf, &buflen));
349 		break;
350 	    }
351 
352 	case ns_t_naptr: {
353 		u_int order, preference;
354 		char t[50];
355 
356 		if (rdlen < 2U*NS_INT16SZ)
357 			goto formerr;
358 
359 		/* Order, Precedence. */
360 		order = ns_get16(rdata);	rdata += NS_INT16SZ;
361 		preference = ns_get16(rdata);	rdata += NS_INT16SZ;
362 		len = SPRINTF((t, "%u %u ", order, preference));
363 		T(addstr(t, (size_t)len, &buf, &buflen));
364 
365 		/* Flags. */
366 		T(len = charstr(rdata, edata, &buf, &buflen));
367 		if (len == 0)
368 			goto formerr;
369 		rdata += len;
370 		T(addstr(" ", (size_t)1, &buf, &buflen));
371 
372 		/* Service. */
373 		T(len = charstr(rdata, edata, &buf, &buflen));
374 		if (len == 0)
375 			goto formerr;
376 		rdata += len;
377 		T(addstr(" ", (size_t)1, &buf, &buflen));
378 
379 		/* Regexp. */
380 		T(len = charstr(rdata, edata, &buf, &buflen));
381 		if (len < 0)
382 			return (-1);
383 		if (len == 0)
384 			goto formerr;
385 		rdata += len;
386 		T(addstr(" ", (size_t)1, &buf, &buflen));
387 
388 		/* Server. */
389 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
390 		break;
391 	    }
392 
393 	case ns_t_srv: {
394 		u_int priority, weight, port;
395 		char t[50];
396 
397 		if (rdlen < 3U*NS_INT16SZ)
398 			goto formerr;
399 
400 		/* Priority, Weight, Port. */
401 		priority = ns_get16(rdata);  rdata += NS_INT16SZ;
402 		weight   = ns_get16(rdata);  rdata += NS_INT16SZ;
403 		port     = ns_get16(rdata);  rdata += NS_INT16SZ;
404 		len = SPRINTF((t, "%u %u %u ", priority, weight, port));
405 		T(addstr(t, (size_t)len, &buf, &buflen));
406 
407 		/* Server. */
408 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
409 		break;
410 	    }
411 
412 	case ns_t_minfo:
413 	case ns_t_rp:
414 		/* Name1. */
415 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
416 		T(addstr(" ", (size_t)1, &buf, &buflen));
417 
418 		/* Name2. */
419 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
420 
421 		break;
422 
423 	case ns_t_wks: {
424 		int n, lcnt;
425 
426 		if (rdlen < 1U + NS_INT32SZ)
427 			goto formerr;
428 
429 		/* Address. */
430 		(void) inet_ntop(AF_INET, rdata, buf, (socklen_t)buflen);
431 		addlen(strlen(buf), &buf, &buflen);
432 		rdata += NS_INADDRSZ;
433 
434 		/* Protocol. */
435 		len = SPRINTF((tmp, " %u ( ", *rdata));
436 		T(addstr(tmp, (size_t)len, &buf, &buflen));
437 		rdata += NS_INT8SZ;
438 
439 		/* Bit map. */
440 		n = 0;
441 		lcnt = 0;
442 		while (rdata < edata) {
443 			u_int c = *rdata++;
444 			do {
445 				if (c & 0200) {
446 					if (lcnt == 0) {
447 						T(addstr("\n\t\t\t\t", (size_t)5,
448 							 &buf, &buflen));
449 						lcnt = 10;
450 						spaced = 0;
451 					}
452 					len = SPRINTF((tmp, "%d ", n));
453 					T(addstr(tmp, (size_t)len, &buf, &buflen));
454 					lcnt--;
455 				}
456 				c <<= 1;
457 			} while (++n & 07);
458 		}
459 		T(addstr(")", (size_t)1, &buf, &buflen));
460 
461 		break;
462 	    }
463 
464 	case ns_t_key:
465 	case ns_t_dnskey: {
466 		char base64_key[NS_MD5RSA_MAX_BASE64];
467 		u_int keyflags, protocol, algorithm, key_id;
468 		const char *leader;
469 		int n;
470 
471 		if (rdlen < 0U + NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
472 			goto formerr;
473 
474 		/* Key flags, Protocol, Algorithm. */
475 #ifndef _LIBC
476 		key_id = dst_s_dns_key_id(rdata, edata-rdata);
477 #else
478 		key_id = 0;
479 #endif
480 		keyflags = ns_get16(rdata);  rdata += NS_INT16SZ;
481 		protocol = *rdata++;
482 		algorithm = *rdata++;
483 		len = SPRINTF((tmp, "0x%04x %u %u",
484 			       keyflags, protocol, algorithm));
485 		T(addstr(tmp, (size_t)len, &buf, &buflen));
486 
487 		/* Public key data. */
488 		len = b64_ntop(rdata, (size_t)(edata - rdata),
489 			       base64_key, sizeof base64_key);
490 		if (len < 0)
491 			goto formerr;
492 		if (len > 15) {
493 			T(addstr(" (", (size_t)2, &buf, &buflen));
494 			leader = "\n\t\t";
495 			spaced = 0;
496 		} else
497 			leader = " ";
498 		for (n = 0; n < len; n += 48) {
499 			T(addstr(leader, strlen(leader), &buf, &buflen));
500 			T(addstr(base64_key + n, (size_t)MIN(len - n, 48),
501 				 &buf, &buflen));
502 		}
503 		if (len > 15)
504 			T(addstr(" )", (size_t)2, &buf, &buflen));
505 		n = SPRINTF((tmp, " ; key_tag= %u", key_id));
506 		T(addstr(tmp, (size_t)n, &buf, &buflen));
507 
508 		break;
509 	    }
510 
511 	case ns_t_sig:
512 	case ns_t_rrsig: {
513 		char base64_key[NS_MD5RSA_MAX_BASE64];
514 		u_int typ, algorithm, labels, footprint;
515 		const char *leader;
516 		u_long t;
517 		int n;
518 
519 		if (rdlen < 22U)
520 			goto formerr;
521 
522 		/* Type covered, Algorithm, Label count, Original TTL. */
523 	        typ = ns_get16(rdata);  rdata += NS_INT16SZ;
524 		algorithm = *rdata++;
525 		labels = *rdata++;
526 		t = ns_get32(rdata);  rdata += NS_INT32SZ;
527 		len = SPRINTF((tmp, "%s %d %d %lu ",
528 			       p_type((int)typ), algorithm, labels, t));
529 		T(addstr(tmp, (size_t)len, &buf, &buflen));
530 		if (labels > (u_int)dn_count_labels(name))
531 			goto formerr;
532 
533 		/* Signature expiry. */
534 		t = ns_get32(rdata);  rdata += NS_INT32SZ;
535 		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
536 		T(addstr(tmp, (size_t)len, &buf, &buflen));
537 
538 		/* Time signed. */
539 		t = ns_get32(rdata);  rdata += NS_INT32SZ;
540 		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
541 		T(addstr(tmp, (size_t)len, &buf, &buflen));
542 
543 		/* Signature Footprint. */
544 		footprint = ns_get16(rdata);  rdata += NS_INT16SZ;
545 		len = SPRINTF((tmp, "%u ", footprint));
546 		T(addstr(tmp, (size_t)len, &buf, &buflen));
547 
548 		/* Signer's name. */
549 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
550 
551 		/* Signature. */
552 		len = b64_ntop(rdata, (size_t)(edata - rdata),
553 			       base64_key, sizeof base64_key);
554 		if (len > 15) {
555 			T(addstr(" (", (size_t)2, &buf, &buflen));
556 			leader = "\n\t\t";
557 			spaced = 0;
558 		} else
559 			leader = " ";
560 		if (len < 0)
561 			goto formerr;
562 		for (n = 0; n < len; n += 48) {
563 			T(addstr(leader, strlen(leader), &buf, &buflen));
564 			T(addstr(base64_key + n, (size_t)MIN(len - n, 48),
565 				 &buf, &buflen));
566 		}
567 		if (len > 15)
568 			T(addstr(" )", (size_t)2, &buf, &buflen));
569 		break;
570 	    }
571 
572 	case ns_t_nxt: {
573 		ptrdiff_t n, c;
574 
575 		/* Next domain name. */
576 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
577 
578 		/* Type bit map. */
579 		n = edata - rdata;
580 		for (c = 0; c < n*8; c++)
581 			if (NS_NXT_BIT_ISSET(c, rdata)) {
582 				len = SPRINTF((tmp, " %s", p_type((int)c)));
583 				T(addstr(tmp, (size_t)len, &buf, &buflen));
584 			}
585 		break;
586 	    }
587 
588 	case ns_t_cert: {
589 		u_int c_type, key_tag, alg;
590 		int n;
591 		size_t siz;
592 		char base64_cert[8192], tmp1[40];
593 		const char *leader;
594 
595 		c_type  = ns_get16(rdata); rdata += NS_INT16SZ;
596 		key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
597 		alg = (u_int) *rdata++;
598 
599 		len = SPRINTF((tmp1, "%d %d %d ", c_type, key_tag, alg));
600 		T(addstr(tmp1, (size_t)len, &buf, &buflen));
601 		siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
602 		if (siz > sizeof(base64_cert) * 3/4) {
603 			const char *str = "record too long to print";
604 			T(addstr(str, strlen(str), &buf, &buflen));
605 		}
606 		else {
607 			len = b64_ntop(rdata, (size_t)(edata-rdata),
608 			    base64_cert, siz);
609 
610 			if (len < 0)
611 				goto formerr;
612 			else if (len > 15) {
613 				T(addstr(" (", (size_t)2, &buf, &buflen));
614 				leader = "\n\t\t";
615 				spaced = 0;
616 			}
617 			else
618 				leader = " ";
619 
620 			for (n = 0; n < len; n += 48) {
621 				T(addstr(leader, strlen(leader),
622 					 &buf, &buflen));
623 				T(addstr(base64_cert + n, (size_t)MIN(len - n, 48),
624 					 &buf, &buflen));
625 			}
626 			if (len > 15)
627 				T(addstr(" )", (size_t)2, &buf, &buflen));
628 		}
629 		break;
630 	    }
631 
632 	case ns_t_tkey: {
633 		/* KJD - need to complete this */
634 		u_long t;
635 		int mode, err, keysize;
636 
637 		/* Algorithm name. */
638 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
639 		T(addstr(" ", (size_t)1, &buf, &buflen));
640 
641 		/* Inception. */
642 		t = ns_get32(rdata);  rdata += NS_INT32SZ;
643 		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
644 		T(addstr(tmp, (size_t)len, &buf, &buflen));
645 
646 		/* Experation. */
647 		t = ns_get32(rdata);  rdata += NS_INT32SZ;
648 		len = SPRINTF((tmp, "%s ", p_secstodate(t)));
649 		T(addstr(tmp, (size_t)len, &buf, &buflen));
650 
651 		/* Mode , Error, Key Size. */
652 		/* Priority, Weight, Port. */
653 		mode = ns_get16(rdata);  rdata += NS_INT16SZ;
654 		err  = ns_get16(rdata);  rdata += NS_INT16SZ;
655 		keysize  = ns_get16(rdata);  rdata += NS_INT16SZ;
656 		len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
657 		T(addstr(tmp, (size_t)len, &buf, &buflen));
658 
659 		/* XXX need to dump key, print otherdata length & other data */
660 		break;
661 	    }
662 
663 	case ns_t_tsig: {
664 		/* BEW - need to complete this */
665 		int n;
666 
667 		T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
668 		T(addstr(" ", (size_t)1, &buf, &buflen));
669 		rdata += 8; /* time */
670 		n = ns_get16(rdata); rdata += INT16SZ;
671 		rdata += n; /* sig */
672 		n = ns_get16(rdata); rdata += INT16SZ; /* original id */
673 		sprintf(buf, "%d", ns_get16(rdata));
674 		rdata += INT16SZ;
675 		addlen(strlen(buf), &buf, &buflen);
676 		break;
677 	    }
678 
679 	case ns_t_a6: {
680 		struct in6_addr a;
681 		int pbyte, pbit;
682 
683 		/* prefix length */
684 		if (rdlen == 0U) goto formerr;
685 		len = SPRINTF((tmp, "%d ", *rdata));
686 		T(addstr(tmp, (size_t)len, &buf, &buflen));
687 		pbit = *rdata;
688 		if (pbit > 128) goto formerr;
689 		pbyte = (pbit & ~7) / 8;
690 		rdata++;
691 
692 		/* address suffix: provided only when prefix len != 128 */
693 		if (pbit < 128) {
694 			if (rdata + pbyte >= edata) goto formerr;
695 			memset(&a, 0, sizeof(a));
696 			memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
697 			(void) inet_ntop(AF_INET6, &a, buf, (socklen_t)buflen);
698 			addlen(strlen(buf), &buf, &buflen);
699 			rdata += sizeof(a) - pbyte;
700 		}
701 
702 		/* prefix name: provided only when prefix len > 0 */
703 		if (pbit == 0)
704 			break;
705 		if (rdata >= edata) goto formerr;
706 		T(addstr(" ", (size_t)1, &buf, &buflen));
707 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
708 
709 		break;
710 	    }
711 
712 	case ns_t_opt: {
713 		len = SPRINTF((tmp, "%u bytes", class));
714 		T(addstr(tmp, (size_t)len, &buf, &buflen));
715 		break;
716 	    }
717 
718 	case ns_t_ds:
719 	case ns_t_dlv:
720 	case ns_t_sshfp: {
721 		u_int t;
722 
723 		if (type == ns_t_ds || type == ns_t_dlv) {
724 			if (rdlen < 4U) goto formerr;
725 			t = ns_get16(rdata);
726 			rdata += NS_INT16SZ;
727 			len = SPRINTF((tmp, "%u ", t));
728 			T(addstr(tmp, (size_t)len, &buf, &buflen));
729 		} else
730 			if (rdlen < 2U) goto formerr;
731 
732 		len = SPRINTF((tmp, "%u ", *rdata));
733 		T(addstr(tmp, (size_t)len, &buf, &buflen));
734 		rdata++;
735 
736 		len = SPRINTF((tmp, "%u ", *rdata));
737 		T(addstr(tmp, (size_t)len, &buf, &buflen));
738 		rdata++;
739 
740 		while (rdata < edata) {
741 			len = SPRINTF((tmp, "%02X", *rdata));
742 			T(addstr(tmp, (size_t)len, &buf, &buflen));
743 			rdata++;
744 		}
745 		break;
746 	    }
747 
748 	case ns_t_nsec3:
749 	case ns_t_nsec3param: {
750 		u_int t, w, l, j, k, c;
751 
752 		len = SPRINTF((tmp, "%u ", *rdata));
753 		T(addstr(tmp, (size_t)len, &buf, &buflen));
754 		rdata++;
755 
756 		len = SPRINTF((tmp, "%u ", *rdata));
757 		T(addstr(tmp, (size_t)len, &buf, &buflen));
758 		rdata++;
759 
760 		t = ns_get16(rdata);
761 		rdata += NS_INT16SZ;
762 		len = SPRINTF((tmp, "%u ", t));
763 		T(addstr(tmp, (size_t)len, &buf, &buflen));
764 
765 		t = *rdata++;
766 		if (t == 0) {
767 			T(addstr("-", 1, &buf, &buflen));
768 		} else {
769 			while (t-- > 0) {
770 				len = SPRINTF((tmp, "%02X", *rdata));
771 				T(addstr(tmp, (size_t)len, &buf, &buflen));
772 				rdata++;
773 			}
774 		}
775 		if (type == ns_t_nsec3param)
776 			break;
777 		T(addstr(" ", 1, &buf, &buflen));
778 
779 		t = *rdata++;
780 		while (t > 0) {
781 			switch (t) {
782 			case 1:
783 				tmp[0] = base32hex[(((uint32_t)rdata[0]>>3)&0x1f)];
784 				tmp[1] = base32hex[(((uint32_t)rdata[0]<<2)&0x1c)];
785 				tmp[2] = tmp[3] = tmp[4] = '=';
786 				tmp[5] = tmp[6] = tmp[7] = '=';
787 				break;
788 			case 2:
789 				tmp[0] = base32hex[(((uint32_t)rdata[0]>>3)&0x1f)];
790 				tmp[1] = base32hex[(((uint32_t)rdata[0]<<2)&0x1c)|
791 						   (((uint32_t)rdata[1]>>6)&0x03)];
792 				tmp[2] = base32hex[(((uint32_t)rdata[1]>>1)&0x1f)];
793 				tmp[3] = base32hex[(((uint32_t)rdata[1]<<4)&0x10)];
794 				tmp[4] = tmp[5] = tmp[6] = tmp[7] = '=';
795 				break;
796 			case 3:
797 				tmp[0] = base32hex[(((uint32_t)rdata[0]>>3)&0x1f)];
798 				tmp[1] = base32hex[(((uint32_t)rdata[0]<<2)&0x1c)|
799 						   (((uint32_t)rdata[1]>>6)&0x03)];
800 				tmp[2] = base32hex[(((uint32_t)rdata[1]>>1)&0x1f)];
801 				tmp[3] = base32hex[(((uint32_t)rdata[1]<<4)&0x10)|
802 						   (((uint32_t)rdata[2]>>4)&0x0f)];
803 				tmp[4] = base32hex[(((uint32_t)rdata[2]<<1)&0x1e)];
804 				tmp[5] = tmp[6] = tmp[7] = '=';
805 				break;
806 			case 4:
807 				tmp[0] = base32hex[(((uint32_t)rdata[0]>>3)&0x1f)];
808 				tmp[1] = base32hex[(((uint32_t)rdata[0]<<2)&0x1c)|
809 						   (((uint32_t)rdata[1]>>6)&0x03)];
810 				tmp[2] = base32hex[(((uint32_t)rdata[1]>>1)&0x1f)];
811 				tmp[3] = base32hex[(((uint32_t)rdata[1]<<4)&0x10)|
812 						   (((uint32_t)rdata[2]>>4)&0x0f)];
813 				tmp[4] = base32hex[(((uint32_t)rdata[2]<<1)&0x1e)|
814 						   (((uint32_t)rdata[3]>>7)&0x01)];
815 				tmp[5] = base32hex[(((uint32_t)rdata[3]>>2)&0x1f)];
816 				tmp[6] = base32hex[((uint32_t)rdata[3]<<3)&0x18];
817 				tmp[7] = '=';
818 				break;
819 			default:
820 				tmp[0] = base32hex[(((uint32_t)rdata[0]>>3)&0x1f)];
821 				tmp[1] = base32hex[(((uint32_t)rdata[0]<<2)&0x1c)|
822 						   (((uint32_t)rdata[1]>>6)&0x03)];
823 				tmp[2] = base32hex[(((uint32_t)rdata[1]>>1)&0x1f)];
824 				tmp[3] = base32hex[(((uint32_t)rdata[1]<<4)&0x10)|
825 						   (((uint32_t)rdata[2]>>4)&0x0f)];
826 				tmp[4] = base32hex[(((uint32_t)rdata[2]<<1)&0x1e)|
827 						   (((uint32_t)rdata[3]>>7)&0x01)];
828 				tmp[5] = base32hex[(((uint32_t)rdata[3]>>2)&0x1f)];
829 				tmp[6] = base32hex[(((uint32_t)rdata[3]<<3)&0x18)|
830 						   (((uint32_t)rdata[4]>>5)&0x07)];
831 				tmp[7] = base32hex[(rdata[4]&0x1f)];
832 				break;
833 			}
834 			T(addstr(tmp, 8, &buf, &buflen));
835 			if (t >= 5) {
836 				rdata += 5;
837 				t -= 5;
838 			} else {
839 				rdata += t;
840 				t -= t;
841 			}
842 		}
843 
844 		while (rdata < edata) {
845 			w = *rdata++;
846 			l = *rdata++;
847 			for (j = 0; j < l; j++) {
848 				if (rdata[j] == 0)
849 					continue;
850 				for (k = 0; k < 8; k++) {
851 					if ((rdata[j] & (0x80 >> k)) == 0)
852 						continue;
853 					c = w * 256 + j * 8 + k;
854 					len = SPRINTF((tmp, " %s", p_type((ns_type)c)));
855 					T(addstr(tmp, (size_t)len, &buf, &buflen));
856 				}
857 			}
858 			rdata += l;
859 		}
860 		break;
861 	    }
862 
863 	case ns_t_nsec: {
864 		u_int w, l, j, k, c;
865 
866 		T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
867 
868 		while (rdata < edata) {
869 			w = *rdata++;
870 			l = *rdata++;
871 			for (j = 0; j < l; j++) {
872 				if (rdata[j] == 0)
873 					continue;
874 				for (k = 0; k < 8; k++) {
875 					if ((rdata[j] & (0x80 >> k)) == 0)
876 						continue;
877 					c = w * 256 + j * 8 + k;
878 					len = SPRINTF((tmp, " %s", p_type((ns_type)c)));
879 					T(addstr(tmp, (size_t)len, &buf, &buflen));
880 				}
881 			}
882 			rdata += l;
883 		}
884 		break;
885 	    }
886 
887 	case ns_t_dhcid: {
888 		int n;
889 		unsigned int siz;
890 		char base64_dhcid[8192];
891 		const char *leader;
892 
893 		siz = (int)(edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
894 		if (siz > sizeof(base64_dhcid) * 3/4) {
895 			const char *str = "record too long to print";
896 			T(addstr(str, strlen(str), &buf, &buflen));
897 		} else {
898 			len = b64_ntop(rdata, (size_t)(edata-rdata),
899 			    base64_dhcid, siz);
900 
901 			if (len < 0)
902 				goto formerr;
903 
904 			else if (len > 15) {
905 				T(addstr(" (", 2, &buf, &buflen));
906 				leader = "\n\t\t";
907 				spaced = 0;
908 			}
909 			else
910 				leader = " ";
911 
912 			for (n = 0; n < len; n += 48) {
913 				T(addstr(leader, strlen(leader),
914 					 &buf, &buflen));
915 				T(addstr(base64_dhcid + n,
916 				    (size_t)MIN(len - n, 48), &buf, &buflen));
917 			}
918 			if (len > 15)
919 				T(addstr(" )", 2, &buf, &buflen));
920 		}
921 		break;
922 	}
923 
924 	case ns_t_ipseckey: {
925 		int n;
926 		unsigned int siz;
927 		char base64_key[8192];
928 		const char *leader;
929 
930 		if (rdlen < 2)
931 			goto formerr;
932 
933 		switch (rdata[1]) {
934 		case 0:
935 		case 3:
936 			if (rdlen < 3)
937 				goto formerr;
938 			break;
939 		case 1:
940 			if (rdlen < 7)
941 				goto formerr;
942 			break;
943 		case 2:
944 			if (rdlen < 19)
945 				goto formerr;
946 			break;
947 		default:
948 			comment = "unknown IPSECKEY gateway type";
949 			goto hexify;
950 		}
951 
952 		len = SPRINTF((tmp, "%u ", *rdata));
953 		T(addstr(tmp, (size_t)len, &buf, &buflen));
954 		rdata++;
955 
956 		len = SPRINTF((tmp, "%u ", *rdata));
957 		T(addstr(tmp, (size_t)len, &buf, &buflen));
958 		rdata++;
959 
960 		len = SPRINTF((tmp, "%u ", *rdata));
961 		T(addstr(tmp, (size_t)len, &buf, &buflen));
962 		rdata++;
963 
964 		switch (rdata[-2]) {
965 		case 0:
966 			T(addstr(".", 1, &buf, &buflen));
967 			break;
968 		case 1:
969 			(void) inet_ntop(AF_INET, rdata, buf, (socklen_t)buflen);
970 			addlen(strlen(buf), &buf, &buflen);
971 			rdata += 4;
972 			break;
973 		case 2:
974 			(void) inet_ntop(AF_INET6, rdata, buf, (socklen_t)buflen);
975 			addlen(strlen(buf), &buf, &buflen);
976 			rdata += 16;
977 			break;
978 		case 3:
979 			T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
980 			break;
981 		}
982 
983 		if (rdata >= edata)
984 			break;
985 
986 		siz = (int)(edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
987 		if (siz > sizeof(base64_key) * 3/4) {
988 			const char *str = "record too long to print";
989 			T(addstr(str, strlen(str), &buf, &buflen));
990 		} else {
991 			len = b64_ntop(rdata, (size_t)(edata-rdata),
992 			    base64_key, siz);
993 
994 			if (len < 0)
995 				goto formerr;
996 
997 			else if (len > 15) {
998 				T(addstr(" (", 2, &buf, &buflen));
999 				leader = "\n\t\t";
1000 				spaced = 0;
1001 			}
1002 			else
1003 				leader = " ";
1004 
1005 			for (n = 0; n < len; n += 48) {
1006 				T(addstr(leader, strlen(leader),
1007 					 &buf, &buflen));
1008 				T(addstr(base64_key + n,
1009 				    (size_t)MIN(len - n, 48), &buf, &buflen));
1010 			}
1011 			if (len > 15)
1012 				T(addstr(" )", 2, &buf, &buflen));
1013 		}
1014 		break;
1015 	}
1016 
1017 	case ns_t_hip: {
1018 		unsigned int i, hip_len, algorithm, key_len;
1019 		char base64_key[NS_MD5RSA_MAX_BASE64];
1020 		unsigned int siz;
1021 		const char *leader = "\n\t\t\t\t\t";
1022 
1023 		hip_len = *rdata++;
1024 		algorithm = *rdata++;
1025 		key_len = ns_get16(rdata);
1026 		rdata += NS_INT16SZ;
1027 
1028 		siz = key_len*4/3 + 4; /* "+4" accounts for trailing \0 */
1029 		if (siz > sizeof(base64_key) * 3/4) {
1030 			const char *str = "record too long to print";
1031 			T(addstr(str, strlen(str), &buf, &buflen));
1032 		} else {
1033 			len = sprintf(tmp, "( %u ", algorithm);
1034 			T(addstr(tmp, (size_t)len, &buf, &buflen));
1035 
1036 			for (i = 0; i < hip_len; i++) {
1037 				len = sprintf(tmp, "%02X", *rdata);
1038 				T(addstr(tmp, (size_t)len, &buf, &buflen));
1039 				rdata++;
1040 			}
1041 			T(addstr(leader, strlen(leader), &buf, &buflen));
1042 
1043 			len = b64_ntop(rdata, key_len, base64_key, siz);
1044 			if (len < 0)
1045 				goto formerr;
1046 
1047 			T(addstr(base64_key, (size_t)len, &buf, &buflen));
1048 
1049 			rdata += key_len;
1050 			while (rdata < edata) {
1051 				T(addstr(leader, strlen(leader), &buf, &buflen));
1052 				T(addname(msg, msglen, &rdata, origin,
1053 					  &buf, &buflen));
1054 			}
1055 			T(addstr(" )", 2, &buf, &buflen));
1056 		}
1057 		break;
1058 	    }
1059 
1060 	default:
1061 		comment = "unknown RR type";
1062 		goto hexify;
1063 	}
1064 	_DIAGASSERT(__type_fit(int, buf - obuf));
1065 	return (int)(buf - obuf);
1066  formerr:
1067 	comment = "RR format error";
1068  hexify: {
1069 	int n, m;
1070 	char *p;
1071 
1072 	len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata),
1073 		       rdlen != 0U ? " (" : "", comment));
1074 	T(addstr(tmp, (size_t)len, &buf, &buflen));
1075 	while (rdata < edata) {
1076 		p = tmp;
1077 		p += SPRINTF((p, "\n\t"));
1078 		spaced = 0;
1079 		n = MIN(16, (int)(edata - rdata));
1080 		for (m = 0; m < n; m++)
1081 			p += SPRINTF((p, "%02x ", rdata[m]));
1082 		T(addstr(tmp, (size_t)(p - tmp), &buf, &buflen));
1083 		if (n < 16) {
1084 			T(addstr(")", (size_t)1, &buf, &buflen));
1085 			T(addtab((size_t)(p - tmp + 1), (size_t)48, spaced, &buf, &buflen));
1086 		}
1087 		p = tmp;
1088 		p += SPRINTF((p, "; "));
1089 		for (m = 0; m < n; m++)
1090 			*p++ = (isascii(rdata[m]) && isprint(rdata[m]))
1091 				? rdata[m]
1092 				: '.';
1093 		T(addstr(tmp, (size_t)(p - tmp), &buf, &buflen));
1094 		rdata += n;
1095 	}
1096 	_DIAGASSERT(__type_fit(int, buf - obuf));
1097 	return (int)(buf - obuf);
1098     }
1099 }
1100 
1101 /* Private. */
1102 
1103 /*
1104  * size_t
1105  * prune_origin(name, origin)
1106  *	Find out if the name is at or under the current origin.
1107  * return:
1108  *	Number of characters in name before start of origin,
1109  *	or length of name if origin does not match.
1110  * notes:
1111  *	This function should share code with samedomain().
1112  */
1113 static size_t
prune_origin(const char * name,const char * origin)1114 prune_origin(const char *name, const char *origin) {
1115 	const char *oname = name;
1116 
1117 	while (*name != '\0') {
1118 		if (origin != NULL && ns_samename(name, origin) == 1)
1119 			return (name - oname - (name > oname));
1120 		while (*name != '\0') {
1121 			if (*name == '\\') {
1122 				name++;
1123 				/* XXX need to handle \nnn form. */
1124 				if (*name == '\0')
1125 					break;
1126 			} else if (*name == '.') {
1127 				name++;
1128 				break;
1129 			}
1130 			name++;
1131 		}
1132 	}
1133 	return (name - oname);
1134 }
1135 
1136 /*
1137  * int
1138  * charstr(rdata, edata, buf, buflen)
1139  *	Format a <character-string> into the presentation buffer.
1140  * return:
1141  *	Number of rdata octets consumed
1142  *	0 for protocol format error
1143  *	-1 for output buffer error
1144  * side effects:
1145  *	buffer is advanced on success.
1146  */
1147 static int
charstr(const u_char * rdata,const u_char * edata,char ** buf,size_t * buflen)1148 charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
1149 	const u_char *odata = rdata;
1150 	size_t save_buflen = *buflen;
1151 	char *save_buf = *buf;
1152 
1153 	if (addstr("\"", (size_t)1, buf, buflen) < 0)
1154 		goto enospc;
1155 	if (rdata < edata) {
1156 		int n = *rdata;
1157 
1158 		if (rdata + 1 + n <= edata) {
1159 			rdata++;
1160 			while (n-- > 0) {
1161 				if (strchr("\n\"\\", *rdata) != NULL)
1162 					if (addstr("\\", (size_t)1, buf, buflen) < 0)
1163 						goto enospc;
1164 				if (addstr((const char *)rdata, (size_t)1,
1165 					   buf, buflen) < 0)
1166 					goto enospc;
1167 				rdata++;
1168 			}
1169 		}
1170 	}
1171 	if (addstr("\"", (size_t)1, buf, buflen) < 0)
1172 		goto enospc;
1173 	_DIAGASSERT(__type_fit(int, rdata - odata));
1174 	return (int)(rdata - odata);
1175  enospc:
1176 	errno = ENOSPC;
1177 	*buf = save_buf;
1178 	*buflen = save_buflen;
1179 	return (-1);
1180 }
1181 
1182 static int
addname(const u_char * msg,size_t msglen,const u_char ** pp,const char * origin,char ** buf,size_t * buflen)1183 addname(const u_char *msg, size_t msglen,
1184 	const u_char **pp, const char *origin,
1185 	char **buf, size_t *buflen)
1186 {
1187 	size_t newlen, save_buflen = *buflen;
1188 	char *save_buf = *buf;
1189 	int n;
1190 
1191 	n = dn_expand(msg, msg + msglen, *pp, *buf, (int)*buflen);
1192 	if (n < 0)
1193 		goto enospc;	/* Guess. */
1194 	newlen = prune_origin(*buf, origin);
1195 	if (**buf == '\0') {
1196 		goto root;
1197 	} else if (newlen == 0U) {
1198 		/* Use "@" instead of name. */
1199 		if (newlen + 2 > *buflen)
1200 			goto enospc;        /* No room for "@\0". */
1201 		(*buf)[newlen++] = '@';
1202 		(*buf)[newlen] = '\0';
1203 	} else {
1204 		if (((origin == NULL || origin[0] == '\0') ||
1205 		    (origin[0] != '.' && origin[1] != '\0' &&
1206 		    (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
1207 			/* No trailing dot. */
1208  root:
1209 			if (newlen + 2 > *buflen)
1210 				goto enospc;	/* No room for ".\0". */
1211 			(*buf)[newlen++] = '.';
1212 			(*buf)[newlen] = '\0';
1213 		}
1214 	}
1215 	*pp += n;
1216 	addlen(newlen, buf, buflen);
1217 	**buf = '\0';
1218 	_DIAGASSERT(__type_fit(int, newlen));
1219 	return (int)newlen;
1220  enospc:
1221 	errno = ENOSPC;
1222 	*buf = save_buf;
1223 	*buflen = save_buflen;
1224 	return (-1);
1225 }
1226 
1227 static void
addlen(size_t len,char ** buf,size_t * buflen)1228 addlen(size_t len, char **buf, size_t *buflen) {
1229 	assert(len <= *buflen);
1230 	*buf += len;
1231 	*buflen -= len;
1232 }
1233 
1234 static int
addstr(const char * src,size_t len,char ** buf,size_t * buflen)1235 addstr(const char *src, size_t len, char **buf, size_t *buflen) {
1236 	if (len >= *buflen) {
1237 		errno = ENOSPC;
1238 		return (-1);
1239 	}
1240 	memcpy(*buf, src, len);
1241 	addlen(len, buf, buflen);
1242 	**buf = '\0';
1243 	return (0);
1244 }
1245 
1246 static int
addtab(size_t len,size_t target,int spaced,char ** buf,size_t * buflen)1247 addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
1248 	size_t save_buflen = *buflen;
1249 	char *save_buf = *buf;
1250 	ptrdiff_t t;
1251 
1252 	if (spaced || len >= target - 1) {
1253 		T(addstr("  ", (size_t)2, buf, buflen));
1254 		spaced = 1;
1255 	} else {
1256 		for (t = (target - len - 1) / 8; t >= 0; t--)
1257 			if (addstr("\t", (size_t)1, buf, buflen) < 0) {
1258 				*buflen = save_buflen;
1259 				*buf = save_buf;
1260 				return (-1);
1261 			}
1262 		spaced = 0;
1263 	}
1264 	return (spaced);
1265 }
1266