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