s390 specific implementations.
[kopensolaris-gnu/glibc.git] / resolv / res_debug.c
index 15e7169..f4e9169 100644 (file)
@@ -1,8 +1,6 @@
 /*
- * ++Copyright++ 1985, 1990
- * -
- * Copyright (c) 1985, 1990 Regents of the University of California.
- * All rights reserved.
+ * 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
  * 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.
@@ -31,7 +25,9 @@
  * 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
  * 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        5.36 (Berkeley) 3/6/91";
-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/types.h>
 #include <sys/param.h>
+#include <sys/socket.h>
+
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <netdb.h>
 #include <resolv.h>
 #include <stdio.h>
-#include "../conf/portability.h"
-
-void __fp_query();
-char *__p_class(), *__p_time(), *__p_type();
-char *p_cdname(), *p_fqname(), *p_rr();
-static char *p_option __P((u_int32_t));
-
-char *_res_opcodes[] = {
-       "QUERY",
-       "IQUERY",
-       "CQUERYM",
-       "CQUERYU",
-       "4",
-       "5",
-       "6",
-       "7",
-       "8",
-       "UPDATEA",
-       "UPDATED",
-       "UPDATEDA",
-       "UPDATEM",
-       "UPDATEMA",
-       "ZONEINIT",
-       "ZONEREF",
-};
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
 
-char *_res_resultcodes[] = {
-       "NOERROR",
-       "FORMERR",
-       "SERVFAIL",
-       "NXDOMAIN",
-       "NOTIMP",
-       "REFUSED",
-       "6",
-       "7",
-       "8",
-       "9",
-       "10",
-       "11",
-       "12",
-       "13",
-       "14",
-       "NOCHANGE",
-};
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
 
-static char retbuf[16];
+extern const char *_res_opcodes[];
+extern const char *_res_sectioncodes[];
 
-static char *
-dewks(wks)
-       int wks;
-{
-       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(retbuf, "%d", wks); return(retbuf);
-       }
-}
+/*
+ * Print the current options.
+ */
+void
+fp_resstat(const res_state statp, FILE *file) {
+       u_long mask;
 
-static char *
-deproto(protonum)
-       int protonum;
-{
-       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(retbuf, "%d", protonum); return(retbuf);
-       }
+       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 char *
-do_rrset(msg, cp, cnt, pflag, file, hs)
-       int cnt, pflag;
-       char *cp,*msg, *hs;
-       FILE *file;
+static void
+do_section(const res_state statp,
+          ns_msg *handle, ns_sect section,
+          int pflag, FILE *file)
 {
-       int n;
-       char *t1, *t2, *list[100],**tt;
-       int sflag;
+       int n, sflag, rrnum;
+       static int buflen = 2048;
+       char *buf;
+       ns_opcode opcode;
+       ns_rr rr;
+
        /*
-        * Print  answer records
+        * Print answer records.
         */
-       sflag = (_res.pfcode & pflag);
-       if (n = ntohs(cnt)) {
-               *list=NULL;
-               if ((!_res.pfcode) || ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
-                       fprintf(file, hs);
-               while (--n >= 0) {
-                       cp = p_rr(cp, msg, file);
-                       if ((cp-msg) > PACKETSZ)
-                               return (NULL);
-               }
-               if ((!_res.pfcode) || ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
-                       putc('\n', file);
-       }
-       return(cp);
-}
-
-__p_query(msg)
-       char *msg;
-{
-       __fp_query(msg, stdout);
-}
+       sflag = (statp->pfcode & pflag);
+       if (statp->pfcode && !sflag)
+               return;
 
-/*
- * Print the current options.
- * This is intended to be primarily a debugging routine.
- */
-void
-__fp_resstat(statp, file)
-       struct __res_state *statp;
-       FILE *file;
-{
-       int bit;
+       buf = malloc(buflen);
+       if (buf == NULL) {
+               fprintf(file, ";; memory allocation failure\n");
+               return;
+       }
 
-       fprintf(file, ";; res options:");
-       if (!statp)
-               statp = &_res;
-       for (bit = 0;  bit < 32;  bit++) {      /* XXX 32 - bad assumption! */
-               if (statp->options & (1<<bit))
-                       fprintf(file, " %s", p_option(1<<bit));
+       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;
+                       }
+                       fputs(buf, file);
+                       fputc('\n', file);
+               }
+               rrnum++;
        }
-       putc('\n', file);
+ cleanup:
+       if (buf != NULL)
+               free(buf);
 }
 
 /*
@@ -247,501 +214,826 @@ __fp_resstat(statp, file)
  * This is intended to be primarily a debugging routine.
  */
 void
-__fp_query(msg,file)
-       char *msg;
-       FILE *file;
-{
-       register char *cp;
-       register 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 (ns_initparse(msg, len, &handle) < 0) {
+               fprintf(file, ";; ns_initparse: %s\n", strerror(errno));
+               return;
+       }
+       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 + sizeof(HEADER);
-       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);
-       }
-       putc(';', file);
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD2)) {
-               fprintf(file,"; flags:");
-               if (hp->qr)
-                       fprintf(file," qr");
-               if (hp->aa)
-                       fprintf(file," aa");
-               if (hp->tc)
-                       fprintf(file," tc");
-               if (hp->rd)
-                       fprintf(file," rd");
-               if (hp->ra)
-                       fprintf(file," ra");
-               if (hp->pr)
-                       fprintf(file," pr");
+       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 ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD2)) {
+               fprintf(file, "; flags:");
+               if (ns_msg_getflag(handle, ns_f_qr))
+                       fprintf(file, " qr");
+               if (ns_msg_getflag(handle, ns_f_aa))
+                       fprintf(file, " aa");
+               if (ns_msg_getflag(handle, ns_f_tc))
+                       fprintf(file, " tc");
+               if (ns_msg_getflag(handle, ns_f_rd))
+                       fprintf(file, " rd");
+               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\n", 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 0
-       if (_res.pfcode & (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1)) {
+       if ((!statp->pfcode) || (statp->pfcode & 
+               (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) {
                putc('\n',file);
        }
-#endif
-       /*
-        * Print question records.
-        */
-       if (n = ntohs(hp->qdcount)) {
-               if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                       fprintf(file,";; QUESTIONS:\n");
-               while (--n >= 0) {
-                       fprintf(file,";;\t");
-                       cp = p_cdname(cp, msg, file);
-                       if (cp == NULL)
-                               return;
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               fprintf(file, ", type = %s",
-                                       __p_type(_getshort(cp)));
-                       cp += sizeof(u_short);
-                       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
-                               fprintf(file, ", class = %s\n\n",
-                                       __p_class(_getshort(cp)));
-                       cp += sizeof(u_short);
-               }
-       }
        /*
-        * Print authoritative answer records
+        * Print the various sections.
         */
-       cp = do_rrset(msg, cp, hp->ancount, RES_PRF_ANS, file,
-                     ";; ANSWERS:\n");
-       if (cp == NULL)
-               return;
-
-       /*
-        * print name server records
-        */
-       cp = do_rrset(msg, cp, hp->nscount, RES_PRF_AUTH, file,
-                     ";; AUTHORITY RECORDS:\n");
-       if (!cp)
-               return;
-
-       /*
-        * print additional records
-        */
-       cp = do_rrset(msg, cp, hp->arcount, RES_PRF_ADD, file,
-                     ";; ADDITIONAL RECORDS:\n");
-       if (!cp)
-               return;
+       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);
 }
 
-char *
-p_cdname(cp, msg, file)
-       char *cp, *msg;
-       FILE *file;
-{
+const u_char *
+p_cdnname(const u_char *cp, const u_char *msg, int len, FILE *file) {
        char name[MAXDNAME];
        int n;
 
-       if ((n = dn_expand((u_char *)msg, (u_char *)msg + 512, (u_char *)cp,
-           (u_char *)name, sizeof(name))) < 0)
+       if ((n = dn_expand(msg, msg + len, cp, name, sizeof name)) < 0)
                return (NULL);
-       if (name[0] == '\0') {
+       if (name[0] == '\0')
                putc('.', file);
-       } else {
+       else
                fputs(name, file);
-       }
        return (cp + n);
 }
 
-char *
-p_fqname(cp, msg, file)
-       char *cp, *msg;
-       FILE *file;
+const u_char *
+p_cdname(const u_char *cp, const u_char *msg, FILE *file) {
+       return (p_cdnname(cp, msg, PACKETSZ, file));
+}
+
+/* Return a fully-qualified domain name from a compressed name (with
+   length supplied).  */
+
+const u_char *
+p_fqnname(cp, msg, msglen, name, namelen)
+       const u_char *cp, *msg;
+       int msglen;
+       char *name;
+       int namelen;
 {
-       char name[MAXDNAME];
-       int n, len;
+       int n, newlen;
 
-       if ((n = dn_expand((u_char *)msg, (u_char *)msg + 512, (u_char *)cp,
-           (u_char *)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);
 }
 
+/* XXX:        the rest of these functions need to become length-limited, too. */
+
+const u_char *
+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);
+       fputs(name, file);
+       return (n);
+}
 
 /*
- * Print resource record fields in human readable form.
+ * 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.)
  */
-char *
-p_rr(cp, msg, file)
-       char *cp, *msg;
-       FILE *file;
-{
-       int type, class, dlen, n, c;
-       struct in_addr inaddr;
-       char *cp1, *cp2;
-       u_int32_t tmpttl, t;
-       int lcnt;
-
-       if ((cp = p_fqname(cp, msg, file)) == NULL)
-               return (NULL);                  /* compression error */
-       type = _getshort(cp);
-       cp += sizeof(u_short);
-       class = _getshort(cp);
-       cp += sizeof(u_short);
-       tmpttl = _getlong(cp);
-       cp += sizeof(u_int32_t);
-       dlen = _getshort(cp);
-       cp += sizeof(u_short);
-       cp1 = cp;
-       if ((!_res.pfcode) || (_res.pfcode & RES_PRF_TTLID))
-               fprintf(file, "\t%lu", 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, sizeof(inaddr));
-                       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 += sizeof(inaddr);
-                               protocol = *(u_char*)cp;
-                               cp += sizeof(u_char);
-                               port = _getshort(cp);
-                               cp += sizeof(u_short);
-                               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);
-               cp = p_fqname(cp, msg, file);
-               break;
+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_HINFO:
-               if (n = *cp++) {
-                       fprintf(file,"\t%.*s", n, cp);
-                       cp += n;
-               }
-               if (n = *cp++) {
-                       fprintf(file,"\t%.*s", n, cp);
-                       cp += n;
-               }
-               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}
+};
 
-       case T_SOA:
-               putc('\t', file);
-               cp = p_fqname(cp, msg, file);   /* origin */
-               putc(' ', file);
-               cp = p_fqname(cp, msg, file);   /* mail addr */
-               fputs(" (\n", file);
-               t = _getlong(cp);  cp += sizeof(u_int32_t);
-               fprintf(file,"\t\t\t%lu\t; serial\n", t);
-               t = _getlong(cp);  cp += sizeof(u_int32_t);
-               fprintf(file,"\t\t\t%lu\t; refresh (%s)\n", t, __p_time(t));
-               t = _getlong(cp);  cp += sizeof(u_int32_t);
-               fprintf(file,"\t\t\t%lu\t; retry (%s)\n", t, __p_time(t));
-               t = _getlong(cp);  cp += sizeof(u_int32_t);
-               fprintf(file,"\t\t\t%lu\t; expire (%s)\n", t, __p_time(t));
-               t = _getlong(cp);  cp += sizeof(u_int32_t);
-               fprintf(file,"\t\t\t%lu )\t; minimum (%s)", t, __p_time(t));
-               break;
+const struct res_sym __p_update_section_syms[] = {
+       {S_ZONE,        "ZONE"},
+       {S_PREREQ,      "PREREQUISITE"},
+       {S_UPDATE,      "UPDATE"},
+       {S_ADDT,        "ADDITIONAL"},
+       {0,             (char *)0}
+};
 
-       case T_MX:
-               fprintf(file,"\t%d ", _getshort(cp));
-               cp += sizeof(u_short);
-               cp = p_fqname(cp, msg, file);
-               break;
+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}
+};
 
-       case T_TXT:
-               (void) fputs("\t\"", file);
-               cp2 = cp1 + dlen;
-               while (cp < cp2) {
-                       if (n = (unsigned char) *cp++) {
-                               for (c = n; c > 0 && cp < cp2; c--)
-                                       if (*cp == '\n') {
-                                           (void) putc('\\', file);
-                                           (void) putc(*cp++, file);
-                                       } else
-                                           (void) putc(*cp++, file);
-                       }
-               }
-               putc('"', file);
-               break;
-
-       case T_MINFO:
-       case T_RP:
-               putc('\t', file);
-               cp = p_fqname(cp, msg, file);
-               putc(' ', file);
-               cp = p_fqname(cp, msg, file);
-               break;
+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_UINFO:
-               putc('\t', file);
-               fputs(cp, file);
-               cp += dlen;
-               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_UID:
-       case T_GID:
-               if (dlen == 4) {
-                       fprintf(file,"\t%u", _getlong(cp));
-                       cp += sizeof(int32_t);
-               }
-               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_WKS:
-               if (dlen < sizeof(u_int32_t) + 1)
-                       break;
-               bcopy(cp, (char *)&inaddr, sizeof(inaddr));
-               cp += sizeof(u_int32_t);
-               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);
+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;
+       }
+       if (success)
+               *success = 0;
+       return (syms->number);          /* The default value. */
+}
 
-#ifdef ALLOW_T_UNSPEC
-       case T_UNSPEC:
-               {
-                       int NumBytes = 8;
-                       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;
-               }
-               break;
-#endif /* ALLOW_T_UNSPEC */
+const char *
+sym_ntos(const struct res_sym *syms, int number, int *success) {
+       static char unname[20];
 
-       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;
+       for ((void)NULL; syms->name != 0; syms++) {
+               if (number == syms->number) {
+                       if (success)
+                               *success = 1;
+                       return (syms->name);
+               }
        }
-       return (cp);
+
+       sprintf(unname, "%d", number);          /* XXX nonreentrant */
+       if (success)
+               *success = 0;
+       return (unname);
 }
 
-static char nbuf[40];
+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);
+               }
+       }
+       sprintf(unname, "%d", number);          /* XXX nonreentrant */
+       if (success)
+               *success = 0;
+       return (unname);
+}
 
 /*
- * Return a string for the type
+ * Return a string for the type.
  */
-char *
-__p_type(type)
-       int type;
-{
-       switch (type) {
-       case T_A:
-               return("A");
-       case T_NS:              /* authoritative server */
-               return("NS");
-       case T_CNAME:           /* canonical name */
-               return("CNAME");
-       case T_SOA:             /* start of authority zone */
-               return("SOA");
-       case T_MB:              /* mailbox domain name */
-               return("MB");
-       case T_MG:              /* mail group member */
-               return("MG");
-       case T_MR:              /* mail rename name */
-               return("MR");
-       case T_NULL:            /* null resource record */
-               return("NULL");
-       case T_WKS:             /* well known service */
-               return("WKS");
-       case T_PTR:             /* domain name pointer */
-               return("PTR");
-       case T_HINFO:           /* host information */
-               return("HINFO");
-       case T_MINFO:           /* mailbox information */
-               return("MINFO");
-       case T_MX:              /* mail routing info */
-               return("MX");
-       case T_TXT:             /* text */
-               return("TXT");
-       case T_RP:              /* responsible person */
-               return("RP");
-       case T_AXFR:            /* zone transfer */
-               return("AXFR");
-       case T_MAILB:           /* mail box */
-               return("MAILB");
-       case T_MAILA:           /* mail address */
-               return("MAILA");
-       case T_ANY:             /* matches any type */
-               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);
-       }
+const char *
+p_type(int type) {
+       return (sym_ntos(__p_type_syms, type, (int *)0));
 }
 
 /*
- * Return a mnemonic for class
+ * Return a string for the type.
  */
-char *
-__p_class(class)
-       int class;
-{
+const char *
+p_section(int section, int opcode) {
+       const struct res_sym *symbols;
 
-       switch (class) {
-       case C_IN:              /* internet class */
-               return("IN");
-       case C_HS:              /* hesiod class */
-               return("HS");
-       case C_ANY:             /* matches any class */
-               return("ANY");
+       switch (opcode) {
+       case ns_o_update:
+               symbols = __p_update_section_syms;
+               break;
        default:
-               (void)sprintf(nbuf, "%d", class);
-               return(nbuf);
+               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
  */
-static char *
-p_option(option)
-       u_int32_t option;
-{
+const char *
+p_option(u_long option) {
+       static char nbuf[40];
+
        switch (option) {
        case RES_INIT:          return "init";
        case RES_DEBUG:         return "debug";
-       case RES_AAONLY:        return "aaonly";
+       case RES_AAONLY:        return "aaonly(unimpl)";
        case RES_USEVC:         return "usevc";
-       case RES_PRIMARY:       return "primry";
+       case RES_PRIMARY:       return "primry(unimpl)";
        case RES_IGNTC:         return "igntc";
        case RES_RECURSE:       return "recurs";
        case RES_DEFNAMES:      return "defnam";
        case RES_STAYOPEN:      return "styopn";
        case RES_DNSRCH:        return "dnsrch";
-       default:                sprintf(nbuf, "?0x%x?", option); return nbuf;
+       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;
+const char *
+p_time(u_int32_t value) {
+       static char nbuf[40];           /* XXX nonreentrant */
+
+       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.
+ */
+
+static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
+                                     1000000,10000000,100000000,1000000000};
+
+/* takes an XeY precision/size value, returns a string representation. */
+static const char *
+precsize_ntoa(prec)
+       u_int8_t prec;
+{
+       static char retbuf[sizeof "90000000.00"];       /* XXX nonreentrant */
+       unsigned long val;
+       int mantissa, exponent;
+
+       mantissa = (int)((prec >> 4) & 0x0f) % 10;
+       exponent = (int)((prec >> 0) & 0x0f) % 10;
+
+       val = mantissa * poweroften[exponent];
+
+       (void) sprintf(retbuf, "%ld.%.2ld", val/100, val%100);
+       return (retbuf);
+}
+
+/* converts ascii size/precision X * 10**Y(cm) to 0xXY.  moves pointer. */
+static u_int8_t
+precsize_aton(strptr)
+       char **strptr;
+{
+       unsigned int mval = 0, cmval = 0;
+       u_int8_t retval = 0;
+       char *cp;
+       int exponent;
+       int mantissa;
+
+       cp = *strptr;
+
+       while (isdigit(*cp))
+               mval = mval * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {               /* centimeters */
+               cp++;
+               if (isdigit(*cp)) {
+                       cmval = (*cp++ - '0') * 10;
+                       if (isdigit(*cp)) {
+                               cmval += (*cp++ - '0');
+                       }
+               }
+       }
+       cmval = (mval * 100) + cmval;
+
+       for (exponent = 0; exponent < 9; exponent++)
+               if (cmval < poweroften[exponent+1])
+                       break;
+
+       mantissa = cmval / poweroften[exponent];
+       if (mantissa > 9)
+               mantissa = 9;
+
+       retval = (mantissa << 4) | exponent;
+
+       *strptr = cp;
+
+       return (retval);
+}
+
+/* converts ascii lat/lon to unsigned encoded 32-bit number.  moves pointer. */
+static u_int32_t
+latlon2ul(latlonstrptr,which)
+       char **latlonstrptr;
+       int *which;
+{
+       char *cp;
+       u_int32_t retval;
+       int deg = 0, min = 0, secs = 0, secsfrac = 0;
+
+       cp = *latlonstrptr;
+
+       while (isdigit(*cp))
+               deg = deg * 10 + (*cp++ - '0');
+
+       while (isspace(*cp))
+               cp++;
+
+       if (!(isdigit(*cp)))
+               goto fndhemi;
+
+       while (isdigit(*cp))
+               min = min * 10 + (*cp++ - '0');
+
+       while (isspace(*cp))
+               cp++;
+
+       if (!(isdigit(*cp)))
+               goto fndhemi;
+
+       while (isdigit(*cp))
+               secs = secs * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {               /* decimal seconds */
+               cp++;
+               if (isdigit(*cp)) {
+                       secsfrac = (*cp++ - '0') * 100;
+                       if (isdigit(*cp)) {
+                               secsfrac += (*cp++ - '0') * 10;
+                               if (isdigit(*cp)) {
+                                       secsfrac += (*cp++ - '0');
+                               }
+                       }
+               }
+       }
+
+       while (!isspace(*cp))   /* if any trailing garbage */
+               cp++;
+
+       while (isspace(*cp))
+               cp++;
+
+ fndhemi:
+       switch (*cp) {
+       case 'N': case 'n':
+       case 'E': case 'e':
+               retval = ((unsigned)1<<31)
+                       + (((((deg * 60) + min) * 60) + secs) * 1000)
+                       + secsfrac;
+               break;
+       case 'S': case 's':
+       case 'W': case 'w':
+               retval = ((unsigned)1<<31)
+                       - (((((deg * 60) + min) * 60) + secs) * 1000)
+                       - secsfrac;
+               break;
+       default:
+               retval = 0;     /* invalid value -- indicates error */
+               break;
+       }
+
+       switch (*cp) {
+       case 'N': case 'n':
+       case 'S': case 's':
+               *which = 1;     /* latitude */
+               break;
+       case 'E': case 'e':
+       case 'W': case 'w':
+               *which = 2;     /* longitude */
+               break;
+       default:
+               *which = 0;     /* error */
+               break;
+       }
+
+       cp++;                   /* skip the hemisphere */
+
+       while (!isspace(*cp))   /* if any trailing garbage */
+               cp++;
+
+       while (isspace(*cp))    /* move to next field */
+               cp++;
+
+       *latlonstrptr = cp;
+
+       return (retval);
+}
+
+/* converts a zone file representation in a string to an RDATA on-the-wire
+ * representation. */
+int
+loc_aton(ascii, binary)
+       const char *ascii;
+       u_char *binary;
 {
-       int secs, mins, hours, days;
-       register char *p;
+       const char *cp, *maxcp;
+       u_char *bcp;
+
+       u_int32_t latit = 0, longit = 0, alt = 0;
+       u_int32_t lltemp1 = 0, lltemp2 = 0;
+       int altmeters = 0, altfrac = 0, altsign = 1;
+       u_int8_t hp = 0x16;     /* default = 1e6 cm = 10000.00m = 10km */
+       u_int8_t vp = 0x13;     /* default = 1e3 cm = 10.00m */
+       u_int8_t siz = 0x12;    /* default = 1e2 cm = 1.00m */
+       int which1 = 0, which2 = 0;
+
+       cp = ascii;
+       maxcp = cp + strlen(ascii);
+
+       lltemp1 = latlon2ul(&cp, &which1);
+
+       lltemp2 = latlon2ul(&cp, &which2);
+
+       switch (which1 + which2) {
+       case 3:                 /* 1 + 2, the only valid combination */
+               if ((which1 == 1) && (which2 == 2)) { /* normal case */
+                       latit = lltemp1;
+                       longit = lltemp2;
+               } else if ((which1 == 2) && (which2 == 1)) { /* reversed */
+                       longit = lltemp1;
+                       latit = lltemp2;
+               } else {        /* some kind of brokenness */
+                       return (0);
+               }
+               break;
+       default:                /* we didn't get one of each */
+               return (0);
+       }
 
-       if (value == 0) {
-               strcpy(nbuf, "0 secs");
-               return(nbuf);
+       /* altitude */
+       if (*cp == '-') {
+               altsign = -1;
+               cp++;
        }
+    
+       if (*cp == '+')
+               cp++;
 
-       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);
+       while (isdigit(*cp))
+               altmeters = altmeters * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {               /* decimal meters */
+               cp++;
+               if (isdigit(*cp)) {
+                       altfrac = (*cp++ - '0') * 10;
+                       if (isdigit(*cp)) {
+                               altfrac += (*cp++ - '0');
+                       }
+               }
        }
-       if (hours) {
-               if (days)
-                       *p++ = ' ';
-               (void)sprintf(p, "%d hour%s", PLURALIZE(hours));
-               while (*++p);
+
+       alt = (10000000 + (altsign * (altmeters * 100 + altfrac)));
+
+       while (!isspace(*cp) && (cp < maxcp)) /* if trailing garbage or m */
+               cp++;
+
+       while (isspace(*cp) && (cp < maxcp))
+               cp++;
+
+       if (cp >= maxcp)
+               goto defaults;
+
+       siz = precsize_aton(&cp);
+       
+       while (!isspace(*cp) && (cp < maxcp))   /* if trailing garbage or m */
+               cp++;
+
+       while (isspace(*cp) && (cp < maxcp))
+               cp++;
+
+       if (cp >= maxcp)
+               goto defaults;
+
+       hp = precsize_aton(&cp);
+
+       while (!isspace(*cp) && (cp < maxcp))   /* if trailing garbage or m */
+               cp++;
+
+       while (isspace(*cp) && (cp < maxcp))
+               cp++;
+
+       if (cp >= maxcp)
+               goto defaults;
+
+       vp = precsize_aton(&cp);
+
+ defaults:
+
+       bcp = binary;
+       *bcp++ = (u_int8_t) 0;  /* version byte */
+       *bcp++ = siz;
+       *bcp++ = hp;
+       *bcp++ = vp;
+       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. */
+const char *
+loc_ntoa(binary, ascii)
+       const u_char *binary;
+       char *ascii;
+{
+       static char *error = "?";
+       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 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) {
+               (void) sprintf(ascii, "; error: unknown LOC RR version");
+               return (ascii);
        }
-       if (mins) {
-               if (days || hours)
-                       *p++ = ' ';
-               (void)sprintf(p, "%d min%s", PLURALIZE(mins));
-               while (*++p);
+
+       sizeval = *cp++;
+
+       hpval = *cp++;
+       vpval = *cp++;
+
+       GETLONG(templ, cp);
+       latval = (templ - ((unsigned)1<<31));
+
+       GETLONG(templ, cp);
+       longval = (templ - ((unsigned)1<<31));
+
+       GETLONG(templ, cp);
+       if (templ < referencealt) { /* below WGS 84 spheroid */
+               altval = referencealt - templ;
+               altsign = -1;
+       } else {
+               altval = templ - referencealt;
+               altsign = 1;
        }
-       if (secs || ! (days || hours || mins)) {
-               if (days || hours || mins)
-                       *p++ = ' ';
-               (void)sprintf(p, "%d sec%s", PLURALIZE(secs));
+
+       if (latval < 0) {
+               northsouth = 'S';
+               latval = -latval;
+       } else
+               northsouth = 'N';
+
+       latsecfrac = latval % 1000;
+       latval = latval / 1000;
+       latsec = latval % 60;
+       latval = latval / 60;
+       latmin = latval % 60;
+       latval = latval / 60;
+       latdeg = latval;
+
+       if (longval < 0) {
+               eastwest = 'W';
+               longval = -longval;
+       } else
+               eastwest = 'E';
+
+       longsecfrac = longval % 1000;
+       longval = longval / 1000;
+       longsec = longval % 60;
+       longval = longval / 60;
+       longmin = longval % 60;
+       longval = longval / 60;
+       longdeg = longval;
+
+       altfrac = altval % 100;
+       altmeters = (altval / 100) * altsign;
+
+       if ((sizestr = strdup(precsize_ntoa(sizeval))) == NULL)
+               sizestr = error;
+       if ((hpstr = strdup(precsize_ntoa(hpval))) == NULL)
+               hpstr = error;
+       if ((vpstr = strdup(precsize_ntoa(vpval))) == NULL)
+               vpstr = error;
+
+       sprintf(ascii,
+             "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %d.%.2dm %sm %sm %sm",
+               latdeg, latmin, latsec, latsecfrac, northsouth,
+               longdeg, longmin, longsec, longsecfrac, eastwest,
+               altmeters, altfrac, sizestr, hpstr, vpstr);
+
+       if (sizestr != error)
+               free(sizestr);
+       if (hpstr != error)
+               free(hpstr);
+       if (vpstr != error)
+               free(vpstr);
+
+       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++;
        }
-       return(nbuf);
+
+       /* 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);
 }