s390 specific implementations.
[kopensolaris-gnu/glibc.git] / resolv / res_debug.c
index c2fefb4..f4e9169 100644 (file)
@@ -1,9 +1,7 @@
 /*
- * ++Copyright++ 1985, 1990, 1993
- * -
- * Copyright (c) 1985, 1990, 1993
+ * Copyright (c) 1985
  *    The Regents of the University of California.  All rights reserved.
- *
+ * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
- *
+ * 
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * -
+ */
+
+/*
  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
- *
+ * 
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies, and that
  * the name of Digital Equipment Corporation not be used in advertising or
  * publicity pertaining to distribution of the document or software without
  * specific, written prior permission.
- *
+ * 
  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  * SOFTWARE.
- * -
- * --Copyright--
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)res_debug.c        8.1 (Berkeley) 6/4/93";
-static char rcsid[] = "$Id$";
+static const char sccsid[] = "@(#)res_debug.c  8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$BINDId: res_debug.c,v 8.34 2000/02/29 05:30:55 vixie Exp $";
 #endif /* LIBC_SCCS and not lint */
 
-#include <sys/param.h>
 #include <sys/types.h>
+#include <sys/param.h>
 #include <sys/socket.h>
+
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
 
-#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
 #include <netdb.h>
 #include <resolv.h>
-#if defined(BSD) && (BSD >= 199103) && defined(AF_INET6)
-# include <string.h>
-#else
-# include "../conf/portability.h"
-#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
 
-#if defined(USE_OPTIONS_H)
-# include "../conf/options.h"
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
 #endif
 
 extern const char *_res_opcodes[];
-extern const char *_res_resultcodes[];
+extern const char *_res_sectioncodes[];
 
-/* XXX: we should use getservbyport() instead. */
-static const char *
-dewks(wks)
-       int wks;
-{
-       static char nbuf[20];
-
-       switch (wks) {
-       case 5: return "rje";
-       case 7: return "echo";
-       case 9: return "discard";
-       case 11: return "systat";
-       case 13: return "daytime";
-       case 15: return "netstat";
-       case 17: return "qotd";
-       case 19: return "chargen";
-       case 20: return "ftp-data";
-       case 21: return "ftp";
-       case 23: return "telnet";
-       case 25: return "smtp";
-       case 37: return "time";
-       case 39: return "rlp";
-       case 42: return "name";
-       case 43: return "whois";
-       case 53: return "domain";
-       case 57: return "apts";
-       case 59: return "apfs";
-       case 67: return "bootps";
-       case 68: return "bootpc";
-       case 69: return "tftp";
-       case 77: return "rje";
-       case 79: return "finger";
-       case 87: return "link";
-       case 95: return "supdup";
-       case 100: return "newacct";
-       case 101: return "hostnames";
-       case 102: return "iso-tsap";
-       case 103: return "x400";
-       case 104: return "x400-snd";
-       case 105: return "csnet-ns";
-       case 109: return "pop-2";
-       case 111: return "sunrpc";
-       case 113: return "auth";
-       case 115: return "sftp";
-       case 117: return "uucp-path";
-       case 119: return "nntp";
-       case 121: return "erpc";
-       case 123: return "ntp";
-       case 133: return "statsrv";
-       case 136: return "profile";
-       case 144: return "NeWS";
-       case 161: return "snmp";
-       case 162: return "snmp-trap";
-       case 170: return "print-srv";
-       default: (void) sprintf(nbuf, "%d", wks); return (nbuf);
-       }
-}
+/*
+ * Print the current options.
+ */
+void
+fp_resstat(const res_state statp, FILE *file) {
+       u_long mask;
 
-/* XXX: we should use getprotobynumber() instead. */
-static const char *
-deproto(protonum)
-       int protonum;
-{
-       static char nbuf[20];
-
-       switch (protonum) {
-       case 1: return "icmp";
-       case 2: return "igmp";
-       case 3: return "ggp";
-       case 5: return "st";
-       case 6: return "tcp";
-       case 7: return "ucl";
-       case 8: return "egp";
-       case 9: return "igp";
-       case 11: return "nvp-II";
-       case 12: return "pup";
-       case 16: return "chaos";
-       case 17: return "udp";
-       default: (void) sprintf(nbuf, "%d", protonum); return (nbuf);
-       }
+       fprintf(file, ";; res options:");
+       for (mask = 1;  mask != 0;  mask <<= 1)
+               if (statp->options & mask)
+                       fprintf(file, " %s", p_option(mask));
+       putc('\n', file);
 }
 
-static const u_char *
-do_rrset(msg, len, cp, cnt, pflag, file, hs)
-       int cnt, pflag, len;
-       const u_char *cp, *msg;
-       const char *hs;
-       FILE *file;
+static void
+do_section(const res_state statp,
+          ns_msg *handle, ns_sect section,
+          int pflag, FILE *file)
 {
-       int n;
-       int sflag;
+       int n, sflag, rrnum;
+       static int buflen = 2048;
+       char *buf;
+       ns_opcode opcode;
+       ns_rr rr;
 
        /*
         * Print answer records.
         */
-       sflag = (_res.pfcode & pflag);
-       if (n = ntohs(cnt)) {
-               if ((!_res.pfcode) ||
-                   ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
-                       fprintf(file, hs);
-               while (--n >= 0) {
-                       if ((!_res.pfcode) || sflag) {
-                               cp = p_rr(cp, msg, file);
-                       } else {
-                               unsigned int dlen;
-                               cp += __dn_skipname(cp, cp + MAXCDNAME);
-                               cp += INT16SZ;
-                               cp += INT16SZ;
-                               cp += INT32SZ;
-                               dlen = _getshort((u_char*)cp);
-                               cp += INT16SZ;
-                               cp += dlen;
+       sflag = (statp->pfcode & pflag);
+       if (statp->pfcode && !sflag)
+               return;
+
+       buf = malloc(buflen);
+       if (buf == NULL) {
+               fprintf(file, ";; memory allocation failure\n");
+               return;
+       }
+
+       opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode);
+       rrnum = 0;
+       for (;;) {
+               if (ns_parserr(handle, section, rrnum, &rr)) {
+                       if (errno != ENODEV)
+                               fprintf(file, ";; ns_parserr: %s\n",
+                                       strerror(errno));
+                       else if (rrnum > 0 && sflag != 0 &&
+                                (statp->pfcode & RES_PRF_HEAD1))
+                               putc('\n', file);
+                       goto cleanup;
+               }
+               if (rrnum == 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1))
+                       fprintf(file, ";; %s SECTION:\n",
+                               p_section(section, opcode));
+               if (section == ns_s_qd)
+                       fprintf(file, ";;\t%s, type = %s, class = %s\n",
+                               ns_rr_name(rr),
+                               p_type(ns_rr_type(rr)),
+                               p_class(ns_rr_class(rr)));
+               else {
+                       n = ns_sprintrr(handle, &rr, NULL, NULL,
+                                       buf, buflen);
+                       if (n < 0) {
+                               if (errno == ENOSPC) {
+                                       free(buf);
+                                       buf = NULL;
+                                       if (buflen < 131072)
+                                               buf = malloc(buflen += 1024);
+                                       if (buf == NULL) {
+                                               fprintf(file,
+                                             ";; memory allocation failure\n");
+                                             return;
+                                       }
+                                       continue;
+                               }
+                               fprintf(file, ";; ns_sprintrr: %s\n",
+                                       strerror(errno));
+                               goto cleanup;
                        }
-                       if ((cp - msg) > len)
-                               return (NULL);
+                       fputs(buf, file);
+                       fputc('\n', file);
                }
-               if ((!_res.pfcode) ||
-                   ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
-                       putc('\n', file);
+               rrnum++;
        }
-       return (cp);
-}
-
-void
-__p_query(msg)
-       const u_char *msg;
-{
-       __fp_query(msg, stdout);
-}
-
-#ifdef ultrix
-/* ultrix 4.0's packaging has some icky packaging.  alias for it here.
- * there is more junk of this kind over in res_comp.c.
- */
-void
-p_query(msg)
-       const u_char *msg;
-{
-       __p_query(msg);
-}
-#endif
-
-/*
- * Print the current options.
- * This is intended to be primarily a debugging routine.
- */
-void
-__fp_resstat(statp, file)
-       struct __res_state *statp;
-       FILE *file;
-{
-       register u_long mask;
-
-       fprintf(file, ";; res options:");
-       if (!statp)
-               statp = &_res;
-       for (mask = 1;  mask != 0;  mask <<= 1)
-               if (statp->options & mask)
-                       fprintf(file, " %s", p_option(mask));
-       putc('\n', file);
+ cleanup:
+       if (buf != NULL)
+               free(buf);
 }
 
 /*
@@ -248,141 +214,79 @@ __fp_resstat(statp, file)
  * This is intended to be primarily a debugging routine.
  */
 void
-__fp_nquery(msg, len, file)
-       const u_char *msg;
-       int len;
-       FILE *file;
-{
-       register const u_char *cp, *endMark;
-       register const HEADER *hp;
-       register int n;
+res_pquery(const res_state statp, const u_char *msg, int len, FILE *file) {
+       ns_msg handle;
+       int qdcount, ancount, nscount, arcount;
+       u_int opcode, rcode, id;
 
-       if ((_res.options & RES_INIT) == 0 && res_init() == -1)
+       if (ns_initparse(msg, len, &handle) < 0) {
+               fprintf(file, ";; ns_initparse: %s\n", strerror(errno));
                return;
-
-#define TruncTest(x) if (x >= endMark) goto trunc
-#define        ErrorTest(x) if (x == NULL) goto error
+       }
+       opcode = ns_msg_getflag(handle, ns_f_opcode);
+       rcode = ns_msg_getflag(handle, ns_f_rcode);
+       id = ns_msg_id(handle);
+       qdcount = ns_msg_count(handle, ns_s_qd);
+       ancount = ns_msg_count(handle, ns_s_an);
+       nscount = ns_msg_count(handle, ns_s_ns);
+       arcount = ns_msg_count(handle, ns_s_ar);
 
        /*
         * Print header fields.
         */
-       hp = (HEADER *)msg;
-       cp = msg + HFIXEDSZ;
-       endMark = cp + len;
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEADX) || hp->rcode) {
-               fprintf(file, ";; ->>HEADER<<- opcode: %s, status: %s, id: %d",
-                       _res_opcodes[hp->opcode],
-                       _res_resultcodes[hp->rcode],
-                       ntohs(hp->id));
-               putc('\n', file);
-       }
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEADX))
+       if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX) || rcode)
+               fprintf(file,
+                       ";; ->>HEADER<<- opcode: %s, status: %s, id: %d\n",
+                       _res_opcodes[opcode], p_rcode(rcode), id);
+       if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX))
                putc(';', file);
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD2)) {
+       if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD2)) {
                fprintf(file, "; flags:");
-               if (hp->qr)
+               if (ns_msg_getflag(handle, ns_f_qr))
                        fprintf(file, " qr");
-               if (hp->aa)
+               if (ns_msg_getflag(handle, ns_f_aa))
                        fprintf(file, " aa");
-               if (hp->tc)
+               if (ns_msg_getflag(handle, ns_f_tc))
                        fprintf(file, " tc");
-               if (hp->rd)
+               if (ns_msg_getflag(handle, ns_f_rd))
                        fprintf(file, " rd");
-               if (hp->ra)
+               if (ns_msg_getflag(handle, ns_f_ra))
                        fprintf(file, " ra");
+               if (ns_msg_getflag(handle, ns_f_z))
+                       fprintf(file, " ??");
+               if (ns_msg_getflag(handle, ns_f_ad))
+                       fprintf(file, " ad");
+               if (ns_msg_getflag(handle, ns_f_cd))
+                       fprintf(file, " cd");
        }
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD1)) {
-               fprintf(file, "; Ques: %d", ntohs(hp->qdcount));
-               fprintf(file, ", Ans: %d", ntohs(hp->ancount));
-               fprintf(file, ", Auth: %d", ntohs(hp->nscount));
-               fprintf(file, ", Addit: %d", ntohs(hp->arcount));
+       if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD1)) {
+               fprintf(file, "; %s: %d",
+                       p_section(ns_s_qd, opcode), qdcount);
+               fprintf(file, ", %s: %d",
+                       p_section(ns_s_an, opcode), ancount);
+               fprintf(file, ", %s: %d",
+                       p_section(ns_s_ns, opcode), nscount);
+               fprintf(file, ", %s: %d",
+                       p_section(ns_s_ar, opcode), arcount);
        }
-       if ((!_res.pfcode) || (_res.pfcode &
+       if ((!statp->pfcode) || (statp->pfcode & 
                (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) {
                putc('\n',file);
        }
        /*
-        * Print question records.
+        * Print the various sections.
         */
-       if (n = ntohs(hp->qdcount)) {
-               if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                       fprintf(file, ";; QUESTIONS:\n");
-               while (--n >= 0) {
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               fprintf(file, ";;\t");
-                       TruncTest(cp);
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               cp = p_cdnname(cp, msg, len, file);
-                       else {
-                               int n;
-                               char name[MAXDNAME];
-
-                               if ((n = dn_expand(msg, msg+len, cp, name,
-                                               sizeof name)) < 0)
-                                       cp = NULL;
-                               else
-                                       cp += n;
-                       }
-                       ErrorTest(cp);
-                       TruncTest(cp);
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               fprintf(file, ", type = %s",
-                                       __p_type(_getshort((u_char*)cp)));
-                       cp += INT16SZ;
-                       TruncTest(cp);
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               fprintf(file, ", class = %s\n",
-                                       __p_class(_getshort((u_char*)cp)));
-                       cp += INT16SZ;
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               putc('\n', file);
-               }
-       }
-       /*
-        * Print authoritative answer records
-        */
-       TruncTest(cp);
-       cp = do_rrset(msg, len, cp, hp->ancount, RES_PRF_ANS, file,
-                     ";; ANSWERS:\n");
-       ErrorTest(cp);
-
-       /*
-        * print name server records
-        */
-       TruncTest(cp);
-       cp = do_rrset(msg, len, cp, hp->nscount, RES_PRF_AUTH, file,
-                     ";; AUTHORITY RECORDS:\n");
-       ErrorTest(cp);
-
-       TruncTest(cp);
-       /*
-        * print additional records
-        */
-       cp = do_rrset(msg, len, cp, hp->arcount, RES_PRF_ADD, file,
-                     ";; ADDITIONAL RECORDS:\n");
-       ErrorTest(cp);
-       return;
- trunc:
-       fprintf(file, "\n;; ...truncated\n");
-       return;
- error:
-       fprintf(file, "\n;; ...malformed\n");
-}
-
-void
-__fp_query(msg, file)
-       const u_char *msg;
-       FILE *file;
-{
-       fp_nquery(msg, PACKETSZ, file);
+       do_section(statp, &handle, ns_s_qd, RES_PRF_QUES, file);
+       do_section(statp, &handle, ns_s_an, RES_PRF_ANS, file);
+       do_section(statp, &handle, ns_s_ns, RES_PRF_AUTH, file);
+       do_section(statp, &handle, ns_s_ar, RES_PRF_ADD, file);
+       if (qdcount == 0 && ancount == 0 &&
+           nscount == 0 && arcount == 0)
+               putc('\n', file);
 }
 
 const u_char *
-__p_cdnname(cp, msg, len, file)
-       const u_char *cp, *msg;
-       int len;
-       FILE *file;
-{
+p_cdnname(const u_char *cp, const u_char *msg, int len, FILE *file) {
        char name[MAXDNAME];
        int n;
 
@@ -396,380 +300,263 @@ __p_cdnname(cp, msg, len, file)
 }
 
 const u_char *
-__p_cdname(cp, msg, file)
-       const u_char *cp, *msg;
-       FILE *file;
-{
+p_cdname(const u_char *cp, const u_char *msg, FILE *file) {
        return (p_cdnname(cp, msg, PACKETSZ, file));
 }
 
-/* XXX:        the rest of these functions need to become length-limited, too. (vix)
- */
+/* Return a fully-qualified domain name from a compressed name (with
  length supplied).  */
 
 const u_char *
-__p_fqname(cp, msg, file)
+p_fqnname(cp, msg, msglen, name, namelen)
        const u_char *cp, *msg;
-       FILE *file;
+       int msglen;
+       char *name;
+       int namelen;
 {
-       char name[MAXDNAME];
-       int n;
+       int n, newlen;
 
-       if ((n = dn_expand(msg, cp + MAXCDNAME, cp, name, sizeof name)) < 0)
+       if ((n = dn_expand(msg, cp + msglen, cp, name, namelen)) < 0)
                return (NULL);
-       if (name[0] == '\0') {
-               putc('.', file);
-       } else {
-               fputs(name, file);
-               if (name[strlen(name) - 1] != '.')
-                       putc('.', file);
+       newlen = strlen(name);
+       if (newlen == 0 || name[newlen - 1] != '.') {
+               if (newlen + 1 >= namelen)      /* Lack space for final dot */
+                       return (NULL);
+               else
+                       strcpy(name + newlen, ".");
        }
        return (cp + n);
 }
 
-/*
- * Print resource record fields in human readable form.
- */
+/* XXX:        the rest of these functions need to become length-limited, too. */
+
 const u_char *
-__p_rr(cp, msg, file)
-       const u_char *cp, *msg;
-       FILE *file;
-{
-       int type, class, dlen, n, c;
-       struct in_addr inaddr;
-       const u_char *cp1, *cp2;
-       u_int32_t tmpttl, t;
-       int lcnt;
-
-       if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
-               h_errno = NETDB_INTERNAL;
+p_fqname(const u_char *cp, const u_char *msg, FILE *file) {
+       char name[MAXDNAME];
+       const u_char *n;
+
+       n = p_fqnname(cp, msg, MAXCDNAME, name, sizeof name);
+       if (n == NULL)
                return (NULL);
-       }
-       if ((cp = p_fqname(cp, msg, file)) == NULL)
-               return (NULL);                  /* compression error */
-       type = _getshort((u_char*)cp);
-       cp += INT16SZ;
-       class = _getshort((u_char*)cp);
-       cp += INT16SZ;
-       tmpttl = _getlong((u_char*)cp);
-       cp += INT32SZ;
-       dlen = _getshort((u_char*)cp);
-       cp += INT16SZ;
-       cp1 = cp;
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_TTLID))
-               fprintf(file, "\t%lu", (u_long)tmpttl);
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_CLASS))
-               fprintf(file, "\t%s", __p_class(class));
-       fprintf(file, "\t%s", __p_type(type));
-       /*
-        * Print type specific data, if appropriate
-        */
-       switch (type) {
-       case T_A:
-               switch (class) {
-               case C_IN:
-               case C_HS:
-                       bcopy(cp, (char *)&inaddr, INADDRSZ);
-                       if (dlen == 4) {
-                               fprintf(file, "\t%s", inet_ntoa(inaddr));
-                               cp += dlen;
-                       } else if (dlen == 7) {
-                               char *address;
-                               u_char protocol;
-                               u_short port;
-
-                               address = inet_ntoa(inaddr);
-                               cp += INADDRSZ;
-                               protocol = *(u_char*)cp;
-                               cp += sizeof(u_char);
-                               port = _getshort((u_char*)cp);
-                               cp += INT16SZ;
-                               fprintf(file, "\t%s\t; proto %d, port %d",
-                                       address, protocol, port);
-                       }
-                       break;
-               default:
-                       cp += dlen;
-               }
-               break;
-       case T_CNAME:
-       case T_MB:
-       case T_MG:
-       case T_MR:
-       case T_NS:
-       case T_PTR:
-               putc('\t', file);
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               break;
+       fputs(name, file);
+       return (n);
+}
 
-       case T_HINFO:
-       case T_ISDN:
-               (void) fputs("\t\"", file);
-               cp2 = cp + dlen;
-               if ((n = (unsigned char) *cp++) != 0) {
-                       for (c = n; c > 0 && cp < cp2; c--) {
-                               if (strchr("\n\"\\", *cp))
-                                       (void) putc('\\', file);
-                               (void) putc(*cp++, file);
-                       }
-                       putc('"', file);
-               }
-               if (cp < cp2 && (n = (unsigned char) *cp++) != 0) {
-                       (void) fputs ("\t\"", file);
-                       for (c = n; c > 0 && cp < cp2; c--) {
-                               if (strchr("\n\"\\", *cp))
-                                       (void) putc('\\', file);
-                               (void) putc(*cp++, file);
-                       }
-                       putc('"', file);
-               } else if (type == T_HINFO) {
-                       (void) fputs("\"?\"", file);
-                       fprintf(file, "\n;; *** Warning *** OS-type missing");
-               }
-               break;
+/*
+ * Names of RR classes and qclasses.  Classes and qclasses are the same, except
+ * that C_ANY is a qclass but not a class.  (You can ask for records of class
+ * C_ANY, but you can't have any records of that class in the database.)
+ */
+const struct res_sym __p_class_syms[] = {
+       {C_IN,          "IN"},
+       {C_CHAOS,       "CHAOS"},
+       {C_HS,          "HS"},
+       {C_HS,          "HESIOD"},
+       {C_ANY,         "ANY"},
+       {C_NONE,        "NONE"},
+       {C_IN,          (char *)0}
+};
 
-       case T_SOA:
-               putc('\t', file);
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               putc(' ', file);
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               fputs(" (\n", file);
-               t = _getlong((u_char*)cp);  cp += INT32SZ;
-               fprintf(file, "\t\t\t%lu\t; serial\n", (u_long)t);
-               t = _getlong((u_char*)cp);  cp += INT32SZ;
-               fprintf(file, "\t\t\t%lu\t; refresh (%s)\n",
-                       (u_long)t, __p_time(t));
-               t = _getlong((u_char*)cp);  cp += INT32SZ;
-               fprintf(file, "\t\t\t%lu\t; retry (%s)\n",
-                       (u_long)t, __p_time(t));
-               t = _getlong((u_char*)cp);  cp += INT32SZ;
-               fprintf(file, "\t\t\t%lu\t; expire (%s)\n",
-                       (u_long)t, __p_time(t));
-               t = _getlong((u_char*)cp);  cp += INT32SZ;
-               fprintf(file, "\t\t\t%lu )\t; minimum (%s)",
-                       (u_long)t, __p_time(t));
-               break;
+/*
+ * Names of message sections.
+ */
+const struct res_sym __p_default_section_syms[] = {
+       {ns_s_qd,       "QUERY"},
+       {ns_s_an,       "ANSWER"},
+       {ns_s_ns,       "AUTHORITY"},
+       {ns_s_ar,       "ADDITIONAL"},
+       {0,             (char *)0}
+};
+
+const struct res_sym __p_update_section_syms[] = {
+       {S_ZONE,        "ZONE"},
+       {S_PREREQ,      "PREREQUISITE"},
+       {S_UPDATE,      "UPDATE"},
+       {S_ADDT,        "ADDITIONAL"},
+       {0,             (char *)0}
+};
+
+const struct res_sym __p_key_syms[] = {
+       {NS_ALG_MD5RSA,         "RSA",          "RSA KEY with MD5 hash"},
+       {NS_ALG_DH,             "DH",           "Diffie Hellman"},
+       {NS_ALG_DSA,            "DSA",          "Digital Signature Algorithm"},
+       {NS_ALG_EXPIRE_ONLY,    "EXPIREONLY",   "No algorithm"},
+       {NS_ALG_PRIVATE_OID,    "PRIVATE",      "Algorithm obtained from OID"},
+       {0,                     NULL,           NULL}
+};
+
+const struct res_sym __p_cert_syms[] = {
+       {cert_t_pkix,   "PKIX",         "PKIX (X.509v3) Certificate"},
+       {cert_t_spki,   "SPKI",         "SPKI certificate"},
+       {cert_t_pgp,    "PGP",          "PGP certificate"},
+       {cert_t_url,    "URL",          "URL Private"},
+       {cert_t_oid,    "OID",          "OID Private"},
+       {0,             NULL,           NULL}
+};
 
-       case T_MX:
-       case T_AFSDB:
-       case T_RT:
-               fprintf(file, "\t%d ", _getshort((u_char*)cp));
-               cp += INT16SZ;
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               break;
+/*
+ * Names of RR types and qtypes.  Types and qtypes are the same, except
+ * that T_ANY is a qtype but not a type.  (You can ask for records of type
+ * T_ANY, but you can't have any records of that type in the database.)
+ */
+const struct res_sym __p_type_syms[] = {
+       {ns_t_a,        "A",            "address"},
+       {ns_t_ns,       "NS",           "name server"},
+       {ns_t_md,       "MD",           "mail destination (deprecated)"},
+       {ns_t_mf,       "MF",           "mail forwarder (deprecated)"},
+       {ns_t_cname,    "CNAME",        "canonical name"},
+       {ns_t_soa,      "SOA",          "start of authority"},
+       {ns_t_mb,       "MB",           "mailbox"},
+       {ns_t_mg,       "MG",           "mail group member"},
+       {ns_t_mr,       "MR",           "mail rename"},
+       {ns_t_null,     "NULL",         "null"},
+       {ns_t_wks,      "WKS",          "well-known service (deprecated)"},
+       {ns_t_ptr,      "PTR",          "domain name pointer"},
+       {ns_t_hinfo,    "HINFO",        "host information"},
+       {ns_t_minfo,    "MINFO",        "mailbox information"},
+       {ns_t_mx,       "MX",           "mail exchanger"},
+       {ns_t_txt,      "TXT",          "text"},
+       {ns_t_rp,       "RP",           "responsible person"},
+       {ns_t_afsdb,    "AFSDB",        "DCE or AFS server"},
+       {ns_t_x25,      "X25",          "X25 address"},
+       {ns_t_isdn,     "ISDN",         "ISDN address"},
+       {ns_t_rt,       "RT",           "router"},
+       {ns_t_nsap,     "NSAP",         "nsap address"},
+       {ns_t_nsap_ptr, "NSAP_PTR",     "domain name pointer"},
+       {ns_t_sig,      "SIG",          "signature"},
+       {ns_t_key,      "KEY",          "key"},
+       {ns_t_px,       "PX",           "mapping information"},
+       {ns_t_gpos,     "GPOS",         "geographical position (withdrawn)"},
+       {ns_t_aaaa,     "AAAA",         "IPv6 address"},
+       {ns_t_loc,      "LOC",          "location"},
+       {ns_t_nxt,      "NXT",          "next valid name (unimplemented)"},
+       {ns_t_eid,      "EID",          "endpoint identifier (unimplemented)"},
+       {ns_t_nimloc,   "NIMLOC",       "NIMROD locator (unimplemented)"},
+       {ns_t_srv,      "SRV",          "server selection"},
+       {ns_t_atma,     "ATMA",         "ATM address (unimplemented)"},
+       {ns_t_tsig,     "TSIG",         "transaction signature"},
+       {ns_t_ixfr,     "IXFR",         "incremental zone transfer"},
+       {ns_t_axfr,     "AXFR",         "zone transfer"},
+       {ns_t_zxfr,     "ZXFR",         "compressed zone transfer"},
+       {ns_t_mailb,    "MAILB",        "mailbox-related data (deprecated)"},
+       {ns_t_maila,    "MAILA",        "mail agent (deprecated)"},
+       {ns_t_naptr,    "NAPTR",        "URN Naming Authority"},
+       {ns_t_kx,       "KX",           "Key Exchange"},
+       {ns_t_cert,     "CERT",         "Certificate"},
+       {ns_t_any,      "ANY",          "\"any\""},
+       {0,             NULL,           NULL}
+};
 
-       case T_PX:
-               fprintf(file, "\t%d ", _getshort((u_char*)cp));
-               cp += INT16SZ;
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               putc(' ', file);
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               break;
+/*
+ * Names of DNS rcodes.
+ */
+const struct res_sym __p_rcode_syms[] = {
+       {ns_r_noerror,  "NOERROR",              "no error"},
+       {ns_r_formerr,  "FORMERR",              "format error"},
+       {ns_r_servfail, "SERVFAIL",             "server failed"},
+       {ns_r_nxdomain, "NXDOMAIN",             "no such domain name"},
+       {ns_r_notimpl,  "NOTIMP",               "not implemented"},
+       {ns_r_refused,  "REFUSED",              "refused"},
+       {ns_r_yxdomain, "YXDOMAIN",             "domain name exists"},
+       {ns_r_yxrrset,  "YXRRSET",              "rrset exists"},
+       {ns_r_nxrrset,  "NXRRSET",              "rrset doesn't exist"},
+       {ns_r_notauth,  "NOTAUTH",              "not authoritative"},
+       {ns_r_notzone,  "NOTZONE",              "Not in zone"},
+       {ns_r_max,      "",                     ""},
+       {ns_r_badsig,   "BADSIG",               "bad signature"},
+       {ns_r_badkey,   "BADKEY",               "bad key"},
+       {ns_r_badtime,  "BADTIME",              "bad time"},
+       {0,             NULL,                   NULL}
+};
 
-       case T_TXT:
-       case T_X25:
-               (void) fputs("\t\"", file);
-               cp2 = cp1 + dlen;
-               while (cp < cp2) {
-                       if (n = (unsigned char) *cp++) {
-                               for (c = n; c > 0 && cp < cp2; c--) {
-                                       if (strchr("\n\"\\", *cp))
-                                               (void) putc('\\', file);
-                                       (void) putc(*cp++, file);
-                               }
-                       }
+int
+sym_ston(const struct res_sym *syms, const char *name, int *success) {
+       for ((void)NULL; syms->name != 0; syms++) {
+               if (strcasecmp (name, syms->name) == 0) {
+                       if (success)
+                               *success = 1;
+                       return (syms->number);
                }
-               putc('"', file);
-               break;
-
-       case T_NSAP:
-               (void) fprintf(file, "\t%s", inet_nsap_ntoa(dlen, cp, NULL));
-               cp += dlen;
-               break;
-
-       case T_AAAA: {
-               char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
-
-               fprintf(file, "\t%s\n", inet_ntop(AF_INET6, cp, t, sizeof t));
-               cp += dlen;
-               break;
-       }
-
-       case T_LOC: {
-               char t[255];
-
-               (void) fprintf(file, "\t%s\n", loc_ntoa(cp, t));
-               cp += dlen;
-               break;
        }
+       if (success)
+               *success = 0;
+       return (syms->number);          /* The default value. */
+}
 
-       case T_MINFO:
-       case T_RP:
-               putc('\t', file);
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               putc(' ', file);
-               if ((cp = p_fqname(cp, msg, file)) == NULL)
-                       return (NULL);
-               break;
-
-       case T_UINFO:
-               putc('\t', file);
-               fputs((char *)cp, file);
-               cp += dlen;
-               break;
-
-       case T_UID:
-       case T_GID:
-               if (dlen == 4) {
-                       fprintf(file, "\t%u", _getlong((u_char*)cp));
-                       cp += INT32SZ;
+const char *
+sym_ntos(const struct res_sym *syms, int number, int *success) {
+       static char unname[20];
+
+       for ((void)NULL; syms->name != 0; syms++) {
+               if (number == syms->number) {
+                       if (success)
+                               *success = 1;
+                       return (syms->name);
                }
-               break;
+       }
 
-       case T_WKS:
-               if (dlen < INT32SZ + 1)
-                       break;
-               bcopy(cp, (char *)&inaddr, INADDRSZ);
-               cp += INT32SZ;
-               fprintf(file, "\t%s %s ( ",
-                       inet_ntoa(inaddr),
-                       deproto((int) *cp));
-               cp += sizeof(u_char);
-               n = 0;
-               lcnt = 0;
-               while (cp < cp1 + dlen) {
-                       c = *cp++;
-                       do {
-                               if (c & 0200) {
-                                       if (lcnt == 0) {
-                                               fputs("\n\t\t\t", file);
-                                               lcnt = 5;
-                                       }
-                                       fputs(dewks(n), file);
-                                       putc(' ', file);
-                                       lcnt--;
-                               }
-                               c <<= 1;
-                       } while (++n & 07);
-               }
-               putc(')', file);
-               break;
+       sprintf(unname, "%d", number);          /* XXX nonreentrant */
+       if (success)
+               *success = 0;
+       return (unname);
+}
 
-#ifdef ALLOW_T_UNSPEC
-       case T_UNSPEC:
-               {
-                       int NumBytes = 8;
-                       u_char *DataPtr;
-                       int i;
-
-                       if (dlen < NumBytes) NumBytes = dlen;
-                       fprintf(file, "\tFirst %d bytes of hex data:",
-                               NumBytes);
-                       for (i = 0, DataPtr = cp; i < NumBytes; i++, DataPtr++)
-                               fprintf(file, " %x", *DataPtr);
-                       cp += dlen;
+const char *
+sym_ntop(const struct res_sym *syms, int number, int *success) {
+       static char unname[20];
+
+       for ((void)NULL; syms->name != 0; syms++) {
+               if (number == syms->number) {
+                       if (success)
+                               *success = 1;
+                       return (syms->humanname);
                }
-               break;
-#endif /* ALLOW_T_UNSPEC */
-
-       default:
-               fprintf(file, "\t?%d?", type);
-               cp += dlen;
        }
-#if 0
-       fprintf(file, "\t; dlen=%d, ttl %s\n", dlen, __p_time(tmpttl));
-#else
-       putc('\n', file);
-#endif
-       if (cp - cp1 != dlen) {
-               fprintf(file, ";; packet size error (found %d, dlen was %d)\n",
-                       cp - cp1, dlen);
-               cp = NULL;
-       }
-       return (cp);
+       sprintf(unname, "%d", number);          /* XXX nonreentrant */
+       if (success)
+               *success = 0;
+       return (unname);
 }
 
 /*
- * Return a string for the type
+ * Return a string for the type.
  */
 const char *
-__p_type(type)
-       int type;
-{
-       static char nbuf[20];
-
-       switch (type) {
-       case T_A:       return "A";
-       case T_NS:      return "NS";
-       case T_CNAME:   return "CNAME";
-       case T_SOA:     return "SOA";
-       case T_MB:      return "MB";
-       case T_MG:      return "MG";
-       case T_MR:      return "MR";
-       case T_NULL:    return "NULL";
-       case T_WKS:     return "WKS";
-       case T_PTR:     return "PTR";
-       case T_HINFO:   return "HINFO";
-       case T_MINFO:   return "MINFO";
-       case T_MX:      return "MX";
-       case T_TXT:     return "TXT";
-       case T_RP:      return "RP";
-       case T_AFSDB:   return "AFSDB";
-       case T_X25:     return "X25";
-       case T_ISDN:    return "ISDN";
-       case T_RT:      return "RT";
-       case T_NSAP:    return "NSAP";
-       case T_NSAP_PTR: return "NSAP_PTR";
-       case T_SIG:     return "SIG";
-       case T_KEY:     return "KEY";
-       case T_PX:      return "PX";
-       case T_GPOS:    return "GPOS";
-       case T_AAAA:    return "AAAA";
-       case T_LOC:     return "LOC";
-       case T_AXFR:    return "AXFR";
-       case T_MAILB:   return "MAILB";
-       case T_MAILA:   return "MAILA";
-       case T_ANY:     return "ANY";
-       case T_UINFO:   return "UINFO";
-       case T_UID:     return "UID";
-       case T_GID:     return "GID";
-#ifdef ALLOW_T_UNSPEC
-       case T_UNSPEC:  return "UNSPEC";
-#endif /* ALLOW_T_UNSPEC */
-       default:        (void)sprintf(nbuf, "%d", type); return (nbuf);
-       }
+p_type(int type) {
+       return (sym_ntos(__p_type_syms, type, (int *)0));
 }
 
 /*
- * Return a mnemonic for class
+ * Return a string for the type.
  */
 const char *
-__p_class(class)
-       int class;
-{
-       static char nbuf[20];
+p_section(int section, int opcode) {
+       const struct res_sym *symbols;
 
-       switch (class) {
-       case C_IN:      return "IN";
-       case C_HS:      return "HS";
-       case C_ANY:     return "ANY";
-       default:        (void)sprintf(nbuf, "%d", class); return (nbuf);
+       switch (opcode) {
+       case ns_o_update:
+               symbols = __p_update_section_syms;
+               break;
+       default:
+               symbols = __p_default_section_syms;
+               break;
        }
+       return (sym_ntos(symbols, section, (int *)0));
+}
+
+/*
+ * Return a mnemonic for class.
+ */
+const char *
+p_class(int class) {
+       return (sym_ntos(__p_class_syms, class, (int *)0));
 }
 
 /*
  * Return a mnemonic for an option
  */
 const char *
-__p_option(option)
-       u_long option;
-{
+p_option(u_long option) {
        static char nbuf[40];
 
        switch (option) {
@@ -785,63 +572,33 @@ __p_option(option)
        case RES_DNSRCH:        return "dnsrch";
        case RES_INSECURE1:     return "insecure1";
        case RES_INSECURE2:     return "insecure2";
+                               /* XXX nonreentrant */
        default:                sprintf(nbuf, "?0x%lx?", (u_long)option);
                                return (nbuf);
        }
 }
 
 /*
- * Return a mnemonic for a time to live
+ * Return a mnemonic for a time to live.
  */
-char *
-__p_time(value)
-       u_int32_t value;
-{
-       static char nbuf[40];
-       int secs, mins, hours, days;
-       register char *p;
-
-       if (value == 0) {
-               strcpy(nbuf, "0 secs");
-               return (nbuf);
-       }
+const char *
+p_time(u_int32_t value) {
+       static char nbuf[40];           /* XXX nonreentrant */
 
-       secs = value % 60;
-       value /= 60;
-       mins = value % 60;
-       value /= 60;
-       hours = value % 24;
-       value /= 24;
-       days = value;
-       value = 0;
-
-#define        PLURALIZE(x)    x, (x == 1) ? "" : "s"
-       p = nbuf;
-       if (days) {
-               (void)sprintf(p, "%d day%s", PLURALIZE(days));
-               while (*++p);
-       }
-       if (hours) {
-               if (days)
-                       *p++ = ' ';
-               (void)sprintf(p, "%d hour%s", PLURALIZE(hours));
-               while (*++p);
-       }
-       if (mins) {
-               if (days || hours)
-                       *p++ = ' ';
-               (void)sprintf(p, "%d min%s", PLURALIZE(mins));
-               while (*++p);
-       }
-       if (secs || ! (days || hours || mins)) {
-               if (days || hours || mins)
-                       *p++ = ' ';
-               (void)sprintf(p, "%d sec%s", PLURALIZE(secs));
-       }
+       if (ns_format_ttl(value, nbuf, sizeof nbuf) < 0)
+               sprintf(nbuf, "%u", value);
        return (nbuf);
 }
 
 /*
+ * Return a string for the rcode.
+ */
+const char *
+p_rcode(int rcode) {
+       return (sym_ntos(__p_rcode_syms, rcode, (int *)0));
+}
+
+/*
  * routines to convert between on-the-wire RR format and zone file format.
  * Does not contain conversion to/from decimal degrees; divide or multiply
  * by 60*60*1000 for that.
@@ -855,7 +612,7 @@ static const char *
 precsize_ntoa(prec)
        u_int8_t prec;
 {
-       static char retbuf[sizeof("90000000.00")];
+       static char retbuf[sizeof "90000000.00"];       /* XXX nonreentrant */
        unsigned long val;
        int mantissa, exponent;
 
@@ -875,9 +632,9 @@ precsize_aton(strptr)
 {
        unsigned int mval = 0, cmval = 0;
        u_int8_t retval = 0;
-       register char *cp;
-       register int exponent;
-       register int mantissa;
+       char *cp;
+       int exponent;
+       int mantissa;
 
        cp = *strptr;
 
@@ -916,7 +673,7 @@ latlon2ul(latlonstrptr,which)
        char **latlonstrptr;
        int *which;
 {
-       register char *cp;
+       char *cp;
        u_int32_t retval;
        int deg = 0, min = 0, secs = 0, secsfrac = 0;
 
@@ -1042,11 +799,11 @@ loc_aton(ascii, binary)
                        longit = lltemp1;
                        latit = lltemp2;
                } else {        /* some kind of brokenness */
-                       return 0;
+                       return (0);
                }
                break;
        default:                /* we didn't get one of each */
-               return 0;
+               return (0);
        }
 
        /* altitude */
@@ -1054,7 +811,7 @@ loc_aton(ascii, binary)
                altsign = -1;
                cp++;
        }
-
+    
        if (*cp == '+')
                cp++;
 
@@ -1083,7 +840,7 @@ loc_aton(ascii, binary)
                goto defaults;
 
        siz = precsize_aton(&cp);
-
+       
        while (!isspace(*cp) && (cp < maxcp))   /* if trailing garbage or m */
                cp++;
 
@@ -1116,36 +873,41 @@ loc_aton(ascii, binary)
        PUTLONG(latit,bcp);
        PUTLONG(longit,bcp);
        PUTLONG(alt,bcp);
-
+    
        return (16);            /* size of RR in octets */
 }
 
 /* takes an on-the-wire LOC RR and formats it in a human readable format. */
-char *
+const char *
 loc_ntoa(binary, ascii)
        const u_char *binary;
        char *ascii;
 {
        static char *error = "?";
-       register const u_char *cp = binary;
+       static char tmpbuf[sizeof
+"1000 60 60.000 N 1000 60 60.000 W -12345678.00m 90000000.00m 90000000.00m 90000000.00m"];
+       const u_char *cp = binary;
 
        int latdeg, latmin, latsec, latsecfrac;
        int longdeg, longmin, longsec, longsecfrac;
        char northsouth, eastwest;
        int altmeters, altfrac, altsign;
 
-       const int referencealt = 100000 * 100;
+       const u_int32_t referencealt = 100000 * 100;
 
        int32_t latval, longval, altval;
        u_int32_t templ;
        u_int8_t sizeval, hpval, vpval, versionval;
-
+    
        char *sizestr, *hpstr, *vpstr;
 
        versionval = *cp++;
 
+       if (ascii == NULL)
+               ascii = tmpbuf;
+
        if (versionval) {
-               sprintf(ascii, "; error: unknown LOC RR version");
+               (void) sprintf(ascii, "; error: unknown LOC RR version");
                return (ascii);
        }
 
@@ -1222,3 +984,56 @@ loc_ntoa(binary, ascii)
 
        return (ascii);
 }
+
+
+/* Return the number of DNS hierarchy levels in the name. */
+int
+dn_count_labels(const char *name) {
+       int i, len, count;
+
+       len = strlen(name);
+       for (i = 0, count = 0; i < len; i++) {
+               /* XXX need to check for \. or use named's nlabels(). */
+               if (name[i] == '.')
+                       count++;
+       }
+
+       /* don't count initial wildcard */
+       if (name[0] == '*')
+               if (count)
+                       count--;
+
+       /* don't count the null label for root. */
+       /* if terminating '.' not found, must adjust */
+       /* count to include last label */
+       if (len > 0 && name[len-1] != '.')
+               count++;
+       return (count);
+}
+
+
+/* 
+ * Make dates expressed in seconds-since-Jan-1-1970 easy to read.  
+ * SIG records are required to be printed like this, by the Secure DNS RFC.
+ */
+char *
+p_secstodate (u_long secs) {
+       /* XXX nonreentrant */
+       static char output[15];         /* YYYYMMDDHHMMSS and null */
+       time_t clock = secs;
+       struct tm *time;
+       
+#ifdef HAVE_TIME_R
+       struct tm timebuf;
+       
+       time = gmtime_r(&clock, &timebuf);
+#else
+       time = gmtime(&clock);
+#endif
+       time->tm_year += 1900;
+       time->tm_mon += 1;
+       sprintf(output, "%04d%02d%02d%02d%02d%02d",
+               time->tm_year, time->tm_mon, time->tm_mday,
+               time->tm_hour, time->tm_min, time->tm_sec);
+       return (output);
+}