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