]> git.saurik.com Git - apple/libresolv.git/commitdiff
libresolv-19.tar.gz mac-os-x-105 mac-os-x-1051 v19
authorApple <opensource@apple.com>
Thu, 11 Oct 2007 00:27:09 +0000 (00:27 +0000)
committerApple <opensource@apple.com>
Thu, 11 Oct 2007 00:27:09 +0000 (00:27 +0000)
44 files changed:
Makefile [new file with mode: 0644]
Makefile.postamble [new file with mode: 0644]
Makefile.preamble [new file with mode: 0644]
PB.project [new file with mode: 0644]
base64.c [new file with mode: 0644]
dns.c [new file with mode: 0644]
dns.h [new file with mode: 0644]
dns_async.c [new file with mode: 0644]
dns_private.h [new file with mode: 0644]
dns_util.c [new file with mode: 0644]
dns_util.h [new file with mode: 0644]
dst.h [new file with mode: 0644]
dst_api.c [new file with mode: 0644]
dst_hmac_link.c [new file with mode: 0644]
dst_internal.h [new file with mode: 0644]
dst_support.c [new file with mode: 0644]
nameser.h [new file with mode: 0644]
ns_date.c [new file with mode: 0644]
ns_name.c [new file with mode: 0644]
ns_netint.c [new file with mode: 0644]
ns_parse.c [new file with mode: 0644]
ns_print.c [new file with mode: 0644]
ns_samedomain.c [new file with mode: 0644]
ns_sign.c [new file with mode: 0644]
ns_ttl.c [new file with mode: 0644]
ns_verify.c [new file with mode: 0644]
res_comp.c [new file with mode: 0644]
res_data.c [new file with mode: 0644]
res_debug.c [new file with mode: 0644]
res_debug.h [new file with mode: 0644]
res_findzonecut.c [new file with mode: 0644]
res_init.c [new file with mode: 0644]
res_mkquery.c [new file with mode: 0644]
res_mkupdate.c [new file with mode: 0644]
res_private.h [new file with mode: 0644]
res_query.c [new file with mode: 0644]
res_send.c [new file with mode: 0644]
res_sendsigned.c [new file with mode: 0644]
res_update.c [new file with mode: 0644]
res_update.h [new file with mode: 0644]
resolv.h [new file with mode: 0644]
resolver.3 [new file with mode: 0644]
resolver.5 [new file with mode: 0644]
resolver_so.3 [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..9f28573
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+#
+# Generated by the Apple Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = resolv
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Library
+
+HFILES = dns.h dns_private.h dns_util.h dst.h dst_internal.h\
+         nameser.h res_debug.h res_private.h res_update.h resolv.h
+
+CFILES = base64.c dns.c dns_async.c dns_util.c dst_api.c\
+         dst_hmac_link.c dst_support.c ns_date.c ns_name.c ns_netint.c\
+         ns_parse.c ns_print.c ns_samedomain.c ns_sign.c ns_ttl.c\
+         ns_verify.c res_comp.c res_data.c res_debug.c\
+         res_findzonecut.c res_init.c res_mkquery.c res_mkupdate.c\
+         res_query.c res_send.c res_sendsigned.c res_update.c
+
+OTHERSRCS = Makefile Makefile.postamble Makefile.preamble resolver.3 resolver_so.3 resolver.5
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CURRENTLY_ACTIVE_VERSION = YES
+DEPLOY_WITH_VERSION_NAME = 9
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = library.make
+NEXTSTEP_INSTALLDIR = /usr/lib
+LIBS = -ldnsinfo 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+PUBLIC_HEADERS = dns.h dns_util.h nameser.h resolv.h
+
+PROJECT_HEADERS = dns.h dns_private.h dns_util.h dst.h dst_internal.h\
+                  nameser.h res_debug.h res_private.h res_update.h\
+                  resolv.h
+
+
+
+WINDOWS_PUBLIC_HEADERS_DIR = LOCAL_DEVELOPER_DIR/Headers/$(NAME)
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/Makefile.postamble b/Makefile.postamble
new file mode 100644 (file)
index 0000000..5a72628
--- /dev/null
@@ -0,0 +1,31 @@
+arpa_headers: $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX)
+       $(RM) -f $(foreach header, $(ARPA_SYMLINKED_HEADERS), $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX)/$(header))
+       $(LN) -s $(foreach header, $(ARPA_SYMLINKED_HEADERS), ../$(header)) $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX)
+       $(SILENT) $(FASTCP) $(ARPA_HEADERS) $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX)
+
+$(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX):
+       $(MKDIRS) $@
+
+after_install: $(DSTROOT)/usr/share/man/man3 $(DSTROOT)/usr/share/man/man5
+       install -d $(DSTROOT)/usr/share/man/man3
+       install -c -m 644 resolver.3 $(DSTROOT)/usr/share/man/man3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/dn_comp.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/dn_expand.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/dn_skipname.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_get16.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_get32.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_put16.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_put32.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_init.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_mkquery.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_query.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_search.3
+       install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_send.3
+       install -d $(DSTROOT)/usr/share/man/man5
+       install -c -m 444 resolver.5 $(DSTROOT)/usr/share/man/man5
+
+$(DSTROOT)/usr/share/man/man3:
+       $(MKDIRS) $@
+
+$(DSTROOT)/usr/share/man/man5:
+       $(MKDIRS) $@
diff --git a/Makefile.preamble b/Makefile.preamble
new file mode 100644 (file)
index 0000000..e50fc53
--- /dev/null
@@ -0,0 +1,8 @@
+PUBLIC_HEADER_DIR=/usr/include
+PUBLIC_HEADER_DIR_SUFFIX = 
+ARPA_HEADER_DIR_SUFFIX = /arpa
+ARPA_SYMLINKED_HEADERS = nameser.h
+AFTER_INSTALLHDRS += arpa_headers
+
+PRIVATE_HEADER_DIR=/usr/local/include
+OTHER_PRIVATE_HEADERS += dns_private.h
diff --git a/PB.project b/PB.project
new file mode 100644 (file)
index 0000000..0862d98
--- /dev/null
@@ -0,0 +1,82 @@
+{
+    "CURRENTLY_ACTIVE_VERSION" = YES; 
+    "DEPLOY_WITH_VERSION_NAME" = 9; 
+    "DYNAMIC_CODE_GEN" = YES; 
+    FILESTABLE = {
+        CLASSES = (); 
+        HEADERSEARCH = (); 
+        "H_FILES" = (
+            "dns.h", 
+            "dns_private.h", 
+            "dns_util.h", 
+            "dst.h", 
+            "dst_internal.h", 
+            "nameser.h", 
+            "res_debug.h", 
+            "res_private.h", 
+            "res_update.h", 
+            "resolv.h"
+        ); 
+        "OTHER_LINKED" = (
+            "base64.c", 
+            "dns.c", 
+            "dns_async.c", 
+            "dns_util.c", 
+            "dst_api.c", 
+            "dst_hmac_link.c", 
+            "dst_support.c", 
+            "ns_date.c", 
+            "ns_name.c", 
+            "ns_netint.c", 
+            "ns_parse.c", 
+            "ns_print.c", 
+            "ns_samedomain.c", 
+            "ns_sign.c", 
+            "ns_ttl.c", 
+            "ns_verify.c", 
+            "res_comp.c", 
+            "res_data.c", 
+            "res_debug.c", 
+            "res_findzonecut.c", 
+            "res_init.c", 
+            "res_mkquery.c", 
+            "res_mkupdate.c", 
+            "res_query.c", 
+            "res_send.c", 
+            "res_sendsigned.c", 
+            "res_update.c"
+        ); 
+        "OTHER_SOURCES" = (Makefile, "Makefile.postamble", "Makefile.preamble", "resolver.5"); 
+        "PROJECT_HEADERS" = (
+            "dns.h", 
+            "dns_private.h", 
+            "dns_util.h", 
+            "dst.h", 
+            "dst_internal.h", 
+            "nameser.h", 
+            "res_debug.h", 
+            "res_private.h", 
+            "res_update.h", 
+            "resolv.h"
+        ); 
+        "PUBLIC_HEADERS" = ("dns.h", "dns_util.h", "nameser.h", "resolv.h"); 
+        SUBPROJECTS = (); 
+    }; 
+    LANGUAGE = English; 
+    "LOCALIZABLE_FILES" = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    "NEXTSTEP_BUILDTOOL" = "/usr/bin/gnumake"; 
+    "NEXTSTEP_INSTALLDIR" = "/usr/lib"; 
+    "NEXTSTEP_JAVA_COMPILER" = "/usr/bin/javac"; 
+    "NEXTSTEP_OBJCPLUS_COMPILER" = "/usr/bin/cc"; 
+    "PDO_UNIX_BUILDTOOL" = "$NEXT_ROOT/Developer/bin/make"; 
+    "PDO_UNIX_JAVA_COMPILER" = "$(JDKBINDIR)/javac"; 
+    "PDO_UNIX_OBJCPLUS_COMPILER" = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = resolv; 
+    PROJECTTYPE = Library; 
+    PROJECTVERSION = "2.8"; 
+    "WINDOWS_BUILDTOOL" = "$NEXT_ROOT/Developer/Executables/make"; 
+    "WINDOWS_JAVA_COMPILER" = "$(JDKBINDIR)/javac.exe"; 
+    "WINDOWS_OBJCPLUS_COMPILER" = "$(DEVDIR)/gcc"; 
+    "WINDOWS_PUBLICHEADERSDIR" = "LOCAL_DEVELOPER_DIR/Headers/$(NAME)"; 
+}
diff --git a/base64.c b/base64.c
new file mode 100644 (file)
index 0000000..d8826e7
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __APPLE__
+#if !defined(LINT) && !defined(CODECENTER)
+static const char rcsid[] = "$Id: base64.c,v 1.1 2006/03/01 19:01:34 majka Exp $";
+#endif /* not lint */
+#endif
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#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 <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+   The following encoding technique is taken from RFC 1521 by Borenstein
+   and Freed.  It is reproduced here in a slightly edited form for
+   convenience.
+
+   A 65-character subset of US-ASCII is used, enabling 6 bits to be
+   represented per printable character. (The extra 65th character, "=",
+   is used to signify a special processing function.)
+
+   The encoding process represents 24-bit groups of input bits as output
+   strings of 4 encoded characters. Proceeding from left to right, a
+   24-bit input group is formed by concatenating 3 8-bit input groups.
+   These 24 bits are then treated as 4 concatenated 6-bit groups, each
+   of which is translated into a single digit in the base64 alphabet.
+
+   Each 6-bit group is used as an index into an array of 64 printable
+   characters. The character referenced by the index is placed in the
+   output string.
+
+                         Table 1: The Base64 Alphabet
+
+      Value Encoding  Value Encoding  Value Encoding  Value Encoding
+          0 A            17 R            34 i            51 z
+          1 B            18 S            35 j            52 0
+          2 C            19 T            36 k            53 1
+          3 D            20 U            37 l            54 2
+          4 E            21 V            38 m            55 3
+          5 F            22 W            39 n            56 4
+          6 G            23 X            40 o            57 5
+          7 H            24 Y            41 p            58 6
+          8 I            25 Z            42 q            59 7
+          9 J            26 a            43 r            60 8
+         10 K            27 b            44 s            61 9
+         11 L            28 c            45 t            62 +
+         12 M            29 d            46 u            63 /
+         13 N            30 e            47 v
+         14 O            31 f            48 w         (pad) =
+         15 P            32 g            49 x
+         16 Q            33 h            50 y
+
+   Special processing is performed if fewer than 24 bits are available
+   at the end of the data being encoded.  A full encoding quantum is
+   always completed at the end of a quantity.  When fewer than 24 input
+   bits are available in an input group, zero bits are added (on the
+   right) to form an integral number of 6-bit groups.  Padding at the
+   end of the data is performed using the '=' character.
+
+   Since all base64 input is an integral number of octets, only the
+         -------------------------------------------------                       
+   following cases can arise:
+   
+       (1) the final quantum of encoding input is an integral
+           multiple of 24 bits; here, the final unit of encoded
+          output will be an integral multiple of 4 characters
+          with no "=" padding,
+       (2) the final quantum of encoding input is exactly 8 bits;
+           here, the final unit of encoded output will be two
+          characters followed by two "=" padding characters, or
+       (3) the final quantum of encoding input is exactly 16 bits;
+           here, the final unit of encoded output will be three
+          characters followed by one "=" padding character.
+   */
+
+int
+b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) {
+       size_t datalength = 0;
+       u_char input[3];
+       u_char output[4];
+       size_t i;
+
+       while (2 < srclength) {
+               input[0] = *src++;
+               input[1] = *src++;
+               input[2] = *src++;
+               srclength -= 3;
+
+               output[0] = input[0] >> 2;
+               output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+               output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+               output[3] = input[2] & 0x3f;
+               Assert(output[0] < 64);
+               Assert(output[1] < 64);
+               Assert(output[2] < 64);
+               Assert(output[3] < 64);
+
+               if (datalength + 4 > targsize)
+                       return (-1);
+               target[datalength++] = Base64[output[0]];
+               target[datalength++] = Base64[output[1]];
+               target[datalength++] = Base64[output[2]];
+               target[datalength++] = Base64[output[3]];
+       }
+    
+       /* Now we worry about padding. */
+       if (0 != srclength) {
+               /* Get what's left. */
+               input[0] = input[1] = input[2] = '\0';
+               for (i = 0; i < srclength; i++)
+                       input[i] = *src++;
+       
+               output[0] = input[0] >> 2;
+               output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+               output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+               Assert(output[0] < 64);
+               Assert(output[1] < 64);
+               Assert(output[2] < 64);
+
+               if (datalength + 4 > targsize)
+                       return (-1);
+               target[datalength++] = Base64[output[0]];
+               target[datalength++] = Base64[output[1]];
+               if (srclength == 1)
+                       target[datalength++] = Pad64;
+               else
+                       target[datalength++] = Base64[output[2]];
+               target[datalength++] = Pad64;
+       }
+       if (datalength >= targsize)
+               return (-1);
+       target[datalength] = '\0';      /* Returned value doesn't count \0. */
+       return (datalength);
+}
+
+/* skips all whitespace anywhere.
+   converts characters, four at a time, starting at (or after)
+   src from base - 64 numbers into three 8 bit bytes in the target area.
+   it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+b64_pton(src, target, targsize)
+       char const *src;
+       u_char *target;
+       size_t targsize;
+{
+       int tarindex, state, ch;
+       char *pos;
+
+       state = 0;
+       tarindex = 0;
+
+       while ((ch = *src++) != '\0') {
+               if (isspace(ch))        /* Skip whitespace anywhere. */
+                       continue;
+
+               if (ch == Pad64)
+                       break;
+
+               pos = strchr(Base64, ch);
+               if (pos == 0)           /* A non-base64 character. */
+                       return (-1);
+
+               switch (state) {
+               case 0:
+                       if (target) {
+                               if ((size_t)tarindex >= targsize)
+                                       return (-1);
+                               target[tarindex] = (pos - Base64) << 2;
+                       }
+                       state = 1;
+                       break;
+               case 1:
+                       if (target) {
+                               if ((size_t)tarindex + 1 >= targsize)
+                                       return (-1);
+                               target[tarindex]   |=  (pos - Base64) >> 4;
+                               target[tarindex+1]  = ((pos - Base64) & 0x0f)
+                                                       << 4 ;
+                       }
+                       tarindex++;
+                       state = 2;
+                       break;
+               case 2:
+                       if (target) {
+                               if ((size_t)tarindex + 1 >= targsize)
+                                       return (-1);
+                               target[tarindex]   |=  (pos - Base64) >> 2;
+                               target[tarindex+1]  = ((pos - Base64) & 0x03)
+                                                       << 6;
+                       }
+                       tarindex++;
+                       state = 3;
+                       break;
+               case 3:
+                       if (target) {
+                               if ((size_t)tarindex >= targsize)
+                                       return (-1);
+                               target[tarindex] |= (pos - Base64);
+                       }
+                       tarindex++;
+                       state = 0;
+                       break;
+               default:
+                       abort();
+               }
+       }
+
+       /*
+        * We are done decoding Base-64 chars.  Let's see if we ended
+        * on a byte boundary, and/or with erroneous trailing characters.
+        */
+
+       if (ch == Pad64) {              /* We got a pad char. */
+               ch = *src++;            /* Skip it, get next. */
+               switch (state) {
+               case 0:         /* Invalid = in first position */
+               case 1:         /* Invalid = in second position */
+                       return (-1);
+
+               case 2:         /* Valid, means one byte of info */
+                       /* Skip any number of spaces. */
+                       for ((void)NULL; ch != '\0'; ch = *src++)
+                               if (!isspace(ch))
+                                       break;
+                       /* Make sure there is another trailing = sign. */
+                       if (ch != Pad64)
+                               return (-1);
+                       ch = *src++;            /* Skip the = */
+                       /* Fall through to "single trailing =" case. */
+                       /* FALLTHROUGH */
+
+               case 3:         /* Valid, means two bytes of info */
+                       /*
+                        * We know this char is an =.  Is there anything but
+                        * whitespace after it?
+                        */
+                       for ((void)NULL; ch != '\0'; ch = *src++)
+                               if (!isspace(ch))
+                                       return (-1);
+
+                       /*
+                        * Now make sure for cases 2 and 3 that the "extra"
+                        * bits that slopped past the last full byte were
+                        * zeros.  If we don't check them, they become a
+                        * subliminal channel.
+                        */
+                       if (target && target[tarindex] != 0)
+                               return (-1);
+               }
+       } else {
+               /*
+                * We ended by seeing the end of the string.  Make sure we
+                * have no partial bytes lying around.
+                */
+               if (state != 0)
+                       return (-1);
+       }
+
+       return (tarindex);
+}
diff --git a/dns.c b/dns.c
new file mode 100644 (file)
index 0000000..b121691
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
+ * Reserved.  This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License').  You may not use this file
+ * except in compliance with the License.  Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "dns.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <fcntl.h>
+#include <notify.h>
+#include <dnsinfo.h>
+#include "dns_private.h"
+#include "res_private.h"
+
+#define INET_NTOP_AF_INET_OFFSET 4
+#define INET_NTOP_AF_INET6_OFFSET 8
+
+#define SEARCH_COUNT_INIT -1
+
+#define DNS_RESOLVER_DIR "/etc/resolver"
+
+#define NOTIFY_DIR_NAME "com.apple.system.dns.resolver.dir"
+#define DNS_DELAY_NAME "com.apple.system.dns.delay"
+
+#define DNS_DELAY_INTERVAL 4
+
+#define DNS_PRIVATE_HANDLE_TYPE_SUPER 0
+#define DNS_PRIVATE_HANDLE_TYPE_PLAIN 1
+
+#define MDNS_MIN_TTL 2
+
+extern uint32_t notify_monitor_file(int token, const char *name, int flags);
+
+extern void res_client_close(res_state res);
+extern res_state res_state_new();
+extern int res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min);
+extern int res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen);
+extern int __res_nsearch_list_2(res_state statp, const char *name,     int class, int type,  u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen, int nsearch, char **search);
+
+extern char *res_next_word(char **p);
+extern res_state res_build_start(res_state res);
+extern int res_build(res_state res, uint16_t port, uint32_t *nsrch, char *key, char *val);
+extern int res_build_sortlist(res_state res, struct in_addr addr, struct in_addr mask);
+
+static void
+_pdns_set_name(pdns_handle_t *pdns, const char *name)
+{
+       int n;
+
+       if (pdns == NULL) return;
+       if (name == NULL) return;
+
+       /* only set the name once */
+       if (pdns->name != NULL) return;
+
+       /* strip trailing dots */
+       n = strlen(name) - 1;
+       while ((n >= 0) && (name[n] == '.')) n--;
+
+       if (n < 0) return;
+
+       n++;
+       pdns->name = calloc(n + 1, sizeof(char));
+       if (pdns->name == NULL) return;
+       memcpy(pdns->name, name, n);
+}
+
+static pdns_handle_t *
+_pdns_build_start(char *name)
+{
+       pdns_handle_t *pdns;
+
+       pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t));
+       if (pdns == NULL) return NULL;
+
+       pdns->res = res_build_start(NULL);
+       if (pdns->res == NULL)
+       {
+               free(pdns);
+               return NULL;
+       }
+
+       _pdns_set_name(pdns, name);
+       pdns->port = NS_DEFAULTPORT;
+
+       return pdns;
+}
+
+static int
+_pdns_build_finish(pdns_handle_t *pdns)
+{
+       uint32_t n;
+
+       if (pdns == NULL) return -1;
+
+       n = pdns->res->nscount;
+       if (n == 0) n = 1;
+
+       if (pdns->total_timeout == 0)
+       {
+               if (pdns->send_timeout == 0) pdns->total_timeout = RES_MAXRETRANS;
+               else pdns->total_timeout = pdns->send_timeout * pdns->res->retry * n;
+       }
+
+       if (pdns->total_timeout == 0) pdns->res->retrans = RES_MAXRETRANS;
+       else pdns->res->retrans = pdns->total_timeout;
+
+       pdns->res->options |= RES_INIT;
+
+       return 0;
+}
+
+static int
+_pdns_build_sortlist(pdns_handle_t *pdns, struct in_addr addr, struct in_addr mask)
+{
+       if (pdns == NULL) return -1;
+       return res_build_sortlist(pdns->res, addr, mask);
+}
+
+static void
+_pdns_free(pdns_handle_t *pdns)
+{
+       int i;
+
+       if (pdns == NULL) return;
+
+       if ((pdns->search_count != SEARCH_COUNT_INIT) && (pdns->search_count > 0) && (pdns->search_list != NULL))
+       {
+               for (i = 0; i < pdns->search_count; i++) free(pdns->search_list[i]);
+               free(pdns->search_list);
+       }
+
+       if (pdns->name != NULL) free(pdns->name);
+       if (pdns->res != NULL) res_client_close(pdns->res);
+
+       free(pdns);
+}
+
+static int
+_pdns_build(pdns_handle_t *pdns, char *key, char *val)
+{
+       struct in6_addr addr6;
+       int32_t status;
+       char *dupval;
+
+       if (pdns == NULL) return -1;
+
+       /*
+        * Detect IPv6 server addresses.
+        */
+       if (((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (!strcmp(key, "nameserver")))
+       {
+               memset(&addr6, 0, sizeof(struct in6_addr));
+               status = inet_pton(AF_INET6, val, &addr6);
+               if (status == 1) pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER;
+       }
+
+       /*
+        * We handle some keys here.
+        * Other keys get passed on to res_build.
+        */
+       if (!strcmp(key, "default"))
+       {
+               pdns->flags |= DNS_FLAG_DEFAULT_RESOLVER;
+               return 0;
+       }
+
+       if (!strcmp(key, "port"))
+       {
+               pdns->port = atoi(val);
+               return 0;
+       }
+
+       if (!strcmp(key, "search"))
+       {
+               dupval = strdup(val);
+               if (dupval == NULL) return -1;
+
+               if (pdns->search_count == SEARCH_COUNT_INIT) pdns->search_count = 0;
+               if (pdns->search_count == 0)
+               {
+                       pdns->search_list = (char **)calloc(1, sizeof(char *));
+               }
+               else
+               {
+                       pdns->search_list = (char **)reallocf(pdns->search_list, (pdns->search_count + 1) * sizeof(char *));
+               }
+
+               if (pdns->search_list == NULL)
+               {
+                       free(dupval);
+                       _pdns_free(pdns);
+                       return -1;
+               }
+
+               pdns->search_list[pdns->search_count] = dupval;
+               pdns->search_count++;
+               return 0;
+       }
+
+       if (!strcmp(key, "total_timeout"))
+       {
+               pdns->total_timeout = atoi(val);
+               return 0;
+       }
+
+       if (!strcmp(key, "timeout"))
+       {
+               pdns->send_timeout = atoi(val);
+               return 0;
+       }
+
+       if (!strcmp(key, "search_order"))
+       {
+               pdns->search_order = atoi(val);
+               return 0;
+       }
+
+       if (!strcmp(key, "pdns"))
+       {
+               pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER;
+               return 0;
+       }
+
+       if (!strcmp(key, "mdns"))
+       {
+               pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER;
+               return 0;
+       }
+
+       /* pass on to res_build */
+       return res_build(pdns->res, pdns->port, &(pdns->search_count), key, val);
+}
+
+static pdns_handle_t *
+_pdns_convert_sc(dns_resolver_t *r)
+{
+       pdns_handle_t *pdns;
+       char *val, *p, *x;
+       int i;
+
+       pdns = _pdns_build_start(r->domain);
+       if (r->domain == NULL) _pdns_build(pdns, "default", NULL);
+
+       p = getenv("RES_RETRY_TIMEOUT");
+       if (p != NULL) pdns->send_timeout = atoi(p);
+
+       p = getenv("RES_RETRY");
+       if (p != NULL) pdns->res->retry= atoi(p);
+
+       if (r->port != 0)
+       {
+               val = NULL;
+               asprintf(&val, "%hu", r->port);
+               if (val == NULL)
+               {
+                       _pdns_free(pdns);
+                       return NULL;
+               }
+
+               _pdns_build(pdns, "port", val);
+               free(val);
+       }
+
+       if (r->n_nameserver > MAXNS) r->n_nameserver = MAXNS;
+       for (i = 0; i < r->n_nameserver; i++)
+       {
+               if (r->nameserver[i]->sa_family == AF_INET)
+               {
+                       val = calloc(1, 256);
+                       if (val == NULL)
+                       {
+                               _pdns_free(pdns);
+                               return NULL;
+                       }
+
+                       inet_ntop(AF_INET, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET_OFFSET, val, 256);
+                       _pdns_build(pdns, "nameserver", val);
+                       free(val);
+               }
+               else if (r->nameserver[i]->sa_family == AF_INET6)
+               {
+                       pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER;
+                       val = calloc(1, 256);
+                       if (val == NULL)
+                       {
+                               _pdns_free(pdns);
+                               return NULL;
+                       }
+
+                       inet_ntop(AF_INET6, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET6_OFFSET, val, 256);
+                       _pdns_build(pdns, "nameserver", val);
+                       free(val);
+               }
+       }
+
+       if (r->n_search > MAXDNSRCH) r->n_search = MAXDNSRCH;
+       for (i = 0; i < r->n_search; i++)
+       {
+               val = NULL;
+               asprintf(&val, "%s", r->search[i]);
+               if (val == NULL)
+               {
+                       _pdns_free(pdns);
+                       return NULL;
+               }
+
+               _pdns_build(pdns, "search", val);
+               free(val);
+       }
+
+       if (r->timeout > 0)
+       {
+               val = NULL;
+               asprintf(&val, "%d", r->timeout);
+               if (val == NULL)
+               {
+                       _pdns_free(pdns);
+                       return NULL;
+               }
+
+               _pdns_build(pdns, "total_timeout", val);
+               free(val);
+       }
+
+       val = NULL;
+       asprintf(&val, "%d", r->search_order);
+       if (val == NULL)
+       {
+               _pdns_free(pdns);
+               return NULL;
+       }
+
+       _pdns_build(pdns, "search_order", val);
+       free(val);
+
+       if (r->n_sortaddr > MAXRESOLVSORT) r->n_sortaddr = MAXRESOLVSORT;
+       for (i = 0; i < r->n_sortaddr; i++)
+       {
+               _pdns_build_sortlist(pdns, r->sortaddr[i]->address, r->sortaddr[i]->mask);
+       }
+
+       p = r->options;
+       while (NULL != (x = res_next_word(&p)))
+       {
+               /* search for and process individual options */
+               if (!strncmp(x, "ndots:", 6))
+               {
+                       _pdns_build(pdns, "ndots", x+6);
+               }
+
+               else if (!strncmp(x, "nibble:", 7))
+               {
+                       _pdns_build(pdns, "nibble", x+7);
+               }
+
+               else if (!strncmp(x, "nibble2:", 8))
+               {
+                       _pdns_build(pdns, "nibble2", x+8);
+               }
+
+               else if (!strncmp(x, "timeout:", 8))
+               {
+                       _pdns_build(pdns, "timeout", x+8);
+               }
+
+               else if (!strncmp(x, "attempts:", 9))
+               {
+                       _pdns_build(pdns, "attempts", x+9);
+               }
+
+               else if (!strncmp(x, "bitstring:", 10))
+               {
+                       _pdns_build(pdns, "bitstring", x+10);
+               }
+
+               else if (!strncmp(x, "v6revmode:", 10))
+               {
+                       _pdns_build(pdns, "v6revmode", x+10);
+               }
+
+               else if (!strcmp(x, "debug"))
+               {
+                       _pdns_build(pdns, "debug", NULL);
+               }
+
+               else if (!strcmp(x, "no_tld_query"))
+               {
+                       _pdns_build(pdns, "no_tld_query", NULL);
+               }
+
+               else if (!strcmp(x, "inet6"))
+               {
+                       _pdns_build(pdns, "inet6", NULL);
+               }
+
+               else if (!strcmp(x, "rotate"))
+               {
+                       _pdns_build(pdns, "rotate", NULL);
+               }
+
+               else if (!strcmp(x, "no-check-names"))
+               {
+                       _pdns_build(pdns, "no-check-names", NULL);
+               }
+
+#ifdef RES_USE_EDNS0
+               else if (!strcmp(x, "edns0"))
+               {
+                       _pdns_build(pdns, "edns0", NULL);
+               }
+#endif
+               else if (!strcmp(x, "a6"))
+               {
+                       _pdns_build(pdns, "a6", NULL);
+               }
+
+               else if (!strcmp(x, "dname"))
+               {
+                       _pdns_build(pdns, "dname", NULL);
+               }
+
+               else if (!strcmp(x, "default"))
+               {
+                       _pdns_build(pdns, "default", NULL);
+               }
+
+               else if (!strcmp(x, "pdns"))
+               {
+                       _pdns_build(pdns, "pdns", NULL);
+               }
+
+               else if (!strcmp(x, "mdns"))
+               {
+                       _pdns_build(pdns, "mdns", NULL);
+               }
+       }
+
+       _pdns_build_finish(pdns);
+       return pdns;
+}
+
+/*
+ * Open a named resolver client from the system config data.
+ */
+static pdns_handle_t *
+_pdns_sc_open(const char *name)
+{
+       pdns_handle_t *pdns;
+       int i;
+       dns_config_t *sc_dns;
+       dns_resolver_t *sc_res;
+
+       sc_dns = dns_configuration_copy();
+       if (sc_dns == NULL) return NULL;
+
+       sc_res = NULL;
+
+       if (name == NULL)
+       {
+               if (sc_dns->n_resolver != 0) sc_res = sc_dns->resolver[0];
+       }
+       else
+       {
+               for (i = 0; (sc_res == NULL) && (i < sc_dns->n_resolver); i++)
+               {
+                       if (sc_dns->resolver[i] == NULL) continue;
+                       if (sc_dns->resolver[i]->domain == NULL) continue;
+                       if (!strcasecmp(name, sc_dns->resolver[i]->domain)) sc_res = sc_dns->resolver[i];
+               }
+       }
+
+       if (sc_res == NULL)
+       {
+               dns_configuration_free(sc_dns);
+               return NULL;
+       }
+
+       pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t));
+       if (pdns == NULL)
+       {
+               dns_configuration_free(sc_dns);
+               return NULL;
+       }
+
+       pdns = _pdns_convert_sc(sc_res);
+
+       dns_configuration_free(sc_dns);
+
+       if (pdns == NULL) return NULL;
+
+       if (pdns->res == NULL)
+       {
+               free(pdns);
+               return NULL;
+       }
+
+       pdns->name = NULL;
+       if (pdns->res->defdname[0] != '\0') _pdns_set_name(pdns, pdns->res->defdname);
+       else if (name != NULL) _pdns_set_name(pdns, name);
+
+       if (name != NULL) pdns->search_count = SEARCH_COUNT_INIT;
+
+       return pdns;
+}
+
+/*
+ * Open a named resolver client from file.
+ */
+static pdns_handle_t *
+_pdns_file_open(const char *name)
+{
+       pdns_handle_t *pdns;
+       char *path, buf[1024];
+       char *p, *x, *y;
+       FILE *fp;
+
+       path = NULL;
+       if (name == NULL)
+       {
+               asprintf(&path, "%s", _PATH_RESCONF);
+       }
+       else if ((name[0] == '.') || (name[0] == '/'))
+       {
+               asprintf(&path, "%s", name);
+       }
+       else
+       {
+               asprintf(&path, "%s/%s", DNS_RESOLVER_DIR, name);
+       }
+
+       if (path == NULL) return NULL;
+
+       fp = fopen(path, "r");
+       free(path);
+       if (fp == NULL) return NULL;
+
+       pdns = _pdns_build_start(NULL);
+       if (pdns == NULL) 
+       {
+               fclose(fp);
+               return NULL;
+       }
+
+       p = getenv("RES_RETRY_TIMEOUT");
+       if (p != NULL) pdns->send_timeout = atoi(p);
+
+       p = getenv("RES_RETRY");
+       if (p != NULL) pdns->res->retry= atoi(p);
+
+       while (fgets(buf, sizeof(buf), fp) != NULL)
+       {
+               /* skip comments */
+               if ((buf[0] == ';') || (buf[0] == '#')) continue;
+               p = buf;
+               x = res_next_word(&p);
+               if (x == NULL) continue;
+               if (!strcmp(x, "sortlist"))
+               {
+                       while (NULL != (x = res_next_word(&p)))
+                       {
+                               _pdns_build(pdns, "sortlist", x);
+                       }
+               }
+               else if (!strcmp(x, "timeout"))
+               {
+                       x = res_next_word(&p);
+                       if (x != NULL) _pdns_build(pdns, "total_timeout", x);
+               }
+               else if (!strcmp(x, "options"))
+               {
+                       while (NULL != (x = res_next_word(&p)))
+                       {
+                               y = strchr(x, ':');
+                               if (y != NULL)
+                               {
+                                       *y = '\0';
+                                       y++;
+                               }
+                               _pdns_build(pdns, x, y);
+                       }
+               }
+               else 
+               {
+                       y = res_next_word(&p);
+                       _pdns_build(pdns, x, y);
+
+                       if ((!strcmp(x, "domain")) && (pdns->name == NULL)) _pdns_set_name(pdns, y);
+               }
+       }
+
+       fclose(fp);
+
+       if (pdns->name == NULL) _pdns_set_name(pdns, name);
+
+       _pdns_build_finish(pdns);
+
+       return pdns;
+}
+
+/*
+ * If there was no search list, use domain name and parent domain components.
+ *
+ * N.B. This code deals with multiple trailing dots, but does not deal with
+ * multiple internal dots, e.g. "foo.....com".  
+ */
+static void
+_pdns_check_search_list(pdns_handle_t *pdns)
+{
+       int n;
+       char *p;
+
+       if (pdns == NULL) return;
+       if (pdns->name == NULL) return;
+       if (pdns->search_count > 0) return;
+
+       /* Count dots */
+       n = 0;
+       for (p = pdns->name; *p != '\0'; p++) 
+       {
+               if (*p == '.') n++;
+       }
+
+       /* Back up over any trailing dots and cut them out of the name */
+       for (p--; (p >= pdns->name) && (*p == '.'); p--)
+       {
+               *p = '\0';
+               n--;
+       }
+
+       /* This will be true if name was all dots */
+       if (p < pdns->name) return;
+
+       /* dots are separators, so number of components is one larger */
+       n++;
+
+       _pdns_build(pdns, "search", pdns->name);
+
+       /* Include parent domains with at least LOCALDOMAINPARTS components */
+       p = pdns->name;
+       while (n > LOCALDOMAINPARTS)
+       {
+               /* Find next component */
+               while ((*p != '.') && (*p != '\0')) p++;
+               if (*p == '\0') break;
+               p++;
+
+               n--;
+               _pdns_build(pdns, "search", p);
+       }
+}
+
+__private_extern__ void
+_check_cache(sdns_handle_t *sdns)
+{
+       int i, n, status, refresh, sc_dns_count;
+       DIR *dp;
+       struct direct *d;
+       pdns_handle_t *c;
+       dns_config_t *sc_dns;
+
+       if (sdns == NULL) return;
+
+       refresh = 0;
+
+       if (sdns->stattime == 0) refresh = 1;
+
+       if (refresh == 0)
+       {
+               if (sdns->notify_sys_config_token == -1) refresh = 1;
+               else
+               {
+                       n = 1;
+                       status = notify_check(sdns->notify_sys_config_token, &n);
+                       if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1;
+               }
+       }
+
+       if (refresh == 0)
+       {
+               if (sdns->notify_dir_token == -1) refresh = 1;
+               else
+               {
+                       n = 1;
+                       status = notify_check(sdns->notify_dir_token, &n);
+                       if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1;
+               }
+       }
+
+       if (refresh == 0) return;
+
+       /* Free old clients */
+       sdns->pdns_primary = NULL;
+
+       for (i = 0; i < sdns->client_count; i++)
+       {
+               _pdns_free(sdns->client[i]);
+       }
+
+       sdns->client_count = 0;
+       if (sdns->client != NULL) free(sdns->client);
+       sdns->client = NULL;
+
+       /* Fetch clients from System Configuration */
+       sc_dns = dns_configuration_copy();
+
+       /* Set up Primary resolver.  It's the one we consult for a search list */
+       sc_dns_count = 0;
+       if ((sc_dns != NULL) && (sc_dns->n_resolver > 0))
+       {
+               sc_dns_count = sc_dns->n_resolver;
+               sdns->pdns_primary = _pdns_convert_sc(sc_dns->resolver[0]);
+               _pdns_check_search_list(sdns->pdns_primary);
+       }
+       else
+       {
+               sdns->pdns_primary = _pdns_file_open(_PATH_RESCONF);
+       }
+
+       if (sdns->pdns_primary != NULL)
+       {
+               if ((sdns->flags & DNS_FLAG_DEBUG) && (sdns->pdns_primary->res != NULL)) sdns->pdns_primary->res->options |= RES_DEBUG;
+               if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) sdns->pdns_primary->flags |= DNS_FLAG_OK_TO_SKIP_AAAA;
+               sdns->pdns_primary->flags |= DNS_FLAG_DEFAULT_RESOLVER;
+
+               sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
+               if (sdns->client == NULL)
+               {
+                       if (sc_dns != NULL) dns_configuration_free(sc_dns);
+                       return;
+               }
+
+               sdns->client[sdns->client_count] = sdns->pdns_primary;
+               sdns->client_count++;
+       }
+
+       /* Convert System Configuration resolvers */
+       for (i = 1; i < sc_dns_count; i++)
+       {
+               c = _pdns_convert_sc(sc_dns->resolver[i]);
+               if (c == NULL) continue;
+
+               if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG;
+               if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA;
+
+               if (sdns->client_count == 0)
+               {
+                       sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
+               }
+               else
+               {
+                       sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *));
+               }
+
+               if (sdns->client == NULL)
+               {
+                       sdns->client_count = 0;
+                       dns_configuration_free(sc_dns);
+                       return;
+               }
+
+               sdns->client[sdns->client_count] = c;
+               sdns->client_count++;
+       }
+
+       if (sc_dns != NULL) dns_configuration_free(sc_dns);
+
+       if (sdns->flags & DNS_FLAG_CHECK_RESOLVER_DIR)
+       {
+               /* Read /etc/resolvers clients */
+               dp = opendir(DNS_RESOLVER_DIR);
+               if (dp == NULL)
+               {
+                       sdns->flags &= ~DNS_FLAG_CHECK_RESOLVER_DIR;
+               }
+               else
+               {
+                       while (NULL != (d = readdir(dp)))
+                       {
+                               if (d->d_name[0] == '.') continue;
+
+                               c = _pdns_file_open(d->d_name);
+                               if (c == NULL) continue;
+                               if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG;
+                               if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA;
+
+                               if (sdns->client_count == 0)
+                               {
+                                       sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
+                               }
+                               else
+                               {
+                                       sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *));
+                               }
+
+                               if (sdns->client == NULL)
+                               {
+                                       sdns->client_count = 0;
+                                       return;
+                               }
+
+                               sdns->client[sdns->client_count] = c;
+                               sdns->client_count++;
+                       }
+                       closedir(dp);
+               }
+       }
+
+       sdns->stattime = 1;
+}
+
+static uint32_t
+_pdns_get_default_handles(sdns_handle_t *sdns, pdns_handle_t ***pdns)
+{
+       int i, j, k, count;
+
+       if (sdns == NULL) return 0;
+       if (pdns == NULL) return 0;
+
+       count = 0;
+
+       for (i = 0; i < sdns->client_count; i++)
+       {
+               if (sdns->client[i]->flags & DNS_FLAG_DEFAULT_RESOLVER)
+               {
+                       if (count == 0)
+                       {
+                               *pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
+                       }
+                       else
+                       {
+                               *pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *));
+                       }
+
+                       if (*pdns == NULL) return 0;
+
+                       /* Insert sorted by search_order */
+                       for (j = 0; j < count; j++)
+                       {
+                               if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break;
+                       }
+
+                       for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1];
+                       (*pdns)[j] = sdns->client[i];
+                       count++;
+               }
+       }
+
+       return count;
+}
+
+static uint32_t
+_pdns_get_handles_for_name(sdns_handle_t *sdns, const char *name, pdns_handle_t ***pdns)
+{
+       char *p, *vname;
+       int i, j, k, count;
+
+       if (sdns == NULL) return 0;
+       if (pdns == NULL) return 0;
+
+       if (name == NULL) return _pdns_get_default_handles(sdns, pdns);
+       else if (name[0] == '\0') return _pdns_get_default_handles(sdns, pdns);
+
+       count = 0;
+
+       vname = strdup(name);
+       i = strlen(vname) - 1;
+       if ((i >= 0) && (vname[i] == '.')) vname[i] = '\0';
+
+       p = vname;
+       while (p != NULL)
+       {
+               for (i = 0; i < sdns->client_count; i++)
+               {
+                       if (sdns->client[i]->name == NULL) continue;
+
+                       if (!strcasecmp(sdns->client[i]->name, p))
+                       {
+                               if (count == 0)
+                               {
+                                       *pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *));
+                               }
+                               else
+                               {
+                                       *pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *));
+                               }
+
+                               if (*pdns == NULL) return 0;
+
+                               /* Insert sorted by search_order */
+                               for (j = 0; j < count; j++)
+                               {
+                                       if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break;
+                               }
+
+                               for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1];
+                               (*pdns)[j] = sdns->client[i];
+                               count++;
+                       }
+               }
+
+               p = strchr(p, '.');
+               if (p != NULL) p++;
+       }
+
+       free(vname);
+
+       if (count != 0) return count;
+
+       return _pdns_get_default_handles(sdns, pdns);;
+}
+
+static void
+_pdns_process_res_search_list(pdns_handle_t *pdns)
+{
+       if (pdns->search_count != SEARCH_COUNT_INIT) return;
+       for (pdns->search_count = 0; (pdns->res->dnsrch[pdns->search_count] != NULL) && (pdns->res->dnsrch[pdns->search_count][0] != '\0'); pdns->search_count++);
+}
+
+static char *
+_pdns_search_list_domain(pdns_handle_t *pdns, uint32_t i)
+{
+       char *s;
+
+       if (pdns == NULL) return NULL;
+       if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns);
+       if (i >= pdns->search_count) return NULL;
+
+       s = pdns->search_list[i];
+       if (s == NULL) return NULL;
+       return strdup(s);
+}
+
+static void
+_dns_open_notify(sdns_handle_t *sdns)
+{
+       int status, n;
+
+       if (sdns == NULL) return;
+
+       if (sdns->notify_delay_token == -1)
+       {
+               status = notify_register_check(DNS_DELAY_NAME, &(sdns->notify_delay_token));
+               if (status != NOTIFY_STATUS_OK) sdns->notify_delay_token = -1;
+               else status = notify_check(sdns->notify_delay_token, &n);
+       }
+
+       if (sdns->notify_sys_config_token == -1)
+       {
+               status = notify_register_check(dns_configuration_notify_key(), &(sdns->notify_sys_config_token));
+               if (status != NOTIFY_STATUS_OK) sdns->notify_sys_config_token = -1;
+       }
+
+       if (sdns->notify_dir_token == -1)
+       {
+               status = notify_register_check(NOTIFY_DIR_NAME, &(sdns->notify_dir_token));
+               if (status == NOTIFY_STATUS_OK)
+               {
+                       status = notify_monitor_file(sdns->notify_dir_token, "/private/etc/resolver", 0);
+                       if (status != NOTIFY_STATUS_OK)
+                       {
+                               notify_cancel(sdns->notify_dir_token);
+                               sdns->notify_dir_token = -1;
+                       }
+               }
+               else 
+               {
+                       sdns->notify_dir_token = -1;
+               }
+       }
+}
+
+static void
+_dns_close_notify(sdns_handle_t *sdns)
+{
+       if (sdns == NULL) return;
+
+       if (sdns->notify_delay_token != -1) notify_cancel(sdns->notify_delay_token);
+       sdns->notify_delay_token = -1;
+
+       if (sdns->notify_sys_config_token != -1) notify_cancel(sdns->notify_sys_config_token);
+       sdns->notify_sys_config_token = -1;
+
+       if (sdns->notify_dir_token != -1) notify_cancel(sdns->notify_dir_token);
+       sdns->notify_dir_token = -1;
+}
+
+dns_handle_t
+dns_open(const char *name)
+{
+       dns_private_handle_t *dns;
+
+       dns = (dns_private_handle_t *)calloc(1, sizeof(dns_private_handle_t));
+       if (dns == NULL) return NULL;
+
+       if (name == NULL)
+       {
+               dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_SUPER;
+               dns->sdns = (sdns_handle_t *)calloc(1, sizeof(sdns_handle_t));
+               if (dns->sdns == NULL)
+               {
+                       free(dns);
+                       return NULL;
+               }
+
+               dns->sdns->flags |= DNS_FLAG_CHECK_RESOLVER_DIR;
+               dns->sdns->notify_sys_config_token = -1;
+               dns->sdns->notify_dir_token = -1;
+               dns->sdns->notify_delay_token = -1;
+               _dns_open_notify(dns->sdns);
+
+               return (dns_handle_t)dns;
+       }
+
+       dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_PLAIN;
+
+       /*  Look for name in System Configuration first */
+       dns->pdns = _pdns_sc_open(name);
+       if (dns->pdns == NULL) dns->pdns = _pdns_file_open(name);
+
+       if (dns->pdns == NULL)
+       {
+               free(dns);
+               return NULL;
+       }
+
+       return (dns_handle_t)dns;
+}
+
+/*
+ * Release a DNS client handle
+ */
+void
+dns_free(dns_handle_t d)
+{
+       dns_private_handle_t *dns;
+       int i;
+
+       if (d == NULL) return;
+
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->recvbuf != NULL) free(dns->recvbuf);
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               if (dns->sdns == NULL) return;
+
+               _dns_close_notify(dns->sdns);
+
+               for (i = 0; i < dns->sdns->client_count; i++)
+               {
+                       _pdns_free(dns->sdns->client[i]);
+               }
+
+               dns->sdns->client_count = 0;
+               if (dns->sdns->client != NULL) free(dns->sdns->client);
+
+               free(dns->sdns);
+       }
+       else
+       {
+               _pdns_free(dns->pdns);
+       }
+
+       free(dns);
+}
+
+static void
+_pdns_debug(pdns_handle_t *pdns, uint32_t flag)
+{
+       if (pdns == NULL) return;
+
+       if (flag == 0)
+       {
+               pdns->res->options &= ~RES_DEBUG;
+       }
+       else
+       {
+               pdns->res->options |= RES_DEBUG;
+       }
+}
+
+static void
+_sdns_debug(sdns_handle_t *sdns, uint32_t flag)
+{
+       int i;
+
+       if (sdns == NULL) return;
+
+       if (flag == 0)
+       {
+               sdns->flags &= ~ DNS_FLAG_DEBUG;
+
+               for (i = 0; i < sdns->client_count; i++)
+               {
+                       sdns->client[i]->res->options &= ~RES_DEBUG;
+               }
+       }
+       else
+       {
+               sdns->flags |= DNS_FLAG_DEBUG;
+
+               for (i = 0; i < sdns->client_count; i++)
+               {
+                       sdns->client[i]->res->options |= RES_DEBUG;
+               }
+       }
+}
+
+/*
+ * Enable / Disable debugging
+ */
+void
+dns_set_debug(dns_handle_t d, uint32_t flag)
+{
+       dns_private_handle_t *dns;
+
+       if (d == NULL) return;
+
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _sdns_debug(dns->sdns, flag);
+       }
+       else
+       {
+               _pdns_debug(dns->pdns, flag);
+       }
+}
+
+/*
+ * Returns the number of names in the search list
+ */
+uint32_t
+dns_search_list_count(dns_handle_t d)
+{
+       dns_private_handle_t *dns;
+       pdns_handle_t *pdns;
+
+       if (d == NULL) return 0;
+
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _check_cache(dns->sdns);
+               pdns = dns->sdns->pdns_primary;
+       }
+       else
+       {
+               pdns = dns->pdns;
+       }
+
+       if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns);
+       return pdns->search_count;
+}
+
+/* 
+ * Returns the domain name at index i in the search list.
+ * Returns NULL if there are no names in the search list.
+ * Caller must free the returned name.
+ */
+char *
+dns_search_list_domain(dns_handle_t d, uint32_t i)
+{
+       dns_private_handle_t *dns;
+       pdns_handle_t *pdns;
+
+       if (d == NULL) return NULL;
+
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _check_cache(dns->sdns);
+               pdns = dns->sdns->pdns_primary;
+       }
+       else
+       {
+               pdns = dns->pdns;
+       }
+
+       return _pdns_search_list_domain(pdns, i);
+}
+
+static int
+_pdns_delay(sdns_handle_t *sdns)
+{
+       int status, n, snooze;
+       time_t tick;
+
+       if (sdns == NULL) return 0;
+
+       snooze = 0;
+       n = 0;
+
+       /* No delay if we are not receiving notifications */
+       if (sdns->notify_delay_token == -1) return 0;
+
+       if (sdns->dns_delay == 0)
+       {
+               status = notify_check(sdns->notify_delay_token, &n);
+               if ((status == NOTIFY_STATUS_OK) && (n == 1))
+               {
+                       /*
+                        * First thread to hit this condition sleeps for DNS_DELAY_INTERVAL seconds
+                        */
+                       sdns->dns_delay = time(NULL) + DNS_DELAY_INTERVAL;
+                       snooze = DNS_DELAY_INTERVAL;
+               }
+       }
+       else
+       {
+               tick = time(NULL);
+               /*
+                * Subsequent threads sleep for the remaining duration.
+                * We add one to round up the interval since our garnularity is coarse.
+                */
+               snooze = 1 + (sdns->dns_delay - tick);
+               if (snooze < 0) snooze = 0;
+       }
+
+       if (snooze == 0) return 0;
+
+       sleep(snooze);
+
+       /* When exiting, first thread in resets the delay condition */
+       if (n == 1) sdns->dns_delay = 0;
+
+       return 0;
+}
+
+static int
+_pdns_query(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min)
+{
+       int n;
+
+       if (name == NULL) return -1;
+       if (pdns == NULL) return -1;
+
+       if (pdns->flags & DNS_FLAG_FORWARD_TO_MDNSRESPONDER)
+       {
+               n = res_query_mDNSResponder(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen);
+               if ((n < 0) && (min != NULL)) *min = MDNS_MIN_TTL;
+               return n;
+       }
+
+       if (pdns->res == NULL) return -1;
+       if (pdns->res->nscount == 0) return -1;
+
+       if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1;
+
+       _pdns_delay(sdns);
+
+       /* BIND_9 API */
+       return res_nquery_soa_min(pdns->res, name, class, type, (u_char *)buf, len, from, (int32_t *)fromlen, min);
+}
+
+__private_extern__ int
+_pdns_search(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen)
+{
+       char *dot, *qname;
+       int append, status;
+
+       if (name == NULL) return -1;
+       if (pdns == NULL) return -1;
+       if (pdns->res == NULL) return -1;
+       if (pdns->res->nscount == 0) return -1;
+
+       if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1;
+
+       qname = NULL;
+       append = 1;
+
+       /*
+        * don't append my name if:
+        * - my name is NULL
+        * - input name is qualified (i.e. not single component)
+        * - there is a search list
+        * - there is a domain name
+        */
+
+       if (pdns->name == NULL) append = 0;
+
+       if (append == 1)
+       {
+               dot = strrchr(name, '.');
+               if (dot != NULL) append = 0;
+       }
+
+       if (append == 1)
+       {
+               if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns);
+               if (pdns->search_count > 0) append = 0;
+       }
+
+       if ((append == 1) && (pdns->res->defdname != NULL) && (pdns->res->defdname[0] != '\0')) append = 0;
+
+       status = -1;
+       if (append == 0)
+       {
+               /* BIND_9 API */
+               _pdns_delay(sdns);
+
+               status = __res_nsearch_list_2(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen, pdns->search_count, pdns->search_list);
+       }
+       else
+       {
+               _pdns_delay(sdns);
+
+               qname = NULL;
+               asprintf(&qname, "%s.%s.", name, pdns->name);
+               if (qname == NULL) return -1;
+
+               /* BIND_9 API */
+               status = res_nsearch_2(pdns->res, qname, class, type, (u_char *)buf, len, from, fromlen);
+               free(qname);
+       }
+
+       return status;
+}
+
+static int
+_sdns_send(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min)
+{
+       char *qname;
+       pdns_handle_t **pdns;
+       uint32_t pdns_count;
+       int i, n, m;
+
+       pdns = NULL;
+       pdns_count = 0;
+       n = -1;
+
+       pdns_count = _pdns_get_handles_for_name(sdns, name, &pdns);
+
+       if (pdns_count == 0) return -1;
+
+       qname = NULL;
+       asprintf(&qname, "%s%s", name, (fqdn == 0) ? "." : "");
+       if (qname == NULL) return -1;
+
+       for (i = 0; i < pdns_count; i++)
+       {
+               m = -1;
+               n = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, &m);
+               if (min != NULL)
+               {
+                       if (*min == -1) *min = m;
+                       else if ((m >= 0) && (m < *min)) *min = m;
+               }
+
+               if (n > 0) break;
+       }
+
+       free(pdns);
+       free(qname);
+       return n;
+}
+
+__private_extern__ int
+_sdns_search(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, uint32_t recurse, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min)
+{
+       pdns_handle_t *primary, **pdns;
+       int i, n, ndots, status, m;
+       char *dot, *qname;
+       uint32_t pdns_count;
+
+       if (sdns == NULL) return -1;
+       if (name == NULL) return -1;
+
+       /* ndots is the threshold for trying a qualified name "as is" */
+       ndots = 1;
+       primary = sdns->pdns_primary;
+       if ((primary != NULL) && (primary->res != NULL)) ndots = primary->res->ndots;
+
+       /* count dots in input name, and keep track of the location of the last dot */
+       n = 0;
+       dot = NULL;
+
+       for (i = 0; name[i] != '\0'; i++)
+       {
+               if (name[i] == '.')
+               {
+                       n++;
+                       dot = (char *)(name + i);
+               }
+       }
+
+       /* the last dot is the last character, name is fully qualified */
+       if ((fqdn == 0) && (dot != NULL) && (*(dot + 1) == '\0')) fqdn = 1;
+
+       /*
+        * If n >= ndots, or it's a FQDN, or if it's a PTR query,
+        * we try a query with the name "as is".
+        */
+       if ((n >= ndots) || (fqdn == 1) || (type == ns_t_ptr))
+       {
+               status = _sdns_send(sdns, name, class, type, fqdn, buf, len, from, fromlen, min);
+               if (status > 0) return status;
+       }
+
+       /* end of the line for FQDNs or PTR queries */
+       if (fqdn == 1) return -1;
+       if (type == ns_t_ptr) return -1;
+
+       if (recurse == 0) return -1;
+       if (primary == NULL) return -1;
+
+       /* Try appending names from the search list */
+       if (primary->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(primary);
+       n = primary->search_count;
+       if (n > 0)
+       {
+               /* Try qualifying with each name in the search list */
+               for (i = 0; i < n ; i++)
+               {
+                       qname = NULL;
+                       asprintf(&qname, "%s.%s", name, primary->search_list[i]);
+                       if (qname == NULL) return -1;
+
+                       m = -1;
+                       status = _sdns_search(sdns, qname, class, type, fqdn, 0, buf, len, from, fromlen, &m);
+                       {
+                               if (*min == -1) *min = m;
+                               else if ((m >= 0) && (m < *min)) *min = m;
+                       }
+                       
+                       free(qname);
+                       if (status > 0) return status;
+               }
+
+               return -1;
+       }
+
+       /*
+        * We get here if the name is not fully qualified (no trailing dot), and there is no search list.
+        * Try each default client, qualifying with that client's name.
+        */
+       pdns = NULL;
+       pdns_count = _pdns_get_default_handles(sdns, &pdns);
+       status = -1;
+
+       if (pdns_count == 0) return -1;
+
+       for (i = 0; i < pdns_count; i++)
+       {
+               qname = NULL;
+               if (pdns[i]->name == NULL) asprintf(&qname, "%s", name);
+               else asprintf(&qname, "%s.%s", name, pdns[i]->name);
+               if (qname == NULL) return -1;
+
+               m = -1;
+               status = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, min);
+               {
+                       if (*min == -1) *min = m;
+                       else if ((m >= 0) && (m < *min)) *min = m;
+               }
+               
+               free(qname);
+               if (status > 0) break;
+       }
+
+       free(pdns);
+       return status;
+}
+
+int
+dns_query(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen)
+{
+       dns_private_handle_t *dns;
+       int status, unused;
+
+       if (d == NULL) return -1;
+       if (name == NULL) return -1;
+       dns = (dns_private_handle_t *)d;
+
+       status = -1;
+       unused = 0;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _check_cache(dns->sdns);
+               status = _sdns_search(dns->sdns, name, class, type, 1, 1, buf, len, from, fromlen, &unused);
+       }
+       else
+       {
+               status = _pdns_query(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen, &unused);
+       }
+
+       return status;
+}
+
+
+int
+dns_search(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen)
+{
+       dns_private_handle_t *dns;
+       int status, unused;
+
+       if (d == NULL) return -1;
+       if (name == NULL) return -1;
+       dns = (dns_private_handle_t *)d;
+
+       status = -1;
+       unused = 0;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _check_cache(dns->sdns);
+               status = _sdns_search(dns->sdns, name, class, type, 0, 1, buf, len, from, fromlen, &unused);
+       }
+       else
+       {
+               status = _pdns_search(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen);
+       }
+
+       return status;
+}
+
+/*
+ * PRIVATE
+ */
+
+uint32_t
+dns_server_list_count(dns_handle_t d)
+{
+       dns_private_handle_t *dns;
+       res_state r;
+
+       if (d == NULL) return 0;
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return 0;
+
+       if (dns->pdns == NULL) return 0;
+
+       r = dns->pdns->res;
+       if (r == NULL) return 0;
+
+       return r->nscount;
+}
+
+struct sockaddr *
+dns_server_list_address(dns_handle_t d, uint32_t i)
+{
+       dns_private_handle_t *dns;
+       res_state r;
+       struct sockaddr_storage *s;
+       struct sockaddr *sa;
+
+       if (d == NULL) return NULL;
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return NULL;
+
+       if (dns->pdns == NULL) return NULL;
+
+       r = dns->pdns->res;
+       if (r == NULL) return NULL;
+
+       if (i >= r->nscount) return NULL;
+       sa = get_nsaddr(r, i);
+       if (sa == NULL) return NULL;
+
+       s = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage));
+       if (s == NULL) return NULL;
+
+       memcpy(s, sa, sizeof(struct sockaddr_storage));
+       return (struct sockaddr *)s;
+}
diff --git a/dns.h b/dns.h
new file mode 100644 (file)
index 0000000..d63803c
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * "Portions Copyright (c) 2003 Apple Computer, Inc.  All Rights
+ * Reserved.  This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License').  You may not use this file
+ * except in compliance with the License.  Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __DNS_H__
+#define __DNS_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <sys/socket.h>
+
+/*
+ * The functions described in this access layer support multiple DNS
+ * client configurations.  Each DNS client has its own set of nameserver
+ * addresses and its own set of operational parameters.  Each client
+ * can perform DNS queries and searches independent of other clients.
+ * Each client has a symbolic name which is of the same format as a
+ * domain name, e.g. "apple.com".  A special meta-client, known as the
+ * "Super" DNS client, acts as a router for DNS queries.  The Super
+ * client chooses among all available clients by finding a best match
+ * between the domain name given in a query and the names of all known
+ * clients.
+ *
+ * The configuration for a particular client may be read from a file
+ * having the same format as the traditional "/etc/resolv.conf" file.
+ * However, client configurations are not limited to being stored in
+ * files.  The implementation of the library may also locate client
+ * configuratins in other data sources, such as the System Configuration
+ * Database.  Users of this API should make no assumptions about the
+ * source of the configuration data.
+ */
+
+typedef const struct __dns_handle_private_struct *dns_handle_t;
+
+
+__BEGIN_DECLS
+
+/*
+ * Create a client handle for DNS access.
+ *
+ * "Super" DNS client
+ *
+ * dns_open(NULL) returns a "super" client that routes DNS queries
+ * among all DNS configurations known to the system.
+ *
+ * Queries for qualified names are sent using a client configuration that
+ * best matches the domain name given in the query. For example, if there
+ * is a client named "apple.com", a search for "foo.apple.com" would use the
+ * resolver configuration specified for that client.  The matching algorithm
+ * chooses the client with the maximum number of matching domain components.
+ * For example, if there are clients named "a.b.c", and "b.c", a search for
+ * "x.a.b.c" would use the "a.b.c" resolver configuration, while a search
+ * for "x.y.b.c" would use the "b.c" client.  If there are no matches, the 
+ * configuration settings in the default client - generally corresponding to
+ * the /etc/resolv.conf file or to the "primary" DNS configuration on the
+ * system are used for the query.
+ *
+ * The domain search list defined in the "default" client is used to search
+ * for unqualified names, by appending each domain in the search list and
+ * then following the logic for matching qualified names described above.
+ *
+ * The DNS access APIs may be used by multiple threads.  Each thread must
+ * use a separate DNS client handle created by calling dns_open().
+ *
+ * A simple DNS client handle may be obtained by providing a non-NULL value
+ * for the "name" parameter.  Simple clients correspond to a single DNS
+ * configuration, derived from a resolv.conf format file or from some other
+ * source of configurations known to the system.
+ * The name parameter may be a full or relative path name (starting with '/'
+ * or '.'), in which case the client's configuration is read from the
+ * named file.  If the name parameter is not a file path name, the routine
+ * will search through all known sources of DNS configuration data on the
+ * system to locate DNS configuration data corresponding to the name supplied,
+ * or NULL if none can be found.
+ *
+ * Use _PATH_RESCONF to open /etc/resolv.conf.
+ */
+extern dns_handle_t dns_open(const char *name);
+
+/*
+ * Close a client and free its resources.
+ */
+extern void dns_free(dns_handle_t dns);
+
+/*
+ * Enable / Disable debug logging.
+ */
+extern void dns_set_debug(dns_handle_t dns, uint32_t flag);
+
+/*
+ * Returns the number of names in the search list.
+ */
+extern uint32_t dns_search_list_domain_count(dns_handle_t dns);
+
+/* 
+ * Returns the domain name at index i in the search list.
+ * Returns NULL if there are no names in the search list,
+ * or if i is out of range.
+ * Caller must free the returned value.
+ */
+extern char *dns_search_list_domain(dns_handle_t dns, uint32_t i);
+
+/*
+ * Resolve a name.
+ * The name is considered fully-qualified (the search list is not used).
+ * Caller must supply buffer space for the reply message and the server address.
+ */
+extern int32_t dns_query(dns_handle_t dns, const char *name, uint32_t dnsclass, uint32_t dnstype, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen);
+
+/*
+ * Search for a name.
+ * Caller must supply buffer space for the reply message and the server address.
+ */
+extern int32_t dns_search(dns_handle_t dns, const char *name, uint32_t dnsclass, uint32_t dnstype, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen);
+
+__END_DECLS
+
+#endif /* __DNS_H__ */
diff --git a/dns_async.c b/dns_async.c
new file mode 100644 (file)
index 0000000..9aa367a
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_headER_START@
+ * 
+ * Portions Copyright (c) 2003 Apple Computer, Inc.  All Rights
+ * Reserved.  This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.1 (the "License").  You may not use this file
+ * except in compliance with the License.  Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * @APPLE_LICENSE_headER_END@
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <netdb.h>
+#include <netdb_async.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dns.h>
+#include <dns_util.h>
+
+typedef struct
+{
+       uint32_t datalen;
+       char *databuf;
+       uint32_t _size;
+       uint32_t _dict;
+       uint32_t _key;
+       uint32_t _vlist;
+       uint32_t _val;
+} kvbuf_t;
+
+typedef struct
+{
+       uint32_t kcount;
+       const char **key;
+       uint32_t *vcount;
+       const char ***val;
+} kvdict_t;
+
+typedef struct
+{
+       uint32_t count;
+       uint32_t curr;
+       kvdict_t *dict;
+       kvbuf_t *kv;
+} kvarray_t;
+
+extern kvbuf_t *kvbuf_new(void);
+extern void kvbuf_add_dict(kvbuf_t *kv);
+extern void kvbuf_add_key(kvbuf_t *kv, const char *key);
+extern void kvbuf_add_val(kvbuf_t *kv, const char *val);
+extern void kvbuf_free(kvbuf_t *kv);
+extern void kvarray_free(kvarray_t *kva);
+extern uint32_t kvbuf_get_val_len(const char *val);
+extern kern_return_t LI_DSLookupGetProcedureNumber(const char *name, int *procno);
+extern kern_return_t LI_async_start(mach_port_t *p, uint32_t proc, kvbuf_t *query, void *callback, void *context);
+extern kern_return_t LI_async_handle_reply(void *msg, kvarray_t **reply, void **callback, void **context);
+extern kern_return_t LI_async_receive(mach_port_t p, kvarray_t **reply);
+extern void LI_async_call_cancel(mach_port_t p, void **context);
+extern uint32_t kvbuf_get_len(const char *p);
+
+static kvbuf_t *
+dns_make_query(const char *name, uint16_t dnsclass, uint16_t dnstype, uint32_t do_search)
+{
+       kvbuf_t *request;
+       char str[128];
+
+       if (name == NULL) return NULL;
+
+       request = kvbuf_new();
+       if (request == NULL) return NULL;
+       
+       kvbuf_add_dict(request);
+       
+       /* Encode name */
+       kvbuf_add_key(request, "domain");
+       kvbuf_add_val(request, name);
+
+       /* Encode class */
+       snprintf(str, 128, "%hu", dnsclass);
+       kvbuf_add_key(request, "class");
+       kvbuf_add_val(request, str);
+
+       /* Encode type */
+       snprintf(str, 128, "%hu", dnstype);
+       kvbuf_add_key(request, "type");
+       kvbuf_add_val(request, str);
+
+       /* Encode do_search */
+       snprintf(str, 128, "%hu", do_search);
+       kvbuf_add_key(request, "search");
+       kvbuf_add_val(request, str);
+
+       return request;
+}
+
+int32_t
+dns_async_start(mach_port_t *p, const char *name, uint16_t dnsclass, uint16_t dnstype, uint32_t do_search, dns_async_callback callback, void *context)
+{
+       int32_t status;
+       kvbuf_t *request;
+       static int proc = -1;
+
+       *p = MACH_PORT_NULL;
+
+       if (name == NULL) return NO_RECOVERY;
+
+       if (proc < 0)
+       {
+               status = LI_DSLookupGetProcedureNumber("dns_proxy", &proc);
+               if (status != KERN_SUCCESS) return NO_RECOVERY;
+       }
+
+       request = dns_make_query(name, dnsclass, dnstype, do_search);
+       if (request == NULL) return NO_RECOVERY;
+
+       status = LI_async_start(p, proc, request, (void *)callback, context);
+       
+       kvbuf_free(request);
+       if (status != 0) return NO_RECOVERY;
+       return 0;
+}
+
+void
+dns_async_cancel(mach_port_t p)
+{
+       LI_async_call_cancel(p, NULL);
+}
+
+int32_t
+dns_async_send(mach_port_t *p, const char *name, uint16_t dnsclass, uint16_t dnstype, uint32_t do_search)
+{
+       return dns_async_start(p, name, dnsclass, dnstype, do_search, NULL, NULL);
+}
+
+static int
+dns_extract_data(kvarray_t *in, char **buf, uint32_t *len, struct sockaddr **from, uint32_t *fromlen)
+{
+       int32_t status;
+       struct in_addr addr4;
+       struct sockaddr_in sin4;
+       struct in6_addr addr6;
+       struct sockaddr_in6 sin6;
+       uint32_t d, k, kcount;
+       
+       if (in == NULL) return -1;
+       if (buf == NULL) return -1;
+       if (len == NULL) return -1;
+       if (from == NULL) return -1;
+       if (fromlen == NULL) return -1;
+       
+       *buf = NULL;
+       *len = 0;
+       *from = NULL;
+       *fromlen = 0;
+       
+       d = in->curr;
+       in->curr++;
+       
+       if (d >= in->count) return -1;
+       
+       kcount = in->dict[d].kcount;
+       
+       for (k = 0; k < kcount; k++)
+       {
+               if (!strcmp(in->dict[d].key[k], "data"))
+               {
+                       if (in->dict[d].vcount[k] == 0) continue;
+                       if (*buf != NULL) continue;
+
+                       /*
+                        * dns_proxy contains binary data, possibly with embedded nuls,
+                        * so we extract the string length from the kvbuf_t reply that
+                        * Libinfo got from directory services, rather than calling strlen().
+                        */
+                       *len = kvbuf_get_len(in->dict[d].val[k][0]);
+                       if (*len == 0) continue;
+
+                       *buf = malloc(*len);
+                       if (*buf == NULL) return -1;
+
+                       memcpy(*buf, in->dict[d].val[k][0], *len);
+               }
+               else if (!strcmp(in->dict[d].key[k], "server"))
+               {
+                       if (in->dict[d].vcount[k] == 0) continue;
+                       if (*from != NULL) continue;
+
+                       memset(&addr4, 0, sizeof(struct in_addr));
+                       memset(&sin4, 0, sizeof(struct sockaddr_in));
+                       
+                       memset(&addr6, 0, sizeof(struct in6_addr));
+                       memset(&sin6, 0, sizeof(struct sockaddr_in6));
+                       
+                       status = inet_pton(AF_INET6, in->dict[d].val[k][0], &addr6);
+                       if (status == 1)
+                       {
+                               sin6.sin6_addr = addr6;
+                               sin6.sin6_family = AF_INET6;
+                               sin6.sin6_len = sizeof(struct sockaddr_in6);
+                               *from = (struct sockaddr *)calloc(1, sin6.sin6_len);
+                               memcpy(*from, &sin6, sin6.sin6_len);
+                               *fromlen = sin6.sin6_len;
+                       }
+
+                       status = inet_pton(AF_INET, in->dict[d].val[k][0], &addr4);
+                       if (status == 1)
+                       {
+                               sin4.sin_addr = addr4;
+                               sin4.sin_family = AF_INET;
+                               sin4.sin_len = sizeof(struct sockaddr_in);
+                               *from = (struct sockaddr *)calloc(1, sin4.sin_len);
+                               memcpy(*from, &sin4, sin4.sin_len);
+                               *fromlen = sin4.sin_len;
+                       }
+               }
+       }
+                       
+       return 0;
+}
+
+int32_t
+dns_async_receive(mach_port_t p, char **buf, uint32_t *len, struct sockaddr **from, uint32_t *fromlen)
+{
+       kern_return_t status;
+       kvarray_t *reply;
+
+       reply = NULL;
+       
+       status = LI_async_receive(p, &reply);
+       if (status != 0) return NO_RECOVERY;
+       if (reply == NULL) return HOST_NOT_FOUND;
+
+       status = dns_extract_data(reply, buf, len, from, fromlen);
+       kvarray_free(reply);
+       if (status != 0) return NO_RECOVERY;
+
+       if (*buf == NULL) return NO_DATA;
+
+       return 0;
+}
+
+int32_t
+dns_async_handle_reply(void *msg)
+{
+       dns_async_callback callback;
+       void *context;
+       char *buf;
+       kvarray_t *reply;
+       kern_return_t status;
+       struct sockaddr *from;
+       uint32_t len, fromlen;
+
+       callback = (dns_async_callback)NULL;
+       context = NULL;
+       reply = NULL;
+       buf = NULL;
+       len = 0;
+       from = NULL;
+       fromlen = 0;
+
+       status = LI_async_handle_reply(msg, &reply, (void **)&callback, &context);
+       if (status != KERN_SUCCESS)
+       {
+               if (status == MIG_REPLY_MISMATCH) return 0;
+               callback(NO_RECOVERY, NULL, 0, NULL, 0, context);
+               return NO_RECOVERY;
+       }
+
+       status = dns_extract_data(reply, &buf, &len, &from, &fromlen);
+       kvarray_free(reply);
+       if (status != 0)
+       {
+               callback(NO_RECOVERY, NULL, 0, NULL, 0, context);
+               return 0;
+       }
+
+       if (buf == NULL)
+       {
+               callback(NO_DATA, NULL, 0, NULL, 0, context);
+               return NO_DATA;
+       }
+
+       callback(0, buf, len, from, fromlen, context);
+
+       return 0;
+}
+
diff --git a/dns_private.h b/dns_private.h
new file mode 100644 (file)
index 0000000..1ca315d
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __DNS_PRIVATE_H__
+#define __DNS_PRIVATE_H__
+
+#include <sys/cdefs.h>
+
+#define DNS_FLAG_DEBUG                    0x00000001
+#define DNS_FLAG_CHECK_RESOLVER_DIR       0x00000002
+#define DNS_FLAG_HAVE_IPV6_SERVER         0x00000004
+#define DNS_FLAG_OK_TO_SKIP_AAAA          0x00000008
+#define DNS_FLAG_DEFAULT_RESOLVER         0x00000010
+#define DNS_FLAG_FORWARD_TO_MDNSRESPONDER 0x00000020
+
+typedef struct
+{
+       res_state res;
+       char *source;
+       char *name;
+       uint32_t search_count;
+       char **search_list;
+       uint16_t port;
+       uint32_t flags;
+       uint32_t total_timeout;
+       uint32_t send_timeout;
+       uint32_t search_order;
+       uint32_t reserved1;
+       void *reserved_pointer1;
+} pdns_handle_t;
+
+typedef struct 
+{
+       pdns_handle_t *pdns_primary;
+       uint32_t client_count;
+       pdns_handle_t **client;
+       uint32_t modtime;
+       uint32_t stattime;
+       uint32_t stat_latency;
+       uint32_t flags;
+       int notify_sys_config_token;
+       int notify_dir_token;
+       int notify_delay_token;
+       time_t dns_delay;
+       uint32_t reserved1;
+       void *reserved_pointer1;
+} sdns_handle_t;
+
+typedef struct __dns_handle_private_struct
+{
+       uint32_t handle_type;
+       sdns_handle_t *sdns;
+       pdns_handle_t *pdns;
+       char *recvbuf;
+       uint32_t recvsize;
+       uint32_t reserved1;
+       uint32_t reserved2;
+       void *reserved_pointer1;
+       void *reserved_pointer2;
+} dns_private_handle_t;
+
+
+__BEGIN_DECLS
+
+/*
+ * Returns the number of nameserver addresses available to the input
+ * DNS client.  Returns zero if the input handle is a "Super" DNS handle.
+ */
+extern uint32_t dns_server_list_count(dns_handle_t d);
+
+/*
+ * Returns the nameserver address at the given index.  Returns NULL
+ * if the index is out of range.  Caller should free the returned sockaddr.
+ */
+extern struct sockaddr *dns_server_list_address(dns_handle_t d, uint32_t i);
+
+/*
+ * Returns a list of all server addresses for all clients.
+ * Caller must free each list entry, and the returned list.
+ */
+extern void dns_all_server_addrs(dns_handle_t d, struct sockaddr ***addrs, uint32_t *count);
+
+/*
+ * Returns the number of names in the search list.
+ */
+uint32_t dns_search_list_count(dns_handle_t d);
+
+__END_DECLS
+
+#endif /* __DNS_PRIVATE_H__ */
diff --git a/dns_util.c b/dns_util.c
new file mode 100644 (file)
index 0000000..96b1f28
--- /dev/null
@@ -0,0 +1,2150 @@
+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
+ * Reserved.  This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License').  You may not use this file
+ * except in compliance with the License.  Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "dns.h"
+#include "dns_util.h"
+#include "dns_private.h"
+#include "res_private.h"
+
+#define DNS_RESOLVER_DIR "/etc/resolver"
+
+#define DNS_PRIVATE_HANDLE_TYPE_SUPER 0
+#define DNS_PRIVATE_HANDLE_TYPE_PLAIN 1
+#define DNS_DEFAULT_RECEIVE_SIZE 8192
+#define DNS_MAX_RECEIVE_SIZE 65536
+
+#define SDNS_DEFAULT_STAT_LATENCY 10
+
+#define DNS_FLAGS_QR_MASK  0x8000
+#define DNS_FLAGS_QR_QUERY 0x0000
+
+#define DNS_FLAGS_OPCODE_MASK    0x7800
+
+#define DNS_FLAGS_RCODE_MASK 0x000f
+
+#define DNS_FLAGS_AA 0x0400
+#define DNS_FLAGS_TC 0x0200
+#define DNS_FLAGS_RD 0x0100
+#define DNS_FLAGS_RA 0x0080
+
+#define DNS_SOCK_UDP 0
+#define DNS_SOCK_TCP_UNCONNECTED 1
+#define DNS_SOCK_TCP_CONNECTED 2
+
+#define INET_NTOP_AF_INET_OFFSET 4
+#define INET_NTOP_AF_INET6_OFFSET 8
+
+#define MAXPACKET 1024
+
+extern void res_client_close(res_state res);
+extern int __res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen);
+extern int dns_res_send(res_state statp, const u_char *buf, int buflen, u_char *ans, int *anssiz, struct sockaddr *from, int *fromlen);
+extern void _check_cache(sdns_handle_t *sdns);
+extern int _sdns_search(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, uint32_t recurse, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min);
+extern int _pdns_search(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen);
+static pthread_mutex_t _dnsPrintLock = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+_dns_print_lock(void)
+{
+       pthread_mutex_lock(&_dnsPrintLock);
+}
+
+static void
+_dns_print_unlock(void)
+{
+       pthread_mutex_unlock(&_dnsPrintLock);
+}
+
+
+static uint8_t
+_dns_parse_uint8(char **p)
+{
+       uint8_t v;
+
+       v = (uint8_t)**p;
+       *p += 1;
+       return v;
+}
+
+static uint16_t
+_dns_parse_uint16(char **p)
+{
+       uint16_t *x, v;
+
+       x = (uint16_t *)*p;
+       v = ntohs(*x);
+       *p += 2;
+       return v;
+}
+
+static uint32_t
+_dns_parse_uint32(char **p)
+{
+       uint32_t *x, v;
+
+       x = (uint32_t *)*p;
+       v = ntohl(*x);
+       *p += 4;
+       return v;
+}
+
+static uint8_t
+_dns_cname_length(char *s)
+{
+       uint8_t l;
+
+       if (s == NULL) return 1;
+       l = strlen(s);
+       while ((s[l - 1] == '.') && (l > 1)) l--;
+       return l;
+}
+
+static void
+_dns_insert_cname(char *s, char *p)
+{
+       int i;
+       uint8_t len, dlen;
+
+       if (s == NULL)
+       {
+               *p = 0;
+               return;
+       }
+
+       if (!strcmp(s, "."))
+       {
+               p[0] = 1;
+               p[1] = '.';
+               p[2] = 0;
+               return;
+       }
+
+       len = _dns_cname_length(s);
+
+       p[0] = '.';
+       memmove(p + 1, s, len);
+       p[len + 1] = '.';
+
+       dlen = 0;
+
+       for (i = len + 1; i >= 0; i--)
+       {
+               if (p[i] == '.')
+               {
+                       p[i] = dlen;
+                       dlen = 0;
+               }
+               else dlen++;
+       }
+}
+
+static char *
+_dns_parse_string(const char *p, char **x, int32_t *remaining)
+{
+       char *str;
+       uint8_t len;
+
+       if (*remaining < 1) return NULL;
+       *remaining -= 1;
+
+       len = (uint8_t)**x;
+       *x += 1;
+
+       if (*remaining < len) return NULL;
+       *remaining -= len;
+
+       str = malloc(len + 1);
+       memmove(str, *x, len);
+       str[len] = '\0';
+       *x += len;
+
+       return str;
+}
+
+static char *
+_dns_parse_domain_name(const char *p, char **x, int32_t *remaining)
+{
+       uint8_t *v8;
+       uint16_t *v16, skip;
+       uint16_t i, j, dlen, len;
+       int more, compressed;
+       char *name, *start, *y, *z;
+
+       if (*remaining < 1) return NULL;
+
+       z = *x + *remaining;
+       start = *x;
+       compressed = 0;
+       more = 1;
+       name = malloc(1);
+       name[0] = '\0';
+       len = 1;
+       j = 0;
+       skip = 0;
+
+       while (more == 1)
+       {
+               if ((*x + 1) > z)
+               {
+                       free(name);
+                       return NULL;
+               }
+
+               v8 = (uint8_t *)*x;
+               dlen = *v8;
+
+               if ((dlen & 0xc0) == 0xc0)
+               {
+                       if ((*x + 2) > z)
+                       {
+                               free(name);
+                               return NULL;
+                       }
+
+                       v16 = (uint16_t *)*x;
+
+                       y = (char *)p + (ntohs(*v16) & 0x3fff);
+                       if ((*x == y) || (y > z))
+                       {
+                               free(name);
+                               return NULL;
+                       }
+
+                       *x = y;
+                       if (compressed == 0) skip += 2;
+                       compressed = 1;
+                       continue;
+               }
+
+               if ((*x + 1) > z)
+               {
+                       free(name);
+                       return NULL;
+               }
+
+               *x += 1;
+
+               if (dlen > 0)
+               {
+                       len += dlen;
+                       name = realloc(name, len);
+               }
+
+               if ((*x + dlen) > z)
+               {
+                       free(name);
+                       return NULL;
+               }
+
+               for (i = 0; i < dlen; i++)
+               {
+                       name[j++] = **x;
+                       *x += 1;
+               }
+
+               name[j] = '\0';
+               if (compressed == 0) skip += (dlen + 1);
+
+               if (dlen == 0) more = 0;
+               else
+               {
+                       if ((*x + 1) > z)
+                       {
+                               free(name);
+                               return NULL;
+                       }
+
+                       v8 = (uint8_t *)*x;
+                       if (*v8 != 0)
+                       {
+                               len += 1;
+                               name = realloc(name, len);
+                               name[j++] = '.';
+                               name[j] = '\0';
+                       }
+               }
+       }
+
+       if ((start + skip) > z)
+       {
+               free(name);
+               return NULL;
+       }
+
+       *x = start + skip;
+       *remaining -= skip;
+
+       return name;
+}
+
+dns_resource_record_t *
+_dns_parse_resource_record_internal(const char *p, char **x, int32_t *remaining)
+{
+       uint32_t size, bx, mi;
+       uint16_t rdlen;
+       uint8_t byte, i;
+       dns_resource_record_t *r;
+       char *eor;
+
+       if (*remaining < 1) return NULL;
+
+       r = (dns_resource_record_t *)calloc(1, sizeof(dns_resource_record_t));
+
+       r->name = _dns_parse_domain_name(p, x, remaining);
+       if (r->name == NULL)
+       {
+               free(r);
+               return NULL;
+       }
+
+       if (*remaining < 10)
+       {
+               free(r);
+               return NULL;
+       }
+
+       r->dnstype = _dns_parse_uint16(x);
+       r->dnsclass = _dns_parse_uint16(x);
+       r->ttl = _dns_parse_uint32(x);
+       rdlen = _dns_parse_uint16(x);
+
+       *remaining -= 10;
+
+       eor = *x;
+       r->data.A = NULL;
+
+       switch (r->dnstype)
+       {
+               case ns_t_a:
+                       if (*remaining < 4)
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 4;
+
+                       size = sizeof(dns_address_record_t);
+                       r->data.A = (dns_address_record_t *)calloc(1, size);
+                       r->data.A->addr.s_addr = htonl(_dns_parse_uint32(x));
+                       break;
+
+               case ns_t_aaaa:
+                       if (*remaining < 16)
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 16;
+
+                       size = sizeof(dns_in6_address_record_t);
+                       r->data.AAAA = (dns_in6_address_record_t *)calloc(1, size);
+                       r->data.AAAA->addr.__u6_addr.__u6_addr32[0] = htonl(_dns_parse_uint32(x));
+                       r->data.AAAA->addr.__u6_addr.__u6_addr32[1] = htonl(_dns_parse_uint32(x));
+                       r->data.AAAA->addr.__u6_addr.__u6_addr32[2] = htonl(_dns_parse_uint32(x));
+                       r->data.AAAA->addr.__u6_addr.__u6_addr32[3] = htonl(_dns_parse_uint32(x));
+                       break;
+
+               case ns_t_ns:
+               case ns_t_md:
+               case ns_t_mf:
+               case ns_t_cname:
+               case ns_t_mb:
+               case ns_t_mg:
+               case ns_t_mr:
+               case ns_t_ptr:
+                       size = sizeof(dns_domain_name_record_t);
+                       r->data.CNAME = (dns_domain_name_record_t *)calloc(1,size);
+                       r->data.CNAME->name = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.CNAME->name == NULL)
+                       {
+                               free(r->data.CNAME);
+                               free(r);
+                               return NULL;
+                       }
+                       break;
+
+               case ns_t_soa:
+                       size = sizeof(dns_SOA_record_t);
+                       r->data.SOA = (dns_SOA_record_t *)calloc(1, size);
+
+                       r->data.SOA->mname = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.SOA->mname == NULL)
+                       {
+                               free(r->data.SOA);
+                               free(r);
+                               return NULL;
+                       }
+
+                       r->data.SOA->rname = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.SOA->rname == NULL)
+                       {
+                               free(r->data.SOA->mname);
+                               free(r->data.SOA);
+                               free(r);
+                               return NULL;
+                       }
+
+                       if (*remaining < 20) 
+                       {
+                               free(r->data.SOA->mname);
+                               free(r->data.SOA->rname);
+                               free(r->data.SOA);
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 20;
+
+                       r->data.SOA->serial = _dns_parse_uint32(x);
+                       r->data.SOA->refresh = _dns_parse_uint32(x);
+                       r->data.SOA->retry = _dns_parse_uint32(x);
+                       r->data.SOA->expire = _dns_parse_uint32(x);
+                       r->data.SOA->minimum = _dns_parse_uint32(x);
+                       break;
+
+               case ns_t_wks:
+                       if (*remaining < 5) 
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= rdlen;
+
+                       size = sizeof(dns_WKS_record_t);
+                       r->data.WKS = (dns_WKS_record_t *)calloc(1, size);
+
+                       r->data.WKS->addr.s_addr = htonl(_dns_parse_uint32(x));
+                       r->data.WKS->protocol = _dns_parse_uint8(x);
+                       size = rdlen - 5;
+                       r->data.WKS->maplength = size * 8;
+                       r->data.WKS->map = NULL;
+                       if (size == 0) break;
+
+                       r->data.WKS->map = (uint8_t *)calloc(1, r->data.WKS->maplength);
+                       mi = 0;
+                       for (bx = 0; bx < size; bx++)
+                       {
+                               byte = _dns_parse_uint8(x);
+                               for (i = 128; i >= 1; i = i/2)
+                               {
+                                       if (byte & i) r->data.WKS->map[mi] = 0xff;
+                                       else r->data.WKS->map[mi] = 0;
+                                       mi++;
+                               }
+                       }
+                       break;
+
+               case ns_t_hinfo:
+                       size = sizeof(dns_HINFO_record_t);
+                       r->data.HINFO = (dns_HINFO_record_t *)calloc(1, size);
+
+                       r->data.HINFO->cpu = _dns_parse_string(p, x, remaining);
+                       if (r->data.HINFO->cpu == NULL)
+                       {
+                               free(r->data.HINFO);
+                               free(r);
+                               return NULL;
+                       }
+
+                       r->data.HINFO->os = _dns_parse_string(p, x, remaining);
+                       if (r->data.HINFO->os == NULL)
+                       {
+                               free(r->data.HINFO->cpu);
+                               free(r->data.HINFO);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_minfo:
+                       size = sizeof(dns_MINFO_record_t);
+                       r->data.MINFO = (dns_MINFO_record_t *)calloc(1, size);
+
+                       r->data.MINFO->rmailbx = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.MINFO->rmailbx == NULL)
+                       {
+                               free(r->data.MINFO);
+                               free(r);
+                               return NULL;
+                       }
+
+                       r->data.MINFO->emailbx = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.MINFO->emailbx == NULL)
+                       {
+                               free(r->data.MINFO->rmailbx);
+                               free(r->data.MINFO);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_mx:
+                       if (*remaining < 2) 
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 2;
+
+                       size = sizeof(dns_MX_record_t);
+                       r->data.MX = (dns_MX_record_t *)calloc(1, size);
+
+                       r->data.MX->preference = _dns_parse_uint16(x);
+                       r->data.MX->name = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.MX->name == NULL)
+                       {
+                               free(r->data.MX);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_txt:
+                       size = sizeof(dns_TXT_record_t);
+                       r->data.TXT = (dns_TXT_record_t *)malloc(size);
+                       r->data.TXT->string_count = 0;
+                       r->data.TXT->strings = NULL;
+
+                       while (*x < (eor + rdlen))
+                       {
+                               if (r->data.TXT->string_count == 0)
+                               {
+                                       r->data.TXT->strings = (char **)calloc(1, sizeof(char *));
+                               }
+                               else
+                               {
+                                       r->data.TXT->strings = (char **)realloc(r->data.TXT->strings, (r->data.TXT->string_count + 1) * sizeof(char *));
+                               }
+
+                               r->data.TXT->strings[r->data.TXT->string_count] = _dns_parse_string(p, x, remaining);
+                               if (r->data.TXT->strings[r->data.TXT->string_count] == NULL)
+                               {
+                                       free(r->data.TXT->strings);
+                                       free(r->data.TXT);
+                                       free(r);
+                                       return NULL;
+                               }
+                               r->data.TXT->string_count++;
+                       }
+
+                       break;
+
+               case ns_t_rp:
+                       size = sizeof(dns_RP_record_t);
+                       r->data.RP = (dns_RP_record_t *)calloc(1, size);
+
+                       r->data.RP->mailbox = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.RP->mailbox == NULL)
+                       {
+                               free(r->data.RP);
+                               free(r);
+                               return NULL;
+                       }
+
+                       r->data.RP->txtdname = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.RP->txtdname == NULL)
+                       {
+                               free(r->data.RP->mailbox);
+                               free(r->data.RP);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_afsdb:
+                       if (*remaining < 4) 
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 4;
+                       size = sizeof(dns_AFSDB_record_t);
+                       r->data.AFSDB = (dns_AFSDB_record_t *)calloc(1, size);
+
+                       r->data.AFSDB->subtype = _dns_parse_uint32(x);
+                       r->data.AFSDB->hostname = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.AFSDB->hostname == NULL)
+                       {
+                               free(r->data.AFSDB);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_x25:
+                       size = sizeof(dns_X25_record_t);
+                       r->data.X25 = (dns_X25_record_t *)calloc(1, size);
+
+                       r->data.X25->psdn_address = _dns_parse_string(p, x, remaining);
+                       if (r->data.X25->psdn_address == NULL)
+                       {
+                               free(r->data.X25);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_isdn:
+                       size = sizeof(dns_ISDN_record_t);
+                       r->data.ISDN = (dns_ISDN_record_t *)calloc(1, size);
+
+                       r->data.ISDN->isdn_address = _dns_parse_string(p, x, remaining);
+                       if (r->data.ISDN->isdn_address == NULL)
+                       {
+                               free(r->data.ISDN);
+                               free(r);
+                               return NULL;
+                       }
+
+                       if (*x < (eor + rdlen))
+                       {
+                               r->data.ISDN->subaddress = _dns_parse_string(p, x, remaining);
+                               if (r->data.ISDN->subaddress == NULL)
+                               {
+                                       free(r->data.ISDN->isdn_address);
+                                       free(r->data.ISDN);
+                                       free(r);
+                                       return NULL;
+                               }
+                       }
+                       else
+                       {
+                               r->data.ISDN->subaddress = NULL;
+                       }
+
+                       break;
+
+               case ns_t_rt:
+                       if (*remaining < 2) 
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 2;
+
+                       size = sizeof(dns_RT_record_t);
+                       r->data.RT = (dns_RT_record_t *)calloc(1, size);
+
+                       r->data.RT->preference = _dns_parse_uint16(x);
+                       r->data.RT->intermediate = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.RT->intermediate == NULL)
+                       {
+                               free(r->data.RT);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_loc:
+                       if (*remaining < 16) 
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 16;
+
+                       size = sizeof(dns_LOC_record_t);
+                       r->data.LOC = (dns_LOC_record_t *)calloc(1, size);
+
+                       r->data.LOC->version = _dns_parse_uint8(x);
+                       r->data.LOC->size = _dns_parse_uint8(x);
+                       r->data.LOC->horizontal_precision = _dns_parse_uint8(x);
+                       r->data.LOC->vertical_precision = _dns_parse_uint8(x);
+                       r->data.LOC->latitude = _dns_parse_uint32(x);
+                       r->data.LOC->longitude = _dns_parse_uint32(x);
+                       r->data.LOC->altitude = _dns_parse_uint32(x);
+                       break;
+
+               case ns_t_srv:
+                       if (*remaining < 6) 
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= 6;
+
+                       size = sizeof(dns_SRV_record_t);
+                       r->data.SRV = (dns_SRV_record_t *)calloc(1, size);
+
+                       r->data.SRV->priority = _dns_parse_uint16(x);
+                       r->data.SRV->weight = _dns_parse_uint16(x);
+                       r->data.SRV->port = _dns_parse_uint16(x);
+                       r->data.SRV->target = _dns_parse_domain_name(p, x, remaining);
+                       if (r->data.SRV->target == NULL)
+                       {
+                               free(r->data.SRV);
+                               free(r);
+                               return NULL;
+                       }
+
+                       break;
+
+               case ns_t_null:
+               default:
+                       if (*remaining < rdlen)
+                       {
+                               free(r);
+                               return NULL;
+                       }
+
+                       *remaining -= rdlen;
+
+                       size = sizeof(dns_raw_resource_record_t);
+                       r->data.DNSNULL = (dns_raw_resource_record_t *)calloc(1, size);
+
+                       r->data.DNSNULL->length = rdlen;
+                       r->data.DNSNULL->data = calloc(1, rdlen);
+                       memmove(r->data.DNSNULL->data, *x, rdlen);
+                       *x += rdlen;
+                       break;
+       }
+
+       *x = eor + rdlen;
+       return r;
+}
+
+dns_resource_record_t *
+dns_parse_resource_record(const char *buf, uint32_t len)
+{
+       char *x;
+       int32_t remaining;
+
+       remaining = len;
+       x = (char *)buf;
+       return _dns_parse_resource_record_internal(buf, &x, &remaining);
+}
+
+dns_question_t *
+_dns_parse_question_internal(const char *p, char **x, int32_t *remaining)
+{
+       dns_question_t *q;
+
+       if (x == NULL) return NULL;
+       if (*x == NULL) return NULL;
+       if (*remaining < 1) return NULL;
+
+       q = (dns_question_t *)calloc(1, sizeof(dns_question_t));
+
+       q->name = _dns_parse_domain_name(p, x, remaining);
+       if (q->name == NULL)
+       {
+               free(q);
+               return NULL;
+       }
+
+       if (*remaining < 4)
+       {
+               free(q->name);
+               free(q);
+               return NULL;
+       }
+
+       *remaining = *remaining - 4;
+
+       q->dnstype = _dns_parse_uint16(x);
+       q->dnsclass = _dns_parse_uint16(x);
+
+       return q;
+}
+
+dns_question_t *
+dns_parse_question(const char *buf, uint32_t len)
+{
+       char *x;
+       int32_t remaining;
+
+       remaining = len;
+       x = (char *)buf;
+       return _dns_parse_question_internal(buf, &x, &remaining);
+}
+
+
+dns_reply_t *
+dns_parse_packet(const char *p, uint32_t len)
+{
+       dns_reply_t *r;
+       dns_header_t *h;
+       char *x;
+       uint32_t i, size;
+    int32_t remaining;
+
+       if (p == NULL) return NULL;
+       if (len < NS_HFIXEDSZ) return NULL;
+
+       x = (char *)p;
+
+       r = (dns_reply_t *)calloc(1, sizeof(dns_reply_t));
+
+       r->header = (dns_header_t *)calloc(1, sizeof(dns_header_t));
+       h = r->header;
+
+       h->xid = _dns_parse_uint16(&x);
+       h->flags = _dns_parse_uint16(&x);
+       h->qdcount = _dns_parse_uint16(&x);
+       h->ancount = _dns_parse_uint16(&x);
+       h->nscount = _dns_parse_uint16(&x);
+       h->arcount = _dns_parse_uint16(&x);
+
+       remaining = len - NS_HFIXEDSZ;
+
+       size = sizeof(dns_question_t *);
+       r->question = (dns_question_t **)calloc(h->qdcount, size);
+       for (i = 0; i < h->qdcount; i++)
+       {
+               r->question[i] = _dns_parse_question_internal(p, &x, &remaining);
+               if (r->question[i] ==NULL)
+               {
+                       h->qdcount = 0;
+                       if (i > 0) h->qdcount = i - 1;
+                       h->ancount = 0;
+                       h->nscount = 0;
+                       h->arcount = 0;
+                       dns_free_reply(r);
+                       return NULL;
+               }
+       }
+
+       size = sizeof(dns_resource_record_t *);
+
+       r->answer = (dns_resource_record_t **)calloc(h->ancount, size);
+       for (i = 0; i < h->ancount; i++)
+       {
+               r->answer[i] = _dns_parse_resource_record_internal(p, &x, &remaining);
+               if (r->answer[i] == NULL)
+               {
+                       h->ancount = 0;
+                       if (i > 0) h->ancount = i - 1;
+                       h->nscount = 0;
+                       h->arcount = 0;
+                       dns_free_reply(r);
+                       return NULL;
+               }
+       }
+
+       r->authority = (dns_resource_record_t **)calloc(h->nscount, size);
+       for (i = 0; i < h->nscount; i++)
+       {
+               r->authority[i] = _dns_parse_resource_record_internal(p, &x, &remaining);
+               if (r->authority[i] == NULL)
+               {
+                       h->nscount = 0;
+                       if (i > 0) h->nscount = i - 1;
+                       h->arcount = 0;
+                       dns_free_reply(r);
+                       return NULL;
+               }
+       }
+
+       r->additional = (dns_resource_record_t **)calloc(h->arcount, size);
+       for (i = 0; i < h->arcount; i++)
+       {
+               r->additional[i] = _dns_parse_resource_record_internal(p, &x, &remaining);
+               if (r->additional[i] == NULL)
+               {
+                       h->arcount = 0;
+                       if (i > 0) h->arcount = i - 1;
+                       dns_free_reply(r);
+                       return NULL;
+               }
+       }
+
+       return r;
+}
+
+void
+dns_free_resource_record(dns_resource_record_t *r)
+{
+       int i;
+
+       free(r->name);
+
+       switch (r->dnstype)
+       {
+               case ns_t_a:
+                       free(r->data.A);
+                       break;
+
+               case ns_t_aaaa:
+                       free(r->data.AAAA);
+                       break;
+
+               case ns_t_ns:
+               case ns_t_md:
+               case ns_t_mf:
+               case ns_t_cname:
+               case ns_t_mb:
+               case ns_t_mg:
+               case ns_t_mr:
+               case ns_t_ptr:
+                       free(r->data.CNAME->name);
+                       free(r->data.CNAME);
+                       break;
+
+               case ns_t_soa:
+                       free(r->data.SOA->mname);
+                       free(r->data.SOA->rname);
+                       free(r->data.SOA);
+                       break;
+
+               case ns_t_wks:
+                       free(r->data.WKS->map);
+                       free(r->data.WKS);
+                       break;
+
+               case ns_t_hinfo:
+                       free(r->data.HINFO->cpu);
+                       free(r->data.HINFO->os);
+                       free(r->data.HINFO);
+                       break;
+
+               case ns_t_minfo:
+                       free(r->data.MINFO->rmailbx);
+                       free(r->data.MINFO->emailbx);
+                       free(r->data.MINFO);
+                       break;
+
+               case ns_t_mx:
+                       free(r->data.MX->name);
+                       free(r->data.MX);
+                       break;
+
+
+               case ns_t_txt:
+                       for (i=0; i<r->data.TXT->string_count; i++)
+                       {
+                               free(r->data.TXT->strings[i]);
+                       }
+                       if (r->data.TXT->strings != NULL)
+                               free(r->data.TXT->strings);
+                       free(r->data.TXT);
+                       break;
+
+               case ns_t_rp:
+                       free(r->data.RP->mailbox);
+                       free(r->data.RP->txtdname);
+                       free(r->data.RP);
+                       break;
+
+               case ns_t_afsdb:
+                       free(r->data.AFSDB->hostname);
+                       free(r->data.AFSDB);
+                       break;
+
+               case ns_t_x25:
+                       free(r->data.X25->psdn_address);
+                       free(r->data.X25);
+                       break;
+
+               case ns_t_isdn:
+                       free(r->data.ISDN->isdn_address);
+                       if (r->data.ISDN->subaddress != NULL)
+                               free(r->data.ISDN->subaddress);
+                       free(r->data.ISDN);
+                       break;
+
+               case ns_t_rt:
+                       free(r->data.RT->intermediate);
+                       free(r->data.RT);
+                       break;
+
+               case ns_t_loc:
+                       free(r->data.LOC);
+                       break;
+
+               case ns_t_srv:
+                       free(r->data.SRV->target);
+                       free(r->data.SRV);
+                       break;
+
+               case ns_t_null:
+               default:
+                       free(r->data.DNSNULL->data);
+                       free(r->data.DNSNULL);
+                       break;
+       }
+
+       free(r);
+}
+
+void
+dns_free_reply(dns_reply_t *r)
+{
+       uint32_t i;
+
+       if (r == NULL) return;
+       if (r->header != NULL)
+       {
+               for (i = 0; i < r->header->qdcount; i++)
+               {
+                       free(r->question[i]->name);
+                       free(r->question[i]);
+               }
+
+               for (i = 0; i < r->header->ancount; i++) dns_free_resource_record(r->answer[i]);
+               for (i = 0; i < r->header->nscount; i++) dns_free_resource_record(r->authority[i]);
+               for (i = 0; i < r->header->arcount; i++) dns_free_resource_record(r->additional[i]);
+
+               free(r->header);
+       }
+
+       if (r->question != NULL) free(r->question);
+       if (r->answer != NULL) free(r->answer);
+       if (r->authority != NULL) free(r->authority);
+       if (r->additional != NULL) free(r->additional);
+
+       if (r->server != NULL) free(r->server);
+
+       free(r);
+}
+
+static void
+_dns_append_question(dns_question_t *q, char **s, uint16_t *l)
+{
+       uint16_t len, *p;
+       char *x;
+
+       if (q == NULL) return;
+
+       len = *l + _dns_cname_length(q->name) + 2 + 4;
+       *s = realloc(*s, len);
+
+       _dns_insert_cname(q->name, (char *)*s + *l);
+       *l = len;
+
+       x = *s + (len - 4);
+
+       p = (uint16_t *)x;
+       *p = htons(q->dnstype);
+       x += 2;
+
+       p = (uint16_t *)x;
+       *p = htons(q->dnsclass);
+
+}
+
+static void
+_dns_append_resource_record(dns_resource_record_t *r, char **s, uint16_t *l)
+{
+       uint16_t clen, len, *p, extra, rdlen;
+       uint32_t *p2;
+       char *x;
+
+       if (r == NULL) return;
+
+       extra = 10;
+       switch (r->dnstype)
+       {
+               case ns_t_a:
+                       extra += 4;
+                       break;
+               case ns_t_ptr:
+                       extra += 2;
+                       clen = _dns_cname_length(r->data.PTR->name);
+                       extra += clen;
+                       break;
+               default: break;
+       }
+
+       len = *l + _dns_cname_length(r->name) + 2 + extra;
+       *s = realloc(*s, len);
+
+       _dns_insert_cname(r->name, (char *)*s + *l);
+       *l = len;
+
+       x = *s + (len - extra);
+
+       p = (uint16_t *)x;
+       *p = htons(r->dnstype);
+       x += 2;
+
+       p = (uint16_t *)x;
+       *p = htons(r->dnsclass);
+       x += 2;
+
+       p2 = (uint32_t *)x;
+       *p2 = htonl(r->ttl);
+       x += 4;
+
+       switch (r->dnstype)
+       {
+               case ns_t_a:
+                       rdlen = 4;
+                       p = (uint16_t *)x;
+                       *p = htons(rdlen);
+                       x += 2;
+
+                       p2 = (uint32_t *)x;
+                       *p2 = htons(r->data.A->addr.s_addr);
+                       x += 4;
+                       return;
+
+               case ns_t_ptr:
+                       clen = _dns_cname_length(r->data.PTR->name) + 2;
+                       p = (uint16_t *)x;
+                       *p = htons(clen);
+                       x += 2;
+                       _dns_insert_cname(r->data.PTR->name, x);
+                       x += clen;
+                       return;
+
+               default: return;
+       }
+}
+
+char *
+dns_build_reply(dns_reply_t *dnsr, uint16_t *rl)
+{
+       uint16_t i, len;
+       dns_header_t *h;
+       char *s, *x;
+
+       if (dnsr == NULL) return NULL;
+
+       len = NS_HFIXEDSZ;
+
+       s = malloc(len);
+       x = s + len;
+
+       memset(s, 0, len);
+       *rl = len;
+
+       h = (dns_header_t *)s;
+
+       h->xid = htons(dnsr->header->xid);
+       h->flags = htons(dnsr->header->flags);
+       h->qdcount = htons(dnsr->header->qdcount);
+       h->ancount = htons(dnsr->header->ancount);
+       h->nscount = htons(dnsr->header->nscount);
+       h->arcount = htons(dnsr->header->arcount);
+
+       for (i = 0; i < dnsr->header->qdcount; i++)
+       {
+               _dns_append_question(dnsr->question[i], &s, rl);
+       }
+
+       for (i = 0; i < dnsr->header->ancount; i++)
+       {
+               _dns_append_resource_record(dnsr->answer[i], &s, rl);
+       }
+
+       for (i = 0; i < dnsr->header->nscount; i++)
+       {
+               _dns_append_resource_record(dnsr->authority[i], &s, rl);
+       }
+
+       for (i = 0; i < dnsr->header->arcount; i++)
+       {
+               _dns_append_resource_record(dnsr->additional[i], &s, rl);
+       }
+
+       return s;
+}
+
+void
+dns_free_question(dns_question_t *q)
+{
+       if (q == NULL) return;
+       if (q->name != NULL) free(q->name);
+       free(q);
+}
+
+void
+dns_set_buffer_size(dns_handle_t d, uint32_t len)
+{
+       dns_private_handle_t *dns;
+       if (d == NULL) return;
+
+       dns = (dns_private_handle_t *)d;
+       if (dns->recvsize == len) return;
+
+       if (dns->recvbuf != NULL)
+       {
+               free(dns->recvbuf);
+               dns->recvbuf = NULL;
+       }
+
+       dns->recvsize = len;
+       if (dns->recvsize > DNS_MAX_RECEIVE_SIZE) dns->recvsize = DNS_MAX_RECEIVE_SIZE;
+
+       if (dns->recvsize > 0) dns->recvbuf = malloc(dns->recvsize);
+}
+
+uint32_t
+dns_get_buffer_size(dns_handle_t d)
+{
+       dns_private_handle_t *dns;
+       if (d == NULL) return 0;
+
+       dns = (dns_private_handle_t *)d;
+       return dns->recvsize;
+}
+
+dns_reply_t *
+dns_lookup_soa_min(dns_handle_t d, const char *name, uint32_t class, uint32_t type, int *min)
+{
+       dns_private_handle_t *dns;
+       dns_reply_t *r;
+       int len;
+       struct sockaddr_storage *from;
+       uint32_t fromlen;
+
+       if (d == NULL) return NULL;
+       if (name == NULL) return NULL;
+
+       dns = (dns_private_handle_t *)d;
+       if (min != NULL) *min = -1;
+
+       if (dns->recvbuf == NULL)
+       {
+               if (dns->recvsize == 0) dns->recvsize = DNS_DEFAULT_RECEIVE_SIZE;
+
+               dns->recvbuf = malloc(dns->recvsize);
+               if (dns->recvbuf == NULL) return NULL;
+       }
+
+       fromlen = sizeof(struct sockaddr_storage);
+       from = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage));
+       len = -1;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _check_cache(dns->sdns);
+               len = _sdns_search(dns->sdns, name, class, type, 0, 1,  dns->recvbuf, dns->recvsize, (struct sockaddr *)from, &fromlen, min);
+       }
+       else
+       {
+               /* NB.  Minumium SOA TTL values are NOT provided when the caller passes a DNS_PRIVATE_HANDLE_TYPE_PLAIN handle */
+               len = _pdns_search(dns->sdns, dns->pdns, name, class, type,  dns->recvbuf, dns->recvsize, (struct sockaddr *)from, &fromlen);
+       }
+
+       if (len <= 0)
+       {
+               free(from);
+               return NULL;
+       }
+
+       r = dns_parse_packet(dns->recvbuf, len);
+
+       if (r == NULL) free(from);
+       else r->server = (struct sockaddr *)from;
+
+       return r;
+}
+
+dns_reply_t *
+dns_lookup(dns_handle_t d, const char *name, uint32_t class, uint32_t type)
+{
+       int unused = 0;
+
+       return dns_lookup_soa_min(d, name, class, type, &unused);
+}
+
+/*
+ * DNS printing utilities
+ */
+
+static char *
+coord_ntoa(int32_t coord, uint32_t islat)
+{
+       int32_t deg, min, sec, secfrac;
+       static char buf[64];
+       char dir;
+
+       coord = coord - 0x80000000;
+       dir = 'N';
+
+       if ((islat == 1) && (coord < 0))
+       {
+               dir = 'S';
+               coord = -coord;
+       }
+
+       if (islat == 0)
+       {
+               dir = 'E';
+               if (coord < 0)
+               {
+                       dir = 'W';
+                       coord = -coord;
+               }
+       }
+
+       secfrac = coord % 1000;
+       coord = coord / 1000;
+       sec = coord % 60;
+       coord = coord / 60;
+       min = coord % 60;
+       coord = coord / 60;
+       deg = coord;
+
+       sprintf(buf, "%d %.2d %.2d.%.3d %c", deg, min, sec, secfrac, dir);
+       return buf;
+}
+
+static char *
+alt_ntoa(int32_t alt)
+{
+       int32_t ref, m, frac, sign;
+       static char buf[128];
+
+       ref = 100000 * 100;
+       sign = 1;
+
+       if (alt < ref)
+       {
+               alt = ref - alt;
+               sign = -1;
+       }
+       else
+       {
+               alt = alt - ref;
+       }
+
+       frac = alt % 100;
+       m = (alt / 100) * sign;
+
+       sprintf(buf, "%d.%.2d", m, frac);
+       return buf;
+}
+
+static unsigned int
+poweroften[10] =
+{      1,
+       10,
+       100,
+       1000,
+       10000,
+       100000,
+       1000000,
+       10000000,
+       100000000,
+       1000000000
+};
+
+static char *
+precsize_ntoa(uint8_t prec)
+{
+       static char buf[19];
+       unsigned long val;
+       int mantissa, exponent;
+
+       mantissa = (int)((prec >> 4) & 0x0f) % 10;
+       exponent = (int)((prec >> 0) & 0x0f) % 10;
+
+       val = mantissa * poweroften[exponent];
+
+       sprintf(buf, "%ld.%.2ld", val/100, val%100);
+       return buf;
+}
+
+const char *
+dns_type_string(uint16_t t)
+{
+       switch (t)
+       {
+               case ns_t_a:     return "A    ";
+               case ns_t_ns:    return "NS   ";
+               case ns_t_md:    return "MD   ";
+               case ns_t_mf:    return "MF   ";
+               case ns_t_cname: return "CNAME";
+               case ns_t_soa:   return "SOA  ";
+               case ns_t_mb:    return "MB  ";
+               case ns_t_mg:    return "MG   ";
+               case ns_t_mr:    return "MR   ";
+               case ns_t_null:  return "NULL ";
+               case ns_t_wks:   return "WKS  ";
+               case ns_t_ptr:   return "PTR  ";
+               case ns_t_hinfo: return "HINFO";
+               case ns_t_minfo: return "MINFO";
+               case ns_t_mx:    return "MX   ";
+               case ns_t_txt:   return "TXT  ";
+               case ns_t_rp:    return "PR   ";
+               case ns_t_afsdb: return "AFSDB";
+               case ns_t_x25:   return "X25  ";
+               case ns_t_isdn:  return "ISDN ";
+               case ns_t_rt:    return "RT   ";
+               case ns_t_nsap:  return "NSAP ";
+               case ns_t_nsap_ptr: return "NSPTR";
+               case ns_t_sig:   return "SIG  ";
+               case ns_t_key:   return "KEY  ";
+               case ns_t_px:    return "PX   ";
+               case ns_t_gpos:  return "GPOS ";
+               case ns_t_aaaa:  return "AAAA ";
+               case ns_t_loc:   return "LOC  ";
+               case ns_t_nxt:   return "NXT  ";
+               case ns_t_eid:   return "EID  ";
+               case ns_t_nimloc: return "NIMLC";
+               case ns_t_srv:   return "SRV  ";
+               case ns_t_atma:  return "ATMA ";
+               case ns_t_naptr: return "NAPTR";
+               case ns_t_kx:    return "KX   ";
+               case ns_t_cert:  return "CERT ";
+               case ns_t_a6:    return "A6   ";
+               case ns_t_dname: return "DNAME";
+               case ns_t_sink:  return "SINK ";
+               case ns_t_opt:   return "OPT  ";
+               case ns_t_tkey:  return "TKEY ";
+               case ns_t_tsig:  return "TSIG ";
+               case ns_t_ixfr:  return "IXFR ";
+               case ns_t_axfr:  return "AXFR ";
+               case ns_t_mailb: return "MAILB";
+               case ns_t_maila: return "MAILA";
+               case ns_t_any:   return "ANY  ";
+               case ns_t_zxfr:  return "ZXFR ";
+               default:         return "?????";
+       }
+
+       return "?????";
+}
+
+int32_t
+dns_type_number(const char *t, uint16_t *n)
+{
+       if (t == NULL) return -1;
+
+       if (!strcasecmp(t, "A"))       { *n = ns_t_a;        return 0; }
+       if (!strcasecmp(t, "NS"))      { *n = ns_t_ns;       return 0; }
+       if (!strcasecmp(t, "MD"))      { *n = ns_t_md;       return 0; }
+       if (!strcasecmp(t, "MF"))      { *n = ns_t_mf;       return 0; }
+       if (!strcasecmp(t, "CNAME"))   { *n = ns_t_cname;    return 0; }
+       if (!strcasecmp(t, "SOA"))     { *n = ns_t_soa;      return 0; }
+       if (!strcasecmp(t, "MB"))      { *n = ns_t_mb;       return 0; }
+       if (!strcasecmp(t, "MG"))      { *n = ns_t_mg;       return 0; }
+       if (!strcasecmp(t, "MR"))      { *n = ns_t_mr;       return 0; }
+       if (!strcasecmp(t, "NULL"))    { *n = ns_t_null;     return 0; }
+       if (!strcasecmp(t, "WKS"))     { *n = ns_t_wks;      return 0; }
+       if (!strcasecmp(t, "PTR"))     { *n = ns_t_ptr;      return 0; }
+       if (!strcasecmp(t, "HINFO"))   { *n = ns_t_hinfo;    return 0; }
+       if (!strcasecmp(t, "MINFO"))   { *n = ns_t_minfo;    return 0; }
+       if (!strcasecmp(t, "TXT"))     { *n = ns_t_txt;      return 0; }
+       if (!strcasecmp(t, "RP"))      { *n = ns_t_rp;       return 0; }
+       if (!strcasecmp(t, "AFSDB"))   { *n = ns_t_afsdb;    return 0; }
+       if (!strcasecmp(t, "X25"))     { *n = ns_t_x25;      return 0; }
+       if (!strcasecmp(t, "ISDN"))    { *n = ns_t_isdn;     return 0; }
+       if (!strcasecmp(t, "RT"))      { *n = ns_t_rt;       return 0; }
+       if (!strcasecmp(t, "NSAP"))    { *n = ns_t_nsap;     return 0; }
+       if (!strcasecmp(t, "NSPTR"))   { *n = ns_t_nsap_ptr; return 0; }
+       if (!strcasecmp(t, "NSAP_PTR")){ *n = ns_t_nsap_ptr; return 0; }
+       if (!strcasecmp(t, "SIG"))     { *n = ns_t_sig;      return 0; }
+       if (!strcasecmp(t, "KEY"))     { *n = ns_t_key;      return 0; }
+       if (!strcasecmp(t, "PX"))      { *n = ns_t_px;       return 0; }
+       if (!strcasecmp(t, "GPOS"))    { *n = ns_t_gpos;     return 0; }
+       if (!strcasecmp(t, "AAAA"))    { *n = ns_t_aaaa;     return 0; }
+       if (!strcasecmp(t, "LOC"))     { *n = ns_t_loc;      return 0; }
+       if (!strcasecmp(t, "NXT"))     { *n = ns_t_nxt;      return 0; }
+       if (!strcasecmp(t, "EID"))     { *n = ns_t_eid;      return 0; }
+       if (!strcasecmp(t, "NIMLOC"))  { *n = ns_t_nimloc;   return 0; }
+       if (!strcasecmp(t, "SRV"))     { *n = ns_t_srv;      return 0; }
+       if (!strcasecmp(t, "ATMA"))    { *n = ns_t_atma;     return 0; }
+       if (!strcasecmp(t, "NAPTR"))   { *n = ns_t_naptr;    return 0; }
+       if (!strcasecmp(t, "KX"))      { *n = ns_t_kx;       return 0; }
+       if (!strcasecmp(t, "CERT"))    { *n = ns_t_cert;     return 0; }
+       if (!strcasecmp(t, "A6"))      { *n = ns_t_a6;       return 0; }
+       if (!strcasecmp(t, "DNAME"))   { *n = ns_t_dname;    return 0; }
+       if (!strcasecmp(t, "SINK"))    { *n = ns_t_sink;     return 0; }
+       if (!strcasecmp(t, "OPT"))     { *n = ns_t_opt;      return 0; }
+       if (!strcasecmp(t, "TKEY"))    { *n = ns_t_tkey;     return 0; }
+       if (!strcasecmp(t, "TSIG"))    { *n = ns_t_tsig;     return 0; }
+       if (!strcasecmp(t, "IXFR"))    { *n = ns_t_ixfr;     return 0; }
+       if (!strcasecmp(t, "AXFR"))    { *n = ns_t_axfr;     return 0; }
+       if (!strcasecmp(t, "MAILB"))   { *n = ns_t_mailb;    return 0; }
+       if (!strcasecmp(t, "MAILA"))   { *n = ns_t_maila;    return 0; }
+       if (!strcasecmp(t, "ANY"))     { *n = ns_t_any;      return 0; }
+       if (!strcasecmp(t, "ZXFR"))    { *n = ns_t_zxfr;     return 0; }
+
+       return -1;
+}
+
+const char *
+dns_class_string(uint16_t c)
+{
+       switch (c)
+       {
+               case ns_c_in: return "IN";
+               case ns_c_2: return "CS";
+               case ns_c_chaos: return "CH";
+               case ns_c_hs: return "HS";
+               case ns_c_none: return "NONE";
+               case ns_c_any: return "ANY";
+               default: return "??";
+       }
+
+       return "??";
+}
+
+int32_t
+dns_class_number(const char *c, uint16_t *n)
+{
+       if (c == NULL) return -1;
+
+       if (!strcasecmp(c, "IN"))   { *n = ns_c_in;    return 0; }
+       if (!strcasecmp(c, "CS"))   { *n = ns_c_2;     return 0; }
+       if (!strcasecmp(c, "CH"))   { *n = ns_c_chaos; return 0; }
+       if (!strcasecmp(c, "HS"))   { *n = ns_c_hs;    return 0; }
+       if (!strcasecmp(c, "NONE")) { *n = ns_c_none;  return 0; }
+       if (!strcasecmp(c, "ANY"))  { *n = ns_c_any;   return 0; }
+
+       return -1;
+}
+
+static void
+_dns_print_question_lock(const dns_question_t *q, FILE *f, int lockit)
+{
+       if (lockit != 0) _dns_print_lock();
+       fprintf(f, "%s %s %s\n", q->name, dns_class_string(q->dnsclass), dns_type_string(q->dnstype));
+       if (lockit != 0) _dns_print_unlock();
+}
+
+void
+dns_print_question(const dns_question_t *q, FILE *f)
+{
+       _dns_print_question_lock(q, f, 1);
+}
+
+static void
+_dns_print_resource_record_lock(const dns_resource_record_t *r, FILE *f, int lockit)
+{
+       struct protoent *p;
+       struct servent *s;
+       uint32_t i, len;
+       uint8_t x;
+       struct sockaddr_in6 s6;
+       char pbuf[64];
+
+       if (lockit != 0) _dns_print_lock();
+
+       fprintf(f, "%s %s %s ", r->name, dns_class_string(r->dnsclass), dns_type_string(r->dnstype));
+       fprintf(f, "%u", r->ttl);
+
+       switch (r->dnstype)
+       {
+               case ns_t_a:
+                       fprintf(f, " %s", inet_ntoa(r->data.A->addr));
+                       break;
+
+               case ns_t_aaaa:
+                       memset(&s6, 0, sizeof(struct sockaddr_in6));
+                       s6.sin6_len = sizeof(struct sockaddr_in6);
+                       s6.sin6_family = AF_INET6;
+                       s6.sin6_addr = r->data.AAAA->addr;
+                       fprintf(f, " %s", inet_ntop(AF_INET6, (char *)(&s6) + INET_NTOP_AF_INET6_OFFSET, pbuf, 64));
+                       break;
+
+               case ns_t_md:
+               case ns_t_mf:
+               case ns_t_cname:
+               case ns_t_mb:
+               case ns_t_mg:
+               case ns_t_mr:
+               case ns_t_ptr:
+               case ns_t_ns:
+                       fprintf(f, " %s", r->data.CNAME->name);
+                       break;
+
+               case ns_t_soa:
+                       fprintf(f, " %s %s %u %u %u %u %u",
+                               r->data.SOA->mname, r->data.SOA->rname,
+                               r->data.SOA->serial, r->data.SOA->refresh, r->data.SOA->retry,
+                               r->data.SOA->expire, r->data.SOA->minimum);
+                       break;
+
+               case ns_t_wks:
+                       fprintf(f, " %s", inet_ntoa(r->data.WKS->addr));
+                       p = getprotobynumber(r->data.WKS->protocol);
+                       if (p != NULL)
+                       {
+                               fprintf(f, " %s", p->p_name);
+
+                               for (i = 0; i < r->data.WKS->maplength; i++)
+                               {
+                                       if (r->data.WKS->map[i])
+                                       {
+                                               s = getservbyport(i, p->p_name);
+                                               if (s == NULL) fprintf(f, " %u", i);
+                                               else fprintf(f, " %s", s->s_name);
+                                       }
+                               }
+                       }
+                       else fprintf(f, " UNKNOWN PROTOCOL %u", r->data.WKS->protocol);
+                       break;
+
+               case ns_t_hinfo:
+                       fprintf(f, " %s %s", r->data.HINFO->cpu, r->data.HINFO->os);
+                       break;
+
+               case ns_t_minfo:
+                       fprintf(f, " %s %s", r->data.MINFO->rmailbx, r->data.MINFO->emailbx);
+                       break;
+
+               case ns_t_mx:
+                       fprintf(f, " %u %s", r->data.MX->preference, r->data.MX->name);
+                       break;
+
+               case ns_t_txt:
+                       for (i = 0; i < r->data.TXT->string_count; i++)
+                       {
+                               fprintf(f, " \"%s\"", r->data.TXT->strings[i]);
+                       }
+                       break;
+
+               case ns_t_rp:
+                       fprintf(f, " %s %s", r->data.RP->mailbox, r->data.RP->txtdname);
+                       break;
+
+               case ns_t_afsdb:
+                       fprintf(f, " %u %s", r->data.AFSDB->subtype,
+                               r->data.AFSDB->hostname);
+                       break;
+
+               case ns_t_x25:
+                       fprintf(f, " %s", r->data.X25->psdn_address);
+                       break;
+
+               case ns_t_isdn:
+                       fprintf(f, " %s", r->data.ISDN->isdn_address);
+                       if (r->data.ISDN->subaddress != NULL)
+                               fprintf(f, " %s", r->data.ISDN->subaddress);
+                       break;
+
+               case ns_t_rt:
+                       fprintf(f, " %hu %s", r->data.RT->preference,
+                               r->data.RT->intermediate);
+                       break;
+
+               case ns_t_loc:
+                       fprintf(f, " %s", coord_ntoa(r->data.LOC->latitude, 1));
+                       fprintf(f, " %s", coord_ntoa(r->data.LOC->longitude, 0));
+                       fprintf(f, " %sm", alt_ntoa(r->data.LOC->altitude));
+                       fprintf(f, " %sm", precsize_ntoa(r->data.LOC->size));
+                       fprintf(f, " %sm", precsize_ntoa(r->data.LOC->horizontal_precision));
+                       fprintf(f, " %sm", precsize_ntoa(r->data.LOC->vertical_precision));
+                       break;
+
+               case ns_t_srv:
+                       fprintf(f, " %hu %hu %hu %s",
+                               r->data.SRV->priority, r->data.SRV->weight, 
+                               r->data.SRV->port, r->data.SRV->target);
+                       break;
+
+               case ns_t_null:
+               default:
+                       len = r->data.DNSNULL->length;
+                       fprintf(f, " %hu ", len);
+                       for (i = 0; i < len; i++)
+                       {
+                               x = r->data.DNSNULL->data[i];
+                               fprintf(f, "%x", x);
+                       }
+
+                       fprintf(f, " (");
+
+                       len = r->data.DNSNULL->length;
+                       for (i = 0; i < len; i++)
+                       {
+                               x = r->data.DNSNULL->data[i];
+                               if (isascii(x)) fprintf(f, "%c", x);
+                               else fprintf(f, " ");
+                       }
+                       fprintf(f, ")\n");
+
+       }
+
+       fprintf(f, "\n");
+
+       if (lockit != 0) _dns_print_unlock();
+}
+
+void
+dns_print_resource_record(const dns_resource_record_t *r, FILE *f)
+{
+       _dns_print_resource_record_lock(r, f, 1);
+}
+
+void
+dns_print_reply(const dns_reply_t *r, FILE *f, uint16_t mask)
+{
+       uint16_t i;
+       dns_header_t *h;
+       char scratch[1024];
+       uint32_t offset, iface;
+
+       _dns_print_lock();
+
+       if (r == NULL)
+       {
+               fprintf(f, "-nil-\n");
+               _dns_print_unlock();
+               return;
+       }
+
+       if (r->status != DNS_STATUS_OK)
+       {
+               if (r->status == DNS_STATUS_TIMEOUT)
+                       fprintf(f, "Timeout\n");
+               else if (r->status == DNS_STATUS_SEND_FAILED)
+                       fprintf(f, "Send failed\n");
+               else if (r->status == DNS_STATUS_RECEIVE_FAILED)
+                       fprintf(f, "Receive failed\n");
+               else fprintf(f, "status %u\n", r->status);
+
+               _dns_print_unlock();
+               return;
+       }
+
+       h = r->header;
+
+       if (mask & DNS_PRINT_XID)
+       {
+               fprintf(f, "Xid: %u\n", h->xid);
+       }
+
+       if (mask & DNS_PRINT_QR)
+       {
+               if ((h->flags & DNS_FLAGS_QR_MASK) == DNS_FLAGS_QR_QUERY)
+                       fprintf(f, "QR: Query\n");
+               else
+                       fprintf(f, "QR: Reply\n");
+       }
+
+       if (mask & DNS_PRINT_SERVER)
+       {
+               if (r->server == NULL)
+               {
+                       fprintf(f, "Server: -nil-\n");
+               }
+               else
+               {
+                       offset = INET_NTOP_AF_INET_OFFSET;
+                       if (r->server->sa_family == AF_INET6) offset = INET_NTOP_AF_INET6_OFFSET;
+
+                       fprintf(f, "Server: %s", inet_ntop(r->server->sa_family, (char *)(r->server) + offset, scratch, 1024));
+                       if (r->server->sa_family == AF_INET)
+                       {
+                               memcpy(&iface, (((struct sockaddr_in *)(r->server))->sin_zero), 4);
+                               if (iface > 0) fprintf(f, "%%%s", if_indextoname(iface, scratch));
+                       }
+                       else if (r->server->sa_family == AF_INET6)
+                       {
+                               iface = ((struct sockaddr_in6 *)(r->server))->sin6_scope_id;
+                               if (iface > 0) fprintf(f, "%%%s", if_indextoname(iface, scratch));
+                       }
+                       fprintf(f, "\n");
+               }
+       }
+
+       if (mask & DNS_PRINT_OPCODE)
+       {
+               fprintf(f, "Opcode: ");
+               switch (h->flags & DNS_FLAGS_OPCODE_MASK)
+               {
+                       case ns_o_query:  fprintf(f, "Standard\n"); break;
+                       case ns_o_iquery: fprintf(f, "Inverse\n"); break;
+                       case ns_o_status: fprintf(f, "Status\n"); break;
+                       case ns_o_notify: fprintf(f, "Notify\n"); break;
+                       case ns_o_update: fprintf(f, "Update\n"); break;
+                       default:
+                               fprintf(f, "Reserved (%hu)\n",
+                                       (h->flags & DNS_FLAGS_OPCODE_MASK) >> 11);
+               }
+       }
+
+       if (mask & DNS_PRINT_AA)
+       {
+               if (h->flags & DNS_FLAGS_AA) fprintf(f, "AA: Authoritative\n");
+               else fprintf(f, "AA: Non-Authoritative\n");
+       }
+
+       if (mask & DNS_PRINT_TC)
+       {
+               if (h->flags & DNS_FLAGS_TC) fprintf(f, "TC: Truncated\n");
+               else fprintf(f, "TC: Non-Truncated\n");
+       }
+
+       if (mask & DNS_PRINT_RD)
+       {
+               if (h->flags & DNS_FLAGS_RD) fprintf(f, "RD: Recursion desired\n");
+               else fprintf(f, "RD: No recursion desired\n");
+       }
+
+       if (mask & DNS_PRINT_RA)
+       {
+               if (h->flags & DNS_FLAGS_RA) fprintf(f, "RA: Recursion available\n");
+               else fprintf(f, "RA: No recursion available \n");
+       }
+
+       if (mask & DNS_PRINT_RCODE)
+       {
+               fprintf(f, "Rcode: ");
+               switch (h->flags & DNS_FLAGS_RCODE_MASK)
+               {
+                       case ns_r_noerror:
+                               fprintf(f, "No error\n");
+                               break;
+                       case ns_r_formerr:
+                               fprintf(f, "Format error \n");
+                               break;
+                       case ns_r_servfail:
+                               fprintf(f, "Server failure\n");
+                               break;
+                       case ns_r_nxdomain:
+                               fprintf(f, "Name error \n");
+                               break;
+                       case ns_r_notimpl:
+                               fprintf(f, "Not implemented\n");
+                               break;
+                       case ns_r_refused:
+                               fprintf(f, "Refused\n");
+                               break;
+                       case ns_r_yxdomain:
+                               fprintf(f, "Name exists\n");
+                               break;
+                       case ns_r_yxrrset:
+                               fprintf(f, "RR Set exists\n");
+                               break;
+                       case ns_r_nxrrset:
+                               fprintf(f, "RR Set does not exist\n");
+                               break;
+                       case ns_r_notauth:
+                               fprintf(f, "Not authoritative\n");
+                               break;
+                       case ns_r_notzone:
+                               fprintf(f, "Record zone does not match section zone\n");
+                               break;
+                       case ns_r_badvers:
+                               fprintf(f, "Invalid EDNS version or TSIG signature\n");
+                               break;
+                       case ns_r_badkey:
+                               fprintf(f, "Invalid key\n");
+                               break;
+                       case ns_r_badtime:
+                               fprintf(f, "Invalid time\n");
+                               break;
+                       default:
+                               fprintf(f, "Reserved (%hu)\n",h->flags & DNS_FLAGS_RCODE_MASK);
+               }
+       }
+
+       if (mask & DNS_PRINT_QUESTION)
+       {
+               fprintf(f, "Question (%hu):\n", h->qdcount);
+               for (i = 0; i < h->qdcount; i++)
+                       _dns_print_question_lock(r->question[i], f, 0);
+       }
+
+       if (mask & DNS_PRINT_ANSWER)
+       {
+               fprintf(f, "Answer (%hu):\n", h->ancount);
+               for (i = 0; i < h->ancount; i++)
+                       _dns_print_resource_record_lock(r->answer[i], f, 0);
+       }
+
+       if (mask & DNS_PRINT_AUTHORITY)
+       {
+               fprintf(f, "Authority (%hu):\n", h->nscount);
+               for (i = 0; i < h->nscount; i++)
+                       _dns_print_resource_record_lock(r->authority[i], f, 0);
+       }
+
+       if (mask & DNS_PRINT_ADDITIONAL)
+       {
+               fprintf(f, "Additional records (%hu):\n", h->arcount);
+               for (i = 0; i < h->arcount; i++)
+                       _dns_print_resource_record_lock(r->additional[i], f, 0);
+       }
+
+       _dns_print_unlock();
+}
+
+static void
+_pdns_print_handle(pdns_handle_t *pdns, FILE *f)
+{
+       uint32_t i, offset;
+       struct in_addr a;
+       char scratch[1024];
+       struct sockaddr *sa;
+       res_state r;
+
+       if (pdns == NULL)
+       {
+               fprintf(f, "-nil-\n");
+               return;
+       }
+
+       if (pdns->name == NULL) fprintf(f, "Name: -nil-\n");
+       else fprintf(f, "Name: %s\n", pdns->name);
+
+       fprintf(f, "Flags:");
+       if (pdns->flags == 0) fprintf(f, " None\n");
+       else 
+       {
+               if (pdns->flags & DNS_FLAG_DEBUG) fprintf(f, " Debug");
+               if (pdns->flags & DNS_FLAG_CHECK_RESOLVER_DIR) fprintf(f, " DirCheck");
+               if (pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) fprintf(f, " IPv6");
+               if (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) fprintf(f, " SkipAAAA");
+               if (pdns->flags & DNS_FLAG_DEFAULT_RESOLVER) fprintf(f, " Default");
+               fprintf(f, "\n");
+       }
+
+       r = pdns->res;
+       if (r == NULL) return;
+
+       if (r->defdname[0] != '\0') fprintf(f, "Domain: %s\n", r->defdname);
+       fprintf(f, "Search Order: %d\n", pdns->search_order);
+       fprintf(f, "Total Timeout: %d\n", pdns->total_timeout);
+       fprintf(f, "Retry Timeout: %d\n", pdns->res->retrans);
+       fprintf(f, "Retry Attempts: %d\n", pdns->res->retry);
+
+       fprintf(f, "Server%s:\n", (r->nscount == 1) ? "" : "s");
+       for (i = 0; i < r->nscount; i++)
+       {
+               sa = get_nsaddr(r, i);
+               offset = INET_NTOP_AF_INET_OFFSET;
+               if (sa->sa_family == AF_INET6) offset = INET_NTOP_AF_INET6_OFFSET;
+               fprintf(f, "  %u: %s", i, inet_ntop(sa->sa_family, (char *)sa + offset, scratch, 1024));
+               fprintf(f, "\n");
+       }
+
+       if (pdns->search_count > 0)
+       {
+               fprintf(f, "Search List:\n");
+               for (i = 0; i < pdns->search_count; i++)
+                       fprintf(f, "  %u: %s\n", i, pdns->search_list[i]);
+       }
+
+       if (r->sort_list[0].addr.s_addr != 0)
+       {
+               fprintf(f, "Sortlist:\n");
+               for (i = 0; (r->sort_list[i].addr.s_addr != 0); i++)
+               {
+                       fprintf(f, "  %u: ", i);
+                       a.s_addr = r->sort_list[i].addr.s_addr;
+                       fprintf(f, "%s/", inet_ntoa(a));
+                       a.s_addr = r->sort_list[i].mask;
+                       fprintf(f, "%s\n", inet_ntoa(a));
+               }
+       }
+}
+
+static void
+_sdns_print_handle(sdns_handle_t *sdns, FILE *f)
+{
+       int i;
+
+       if (sdns == NULL)
+       {
+               fprintf(f, "-nil-\n");
+               return;
+       }
+
+       for (i = 0; i < sdns->client_count; i++)
+       {
+               fprintf(f, "DNS client %d\n", i);
+               _pdns_print_handle(sdns->client[i], f);
+               fprintf(f, "\n");
+       }
+
+       fprintf(f, "resolver dir mod time = %u\n", sdns->modtime);
+       fprintf(f, "resolver dir stat time = %u\n", sdns->stattime);
+       fprintf(f, "resolver dir stat latency = %u\n", sdns->stat_latency);
+}
+
+void
+dns_print_handle(dns_handle_t d, FILE *f)
+{
+       dns_private_handle_t *dns;
+
+       _dns_print_lock();
+
+       if (d == NULL)
+       {
+               fprintf(f, "-nil-\n");
+               _dns_print_unlock();
+               return;
+       }
+
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER)
+       {
+               _sdns_print_handle(dns->sdns, f);
+       }
+       else
+       {
+               _pdns_print_handle(dns->pdns, f);
+       }
+
+       _dns_print_unlock();
+}
+
+void
+dns_all_server_addrs(dns_handle_t d, struct sockaddr ***addrs, uint32_t *count)
+{
+       int i, j, k, n, found;
+       dns_private_handle_t *dns;
+       pdns_handle_t *pdns;
+       struct sockaddr *sa;
+       struct sockaddr_storage **l;
+       res_state r;
+
+       *addrs = NULL;
+       *count = 0;
+       l = NULL;
+       n = 0;
+
+       if (d == NULL) return;
+
+       dns = (dns_private_handle_t *)d;
+
+       if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_SUPER) return;
+
+       if (dns->sdns == NULL) return;
+
+       /* Just to initialize / validate clients */
+       i = dns_search_list_count(d);
+
+       for (i = 0; i < dns->sdns->client_count; i++)
+       {
+               pdns = dns->sdns->client[i];
+               if (pdns == NULL) continue;
+
+               r = pdns->res;
+               if (r == NULL) continue;
+
+               for (j = 0; j < r->nscount; j++)
+               {
+                       sa = get_nsaddr(r, j);
+                       found = 0;
+                       for (k = 0; (found == 0) && (k < n); k++)
+                       {
+                               if (memcmp(l[k], sa, sa->sa_len) == 0) found = 1;
+                       }
+                       if (found == 1) continue;
+
+                       if (n == 0)
+                       {
+                               l = (struct sockaddr_storage **)calloc(1, sizeof(struct sockaddr_storage *));
+                       }
+                       else
+                       {
+                               l = (struct sockaddr_storage **)reallocf(l, (n + 1) * sizeof(struct sockaddr_storage *));
+                       }
+
+                       if (l == NULL) return;
+
+                       l[n] = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage));
+                       if (l[n] == NULL) return;
+
+                       memset(l[n], 0, sizeof(struct sockaddr_storage));
+                       memcpy(l[n], sa, sa->sa_len);
+                       n++;
+               }
+       }
+
+       *addrs = (struct sockaddr **)l;
+       *count = n;
+}
+
+int
+dns_res_once(struct sockaddr *server, struct timeval *timeout, int options, const char *name, int class, int type, u_char *res, int *reslen)
+{
+       res_state statp;
+       int n, fromlen, status;
+       struct sockaddr_storage from;
+       u_char buf[MAXPACKET];
+
+       if (server == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT;
+       if (name == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT;
+       if (res == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT;
+       if (reslen == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT;
+
+       fromlen = sizeof(struct sockaddr_storage);
+
+       statp = res_state_new();
+       statp->retry = 1;
+       statp->options = options;
+       statp->id = res_randomid();
+       if (timeout == NULL) statp->retrans = 5;
+       else statp->retrans = timeout->tv_sec;
+
+       statp->ndots = 1;
+       statp->_vcsock = -1;
+       statp->nscount = 1;
+
+       strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa");
+       strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int");
+       strcpy(statp->_u._ext.ext->bsuffix, "ip6.arpa");
+
+       if (server->sa_family == AF_INET6)
+       {
+               memcpy(&(statp->_u._ext.ext->nsaddrs[0]), server, sizeof(struct sockaddr_in6));
+               statp->nsaddr_list[0].sin_family = 0;
+       }
+       else
+       {
+               memcpy(&(statp->_u._ext.ext->nsaddrs[0]), server, sizeof(struct sockaddr_in));
+               memcpy(&(statp->nsaddr_list[0]), server, sizeof(struct sockaddr_in));
+       }
+
+       n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf));
+
+       status = dns_res_send(statp, buf, n, res, reslen, (struct sockaddr *)&from, &fromlen);
+
+       res_client_close(statp);
+
+       return status;
+}
diff --git a/dns_util.h b/dns_util.h
new file mode 100644 (file)
index 0000000..a291584
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * "Portions Copyright (c) 2003 Apple Computer, Inc.  All Rights
+ * Reserved.  This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License').  You may not use this file
+ * except in compliance with the License.  Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __DNS_UTIL_H__
+#define __DNS_UTIL_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dns.h>
+
+/*
+ * Status returned in a dns_reply_t
+ */
+#define DNS_STATUS_OK                                  0
+#define DNS_STATUS_BAD_HANDLE                  1
+#define DNS_STATUS_MALFORMED_QUERY             2
+#define DNS_STATUS_TIMEOUT                             3
+#define DNS_STATUS_SEND_FAILED                 4
+#define DNS_STATUS_RECEIVE_FAILED              5
+#define DNS_STATUS_CONNECTION_FAILED   6
+#define DNS_STATUS_WRONG_SERVER                        7
+#define DNS_STATUS_WRONG_XID                   8
+#define DNS_STATUS_WRONG_QUESTION              9
+
+/*
+ * dns_print_reply mask
+ */
+#define DNS_PRINT_XID        0x0001
+#define DNS_PRINT_QR         0x0002
+#define DNS_PRINT_OPCODE     0x0004
+#define DNS_PRINT_AA         0x0008
+#define DNS_PRINT_TC         0x0010
+#define DNS_PRINT_RD         0x0020
+#define DNS_PRINT_RA         0x0040
+#define DNS_PRINT_PR         0x0080
+#define DNS_PRINT_RCODE      0x0100
+#define DNS_PRINT_QUESTION   0x0200
+#define DNS_PRINT_ANSWER     0x0400
+#define DNS_PRINT_AUTHORITY  0x0800
+#define DNS_PRINT_ADDITIONAL 0x1000
+#define DNS_PRINT_SERVER     0x2000
+
+/*
+ * DNS query / reply header
+ */
+typedef struct {
+       uint16_t xid;
+       uint16_t flags;
+       uint16_t qdcount;
+       uint16_t ancount;
+       uint16_t nscount;
+       uint16_t arcount;
+} dns_header_t;
+
+/*
+ * DNS query
+ */
+typedef struct
+{
+       char *name;
+       uint16_t dnstype;
+       uint16_t dnsclass;
+} dns_question_t;
+
+/*
+ * Resource Record types
+ * dns_parse_packet() creates resourse records of these types.
+ */
+typedef struct
+{
+       uint16_t length;
+       char *data;
+} dns_raw_resource_record_t;
+
+typedef struct
+{
+       struct in_addr addr;
+} dns_address_record_t;
+
+typedef struct
+{
+       struct in6_addr addr;
+} dns_in6_address_record_t;
+
+typedef struct
+{
+       char *name;
+} dns_domain_name_record_t;
+
+typedef struct
+{
+       char *mname;
+       char *rname;
+       uint32_t serial;
+       uint32_t refresh;
+       uint32_t retry;
+       uint32_t expire;
+       uint32_t minimum;
+} dns_SOA_record_t;
+
+typedef struct
+{
+       char *cpu;
+       char *os;
+} dns_HINFO_record_t;
+
+typedef struct
+{
+       char *rmailbx;
+       char *emailbx;
+} dns_MINFO_record_t;
+
+typedef struct
+{
+       uint16_t preference;
+       char *name;
+} dns_MX_record_t;
+
+typedef struct
+{
+       uint32_t string_count;
+       char **strings;
+} dns_TXT_record_t;
+
+typedef struct
+{
+       struct in_addr addr;
+       uint8_t protocol;
+       uint32_t maplength;
+       uint8_t *map;
+} dns_WKS_record_t;
+
+typedef struct
+{
+       char *mailbox;
+       char *txtdname;
+} dns_RP_record_t;
+
+typedef struct
+{
+       uint32_t subtype;
+       char *hostname;
+} dns_AFSDB_record_t;
+
+typedef struct
+{
+       char *psdn_address;
+} dns_X25_record_t;
+
+typedef struct
+{
+       char *isdn_address;
+       char *subaddress;
+} dns_ISDN_record_t;
+
+typedef struct
+{
+       uint16_t preference;
+       char * intermediate;
+} dns_RT_record_t;
+
+typedef struct
+{
+       uint8_t version;
+       uint8_t size;
+       uint8_t horizontal_precision;
+       uint8_t vertical_precision;
+       uint32_t latitude;
+       uint32_t longitude;
+       uint32_t altitude;
+} dns_LOC_record_t;
+
+typedef struct
+{
+       uint16_t priority;
+       uint16_t weight;
+       uint16_t port;
+       char *target;
+} dns_SRV_record_t;
+
+/*
+ * DNS Resource Record
+ *
+ * Data contained in unsupported or obsolete Resource Record types
+ * may be accessed via DNSNULL as a dns_raw_resource_record_t. 
+ */
+typedef struct
+{
+       char *name;
+       uint16_t dnstype;
+       uint16_t dnsclass;
+       uint32_t ttl;
+       union
+       {
+               dns_address_record_t *A;
+               dns_domain_name_record_t *NS;
+               dns_domain_name_record_t *MD;           /* Obsolete */
+               dns_domain_name_record_t *MF;           /* Obsolete */
+               dns_domain_name_record_t *CNAME;
+               dns_SOA_record_t *SOA;
+               dns_domain_name_record_t *MB;
+               dns_domain_name_record_t *MG;
+               dns_domain_name_record_t *MR;
+               dns_raw_resource_record_t *DNSNULL;
+               dns_WKS_record_t *WKS;
+               dns_domain_name_record_t *PTR;
+               dns_HINFO_record_t *HINFO;
+               dns_MINFO_record_t *MINFO;
+               dns_MX_record_t *MX;
+               dns_TXT_record_t *TXT;
+               dns_RP_record_t *RP;
+               dns_AFSDB_record_t *AFSDB;
+               dns_X25_record_t *X25;
+               dns_ISDN_record_t *ISDN;
+               dns_RT_record_t *RT;
+               dns_in6_address_record_t *AAAA;
+               dns_LOC_record_t *LOC;
+               dns_SRV_record_t *SRV;
+       } data;
+} dns_resource_record_t;
+
+/*
+ * A parsed DNS record.  Returned by dns_parse_packet() and dns_lookup().
+ * The contents may be printed using dns_print_reply().
+ */
+typedef struct
+{
+       uint32_t status;
+       struct sockaddr *server;
+       dns_header_t *header;
+       dns_question_t **question;
+       dns_resource_record_t **answer;
+       dns_resource_record_t **authority;
+       dns_resource_record_t **additional;
+} dns_reply_t;
+
+
+__BEGIN_DECLS
+
+/*
+ * High-level lookup performs a search (using dns_search), parses the 
+ * reply and returns a dns_reply_t structure.
+ *
+ * The DNS handle contains an internal buffer used for fetching replies.
+ * The buffer is reused for each query, and is released with the DNS client
+ * handle when dns_free() is called.  The default buffer size is 1024 bytes.
+ * The size may be changed with dns_set_buffer_size.
+ *
+ * Note that in a multithreaded application, each thread using this API must
+ * open a separate handle.
+ */
+extern dns_reply_t *dns_lookup(dns_handle_t dns, const char *name, uint32_t dnsclass, uint32_t dnstype);
+
+/*
+ * Get / Set the size of the internal receive buffer used by dns_lookup()
+ */
+extern uint32_t dns_get_buffer_size(dns_handle_t d);
+extern void dns_set_buffer_size(dns_handle_t d, uint32_t len);
+
+/*
+ * Parse a reply packet into a reply structure.
+ */
+extern dns_reply_t *dns_parse_packet(const char *buf, uint32_t len);
+
+/*
+ * Free a reply structure.
+ */
+extern void dns_free_reply(dns_reply_t *r);
+
+/*
+ * Parse a query packet into a question structure.
+ */
+extern dns_question_t *dns_parse_question(const char *buf, uint32_t len);
+
+/*
+ * Free a question structure.
+ */
+extern void dns_free_question(dns_question_t *q);
+
+/*
+ * Parse a resource record into a structure.
+ */
+extern dns_resource_record_t *dns_parse_resource_record(const char *buf, uint32_t len);
+
+/*
+ * Free a resource record structure.
+ */
+extern void dns_free_resource_record(dns_resource_record_t *rr);
+
+/*
+ * String / number representation of a DNS class
+ * dns_class_number returns 0 if the string is recognized,
+ * non-zero if the class string is unknown.
+ */
+extern const char *dns_class_string(uint16_t dnsclass);
+extern int32_t dns_class_number(const char *c, uint16_t *n);
+
+/*
+ * String / number representation of a DNS type
+ * dns_type_number returns 0 if the string is recognized,
+ * non-zero if the class string is unknown.
+ */
+extern const char *dns_type_string(uint16_t dnstype);
+extern int32_t dns_type_number(const char *t, uint16_t *n);
+
+/*
+ * Print a dns handle.
+ */
+extern void dns_print_handle(dns_handle_t d, FILE *f);
+
+/*
+ * Print the contents of a question structure.
+ */
+extern void dns_print_question(const dns_question_t *q, FILE *f);
+
+/*
+ * Print the contents of a resource record structure.
+ */
+extern void dns_print_resource_record(const dns_resource_record_t *r, FILE *f);
+
+/*
+ * Print the contents of a reply structure.
+ */
+extern void dns_print_reply(const dns_reply_t *r, FILE *f, uint16_t mask);
+
+__END_DECLS
+
+#endif /* __DNS_UTIL_H__ */
diff --git a/dst.h b/dst.h
new file mode 100644 (file)
index 0000000..791bd82
--- /dev/null
+++ b/dst.h
@@ -0,0 +1,156 @@
+#ifndef DST_H
+#define DST_H
+
+#ifndef HAS_DST_KEY
+#define DST_KEY RES_9_DST_KEY
+typedef struct dst_key {
+       char    *dk_key_name;   /* name of the key */
+       int     dk_key_size;    /* this is the size of the key in bits */
+       int     dk_proto;       /* what protocols this key can be used for */
+       int     dk_alg;         /* algorithm number from key record */
+       u_int32_t dk_flags;     /* and the flags of the public key */
+       u_int16_t dk_id;        /* identifier of the key */
+} DST_KEY;
+#endif /* HAS_DST_KEY */
+
+/* 
+ * DST Crypto API defintions 
+ */
+#define dst_init res_9_dst_init
+void     dst_init(void);
+#ifndef __APPLE__
+int      dst_check_algorithm(const int);
+#endif
+
+#define dst_sign_data res_9_dst_sign_data
+int dst_sign_data(const int mode,       /* specifies INIT/UPDATE/FINAL/ALL */
+                 DST_KEY *in_key,       /* the key to use */
+                 void **context,        /* pointer to state structure */
+                 const u_char *data,    /* data to be signed */
+                 const int len,         /* length of input data */
+                 u_char *signature,     /* buffer to write signature to */
+                 const int sig_len);    /* size of output buffer */
+
+#define dst_verify_data res_9_dst_verify_data
+int dst_verify_data(const int mode,     /* specifies INIT/UPDATE/FINAL/ALL */
+                   DST_KEY *in_key,     /* the key to use */
+                   void **context,      /* pointer to state structure */
+                   const u_char *data,  /* data to be verified */
+                   const int len,       /* length of input data */
+                   const u_char *signature,/* buffer containing signature */
+                   const int sig_len);  /* length of signature */
+
+#define dst_read_key res_9_dst_read_key
+DST_KEY *dst_read_key(const char *in_name,   /* name of key */
+                     const u_int16_t in_id, /* key tag identifier */
+                     const int in_alg,      /* key algorithm */
+                     const int key_type);   /* Private/PublicKey wanted*/
+
+#define dst_write_key res_9_dst_write_key
+int      dst_write_key(const DST_KEY *key,  /* key to write out */
+                      const int key_type); /* Public/Private */
+
+#define dst_dnskey_to_key res_9_dst_dnskey_to_key
+DST_KEY *dst_dnskey_to_key(const char *in_name,        /* KEY record name */
+                          const u_char *key,   /* KEY RDATA */
+                          const int len);      /* size of input buffer*/
+
+#ifndef __APPLE__
+int      dst_key_to_dnskey(const DST_KEY *key, /* key to translate */
+                          u_char *out_storage, /* output buffer */
+                          const int out_len);  /* size of out_storage*/
+#endif
+
+#define dst_buffer_to_key res_9_dst_buffer_to_key
+DST_KEY *dst_buffer_to_key(const char *key_name,  /* name of the key */
+                          const int alg,         /* algorithm */
+                          const int flags,       /* dns flags */
+                          const int protocol,    /* dns protocol */
+                          const u_char *key_buf, /* key in dns wire fmt */
+                          const int key_len);    /* size of key */
+
+
+#define dst_key_to_buffer res_9_dst_key_to_buffer
+int     dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len);
+
+#define dst_generate_key res_9_dst_generate_key
+DST_KEY *dst_generate_key(const char *name,    /* name of new key */
+                         const int alg,       /* key algorithm to generate */
+                         const int bits,      /* size of new key */
+                         const int exp,       /* alg dependent parameter*/
+                         const int flags,     /* key DNS flags */
+                         const int protocol); /* key DNS protocol */
+
+#define dst_free_key res_9_dst_free_key
+DST_KEY *dst_free_key(DST_KEY *f_key);
+
+#define dst_compare_keys res_9_dst_compare_keys
+int      dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2);
+
+#define dst_sig_size res_9_dst_sig_size
+int    dst_sig_size(DST_KEY *key);
+
+/* support for dns key tags/ids */
+#define dst_s_dns_key_id res_9_dst_s_dns_key_id
+u_int16_t dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len);
+#ifndef __APPLE__
+u_int16_t dst_s_id_calc(const u_char *key_data, const int key_len);
+#endif
+
+/* Used by callers as well as by the library.  */
+#define RAW_KEY_SIZE    8192        /* large enough to store any key */
+
+/* DST_API control flags */
+/* These are used used in functions dst_sign_data and dst_verify_data */
+#define SIG_MODE_INIT          1  /* initialize digest */
+#define SIG_MODE_UPDATE                2  /* add data to digest */
+#define SIG_MODE_FINAL         4  /* generate/verify signature */
+#define SIG_MODE_ALL           (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL)
+
+/* Flags for dst_read_private_key()  */
+#define DST_FORCE_READ         0x1000000
+#define DST_CAN_SIGN           0x010F
+#define DST_NO_AUTHEN          0x8000
+#define DST_EXTEND_FLAG         0x1000
+#define DST_STANDARD           0
+#define DST_PRIVATE             0x2000000
+#define DST_PUBLIC              0x4000000
+#define DST_RAND_SEMI           1
+#define DST_RAND_STD            2
+#define DST_RAND_KEY            3
+#define DST_RAND_DSS            4
+
+
+/* DST algorithm codes */
+#define KEY_RSA                        1
+#define KEY_DH                 2
+#define KEY_DSA                        3
+#define KEY_PRIVATE            254
+#define KEY_EXPAND             255
+#define KEY_HMAC_MD5           157
+#define KEY_HMAC_SHA1          158
+#define UNKNOWN_KEYALG         0
+#define DST_MAX_ALGS            KEY_HMAC_SHA1
+
+/* DST constants to locations in KEY record  changes in new KEY record */
+#define DST_FLAGS_SIZE         2
+#define DST_KEY_PROT           2
+#define DST_KEY_ALG            3
+#define DST_EXT_FLAG            4
+#define DST_KEY_START          4
+
+#ifndef SIGN_F_NOKEY 
+#define SIGN_F_NOKEY           0xC000
+#endif
+
+/* error codes from dst routines */
+#define SIGN_INIT_FAILURE      (-23)
+#define SIGN_UPDATE_FAILURE    (-24)
+#define SIGN_FINAL_FAILURE     (-25)
+#define VERIFY_INIT_FAILURE    (-26)
+#define VERIFY_UPDATE_FAILURE  (-27)
+#define VERIFY_FINAL_FAILURE   (-28)
+#define MISSING_KEY_OR_SIGNATURE (-30)
+#define UNSUPPORTED_KEYALG     (-31)
+
+#endif /* DST_H */
diff --git a/dst_api.c b/dst_api.c
new file mode 100644 (file)
index 0000000..2ff3faf
--- /dev/null
+++ b/dst_api.c
@@ -0,0 +1,1076 @@
+#ifndef __APPLE__
+#ifndef LINT
+static const char rcsid[] = "$Header: /Users/Shared/libresolv_2/libresolv/dst_api.c,v 1.1 2006/03/01 19:01:36 majka Exp $";
+#endif
+#endif
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+/*
+ * This file contains the interface between the DST API and the crypto API.
+ * This is the only file that needs to be changed if the crypto system is
+ * changed.  Exported functions are:
+ * void dst_init()      Initialize the toolkit
+ * int  dst_check_algorithm()   Function to determines if alg is suppored.
+ * int  dst_compare_keys()      Function to compare two keys for equality.
+ * int  dst_sign_data()         Incremental signing routine.
+ * int  dst_verify_data()       Incremental verify routine.
+ * int  dst_generate_key()      Function to generate new KEY
+ * DST_KEY *dst_read_key()      Function to retrieve private/public KEY.
+ * void dst_write_key()         Function to write out a key.
+ * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST
+ *                             KEY structure.
+ * int dst_key_to_dnskey()     Function to return a public key in DNS 
+ *                             format binary
+ * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY
+ * int *dst_key_to_buffer()    Writes out DST_KEY key matterial in buffer
+ * void dst_free_key()         Releases all memory referenced by key structure
+ */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "dst_internal.h"
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* static variables */
+static int done_init = 0;
+dst_func *dst_t_func[DST_MAX_ALGS];
+const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n";
+const char *dst_path = "";
+
+/* internal I/O functions */
+#ifdef _UNUSED_API_
+static DST_KEY *dst_s_read_public_key(const char *in_name, 
+                                     const u_int16_t in_id, int in_alg);
+static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key,
+                                      u_int16_t in_id, int in_alg);
+static int dst_s_write_public_key(const DST_KEY *key);
+static int dst_s_write_private_key(const DST_KEY *key);
+#endif
+
+/* internal function to set up data structure */
+static DST_KEY *dst_s_get_key_struct(const char *name, const int alg,
+                                    const int flags, const int protocol,
+                                    const int bits);
+
+/*
+ *  dst_init
+ *     This function initializes the Digital Signature Toolkit.
+ *     Right now, it just checks the DSTKEYPATH environment variable.
+ *  Parameters
+ *     none
+ *  Returns
+ *     none
+ */
+void
+dst_init()
+{
+       char *s;
+       int len;
+
+       if (done_init != 0)
+               return;
+       done_init = 1;
+
+       s = getenv("DSTKEYPATH");
+       len = 0;
+       if (s) {
+               struct stat statbuf;
+
+               len = strlen(s);
+               if (len > PATH_MAX) {
+                       EREPORT(("%s is longer than %d characters, ignoring\n",
+                                s, PATH_MAX));
+               } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
+                       EREPORT(("%s is not a valid directory\n", s));
+               } else {
+                       char *tmp;
+                       tmp = (char *) malloc(len + 2);
+                       memcpy(tmp, s, len + 1);
+                       if (tmp[strlen(tmp) - 1] != '/') {
+                               tmp[strlen(tmp) + 1] = 0;
+                               tmp[strlen(tmp)] = '/';
+                       }
+                       dst_path = tmp;
+               }
+       }
+       memset(dst_t_func, 0, sizeof(dst_t_func));
+       /* first one is selected */
+       dst_hmac_md5_init();
+}
+
+/*
+ *  dst_check_algorithm
+ *     This function determines if the crypto system for the specified
+ *     algorithm is present.
+ *  Parameters
+ *     alg     1       KEY_RSA
+ *             3       KEY_DSA
+ *           157     KEY_HMAC_MD5
+ *                   future algorithms TBD and registered with IANA.
+ *  Returns
+ *     1 - The algorithm is available.
+ *     0 - The algorithm is not available.
+ */
+#ifdef __APPLE__
+static
+#endif
+int
+dst_check_algorithm(const int alg)
+{
+       return (dst_t_func[alg] != NULL);
+}
+
+/* 
+ * dst_s_get_key_struct 
+ *     This function allocates key structure and fills in some of the 
+ *     fields of the structure. 
+ * Parameters: 
+ *     name:     the name of the key 
+ *     alg:      the algorithm number 
+ *     flags:    the dns flags of the key
+ *     protocol: the dns protocol of the key
+ *     bits:     the size of the key
+ * Returns:
+ *       NULL if error
+ *       valid pointer otherwise
+ */
+static DST_KEY *
+dst_s_get_key_struct(const char *name, const int alg, const int flags,
+                    const int protocol, const int bits)
+{
+       DST_KEY *new_key = NULL; 
+
+       if (dst_check_algorithm(alg)) /* make sure alg is available */
+               new_key = (DST_KEY *) malloc(sizeof(*new_key));
+       if (new_key == NULL)
+               return (NULL);
+
+       memset(new_key, 0, sizeof(*new_key));
+       new_key->dk_key_name = strdup(name);
+       new_key->dk_alg = alg;
+       new_key->dk_flags = flags;
+       new_key->dk_proto = protocol;
+       new_key->dk_KEY_struct = NULL;
+       new_key->dk_key_size = bits;
+       new_key->dk_func = dst_t_func[alg];
+       return (new_key);
+}
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_compare_keys
+ *     Compares two keys for equality.
+ *  Parameters
+ *     key1, key2      Two keys to be compared.
+ *  Returns
+ *     0              The keys are equal.
+ *     non-zero        The keys are not equal.
+ */
+
+int
+dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
+{
+       if (key1 == key2)
+               return (0);
+       if (key1 == NULL || key2 == NULL)
+               return (4);
+       if (key1->dk_alg != key2->dk_alg)
+               return (1);
+       if (key1->dk_key_size != key2->dk_key_size)
+               return (2);
+       if (key1->dk_id != key2->dk_id)
+               return (3);
+       return (key1->dk_func->compare(key1, key2));
+}
+#endif
+
+/*
+ * dst_sign_data
+ *     An incremental signing function.  Data is signed in steps.
+ *     First the context must be initialized (SIG_MODE_INIT).
+ *     Then data is hashed (SIG_MODE_UPDATE).  Finally the signature
+ *     itself is created (SIG_MODE_FINAL).  This function can be called
+ *     once with INIT, UPDATE and FINAL modes all set, or it can be
+ *     called separately with a different mode set for each step.  The
+ *     UPDATE step can be repeated.
+ * Parameters
+ *     mode    A bit mask used to specify operation(s) to be performed.
+ *               SIG_MODE_INIT    1   Initialize digest
+ *               SIG_MODE_UPDATE        2   Add data to digest
+ *               SIG_MODE_FINAL          4   Generate signature
+ *                                           from signature
+ *               SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL
+ *     data    Data to be signed.
+ *     len     The length in bytes of data to be signed.
+ *     in_key  Contains a private key to sign with.
+ *               KEY structures should be handled (created, converted,
+ *               compared, stored, freed) by the DST.
+ *     signature
+ *           The location to which the signature will be written.
+ *     sig_len Length of the signature field in bytes.
+ * Return
+ *      0      Successfull INIT or Update operation
+ *     >0      success FINAL (sign) operation
+ *     <0      failure
+ */
+
+int
+dst_sign_data(const int mode, DST_KEY *in_key, void **context, 
+             const u_char *data, const int len,
+             u_char *signature, const int sig_len)
+{
+       DUMP(data, mode, len, "dst_sign_data()");
+
+       if (mode & SIG_MODE_FINAL &&
+           (in_key->dk_KEY_struct == NULL || signature == NULL))
+               return (MISSING_KEY_OR_SIGNATURE);
+
+       if (in_key->dk_func && in_key->dk_func->sign)
+               return (in_key->dk_func->sign(mode, in_key, context, data, len,
+                                             signature, sig_len));
+       return (UNKNOWN_KEYALG);
+}
+
+
+/*
+ *  dst_verify_data
+ *     An incremental verify function.  Data is verified in steps.
+ *     First the context must be initialized (SIG_MODE_INIT).
+ *     Then data is hashed (SIG_MODE_UPDATE).  Finally the signature
+ *     is verified (SIG_MODE_FINAL).  This function can be called
+ *     once with INIT, UPDATE and FINAL modes all set, or it can be
+ *     called separately with a different mode set for each step.  The
+ *     UPDATE step can be repeated.
+ *  Parameters
+ *     mode    Operations to perform this time.
+ *                   SIG_MODE_INIT       1   Initialize digest
+ *                   SIG_MODE_UPDATE     2   add data to digest
+ *                   SIG_MODE_FINAL      4   verify signature
+ *                   SIG_MODE_ALL
+ *                       (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL)
+ *     data    Data to pass through the hash function.
+ *     len      Length of the data in bytes.
+ *     in_key      Key for verification.
+ *     signature   Location of signature.
+ *     sig_len     Length of the signature in bytes.
+ *  Returns
+ *     0          Verify success
+ *     Non-Zero    Verify Failure
+ */
+
+int
+dst_verify_data(const int mode, DST_KEY *in_key, void **context, 
+               const u_char *data, const int len,
+               const u_char *signature, const int sig_len)
+{
+       DUMP(data, mode, len, "dst_verify_data()");
+       if (mode & SIG_MODE_FINAL &&
+           (in_key->dk_KEY_struct == NULL || signature == NULL))
+               return (MISSING_KEY_OR_SIGNATURE);
+
+       if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL)
+               return (UNSUPPORTED_KEYALG);
+       return (in_key->dk_func->verify(mode, in_key, context, data, len,
+                                       signature, sig_len));
+}
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_read_private_key
+ *     Access a private key.  First the list of private keys that have
+ *     already been read in is searched, then the key accessed on disk.
+ *     If the private key can be found, it is returned.  If the key cannot
+ *     be found, a null pointer is returned.  The options specify required
+ *     key characteristics.  If the private key requested does not have
+ *     these characteristics, it will not be read.
+ *  Parameters
+ *     in_keyname  The private key name.
+ *     in_id       The id of the private key.
+ *     options     DST_FORCE_READ  Read from disk - don't use a previously
+ *                                   read key.
+ *               DST_CAN_SIGN    The key must be useable for signing.
+ *               DST_NO_AUTHEN   The key must be useable for authentication.
+ *               DST_STANDARD    Return any key 
+ *  Returns
+ *     NULL    If there is no key found in the current directory or
+ *                   this key has not been loaded before.
+ *     !NULL       Success - KEY structure returned.
+ */
+
+DST_KEY *
+dst_read_key(const char *in_keyname, const u_int16_t in_id, 
+            const int in_alg, const int type)
+{
+       char keyname[PATH_MAX];
+       DST_KEY *dg_key = NULL, *pubkey = NULL;
+
+       if (!dst_check_algorithm(in_alg)) { /* make sure alg is available */
+               EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n",
+                        in_alg));
+               return (NULL);
+       }
+       if ((type & (DST_PUBLIC | DST_PRIVATE)) == 0) 
+               return (NULL);
+       if (in_keyname == NULL) {
+               EREPORT(("dst_read_private_key(): Null key name passed in\n"));
+               return (NULL);
+       } else
+               strcpy(keyname, in_keyname);
+
+       /* before I read in the public key, check if it is allowed to sign */
+       if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL)
+               return (NULL);
+
+       if (type == DST_PUBLIC) 
+               return pubkey; 
+
+       if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg,
+                                        pubkey->dk_flags, pubkey->dk_proto,
+                                           0)))
+               return (dg_key);
+       /* Fill in private key and some fields in the general key structure */
+       if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id,
+                                       pubkey->dk_alg) == 0)
+               dg_key = dst_free_key(dg_key);
+
+       pubkey = dst_free_key(pubkey);
+       return (dg_key);
+}
+#endif
+
+#ifdef _UNUSED_API_
+int 
+dst_write_key(const DST_KEY *key, const int type)
+{
+       int pub = 0, priv = 0;
+
+       if (key == NULL) 
+               return (0);
+       if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */
+               EREPORT(("dst_write_key(): Algorithm %d not suppored\n", 
+                        key->dk_alg));
+               return (UNSUPPORTED_KEYALG);
+       }
+       if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0)
+               return (0);
+
+       if (type & DST_PUBLIC) 
+               if ((pub = dst_s_write_public_key(key)) < 0)
+                       return (pub);
+       if (type & DST_PRIVATE)
+               if ((priv = dst_s_write_private_key(key)) < 0)
+                       return (priv);
+       return (priv+pub);
+}
+#endif
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_write_private_key
+ *     Write a private key to disk.  The filename will be of the form:
+ *     K<key->dk_name>+<key->dk_alg>+<key->dk_id>.<private key suffix>.
+ *     If there is already a file with this name, an error is returned.
+ *
+ *  Parameters
+ *     key     A DST managed key structure that contains
+ *           all information needed about a key.
+ *  Return
+ *     >= 0    Correct behavior.  Returns length of encoded key value
+ *               written to disk.
+ *     <  0    error.
+ */
+
+static int
+dst_s_write_private_key(const DST_KEY *key)
+{
+       u_char encoded_block[RAW_KEY_SIZE];
+       char file[PATH_MAX];
+       int len;
+       FILE *fp;
+
+       /* First encode the key into the portable key format */
+       if (key == NULL)
+               return (-1);
+       if (key->dk_KEY_struct == NULL)
+               return (0);     /* null key has no private key */
+
+       if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) {
+               EREPORT(("dst_write_private_key(): Unsupported operation %d\n",
+                        key->dk_alg));
+               return (-5);
+       } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block,
+                                            sizeof(encoded_block))) <= 0) {
+               EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len));
+               return (-8);
+       }
+       /* Now I can create the file I want to use */
+       dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg,
+                            PRIVATE_KEY, PATH_MAX);
+
+       /* Do not overwrite an existing file */
+       if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) {
+               int nn;
+               if ((nn = fwrite(encoded_block, 1, len, fp)) != len) {
+                       EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n",
+                                file, len, nn, errno));
+                       return (-5);
+               }
+               fclose(fp);
+       } else {
+               EREPORT(("dst_write_private_key(): Can not create file %s\n"
+                        ,file));
+               return (-6);
+       }
+       memset(encoded_block, 0, len);
+       return (len);
+}
+#endif
+
+#ifdef _UNUSED_API_
+/*
+*
+ *  dst_read_public_key
+ *     Read a public key from disk and store in a DST key structure.
+ *  Parameters
+ *     in_name  K<in_name><in_id>.<public key suffix> is the
+ *                   filename of the key file to be read.
+ *  Returns
+ *     NULL        If the key does not exist or no name is supplied.
+ *     NON-NULL        Initialized key structure if the key exists.
+ */
+
+static DST_KEY *
+dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg)
+{
+       int flags, proto, alg, len, dlen;
+       int c;
+       char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace;
+       u_char deckey[RAW_KEY_SIZE];
+       FILE *fp;
+
+       if (in_name == NULL) {
+               EREPORT(("dst_read_public_key(): No key name given\n"));
+               return (NULL);
+       }
+       if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY,
+                                PATH_MAX) == -1) {
+               EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n",
+                        in_name, in_id, PUBLIC_KEY));
+               return (NULL);
+       }
+       /*
+        * Open the file and read it's formatted contents up to key
+        * File format:
+        *    domain.name [ttl] [IN] KEY  <flags> <protocol> <algorithm> <key>
+        * flags, proto, alg stored as decimal (or hex numbers FIXME).
+        * (FIXME: handle parentheses for line continuation.)
+        */
+       if ((fp = dst_s_fopen(name, "r", 0)) == NULL) {
+               EREPORT(("dst_read_public_key(): Public Key not found %s\n",
+                        name));
+               return (NULL);
+       }
+       /* Skip domain name, which ends at first blank */
+       while ((c = getc(fp)) != EOF)
+               if (isspace(c))
+                       break;
+       /* Skip blank to get to next field */
+       while ((c = getc(fp)) != EOF)
+               if (!isspace(c))
+                       break;
+
+       /* Skip optional TTL -- if initial digit, skip whole word. */
+       if (isdigit(c)) {
+               while ((c = getc(fp)) != EOF)
+                       if (isspace(c))
+                               break;
+               while ((c = getc(fp)) != EOF)
+                       if (!isspace(c))
+                               break;
+       }
+       /* Skip optional "IN" */
+       if (c == 'I' || c == 'i') {
+               while ((c = getc(fp)) != EOF)
+                       if (isspace(c))
+                               break;
+               while ((c = getc(fp)) != EOF)
+                       if (!isspace(c))
+                               break;
+       }
+       /* Locate and skip "KEY" */
+       if (c != 'K' && c != 'k') {
+               EREPORT(("\"KEY\" doesn't appear in file: %s", name));
+               return NULL;
+       }
+       while ((c = getc(fp)) != EOF)
+               if (isspace(c))
+                       break;
+       while ((c = getc(fp)) != EOF)
+               if (!isspace(c))
+                       break;
+       ungetc(c, fp);          /* return the charcter to the input field */
+       /* Handle hex!! FIXME.  */
+
+       if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) {
+               EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n"
+                        ,name));
+               return (NULL);
+       }
+       /* read in the key string */
+       fgets(enckey, sizeof(enckey), fp);
+
+       /* If we aren't at end-of-file, something is wrong.  */
+       while ((c = getc(fp)) != EOF)
+               if (!isspace(c))
+                       break;
+       if (!feof(fp)) {
+               EREPORT(("Key too long in file: %s", name));
+               return NULL;
+       }
+       fclose(fp);
+
+       if ((len = strlen(enckey)) <= 0)
+               return (NULL);
+
+       /* discard \n */
+       enckey[--len] = '\0';
+
+       /* remove leading spaces */
+       for (notspace = (char *) enckey; isspace((*notspace)&0xff); len--)
+               notspace++;
+
+       dlen = b64_pton(notspace, deckey, sizeof(deckey));
+       if (dlen < 0) {
+               EREPORT(("dst_read_public_key: bad return from b64_pton = %d",
+                        dlen));
+               return (NULL);
+       }
+       /* store key and info in a key structure that is returned */
+/*     return dst_store_public_key(in_name, alg, proto, 666, flags, deckey,
+                                   dlen);*/
+       return dst_buffer_to_key(in_name, alg, flags, proto, deckey, dlen);
+}
+#endif
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_write_public_key
+ *     Write a key to disk in DNS format.
+ *  Parameters
+ *     key     Pointer to a DST key structure.
+ *  Returns
+ *     0       Failure
+ *     1       Success
+ */
+
+static int
+dst_s_write_public_key(const DST_KEY *key)
+{
+       FILE *fp;
+       char filename[PATH_MAX];
+       u_char out_key[RAW_KEY_SIZE];
+       char enc_key[RAW_KEY_SIZE];
+       int len = 0;
+       int mode;
+
+       memset(out_key, 0, sizeof(out_key));
+       if (key == NULL) {
+               EREPORT(("dst_write_public_key(): No key specified \n"));
+               return (0);
+       } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0)
+               return (0);
+
+       /* Make the filename */
+       if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id,
+                                key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) {
+               EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n",
+                        key->dk_key_name, key->dk_id, PUBLIC_KEY));
+               return (0);
+       }
+       /* XXX in general this should be a check for symmetric keys */
+       mode = (key->dk_alg == KEY_HMAC_MD5) ? 0600 : 0644;
+       /* create public key file */
+       if ((fp = dst_s_fopen(filename, "w+", mode)) == NULL) {
+               EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n",
+                        filename, errno));
+               return (0);
+       }
+       /*write out key first base64 the key data */
+       if (key->dk_flags & DST_EXTEND_FLAG)
+               b64_ntop(&out_key[6], len - 6, enc_key, sizeof(enc_key));
+       else
+               b64_ntop(&out_key[4], len - 4, enc_key, sizeof(enc_key));
+       fprintf(fp, "%s IN KEY %d %d %d %s\n",
+               key->dk_key_name,
+               key->dk_flags, key->dk_proto, key->dk_alg, enc_key);
+       fclose(fp);
+       return (1);
+}
+#endif
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_dnskey_to_public_key
+ *     This function converts the contents of a DNS KEY RR into a DST
+ *     key structure.
+ *  Paramters
+ *     len      Length of the RDATA of the KEY RR RDATA
+ *     rdata    A pointer to the the KEY RR RDATA.
+ *     in_name     Key name to be stored in key structure.
+ *  Returns
+ *     NULL        Failure
+ *     NON-NULL        Success.  Pointer to key structure.
+ *                     Caller's responsibility to free() it.
+ */
+
+DST_KEY *
+dst_dnskey_to_key(const char *in_name, const u_char *rdata, const int len)
+{
+       DST_KEY *key_st;
+       int alg ;
+       int start = DST_KEY_START;
+
+       if (rdata == NULL || len <= DST_KEY_ALG) /* no data */
+               return (NULL);
+       alg = (u_int8_t) rdata[DST_KEY_ALG];
+       if (!dst_check_algorithm(alg)) { /* make sure alg is available */
+               EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n",
+                        alg));
+               return (NULL);
+       }
+       if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL)
+               return (NULL);
+
+       if (in_name == NULL)
+               return (NULL);
+       key_st->dk_id = dst_s_dns_key_id(rdata, len);
+       key_st->dk_flags = dst_s_get_int16(rdata);
+       key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT];
+       if (key_st->dk_flags & DST_EXTEND_FLAG) {
+               u_int32_t ext_flags;
+               ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]);
+               key_st->dk_flags = key_st->dk_flags | (ext_flags << 16);
+               start += 2;
+       }
+       /*
+        * now point to the begining of the data representing the encoding
+        * of the key
+        */
+       if (key_st->dk_func && key_st->dk_func->from_dns_key) {
+               if (key_st->dk_func->from_dns_key(key_st, &rdata[start],
+                                                 len - start) > 0)
+                       return (key_st);
+       } else
+               EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n",
+                        alg));
+
+       SAFE_FREE(key_st);
+       return (key_st);
+}
+#endif
+
+/*
+ *  dst_public_key_to_dnskey
+ *     Function to encode a public key into DNS KEY wire format 
+ *  Parameters
+ *     key          Key structure to encode.
+ *     out_storage     Location to write the encoded key to.
+ *     out_len  Size of the output array.
+ *  Returns
+ *     <0      Failure
+ *     >=0     Number of bytes written to out_storage
+ */
+
+#ifdef __APPLE__
+static
+#endif
+int
+dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage,
+                        const int out_len)
+{
+       u_int16_t val;
+       int loc = 0;
+       int enc_len = 0;
+       if (key == NULL)
+               return (-1);
+
+       if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */
+               EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n",
+                        key->dk_alg));
+               return (UNSUPPORTED_KEYALG);
+       }
+       memset(out_storage, 0, out_len);
+       val = (u_int16_t)(key->dk_flags & 0xffff);
+       dst_s_put_int16(out_storage, val);
+       loc += 2;
+
+       out_storage[loc++] = (u_char) key->dk_proto;
+       out_storage[loc++] = (u_char) key->dk_alg;
+
+       if (key->dk_flags > 0xffff) {   /* Extended flags */
+               val = (u_int16_t)((key->dk_flags >> 16) & 0xffff);
+               dst_s_put_int16(&out_storage[loc], val);
+               loc += 2;
+       }
+       if (key->dk_KEY_struct == NULL)
+               return (loc);
+       if (key->dk_func && key->dk_func->to_dns_key) {
+               enc_len = key->dk_func->to_dns_key(key,
+                                                (u_char *) &out_storage[loc],
+                                                  out_len - loc);
+               if (enc_len > 0)
+                       return (enc_len + loc);
+               else
+                       return (-1);
+       } else
+               EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n",
+                        key->dk_alg));
+       return (-1);
+}
+
+/*
+ *  dst_buffer_to_key
+ *     Function to encode a string of raw data into a DST key
+ *  Parameters
+ *     alg             The algorithm (HMAC only)
+ *     key             A pointer to the data
+ *     keylen          The length of the data
+ *  Returns
+ *     NULL        an error occurred
+ *     NON-NULL        the DST key
+ */
+DST_KEY *
+dst_buffer_to_key(const char *key_name,                /* name of the key */
+                 const int alg,                /* algorithm */
+                 const int flags,              /* dns flags */
+                 const int protocol,           /* dns protocol */
+                 const u_char *key_buf,        /* key in dns wire fmt */
+                 const int key_len)            /* size of key */
+{
+       
+       DST_KEY *dkey = NULL; 
+       int dnslen;
+       u_char dns[2048];
+
+       if (!dst_check_algorithm(alg)) { /* make sure alg is available */
+               EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg));
+               return (NULL);
+       }
+
+       dkey = dst_s_get_key_struct(key_name, alg, flags, 
+                                            protocol, -1);
+
+       if (dkey == NULL)
+               return (NULL);
+       if (dkey->dk_func == NULL || dkey->dk_func->from_dns_key == NULL)
+               return NULL;
+
+       if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) {
+               EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n"));
+               return (dst_free_key(dkey));
+       }
+
+       dnslen = dst_key_to_dnskey(dkey, dns, sizeof(dns));
+       dkey->dk_id = dst_s_dns_key_id(dns, dnslen);
+       return (dkey);
+}
+
+#ifdef _UNUSED_API_
+int 
+dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len)
+{
+       int len;
+  /* this function will extrac the secret of HMAC into a buffer */
+       if (key == NULL) 
+               return (0);
+       if (key->dk_func != NULL && key->dk_func->to_dns_key != NULL) {
+               len = key->dk_func->to_dns_key(key, out_buff, buf_len);
+               if (len < 0)
+                       return (0);
+               return (len);
+       }
+       return (0);
+}
+#endif
+
+#ifdef _UNUSED_API_
+/*
+ * dst_s_read_private_key_file
+ *     Function reads in private key from a file.
+ *     Fills out the KEY structure.
+ * Parameters
+ *     name    Name of the key to be read.
+ *     pk_key  Structure that the key is returned in.
+ *     in_id   Key identifier (tag)
+ * Return
+ *     1 if everthing works
+ *     0 if there is any problem
+ */
+
+static int
+dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id,
+                           int in_alg)
+{
+       int cnt, alg, len, major, minor, file_major, file_minor;
+       int ret, id;
+       char filename[PATH_MAX];
+       u_char in_buff[RAW_KEY_SIZE], *p;
+       FILE *fp;
+       int dnslen;
+       u_char dns[2048];
+
+       if (name == NULL || pk_key == NULL) {
+               EREPORT(("dst_read_private_key_file(): No key name given\n"));
+               return (0);
+       }
+       /* Make the filename */
+       if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY,
+                                PATH_MAX) == -1) {
+               EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n",
+                        name, in_id, PRIVATE_KEY));
+               return (0);
+       }
+       /* first check if we can find the key file */
+       if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) {
+               EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n",
+                        filename, dst_path[0] ? dst_path :
+                        (char *) getcwd(NULL, PATH_MAX - 1)));
+               return (0);
+       }
+       /* now read the header info from the file */
+       if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) {
+               fclose(fp);
+               EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n",
+                        filename));
+               return (0);
+       }
+       /* decrypt key */
+       fclose(fp);
+       if (memcmp(in_buff, "Private-key-format: v", 20) != 0)
+               goto fail;
+       len = cnt;
+       p = in_buff;
+
+       if (!dst_s_verify_str((const char **) &p, "Private-key-format: v")) {
+               EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name));
+               goto fail;
+       }
+       /* read in file format */
+       sscanf((char *)p, "%d.%d", &file_major, &file_minor);
+       sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor);
+       if (file_major < 1) {
+               EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n",
+                        file_major, file_minor, name));
+               goto fail;
+       } else if (file_major > major || file_minor > minor)
+               EREPORT((
+                               "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n",
+                               name, file_major, file_minor));
+
+       while (*p++ != '\n') ;  /* skip to end of line */
+
+       if (!dst_s_verify_str((const char **) &p, "Algorithm: "))
+               goto fail;
+
+       if (sscanf((char *)p, "%d", &alg) != 1)
+               goto fail;
+       while (*p++ != '\n') ;  /* skip to end of line */
+
+       if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name))
+               SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name));
+       pk_key->dk_key_name = (char *) strdup(name);
+
+       /* allocate and fill in key structure */
+       if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL)
+               goto fail;
+
+       ret = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, &in_buff[len] - p);
+       if (ret < 0)
+               goto fail;
+
+       dnslen = dst_key_to_dnskey(pk_key, dns, sizeof(dns));
+       id = dst_s_dns_key_id(dns, dnslen);
+
+       /* Make sure the actual key tag matches the input tag used in the filename
+        */
+       if (id != in_id) {
+               EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id));
+               goto fail;
+       }
+       pk_key->dk_id = (u_int16_t) id;
+       pk_key->dk_alg = alg;
+       memset(in_buff, 0, cnt);
+       return (1);
+
+ fail:
+       memset(in_buff, 0, cnt);
+       return (0);
+}
+#endif
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_generate_key
+ *     Generate and store a public/private keypair.
+ *     Keys will be stored in formatted files.
+ *  Parameters
+ *     name    Name of the new key.  Used to create key files
+ *               K<name>+<alg>+<id>.public and K<name>+<alg>+<id>.private.
+ *     bits    Size of the new key in bits.
+ *     exp     What exponent to use:
+ *               0        use exponent 3
+ *               non-zero    use Fermant4
+ *     flags   The default value of the DNS Key flags.
+ *               The DNS Key RR Flag field is defined in RFC 2065,
+ *               section 3.3.  The field has 16 bits.
+ *     protocol
+ *           Default value of the DNS Key protocol field.
+ *               The DNS Key protocol field is defined in RFC 2065,
+ *               section 3.4.  The field has 8 bits.
+ *     alg     What algorithm to use.  Currently defined:
+ *               KEY_RSA       1
+ *               KEY_DSA       3
+ *               KEY_HMAC    157
+ *     out_id The key tag is returned.
+ *
+ *  Return
+ *     NULL            Failure
+ *     non-NULL        the generated key pair
+ *                     Caller frees the result, and its dk_name pointer.
+ */
+DST_KEY *
+dst_generate_key(const char *name, const int bits, const int exp,
+                const int flags, const int protocol, const int alg)
+{
+       DST_KEY *new_key = NULL;
+       int res;
+       int dnslen;
+       u_char dns[2048];
+
+       if (name == NULL)
+               return (NULL);
+
+       if (!dst_check_algorithm(alg)) { /* make sure alg is available */
+               EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg));
+               return (NULL);
+       }
+
+       new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits);
+       if (new_key == NULL)
+               return (NULL);
+       if (bits == 0) /* null key we are done */
+               return (new_key);
+       if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) {
+               EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n",
+                        alg));
+               return (dst_free_key(new_key));
+       }
+       if ((res = new_key->dk_func->generate(new_key, exp)) <= 0) {
+               EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n",
+                        new_key->dk_key_name, new_key->dk_alg,
+                        new_key->dk_key_size, exp));
+               return (dst_free_key(new_key));
+       }
+
+       dnslen = dst_key_to_dnskey(new_key, dns, sizeof(dns));
+       if (dnslen != UNSUPPORTED_KEYALG)
+               new_key->dk_id = dst_s_dns_key_id(dns, dnslen);
+       else
+               new_key->dk_id = 0;
+
+       return (new_key);
+}
+#endif
+
+/*
+ *  dst_free_key
+ *     Release all data structures pointed to by a key structure.
+ *  Parameters
+ *     f_key   Key structure to be freed.
+ */
+
+DST_KEY *
+dst_free_key(DST_KEY *f_key)
+{
+
+       if (f_key == NULL)
+               return (f_key);
+       if (f_key->dk_func && f_key->dk_func->destroy)
+               f_key->dk_KEY_struct =
+                       f_key->dk_func->destroy(f_key->dk_KEY_struct);
+       else {
+               EREPORT(("dst_free_key(): Unknown key alg %d\n",
+                        f_key->dk_alg));
+               free(f_key->dk_KEY_struct);     /* SHOULD NOT happen */
+       }
+       if (f_key->dk_KEY_struct) {
+               free(f_key->dk_KEY_struct);
+               f_key->dk_KEY_struct = NULL;
+       }
+       if (f_key->dk_key_name)
+               SAFE_FREE(f_key->dk_key_name);
+       SAFE_FREE(f_key);
+       return (NULL);
+}
+
+#ifdef _UNUSED_API_
+/*
+ * dst_sig_size
+ *     Return the maximim size of signature from the key specified in bytes
+ * Parameters
+ *      key 
+ * Returns
+ *     bytes
+ */
+int
+dst_sig_size(DST_KEY *key) {
+       switch (key->dk_alg) {
+           case KEY_HMAC_MD5:
+               return (16);
+           case KEY_HMAC_SHA1:
+               return (20);
+           case KEY_RSA:
+               return (key->dk_key_size + 7) / 8;
+           case KEY_DSA:
+               return (40);
+           default:
+               EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg));
+               return -1;
+       }
+}
+#endif
diff --git a/dst_hmac_link.c b/dst_hmac_link.c
new file mode 100644 (file)
index 0000000..b5b3375
--- /dev/null
@@ -0,0 +1,481 @@
+#ifdef HMAC_MD5
+#ifndef __APPLE__
+#ifndef LINT
+static const char rcsid[] = "$Header: /Users/Shared/libresolv_2/libresolv/dst_hmac_link.c,v 1.1 2006/03/01 19:01:36 majka Exp $";
+#endif
+#endif
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+
+/* 
+ * This file contains an implementation of the HMAC-MD5 algorithm.
+ */
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "dst_internal.h"
+#ifdef USE_MD5
+# include "md5.h"
+# ifndef _MD5_H_
+#  define _MD5_H_ 1    /* make sure we do not include rsaref md5.h file */
+# endif
+#endif
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+
+#define HMAC_LEN       64
+#define HMAC_IPAD      0x36
+#define HMAC_OPAD      0x5c
+#define MD5_LEN                16
+
+
+typedef struct hmackey {
+       u_char hk_ipad[64], hk_opad[64];
+} HMAC_Key;
+
+
+/************************************************************************** 
+ * dst_hmac_md5_sign
+ *     Call HMAC signing functions to sign a block of data.
+ *     There are three steps to signing, INIT (initialize structures), 
+ *     UPDATE (hash (more) data), FINAL (generate a signature).  This
+ *     routine performs one or more of these steps.
+ * Parameters
+ *     mode    SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
+ *     priv_key    key to use for signing.
+ *     context   the context to be used in this digest
+ *     data    data to be signed.
+ *     len      length in bytes of data.
+ *     signature   location to store signature.
+ *     sig_len     size of the signature location
+ * returns 
+ *     N  Success on SIG_MODE_FINAL = returns signature length in bytes
+ *     0  Success on SIG_MODE_INIT  and UPDATE
+ *      <0  Failure
+ */
+
+static int
+dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context, 
+                 const u_char *data, const int len, 
+                 u_char *signature, const int sig_len)
+{
+       HMAC_Key *key;
+       int sign_len = 0;
+       MD5_CTX *ctx = NULL;
+
+       if (mode & SIG_MODE_INIT) 
+               ctx = (MD5_CTX *) malloc(sizeof(*ctx));
+       else if (context)
+               ctx = (MD5_CTX *) *context;
+       if (ctx == NULL) 
+               return (-1);
+
+       if (d_key == NULL || d_key->dk_KEY_struct == NULL)
+               return (-1);
+       key = (HMAC_Key *) d_key->dk_KEY_struct;
+
+       if (mode & SIG_MODE_INIT) {
+               MD5Init(ctx);
+               MD5Update(ctx, key->hk_ipad, HMAC_LEN);
+       }
+
+       if ((mode & SIG_MODE_UPDATE) && (data && len > 0))
+               MD5Update(ctx, data, len);
+
+       if (mode & SIG_MODE_FINAL) {
+               if (signature == NULL || sig_len < MD5_LEN)
+                       return (SIGN_FINAL_FAILURE);
+               MD5Final(signature, ctx);
+
+               /* perform outer MD5 */
+               MD5Init(ctx);
+               MD5Update(ctx, key->hk_opad, HMAC_LEN);
+               MD5Update(ctx, signature, MD5_LEN);
+               MD5Final(signature, ctx);
+               sign_len = MD5_LEN;
+               SAFE_FREE(ctx);
+       }
+       else { 
+               if (context == NULL) 
+                       return (-1);
+               *context = (void *) ctx;
+       }               
+       return (sign_len);
+}
+
+
+/************************************************************************** 
+ * dst_hmac_md5_verify() 
+ *     Calls HMAC verification routines.  There are three steps to 
+ *     verification, INIT (initialize structures), UPDATE (hash (more) data), 
+ *     FINAL (generate a signature).  This routine performs one or more of 
+ *     these steps.
+ * Parameters
+ *     mode    SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
+ *     dkey    key to use for verify.
+ *     data    data signed.
+ *     len      length in bytes of data.
+ *     signature   signature.
+ *     sig_len     length in bytes of signature.
+ * returns 
+ *     0  Success 
+ *    <0  Failure
+ */
+
+static int
+dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context,
+               const u_char *data, const int len,
+               const u_char *signature, const int sig_len)
+{
+       HMAC_Key *key;
+       MD5_CTX *ctx = NULL;
+
+       if (mode & SIG_MODE_INIT) 
+               ctx = (MD5_CTX *) malloc(sizeof(*ctx));
+       else if (context)
+               ctx = (MD5_CTX *) *context;
+       if (ctx == NULL) 
+               return (-1);
+
+       if (d_key == NULL || d_key->dk_KEY_struct == NULL)
+               return (-1);
+
+       key = (HMAC_Key *) d_key->dk_KEY_struct;
+       if (mode & SIG_MODE_INIT) {
+               MD5Init(ctx);
+               MD5Update(ctx, key->hk_ipad, HMAC_LEN);
+       }
+       if ((mode & SIG_MODE_UPDATE) && (data && len > 0))
+               MD5Update(ctx, data, len);
+
+       if (mode & SIG_MODE_FINAL) {
+               u_char digest[MD5_LEN];
+               if (signature == NULL || key == NULL || sig_len != MD5_LEN)
+                       return (VERIFY_FINAL_FAILURE);
+               MD5Final(digest, ctx);
+
+               /* perform outer MD5 */
+               MD5Init(ctx);
+               MD5Update(ctx, key->hk_opad, HMAC_LEN);
+               MD5Update(ctx, digest, MD5_LEN);
+               MD5Final(digest, ctx);
+
+               SAFE_FREE(ctx);
+               if (memcmp(digest, signature, MD5_LEN) != 0)
+                       return (VERIFY_FINAL_FAILURE);
+       }
+       else { 
+               if (context == NULL) 
+                       return (-1);
+               *context = (void *) ctx;
+       }               
+       return (0);
+}
+
+
+/************************************************************************** 
+ * dst_buffer_to_hmac_md5
+ *     Converts key from raw data to an HMAC Key
+ *     This function gets in a pointer to the data
+ * Parameters
+ *     hkey    the HMAC key to be filled in
+ *     key     the key in raw format
+ *     keylen  the length of the key
+ * Return
+ *     0       Success
+ *     <0      Failure
+ */
+static int
+dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const int keylen)
+{
+       int i;
+       HMAC_Key *hkey = NULL;
+       MD5_CTX ctx;
+       int local_keylen = keylen;
+
+       if (dkey == NULL || key == NULL || keylen < 0)
+               return (-1);
+
+       if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL)
+                 return (-2);
+
+       memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad));
+       memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad));
+
+       /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */
+       if (keylen > HMAC_LEN) {
+               u_char tk[MD5_LEN];
+               MD5Init(&ctx);
+               MD5Update(&ctx, key, keylen);
+               MD5Final(tk, &ctx);
+               memset((void *) &ctx, 0, sizeof(ctx));
+               key = tk;
+               local_keylen = MD5_LEN;
+       }
+       /* start out by storing key in pads */
+       memcpy(hkey->hk_ipad, key, local_keylen);
+       memcpy(hkey->hk_opad, key, local_keylen);
+
+       /* XOR key with hk_ipad and opad values */
+       for (i = 0; i < HMAC_LEN; i++) {
+               hkey->hk_ipad[i] ^= HMAC_IPAD;
+               hkey->hk_opad[i] ^= HMAC_OPAD;
+       }
+       dkey->dk_key_size = local_keylen;
+       dkey->dk_KEY_struct = (void *) hkey;
+       return (1);
+}
+
+
+/************************************************************************** 
+ *  dst_hmac_md5_key_to_file_format
+ *     Encodes an HMAC Key into the portable file format.
+ *  Parameters 
+ *     hkey      HMAC KEY structure 
+ *     buff      output buffer
+ *     buff_len  size of output buffer 
+ *  Return
+ *     0  Failure - null input hkey
+ *     -1  Failure - not enough space in output area
+ *     N  Success - Length of data returned in buff
+ */
+
+static int
+dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff,
+                           const int buff_len)
+{
+       char *bp;
+       int len, b_len, i, key_len;
+       u_char key[HMAC_LEN];
+       HMAC_Key *hkey;
+
+       if (dkey == NULL || dkey->dk_KEY_struct == NULL) 
+               return (0);
+       if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str))
+               return (-1);    /* no OR not enough space in output area */
+
+       hkey = (HMAC_Key *) dkey->dk_KEY_struct;
+       memset(buff, 0, buff_len);      /* just in case */
+       /* write file header */
+       sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC");
+
+       bp = (char *) strchr(buff, '\0');
+       b_len = buff_len - (bp - buff);
+
+       memset(key, 0, HMAC_LEN);
+       for (i = 0; i < HMAC_LEN; i++)
+               key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD;
+       for (i = HMAC_LEN - 1; i >= 0; i--)
+               if (key[i] != 0)
+                       break;
+       key_len = i + 1;
+
+       strcat(bp, "Key: ");
+       bp += strlen("Key: ");
+       b_len = buff_len - (bp - buff);
+
+       len = b64_ntop(key, key_len, bp, b_len);
+       if (len < 0) 
+               return (-1);
+       bp += len;
+       *(bp++) = '\n';
+       *bp = '\0';
+       b_len = buff_len - (bp - buff);
+
+       return (buff_len - b_len);
+}
+
+
+/************************************************************************** 
+ * dst_hmac_md5_key_from_file_format
+ *     Converts contents of a key file into an HMAC key. 
+ * Parameters 
+ *     hkey    structure to put key into 
+ *     buff       buffer containing the encoded key 
+ *     buff_len   the length of the buffer
+ * Return
+ *     n >= 0 Foot print of the key converted 
+ *     n <  0 Error in conversion 
+ */
+
+static int
+dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff,
+                             const int buff_len)
+{
+       const char *p = buff, *eol;
+       u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode
+                                                        * it should probably be fixed rather than doing
+                                                        * this
+                                                        */
+       u_char *tmp;
+       int key_len, len;
+
+       if (dkey == NULL)
+               return (-2);
+       if (buff == NULL || buff_len < 0)
+               return (-1);
+
+       memset(key, 0, sizeof(key));
+
+       if (!dst_s_verify_str(&p, "Key: "))
+               return (-3);
+
+       eol = strchr(p, '\n');
+       if (eol == NULL)
+               return (-4);
+       len = eol - p;
+       tmp = malloc(len + 2);
+       memcpy(tmp, p, len);
+       *(tmp + len) = 0x0;
+       key_len = b64_pton((char *)tmp, key, HMAC_LEN+1);       /* see above */
+       SAFE_FREE2(tmp, len + 2);
+
+       if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) {
+               return (-6);
+       }
+       return (0);
+}
+
+/*
+ * dst_hmac_md5_to_dns_key() 
+ *         function to extract hmac key from DST_KEY structure 
+ * intput: 
+ *      in_key:  HMAC-MD5 key 
+ * output: 
+ *     out_str: buffer to write ot
+ *      out_len: size of output buffer 
+ * returns:
+ *      number of bytes written to output buffer 
+ */
+static int
+dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str,
+                       const int out_len)
+{
+
+       HMAC_Key *hkey;
+       int i;
+       
+       if (in_key == NULL || in_key->dk_KEY_struct == NULL ||
+           out_len <= in_key->dk_key_size || out_str == NULL)
+               return (-1);
+
+       hkey = (HMAC_Key *) in_key->dk_KEY_struct;
+       for (i = 0; i < in_key->dk_key_size; i++)
+               out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD;
+       return (i);
+}
+
+/************************************************************************** 
+ *  dst_hmac_md5_compare_keys
+ *     Compare two keys for equality.
+ *  Return
+ *     0         The keys are equal
+ *     NON-ZERO   The keys are not equal
+ */
+
+static int
+dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
+{
+       HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct;
+       HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct;
+       return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN);
+}
+
+/************************************************************************** 
+ * dst_hmac_md5_free_key_structure
+ *     Frees all (none) dynamically allocated structures in hkey
+ */
+
+static void *
+dst_hmac_md5_free_key_structure(void *key)
+{
+       HMAC_Key *hkey = key;
+       SAFE_FREE(hkey);
+       return (NULL);
+}
+
+
+/*************************************************************************** 
+ * dst_hmac_md5_generate_key
+ *     Creates a HMAC key of size size with a maximum size of 63 bytes
+ *     generating a HMAC key larger than 63 bytes makes no sense as that key 
+ *     is digested before use. 
+ */
+
+static int
+dst_hmac_md5_generate_key(DST_KEY *key, const int nothing)
+{
+       (void)key;
+       (void)nothing;
+       return (-1);
+}
+
+/*
+ * dst_hmac_md5_init()  Function to answer set up function pointers for HMAC
+ *        related functions 
+ */
+int
+dst_hmac_md5_init()
+{
+       if (dst_t_func[KEY_HMAC_MD5] != NULL)
+               return (1);
+       dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func));
+       if (dst_t_func[KEY_HMAC_MD5] == NULL)
+               return (0);
+       memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func));
+       dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign;
+       dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify;
+       dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys;
+       dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key;
+       dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure;
+       dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key;
+       dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5;
+       dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format;
+       dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format;
+       return (1);
+}
+
+#else 
+#define dst_hmac_md5_init res_9_dst_hmac_md5_init
+int
+dst_hmac_md5_init(){
+       return (0);
+}
+#endif
+
+
+
+
+
+
+
diff --git a/dst_internal.h b/dst_internal.h
new file mode 100644 (file)
index 0000000..177af3a
--- /dev/null
@@ -0,0 +1,188 @@
+#ifndef DST_INTERNAL_H
+#define DST_INTERNAL_H
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+#include <limits.h>
+#include <sys/param.h>
+#if (!defined(BSD)) || (BSD < 199306)
+# include <sys/bitypes.h>
+#else
+# include <sys/types.h>
+#endif
+
+#ifndef PATH_MAX
+# ifdef POSIX_PATH_MAX
+#  define PATH_MAX POSIX_PATH_MAX
+# else
+#  define PATH_MAX 255 /* this is the value of POSIX_PATH_MAX */
+# endif
+#endif 
+
+#ifndef dst_func
+#define dst_func res_9_dst_func
+#endif
+#define DST_KEY res_9_DST_KEY
+typedef struct dst_key {
+       char    *dk_key_name;   /* name of the key */
+       int     dk_key_size;    /* this is the size of the key in bits */
+       int     dk_proto;       /* what protocols this key can be used for */
+       int     dk_alg;         /* algorithm number from key record */
+       u_int32_t dk_flags;     /* and the flags of the public key */
+       u_int16_t dk_id;        /* identifier of the key */
+       void    *dk_KEY_struct; /* pointer to key in crypto pkg fmt */
+       struct dst_func *dk_func; /* point to cryptto pgk specific function table */
+} DST_KEY;
+#define HAS_DST_KEY 
+
+#ifdef __APPLE__
+#include "dst.h"
+#else
+#include <isc/dst.h>
+#endif
+
+/* 
+ * define what crypto systems are supported for RSA, 
+ * BSAFE is prefered over RSAREF; only one can be set at any time
+ */
+#if defined(BSAFE) && defined(RSAREF)
+# error "Cannot have both BSAFE and RSAREF defined"
+#endif
+
+/* Declare dst_lib specific constants */
+#define KEY_FILE_FORMAT "1.2"
+
+/* suffixes for key file names */
+#define PRIVATE_KEY            "private"
+#define PUBLIC_KEY             "key"
+
+/* error handling */
+#ifdef REPORT_ERRORS
+#define EREPORT(str)           printf str
+#else
+#define EREPORT(str)           (void)0
+#endif
+
+/* use our own special macro to FRRE memory */
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(a) \
+do{if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;}} while (0)
+#define SAFE_FREE2(a,s) if (a != NULL && s > 0){memset(a,0, s);free(a); a=NULL;}
+#endif
+
+#define dst_func res_9_dst_func
+typedef struct dst_func {
+       int (*sign)(const int mode, DST_KEY *key, void **context,
+                    const u_int8_t *data, const int len,
+                    u_int8_t *signature, const int sig_len);
+       int (*verify)(const int mode, DST_KEY *key, void **context,
+                      const u_int8_t *data, const int len,
+                      const u_int8_t *signature, const int sig_len);
+       int (*compare)(const DST_KEY *key1, const DST_KEY *key2);
+       int (*generate)(DST_KEY *key, int parms);
+       void *(*destroy)(void *key);
+       /* conversion functions */
+       int (*to_dns_key)(const DST_KEY *key, u_int8_t *out,
+                          const int out_len);
+       int (*from_dns_key)(DST_KEY *key, const u_int8_t *str,
+                            const int str_len);
+       int (*to_file_fmt)(const DST_KEY *key, char *out,
+                           const int out_len);
+       int (*from_file_fmt)(DST_KEY *key, const char *out,
+                             const int out_len);
+
+} dst_func;
+
+#define dst_t_func res_9_dst_t_func
+extern dst_func *dst_t_func[DST_MAX_ALGS];
+
+#define key_file_fmt_str res_9_key_file_fmt_str
+extern const char *key_file_fmt_str;
+
+#define dst_path res_9_dst_path
+extern const char *dst_path;
+
+#ifndef DST_HASH_SIZE
+#define DST_HASH_SIZE 20       /* RIPEMD160 and SHA-1 are 20 bytes MD5 is 16 */
+#endif
+
+#define dst_bsafe_init res_9_dst_bsafe_init
+int dst_bsafe_init(void);
+
+#define dst_rsaref_init res_9_dst_rsaref_init
+int dst_rsaref_init(void);
+
+#define dst_hmac_md5_init res_9_dst_hmac_md5_init
+int dst_hmac_md5_init(void);
+
+#define dst_cylink_init res_9_dst_cylink_init
+int dst_cylink_init(void);
+
+#define dst_eay_dss_init res_9_dst_eay_dss_init
+int dst_eay_dss_init(void);
+
+/* from higher level support routines */
+#define dst_s_calculate_bits res_9_dst_s_calculate_bits
+int       dst_s_calculate_bits( const u_int8_t *str, const int max_bits);
+
+#define dst_s_verify_str res_9_dst_s_verify_str
+int       dst_s_verify_str( const char **buf, const char *str);
+
+
+/* conversion between dns names and key file names */
+#define dst_s_filename_length res_9_dst_s_filename_length
+size_t    dst_s_filename_length( const char *name, const char *suffix);
+
+#define dst_s_build_filename res_9_dst_s_build_filename
+int       dst_s_build_filename(  char *filename, const char *name, 
+                                u_int16_t id, int alg, const char *suffix, 
+                                size_t filename_length);
+
+#define dst_s_fopen res_9_dst_s_fopen
+FILE      *dst_s_fopen (const char *filename, const char *mode, int perm);
+
+/* 
+ * read and write network byte order into u_int?_t  
+ *  all of these should be retired
+ */
+#define dst_s_get_int16 res_9_dst_s_get_int16
+u_int16_t dst_s_get_int16( const u_int8_t *buf);
+
+#define dst_s_put_int16 res_9_dst_s_put_int16
+void      dst_s_put_int16( u_int8_t *buf, const u_int16_t val);
+
+#define dst_s_get_int32 res_9_dst_s_get_int32
+u_int32_t dst_s_get_int32( const u_int8_t *buf);
+
+#define dst_s_put_int32 res_9_dst_s_put_int32
+void      dst_s_put_int32( u_int8_t *buf, const u_int32_t val);
+
+#ifdef DUMP
+# undef DUMP
+# define DUMP(a,b,c,d) dst_s_dump(a,b,c,d)
+#else
+# define DUMP(a,b,c,d)
+#endif
+#define dst_s_dump res_9_dst_s_dump
+void
+dst_s_dump(const int mode, const u_char *data, const int size,
+            const char *msg);
+
+
+
+#endif /* DST_INTERNAL_H */
diff --git a/dst_support.c b/dst_support.c
new file mode 100644 (file)
index 0000000..eddccc1
--- /dev/null
@@ -0,0 +1,367 @@
+#ifndef __APPLE__
+static const char rcsid[] = "$Header: /Users/Shared/libresolv_2/libresolv/dst_support.c,v 1.1 2006/03/01 19:01:36 majka Exp $";
+#endif
+
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <memory.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "dst_internal.h"
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/*
+ * dst_s_verify_str()
+ *     Validate that the input string(*str) is at the head of the input
+ *     buffer(**buf).  If so, move the buffer head pointer (*buf) to
+ *     the first byte of data following the string(*str).
+ * Parameters
+ *     buf     Input buffer.
+ *     str     Input string.
+ * Return
+ *     0       *str is not the head of **buff
+ *     1       *str is the head of **buff, *buf is is advanced to
+ *     the tail of **buf.
+ */
+
+int
+dst_s_verify_str(const char **buf, const char *str)
+{
+       int b, s;
+       if (*buf == NULL)       /* error checks */
+               return (0);
+       if (str == NULL || *str == '\0')
+               return (1);
+
+       b = strlen(*buf);       /* get length of strings */
+       s = strlen(str);
+       if (s > b || strncmp(*buf, str, s))     /* check if same */
+               return (0);     /* not a match */
+       (*buf) += s;            /* advance pointer */
+       return (1);
+}
+
+#ifdef _UNUSED_API_
+/*
+ * dst_s_calculate_bits
+ *     Given a binary number represented in a u_char[], determine
+ *     the number of significant bits used.
+ * Parameters
+ *     str       An input character string containing a binary number.
+ *     max_bits The maximum possible significant bits.
+ * Return
+ *     N       The number of significant bits in str.
+ */
+
+int
+dst_s_calculate_bits(const u_char *str, const int max_bits)
+{
+       const u_char *p = str;
+       u_char i, j = 0x80;
+       int bits;
+       for (bits = max_bits; *p == 0x00 && bits > 0; p++)
+               bits -= 8;
+       for (i = *p; (i & j) != j; j >>= 1)
+               bits--;
+       return (bits);
+}
+#endif
+
+/*
+ * calculates a checksum used in dst for an id.
+ * takes an array of bytes and a length.
+ * returns a 16  bit checksum.
+ */
+#ifdef __APPLE__
+static
+#endif
+u_int16_t
+dst_s_id_calc(const u_char *key, const int keysize)
+{
+       u_int32_t ac;
+       const u_char *kp = key;
+       int size = keysize;
+
+       if (!key || (keysize <= 0))
+               return (-1);
+       for (ac = 0; size > 1; size -= 2, kp += 2)
+               ac += ((*kp) << 8) + *(kp + 1);
+
+       if (size > 0)
+               ac += ((*kp) << 8);
+       ac += (ac >> 16) & 0xffff;
+
+       return (ac & 0xffff);
+}
+
+/* 
+ * dst_s_dns_key_id() Function to calculate DNSSEC footprint from KEY record
+ *   rdata
+ * Input:
+ *     dns_key_rdata: the raw data in wire format 
+ *      rdata_len: the size of the input data 
+ * Output:
+ *      the key footprint/id calculated from the key data 
+ */ 
+u_int16_t
+dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len)
+{
+       if (!dns_key_rdata)
+               return 0;
+
+       /* compute id */
+       if (dns_key_rdata[3] == KEY_RSA)        /* Algorithm RSA */
+               return dst_s_get_int16((const u_char *)
+                                      &dns_key_rdata[rdata_len - 3]);
+       else if (dns_key_rdata[3] == KEY_HMAC_MD5)
+               /* compatibility */
+               return 0;
+       else
+               /* compute a checksum on the key part of the key rr */
+               return dst_s_id_calc(dns_key_rdata, rdata_len);
+}
+
+/*
+ * dst_s_get_int16
+ *     This routine extracts a 16 bit integer from a two byte character
+ *     string.  The character string is assumed to be in network byte
+ *     order and may be unaligned.  The number returned is in host order.
+ * Parameter
+ *     buf     A two byte character string.
+ * Return
+ *     The converted integer value.
+ */
+
+u_int16_t
+dst_s_get_int16(const u_char *buf)
+{
+       register u_int16_t a = 0;
+       a = ((u_int16_t)(buf[0] << 8)) | ((u_int16_t)(buf[1]));
+       return (a);
+}
+
+
+#ifdef _UNUSED_API_
+/*
+ * dst_s_get_int32
+ *     This routine extracts a 32 bit integer from a four byte character
+ *     string.  The character string is assumed to be in network byte
+ *     order and may be unaligned.  The number returned is in host order.
+ * Parameter
+ *     buf     A four byte character string.
+ * Return
+ *     The converted integer value.
+ */
+
+u_int32_t
+dst_s_get_int32(const u_char *buf)
+{
+       register u_int32_t a = 0;
+       a = ((u_int32_t)(buf[0] << 24)) | ((u_int32_t)(buf[1] << 16)) |
+               ((u_int32_t)(buf[2] << 8)) | ((u_int32_t)(buf[3]));
+       return (a);
+}
+#endif
+
+/*
+ * dst_s_put_int16
+ *     Take a 16 bit integer and store the value in a two byte
+ *     character string.  The integer is assumed to be in network
+ *     order and the string is returned in host order.
+ *
+ * Parameters
+ *     buf     Storage for a two byte character string.
+ *     val     16 bit integer.
+ */
+
+void
+dst_s_put_int16(u_int8_t *buf, const u_int16_t val)
+{
+       buf[0] = (u_int8_t)(val >> 8);
+       buf[1] = (u_int8_t)(val);
+}
+
+#ifdef _UNUSED_API_
+/*
+ * dst_s_put_int32
+ *     Take a 32 bit integer and store the value in a four byte
+ *     character string.  The integer is assumed to be in network
+ *     order and the string is returned in host order.
+ *
+ * Parameters
+ *     buf     Storage for a four byte character string.
+ *     val     32 bit integer.
+ */
+
+void
+dst_s_put_int32(u_int8_t *buf, const u_int32_t val)
+{
+       buf[0] = (u_int8_t)(val >> 24);
+       buf[1] = (u_int8_t)(val >> 16);
+       buf[2] = (u_int8_t)(val >> 8);
+       buf[3] = (u_int8_t)(val);
+}
+#endif
+
+
+#ifdef _UNUSED_API_
+/*
+ *  dst_s_filename_length
+ *
+ *     This function returns the number of bytes needed to hold the
+ *     filename for a key file.  '/', '\' and ':' are not allowed.
+ *     form:  K<keyname>+<alg>+<id>.<suffix>
+ *
+ *     Returns 0 if the filename would contain either '\', '/' or ':'
+ */
+size_t
+dst_s_filename_length(const char *name, const char *suffix)
+{
+       if (name == NULL)
+               return (0);
+       if (strrchr(name, '\\'))
+               return (0);
+       if (strrchr(name, '/'))
+               return (0);
+       if (strrchr(name, ':'))
+               return (0);
+       if (suffix == NULL)
+               return (0);
+       if (strrchr(suffix, '\\'))
+               return (0);
+       if (strrchr(suffix, '/'))
+               return (0);
+       if (strrchr(suffix, ':'))
+               return (0);
+       return (1 + strlen(name) + 6 + strlen(suffix));
+}
+#endif
+
+/*
+ *  dst_s_build_filename ()
+ *     Builds a key filename from the key name, it's id, and a
+ *     suffix.  '\', '/' and ':' are not allowed. fA filename is of the
+ *     form:  K<keyname><id>.<suffix>
+ *     form: K<keyname>+<alg>+<id>.<suffix>
+ *
+ *     Returns -1 if the conversion fails:
+ *       if the filename would be too long for space allotted
+ *       if the filename would contain a '\', '/' or ':'
+ *     Returns 0 on success
+ */
+
+int
+dst_s_build_filename(char *filename, const char *name, u_int16_t id,
+                    int alg, const char *suffix, size_t filename_length)
+{
+       u_int32_t my_id;
+       if (filename == NULL)
+               return (-1);
+       memset(filename, 0, filename_length);
+       if (name == NULL)
+               return (-1);
+       if (suffix == NULL)
+               return (-1);
+       if (filename_length < 1 + strlen(name) + 4 + 6 + 1 + strlen(suffix))
+               return (-1);
+       my_id = id;
+       sprintf(filename, "K%s+%03d+%05d.%s", name, alg, my_id,
+               (const char *) suffix);
+       if (strrchr(filename, '/'))
+               return (-1);
+       if (strrchr(filename, '\\'))
+               return (-1);
+       if (strrchr(filename, ':'))
+               return (-1);
+       return (0);
+}
+
+/*
+ *  dst_s_fopen ()
+ *     Open a file in the dst_path directory.  If perm is specified, the
+ *     file is checked for existence first, and not opened if it exists.
+ *  Parameters
+ *     filename  File to open
+ *     mode       Mode to open the file (passed directly to fopen)
+ *     perm       File permission, if creating a new file.
+ *  Returns
+ *     NULL       Failure
+ *     NON-NULL  (FILE *) of opened file.
+ */
+FILE *
+dst_s_fopen(const char *filename, const char *mode, int perm)
+{
+       FILE *fp;
+       char pathname[PATH_MAX];
+       size_t plen = sizeof(pathname);
+
+       if (*dst_path != '\0') {
+               strcpy(pathname, dst_path);
+               plen -= strlen(pathname);
+       }
+       else 
+               pathname[0] = '\0';
+
+       if (plen > strlen(filename))
+               strncpy(&pathname[PATH_MAX - plen], filename, plen-1);
+       else 
+               return (NULL);
+       
+       fp = fopen(pathname, mode);
+       if (perm)
+               chmod(pathname, perm);
+       return (fp);
+}
+
+#ifdef _UNUSED_API_
+void
+dst_s_dump(const int mode, const u_char *data, const int size, 
+           const char *msg)
+{
+#ifndef __APPLE__      
+       UNUSED(data);
+#endif
+
+       if (size > 0) {
+#ifdef LONG_TEST
+               static u_char scratch[1000];
+               int n ;
+               n = b64_ntop(data, scratch, size, sizeof(scratch));
+               printf("%s: %x %d %s\n", msg, mode, n, scratch);
+#else
+               printf("%s,%x %d\n", msg, mode, size);
+#endif
+       }
+}
+#endif
diff --git a/nameser.h b/nameser.h
new file mode 100644 (file)
index 0000000..8f40203
--- /dev/null
+++ b/nameser.h
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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.
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ *     $Id: nameser.h,v 1.1 2006/03/01 19:01:36 majka Exp $
+ */
+
+#ifndef _NAMESER_9_H_
+#define _NAMESER_9_H_
+
+#ifdef BIND_8_COMPAT
+#include <arpa/nameser8_compat.h>
+#else
+
+#include <sys/param.h>
+#if (!defined(BSD)) || (BSD < 199306)
+# include <sys/bitypes.h>
+#else
+# include <sys/types.h>
+#endif
+#include <sys/cdefs.h>
+
+/*
+ * Revision information.  This is the release date in YYYYMMDD format.
+ * It can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__NAMESER > 19931104)".  Do not
+ * compare for equality; rather, use it to determine whether your libbind.a
+ * contains a new enough lib/nameser/ to support the feature you need.
+ */
+
+#define __NAMESER      19991006        /* New interface version stamp. */
+
+/*
+ * Define constants based on RFC 883, RFC 1034, RFC 1035
+ */
+#define NS_PACKETSZ    512     /* maximum packet size */
+#define NS_MAXDNAME    1025    /* maximum domain name */
+#define NS_MAXCDNAME   255     /* maximum compressed domain name */
+#define NS_MAXLABEL    63      /* maximum length of domain label */
+#define NS_HFIXEDSZ    12      /* #/bytes of fixed data in header */
+#define NS_QFIXEDSZ    4       /* #/bytes of fixed data in query */
+#define NS_RRFIXEDSZ   10      /* #/bytes of fixed data in r record */
+#define NS_INT32SZ     4       /* #/bytes of data in a u_int32_t */
+#define NS_INT16SZ     2       /* #/bytes of data in a u_int16_t */
+#define NS_INT8SZ      1       /* #/bytes of data in a u_int8_t */
+#define NS_INADDRSZ    4       /* IPv4 T_A */
+#define NS_IN6ADDRSZ   16      /* IPv6 T_AAAA */
+#define NS_CMPRSFLGS   0xc0    /* Flag bits indicating name compression. */
+#define NS_DEFAULTPORT 53      /* For both TCP and UDP. */
+
+/*
+ * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord()
+ * in synch with it.
+ */
+#define ns_sect res_9_ns_sect
+typedef enum __ns_sect {
+       ns_s_qd = 0,            /* Query: Question. */
+       ns_s_zn = 0,            /* Update: Zone. */
+       ns_s_an = 1,            /* Query: Answer. */
+       ns_s_pr = 1,            /* Update: Prerequisites. */
+       ns_s_ns = 2,            /* Query: Name servers. */
+       ns_s_ud = 2,            /* Update: Update. */
+       ns_s_ar = 3,            /* Query|Update: Additional records. */
+       ns_s_max = 4
+} ns_sect;
+
+/*
+ * This is a message handle.  It is caller allocated and has no dynamic data.
+ * This structure is intended to be opaque to all but ns_parse.c, thus the
+ * leading _'s on the member names.  Use the accessor functions, not the _'s.
+ */
+#define ns_msg res_9_ns_msg
+typedef struct __ns_msg {
+       const u_char    *_msg, *_eom;
+       u_int16_t       _id, _flags, _counts[ns_s_max];
+       const u_char    *_sections[ns_s_max];
+       ns_sect         _sect;
+       int             _rrnum;
+       const u_char    *_msg_ptr;
+} ns_msg;
+
+/* Private data structure - do not use from outside library. */
+#define _ns_flagdata _res_9_ns_flagdata
+struct _ns_flagdata {  int mask, shift;  };
+extern struct _ns_flagdata _ns_flagdata[];
+
+/* Accessor macros - this is part of the public interface. */
+
+#define ns_msg_id(handle) ((handle)._id + 0)
+#define ns_msg_base(handle) ((handle)._msg + 0)
+#define ns_msg_end(handle) ((handle)._eom + 0)
+#define ns_msg_size(handle) ((handle)._eom - (handle)._msg)
+#define ns_msg_count(handle, section) ((handle)._counts[section] + 0)
+
+/*
+ * This is a parsed record.  It is caller allocated and has no dynamic data.
+ */
+#define ns_rr res_9_ns_rr
+
+typedef        struct __ns_rr {
+       char            name[NS_MAXDNAME];
+       u_int16_t       type;
+       u_int16_t       rr_class;
+       u_int32_t       ttl;
+       u_int16_t       rdlength;
+       const u_char *  rdata;
+} ns_rr;
+
+/* Accessor macros - this is part of the public interface. */
+#define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".")
+#define ns_rr_type(rr) ((ns_type)((rr).type + 0))
+#define ns_rr_class(rr)        ((ns_class)((rr).rr_class + 0))
+#define ns_rr_ttl(rr)  ((rr).ttl + 0)
+#define ns_rr_rdlen(rr)        ((rr).rdlength + 0)
+#define ns_rr_rdata(rr)        ((rr).rdata + 0)
+
+/*
+ * These don't have to be in the same order as in the packet flags word,
+ * and they can even overlap in some cases, but they will need to be kept
+ * in synch with ns_parse.c:ns_flagdata[].
+ */
+#define ns_flag res_9_ns_flag
+typedef enum __ns_flag {
+       ns_f_qr,                /* Question/Response. */
+       ns_f_opcode,            /* Operation code. */
+       ns_f_aa,                /* Authoritative Answer. */
+       ns_f_tc,                /* Truncation occurred. */
+       ns_f_rd,                /* Recursion Desired. */
+       ns_f_ra,                /* Recursion Available. */
+       ns_f_z,                 /* MBZ. */
+       ns_f_ad,                /* Authentic Data (DNSSEC). */
+       ns_f_cd,                /* Checking Disabled (DNSSEC). */
+       ns_f_rcode,             /* Response code. */
+       ns_f_max
+} ns_flag;
+
+/*
+ * Currently defined opcodes.
+ */
+#define ns_opcode res_9_ns_opcode
+typedef enum __ns_opcode {
+       ns_o_query = 0,         /* Standard query. */
+       ns_o_iquery = 1,        /* Inverse query (deprecated/unsupported). */
+       ns_o_status = 2,        /* Name server status query (unsupported). */
+                               /* Opcode 3 is undefined/reserved. */
+       ns_o_notify = 4,        /* Zone change notification. */
+       ns_o_update = 5,        /* Zone update message. */
+       ns_o_max = 6
+} ns_opcode;
+
+/*
+ * Currently defined response codes.
+ */
+#define ns_rcode res_9_ns_rcode
+typedef        enum __ns_rcode {
+       ns_r_noerror = 0,       /* No error occurred. */
+       ns_r_formerr = 1,       /* Format error. */
+       ns_r_servfail = 2,      /* Server failure. */
+       ns_r_nxdomain = 3,      /* Name error. */
+       ns_r_notimpl = 4,       /* Unimplemented. */
+       ns_r_refused = 5,       /* Operation refused. */
+       /* these are for BIND_UPDATE */
+       ns_r_yxdomain = 6,      /* Name exists */
+       ns_r_yxrrset = 7,       /* RRset exists */
+       ns_r_nxrrset = 8,       /* RRset does not exist */
+       ns_r_notauth = 9,       /* Not authoritative for zone */
+       ns_r_notzone = 10,      /* Zone of record different from zone section */
+       ns_r_max = 11,
+       /* The following are EDNS extended rcodes */
+       ns_r_badvers = 16,
+       /* The following are TSIG errors */
+       ns_r_badsig = 16,
+       ns_r_badkey = 17,
+       ns_r_badtime = 18
+} ns_rcode;
+
+/* BIND_UPDATE */
+#define ns_update_operation res_9_ns_update_operation
+typedef enum __ns_update_operation {
+       ns_uop_delete = 0,
+       ns_uop_add = 1,
+       ns_uop_max = 2
+} ns_update_operation;
+
+/*
+ * This structure is used for TSIG authenticated messages
+ */
+#define ns_tsig_key res_9_ns_tsig_key
+struct ns_tsig_key {
+        char name[NS_MAXDNAME], alg[NS_MAXDNAME];
+        unsigned char *data;
+        int len;
+};
+typedef struct ns_tsig_key ns_tsig_key;
+
+/*
+ * This structure is used for TSIG authenticated TCP messages
+ */
+#define ns_tcp_tsig_state res_9_ns_tcp_tsig_state
+struct ns_tcp_tsig_state {
+       int counter;
+       struct dst_key *key;
+       void *ctx;
+       unsigned char sig[NS_PACKETSZ];
+       int siglen;
+};
+typedef struct ns_tcp_tsig_state ns_tcp_tsig_state;
+
+#define NS_TSIG_FUDGE 300
+#define NS_TSIG_TCP_COUNT 100
+#define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT"
+
+#define NS_TSIG_ERROR_NO_TSIG -10
+#define NS_TSIG_ERROR_NO_SPACE -11
+#define NS_TSIG_ERROR_FORMERR -12
+
+/*
+ * Currently defined type values for resources and queries.
+ */
+#define ns_typw res_9_ns_type
+typedef enum __ns_type {
+       ns_t_invalid = 0,       /* Cookie. */
+       ns_t_a = 1,             /* Host address. */
+       ns_t_ns = 2,            /* Authoritative server. */
+       ns_t_md = 3,            /* Mail destination. */
+       ns_t_mf = 4,            /* Mail forwarder. */
+       ns_t_cname = 5,         /* Canonical name. */
+       ns_t_soa = 6,           /* Start of authority zone. */
+       ns_t_mb = 7,            /* Mailbox domain name. */
+       ns_t_mg = 8,            /* Mail group member. */
+       ns_t_mr = 9,            /* Mail rename name. */
+       ns_t_null = 10,         /* Null resource record. */
+       ns_t_wks = 11,          /* Well known service. */
+       ns_t_ptr = 12,          /* Domain name pointer. */
+       ns_t_hinfo = 13,        /* Host information. */
+       ns_t_minfo = 14,        /* Mailbox information. */
+       ns_t_mx = 15,           /* Mail routing information. */
+       ns_t_txt = 16,          /* Text strings. */
+       ns_t_rp = 17,           /* Responsible person. */
+       ns_t_afsdb = 18,        /* AFS cell database. */
+       ns_t_x25 = 19,          /* X_25 calling address. */
+       ns_t_isdn = 20,         /* ISDN calling address. */
+       ns_t_rt = 21,           /* Router. */
+       ns_t_nsap = 22,         /* NSAP address. */
+       ns_t_nsap_ptr = 23,     /* Reverse NSAP lookup (deprecated). */
+       ns_t_sig = 24,          /* Security signature. */
+       ns_t_key = 25,          /* Security key. */
+       ns_t_px = 26,           /* X.400 mail mapping. */
+       ns_t_gpos = 27,         /* Geographical position (withdrawn). */
+       ns_t_aaaa = 28,         /* Ip6 Address. */
+       ns_t_loc = 29,          /* Location Information. */
+       ns_t_nxt = 30,          /* Next domain (security). */
+       ns_t_eid = 31,          /* Endpoint identifier. */
+       ns_t_nimloc = 32,       /* Nimrod Locator. */
+       ns_t_srv = 33,          /* Server Selection. */
+       ns_t_atma = 34,         /* ATM Address */
+       ns_t_naptr = 35,        /* Naming Authority PoinTeR */
+       ns_t_kx = 36,           /* Key Exchange */
+       ns_t_cert = 37,         /* Certification record */
+       ns_t_a6 = 38,           /* IPv6 address (deprecates AAAA) */
+       ns_t_dname = 39,        /* Non-terminal DNAME (for IPv6) */
+       ns_t_sink = 40,         /* Kitchen sink (experimentatl) */
+       ns_t_opt = 41,          /* EDNS0 option (meta-RR) */
+       ns_t_tkey = 249,        /* Transaction key */
+       ns_t_tsig = 250,        /* Transaction signature. */
+       ns_t_ixfr = 251,        /* Incremental zone transfer. */
+       ns_t_axfr = 252,        /* Transfer zone of authority. */
+       ns_t_mailb = 253,       /* Transfer mailbox records. */
+       ns_t_maila = 254,       /* Transfer mail agent records. */
+       ns_t_any = 255,         /* Wildcard match. */
+       ns_t_zxfr = 256,        /* BIND-specific, nonstandard. */
+       ns_t_max = 65536
+} ns_type;
+
+/* Exclusively a QTYPE? (not also an RTYPE) */
+#define        ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \
+                     (t) == ns_t_mailb || (t) == ns_t_maila)
+/* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */
+#define        ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt)
+/* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */
+#define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t))
+#define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr)
+#define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \
+                      (t) == ns_t_zxfr)
+
+/*
+ * Values for class field
+ */
+#define ns_class res_9_ns_class
+typedef enum __ns_class {
+       ns_c_invalid = 0,       /* Cookie. */
+       ns_c_in = 1,            /* Internet. */
+       ns_c_2 = 2,             /* unallocated/unsupported. */
+       ns_c_chaos = 3,         /* MIT Chaos-net. */
+       ns_c_hs = 4,            /* MIT Hesiod. */
+       /* Query class values which do not appear in resource records */
+       ns_c_none = 254,        /* for prereq. sections in update requests */
+       ns_c_any = 255,         /* Wildcard match. */
+       ns_c_max = 65536
+} ns_class;
+
+/* DNSSEC constants. */
+
+#define ns_key_types res_9_ns_key_types
+typedef enum __ns_key_types {
+       ns_kt_rsa = 1,          /* key type RSA/MD5 */
+       ns_kt_dh  = 2,          /* Diffie Hellman */
+       ns_kt_dsa = 3,          /* Digital Signature Standard (MANDATORY) */
+       ns_kt_private = 254     /* Private key type starts with OID */
+} ns_key_types;
+
+#define ns_cert_types res_9_ns_cert_types
+typedef enum __ns_cert_types {
+       cert_t_pkix = 1,        /* PKIX (X.509v3) */
+       cert_t_spki = 2,        /* SPKI */
+       cert_t_pgp  = 3,        /* PGP */
+       cert_t_url  = 253,      /* URL private type */
+       cert_t_oid  = 254       /* OID private type */
+} ns_cert_types;
+
+/* Flags field of the KEY RR rdata. */
+#define        NS_KEY_TYPEMASK         0xC000  /* Mask for "type" bits */
+#define        NS_KEY_TYPE_AUTH_CONF   0x0000  /* Key usable for both */
+#define        NS_KEY_TYPE_CONF_ONLY   0x8000  /* Key usable for confidentiality */
+#define        NS_KEY_TYPE_AUTH_ONLY   0x4000  /* Key usable for authentication */
+#define        NS_KEY_TYPE_NO_KEY      0xC000  /* No key usable for either; no key */
+/* The type bits can also be interpreted independently, as single bits: */
+#define        NS_KEY_NO_AUTH          0x8000  /* Key unusable for authentication */
+#define        NS_KEY_NO_CONF          0x4000  /* Key unusable for confidentiality */
+#define        NS_KEY_RESERVED2        0x2000  /* Security is *mandatory* if bit=0 */
+#define        NS_KEY_EXTENDED_FLAGS   0x1000  /* reserved - must be zero */
+#define        NS_KEY_RESERVED4        0x0800  /* reserved - must be zero */
+#define        NS_KEY_RESERVED5        0x0400  /* reserved - must be zero */
+#define        NS_KEY_NAME_TYPE        0x0300  /* these bits determine the type */
+#define        NS_KEY_NAME_USER        0x0000  /* key is assoc. with user */
+#define        NS_KEY_NAME_ENTITY      0x0200  /* key is assoc. with entity eg host */
+#define        NS_KEY_NAME_ZONE        0x0100  /* key is zone key */
+#define        NS_KEY_NAME_RESERVED    0x0300  /* reserved meaning */
+#define        NS_KEY_RESERVED8        0x0080  /* reserved - must be zero */
+#define        NS_KEY_RESERVED9        0x0040  /* reserved - must be zero */
+#define        NS_KEY_RESERVED10       0x0020  /* reserved - must be zero */
+#define        NS_KEY_RESERVED11       0x0010  /* reserved - must be zero */
+#define        NS_KEY_SIGNATORYMASK    0x000F  /* key can sign RR's of same name */
+#define        NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \
+                                 NS_KEY_RESERVED4 | \
+                                 NS_KEY_RESERVED5 | \
+                                 NS_KEY_RESERVED8 | \
+                                 NS_KEY_RESERVED9 | \
+                                 NS_KEY_RESERVED10 | \
+                                 NS_KEY_RESERVED11 )
+#define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */
+
+/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */
+#define        NS_ALG_MD5RSA           1       /* MD5 with RSA */
+#define        NS_ALG_DH               2       /* Diffie Hellman KEY */
+#define        NS_ALG_DSA              3       /* DSA KEY */
+#define        NS_ALG_DSS              NS_ALG_DSA
+#define        NS_ALG_EXPIRE_ONLY      253     /* No alg, no security */
+#define        NS_ALG_PRIVATE_OID      254     /* Key begins with OID giving alg */
+
+/* Protocol values  */
+/* value 0 is reserved */
+#define NS_KEY_PROT_TLS         1
+#define NS_KEY_PROT_EMAIL       2
+#define NS_KEY_PROT_DNSSEC      3
+#define NS_KEY_PROT_IPSEC       4
+#define NS_KEY_PROT_ANY                255
+
+/* Signatures */
+#define        NS_MD5RSA_MIN_BITS       512    /* Size of a mod or exp in bits */
+#define        NS_MD5RSA_MAX_BITS      2552
+       /* Total of binary mod and exp */
+#define        NS_MD5RSA_MAX_BYTES     ((NS_MD5RSA_MAX_BITS+7/8)*2+3)
+       /* Max length of text sig block */
+#define        NS_MD5RSA_MAX_BASE64    (((NS_MD5RSA_MAX_BYTES+2)/3)*4)
+#define NS_MD5RSA_MIN_SIZE     ((NS_MD5RSA_MIN_BITS+7)/8)
+#define NS_MD5RSA_MAX_SIZE     ((NS_MD5RSA_MAX_BITS+7)/8)
+
+#define NS_DSA_SIG_SIZE         41
+#define NS_DSA_MIN_SIZE         213
+#define NS_DSA_MAX_BYTES        405
+
+/* Offsets into SIG record rdata to find various values */
+#define        NS_SIG_TYPE     0       /* Type flags */
+#define        NS_SIG_ALG      2       /* Algorithm */
+#define        NS_SIG_LABELS   3       /* How many labels in name */
+#define        NS_SIG_OTTL     4       /* Original TTL */
+#define        NS_SIG_EXPIR    8       /* Expiration time */
+#define        NS_SIG_SIGNED   12      /* Signature time */
+#define        NS_SIG_FOOT     16      /* Key footprint */
+#define        NS_SIG_SIGNER   18      /* Domain name of who signed it */
+
+/* How RR types are represented as bit-flags in NXT records */
+#define        NS_NXT_BITS 8
+#define        NS_NXT_BIT_SET(  n,p) (p[(n)/NS_NXT_BITS] |=  (0x80>>((n)%NS_NXT_BITS)))
+#define        NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS)))
+#define        NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] &   (0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_MAX 127
+
+/*
+ * EDNS0 extended flags, host order.
+ */
+#define NS_OPT_DNSSEC_OK       0x8000U
+
+/*
+ * Inline versions of get/put short/long.  Pointer is advanced.
+ */
+#define NS_GET16(s, cp) do { \
+       register const u_char *t_cp = (const u_char *)(cp); \
+       (s) = ((u_int16_t)t_cp[0] << 8) \
+           | ((u_int16_t)t_cp[1]) \
+           ; \
+       (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_GET32(l, cp) do { \
+       register const u_char *t_cp = (const u_char *)(cp); \
+       (l) = ((u_int32_t)t_cp[0] << 24) \
+           | ((u_int32_t)t_cp[1] << 16) \
+           | ((u_int32_t)t_cp[2] << 8) \
+           | ((u_int32_t)t_cp[3]) \
+           ; \
+       (cp) += NS_INT32SZ; \
+} while (0)
+
+#define NS_PUT16(s, cp) do { \
+       register u_int16_t t_s = (u_int16_t)(s); \
+       register u_char *t_cp = (u_char *)(cp); \
+       *t_cp++ = t_s >> 8; \
+       *t_cp   = t_s; \
+       (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_PUT32(l, cp) do { \
+       register u_int32_t t_l = (u_int32_t)(l); \
+       register u_char *t_cp = (u_char *)(cp); \
+       *t_cp++ = t_l >> 24; \
+       *t_cp++ = t_l >> 16; \
+       *t_cp++ = t_l >> 8; \
+       *t_cp   = t_l; \
+       (cp) += NS_INT32SZ; \
+} while (0)
+
+/*
+ * ANSI C identifier hiding for bind's lib/nameser.
+ */
+#define        ns_msg_getflag          res_9_ns_msg_getflag
+#define ns_get16               res_9_ns_get16
+#define ns_get32               res_9_ns_get32
+#define ns_put16               res_9_ns_put16
+#define ns_put32               res_9_ns_put32
+#define ns_initparse           res_9_ns_initparse
+#define ns_skiprr              res_9_ns_skiprr
+#define ns_parserr             res_9_ns_parserr
+#define        ns_sprintrr             res_9_ns_sprintrr
+#define        ns_sprintrrf            res_9_ns_sprintrrf
+#define        ns_format_ttl           res_9_ns_format_ttl
+#define        ns_parse_ttl            res_9_ns_parse_ttl
+#define ns_datetosecs          res_9_ns_datetosecs
+#define        ns_name_ntol            res_9_ns_name_ntol
+#define        ns_name_ntop            res_9_ns_name_ntop
+#define        ns_name_pton            res_9_ns_name_pton
+#define        ns_name_unpack          res_9_ns_name_unpack
+#define        ns_name_pack            res_9_ns_name_pack
+#define        ns_name_compress        res_9_ns_name_compress
+#define        ns_name_uncompress      res_9_ns_name_uncompress
+#define        ns_name_skip            res_9_ns_name_skip
+#define        ns_name_rollback        res_9_ns_name_rollback
+#define        ns_sign                 res_9_ns_sign
+#define        ns_sign2                res_9_ns_sign2
+#define        ns_sign_tcp             res_9_ns_sign_tcp
+#define        ns_sign_tcp2            res_9_ns_sign_tcp2
+#define        ns_sign_tcp_init        res_9_ns_sign_tcp_init
+#define ns_find_tsig           res_9_ns_find_tsig
+#define        ns_verify               res_9_ns_verify
+#define        ns_verify_tcp           res_9_ns_verify_tcp
+#define        ns_verify_tcp_init      res_9_ns_verify_tcp_init
+#define        ns_samedomain           res_9_ns_samedomain
+#define        ns_subdomain            res_9_ns_subdomain
+#define        ns_makecanon            res_9_ns_makecanon
+#define        ns_samename             res_9_ns_samename
+
+__BEGIN_DECLS
+int            ns_msg_getflag __P((ns_msg, int));
+u_int          ns_get16 __P((const u_char *));
+u_long         ns_get32 __P((const u_char *));
+void           ns_put16 __P((u_int, u_char *));
+void           ns_put32 __P((u_long, u_char *));
+int            ns_initparse __P((const u_char *, int, ns_msg *));
+int            ns_skiprr __P((const u_char *, const u_char *, ns_sect, int));
+int            ns_parserr __P((ns_msg *, ns_sect, int, ns_rr *));
+int            ns_sprintrr __P((const ns_msg *, const ns_rr *,
+                                const char *, const char *, char *, size_t));
+int            ns_sprintrrf __P((const u_char *, size_t, const char *,
+                                 ns_class, ns_type, u_long, const u_char *,
+                                 size_t, const char *, const char *,
+                                 char *, size_t));
+int            ns_format_ttl __P((u_long, char *, size_t));
+int            ns_parse_ttl __P((const char *, u_long *));
+u_int32_t      ns_datetosecs __P((const char *cp, int *errp));
+int            ns_name_ntol __P((const u_char *, u_char *, size_t));
+int            ns_name_ntop __P((const u_char *, char *, size_t));
+int            ns_name_pton __P((const char *, u_char *, size_t));
+int            ns_name_unpack __P((const u_char *, const u_char *,
+                                   const u_char *, u_char *, size_t));
+int            ns_name_pack __P((const u_char *, u_char *, int,
+                                 const u_char **, const u_char **));
+int            ns_name_uncompress __P((const u_char *, const u_char *,
+                                       const u_char *, char *, size_t));
+int            ns_name_compress __P((const char *, u_char *, size_t,
+                                     const u_char **, const u_char **));
+int            ns_name_skip __P((const u_char **, const u_char *));
+void           ns_name_rollback __P((const u_char *, const u_char **,
+                                     const u_char **));
+int            ns_sign __P((u_char *, int *, int, int, void *,
+                            const u_char *, int, u_char *, int *, time_t));
+int            ns_sign2 __P((u_char *, int *, int, int, void *,
+                             const u_char *, int, u_char *, int *, time_t,
+                             u_char **, u_char **));
+int            ns_sign_tcp __P((u_char *, int *, int, int,
+                                ns_tcp_tsig_state *, int));
+int            ns_sign_tcp2 __P((u_char *, int *, int, int,
+                                 ns_tcp_tsig_state *, int,
+                                 u_char **, u_char **));
+int            ns_sign_tcp_init __P((void *, const u_char *, int,
+                                       ns_tcp_tsig_state *));
+u_char         *ns_find_tsig __P((u_char *, u_char *));
+int            ns_verify __P((u_char *, int *, void *,
+                              const u_char *, int, u_char *, int *,
+                              time_t *, int));
+int            ns_verify_tcp __P((u_char *, int *, ns_tcp_tsig_state *, int));
+int            ns_verify_tcp_init __P((void *, const u_char *, int,
+                                       ns_tcp_tsig_state *));
+int            ns_samedomain __P((const char *, const char *));
+int            ns_subdomain __P((const char *, const char *));
+int            ns_makecanon __P((const char *, char *, size_t));
+int            ns_samename __P((const char *, const char *));
+__END_DECLS
+
+#endif /* !BIND_8_COMPAT */
+#endif /* !_NAMESER_9_H_ */
diff --git a/ns_date.c b/ns_date.c
new file mode 100644 (file)
index 0000000..89ed7d1
--- /dev/null
+++ b/ns_date.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_date.c,v 1.1 2006/03/01 19:01:36 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Forward. */
+
+static int     datepart(const char *, int, int, int, int *);
+
+/* Public. */
+
+/* Convert a date in ASCII into the number of seconds since
+   1 January 1970 (GMT assumed).  Format is yyyymmddhhmmss, all
+   digits required, no spaces allowed.  */
+
+u_int32_t
+ns_datetosecs(const char *cp, int *errp) {
+       struct tm time;
+       u_int32_t result;
+       int mdays, i;
+       static const int days_per_month[12] =
+               {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+       if (strlen(cp) != 14) {
+               *errp = 1;
+               return (0);
+       }
+       *errp = 0;
+
+       memset(&time, 0, sizeof time);
+       time.tm_year  = datepart(cp +  0, 4, 1990, 9999, errp) - 1900;
+       time.tm_mon   = datepart(cp +  4, 2,   01,   12, errp) - 1;
+       time.tm_mday  = datepart(cp +  6, 2,   01,   31, errp);
+       time.tm_hour  = datepart(cp +  8, 2,   00,   23, errp);
+       time.tm_min   = datepart(cp + 10, 2,   00,   59, errp);
+       time.tm_sec   = datepart(cp + 12, 2,   00,   59, errp);
+       if (*errp)              /* Any parse errors? */
+               return (0);
+
+       /* 
+        * OK, now because timegm() is not available in all environments,
+        * we will do it by hand.  Roll up sleeves, curse the gods, begin!
+        */
+
+#define SECS_PER_DAY    ((u_int32_t)24*60*60)
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+
+       result  = time.tm_sec;                          /* Seconds */
+       result += time.tm_min * 60;                     /* Minutes */
+       result += time.tm_hour * (60*60);               /* Hours */
+       result += (time.tm_mday - 1) * SECS_PER_DAY;    /* Days */
+
+       /* Months are trickier.  Look without leaping, then leap */
+       mdays = 0;
+       for (i = 0; i < time.tm_mon; i++)
+               mdays += days_per_month[i];
+       result += mdays * SECS_PER_DAY;                 /* Months */
+       if (time.tm_mon > 1 && isleap(1900+time.tm_year))
+               result += SECS_PER_DAY;         /* Add leapday for this year */
+
+       /* First figure years without leapdays, then add them in.  */
+       /* The loop is slow, FIXME, but simple and accurate.  */
+       result += (time.tm_year - 70) * (SECS_PER_DAY*365); /* Years */
+       for (i = 70; i < time.tm_year; i++)
+               if (isleap(1900+i))
+                       result += SECS_PER_DAY; /* Add leapday for prev year */
+
+       return (result);
+}
+
+/* Private. */
+
+/*
+ * Parse part of a date.  Set error flag if any error.
+ * Don't reset the flag if there is no error.
+ */
+static int
+datepart(const char *buf, int size, int min, int max, int *errp) {
+       int result = 0;
+       int i;
+
+       for (i = 0; i < size; i++) {
+               if (!isdigit((unsigned char)(buf[i])))
+                       *errp = 1;
+               result = (result * 10) + buf[i] - '0';
+       }
+       if (result < min)
+               *errp = 1;
+       if (result > max)
+               *errp = 1;
+       return (result);
+}
diff --git a/ns_name.c b/ns_name.c
new file mode 100644 (file)
index 0000000..07b0739
--- /dev/null
+++ b/ns_name.c
@@ -0,0 +1,949 @@
+/*
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_name.c,v 1.1 2006/03/01 19:01:36 majka Exp $";
+#endif
+#endif
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+#define NS_TYPE_ELT                    0x40 /* EDNS0 extended label type */
+#define DNS_LABELTYPE_BITSTRING                0x41
+
+/* Data. */
+
+static const char      digits[] = "0123456789";
+
+static const char digitvalue[256] = {
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
+        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
+       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
+       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
+};
+
+/* Forward. */
+
+static int             special(int);
+static int             printable(int);
+static int             dn_find(const u_char *, const u_char *,
+                               const u_char * const *,
+                               const u_char * const *);
+static int             encode_bitsring(const char **, const char *,
+                                       char **, char **, const char *);
+static int             labellen(const u_char *);
+static int             decode_bitstring(const char **, char *, const char *);
+
+/* Public. */
+
+/*
+ * ns_name_ntop(src, dst, dstsiz)
+ *     Convert an encoded domain name to printable ascii as per RFC1035.
+ * return:
+ *     Number of bytes written to buffer, or -1 (with errno set)
+ * notes:
+ *     The root is returned as "."
+ *     All other domains are returned in non absolute form
+ */
+int
+ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
+{
+       const u_char *cp;
+       char *dn, *eom;
+       u_char c;
+       u_int n;
+       int l;
+
+       cp = src;
+       dn = dst;
+       eom = dst + dstsiz;
+
+       while ((n = *cp++) != 0) {
+               if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       /* Some kind of compression pointer. */
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               if (dn != dst) {
+                       if (dn >= eom) {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       *dn++ = '.';
+               }
+               if ((l = labellen(cp - 1)) < 0) {
+                       errno = EMSGSIZE; /* XXX */
+                       return(-1);
+               }
+               if (dn + l >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+                       int m;
+
+                       if (n != DNS_LABELTYPE_BITSTRING) {
+                               /* XXX: labellen should reject this case */
+                               errno = EINVAL;
+                               return(-1);
+                       }
+                       if ((m = decode_bitstring((const char **)&cp, dn, eom)) < 0)
+                       {
+                               errno = EMSGSIZE;
+                               return(-1);
+                       }
+                       dn += m; 
+                       continue;
+               }
+               for ((void)NULL; l > 0; l--) {
+                       c = *cp++;
+                       if (special(c)) {
+                               if (dn + 1 >= eom) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               *dn++ = '\\';
+                               *dn++ = (char)c;
+                       } else if (!printable(c)) {
+                               if (dn + 3 >= eom) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               *dn++ = '\\';
+                               *dn++ = digits[c / 100];
+                               *dn++ = digits[(c % 100) / 10];
+                               *dn++ = digits[c % 10];
+                       } else {
+                               if (dn >= eom) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               *dn++ = (char)c;
+                       }
+               }
+       }
+       if (dn == dst) {
+               if (dn >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               *dn++ = '.';
+       }
+       if (dn >= eom) {
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       *dn++ = '\0';
+       return (dn - dst);
+}
+
+/*
+ * ns_name_pton(src, dst, dstsiz)
+ *     Convert a ascii string into an encoded domain name as per RFC1035.
+ * return:
+ *     -1 if it fails
+ *     1 if string was fully qualified
+ *     0 is string was not fully qualified
+ * notes:
+ *     Enforces label and domain length limits.
+ */
+
+int
+ns_name_pton(const char *src, u_char *dst, size_t dstsiz)
+{
+       u_char *label, *bp, *eom;
+       int c, n, escaped, e = 0;
+       char *cp;
+
+       escaped = 0;
+       bp = dst;
+       eom = dst + dstsiz;
+       label = bp++;
+
+       while ((c = *src++) != 0) {
+               if (escaped) {
+                       if (c == '[') { /* start a bit string label */
+                               if ((cp = strchr(src, ']')) == NULL) {
+                                       errno = EINVAL; /* ??? */
+                                       return(-1);
+                               }
+                               if ((e = encode_bitsring(&src,
+                                                        cp + 2,
+                                                        (char **)&label,
+                                                        (char **)&bp,
+                                                        (const char *)eom))
+                                   != 0) {
+                                       errno = e;
+                                       return(-1);
+                               }
+                               escaped = 0;
+                               label = bp++;
+                               if ((c = *src++) == 0)
+                                       goto done;
+                               else if (c != '.') {
+                                       errno = EINVAL;
+                                       return(-1);
+                               }
+                               continue;
+                       }
+                       else if ((cp = strchr(digits, c)) != NULL) {
+                               n = (cp - digits) * 100;
+                               if ((c = *src++) == 0 ||
+                                   (cp = strchr(digits, c)) == NULL) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               n += (cp - digits) * 10;
+                               if ((c = *src++) == 0 ||
+                                   (cp = strchr(digits, c)) == NULL) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               n += (cp - digits);
+                               if (n > 255) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               c = n;
+                       }
+                       escaped = 0;
+               } else if (c == '\\') {
+                       escaped = 1;
+                       continue;
+               } else if (c == '.') {
+                       c = (bp - label - 1);
+                       if ((c & NS_CMPRSFLGS) != 0) {  /* Label too big. */
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       if (label >= eom) {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       *label = c;
+                       /* Fully qualified ? */
+                       if (*src == '\0') {
+                               if (c != 0) {
+                                       if (bp >= eom) {
+                                               errno = EMSGSIZE;
+                                               return (-1);
+                                       }
+                                       *bp++ = '\0';
+                               }
+                               if ((bp - dst) > NS_MAXCDNAME) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               return (1);
+                       }
+                       if (c == 0 || *src == '.') {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       label = bp++;
+                       continue;
+               }
+               if (bp >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               *bp++ = (u_char)c;
+       }
+       c = (bp - label - 1);
+       if ((c & NS_CMPRSFLGS) != 0) {          /* Label too big. */
+               errno = EMSGSIZE;
+               return (-1);
+       }
+  done:
+       if (label >= eom) {
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       *label = c;
+       if (c != 0) {
+               if (bp >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               *bp++ = 0;
+       }
+       if ((bp - dst) > NS_MAXCDNAME) {        /* src too big */
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       return (0);
+}
+
+/*
+ * ns_name_ntol(src, dst, dstsiz)
+ *     Convert a network strings labels into all lowercase.
+ * return:
+ *     Number of bytes written to buffer, or -1 (with errno set)
+ * notes:
+ *     Enforces label and domain length limits.
+ */
+
+int
+ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
+{
+       const u_char *cp;
+       u_char *dn, *eom;
+       u_char c;
+       u_int n;
+       int l;
+
+       cp = src;
+       dn = dst;
+       eom = dst + dstsiz;
+
+       while ((n = *cp++) != 0) {
+               if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       /* Some kind of compression pointer. */
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               *dn++ = n;
+               if ((l = labellen(cp - 1)) < 0) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               if (dn + l >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               for ((void)NULL; l > 0; l--) {
+                       c = *cp++;
+                       if (isupper(c))
+                               *dn++ = tolower(c);
+                       else
+                               *dn++ = c;
+               }
+       }
+       *dn++ = '\0';
+       return (dn - dst);
+}
+
+/*
+ * ns_name_unpack(msg, eom, src, dst, dstsiz)
+ *     Unpack a domain name from a message, source may be compressed.
+ * return:
+ *     -1 if it fails, or consumed octets if it succeeds.
+ */
+int
+ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
+              u_char *dst, size_t dstsiz)
+{
+       const u_char *srcp, *dstlim;
+       u_char *dstp;
+       int n, len, checked, l;
+
+       len = -1;
+       checked = 0;
+       dstp = dst;
+       srcp = src;
+       dstlim = dst + dstsiz;
+       if (srcp < msg || srcp >= eom) {
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       /* Fetch next label in domain name. */
+       while ((n = *srcp++) != 0) {
+               /* Check for indirection. */
+               switch (n & NS_CMPRSFLGS) {
+               case 0:
+               case NS_TYPE_ELT:
+                       /* Limit checks. */
+                       if ((l = labellen(srcp - 1)) < 0) {
+                               errno = EMSGSIZE;
+                               return(-1);
+                       }
+                       if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       checked += l + 1;
+                       *dstp++ = n;
+                       memcpy(dstp, srcp, l);
+                       dstp += l;
+                       srcp += l;
+                       break;
+
+               case NS_CMPRSFLGS:
+                       if (srcp >= eom) {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       if (len < 0)
+                               len = srcp - src + 1;
+                       srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
+                       if (srcp < msg || srcp >= eom) {  /* Out of range. */
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       checked += 2;
+                       /*
+                        * Check for loops in the compressed name;
+                        * if we've looked at the whole message,
+                        * there must be a loop.
+                        */
+                       if (checked >= eom - msg) {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       break;
+
+               default:
+                       errno = EMSGSIZE;
+                       return (-1);                    /* flag error */
+               }
+       }
+       *dstp = '\0';
+       if (len < 0)
+               len = srcp - src;
+       return (len);
+}
+
+/*
+ * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
+ *     Pack domain name 'domain' into 'comp_dn'.
+ * return:
+ *     Size of the compressed name, or -1.
+ * notes:
+ *     'dnptrs' is an array of pointers to previous compressed names.
+ *     dnptrs[0] is a pointer to the beginning of the message. The array
+ *     ends with NULL.
+ *     'lastdnptr' is a pointer to the end of the array pointed to
+ *     by 'dnptrs'.
+ * Side effects:
+ *     The list of pointers in dnptrs is updated for labels inserted into
+ *     the message as we compress the name.  If 'dnptr' is NULL, we don't
+ *     try to compress names. If 'lastdnptr' is NULL, we don't update the
+ *     list.
+ */
+int
+ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
+            const u_char **dnptrs, const u_char **lastdnptr)
+{
+       u_char *dstp;
+       const u_char **cpp, **lpp, *eob, *msg;
+       const u_char *srcp;
+       int n, l, first = 1;
+
+       srcp = src;
+       dstp = dst;
+       eob = dstp + dstsiz;
+       lpp = cpp = NULL;
+       if (dnptrs != NULL) {
+               if ((msg = *dnptrs++) != NULL) {
+                       for (cpp = dnptrs; *cpp != NULL; cpp++)
+                               (void)NULL;
+                       lpp = cpp;      /* end of list to search */
+               }
+       } else
+               msg = NULL;
+
+       /* make sure the domain we are about to add is legal */
+       l = 0;
+       do {
+               int l0;
+
+               n = *srcp;
+               if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               if ((l0 = labellen(srcp)) < 0) {
+                       errno = EINVAL;
+                       return(-1);
+               }
+               l += l0 + 1;
+               if (l > NS_MAXCDNAME) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               srcp += l0 + 1;
+       } while (n != 0);
+
+       /* from here on we need to reset compression pointer array on error */
+       srcp = src;
+       do {
+               /* Look to see if we can use pointers. */
+               n = *srcp;
+               if (n != 0 && msg != NULL) {
+                       l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
+                                   (const u_char * const *)lpp);
+                       if (l >= 0) {
+                               if (dstp + 1 >= eob) {
+                                       goto cleanup;
+                               }
+                               *dstp++ = (l >> 8) | NS_CMPRSFLGS;
+                               *dstp++ = l % 256;
+                               return (dstp - dst);
+                       }
+                       /* Not found, save it. */
+                       if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
+                           (dstp - msg) < 0x4000 && first) {
+                               *cpp++ = dstp;
+                               *cpp = NULL;
+                               first = 0;
+                       }
+               }
+               /* copy label to buffer */
+               if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       /* Should not happen. */
+                       goto cleanup;
+               }
+               n = labellen(srcp);
+               if (dstp + 1 + n >= eob) {
+                       goto cleanup;
+               }
+               memcpy(dstp, srcp, n + 1);
+               srcp += n + 1;
+               dstp += n + 1;
+       } while (n != 0);
+
+       if (dstp > eob) {
+cleanup:
+               if (msg != NULL)
+                       *lpp = NULL;
+               errno = EMSGSIZE;
+               return (-1);
+       } 
+       return (dstp - dst);
+}
+
+/*
+ * ns_name_uncompress(msg, eom, src, dst, dstsiz)
+ *     Expand compressed domain name to presentation format.
+ * return:
+ *     Number of bytes read out of `src', or -1 (with errno set).
+ * note:
+ *     Root domain returns as "." not "".
+ */
+int
+ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
+                  char *dst, size_t dstsiz)
+{
+       u_char tmp[NS_MAXCDNAME];
+       int n;
+       
+       if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
+               return (-1);
+       if (ns_name_ntop(tmp, dst, dstsiz) == -1)
+               return (-1);
+       return (n);
+}
+
+/*
+ * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
+ *     Compress a domain name into wire format, using compression pointers.
+ * return:
+ *     Number of bytes consumed in `dst' or -1 (with errno set).
+ * notes:
+ *     'dnptrs' is an array of pointers to previous compressed names.
+ *     dnptrs[0] is a pointer to the beginning of the message.
+ *     The list ends with NULL.  'lastdnptr' is a pointer to the end of the
+ *     array pointed to by 'dnptrs'. Side effect is to update the list of
+ *     pointers for labels inserted into the message as we compress the name.
+ *     If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
+ *     is NULL, we don't update the list.
+ */
+int
+ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
+                const u_char **dnptrs, const u_char **lastdnptr)
+{
+       u_char tmp[NS_MAXCDNAME];
+
+       if (ns_name_pton(src, tmp, sizeof tmp) == -1)
+               return (-1);
+       return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
+}
+
+/*
+ * Reset dnptrs so that there are no active references to pointers at or
+ * after src.
+ */
+void
+ns_name_rollback(const u_char *src, const u_char **dnptrs,
+                const u_char **lastdnptr)
+{
+       while (dnptrs < lastdnptr && *dnptrs != NULL) {
+               if (*dnptrs >= src) {
+                       *dnptrs = NULL;
+                       break;
+               }
+               dnptrs++;
+       }
+}
+
+/*
+ * ns_name_skip(ptrptr, eom)
+ *     Advance *ptrptr to skip over the compressed name it points at.
+ * return:
+ *     0 on success, -1 (with errno set) on failure.
+ */
+int
+ns_name_skip(const u_char **ptrptr, const u_char *eom)
+{
+       const u_char *cp;
+       u_int n;
+       int l;
+
+       cp = *ptrptr;
+       while (cp < eom && (n = *cp++) != 0) {
+               /* Check for indirection. */
+               switch (n & NS_CMPRSFLGS) {
+               case 0:                 /* normal case, n == len */
+                       cp += n;
+                       continue;
+               case NS_TYPE_ELT: /* EDNS0 extended label */
+                       if ((l = labellen(cp - 1)) < 0) {
+                               errno = EMSGSIZE; /* XXX */
+                               return(-1);
+                       }
+                       cp += l;
+                       continue;
+               case NS_CMPRSFLGS:      /* indirection */
+                       cp++;
+                       break;
+               default:                /* illegal type */
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               break;
+       }
+       if (cp > eom) {
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       *ptrptr = cp;
+       return (0);
+}
+
+/* Private. */
+
+/*
+ * special(ch)
+ *     Thinking in noninternationalized USASCII (per the DNS spec),
+ *     is this characted special ("in need of quoting") ?
+ * return:
+ *     boolean.
+ */
+static int
+special(int ch) {
+       switch (ch) {
+       case 0x22: /* '"' */
+       case 0x2E: /* '.' */
+       case 0x3B: /* ';' */
+       case 0x5C: /* '\\' */
+       case 0x28: /* '(' */
+       case 0x29: /* ')' */
+       /* Special modifiers in zone files. */
+       case 0x40: /* '@' */
+       case 0x24: /* '$' */
+               return (1);
+       default:
+               return (0);
+       }
+}
+
+/*
+ * printable(ch)
+ *     Thinking in noninternationalized USASCII (per the DNS spec),
+ *     is this character visible and not a space when printed ?
+ * return:
+ *     boolean.
+ */
+static int
+printable(int ch) {
+       return (ch > 0x20 && ch < 0x7f);
+}
+
+/*
+ *     Thinking in noninternationalized USASCII (per the DNS spec),
+ *     convert this character to lower case if it's upper case.
+ */
+static int
+mklower(int ch) {
+       if (ch >= 0x41 && ch <= 0x5A)
+               return (ch + 0x20);
+       return (ch);
+}
+
+/*
+ * dn_find(domain, msg, dnptrs, lastdnptr)
+ *     Search for the counted-label name in an array of compressed names.
+ * return:
+ *     offset from msg if found, or -1.
+ * notes:
+ *     dnptrs is the pointer to the first name on the list,
+ *     not the pointer to the start of the message.
+ */
+static int
+dn_find(const u_char *domain, const u_char *msg,
+       const u_char * const *dnptrs,
+       const u_char * const *lastdnptr)
+{
+       const u_char *dn, *cp, *sp;
+       const u_char * const *cpp;
+       u_int n;
+
+       for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
+               sp = *cpp;
+               /*
+                * terminate search on:
+                * root label
+                * compression pointer
+                * unusable offset
+                */
+               while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
+                      (sp - msg) < 0x4000) {
+                       dn = domain;
+                       cp = sp;
+                       while ((n = *cp++) != 0) {
+                               /*
+                                * check for indirection
+                                */
+                               switch (n & NS_CMPRSFLGS) {
+                               case 0:         /* normal case, n == len */
+                                       n = labellen(cp - 1); /* XXX */
+
+                                       if (n != *dn++)
+                                               goto next;
+
+                                       for ((void)NULL; n > 0; n--)
+                                               if (mklower(*dn++) !=
+                                                   mklower(*cp++))
+                                                       goto next;
+                                       /* Is next root for both ? */
+                                       if (*dn == '\0' && *cp == '\0')
+                                               return (sp - msg);
+                                       if (*dn)
+                                               continue;
+                                       goto next;
+                               case NS_CMPRSFLGS:      /* indirection */
+                                       cp = msg + (((n & 0x3f) << 8) | *cp);
+                                       break;
+
+                               default:        /* illegal type */
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                       }
+ next: ;
+                       sp += *sp + 1;
+               }
+       }
+       errno = ENOENT;
+       return (-1);
+}
+
+static int
+decode_bitstring(const char **cpp, char *dn, const char *eom)
+{
+       const char *cp = *cpp;
+       char *beg = dn, tc;
+       int b, blen, plen;
+
+       if ((blen = (*cp & 0xff)) == 0)
+               blen = 256;
+       plen = (blen + 3) / 4;
+       plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
+       if (dn + plen >= eom)
+               return(-1);
+
+       cp++;
+       dn += SPRINTF((dn, "\\[x"));
+       for (b = blen; b > 7; b -= 8, cp++)
+               dn += SPRINTF((dn, "%02x", *cp & 0xff));
+       if (b > 4) {
+               tc = *cp++;
+               dn += SPRINTF((dn, "%02x", tc & (0xff << (8 - b))));
+       } else if (b > 0) {
+               tc = *cp++;
+               dn += SPRINTF((dn, "%1x",
+                              ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); 
+       }
+       dn += SPRINTF((dn, "/%d]", blen));
+
+       *cpp = cp;
+       return(dn - beg);
+}
+
+static int
+encode_bitsring(const char **bp, const char *end, char **labelp,
+               char ** dst, const char *eom)
+{
+       int afterslash = 0;
+       const char *cp = *bp;
+       char *tp, c;
+       const char *beg_blen;
+       char *end_blen = NULL;
+       int value = 0, count = 0, tbcount = 0, blen = 0;
+
+       beg_blen = end_blen = NULL;
+
+       /* a bitstring must contain at least 2 characters */
+       if (end - cp < 2)
+               return(EINVAL);
+
+       /* XXX: currently, only hex strings are supported */
+       if (*cp++ != 'x')
+               return(EINVAL);
+       if (!isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */
+               return(EINVAL);
+
+       for (tp = *dst + 1; cp < end && tp < eom; cp++) {
+               switch((c = *cp)) {
+               case ']':       /* end of the bitstring */
+                       if (afterslash) {
+                               if (beg_blen == NULL)
+                                       return(EINVAL);
+                               blen = (int)strtol(beg_blen, &end_blen, 10);
+                               if (*end_blen != ']')
+                                       return(EINVAL);
+                       }
+                       if (count)
+                               *tp++ = ((value << 4) & 0xff);
+                       cp++;   /* skip ']' */
+                       goto done;
+               case '/':
+                       afterslash = 1;
+                       break;
+               default:
+                       if (afterslash) {
+                               if (!isdigit(c&0xff))
+                                       return(EINVAL);
+                               if (beg_blen == NULL) {
+                                       
+                                       if (c == '0') {
+                                               /* blen never begings with 0 */
+                                               return(EINVAL);
+                                       }
+                                       beg_blen = cp;
+                               }
+                       } else {
+                               if (!isxdigit(c&0xff))
+                                       return(EINVAL);
+                               value <<= 4;
+                               value += digitvalue[(int)c];
+                               count += 4;
+                               tbcount += 4;
+                               if (tbcount > 256)
+                                       return(EINVAL);
+                               if (count == 8) {
+                                       *tp++ = value;
+                                       count = 0;
+                               }
+                       }
+                       break;
+               }
+       }
+  done:
+       if (cp >= end || tp >= eom)
+               return(EMSGSIZE);
+
+       /*
+        * bit length validation:
+        * If a <length> is present, the number of digits in the <bit-data>
+        * MUST be just sufficient to contain the number of bits specified
+        * by the <length>. If there are insignificant bits in a final
+        * hexadecimal or octal digit, they MUST be zero.
+        * RFC 2673, Section 3.2.
+        */
+       if (blen > 0) {
+               int traillen;
+
+               if (((blen + 3) & ~3) != tbcount)
+                       return(EINVAL);
+               traillen = tbcount - blen; /* between 0 and 3 */
+               if (((value << (8 - traillen)) & 0xff) != 0)
+                       return(EINVAL);
+       }
+       else
+               blen = tbcount;
+       if (blen == 256)
+               blen = 0;
+
+       /* encode the type and the significant bit fields */
+       **labelp = DNS_LABELTYPE_BITSTRING;
+       **dst = blen;
+
+       *bp = cp;
+       *dst = tp;
+
+       return(0);
+}
+
+static int
+labellen(const u_char *lp)
+{
+       int bitlen;
+       u_char l = *lp;
+
+       if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+               /* should be avoided by the caller */
+               return(-1);
+       }
+
+       if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+               if (l == DNS_LABELTYPE_BITSTRING) {
+                       if ((bitlen = *(lp + 1)) == 0)
+                               bitlen = 256;
+                       return((bitlen + 7 ) / 8 + 1);
+               }
+               return(-1);     /* unknwon ELT */
+       }
+       return(l);
+}
diff --git a/ns_netint.c b/ns_netint.c
new file mode 100644 (file)
index 0000000..b01e08b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_netint.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <arpa/nameser.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* Public. */
+
+u_int
+ns_get16(const u_char *src) {
+       u_int dst;
+
+       NS_GET16(dst, src);
+       return (dst);
+}
+
+u_long
+ns_get32(const u_char *src) {
+       u_long dst;
+
+       NS_GET32(dst, src);
+       return (dst);
+}
+
+void
+ns_put16(u_int src, u_char *dst) {
+       NS_PUT16(src, dst);
+}
+
+void
+ns_put32(u_long src, u_char *dst) {
+       NS_PUT32(src, dst);
+}
diff --git a/ns_parse.c b/ns_parse.c
new file mode 100644 (file)
index 0000000..fbd1d2c
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_parse.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* Forward. */
+
+static void    setsection(ns_msg *msg, ns_sect sect);
+
+/* Macros. */
+
+#define RETERR(err) do { errno = (err); return (-1); } while (0)
+
+/* Public. */
+
+/* These need to be in the same order as the nres.h:ns_flag enum. */
+struct _ns_flagdata _ns_flagdata[16] = {
+       { 0x8000, 15 },         /* qr. */
+       { 0x7800, 11 },         /* opcode. */
+       { 0x0400, 10 },         /* aa. */
+       { 0x0200, 9 },          /* tc. */
+       { 0x0100, 8 },          /* rd. */
+       { 0x0080, 7 },          /* ra. */
+       { 0x0040, 6 },          /* z. */
+       { 0x0020, 5 },          /* ad. */
+       { 0x0010, 4 },          /* cd. */
+       { 0x000f, 0 },          /* rcode. */
+       { 0x0000, 0 },          /* expansion (1/6). */
+       { 0x0000, 0 },          /* expansion (2/6). */
+       { 0x0000, 0 },          /* expansion (3/6). */
+       { 0x0000, 0 },          /* expansion (4/6). */
+       { 0x0000, 0 },          /* expansion (5/6). */
+       { 0x0000, 0 },          /* expansion (6/6). */
+};
+
+int ns_msg_getflag(ns_msg handle, int flag) {
+       return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift);
+}
+
+int
+ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) {
+       const u_char *optr = ptr;
+
+       for ((void)NULL; count > 0; count--) {
+               int b, rdlength;
+
+               b = dn_skipname(ptr, eom);
+               if (b < 0)
+                       RETERR(EMSGSIZE);
+               ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/;
+               if (section != ns_s_qd) {
+                       if (ptr + NS_INT32SZ + NS_INT16SZ > eom)
+                               RETERR(EMSGSIZE);
+                       ptr += NS_INT32SZ/*TTL*/;
+                       NS_GET16(rdlength, ptr);
+                       ptr += rdlength/*RData*/;
+               }
+       }
+       if (ptr > eom)
+               RETERR(EMSGSIZE);
+       return (ptr - optr);
+}
+
+int
+ns_initparse(const u_char *msg, int msglen, ns_msg *handle) {
+       const u_char *eom = msg + msglen;
+       int i;
+
+       memset(handle, 0x5e, sizeof *handle);
+       handle->_msg = msg;
+       handle->_eom = eom;
+       if (msg + NS_INT16SZ > eom)
+               RETERR(EMSGSIZE);
+       NS_GET16(handle->_id, msg);
+       if (msg + NS_INT16SZ > eom)
+               RETERR(EMSGSIZE);
+       NS_GET16(handle->_flags, msg);
+       for (i = 0; i < ns_s_max; i++) {
+               if (msg + NS_INT16SZ > eom)
+                       RETERR(EMSGSIZE);
+               NS_GET16(handle->_counts[i], msg);
+       }
+       for (i = 0; i < ns_s_max; i++)
+               if (handle->_counts[i] == 0)
+                       handle->_sections[i] = NULL;
+               else {
+                       int b = ns_skiprr(msg, eom, (ns_sect)i,
+                                         handle->_counts[i]);
+
+                       if (b < 0)
+                               return (-1);
+                       handle->_sections[i] = msg;
+                       msg += b;
+               }
+       if (msg != eom)
+               RETERR(EMSGSIZE);
+       setsection(handle, ns_s_max);
+       return (0);
+}
+
+int
+ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) {
+       int b;
+
+       /* Make section right. */
+       if (section >= ns_s_max)
+               RETERR(ENODEV);
+       if (section != handle->_sect)
+               setsection(handle, section);
+
+       /* Make rrnum right. */
+       if (rrnum == -1)
+               rrnum = handle->_rrnum;
+       if (rrnum < 0 || rrnum >= handle->_counts[(int)section])
+               RETERR(ENODEV);
+       if (rrnum < handle->_rrnum)
+               setsection(handle, section);
+       if (rrnum > handle->_rrnum) {
+               b = ns_skiprr(handle->_msg_ptr, handle->_eom, section,
+                             rrnum - handle->_rrnum);
+
+               if (b < 0)
+                       return (-1);
+               handle->_msg_ptr += b;
+               handle->_rrnum = rrnum;
+       }
+
+       /* Do the parse. */
+       b = dn_expand(handle->_msg, handle->_eom,
+                     handle->_msg_ptr, rr->name, NS_MAXDNAME);
+       if (b < 0)
+               return (-1);
+       handle->_msg_ptr += b;
+       if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom)
+               RETERR(EMSGSIZE);
+       NS_GET16(rr->type, handle->_msg_ptr);
+       NS_GET16(rr->rr_class, handle->_msg_ptr);
+       if (section == ns_s_qd) {
+               rr->ttl = 0;
+               rr->rdlength = 0;
+               rr->rdata = NULL;
+       } else {
+               if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom)
+                       RETERR(EMSGSIZE);
+               NS_GET32(rr->ttl, handle->_msg_ptr);
+               NS_GET16(rr->rdlength, handle->_msg_ptr);
+               if (handle->_msg_ptr + rr->rdlength > handle->_eom)
+                       RETERR(EMSGSIZE);
+               rr->rdata = handle->_msg_ptr;
+               handle->_msg_ptr += rr->rdlength;
+       }
+       if (++handle->_rrnum > handle->_counts[(int)section])
+               setsection(handle, (ns_sect)((int)section + 1));
+
+       /* All done. */
+       return (0);
+}
+
+/* Private. */
+
+static void
+setsection(ns_msg *msg, ns_sect sect) {
+       msg->_sect = sect;
+       if (sect == ns_s_max) {
+               msg->_rrnum = -1;
+               msg->_msg_ptr = NULL;
+       } else {
+               msg->_rrnum = 0;
+               msg->_msg_ptr = msg->_sections[(int)sect];
+       }
+}
diff --git a/ns_print.c b/ns_print.c
new file mode 100644 (file)
index 0000000..fd9c71f
--- /dev/null
@@ -0,0 +1,914 @@
+/*
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_print.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef __APPLE__
+#include <isc/assertions.h>
+#include "port_after.h"
+#endif
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Forward. */
+
+static size_t  prune_origin(const char *name, const char *origin);
+static int     charstr(const u_char *rdata, const u_char *edata,
+                       char **buf, size_t *buflen);
+static int     addname(const u_char *msg, size_t msglen,
+                       const u_char **p, const char *origin,
+                       char **buf, size_t *buflen);
+static void    addlen(size_t len, char **buf, size_t *buflen);
+static int     addstr(const char *src, size_t len,
+                      char **buf, size_t *buflen);
+static int     addtab(size_t len, size_t target, int spaced,
+                      char **buf, size_t *buflen);
+
+/* Proto. */
+
+#ifndef dst_s_dns_key_id
+#define dst_s_dns_key_id res_9_dst_s_dns_key_id
+#endif
+u_int16_t       dst_s_dns_key_id(const u_char *, const int);
+
+/* Macros. */
+
+#define        T(x) \
+       do { \
+               if ((x) < 0) \
+                       return (-1); \
+       } while (0)
+
+/* Public. */
+
+/*
+ * int
+ * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen)
+ *     Convert an RR to presentation format.
+ * return:
+ *     Number of characters written to buf, or -1 (check errno).
+ */
+int
+ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
+           const char *name_ctx, const char *origin,
+           char *buf, size_t buflen)
+{
+       int n;
+
+       n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle),
+                        ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr),
+                        ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr),
+                        name_ctx, origin, buf, buflen);
+       return (n);
+}
+
+/*
+ * int
+ * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen,
+ *            name_ctx, origin, buf, buflen)
+ *     Convert the fields of an RR into presentation format.
+ * return:
+ *     Number of characters written to buf, or -1 (check errno).
+ */
+int
+ns_sprintrrf(const u_char *msg, size_t msglen,
+           const char *name, ns_class class, ns_type type,
+           u_long ttl, const u_char *rdata, size_t rdlen,
+           const char *name_ctx, const char *origin,
+           char *buf, size_t buflen)
+{
+       const char *obuf = buf;
+       const u_char *edata = rdata + rdlen;
+       int spaced = 0;
+
+       const char *comment;
+       char tmp[100];
+       int len, x;
+
+       /*
+        * Owner.
+        */
+       if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) {
+               T(addstr("\t\t\t", 3, &buf, &buflen));
+       } else {
+               len = prune_origin(name, origin);
+               if (*name == '\0') {
+                       goto root;
+               } else if (len == 0) {
+                       T(addstr("@\t\t\t", 4, &buf, &buflen));
+               } else {
+                       T(addstr(name, len, &buf, &buflen));
+                       /* Origin not used or not root, and no trailing dot? */
+                       if (((origin == NULL || origin[0] == '\0') ||
+                           (origin[0] != '.' && origin[1] != '\0' &&
+                           name[len] == '\0')) && name[len - 1] != '.') {
+ root:
+                               T(addstr(".", 1, &buf, &buflen));
+                               len++;
+                       }
+                       T(spaced = addtab(len, 24, spaced, &buf, &buflen));
+               }
+       }
+
+       /*
+        * TTL, Class, Type.
+        */
+       T(x = ns_format_ttl(ttl, buf, buflen));
+       addlen(x, &buf, &buflen);
+       len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
+       T(addstr(tmp, len, &buf, &buflen));
+       if (rdlen == 0)
+               return (buf - obuf);
+       T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
+
+       /*
+        * RData.
+        */
+       switch (type) {
+       case ns_t_a:
+               if (rdlen != NS_INADDRSZ)
+                       goto formerr;
+               (void) inet_ntop(AF_INET, rdata, buf, buflen);
+               addlen(strlen(buf), &buf, &buflen);
+               break;
+
+       case ns_t_cname:
+       case ns_t_mb:
+       case ns_t_mg:
+       case ns_t_mr:
+       case ns_t_ns:
+       case ns_t_ptr:
+       case ns_t_dname:
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               break;
+
+       case ns_t_hinfo:
+       case ns_t_isdn:
+               /* First word. */
+               T(len = charstr(rdata, edata, &buf, &buflen));
+               if (len == 0)
+                       goto formerr;
+               rdata += len;
+               T(addstr(" ", 1, &buf, &buflen));
+
+                   
+               /* Second word, optional in ISDN records. */
+               if (type == ns_t_isdn && rdata == edata)
+                       break;
+                   
+               T(len = charstr(rdata, edata, &buf, &buflen));
+               if (len == 0)
+                       goto formerr;
+               rdata += len;
+               break;
+
+       case ns_t_soa: {
+               u_long t;
+
+               /* Server name. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Administrator name. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               T(addstr(" (\n", 3, &buf, &buflen));
+               spaced = 0;
+
+               if ((edata - rdata) != 5*NS_INT32SZ)
+                       goto formerr;
+
+               /* Serial number. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+               len = SPRINTF((tmp, "%lu", t));
+               T(addstr(tmp, len, &buf, &buflen));
+               T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+               T(addstr("; serial\n", 9, &buf, &buflen));
+               spaced = 0;
+
+               /* Refresh interval. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+               T(len = ns_format_ttl(t, buf, buflen));
+               addlen(len, &buf, &buflen);
+               T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+               T(addstr("; refresh\n", 10, &buf, &buflen));
+               spaced = 0;
+
+               /* Retry interval. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+               T(len = ns_format_ttl(t, buf, buflen));
+               addlen(len, &buf, &buflen);
+               T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+               T(addstr("; retry\n", 8, &buf, &buflen));
+               spaced = 0;
+
+               /* Expiry. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+               T(len = ns_format_ttl(t, buf, buflen));
+               addlen(len, &buf, &buflen);
+               T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+               T(addstr("; expiry\n", 9, &buf, &buflen));
+               spaced = 0;
+
+               /* Minimum TTL. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+               T(len = ns_format_ttl(t, buf, buflen));
+               addlen(len, &buf, &buflen);
+               T(addstr(" )", 2, &buf, &buflen));
+               T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+               T(addstr("; minimum\n", 10, &buf, &buflen));
+
+               break;
+           }
+
+       case ns_t_mx:
+       case ns_t_afsdb:
+       case ns_t_rt: {
+               u_int t;
+
+               if (rdlen < NS_INT16SZ)
+                       goto formerr;
+
+               /* Priority. */
+               t = ns_get16(rdata);
+               rdata += NS_INT16SZ;
+               len = SPRINTF((tmp, "%u ", t));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Target. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+               break;
+           }
+
+       case ns_t_px: {
+               u_int t;
+
+               if (rdlen < NS_INT16SZ)
+                       goto formerr;
+
+               /* Priority. */
+               t = ns_get16(rdata);
+               rdata += NS_INT16SZ;
+               len = SPRINTF((tmp, "%u ", t));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Name1. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Name2. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+               break;
+           }
+
+       case ns_t_x25:
+               T(len = charstr(rdata, edata, &buf, &buflen));
+               if (len == 0)
+                       goto formerr;
+               rdata += len;
+               break;
+
+       case ns_t_txt:
+               while (rdata < edata) {
+                       T(len = charstr(rdata, edata, &buf, &buflen));
+                       if (len == 0)
+                               goto formerr;
+                       rdata += len;
+                       if (rdata < edata)
+                               T(addstr(" ", 1, &buf, &buflen));
+               }
+               break;
+
+       case ns_t_nsap: {
+               char t[2+255*3];
+
+               (void) inet_nsap_ntoa(rdlen, rdata, t);
+               T(addstr(t, strlen(t), &buf, &buflen));
+               break;
+           }
+
+       case ns_t_aaaa:
+               if (rdlen != NS_IN6ADDRSZ)
+                       goto formerr;
+               (void) inet_ntop(AF_INET6, rdata, buf, buflen);
+               addlen(strlen(buf), &buf, &buflen);
+               break;
+
+       case ns_t_loc: {
+               char t[255];
+
+               /* XXX protocol format checking? */
+               (void) loc_ntoa(rdata, t);
+               T(addstr(t, strlen(t), &buf, &buflen));
+               break;
+           }
+
+       case ns_t_naptr: {
+               u_int order, preference;
+               char t[50];
+
+               if (rdlen < 2*NS_INT16SZ)
+                       goto formerr;
+
+               /* Order, Precedence. */
+               order = ns_get16(rdata);        rdata += NS_INT16SZ;
+               preference = ns_get16(rdata);   rdata += NS_INT16SZ;
+               len = SPRINTF((t, "%u %u ", order, preference));
+               T(addstr(t, len, &buf, &buflen));
+
+               /* Flags. */
+               T(len = charstr(rdata, edata, &buf, &buflen));
+               if (len == 0)
+                       goto formerr;
+               rdata += len;
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Service. */
+               T(len = charstr(rdata, edata, &buf, &buflen));
+               if (len == 0)
+                       goto formerr;
+               rdata += len;
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Regexp. */
+               T(len = charstr(rdata, edata, &buf, &buflen));
+               if (len < 0)
+                       return (-1);
+               if (len == 0)
+                       goto formerr;
+               rdata += len;
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Server. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               break;
+           }
+
+       case ns_t_srv: {
+               u_int priority, weight, port;
+               char t[50];
+
+               if (rdlen < NS_INT16SZ*3)
+                       goto formerr;
+
+               /* Priority, Weight, Port. */
+               priority = ns_get16(rdata);  rdata += NS_INT16SZ;
+               weight   = ns_get16(rdata);  rdata += NS_INT16SZ;
+               port     = ns_get16(rdata);  rdata += NS_INT16SZ;
+               len = SPRINTF((t, "%u %u %u ", priority, weight, port));
+               T(addstr(t, len, &buf, &buflen));
+
+               /* Server. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               break;
+           }
+
+       case ns_t_minfo:
+       case ns_t_rp:
+               /* Name1. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Name2. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+               break;
+
+       case ns_t_wks: {
+               int n, lcnt;
+
+               if (rdlen < NS_INT32SZ + 1)
+                       goto formerr;
+
+               /* Address. */
+               (void) inet_ntop(AF_INET, rdata, buf, buflen);
+               addlen(strlen(buf), &buf, &buflen);
+               rdata += NS_INADDRSZ;
+
+               /* Protocol. */
+               len = SPRINTF((tmp, " %u ( ", *rdata));
+               T(addstr(tmp, len, &buf, &buflen));
+               rdata += NS_INT8SZ;
+
+               /* Bit map. */
+               n = 0;
+               lcnt = 0;
+               while (rdata < edata) {
+                       u_int c = *rdata++;
+                       do {
+                               if (c & 0200) {
+                                       if (lcnt == 0) {
+                                               T(addstr("\n\t\t\t\t", 5,
+                                                        &buf, &buflen));
+                                               lcnt = 10;
+                                               spaced = 0;
+                                       }
+                                       len = SPRINTF((tmp, "%d ", n));
+                                       T(addstr(tmp, len, &buf, &buflen));
+                                       lcnt--;
+                               }
+                               c <<= 1;
+                       } while (++n & 07);
+               }
+               T(addstr(")", 1, &buf, &buflen));
+
+               break;
+           }
+
+       case ns_t_key: {
+               char base64_key[NS_MD5RSA_MAX_BASE64];
+               u_int keyflags, protocol, algorithm, key_id;
+               const char *leader;
+               int n;
+
+               if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
+                       goto formerr;
+
+               /* Key flags, Protocol, Algorithm. */
+               key_id = dst_s_dns_key_id(rdata, edata-rdata);
+               keyflags = ns_get16(rdata);  rdata += NS_INT16SZ;
+               protocol = *rdata++;
+               algorithm = *rdata++;
+               len = SPRINTF((tmp, "0x%04x %u %u",
+                              keyflags, protocol, algorithm));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Public key data. */
+               len = b64_ntop(rdata, edata - rdata,
+                              base64_key, sizeof base64_key);
+               if (len < 0)
+                       goto formerr;
+               if (len > 15) {
+                       T(addstr(" (", 2, &buf, &buflen));
+                       leader = "\n\t\t";
+                       spaced = 0;
+               } else
+                       leader = " ";
+               for (n = 0; n < len; n += 48) {
+                       T(addstr(leader, strlen(leader), &buf, &buflen));
+                       T(addstr(base64_key + n, MIN(len - n, 48),
+                                &buf, &buflen));
+               }
+               if (len > 15)
+                       T(addstr(" )", 2, &buf, &buflen));
+               n = SPRINTF((tmp, " ; key_tag= %u", key_id));
+               T(addstr(tmp, n, &buf, &buflen));
+
+               break;
+           }
+
+       case ns_t_sig: {
+               char base64_key[NS_MD5RSA_MAX_BASE64];
+               u_int type, algorithm, labels, footprint;
+               const char *leader;
+               u_long t;
+               int n;
+
+               if (rdlen < 22)
+                       goto formerr;
+
+               /* Type covered, Algorithm, Label count, Original TTL. */
+               type = ns_get16(rdata);  rdata += NS_INT16SZ;
+               algorithm = *rdata++;
+               labels = *rdata++;
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               len = SPRINTF((tmp, "%s %d %d %lu ",
+                              p_type(type), algorithm, labels, t));
+               T(addstr(tmp, len, &buf, &buflen));
+               if (labels > (u_int)dn_count_labels(name))
+                       goto formerr;
+
+               /* Signature expiry. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Time signed. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Signature Footprint. */
+               footprint = ns_get16(rdata);  rdata += NS_INT16SZ;
+               len = SPRINTF((tmp, "%u ", footprint));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Signer's name. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+               /* Signature. */
+               len = b64_ntop(rdata, edata - rdata,
+                              base64_key, sizeof base64_key);
+               if (len > 15) {
+                       T(addstr(" (", 2, &buf, &buflen));
+                       leader = "\n\t\t";
+                       spaced = 0;
+               } else
+                       leader = " ";
+               if (len < 0)
+                       goto formerr;
+               for (n = 0; n < len; n += 48) {
+                       T(addstr(leader, strlen(leader), &buf, &buflen));
+                       T(addstr(base64_key + n, MIN(len - n, 48),
+                                &buf, &buflen));
+               }
+               if (len > 15)
+                       T(addstr(" )", 2, &buf, &buflen));
+               break;
+           }
+
+       case ns_t_nxt: {
+               int n, c;
+
+               /* Next domain name. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+               /* Type bit map. */
+               n = edata - rdata;
+               for (c = 0; c < n*8; c++)
+                       if (NS_NXT_BIT_ISSET(c, rdata)) {
+                               len = SPRINTF((tmp, " %s", p_type(c)));
+                               T(addstr(tmp, len, &buf, &buflen));
+                       }
+               break;
+           }
+
+       case ns_t_cert: {
+               u_int c_type, key_tag, alg;
+               int n;
+               unsigned int siz;
+               char base64_cert[8192], tmp[40];
+               const char *leader;
+
+               c_type  = ns_get16(rdata); rdata += NS_INT16SZ;
+               key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
+               alg = (u_int) *rdata++;
+
+               len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
+               T(addstr(tmp, len, &buf, &buflen));
+               siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
+               if (siz > sizeof(base64_cert) * 3/4) {
+                       const char *str = "record too long to print";
+                       T(addstr(str, strlen(str), &buf, &buflen));
+               }
+               else {
+                       len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
+
+                       if (len < 0)
+                               goto formerr;
+                       else if (len > 15) {
+                               T(addstr(" (", 2, &buf, &buflen));
+                               leader = "\n\t\t";
+                               spaced = 0;
+                       }
+                       else
+                               leader = " ";
+       
+                       for (n = 0; n < len; n += 48) {
+                               T(addstr(leader, strlen(leader),
+                                        &buf, &buflen));
+                               T(addstr(base64_cert + n, MIN(len - n, 48),
+                                        &buf, &buflen));
+                       }
+                       if (len > 15)
+                               T(addstr(" )", 2, &buf, &buflen));
+               }
+               break;
+           }
+
+       case ns_t_tkey: {
+               /* KJD - need to complete this */
+               u_long t;
+               int mode, err, keysize;
+
+               /* Algorithm name. */
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               T(addstr(" ", 1, &buf, &buflen));
+
+               /* Inception. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Experation. */
+               t = ns_get32(rdata);  rdata += NS_INT32SZ;
+               len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+               T(addstr(tmp, len, &buf, &buflen));
+
+               /* Mode , Error, Key Size. */
+               /* Priority, Weight, Port. */
+               mode = ns_get16(rdata);  rdata += NS_INT16SZ;
+               err  = ns_get16(rdata);  rdata += NS_INT16SZ;
+               keysize  = ns_get16(rdata);  rdata += NS_INT16SZ;
+               len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
+               T(addstr(tmp, len, &buf, &buflen));
+
+        /* needs to dump key, print otherdata length & other data */
+               break;
+           }
+       case ns_t_tsig: {
+               /* BEW - need to complete this */
+               int n;
+
+               T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               T(addstr(" ", 1, &buf, &buflen));
+               rdata += 8; /* time */
+               n = ns_get16(rdata); rdata += NS_INT16SZ;
+               rdata += n; /* sig */
+               n = ns_get16(rdata); rdata += NS_INT16SZ; /* original id */
+               sprintf(buf, "%d", ns_get16(rdata));
+               rdata += NS_INT16SZ;
+               addlen(strlen(buf), &buf, &buflen);
+               break;
+           }
+
+       case ns_t_a6: {
+               struct in6_addr a;
+               int pbyte, pbit;
+
+               /* prefix length */
+               if (rdlen == 0) goto formerr;
+               len = SPRINTF((tmp, "%d ", *rdata));
+               T(addstr(tmp, len, &buf, &buflen));
+               pbit = *rdata;
+               if (pbit > 128) goto formerr;
+               pbyte = (pbit & ~7) / 8;
+               rdata++;
+
+               /* address suffix: provided only when prefix len != 128 */
+               if (pbit < 128) {
+                       if (rdata + pbyte >= edata) goto formerr;
+                       memset(&a, 0, sizeof(a));
+                       memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
+                       (void) inet_ntop(AF_INET6, &a, buf, buflen);
+                       addlen(strlen(buf), &buf, &buflen);
+                       rdata += sizeof(a) - pbyte;
+               }
+
+               /* prefix name: provided only when prefix len > 0 */
+               if (pbit == 0)
+                       break;
+               if (rdata >= edata) goto formerr;
+               T(addstr(" ", 1, &buf, &buflen));
+               T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+               
+               break;
+       }
+
+       case ns_t_opt: {
+               len = SPRINTF((tmp, "%u bytes", class));
+               T(addstr(tmp, len, &buf, &buflen));
+               break;
+       }
+
+       default:
+               comment = "unknown RR type";
+               goto hexify;
+       }
+       return (buf - obuf);
+ formerr:
+       comment = "RR format error";
+ hexify: {
+       int n, m;
+       char *p;
+
+       len = SPRINTF((tmp, "\\# %u (\t; %s", edata - rdata, comment));
+       T(addstr(tmp, len, &buf, &buflen));
+       while (rdata < edata) {
+               p = tmp;
+               p += SPRINTF((p, "\n\t"));
+               spaced = 0;
+               n = MIN(16, edata - rdata);
+               for (m = 0; m < n; m++)
+                       p += SPRINTF((p, "%02x ", rdata[m]));
+               T(addstr(tmp, p - tmp, &buf, &buflen));
+               if (n < 16) {
+                       T(addstr(")", 1, &buf, &buflen));
+                       T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen));
+               }
+               p = tmp;
+               p += SPRINTF((p, "; "));
+               for (m = 0; m < n; m++)
+                       *p++ = (isascii(rdata[m]) && isprint(rdata[m]))
+                               ? rdata[m]
+                               : '.';
+               T(addstr(tmp, p - tmp, &buf, &buflen));
+               rdata += n;
+       }
+       return (buf - obuf);
+    }
+}
+
+/* Private. */
+
+/*
+ * size_t
+ * prune_origin(name, origin)
+ *     Find out if the name is at or under the current origin.
+ * return:
+ *     Number of characters in name before start of origin,
+ *     or length of name if origin does not match.
+ * notes:
+ *     This function should share code with samedomain().
+ */
+static size_t
+prune_origin(const char *name, const char *origin) {
+       const char *oname = name;
+
+       while (*name != '\0') {
+               if (origin != NULL && ns_samename(name, origin) == 1)
+                       return (name - oname - (name > oname));
+               while (*name != '\0') {
+                       if (*name == '\\') {
+                               name++;
+                               /* XXX need to handle \nnn form. */
+                               if (*name == '\0')
+                                       break;
+                       } else if (*name == '.') {
+                               name++;
+                               break;
+                       }
+                       name++;
+               }
+       }
+       return (name - oname);
+}
+
+/*
+ * int
+ * charstr(rdata, edata, buf, buflen)
+ *     Format a <character-string> into the presentation buffer.
+ * return:
+ *     Number of rdata octets consumed
+ *     0 for protocol format error
+ *     -1 for output buffer error
+ * side effects:
+ *     buffer is advanced on success.
+ */
+static int
+charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
+       const u_char *odata = rdata;
+       size_t save_buflen = *buflen;
+       char *save_buf = *buf;
+
+       if (addstr("\"", 1, buf, buflen) < 0)
+               goto enospc;
+       if (rdata < edata) {
+               int n = *rdata;
+
+               if (rdata + 1 + n <= edata) {
+                       rdata++;
+                       while (n-- > 0) {
+                               if (strchr("\n\"\\", *rdata) != NULL)
+                                       if (addstr("\\", 1, buf, buflen) < 0)
+                                               goto enospc;
+                               if (addstr((const char *)rdata, 1,
+                                          buf, buflen) < 0)
+                                       goto enospc;
+                               rdata++;
+                       }
+               }
+       }
+       if (addstr("\"", 1, buf, buflen) < 0)
+               goto enospc;
+       return (rdata - odata);
+ enospc:
+       errno = ENOSPC;
+       *buf = save_buf;
+       *buflen = save_buflen;
+       return (-1);
+}
+
+static int
+addname(const u_char *msg, size_t msglen,
+       const u_char **pp, const char *origin,
+       char **buf, size_t *buflen)
+{
+       size_t newlen, save_buflen = *buflen;
+       char *save_buf = *buf;
+       int n;
+
+       n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen);
+       if (n < 0)
+               goto enospc;    /* Guess. */
+       newlen = prune_origin(*buf, origin);
+       if (**buf == '\0') {
+               goto root;
+       } else if (newlen == 0) {
+               /* Use "@" instead of name. */
+               if (newlen + 2 > *buflen)
+                       goto enospc;        /* No room for "@\0". */
+               (*buf)[newlen++] = '@';
+               (*buf)[newlen] = '\0';
+       } else {
+               if (((origin == NULL || origin[0] == '\0') ||
+                   (origin[0] != '.' && origin[1] != '\0' &&
+                   (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
+                       /* No trailing dot. */
+ root:
+                       if (newlen + 2 > *buflen)
+                               goto enospc;    /* No room for ".\0". */
+                       (*buf)[newlen++] = '.';
+                       (*buf)[newlen] = '\0';
+               }
+       }
+       *pp += n;
+       addlen(newlen, buf, buflen);
+       **buf = '\0';
+       return (newlen);
+ enospc:
+       errno = ENOSPC;
+       *buf = save_buf;
+       *buflen = save_buflen;
+       return (-1);
+}
+
+static void
+addlen(size_t len, char **buf, size_t *buflen) {
+#ifdef __APPLE__
+       if (len > *buflen) return;
+#else
+       INSIST(len <= *buflen);
+#endif
+       *buf += len;
+       *buflen -= len;
+}
+
+static int
+addstr(const char *src, size_t len, char **buf, size_t *buflen) {
+       if (len >= *buflen) {
+               errno = ENOSPC;
+               return (-1);
+       }
+       memcpy(*buf, src, len);
+       addlen(len, buf, buflen);
+       **buf = '\0';
+       return (0);
+}
+
+static int
+addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
+       size_t save_buflen = *buflen;
+       char *save_buf = *buf;
+       int t;
+
+       if (spaced || len >= target - 1) {
+               T(addstr("  ", 2, buf, buflen));
+               spaced = 1;
+       } else {
+               for (t = (target - len - 1) / 8; t >= 0; t--)
+                       if (addstr("\t", 1, buf, buflen) < 0) {
+                               *buflen = save_buflen;
+                               *buf = save_buf;
+                               return (-1);
+                       }
+               spaced = 0;
+       }
+       return (spaced);
+}
diff --git a/ns_samedomain.c b/ns_samedomain.c
new file mode 100644 (file)
index 0000000..1d53bb2
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1995,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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_samedomain.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/types.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <string.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/*
+ * int
+ * ns_samedomain(a, b)
+ *     Check whether a name belongs to a domain.
+ * Inputs:
+ *     a - the domain whose ancestory is being verified
+ *     b - the potential ancestor we're checking against
+ * Return:
+ *     boolean - is a at or below b?
+ * Notes:
+ *     Trailing dots are first removed from name and domain.
+ *     Always compare complete subdomains, not only whether the
+ *     domain name is the trailing string of the given name.
+ *
+ *     "host.foobar.top" lies in "foobar.top" and in "top" and in ""
+ *     but NOT in "bar.top"
+ */
+
+int
+ns_samedomain(const char *a, const char *b) {
+       size_t la, lb;
+       int diff, i, escaped;
+       const char *cp;
+
+       la = strlen(a);
+       lb = strlen(b);
+
+       /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
+       if (la != 0 && a[la - 1] == '.') {
+               escaped = 0;
+               /* Note this loop doesn't get executed if la==1. */
+               for (i = la - 2; i >= 0; i--)
+                       if (a[i] == '\\') {
+                               if (escaped)
+                                       escaped = 0;
+                               else
+                                       escaped = 1;
+                       } else
+                               break;
+               if (!escaped)
+                       la--;
+       }
+
+       /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
+       if (lb != 0 && b[lb - 1] == '.') {
+               escaped = 0;
+               /* note this loop doesn't get executed if lb==1 */
+               for (i = lb - 2; i >= 0; i--)
+                       if (b[i] == '\\') {
+                               if (escaped)
+                                       escaped = 0;
+                               else
+                                       escaped = 1;
+                       } else
+                               break;
+               if (!escaped)
+                       lb--;
+       }
+
+       /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
+       if (lb == 0)
+               return (1);
+
+       /* 'b' longer than 'a' means 'a' can't be in 'b'. */
+       if (lb > la)
+               return (0);
+
+       /* 'a' and 'b' being equal at this point indicates sameness. */
+       if (lb == la)
+               return (strncasecmp(a, b, lb) == 0);
+
+       /* Ok, we know la > lb. */
+
+       diff = la - lb;
+
+       /*
+        * If 'a' is only 1 character longer than 'b', then it can't be
+        * a subdomain of 'b' (because of the need for the '.' label
+        * separator).
+        */
+       if (diff < 2)
+               return (0);
+
+       /*
+        * If the character before the last 'lb' characters of 'b'
+        * isn't '.', then it can't be a match (this lets us avoid
+        * having "foobar.com" match "bar.com").
+        */
+       if (a[diff - 1] != '.')
+               return (0);
+
+       /*
+        * We're not sure about that '.', however.  It could be escaped
+         * and thus not a really a label separator.
+        */
+       escaped = 0;
+       for (i = diff - 2; i >= 0; i--)
+               if (a[i] == '\\') {
+                       if (escaped)
+                               escaped = 0;
+                       else
+                               escaped = 1;
+               } else
+                       break;
+       if (escaped)
+               return (0);
+         
+       /* Now compare aligned trailing substring. */
+       cp = a + diff;
+       return (strncasecmp(cp, b, lb) == 0);
+}
+
+/*
+ * int
+ * ns_subdomain(a, b)
+ *     is "a" a subdomain of "b"?
+ */
+int
+ns_subdomain(const char *a, const char *b) {
+       return (ns_samename(a, b) != 1 && ns_samedomain(a, b));
+}
+
+/*
+ * int
+ * ns_makecanon(src, dst, dstsize)
+ *     make a canonical copy of domain name "src"
+ * notes:
+ *     foo -> foo.
+ *     foo. -> foo.
+ *     foo.. -> foo.
+ *     foo\. -> foo\..
+ *     foo\\. -> foo\\.
+ */
+
+int
+ns_makecanon(const char *src, char *dst, size_t dstsize) {
+       size_t n = strlen(src);
+
+       if (n + sizeof "." > dstsize) {
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       strcpy(dst, src);
+       while (n > 0 && dst[n - 1] == '.')              /* Ends in "." */
+               if (n > 1 && dst[n - 2] == '\\' &&      /* Ends in "\." */
+                   (n < 2 || dst[n - 3] != '\\'))      /* But not "\\." */
+                       break;
+               else
+                       dst[--n] = '\0';
+       dst[n++] = '.';
+       dst[n] = '\0';
+       return (0);
+}
+
+/*
+ * int
+ * ns_samename(a, b)
+ *     determine whether domain name "a" is the same as domain name "b"
+ * return:
+ *     -1 on error
+ *     0 if names differ
+ *     1 if names are the same
+ */
+
+int
+ns_samename(const char *a, const char *b) {
+       char ta[NS_MAXDNAME], tb[NS_MAXDNAME];
+
+       if (ns_makecanon(a, ta, sizeof ta) < 0 ||
+           ns_makecanon(b, tb, sizeof tb) < 0)
+               return (-1);
+       if (strcasecmp(ta, tb) == 0)
+               return (1);
+       else
+               return (0);
+}
diff --git a/ns_sign.c b/ns_sign.c
new file mode 100644 (file)
index 0000000..f19077a
--- /dev/null
+++ b/ns_sign.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_sign.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#include "fd_setsize.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef __APPLE__
+#include <isc/dst.h>
+#include "port_after.h"
+#else
+#include "dst_internal.h"
+#include "res_private.h"
+#endif
+
+#define BOUNDS_CHECK(ptr, count) \
+       do { \
+               if ((ptr) + (count) > eob) { \
+                       errno = EMSGSIZE; \
+                       return(NS_TSIG_ERROR_NO_SPACE); \
+               } \
+       } while (0)
+
+/* ns_sign
+ * Parameters:
+ *     msg             message to be sent
+ *     msglen          input - length of message
+ *                     output - length of signed message
+ *     msgsize         length of buffer containing message
+ *     error           value to put in the error field
+ *     key             tsig key used for signing
+ *     querysig        (response), the signature in the query
+ *     querysiglen     (response), the length of the signature in the query
+ *     sig             a buffer to hold the generated signature
+ *     siglen          input - length of signature buffer
+ *                     output - length of signature
+ *
+ * Errors:
+ *     - bad input data (-1)
+ *     - bad key / sign failed (-BADKEY)
+ *     - not enough space (NS_TSIG_ERROR_NO_SPACE)
+ */
+int
+ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k,
+       const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
+       time_t in_timesigned)
+{
+       return(ns_sign2(msg, msglen, msgsize, error, k,
+                       querysig, querysiglen, sig, siglen,
+                       in_timesigned, NULL, NULL));
+}
+
+int
+ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k,
+        const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
+        time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr)
+{
+       HEADER *hp = (HEADER *)msg;
+       DST_KEY *key = (DST_KEY *)k;
+       u_char *cp = msg + *msglen, *eob = msg + msgsize;
+       u_char *lenp;
+       u_char *name, *alg;
+       int n;
+       time_t timesigned;
+
+       dst_init();
+       if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL)
+               return (-1);
+
+       /* Name. */
+       if (key != NULL && error != ns_r_badsig && error != ns_r_badkey)
+               n = dn_comp(key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr);
+       else
+               n = dn_comp("", cp, eob - cp, NULL, NULL);
+       if (n < 0)
+               return (NS_TSIG_ERROR_NO_SPACE);
+       name = cp;
+       cp += n;
+
+       /* Type, class, ttl, length (not filled in yet). */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       NS_PUT16(ns_t_tsig, cp);
+       NS_PUT16(ns_c_any, cp);
+       NS_PUT32(0, cp);                /* TTL */
+       lenp = cp;
+       cp += 2;
+
+       /* Alg. */
+       if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+               if (key->dk_alg != KEY_HMAC_MD5)
+                       return (-ns_r_badkey);
+               n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
+       }
+       else
+               n = dn_comp("", cp, eob - cp, NULL, NULL);
+       if (n < 0)
+               return (NS_TSIG_ERROR_NO_SPACE);
+       alg = cp;
+       cp += n;
+       
+       /* Time. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       NS_PUT16(0, cp);
+       timesigned = time(NULL);
+       if (error != ns_r_badtime)
+               NS_PUT32(timesigned, cp);
+       else
+               NS_PUT32(in_timesigned, cp);
+       NS_PUT16(NS_TSIG_FUDGE, cp);
+
+       /* Compute the signature. */
+       if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+               void *ctx;
+               u_char buf[NS_MAXDNAME], *cp2;
+               int n;
+
+               dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
+
+               /* Digest the query signature, if this is a response. */
+               if (querysiglen > 0 && querysig != NULL) {
+                       u_int16_t len_n = htons(querysiglen);
+                       dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
+                                     (u_char *)&len_n, NS_INT16SZ, NULL, 0);
+                       dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
+                                     querysig, querysiglen, NULL, 0);
+               }
+
+               /* Digest the message. */
+               dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen,
+                             NULL, 0);
+
+               /* Digest the key name. */
+               n = ns_name_ntol(name, buf, sizeof(buf));
+               dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+               /* Digest the class and TTL. */
+               cp2 = buf;
+               NS_PUT16(ns_c_any, cp2);
+               NS_PUT32(0, cp2);
+               dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
+                             NULL, 0);
+
+               /* Digest the algorithm. */
+               n = ns_name_ntol(alg, buf, sizeof(buf));
+               dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+               /* Digest the time signed, fudge, error, and other data */
+               cp2 = buf;
+               NS_PUT16(0, cp2);       /* Top 16 bits of time */
+               if (error != ns_r_badtime)
+                       NS_PUT32(timesigned, cp2);
+               else
+                       NS_PUT32(in_timesigned, cp2);
+               NS_PUT16(NS_TSIG_FUDGE, cp2);
+               NS_PUT16(error, cp2);   /* Error */
+               if (error != ns_r_badtime)
+                       NS_PUT16(0, cp2);       /* Other data length */
+               else {
+                       NS_PUT16(NS_INT16SZ+NS_INT32SZ, cp2);   /* Other data length */
+                       NS_PUT16(0, cp2);       /* Top 16 bits of time */
+                       NS_PUT32(timesigned, cp2);
+               }
+               dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
+                             NULL, 0);
+
+               n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
+                                 sig, *siglen);
+               if (n < 0)
+                       return (-ns_r_badkey);
+               *siglen = n;
+       } else
+               *siglen = 0;
+
+       /* Add the signature. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + (*siglen));
+       NS_PUT16(*siglen, cp);
+       memcpy(cp, sig, *siglen);
+       cp += (*siglen);
+
+       /* The original message ID & error. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ);
+       NS_PUT16(ntohs(hp->id), cp);    /* already in network order */
+       NS_PUT16(error, cp);
+
+       /* Other data. */
+       BOUNDS_CHECK(cp, NS_INT16SZ);
+       if (error != ns_r_badtime)
+               NS_PUT16(0, cp);        /* Other data length */
+       else {
+               NS_PUT16(NS_INT16SZ+NS_INT32SZ, cp);    /* Other data length */
+               BOUNDS_CHECK(cp, NS_INT32SZ+NS_INT16SZ);
+               NS_PUT16(0, cp);        /* Top 16 bits of time */
+               NS_PUT32(timesigned, cp);
+       }
+
+       /* Go back and fill in the length. */
+       NS_PUT16(cp - lenp - NS_INT16SZ, lenp);
+
+       hp->arcount = htons(ntohs(hp->arcount) + 1);
+       *msglen = (cp - msg);
+       return (0);
+}
+
+int
+ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen,
+                ns_tcp_tsig_state *state)
+{
+       dst_init();
+       if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
+               return (-1);
+       state->counter = -1;
+       state->key = k;
+       if (state->key->dk_alg != KEY_HMAC_MD5)
+               return (-ns_r_badkey);
+       if (querysiglen > (int)sizeof(state->sig))
+               return (-1);
+       memcpy(state->sig, querysig, querysiglen);
+       state->siglen = querysiglen;
+       return (0);
+}
+
+int
+ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error,
+           ns_tcp_tsig_state *state, int done)
+{
+       return (ns_sign_tcp2(msg, msglen, msgsize, error, state,
+                            done, NULL, NULL));
+}
+
+int
+ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error,
+            ns_tcp_tsig_state *state, int done,
+            u_char **dnptrs, u_char **lastdnptr)
+{
+       u_char *cp, *eob, *lenp;
+       u_char buf[NS_MAXDNAME], *cp2;
+       HEADER *hp = (HEADER *)msg;
+       time_t timesigned;
+       int n;
+
+       if (msg == NULL || msglen == NULL || state == NULL)
+               return (-1);
+
+       state->counter++;
+       if (state->counter == 0)
+               return (ns_sign2(msg, msglen, msgsize, error, state->key,
+                                state->sig, state->siglen,
+                                state->sig, &state->siglen, 0,
+                                dnptrs, lastdnptr));
+
+       if (state->siglen > 0) {
+               u_int16_t siglen_n = htons(state->siglen);
+               dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx,
+                             NULL, 0, NULL, 0);
+               dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                             (u_char *)&siglen_n, NS_INT16SZ, NULL, 0);
+               dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                             state->sig, state->siglen, NULL, 0);
+               state->siglen = 0;
+       }
+
+       dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen,
+                     NULL, 0);
+
+       if (done == 0 && (state->counter % 100 != 0))
+               return (0);
+
+       cp = msg + *msglen;
+       eob = msg + msgsize;
+
+       /* Name. */
+       n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr);
+       if (n < 0)
+               return (NS_TSIG_ERROR_NO_SPACE);
+       cp += n;
+
+       /* Type, class, ttl, length (not filled in yet). */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       NS_PUT16(ns_t_tsig, cp);
+       NS_PUT16(ns_c_any, cp);
+       NS_PUT32(0, cp);                /* TTL */
+       lenp = cp;
+       cp += 2;
+
+       /* Alg. */
+       n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
+       if (n < 0)
+               return (NS_TSIG_ERROR_NO_SPACE);
+       cp += n;
+       
+       /* Time. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       NS_PUT16(0, cp);
+       timesigned = time(NULL);
+       NS_PUT32(timesigned, cp);
+       NS_PUT16(NS_TSIG_FUDGE, cp);
+
+       /*
+        * Compute the signature.
+        */
+
+       /* Digest the time signed and fudge. */
+       cp2 = buf;
+       NS_PUT16(0, cp2);       /* Top 16 bits of time */
+       NS_PUT32(timesigned, cp2);
+       NS_PUT16(NS_TSIG_FUDGE, cp2);
+
+       dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                     buf, cp2 - buf, NULL, 0);
+
+       n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
+                         state->sig, sizeof(state->sig));
+       if (n < 0)
+               return (-ns_r_badkey);
+       state->siglen = n;
+
+       /* Add the signature. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + state->siglen);
+       NS_PUT16(state->siglen, cp);
+       memcpy(cp, state->sig, state->siglen);
+       cp += state->siglen;
+
+       /* The original message ID & error. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ);
+       NS_PUT16(ntohs(hp->id), cp);    /* already in network order */
+       NS_PUT16(error, cp);
+
+       /* Other data. */
+       BOUNDS_CHECK(cp, NS_INT16SZ);
+       NS_PUT16(0, cp);
+
+       /* Go back and fill in the length. */
+       NS_PUT16(cp - lenp - NS_INT16SZ, lenp);
+
+       hp->arcount = htons(ntohs(hp->arcount) + 1);
+       *msglen = (cp - msg);
+       return (0);
+}
diff --git a/ns_ttl.c b/ns_ttl.c
new file mode 100644 (file)
index 0000000..5a88f24
--- /dev/null
+++ b/ns_ttl.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_ttl.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Forward. */
+
+static int     fmt1(int t, char s, char **buf, size_t *buflen);
+
+/* Macros. */
+
+#define T(x) if ((x) < 0) return (-1); else (void)NULL
+
+/* Public. */
+
+int
+ns_format_ttl(u_long src, char *dst, size_t dstlen) {
+       char *odst = dst;
+       int secs, mins, hours, days, weeks, x;
+       char *p;
+
+       secs = src % 60;   src /= 60;
+       mins = src % 60;   src /= 60;
+       hours = src % 24;  src /= 24;
+       days = src % 7;    src /= 7;
+       weeks = src;       src = 0;
+
+       x = 0;
+       if (weeks) {
+               T(fmt1(weeks, 'W', &dst, &dstlen));
+               x++;
+       }
+       if (days) {
+               T(fmt1(days, 'D', &dst, &dstlen));
+               x++;
+       }
+       if (hours) {
+               T(fmt1(hours, 'H', &dst, &dstlen));
+               x++;
+       }
+       if (mins) {
+               T(fmt1(mins, 'M', &dst, &dstlen));
+               x++;
+       }
+       if (secs || !(weeks || days || hours || mins)) {
+               T(fmt1(secs, 'S', &dst, &dstlen));
+               x++;
+       }
+
+       if (x > 1) {
+               int ch;
+
+               for (p = odst; (ch = *p) != '\0'; p++)
+                       if (isascii(ch) && isupper(ch))
+                               *p = tolower(ch);
+       }
+
+       return (dst - odst);
+}
+
+int
+ns_parse_ttl(const char *src, u_long *dst) {
+       u_long ttl, tmp;
+       int ch, digits, dirty;
+
+       ttl = 0;
+       tmp = 0;
+       digits = 0;
+       dirty = 0;
+       while ((ch = *src++) != '\0') {
+               if (!isascii(ch) || !isprint(ch))
+                       goto einval;
+               if (isdigit(ch)) {
+                       tmp *= 10;
+                       tmp += (ch - '0');
+                       digits++;
+                       continue;
+               }
+               if (digits == 0)
+                       goto einval;
+               if (islower(ch))
+                       ch = toupper(ch);
+               switch (ch) {
+               case 'W':  tmp *= 7;
+               case 'D':  tmp *= 24;
+               case 'H':  tmp *= 60;
+               case 'M':  tmp *= 60;
+               case 'S':  break;
+               default:   goto einval;
+               }
+               ttl += tmp;
+               tmp = 0;
+               digits = 0;
+               dirty = 1;
+       }
+       if (digits > 0) {
+               if (dirty)
+                       goto einval;
+               else
+                       ttl += tmp;
+       }
+       *dst = ttl;
+       return (0);
+
+ einval:
+       errno = EINVAL;
+       return (-1);
+}
+
+/* Private. */
+
+static int
+fmt1(int t, char s, char **buf, size_t *buflen) {
+       char tmp[50];
+       size_t len;
+
+       len = SPRINTF((tmp, "%d%c", t, s));
+       if (len + 1 > *buflen)
+               return (-1);
+       strcpy(*buf, tmp);
+       *buf += len;
+       *buflen -= len;
+       return (0);
+}
diff --git a/ns_verify.c b/ns_verify.c
new file mode 100644 (file)
index 0000000..58b876b
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __APPLE__
+#ifndef lint
+static const char rcsid[] = "$Id: ns_verify.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif
+#endif
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#include "fd_setsize.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef __APPLE__
+#include <isc/dst.h>
+#include "port_after.h"
+#else
+#include "dst_internal.h"
+#include "res_private.h"
+#endif
+
+/* Private. */
+
+#define BOUNDS_CHECK(ptr, count) \
+       do { \
+               if ((ptr) + (count) > eom) { \
+                       return (NS_TSIG_ERROR_FORMERR); \
+               } \
+       } while (0)
+
+/* Public. */
+
+u_char *
+ns_find_tsig(u_char *msg, u_char *eom) {
+       HEADER *hp = (HEADER *)msg;
+       int n, type;
+       u_char *cp = msg, *start;
+
+       if (msg == NULL || eom == NULL || msg > eom)
+               return (NULL);
+
+       if (cp + NS_HFIXEDSZ >= eom)
+               return (NULL);
+
+       if (hp->arcount == 0)
+               return (NULL);
+
+       cp += NS_HFIXEDSZ;
+
+       n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount));
+       if (n < 0)
+               return (NULL);
+       cp += n;
+
+       n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount));
+       if (n < 0)
+               return (NULL);
+       cp += n;
+
+       n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount));
+       if (n < 0)
+               return (NULL);
+       cp += n;
+
+       n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1);
+       if (n < 0)
+               return (NULL);
+       cp += n;
+
+       start = cp;
+       n = dn_skipname(cp, eom);
+       if (n < 0)
+               return (NULL);
+       cp += n;
+       if (cp + NS_INT16SZ >= eom)
+               return (NULL);
+
+       NS_GET16(type, cp);
+       if (type != ns_t_tsig)
+               return (NULL);
+       return (start);
+}
+
+/* ns_verify
+ * Parameters:
+ *     statp           res stuff
+ *     msg             received message
+ *     msglen          length of message
+ *     key             tsig key used for verifying.
+ *     querysig        (response), the signature in the query
+ *     querysiglen     (response), the length of the signature in the query
+ *     sig             (query), a buffer to hold the signature
+ *     siglen          (query), input - length of signature buffer
+ *                              output - length of signature
+ *
+ * Errors:
+ *     - bad input (-1)
+ *     - invalid dns message (NS_TSIG_ERROR_FORMERR)
+ *     - TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
+ *     - key doesn't match (-ns_r_badkey)
+ *     - TSIG verification fails with BADKEY (-ns_r_badkey)
+ *     - TSIG verification fails with BADSIG (-ns_r_badsig)
+ *     - TSIG verification fails with BADTIME (-ns_r_badtime)
+ *     - TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
+ *     - TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
+ *     - TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
+ */
+int
+ns_verify(u_char *msg, int *msglen, void *k,
+         const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
+         time_t *timesigned, int nostrip)
+{
+       HEADER *hp = (HEADER *)msg;
+       DST_KEY *key = (DST_KEY *)k;
+       u_char *cp = msg, *eom;
+       char name[NS_MAXDNAME], alg[NS_MAXDNAME];
+       u_char *recstart, *rdatastart;
+       u_char *sigstart, *otherstart;
+       int n;
+       int error;
+       u_int16_t type, length;
+       u_int16_t fudge, sigfieldlen, id, otherfieldlen;
+
+       dst_init();
+       if (msg == NULL || msglen == NULL || *msglen < 0)
+               return (-1);
+
+       eom = msg + *msglen;
+
+       recstart = ns_find_tsig(msg, eom);
+       if (recstart == NULL)
+               return (NS_TSIG_ERROR_NO_TSIG);
+
+       cp = recstart;
+
+       /* Read the key name. */
+       n = dn_expand(msg, eom, cp, name, NS_MAXDNAME);
+       if (n < 0)
+               return (NS_TSIG_ERROR_FORMERR);
+       cp += n;
+
+       /* Read the type. */
+       BOUNDS_CHECK(cp, 2*NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       NS_GET16(type, cp);
+       if (type != ns_t_tsig)
+               return (NS_TSIG_ERROR_NO_TSIG);
+
+       /* Skip the class and TTL, save the length. */
+       cp += NS_INT16SZ + NS_INT32SZ;
+       NS_GET16(length, cp);
+       if (eom - cp != length)
+               return (NS_TSIG_ERROR_FORMERR);
+
+       /* Read the algorithm name. */
+       rdatastart = cp;
+       n = dn_expand(msg, eom, cp, alg, NS_MAXDNAME);
+       if (n < 0)
+               return (NS_TSIG_ERROR_FORMERR);
+       if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
+               return (-ns_r_badkey);
+       cp += n;
+
+       /* Read the time signed and fudge. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       cp += NS_INT16SZ;
+       NS_GET32((*timesigned), cp);
+       NS_GET16(fudge, cp);
+
+       /* Read the signature. */
+       BOUNDS_CHECK(cp, NS_INT16SZ);
+       NS_GET16(sigfieldlen, cp);
+       BOUNDS_CHECK(cp, sigfieldlen);
+       sigstart = cp;
+       cp += sigfieldlen;
+
+       /* Read the original id and error. */
+       BOUNDS_CHECK(cp, 2*NS_INT16SZ);
+       NS_GET16(id, cp);
+       NS_GET16(error, cp);
+
+       /* Parse the other data. */
+       BOUNDS_CHECK(cp, NS_INT16SZ);
+       NS_GET16(otherfieldlen, cp);
+       BOUNDS_CHECK(cp, otherfieldlen);
+       otherstart = cp;
+       cp += otherfieldlen;
+
+       if (cp != eom)
+               return (NS_TSIG_ERROR_FORMERR);
+
+       /* Verify that the key used is OK. */
+       if (key != NULL) {
+               if (key->dk_alg != KEY_HMAC_MD5)
+                       return (-ns_r_badkey);
+               if (error != ns_r_badsig && error != ns_r_badkey) {
+                       if (ns_samename(key->dk_key_name, name) != 1)
+                               return (-ns_r_badkey);
+               }
+       }
+
+       hp->arcount = htons(ntohs(hp->arcount) - 1);
+
+       /*
+        * Do the verification.
+        */
+
+       if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+               void *ctx;
+               u_char buf[NS_MAXDNAME];
+               u_char buf2[NS_MAXDNAME];
+
+               /* Digest the query signature, if this is a response. */
+               dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
+               if (querysiglen > 0 && querysig != NULL) {
+                       u_int16_t len_n = htons(querysiglen);
+                       dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+                                       (u_char *)&len_n, NS_INT16SZ, NULL, 0);
+                       dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+                                       querysig, querysiglen, NULL, 0);
+               }
+               
+               /* Digest the message. */
+               dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg,
+                               NULL, 0);
+
+               /* Digest the key name. */
+               n = ns_name_pton(name, buf2, sizeof(buf2));
+               if (n < 0)
+                       return (-1);
+               n = ns_name_ntol(buf2, buf, sizeof(buf));
+               if (n < 0)
+                       return (-1);
+               dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+               /* Digest the class and TTL. */
+               dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+                               recstart + dn_skipname(recstart, eom) + NS_INT16SZ,
+                               NS_INT16SZ + NS_INT32SZ, NULL, 0);
+
+               /* Digest the algorithm. */
+               n = ns_name_pton(alg, buf2, sizeof(buf2));
+               if (n < 0)
+                       return (-1);
+               n = ns_name_ntol(buf2, buf, sizeof(buf));
+               if (n < 0)
+                       return (-1);
+               dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+               /* Digest the time signed and fudge. */
+               dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+                               rdatastart + dn_skipname(rdatastart, eom),
+                               NS_INT16SZ + NS_INT32SZ + NS_INT16SZ, NULL, 0);
+
+               /* Digest the error and other data. */
+               dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+                               otherstart - NS_INT16SZ - NS_INT16SZ,
+                               otherfieldlen + NS_INT16SZ + NS_INT16SZ, NULL, 0);
+
+               n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
+                                   sigstart, sigfieldlen);
+
+               if (n < 0)
+                       return (-ns_r_badsig);
+
+               if (sig != NULL && siglen != NULL) {
+                       if (*siglen < sigfieldlen)
+                               return (NS_TSIG_ERROR_NO_SPACE);
+                       memcpy(sig, sigstart, sigfieldlen);
+                       *siglen = sigfieldlen;
+               }
+       } else {
+               if (sigfieldlen > 0)
+                       return (NS_TSIG_ERROR_FORMERR);
+               if (sig != NULL && siglen != NULL)
+                       *siglen = 0;
+       }
+
+       /* Reset the counter, since we still need to check for badtime. */
+       hp->arcount = htons(ntohs(hp->arcount) + 1);
+
+       /* Verify the time. */
+       if (abs((*timesigned) - time(NULL)) > fudge)
+               return (-ns_r_badtime);
+
+       if (nostrip == 0) {
+               *msglen = recstart - msg;
+               hp->arcount = htons(ntohs(hp->arcount) - 1);
+       }
+
+       if (error != ns_r_noerror)
+               return (error);
+
+       return (0);
+}
+
+int
+ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen,
+                  ns_tcp_tsig_state *state)
+{
+       dst_init();
+       if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
+               return (-1);
+       state->counter = -1;
+       state->key = k;
+       if (state->key->dk_alg != KEY_HMAC_MD5)
+               return (-ns_r_badkey);
+       if (querysiglen > (int)sizeof(state->sig))
+               return (-1);
+       memcpy(state->sig, querysig, querysiglen);
+       state->siglen = querysiglen;
+       return (0);
+}
+
+int
+ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state,
+             int required)
+{
+       HEADER *hp = (HEADER *)msg;
+       u_char *recstart, *rdatastart, *sigstart;
+       unsigned int sigfieldlen, otherfieldlen;
+       u_char *cp, *eom = msg + *msglen, *cp2;
+       char name[NS_MAXDNAME], alg[NS_MAXDNAME];
+       u_char buf[NS_MAXDNAME];
+       int n, type, length, fudge, id, error;
+       time_t timesigned;
+
+       if (msg == NULL || msglen == NULL || state == NULL)
+               return (-1);
+
+       state->counter++;
+       if (state->counter == 0)
+               return (ns_verify(msg, msglen, state->key,
+                                 state->sig, state->siglen,
+                                 state->sig, &state->siglen, &timesigned, 0));
+
+       if (state->siglen > 0) {
+               u_int16_t siglen_n = htons(state->siglen);
+
+               dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx,
+                               NULL, 0, NULL, 0);
+               dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                               (u_char *)&siglen_n, NS_INT16SZ, NULL, 0);
+               dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                               state->sig, state->siglen, NULL, 0);
+               state->siglen = 0;
+       }
+
+       cp = recstart = ns_find_tsig(msg, eom);
+
+       if (recstart == NULL) {
+               if (required)
+                       return (NS_TSIG_ERROR_NO_TSIG);
+               dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                               msg, *msglen, NULL, 0);
+               return (0);
+       }
+
+       hp->arcount = htons(ntohs(hp->arcount) - 1);
+       dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                       msg, recstart - msg, NULL, 0);
+       
+       /* Read the key name. */
+       n = dn_expand(msg, eom, cp, name, NS_MAXDNAME);
+       if (n < 0)
+               return (NS_TSIG_ERROR_FORMERR);
+       cp += n;
+
+       /* Read the type. */
+       BOUNDS_CHECK(cp, 2*NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       NS_GET16(type, cp);
+       if (type != ns_t_tsig)
+               return (NS_TSIG_ERROR_NO_TSIG);
+
+       /* Skip the class and TTL, save the length. */
+       cp += NS_INT16SZ + NS_INT32SZ;
+       NS_GET16(length, cp);
+       if (eom - cp != length)
+               return (NS_TSIG_ERROR_FORMERR);
+
+       /* Read the algorithm name. */
+       rdatastart = cp;
+       n = dn_expand(msg, eom, cp, alg, NS_MAXDNAME);
+       if (n < 0)
+               return (NS_TSIG_ERROR_FORMERR);
+       if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
+               return (-ns_r_badkey);
+       cp += n;
+
+       /* Verify that the key used is OK. */
+       if ((ns_samename(state->key->dk_key_name, name) != 1 ||
+            state->key->dk_alg != KEY_HMAC_MD5))
+               return (-ns_r_badkey);
+
+       /* Read the time signed and fudge. */
+       BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
+       cp += NS_INT16SZ;
+       NS_GET32(timesigned, cp);
+       NS_GET16(fudge, cp);
+
+       /* Read the signature. */
+       BOUNDS_CHECK(cp, NS_INT16SZ);
+       NS_GET16(sigfieldlen, cp);
+       BOUNDS_CHECK(cp, sigfieldlen);
+       sigstart = cp;
+       cp += sigfieldlen;
+
+       /* Read the original id and error. */
+       BOUNDS_CHECK(cp, 2*NS_INT16SZ);
+       NS_GET16(id, cp);
+       NS_GET16(error, cp);
+
+       /* Parse the other data. */
+       BOUNDS_CHECK(cp, NS_INT16SZ);
+       NS_GET16(otherfieldlen, cp);
+       BOUNDS_CHECK(cp, otherfieldlen);
+       cp += otherfieldlen;
+
+       if (cp != eom)
+               return (NS_TSIG_ERROR_FORMERR);
+
+       /*
+        * Do the verification.
+        */
+
+       /* Digest the time signed and fudge. */
+       cp2 = buf;
+       NS_PUT16(0, cp2);       /* Top 16 bits of time. */
+       NS_PUT32(timesigned, cp2);
+       NS_PUT16(NS_TSIG_FUDGE, cp2);
+
+       dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+                       buf, cp2 - buf, NULL, 0);
+
+       n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
+                           sigstart, sigfieldlen);
+       if (n < 0)
+               return (-ns_r_badsig);
+
+       if (sigfieldlen > sizeof(state->sig))
+               return (NS_TSIG_ERROR_NO_SPACE);
+
+       memcpy(state->sig, sigstart, sigfieldlen);
+       state->siglen = sigfieldlen;
+
+       /* Verify the time. */
+       if (abs(timesigned - time(NULL)) > fudge)
+               return (-ns_r_badtime);
+
+       *msglen = recstart - msg;
+
+       if (error != ns_r_noerror)
+               return (error);
+
+       return (0);
+}
diff --git a/res_comp.c b/res_comp.c
new file mode 100644 (file)
index 0000000..2b44cca
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 1985, 1993
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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
+ * CORPORATION 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.
+ */
+
+/*
+ * 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 const char sccsid[] = "@(#)res_comp.c   8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$Id: res_comp.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <ctype.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/*
+ * Expand compressed domain name 'comp_dn' to full domain name.
+ * 'msg' is a pointer to the begining of the message,
+ * 'eomorig' points to the first location after the message,
+ * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
+ * Return size of compressed name or -1 if there was an error.
+ */
+int
+dn_expand(const u_char *msg, const u_char *eom, const u_char *src,
+         char *dst, int dstsiz)
+{
+       int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz);
+
+       if (n > 0 && dst[0] == '.')
+               dst[0] = '\0';
+       return (n);
+}
+
+/*
+ * Pack domain name 'exp_dn' in presentation form into 'comp_dn'.
+ * Return the size of the compressed name or -1.
+ * 'length' is the size of the array pointed to by 'comp_dn'.
+ */
+int
+dn_comp(const char *src, u_char *dst, int dstsiz,
+       u_char **dnptrs, u_char **lastdnptr)
+{
+       return (ns_name_compress(src, dst, (size_t)dstsiz,
+                                (const u_char **)dnptrs,
+                                (const u_char **)lastdnptr));
+}
+
+/*
+ * Skip over a compressed domain name. Return the size or -1.
+ */
+int
+dn_skipname(const u_char *ptr, const u_char *eom) {
+       const u_char *saveptr = ptr;
+
+       if (ns_name_skip(&ptr, eom) == -1)
+               return (-1);
+       return (ptr - saveptr);
+}
+
+/*
+ * Verify that a domain name uses an acceptable character set.
+ */
+
+/*
+ * Note the conspicuous absence of ctype macros in these definitions.  On
+ * non-ASCII hosts, we can't depend on string literals or ctype macros to
+ * tell us anything about network-format data.  The rest of the BIND system
+ * is not careful about this, but for some reason, we're doing it right here.
+ */
+#define PERIOD 0x2e
+#define        hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \
+                  || ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c))
+#define        domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+int
+res_hnok(const char *dn) {
+       int ppch = '\0', pch = PERIOD, ch = *dn++;
+
+       while (ch != '\0') {
+               int nch = *dn++;
+
+               if (periodchar(ch)) {
+                       (void)NULL;
+               } else if (periodchar(pch)) {
+                       if (!borderchar(ch))
+                               return (0);
+               } else if (periodchar(nch) || nch == '\0') {
+                       if (!borderchar(ch))
+                               return (0);
+               } else {
+                       if (!middlechar(ch))
+                               return (0);
+               }
+               ppch = pch, pch = ch, ch = nch;
+       }
+       return (1);
+}
+
+/*
+ * hostname-like (A, MX, WKS) owners can have "*" as their first label
+ * but must otherwise be as a host name.
+ */
+int
+res_ownok(const char *dn) {
+       if (asterchar(dn[0])) {
+               if (periodchar(dn[1]))
+                       return (res_hnok(dn+2));
+               if (dn[1] == '\0')
+                       return (1);
+       }
+       return (res_hnok(dn));
+}
+
+/*
+ * SOA RNAMEs and RP RNAMEs can have any printable character in their first
+ * label, but the rest of the name has to look like a host name.
+ */
+int
+res_mailok(const char *dn) {
+       int ch, escaped = 0;
+
+       /* "." is a valid missing representation */
+       if (*dn == '\0')
+               return (1);
+
+       /* otherwise <label>.<hostname> */
+       while ((ch = *dn++) != '\0') {
+               if (!domainchar(ch))
+                       return (0);
+               if (!escaped && periodchar(ch))
+                       break;
+               if (escaped)
+                       escaped = 0;
+               else if (bslashchar(ch))
+                       escaped = 1;
+       }
+       if (periodchar(ch))
+               return (res_hnok(dn));
+       return (0);
+}
+
+/*
+ * This function is quite liberal, since RFC 1034's character sets are only
+ * recommendations.
+ */
+int
+res_dnok(const char *dn) {
+       int ch;
+
+       while ((ch = *dn++) != '\0')
+               if (!domainchar(ch))
+                       return (0);
+       return (1);
+}
+
+#ifdef __APPLE__
+void putlong(u_int32_t src, u_char *dst) { ns_put32(src, dst); }
+void putshort(u_int16_t src, u_char *dst) { ns_put16(src, dst); }
+u_int32_t _getlong(const u_char *src) { return (ns_get32(src)); }
+u_int16_t _getshort(const u_char *src) { return (ns_get16(src)); }
+#else
+#ifdef BIND_4_COMPAT
+/*
+ * This module must export the following externally-visible symbols:
+ *     ___putlong
+ *     ___putshort
+ *     __getlong
+ *     __getshort
+ * Note that one _ comes from C and the others come from us.
+ */
+void putlong(u_int32_t src, u_char *dst) { ns_put32(src, dst); }
+void putshort(u_int16_t src, u_char *dst) { ns_put16(src, dst); }
+#ifndef __ultrix__
+u_int32_t _getlong(const u_char *src) { return (ns_get32(src)); }
+u_int16_t _getshort(const u_char *src) { return (ns_get16(src)); }
+#endif /*__ultrix__*/
+#endif /*BIND_4_COMPAT*/
+#endif /* __APPLE__ */
diff --git a/res_data.c b/res_data.c
new file mode 100644 (file)
index 0000000..64af8f3
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 1995-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 const char rcsid[] = "$Id: res_data.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <res_update.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "res_private.h"
+
+static struct __res_state *_res_static;
+
+#ifdef USE__RES_9
+struct __res_9_state _res_9;
+#endif
+
+extern int __res_vinit(res_state, int);
+
+const char *__res_opcodes[] = {
+       "QUERY",
+       "IQUERY",
+       "CQUERYM",
+       "CQUERYU",      /* experimental */
+       "NOTIFY",       /* experimental */
+       "UPDATE",
+       "6",
+       "7",
+       "8",
+       "9",
+       "10",
+       "11",
+       "12",
+       "13",
+       "ZONEINIT",
+       "ZONEREF",
+};
+
+void
+__h_errno_set(struct __res_state *res, int err)
+{
+       h_errno = res->res_h_errno = err;
+}
+
+void
+res_client_close(res_state res)
+{
+       if (res == NULL) return;
+
+       if (res->_u._ext.ext != NULL) free(res->_u._ext.ext);
+       free(res);
+}
+
+res_state
+res_state_new()
+{
+       res_state x;
+
+       x = (res_state)calloc(1, sizeof(struct __res_state));
+       if (x == NULL) return NULL;
+       
+       /*
+        * We use _pad (normally unused) to hold a version number.
+        * We use it provide limited compatibility between versions.
+        */
+       x->_pad = 9;
+
+       x->_u._ext.ext = (struct __res_state_ext *)calloc(1, sizeof(struct __res_state_ext));
+       if (x->_u._ext.ext == NULL)
+       {
+               free(x);
+               return NULL;
+       }
+
+       return x;
+}
+
+int
+res_init(void)
+{
+       extern int __res_vinit(res_state, int);
+       unsigned int save_retrans, save_retry, save_options, save_id;
+       struct __res_state_ext *save_ext;
+
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+
+       save_retrans = RES_TIMEOUT;
+       save_retry = RES_DFLRETRY;
+       save_options = RES_DEFAULT;
+       save_id = res_randomid();
+       save_ext = _res_static->_u._ext.ext;
+
+       if (_res_static->options & RES_INIT)
+       {
+               /* Caller wants to override default options */
+               save_options = _res_static->options;
+               if (_res_static->retrans != 0) save_retrans = _res_static->retrans;
+               if (_res_static->retry != 0) save_retry = _res_static->retry;
+               if (_res_static->id != 0) save_id = _res_static->id;
+       }
+
+       memset(_res_static, 0, sizeof(struct __res_state));
+       _res_static->_vcsock = -1;
+
+       _res_static->retrans = save_retrans;
+       _res_static->retry = save_retry;
+       _res_static->id = save_id;
+       _res_static->options = save_options;
+       _res_static->_u._ext.ext = save_ext;
+
+       _res_static->_pad = 9;
+       
+       if (_res_static->_u._ext.ext == NULL) _res_static->_u._ext.ext = (struct __res_state_ext *)calloc(1, sizeof(struct __res_state_ext));
+
+       return (__res_vinit(_res_static, 1));
+}
+
+int
+res_query(const char *name, int class, int type, u_char *answer, int anslen)   
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1))
+       {
+               RES_SET_H_ERRNO(_res_static, NETDB_INTERNAL);
+               return -1;
+       }
+       return (res_nquery(_res_static, name, class, type, answer, anslen));
+}
+
+void
+fp_nquery(const u_char *msg, int len, FILE *file)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1)) return;
+
+       res_pquery(_res_static, msg, len, file);
+}
+
+void
+fp_query(const u_char *msg, FILE *file)
+{
+       fp_nquery(msg, NS_PACKETSZ, file);
+}
+
+void
+p_query(const u_char *msg)
+{
+       fp_query(msg, stdout);
+}
+
+const char *
+hostalias(const char *name)
+{
+       static char abuf[NS_MAXDNAME];
+
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       return (res_hostalias(_res_static, name, abuf, sizeof abuf));
+}
+
+void
+res_close(void)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       res_nclose(_res_static);
+}
+
+int
+res_isourserver(const struct sockaddr_in *inp)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       return (res_ourserver_p(_res_static, (const struct sockaddr *)inp));
+}
+
+int
+res_nisourserver(const res_state res, const struct sockaddr_in *inp)
+{
+       return (res_ourserver_p(res, (const struct sockaddr *)inp));
+}
+
+int
+res_mkquery(int op, const char *dname, int class, int type, const u_char *data, int datalen, const u_char *newrr_in, u_char *buf, int buflen)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1))
+       {
+               RES_SET_H_ERRNO(_res_static, NETDB_INTERNAL);
+               return -1;
+       }
+
+       return res_nmkquery(_res_static, op, dname, class, type, data, datalen, newrr_in, buf, buflen);
+}
+
+int
+res_querydomain(const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1))
+       {
+               RES_SET_H_ERRNO(_res_static, NETDB_INTERNAL);
+               return -1;
+       }
+
+       return res_nquerydomain(_res_static, name, domain, class, type, answer, anslen);
+}
+
+int
+res_search(const char *name, int class, int type, u_char *answer, int anslen)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1))
+       {
+               RES_SET_H_ERRNO(_res_static, NETDB_INTERNAL);
+               return -1;
+       }
+
+       return res_nsearch(_res_static, name, class, type, answer, anslen);
+}
+
+int
+res_send(const u_char *buf, int buflen, u_char *ans, int anssiz)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1))
+       {
+               /* errno should have been set by res_init() in this case. */
+               return -1;
+       }
+
+       return res_nsend(_res_static, buf, buflen, ans, anssiz);
+}
+
+int
+res_sendsigned(const u_char *buf, int buflen, ns_tsig_key *key, u_char *ans, int anssiz)
+{
+#ifdef USE__RES_9
+       _res_static = &_res_9;
+#else
+       _res_static = &_res;
+#endif
+       
+       if (((_res_static->options & RES_INIT) == 0) && (res_init() == -1))
+       {
+               /* errno should have been set by res_init() in this case. */
+               return -1;
+       }
+
+       return res_nsendsigned(_res_static, buf, buflen, key, ans, anssiz);
+}
diff --git a/res_debug.c b/res_debug.c
new file mode 100644 (file)
index 0000000..0dbe115
--- /dev/null
@@ -0,0 +1,1143 @@
+/*
+ * 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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
+ * CORPORATION 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.
+ */
+
+/*
+ * 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 const char sccsid[] = "@(#)res_debug.c  8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$Id: res_debug.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+#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_sectioncodes[];
+
+/*
+ * Print the current options.
+ */
+void
+fp_resstat(const res_state statp, FILE *file) {
+       u_long mask;
+
+       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 void
+do_section(const res_state statp,
+          ns_msg *handle, ns_sect section,
+          int pflag, FILE *file)
+{
+       int n, sflag, rrnum;
+       static int buflen = 2048;
+       char *buf;
+       ns_opcode opcode;
+       ns_rr rr;
+
+       /*
+        * Print answer records.
+        */
+       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 if (section == ns_s_ar && ns_rr_type(rr) == ns_t_opt) {
+                       u_int32_t ttl = ns_rr_ttl(rr);
+                       fprintf(file,
+                               "; EDNS: version: %u, udp=%u, flags=%04x\n",
+                               (ttl>>16)&0xff, ns_rr_class(rr), ttl&0xffff);
+               } 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++;
+       }
+ cleanup:
+       if (buf != NULL)
+               free(buf);
+}
+
+/*
+ * Print the contents of a query.
+ * This is intended to be primarily a debugging routine.
+ */
+void
+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.
+        */
+       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 ((!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 ((!statp->pfcode) || (statp->pfcode & 
+               (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) {
+               putc('\n',file);
+       }
+       /*
+        * Print the various sections.
+        */
+       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(const u_char *cp, const u_char *msg, int len, FILE *file) {
+       char name[NS_MAXDNAME];
+       int n;
+
+       if ((n = dn_expand(msg, msg + len, cp, name, sizeof name)) < 0)
+               return (NULL);
+       if (name[0] == '\0')
+               putc('.', file);
+       else
+               fputs(name, file);
+       return (cp + n);
+}
+
+const u_char *
+p_cdname(const u_char *cp, const u_char *msg, FILE *file) {
+       return (p_cdnname(cp, msg, NS_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;
+{
+       int n, newlen;
+
+       if ((n = dn_expand(msg, cp + msglen, cp, name, namelen)) < 0)
+               return (NULL);
+       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[NS_MAXDNAME];
+       const u_char *n;
+
+       n = p_fqnname(cp, msg, NS_MAXCDNAME, name, sizeof name);
+       if (n == NULL)
+               return (NULL);
+       fputs(name, file);
+       return (n);
+}
+
+/*
+ * Names of RR classes and qclasses.  Classes and qclasses are the same, except
+ * that ns_c_any is a qclass but not a class.  (You can ask for records of class
+ * ns_c_any, but you can't have any records of that class in the database.)
+ */
+const struct res_sym __res_p_class_syms[] = {
+       {ns_c_in,               "IN",           (char *)0},
+       {ns_c_chaos,    "CHAOS",        (char *)0},
+       {ns_c_hs,               "HS",           (char *)0},
+       {ns_c_hs,               "HESIOD",       (char *)0},
+       {ns_c_any,              "ANY",          (char *)0},
+       {ns_c_none,     "NONE",         (char *)0},
+       {ns_c_in,               (char *)0,      (char *)0}
+};
+
+/*
+ * Names of message sections.
+ */
+#ifdef __APPLE__
+static
+#endif
+const struct res_sym __res_p_default_section_syms[] = {
+       {ns_s_qd,       "QUERY",        (char *)0},
+       {ns_s_an,       "ANSWER",       (char *)0},
+       {ns_s_ns,       "AUTHORITY",    (char *)0},
+       {ns_s_ar,       "ADDITIONAL",   (char *)0},
+       {0,             (char *)0,      (char *)0}
+};
+
+#ifdef __APPLE__
+static
+#endif
+const struct res_sym __res_p_update_section_syms[] = {
+       {ns_s_zn,       "ZONE",         (char *)0},
+       {ns_s_pr,       "PREREQUISITE", (char *)0},
+       {ns_s_ud,       "UPDATE",       (char *)0},
+       {ns_s_ar,       "ADDITIONAL",   (char *)0},
+       {0,             (char *)0,      (char *)0}
+};
+
+const struct res_sym __res_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 __res_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}
+};
+
+/*
+ * 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_tkey,     "TKEY",         "tkey"},
+       {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_a6,       "A6",           "IPv6 Address"},
+       {ns_t_dname,    "DNAME",        "dname"},
+       {ns_t_sink,     "SINK",         "Kitchen Sink (experimental)"},
+       {ns_t_opt,      "OPT",          "EDNS Options"},
+       {ns_t_any,      "ANY",          "\"any\""},
+       {0,             NULL,           NULL}
+};
+
+/*
+ * Names of DNS rcodes.
+ */
+const struct res_sym __res_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}
+};
+
+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);
+               }
+       }
+       if (success)
+               *success = 0;
+       return (syms->number);          /* The default value. */
+}
+
+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);
+               }
+       }
+
+       sprintf(unname, "%d", number);          /* XXX nonreentrant */
+       if (success)
+               *success = 0;
+       return (unname);
+}
+
+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.
+ */
+const char *
+p_type(int type) {
+       int success;
+       const char *result;
+       static char typebuf[20];
+
+       result = sym_ntos(__p_type_syms, type, &success);
+       if (success)
+               return (result);
+       if (type < 0 || type > 0xfff)
+               return ("BADTYPE");
+       sprintf(typebuf, "TYPE%d", type);
+       return (typebuf);
+}
+
+/*
+ * Return a string for the type.
+ */
+const char *
+p_section(int section, int opcode) {
+       const struct res_sym *symbols;
+
+       switch (opcode) {
+       case ns_o_update:
+               symbols = __res_p_update_section_syms;
+               break;
+       default:
+               symbols = __res_p_default_section_syms;
+               break;
+       }
+       return (sym_ntos(symbols, section, (int *)0));
+}
+
+/*
+ * Return a mnemonic for class.
+ */
+const char *
+p_class(int class) {
+       int success;
+       const char *result;
+       static char classbuf[20];
+
+       result = sym_ntos(__res_p_class_syms, class, &success);
+       if (success)
+               return (result);
+       if (class < 0 || class > 0xfff)
+               return ("BADCLASS");
+       sprintf(classbuf, "CLASS%d", class);
+       return (classbuf);
+}
+
+/*
+ * Return a mnemonic for an 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(unimpl)";
+       case RES_USEVC:         return "usevc";
+       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";
+       case RES_INSECURE1:     return "insecure1";
+       case RES_INSECURE2:     return "insecure2";
+       case RES_NOALIASES:     return "noaliases";
+       case RES_USE_INET6:     return "inet6";
+#ifdef RES_USE_EDNS0   /* KAME extension */
+       case RES_USE_EDNS0:     return "edns0";
+#endif
+#ifdef RES_USE_A6
+       case RES_USE_A6:        return "a6";
+#endif
+#ifdef RES_USE_DNAME
+       case RES_USE_DNAME:     return "dname";
+#endif
+#ifdef RES_USE_DNSSEC
+       case RES_USE_DNSSEC:    return "dnssec";
+#endif
+#ifdef RES_NOTLDQUERY
+       case RES_NOTLDQUERY:    return "no-tld-query";
+#endif
+
+                               /* XXX nonreentrant */
+       default:                sprintf(nbuf, "?0x%lx?", (u_long)option);
+                               return (nbuf);
+       }
+}
+
+/*
+ * Return a mnemonic for a time to live.
+ */
+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(__res_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, "%lu.%.2lu", val/100, val%100);
+       return (retbuf);
+}
+
+/* converts ascii size/precision X * 10**Y(cm) to 0xXY.  moves pointer. */
+static u_int8_t
+precsize_aton(const char **strptr) {
+       unsigned int mval = 0, cmval = 0;
+       u_int8_t retval = 0;
+       const char *cp;
+       int exponent;
+       int mantissa;
+
+       cp = *strptr;
+
+       while (isdigit((unsigned char)*cp))
+               mval = mval * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {               /* centimeters */
+               cp++;
+               if (isdigit((unsigned char)*cp)) {
+                       cmval = (*cp++ - '0') * 10;
+                       if (isdigit((unsigned char)*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(const char **latlonstrptr, int *which) {
+       const char *cp;
+       u_int32_t retval;
+       int deg = 0, min = 0, secs = 0, secsfrac = 0;
+
+       cp = *latlonstrptr;
+
+       while (isdigit((unsigned char)*cp))
+               deg = deg * 10 + (*cp++ - '0');
+
+       while (isspace((unsigned char)*cp))
+               cp++;
+
+       if (!(isdigit((unsigned char)*cp)))
+               goto fndhemi;
+
+       while (isdigit((unsigned char)*cp))
+               min = min * 10 + (*cp++ - '0');
+
+       while (isspace((unsigned char)*cp))
+               cp++;
+
+       if (!(isdigit((unsigned char)*cp)))
+               goto fndhemi;
+
+       while (isdigit((unsigned char)*cp))
+               secs = secs * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {               /* decimal seconds */
+               cp++;
+               if (isdigit((unsigned char)*cp)) {
+                       secsfrac = (*cp++ - '0') * 100;
+                       if (isdigit((unsigned char)*cp)) {
+                               secsfrac += (*cp++ - '0') * 10;
+                               if (isdigit((unsigned char)*cp)) {
+                                       secsfrac += (*cp++ - '0');
+                               }
+                       }
+               }
+       }
+
+       while (!isspace((unsigned char)*cp))    /* if any trailing garbage */
+               cp++;
+
+       while (isspace((unsigned char)*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((unsigned char)*cp))    /* if any trailing garbage */
+               cp++;
+
+       while (isspace((unsigned char)*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;
+{
+       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);
+       }
+
+       /* altitude */
+       if (*cp == '-') {
+               altsign = -1;
+               cp++;
+       }
+    
+       if (*cp == '+')
+               cp++;
+
+       while (isdigit((unsigned char)*cp))
+               altmeters = altmeters * 10 + (*cp++ - '0');
+
+       if (*cp == '.') {               /* decimal meters */
+               cp++;
+               if (isdigit((unsigned char)*cp)) {
+                       altfrac = (*cp++ - '0') * 10;
+                       if (isdigit((unsigned char)*cp)) {
+                               altfrac += (*cp++ - '0');
+                       }
+               }
+       }
+
+       alt = (10000000 + (altsign * (altmeters * 100 + altfrac)));
+
+       while (!isspace((unsigned char)*cp) && (cp < maxcp)) /* if trailing garbage or m */
+               cp++;
+
+       while (isspace((unsigned char)*cp) && (cp < maxcp))
+               cp++;
+
+       if (cp >= maxcp)
+               goto defaults;
+
+       siz = precsize_aton(&cp);
+       
+       while (!isspace((unsigned char)*cp) && (cp < maxcp))    /* if trailing garbage or m */
+               cp++;
+
+       while (isspace((unsigned char)*cp) && (cp < maxcp))
+               cp++;
+
+       if (cp >= maxcp)
+               goto defaults;
+
+       hp = precsize_aton(&cp);
+
+       while (!isspace((unsigned char)*cp) && (cp < maxcp))    /* if trailing garbage or m */
+               cp++;
+
+       while (isspace((unsigned char)*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;
+       NS_PUT32(latit,bcp);
+       NS_PUT32(longit,bcp);
+       NS_PUT32(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 const 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;
+       const char *altsign;
+       int altmeters, altfrac;
+
+       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);
+       }
+
+       sizeval = *cp++;
+
+       hpval = *cp++;
+       vpval = *cp++;
+
+       NS_GET32(templ, cp);
+       latval = (templ - ((unsigned)1<<31));
+
+       NS_GET32(templ, cp);
+       longval = (templ - ((unsigned)1<<31));
+
+       NS_GET32(templ, cp);
+       if (templ < referencealt) { /* below WGS 84 spheroid */
+               altval = referencealt - templ;
+               altsign = "-";
+       } else {
+               altval = templ - referencealt;
+               altsign = "";
+       }
+
+       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);
+
+       sizestr = strdup(precsize_ntoa(sizeval));
+       hpstr = strdup(precsize_ntoa(hpval));
+       vpstr = strdup(precsize_ntoa(vpval));
+
+       sprintf(ascii,
+           "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %s%d.%.2dm %sm %sm %sm",
+               latdeg, latmin, latsec, latsecfrac, northsouth,
+               longdeg, longmin, longsec, longsecfrac, eastwest,
+               altsign, altmeters, altfrac,
+               (sizestr != NULL) ? sizestr : error,
+               (hpstr != NULL) ? hpstr : error,
+               (vpstr != NULL) ? vpstr : error);
+
+       if (sizestr != NULL)
+               free(sizestr);
+       if (hpstr != NULL)
+               free(hpstr);
+       if (vpstr != NULL)
+               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++;
+       }
+
+       /* 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
+       gmtime_r(&clock, &time);
+#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);
+}
+
+u_int16_t
+res_nametoclass(const char *buf, int *successp) {
+       unsigned long result;
+       char *endptr;
+       int success;
+
+       result = sym_ston(__res_p_class_syms, buf, &success);
+       if (success)
+               goto done;
+
+       if (strncasecmp(buf, "CLASS", 5) != 0 ||
+           !isdigit((unsigned char)buf[5]))
+               goto done;
+       result = strtoul(buf + 5, &endptr, 10);
+       if (*endptr == '\0' && result <= 0xffff)
+               success = 1;
+ done:
+       if (successp)
+               *successp = success;
+       return (result);
+}
+
+u_int16_t
+res_nametotype(const char *buf, int *successp) {
+       unsigned long result;
+       char *endptr;
+       int success;
+
+       result = sym_ston(__p_type_syms, buf, &success);
+       if (success)
+               goto done;
+
+       if (strncasecmp(buf, "type", 4) != 0 ||
+           !isdigit((unsigned char)buf[4]))
+               goto done;
+       result = strtoul(buf + 4, &endptr, 10);
+       if (*endptr == '\0' && result <= 0xffff)
+               success = 1;
+ done:
+       if (successp)
+               *successp = success;
+       return (result);
+}
diff --git a/res_debug.h b/res_debug.h
new file mode 100644 (file)
index 0000000..4a0aa99
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef _RES_DEBUG_H_
+#define _RES_DEBUG_H_
+
+#ifndef DEBUG
+#   define Dprint(cond, args) /*empty*/
+#   define DprintQ(cond, args, query, size) /*empty*/
+#   define Aerror(statp, file, string, error, address) /*empty*/
+#   define Perror(statp, file, string, error) /*empty*/
+#else
+#   define Dprint(cond, args) if (cond) {fprintf args;} else {}
+#   define DprintQ(cond, args, query, size) if (cond) {\
+                       fprintf args;\
+                       res_pquery(statp, query, size, stdout);\
+               } else {}
+#endif
+
+#endif /* _RES_DEBUG_H_ */ 
diff --git a/res_findzonecut.c b/res_findzonecut.c
new file mode 100644 (file)
index 0000000..c142648
--- /dev/null
@@ -0,0 +1,702 @@
+#ifndef __APPLE__
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: res_findzonecut.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* not lint */
+#endif
+
+/*
+ * Copyright (c) 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.
+ */
+
+/* Import. */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __APPLE__
+#include <isc/list.h>
+#include "port_after.h"
+#else
+#include <res_update.h>
+#endif
+
+#include <resolv.h>
+
+/* Data structures. */
+
+typedef struct rr_a {
+       LINK(struct rr_a)       link;
+       union res_sockaddr_union                addr;
+} rr_a;
+typedef LIST(rr_a) rrset_a;
+
+typedef struct rr_ns {
+       LINK(struct rr_ns)      link;
+       const char *            name;
+       int                     have_v4;
+       int                     have_v6;
+       rrset_a                 addrs;
+} rr_ns;
+typedef LIST(rr_ns) rrset_ns;
+
+/* Forward. */
+
+static int     satisfy(res_state, const char *, rrset_ns *,
+                       union res_sockaddr_union *, int);
+static int     add_addrs(res_state, rr_ns *,
+                         union res_sockaddr_union *, int);
+static int     get_soa(res_state, const char *, ns_class, int,
+                       char *, size_t, char *, size_t,
+                       rrset_ns *);
+static int     get_ns(res_state, const char *, ns_class, int, rrset_ns *);
+static int     get_glue(res_state, ns_class, int, rrset_ns *);
+static int     save_ns(res_state, ns_msg *, ns_sect,
+                       const char *, ns_class, int, rrset_ns *);
+static int     save_a(res_state, ns_msg *, ns_sect,
+                      const char *, ns_class, int, rr_ns *);
+static void    free_nsrrset(rrset_ns *);
+static void    free_nsrr(rrset_ns *, rr_ns *);
+static rr_ns * find_ns(rrset_ns *, const char *);
+static int     do_query(res_state, const char *, ns_class, ns_type,
+                        u_char *, ns_msg *);
+#ifndef __APPLE__
+static void    res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
+#else
+static void    res_dprintf(const char *, ...);
+#endif
+
+/* Macros. */
+
+#define DPRINTF(x) do {\
+               int save_errno = errno; \
+               if ((statp->options & RES_DEBUG) != 0) res_dprintf x; \
+               errno = save_errno; \
+       } while (0)
+
+/* Public. */
+
+/*
+ * int
+ * res_findzonecut(res, dname, class, zname, zsize, addrs, naddrs)
+ *     find enclosing zone for a <dname,class>, and some server addresses
+ * parameters:
+ *     res - resolver context to work within (is modified)
+ *     dname - domain name whose enclosing zone is desired
+ *     class - class of dname (and its enclosing zone)
+ *     zname - found zone name
+ *     zsize - allocated size of zname
+ *     addrs - found server addresses
+ *     naddrs - max number of addrs
+ * return values:
+ *     < 0 - an error occurred (check errno)
+ *     = 0 - zname is now valid, but addrs[] wasn't changed
+ *     > 0 - zname is now valid, and return value is number of addrs[] found
+ * notes:
+ *     this function calls res_nsend() which means it depends on correctly
+ *     functioning recursive nameservers (usually defined in /etc/resolv.conf
+ *     or its local equivilent).
+ *
+ *     we start by asking for an SOA<dname,class>.  if we get one as an
+ *     answer, that just means <dname,class> is a zone top, which is fine.
+ *     more than likely we'll be told to go pound sand, in the form of a
+ *     negative answer.
+ *
+ *     note that we are not prepared to deal with referrals since that would
+ *     only come from authority servers and our correctly functioning local
+ *     recursive server would have followed the referral and got us something
+ *     more definite.
+ *
+ *     if the authority section contains an SOA, this SOA should also be the
+ *     closest enclosing zone, since any intermediary zone cuts would've been
+ *     returned as referrals and dealt with by our correctly functioning local
+ *     recursive name server.  but an SOA in the authority section should NOT
+ *     match our dname (since that would have been returned in the answer
+ *     section).  an authority section SOA has to be "above" our dname.
+ *
+ *     however, since authority section SOA's were once optional, it's
+ *     possible that we'll have to go hunting for the enclosing SOA by
+ *     ripping labels off the front of our dname -- this is known as "doing
+ *     it the hard way."
+ *
+ *     ultimately we want some server addresses, which are ideally the ones
+ *     pertaining to the SOA.MNAME, but only if there is a matching NS RR.
+ *     so the second phase (after we find an SOA) is to go looking for the
+ *     NS RRset for that SOA's zone.
+ *
+ *     no answer section processed by this code is allowed to contain CNAME
+ *     or DNAME RR's.  for the SOA query this means we strip a label and
+ *     keep going.  for the NS and A queries this means we just give up.
+ */
+
+int
+res_findzonecut(res_state statp, const char *dname, ns_class class, int opts,
+               char *zname, size_t zsize, struct in_addr *addrs, int naddrs) {
+       int result, i;
+       union res_sockaddr_union *u;
+
+       
+       opts |= RES_IPV4ONLY;
+       opts &= ~RES_IPV6ONLY;
+
+       u = calloc(naddrs, sizeof(*u));
+       if (u == NULL)
+               return(-1);
+
+       result = res_findzonecut2(statp, dname, class, opts, zname, zsize,
+                                 u, naddrs);
+
+       for (i = 0; i < result; i++) {
+               addrs[i] = u[i].sin.sin_addr;
+       }
+       free(u);
+       return (result);
+}
+
+int
+res_findzonecut2(res_state statp, const char *dname, ns_class class, int opts,
+                char *zname, size_t zsize, union res_sockaddr_union *addrs,
+                int naddrs)
+{
+       char mname[NS_MAXDNAME];
+       u_long save_pfcode;
+       rrset_ns nsrrs;
+       int n;
+
+       DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d",
+                dname, p_class(class), (long)zsize, naddrs));
+       save_pfcode = statp->pfcode;
+       statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX |
+                        RES_PRF_QUES | RES_PRF_ANS |
+                        RES_PRF_AUTH | RES_PRF_ADD;
+       INIT_LIST(nsrrs);
+
+       DPRINTF(("get the soa, and see if it has enough glue"));
+       if ((n = get_soa(statp, dname, class, opts, zname, zsize,
+                        mname, sizeof mname, &nsrrs)) < 0 ||
+           ((opts & RES_EXHAUSTIVE) == 0 &&
+            (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
+               goto done;
+
+       DPRINTF(("get the ns rrset and see if it has enough glue"));
+       if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 ||
+           ((opts & RES_EXHAUSTIVE) == 0 &&
+            (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
+               goto done;
+
+       DPRINTF(("get the missing glue and see if it's finally enough"));
+       if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0)
+               n = satisfy(statp, mname, &nsrrs, addrs, naddrs);
+
+ done:
+       DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK"));
+       free_nsrrset(&nsrrs);
+       statp->pfcode = save_pfcode;
+       return (n);
+}
+
+/* Private. */
+
+static int
+satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp,
+       union res_sockaddr_union *addrs, int naddrs)
+{
+       rr_ns *nsrr;
+       int n, x;
+
+       n = 0;
+       nsrr = find_ns(nsrrsp, mname);
+       if (nsrr != NULL) {
+               x = add_addrs(statp, nsrr, addrs, naddrs);
+               addrs += x;
+               naddrs -= x;
+               n += x;
+       }
+       for (nsrr = HEAD(*nsrrsp);
+            nsrr != NULL && naddrs > 0;
+            nsrr = NEXT(nsrr, link))
+               if (ns_samename(nsrr->name, mname) != 1) {
+                       x = add_addrs(statp, nsrr, addrs, naddrs);
+                       addrs += x;
+                       naddrs -= x;
+                       n += x;
+               }
+       DPRINTF(("satisfy(%s): %d", mname, n));
+       return (n);
+}
+
+static int
+add_addrs(res_state statp, rr_ns *nsrr,
+         union res_sockaddr_union *addrs, int naddrs)
+{
+       rr_a *arr;
+       int n = 0;
+
+       for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) {
+               if (naddrs <= 0)
+                       return (0);
+               *addrs++ = arr->addr;
+               naddrs--;
+               n++;
+       }
+       DPRINTF(("add_addrs: %d", n));
+       return (n);
+}
+
+static int
+get_soa(res_state statp, const char *dname, ns_class class, int opts,
+       char *zname, size_t zsize, char *mname, size_t msize,
+       rrset_ns *nsrrsp)
+{
+       char tname[NS_MAXDNAME];
+       u_char resp[NS_PACKETSZ];
+       int n, i, ancount, nscount;
+       ns_sect sect;
+       ns_msg msg;
+       u_int rcode;
+
+       /*
+        * Find closest enclosing SOA, even if it's for the root zone.
+        */
+
+       /* First canonicalize dname (exactly one unescaped trailing "."). */
+       if (ns_makecanon(dname, tname, sizeof tname) < 0)
+               return (-1);
+       dname = tname;
+
+       /* Now grovel the subdomains, hunting for an SOA answer or auth. */
+       for (;;) {
+               /* Leading or inter-label '.' are skipped here. */
+               while (*dname == '.')
+                       dname++;
+
+               /* Is there an SOA? */
+               n = do_query(statp, dname, class, ns_t_soa, resp, &msg);
+               if (n < 0) {
+                       DPRINTF(("get_soa: do_query('%s', %s) failed (%d)",
+                                dname, p_class(class), n));
+                       return (-1);
+               }
+               if (n > 0) {
+                       DPRINTF(("get_soa: CNAME or DNAME found"));
+                       sect = ns_s_max, n = 0;
+               } else {
+                       rcode = ns_msg_getflag(msg, ns_f_rcode);
+                       ancount = ns_msg_count(msg, ns_s_an);
+                       nscount = ns_msg_count(msg, ns_s_ns);
+                       if (ancount > 0 && rcode == ns_r_noerror)
+                               sect = ns_s_an, n = ancount;
+                       else if (nscount > 0)
+                               sect = ns_s_ns, n = nscount;
+                       else
+                               sect = ns_s_max, n = 0;
+               }
+               for (i = 0; i < n; i++) {
+                       const char *t;
+                       const u_char *rdata;
+                       int rdlen;
+                       ns_rr rr;
+
+                       if (ns_parserr(&msg, sect, i, &rr) < 0) {
+                               DPRINTF(("get_soa: ns_parserr(%s, %d) failed",
+                                        p_section(sect, ns_o_query), i));
+                               return (-1);
+                       }
+                       if (ns_rr_type(rr) == ns_t_cname ||
+                           ns_rr_type(rr) == ns_t_dname)
+                               break;
+                       if (ns_rr_type(rr) != ns_t_soa ||
+                           ns_rr_class(rr) != class)
+                               continue;
+                       t = ns_rr_name(rr);
+                       switch (sect) {
+                       case ns_s_an:
+                               if (ns_samedomain(dname, t) == 0) {
+                                       DPRINTF(("get_soa: ns_samedomain('%s', '%s') == 0",
+                                                dname, t));
+                                       errno = EPROTOTYPE;
+                                       return (-1);
+                               }
+                               break;
+                       case ns_s_ns:
+                               if (ns_samename(dname, t) == 1 ||
+                                   ns_samedomain(dname, t) == 0) {
+                                       DPRINTF(("get_soa: ns_samename() || !ns_samedomain('%s', '%s')",
+                                                dname, t));
+                                       errno = EPROTOTYPE;
+                                       return (-1);
+                               }
+                               break;
+                       default:
+                               abort();
+                       }
+                       if (strlen(t) + 1 > zsize) {
+                               DPRINTF(("get_soa: zname(%d) too small (%d)",
+                                        zsize, strlen(t) + 1));
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       strcpy(zname, t);
+                       rdata = ns_rr_rdata(rr);
+                       rdlen = ns_rr_rdlen(rr);
+                       if (ns_name_uncompress(resp, ns_msg_end(msg), rdata,
+                                              mname, msize) < 0) {
+                               DPRINTF(("get_soa: ns_name_uncompress failed"));
+                               return (-1);
+                       }
+                       if (save_ns(statp, &msg, ns_s_ns,
+                                   zname, class, opts, nsrrsp) < 0) {
+                               DPRINTF(("get_soa: save_ns failed"));
+                               return (-1);
+                       }
+                       return (0);
+               }
+
+               /* If we're out of labels, then not even "." has an SOA! */
+               if (*dname == '\0')
+                       break;
+
+               /* Find label-terminating "."; top of loop will skip it. */
+               while (*dname != '.') {
+                       if (*dname == '\\')
+                               if (*++dname == '\0') {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                       dname++;
+               }
+       }
+       DPRINTF(("get_soa: out of labels"));
+       errno = EDESTADDRREQ;
+       return (-1);
+}
+
+static int
+get_ns(res_state statp, const char *zname, ns_class class, int opts,
+      rrset_ns *nsrrsp)
+{
+       u_char resp[NS_PACKETSZ];
+       ns_msg msg;
+       int n;
+
+       /* Go and get the NS RRs for this zone. */
+       n = do_query(statp, zname, class, ns_t_ns, resp, &msg);
+       if (n != 0) {
+               DPRINTF(("get_ns: do_query('%s', %s) failed (%d)",
+                        zname, p_class(class), n));
+               return (-1);
+       }
+
+       /* Remember the NS RRs and associated A RRs that came back. */
+       if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) {
+               DPRINTF(("get_ns save_ns('%s', %s) failed",
+                        zname, p_class(class)));
+               return (-1);
+       }
+
+       return (0);
+}
+
+static int
+get_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) {
+       rr_ns *nsrr, *nsrr_n;
+
+       /* Go and get the A RRs for each empty NS RR on our list. */
+       for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) {
+               u_char resp[NS_PACKETSZ];
+               ns_msg msg;
+               int n;
+
+               nsrr_n = NEXT(nsrr, link);
+
+               if (!nsrr->have_v4) {
+                       n = do_query(statp, nsrr->name, class, ns_t_a,
+                                    resp, &msg);
+                       if (n < 0) {
+                               DPRINTF(("get_glue: do_query('%s', %s') failed",
+                                        nsrr->name, p_class(class)));
+                               return (-1);
+                       }
+                       if (n > 0) {
+                               DPRINTF((
+                       "get_glue: do_query('%s', %s') CNAME or DNAME found",
+                                        nsrr->name, p_class(class)));
+                       }
+                       if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
+                                  opts, nsrr) < 0) {
+                               DPRINTF(("get_glue: save_r('%s', %s) failed",
+                                        nsrr->name, p_class(class)));
+                               return (-1);
+                       }
+               }
+
+               if (!nsrr->have_v6) {
+                       n = do_query(statp, nsrr->name, class, ns_t_aaaa,
+                                    resp, &msg);
+                       if (n < 0) {
+                               DPRINTF(("get_glue: do_query('%s', %s') failed",
+                                        nsrr->name, p_class(class)));
+                               return (-1);
+                       }
+                       if (n > 0) {
+                               DPRINTF((
+                       "get_glue: do_query('%s', %s') CNAME or DNAME found",
+                                        nsrr->name, p_class(class)));
+                       }
+                       if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
+                                  opts, nsrr) < 0) {
+                               DPRINTF(("get_glue: save_r('%s', %s) failed",
+                                        nsrr->name, p_class(class)));
+                               return (-1);
+                       }
+               }
+
+               /* If it's still empty, it's just chaff. */
+               if (EMPTY(nsrr->addrs)) {
+                       DPRINTF(("get_glue: removing empty '%s' NS",
+                                nsrr->name));
+                       free_nsrr(nsrrsp, nsrr);
+               }
+       }
+       return (0);
+}
+
+static int
+save_ns(res_state statp, ns_msg *msg, ns_sect sect,
+       const char *owner, ns_class class, int opts,
+       rrset_ns *nsrrsp)
+{
+       int i;
+
+       for (i = 0; i < ns_msg_count(*msg, sect); i++) {
+               char tname[NS_MAXDNAME];
+               const u_char *rdata;
+               rr_ns *nsrr;
+               ns_rr rr;
+               int rdlen;
+
+               if (ns_parserr(msg, sect, i, &rr) < 0) {
+                       DPRINTF(("save_ns: ns_parserr(%s, %d) failed",
+                                p_section(sect, ns_o_query), i));
+                       return (-1);
+               }
+               if (ns_rr_type(rr) != ns_t_ns ||
+                   ns_rr_class(rr) != class ||
+                   ns_samename(ns_rr_name(rr), owner) != 1)
+                       continue;
+               nsrr = find_ns(nsrrsp, ns_rr_name(rr));
+               if (nsrr == NULL) {
+                       nsrr = malloc(sizeof *nsrr);
+                       if (nsrr == NULL) {
+                               DPRINTF(("save_ns: malloc failed"));
+                               return (-1);
+                       }
+                       rdata = ns_rr_rdata(rr);
+                       rdlen = ns_rr_rdlen(rr);
+                       if (ns_name_uncompress(ns_msg_base(*msg),
+                                              ns_msg_end(*msg), rdata,
+                                              tname, sizeof tname) < 0) {
+                               DPRINTF(("save_ns: ns_name_uncompress failed"));
+                               free(nsrr);
+                               return (-1);
+                       }
+                       nsrr->name = strdup(tname);
+                       if (nsrr->name == NULL) {
+                               DPRINTF(("save_ns: strdup failed"));
+                               free(nsrr);
+                               return (-1);
+                       }
+                       INIT_LINK(nsrr, link);
+                       INIT_LIST(nsrr->addrs);
+                       nsrr->have_v4 = 0;
+                       nsrr->have_v6 = 0;
+                       APPEND(*nsrrsp, nsrr, link);
+               }
+               if (save_a(statp, msg, ns_s_ar,
+                          nsrr->name, class, opts, nsrr) < 0) {
+                       DPRINTF(("save_ns: save_r('%s', %s) failed",
+                                nsrr->name, p_class(class)));
+                       return (-1);
+               }
+       }
+       return (0);
+}
+
+static int
+save_a(res_state statp, ns_msg *msg, ns_sect sect,
+       const char *owner, ns_class class, int opts,
+       rr_ns *nsrr)
+{
+       int i;
+
+       for (i = 0; i < ns_msg_count(*msg, sect); i++) {
+               ns_rr rr;
+               rr_a *arr;
+
+               if (ns_parserr(msg, sect, i, &rr) < 0) {
+                       DPRINTF(("save_a: ns_parserr(%s, %d) failed",
+                                p_section(sect, ns_o_query), i));
+                       return (-1);
+               }
+               if ((ns_rr_type(rr) != ns_t_a && ns_rr_type(rr) != ns_t_aaaa) ||
+                   ns_rr_class(rr) != class ||
+                   ns_samename(ns_rr_name(rr), owner) != 1 ||
+                   ns_rr_rdlen(rr) != NS_INADDRSZ)
+                       continue;
+               if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa)
+                       continue;
+               if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a)
+                       continue;
+               arr = malloc(sizeof *arr);
+               if (arr == NULL) {
+                       DPRINTF(("save_a: malloc failed"));
+                       return (-1);
+               }
+               INIT_LINK(arr, link);
+               memset(&arr->addr, 0, sizeof(arr->addr));
+               switch (ns_rr_type(rr)) {
+               case ns_t_a:
+                       arr->addr.sin.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+                       arr->addr.sin.sin_len = sizeof(arr->addr.sin);
+#endif
+                       memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr),
+                              NS_INADDRSZ);
+                       arr->addr.sin.sin_port = htons(NS_DEFAULTPORT);
+                       nsrr->have_v4 = 1;
+                       break;
+               case ns_t_aaaa:
+                       arr->addr.sin6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+                       arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6);
+#endif
+                       memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16);
+                       arr->addr.sin.sin_port = htons(NS_DEFAULTPORT);
+                       nsrr->have_v6 = 1;
+                       break;
+               default:
+                       abort();
+               }
+               APPEND(nsrr->addrs, arr, link);
+       }
+       return (0);
+}
+
+static void
+free_nsrrset(rrset_ns *nsrrsp) {
+       rr_ns *nsrr;
+
+       while ((nsrr = HEAD(*nsrrsp)) != NULL)
+               free_nsrr(nsrrsp, nsrr);
+}
+
+static void
+free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) {
+       rr_a *arr;
+       char *tmp;
+
+       while ((arr = HEAD(nsrr->addrs)) != NULL) {
+               UNLINK(nsrr->addrs, arr, link);
+               free(arr);
+       }
+#ifdef __APPLE__
+       tmp = (char *)nsrr->name;
+#else
+       DE_CONST(nsrr->name, tmp);
+#endif
+       free(tmp);
+       UNLINK(*nsrrsp, nsrr, link);
+       free(nsrr);
+}
+
+static rr_ns *
+find_ns(rrset_ns *nsrrsp, const char *dname) {
+       rr_ns *nsrr;
+
+       for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link))
+               if (ns_samename(nsrr->name, dname) == 1)
+                       return (nsrr);
+       return (NULL);
+}
+
+static int
+do_query(res_state statp, const char *dname, ns_class class, ns_type qtype,
+        u_char *resp, ns_msg *msg)
+{
+       u_char req[NS_PACKETSZ];
+       int i, n;
+
+       n = res_nmkquery(statp, ns_o_query, dname, class, qtype,
+                        NULL, 0, NULL, req, NS_PACKETSZ);
+       if (n < 0) {
+               DPRINTF(("do_query: res_nmkquery failed"));
+               return (-1);
+       }
+       n = res_nsend(statp, req, n, resp, NS_PACKETSZ);
+       if (n < 0) {
+               DPRINTF(("do_query: res_nsend failed"));
+               return (-1);
+       }
+       if (n == 0) {
+               DPRINTF(("do_query: res_nsend returned 0"));
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       if (ns_initparse(resp, n, msg) < 0) {
+               DPRINTF(("do_query: ns_initparse failed"));
+               return (-1);
+       }
+       n = 0;
+       for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) {
+               ns_rr rr;
+
+               if (ns_parserr(msg, ns_s_an, i, &rr) < 0) {
+                       DPRINTF(("do_query: ns_parserr failed"));
+                       return (-1);
+               }
+               n += (ns_rr_class(rr) == class &&
+                     (ns_rr_type(rr) == ns_t_cname ||
+                      ns_rr_type(rr) == ns_t_dname));
+       }
+       return (n);
+}
+
+static void
+res_dprintf(const char *fmt, ...) {
+       va_list ap;
+
+       va_start(ap, fmt);
+       fputs(";; res_findzonecut: ", stderr);
+       vfprintf(stderr, fmt, ap);
+       fputc('\n', stderr);
+       va_end(ap);
+}
diff --git a/res_init.c b/res_init.c
new file mode 100644 (file)
index 0000000..0057cbb
--- /dev/null
@@ -0,0 +1,1562 @@
+/*
+ * Copyright (c) 1985, 1989, 1993
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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
+ * CORPORATION 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.
+ */
+
+/*
+ * 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 const char sccsid[] = "@(#)res_init.c   8.1 (Berkeley) 6/7/93";
+static const char rcsid[] = "$Id: res_init.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#ifdef __APPLE__
+#include <fcntl.h>
+#include <dnsinfo.h>
+#endif
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */
+#include <resolv.h>
+
+#include "res_private.h"
+
+/* Options.  Should all be left alone. */
+#define RESOLVSORT
+#define DEBUG
+
+static void res_setoptions __P((res_state, const char *, const char *));
+extern res_state res_state_new();
+
+#ifdef RESOLVSORT
+static const char sort_mask[] = "/&";
+#define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL)
+static u_int32_t net_mask __P((struct in_addr));
+#endif
+
+#if !defined(isascii)  /* XXX - could be a function */
+# define isascii(c) (!(c & 0200))
+#endif
+
+#define INET_NTOP_AF_INET_OFFSET 4
+#define INET_NTOP_AF_INET6_OFFSET 8
+
+/*
+ * Resolver state default settings.
+ */
+
+res_state 
+res_build_start(res_state x)
+{
+       res_state res;
+       int rf;
+
+       res = NULL;
+
+       if (x == NULL)
+       {
+               res = res_state_new();
+       }
+       else
+       {
+               if ((x->options & RES_INIT) != 0) res_ndestroy(x);
+               res = x;
+       }
+
+       if (res == NULL) return NULL;
+
+       /* res_ndestroy frees _u._ext.ext so we create a new one if necessary */
+       if (res->_u._ext.ext == NULL) res->_u._ext.ext = (struct __res_state_ext *)calloc(1, sizeof(struct __res_state_ext));
+
+       /* make sure version is set */
+       res->_pad = 9;
+
+       res->retry = RES_DFLRETRY;
+       res->options = RES_DEFAULT;
+       
+       rf = open("/dev/random", O_RDONLY, 0);
+       read(rf, &(res->id), 2);
+       close(rf);
+       
+       res->ndots = 1;
+       res->_vcsock = -1;
+
+       if (res->_u._ext.ext != NULL)
+       {
+               strcpy(res->_u._ext.ext->nsuffix, "ip6.arpa");
+               strcpy(res->_u._ext.ext->nsuffix2, "ip6.int");
+               strcpy(res->_u._ext.ext->bsuffix, "ip6.arpa");
+       }
+
+       return res;
+}
+
+int
+res_build_finish(res_state res, uint32_t timeout, uint16_t port)
+{
+       if (res == NULL) return -1;
+       
+       if (res->nscount == 0)
+       {
+#ifdef USELOOPBACK
+               res->nsaddr_list[0].sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
+#else
+               res->nsaddr_list[0].sin_addr.s_addr = INADDR_ANY;
+#endif
+               
+               res->nsaddr_list[0].sin_family = AF_INET;
+               res->nsaddr_list[0].sin_port = htons(port);
+#ifdef HAVE_SA_LEN
+               res->nsaddr_list[0].sin_len = sizeof(struct sockaddr_in);
+#endif
+               
+               res->nscount = 1;
+
+               /*
+                * Force loopback queries to use TCP
+                * so we don't take a timeout if named isn't running.
+                */
+               res->options |= RES_USEVC;
+       }
+       
+       if (timeout == 0) res->retrans = RES_MAXRETRANS;
+       else res->retrans = timeout;
+
+       res->options |= RES_INIT;
+       return 0;
+}
+
+int
+res_build_sortlist(res_state res, struct in_addr addr, struct in_addr mask)
+{
+       uint32_t n;
+       
+       if (res == NULL) return -1;
+       
+       n = res->nsort;
+       if (n >= MAXRESOLVSORT) return -1;
+       
+       res->sort_list[n].addr = addr;
+       res->sort_list[n].mask = mask.s_addr;
+       n++;
+       res->nsort = n;
+       
+       return 0;
+}
+
+char *
+res_next_word(char **p)
+{
+       char *s, *x;
+
+       if (p == NULL) return NULL;
+       if (*p == NULL) return NULL;
+
+       s = *p;
+
+       /* Skip leading white space */
+       while ((*s == ' ') || (*s == '\t') || (*s == '\n')) s++;
+       *p = s;
+
+       if (*s == '\0') return NULL;
+
+       /* Find end of word */
+       x = s;
+       while ((*x != ' ') && (*x != '\t') && (*x != '\n') && (*x != '\0')) x++;
+       if (*x != '\0')
+       {
+               *x = '\0';
+               x++;
+       }
+
+       *p = x;
+
+       if (s == x) return NULL;
+       return s;
+}
+
+int
+res_build(res_state res, uint16_t port, uint32_t *nsrch, char *key, char *val)
+{
+       int32_t dotcount, semicount, status;
+       int32_t ival, len;
+       char *p, *lastdot;
+       uint16_t aport;
+       struct in_addr addr4, mask;
+       struct sockaddr_in sin4;
+       struct in6_addr addr6;
+       struct sockaddr_in6 sin6;
+                               
+       if (res == NULL) return -1;     
+       
+       if (!strcmp(key, "domain"))
+       {
+               strncpy(res->defdname, val, sizeof(res->defdname) - 1);
+               res->defdname[sizeof(res->defdname) - 1] = '\0';
+               return 0;
+       }
+
+       else if (!strcmp(key, "search"))
+       {
+               len = strlen(val) + 1;
+               ival = sizeof(res->defdname);
+               p = res->defdname;
+
+               if ((*nsrch) == 0)
+               {
+                       memset(res->defdname, 0, sizeof(res->defdname));
+               }
+               else
+               {
+                       p = res->dnsrch[*nsrch - 1];
+                       for (; (ival > 0) && (*p != '\0'); ival--) p++;
+                       if (*p == '\0') p++;
+               }
+
+               if (len > ival) return -1;
+
+               memcpy(p, val, len);
+               res->dnsrch[*nsrch] = p;
+               *nsrch = *nsrch + 1;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "nameserver"))
+       {               
+               dotcount = 0;
+               semicount = 0;
+               lastdot = NULL;
+               aport = port;
+               
+               for (p = val; *p != '\0'; p++)
+               {
+                       if (*p == ':') semicount++;
+                       else if (*p == '.')
+                       {
+                               dotcount++;
+                               lastdot = p;
+                       }
+               }
+               
+               if ((dotcount == 4) || ((semicount > 0) && (lastdot != NULL)))
+               {
+                       aport = atoi(lastdot + 1);
+                       if (aport == 0) aport = port;
+                       *lastdot = '\0';
+               }
+
+               memset(&addr4, 0, sizeof(struct in_addr));
+               memset(&sin4, 0, sizeof(struct sockaddr_in));
+               
+               memset(&addr6, 0, sizeof(struct in6_addr));
+               memset(&sin6, 0, sizeof(struct sockaddr_in6));
+               
+               status = inet_pton(AF_INET6, val, &addr6);
+               if (status == 1)
+               {
+                       sin6.sin6_addr = addr6;
+                       sin6.sin6_family = AF_INET6;
+                       sin6.sin6_port = htons(aport);
+                       sin6.sin6_len = sizeof(struct sockaddr_in6);
+                       if (res->_u._ext.ext != NULL)
+                       {
+                               memcpy(&(res->_u._ext.ext->nsaddrs[res->nscount]), &sin6, sizeof(struct sockaddr_in6));
+                       }
+                       res->nsaddr_list[res->nscount].sin_family = 0;
+                       res->nscount++;
+               }
+               else
+               {
+                       status = inet_pton(AF_INET, val, &addr4);
+                       if (status == 1)
+                       {
+                               sin4.sin_addr = addr4;
+                               sin4.sin_family = AF_INET;
+                               sin4.sin_port = htons(aport);
+                               sin4.sin_len = sizeof(struct sockaddr_in);
+                               if (res->_u._ext.ext != NULL)
+                               {
+                                       memcpy(&(res->_u._ext.ext->nsaddrs[res->nscount]), &sin4, sizeof(struct sockaddr_in));
+                               }
+                               memcpy(&(res->nsaddr_list[res->nscount]), &sin4, sizeof(struct sockaddr_in));
+                               res->nscount++;
+                       }
+               }
+               
+               return 0;
+       }
+       
+       else if (!strcmp(key, "sortlist"))
+       {
+               p = strchr(val, '/');
+               
+               if (p != NULL)
+               {
+                       *p = '\0';
+                       p++;
+               }
+               
+               /* we use inet_aton rather than inet_pton to be compatible with res_init() */
+               status = inet_aton(val, &addr4);
+               if (status == 0) return -1;
+                       
+               status = 0;
+               if (p != NULL) status = inet_aton(p, &mask);
+               if (status == 0)
+               {
+                       ival = ntohl(addr4.s_addr);
+
+                       if (IN_CLASSA(ival)) mask.s_addr = htonl(IN_CLASSA_NET);
+                       else if (IN_CLASSB(ival)) mask.s_addr = htonl(IN_CLASSB_NET);
+                       else mask.s_addr = htonl(IN_CLASSC_NET);
+               }
+
+               return res_build_sortlist(res, addr4, mask);
+       }
+       
+       else if (!strcmp(key, "ndots"))
+       {
+               ival = atoi(val);
+               if (ival < 0) ival = 0;
+               if (ival > RES_MAXNDOTS) ival = RES_MAXNDOTS;
+               res->ndots = ival;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "nibble"))
+       {
+               if ((strlen(val) + 1) > RES_EXT_SUFFIX_LEN) return -1;
+               if (res->_u._ext.ext == NULL) return -1;
+               strcpy(res->_u._ext.ext->nsuffix, val);
+               return 0;
+       }
+       
+       else if (!strcmp(key, "nibble2"))
+       {
+               if ((strlen(val) + 1) > RES_EXT_SUFFIX_LEN) return -1;
+               if (res->_u._ext.ext == NULL) return -1;
+               strcpy(res->_u._ext.ext->nsuffix2, val);
+               return 0;
+       }
+       
+       else if (!strcmp(key, "bitstring"))
+       {
+               if ((strlen(val) + 1) > RES_EXT_SUFFIX_LEN) return -1;
+               if (res->_u._ext.ext == NULL) return -1;
+               strcpy(res->_u._ext.ext->bsuffix, val);
+               return 0;
+       }
+       
+       else if (!strcmp(key, "attempts"))
+       {
+               ival = atoi(val);
+               if (ival < 0) ival = 0;
+               if (ival > RES_MAXRETRY) ival = RES_MAXRETRY;
+               res->retry = ival;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "v6revmode"))
+       {
+               if (!strcmp(val, "single")) res->options |= RES_NO_NIBBLE2;
+               else if (!strcmp(val, "both")) res->options &= ~RES_NO_NIBBLE2;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "debug"))
+       {
+               res->options |= RES_DEBUG;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "no_tld_query"))
+       {
+               res->options |= RES_NOTLDQUERY;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "inet6"))
+       {
+               res->options |= RES_USE_INET6;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "rotate"))
+       {
+               res->options |= RES_ROTATE;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "no-check-names"))
+       {
+               res->options |= RES_NOCHECKNAME;
+               return 0;
+       }
+       
+#ifdef RES_USE_EDNS0
+       else if (!strcmp(key, "edns0"))
+       {
+               res->options |= RES_USE_EDNS0;
+               return 0;
+       }
+#endif
+       
+       else if (!strcmp(key, "a6"))
+       {
+               res->options |= RES_USE_A6;
+               return 0;
+       }
+       
+       else if (!strcmp(key, "dname"))
+       {
+               res->options |= RES_USE_DNAME;
+               return 0;
+       }
+
+       return -1;
+}
+
+/*
+ * Set up default settings.  If the configuration file exist, the values
+ * there will have precedence.  Otherwise, the server address is set to
+ * INADDR_ANY and the default domain name comes from the gethostname().
+ *
+ * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1
+ * rather than INADDR_ANY ("0.0.0.0") as the default name server address
+ * since it was noted that INADDR_ANY actually meant ``the first interface
+ * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface,
+ * it had to be "up" in order for you to reach your own name server.  It
+ * was later decided that since the recommended practice is to always 
+ * install local static routes through 127.0.0.1 for all your network
+ * interfaces, that we could solve this problem without a code change.
+ *
+ * The configuration file should always be used, since it is the only way
+ * to specify a default domain.  If you are running a server on your local
+ * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1"
+ * in the configuration file.
+ *
+ * Return 0 if completes successfully, -1 on error
+ */
+
+int
+res_vinit_from_file(res_state statp, int preinit, char *resconf_file)
+{
+       register FILE *fp;
+       register char *cp, **pp;
+       register int n;
+       char buf[BUFSIZ];
+       int nserv = 0;    /* number of nameserver records read from file */
+       int haveenv = 0;
+       int havesearch = 0;
+       int isresdefault = 0;
+       unsigned short port; /* HOST BYTE ORDER */
+       unsigned int i, total_timeout;
+#ifdef RESOLVSORT
+       int nsort = 0;
+       char *net;
+#endif
+       int dots;
+
+       port = NS_DEFAULTPORT;
+       total_timeout = 0;
+
+       if (!preinit)
+       {
+               statp->retrans = RES_TIMEOUT;
+               statp->retry = RES_DFLRETRY;
+               statp->options = RES_DEFAULT;
+               statp->id = res_randomid();
+       }
+
+       if ((statp->options & RES_INIT) != 0) res_ndestroy(statp);
+
+       /* N.B. we allocate a new statp->_u._ext.ext below */
+
+       /* make sure version is set */
+       statp->_pad = 9;
+       
+       statp->nscount = 0;
+       
+       if ((resconf_file == NULL) || (!strcmp(resconf_file, _PATH_RESCONF)))
+       {
+               isresdefault = 1;
+               
+#ifdef USELOOPBACK
+               statp->nsaddr.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
+#else
+               statp->nsaddr.sin_addr.s_addr = INADDR_ANY;
+#endif
+               statp->nsaddr.sin_family = AF_INET;
+               statp->nsaddr.sin_port = htons(NS_DEFAULTPORT);
+#ifdef HAVE_SA_LEN
+               statp->nsaddr.sin_len = sizeof(struct sockaddr_in);
+#endif
+               statp->nscount = 1;
+
+               /*
+                * Force loopback queries to use TCP
+                * so we don't take a timeout if named isn't running.
+                */
+               statp->options |= RES_USEVC;
+       }
+       
+       statp->ndots = 1;
+       statp->pfcode = 0;
+       statp->_vcsock = -1;
+       statp->_flags = 0;
+       statp->qhook = NULL;
+       statp->rhook = NULL;
+
+       statp->_u._ext.nscount = 0;
+       statp->_u._ext.ext = (struct __res_state_ext *)calloc(1, sizeof(struct __res_state_ext));
+       
+       if (statp->_u._ext.ext != NULL)
+       {
+               memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext));
+               statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr;
+               strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa");
+               strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int");
+               strcpy(statp->_u._ext.ext->bsuffix, "ip6.arpa");
+       }
+       
+#ifdef RESOLVSORT
+       statp->nsort = 0;
+#endif
+       
+       /* Allow user to override the local domain definition */
+       cp = getenv("LOCALDOMAIN");
+       if ((cp != NULL) && (isresdefault != 0))
+       {
+               (void)strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1);
+               statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+               haveenv++;
+
+               /*
+                * Set search list to be blank-separated strings
+                * from rest of env value.  Permits users of LOCALDOMAIN
+                * to still have a search list, and anyone to set the
+                * one that they want to use as an individual (even more
+                * important now that the rfc1535 stuff restricts searches)
+                */
+               cp = statp->defdname;
+               pp = statp->dnsrch;
+               *pp++ = cp;
+               for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++)
+               {
+                       if (*cp == '\n') break;
+                       else if (*cp == ' ' || *cp == '\t')
+                       {
+                               *cp = 0;
+                               n = 1;
+                       }
+                       else if (n != 0)
+                       {
+                               *pp++ = cp;
+                               n = 0;
+                               havesearch = 1;
+                       }
+               }
+
+               /* null terminate last domain if there are excess */
+               while ((*cp != '\0') && (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) cp++;
+               *cp = '\0';
+               *pp++ = 0;
+       }
+
+#define        MATCH(line, name) \
+       (!strncmp(line, name, sizeof(name) - 1) && \
+       (line[sizeof(name) - 1] == ' ' || \
+        line[sizeof(name) - 1] == '\t'))
+
+       if (resconf_file == NULL) resconf_file = _PATH_RESCONF;
+
+       if ((fp = fopen(resconf_file, "r")) != NULL)
+       {
+               /* look for port number */
+               while (fgets(buf, sizeof(buf), fp) != NULL)
+               {
+                       /* skip comments */
+                       if (*buf == ';' || *buf == '#') continue;
+
+                       /* read default domain name */
+                       if (MATCH(buf, "port"))
+                       {
+                               cp = buf + sizeof("port") - 1;
+                               while (*cp == ' ' || *cp == '\t') cp++;
+                               if ((*cp == '\0') || (*cp == '\n')) continue;
+                               port = atoi(cp);
+                               break;
+                       }
+               }
+               fclose(fp);
+       }
+
+       if ((fp = fopen(resconf_file, "r")) != NULL)
+       {
+           /* read the config file */
+           while (fgets(buf, sizeof(buf), fp) != NULL)
+               {
+                       /* skip comments */
+                       if ((*buf == ';') || (*buf == '#')) continue;
+                       
+                       /* read default domain name */
+                       if (MATCH(buf, "domain"))
+                       {
+                               /* skip if have from environ */
+                               if (haveenv) continue;
+
+                               cp = buf + sizeof("domain") - 1;
+                               while ((*cp == ' ') || (*cp == '\t')) cp++;
+                               if ((*cp == '\0') || (*cp == '\n')) continue;
+
+                               strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1);
+                               statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+                               cp = strpbrk(statp->defdname, " \t\n");
+                               if (cp != NULL) *cp = '\0';
+                               havesearch = 0;
+                               continue;
+                       }
+
+                       /* set search list */
+                       if (MATCH(buf, "search"))
+                       {
+                               /* skip if have from environ */
+                           if (haveenv) continue;
+
+                           cp = buf + sizeof("search") - 1;
+                           while ((*cp == ' ') || (*cp == '\t')) cp++;
+                           if ((*cp == '\0') || (*cp == '\n')) continue;
+                               
+                           strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1);
+                           statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+                               cp = strchr(statp->defdname, '\n');
+                           if (cp != NULL) *cp = '\0';
+
+                           /*
+                            * Set search list to be blank-separated strings
+                            * on rest of line.
+                            */
+                           cp = statp->defdname;
+                           pp = statp->dnsrch;
+                           *pp++ = cp;
+                           for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++)
+                               {
+                                   if ((*cp == ' ') || (*cp == '\t'))
+                                       {
+                                           *cp = 0;
+                                           n = 1;
+                                   }
+                                       else if (n != 0)
+                                       {
+                                           *pp++ = cp;
+                                           n = 0;
+                                   }
+                           }
+
+                           /* null terminate last domain if there are excess */
+                           while ((*cp != '\0') && (*cp != ' ') && (*cp != '\t')) cp++;
+                           *cp = '\0';
+                           *pp++ = 0;
+                           havesearch = 1;
+                           continue;
+                       }
+
+                       /* read nameservers to query */
+                       if (MATCH(buf, "nameserver") && (nserv < MAXNS))
+                       {
+#ifndef __APPLE__
+                           struct addrinfo hints, *ai;
+#endif
+                               int dotcount, semicount, status;
+                               struct in_addr addr4;
+                               struct sockaddr_in sin4;
+                               struct in6_addr addr6;
+                               struct sockaddr_in6 sin6;
+                               unsigned short aport; /* HOST BYTE ORDER */
+                               char *lastdot, *checkp;
+                           char sbuf[NI_MAXSERV];
+#ifndef __APPLE__
+                           const size_t minsiz = sizeof(statp->_u._ext.ext->nsaddrs[0]);
+#endif
+
+                           cp = buf + sizeof("nameserver") - 1;
+                           while ((*cp == ' ') || (*cp == '\t')) cp++;
+                           cp[strcspn(cp, ";# \t\n")] = '\0';
+                           if ((*cp != '\0') && (*cp != '\n'))
+                               {
+#ifndef __APPLE__
+                                       memset(&hints, 0, sizeof(hints));
+                                       hints.ai_family = PF_UNSPEC;
+                                       hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+                                       hints.ai_flags = AI_NUMERICHOST;
+#endif
+                                       dotcount = 0;
+                                       semicount = 0;
+                                       lastdot = NULL;
+                                       aport = port;
+
+                                       for (checkp = cp; *checkp != '\0'; checkp++)
+                                       {
+                                               if (*checkp == ':') semicount++;
+                                               else if (*checkp == '.')
+                                               {
+                                                       dotcount++;
+                                                       lastdot = checkp;
+                                               }
+                                       }
+
+                                       if ((dotcount == 4) || ((semicount > 0) && (lastdot != NULL)))
+                                       {
+                                               aport = atoi(lastdot + 1);
+                                               if (aport == 0) aport = port;
+                                               *lastdot = '\0';
+                                       }
+                                       
+                                       sprintf(sbuf, "%u", aport);
+
+#ifdef __APPLE__
+                                       memset(&addr4, 0, sizeof(struct in_addr));
+                                       memset(&sin4, 0, sizeof(struct sockaddr_in));
+
+                                       memset(&addr6, 0, sizeof(struct in6_addr));
+                                       memset(&sin6, 0, sizeof(struct sockaddr_in6));
+
+                                       status = inet_pton(AF_INET6, cp, &addr6);
+                                       if (status == 1)
+                                       {
+                                               sin6.sin6_addr = addr6;
+                                               sin6.sin6_family = AF_INET6;
+                                               sin6.sin6_port = htons(aport);
+                                               sin6.sin6_len = sizeof(struct sockaddr_in6);
+                                               if (statp->_u._ext.ext != NULL)
+                                               {
+                                                       memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &sin6, sizeof(struct sockaddr_in6));
+                                               }
+                                               statp->nsaddr_list[nserv].sin_family = 0;
+                                               nserv++;
+                                       }
+                                       else
+                                       {
+                                               status = inet_pton(AF_INET, cp, &addr4);
+                                               if (status == 1)
+                                               {
+                                                       sin4.sin_addr = addr4;
+                                                       sin4.sin_family = AF_INET;
+                                                       sin4.sin_port = htons(port);
+                                                       sin4.sin_len = sizeof(struct sockaddr_in);
+                                                       if (statp->_u._ext.ext != NULL)
+                                                       {
+                                                               memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &sin4, sizeof(struct sockaddr_in));
+                                                       }
+                                                       memcpy(&statp->nsaddr_list[nserv], &sin4, sizeof(struct sockaddr_in));
+                                                       nserv++;
+                                               }
+                  }
+
+                                       
+#else
+                                       if (getaddrinfo(cp, sbuf, &hints, &ai) == 0 && ai->ai_addrlen <= minsiz)
+                                       {
+                                               if (statp->_u._ext.ext != NULL)
+                                               {
+                                                       memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
+                                               }
+
+                                               if (ai->ai_addrlen <= sizeof(statp->nsaddr_list[nserv]))
+                                               {
+                                                       memcpy(&statp->nsaddr_list[nserv], ai->ai_addr, ai->ai_addrlen);
+                                               }
+                                               else
+                                               {
+                                                       statp->nsaddr_list[nserv].sin_family = 0;
+                                               }
+
+                                               freeaddrinfo(ai);
+                                               nserv++;
+                                       }
+#endif
+                               }
+
+                               continue;
+                       }
+
+#ifdef RESOLVSORT
+                       if (MATCH(buf, "sortlist"))
+                       {
+                           struct in_addr a;
+
+                           cp = buf + sizeof("sortlist") - 1;
+                           while (nsort < MAXRESOLVSORT)
+                               {
+                                       while ((*cp == ' ') || (*cp == '\t')) cp++;
+                                       if ((*cp == '\0') || (*cp == '\n') || (*cp == ';')) break;
+
+                                       net = cp;
+                                       while (*cp && !ISSORTMASK(*cp) && (*cp != ';') && isascii(*cp) && !isspace((unsigned char)*cp)) cp++;
+                                       n = *cp;
+                                       *cp = 0;
+                                       if (inet_aton(net, &a))
+                                       {
+                                               statp->sort_list[nsort].addr = a;
+                                               if (ISSORTMASK(n))
+                                               {
+                                                       *cp++ = n;
+                                                       net = cp;
+                                                       while (*cp && (*cp != ';') && isascii(*cp) && !isspace((unsigned char)*cp)) cp++;
+                                                       n = *cp;
+                                                       *cp = 0;
+                                                       if (inet_aton(net, &a))
+                                                       {
+                                                               statp->sort_list[nsort].mask = a.s_addr;
+                                                       }
+                                                       else
+                                                       {
+                                                               statp->sort_list[nsort].mask = net_mask(statp->sort_list[nsort].addr);
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       statp->sort_list[nsort].mask = net_mask(statp->sort_list[nsort].addr);
+                                               }
+                                               nsort++;
+                                       }
+
+                                       *cp = n;
+                               }
+
+                               continue;
+                       }
+#endif
+
+                       if (MATCH(buf, "options"))
+                       {
+                           res_setoptions(statp, buf + sizeof("options") - 1, "conf");
+                           continue;
+                       }
+
+                       if (MATCH(buf, "timeout"))
+                       {
+                               cp = buf + sizeof("timeout") - 1;
+                               while (*cp == ' ' || *cp == '\t') cp++;
+                               if ((*cp == '\0') || (*cp == '\n')) continue;
+                               i = atoi(cp);
+                               if (i > RES_MAXRETRANS) i = RES_MAXRETRANS;
+                               total_timeout = i;
+                               continue;
+                       }
+               }
+
+               if (nserv > 1) statp->nscount = nserv;
+#ifdef RESOLVSORT
+               statp->nsort = nsort;
+#endif
+               fclose(fp);
+       }
+
+       /*
+        * Last chance to get a nameserver.  This should not normally
+        * be necessary
+        */
+#ifdef NO_RESOLV_CONF
+       if(nserv == 0) nserv = get_nameservers(statp);
+#endif
+
+       if (isresdefault != 0)
+       {
+               if ((statp->defdname[0] == 0) && (gethostname(buf, sizeof(statp->defdname) - 1) == 0) && ((cp = strchr(buf, '.')) != NULL))
+               {
+                       strcpy(statp->defdname, cp + 1);
+               }
+       }
+
+       /* find components of local domain that might be searched */
+       if (havesearch == 0)
+       {
+               pp = statp->dnsrch;
+               *pp++ = statp->defdname;
+               *pp = NULL;
+
+               dots = 0;
+               for (cp = statp->defdname; *cp; cp++)
+               {
+                       if (*cp == '.') dots++;
+               }
+
+               /* Back up over any trailing dots and cut them out of the name */
+               for (cp--; (cp >= statp->defdname) && (*cp == '.'); cp--)
+               {
+                       *cp = '\0';
+                       dots--;
+               }
+
+               cp = statp->defdname;
+               while (pp < statp->dnsrch + MAXDFLSRCH)
+               {
+                       if (dots < LOCALDOMAINPARTS) break;
+
+                       /* we know there is a dot */
+                       cp = strchr(cp, '.') + 1;   
+                       *pp++ = cp;
+                       dots--;
+               }
+
+               *pp = NULL;
+#ifdef DEBUG
+               if (statp->options & RES_DEBUG)
+               {
+                       printf(";; res_init()... default dnsrch list:\n");
+                       for (pp = statp->dnsrch; *pp; pp++) printf(";;\t%s\n", *pp);
+                       printf(";;\t..END..\n");
+               }
+#endif
+       }
+
+       cp = getenv("RES_OPTIONS");
+       if ((cp != NULL) && (isresdefault != 0))
+       {
+               res_setoptions(statp, cp, "env");
+       }
+
+#ifdef __APPLE__
+       /*
+        * Post processing to set the timeout value.
+        */
+       if (total_timeout != 0)
+       {
+               /* Timeout was set with a "timeout" line in the file */
+               statp->retrans = total_timeout;
+       }
+       else
+       {
+               /*
+                * Timeout is default, or was set with "options timeout:"
+                * This is a per-select timeout value.  Calculate total timeout
+                * and set statp->restrans which we (Apple) use to indicate
+                * total time allowed for a query to be resolved.
+                */
+               total_timeout = statp->retrans * (statp->retry + 1) * statp->nscount;
+               statp->retrans = total_timeout;
+       }
+#endif
+
+       statp->options |= RES_INIT;
+       return (0);
+}
+
+static void
+res_setoptions(res_state statp, const char *options, const char *source)
+{
+       const char *cp = options;
+       int i;
+       struct __res_state_ext *ext = statp->_u._ext.ext;
+
+#ifdef DEBUG
+       if (statp->options & RES_DEBUG)
+               printf(";; res_setoptions(\"%s\", \"%s\")...\n",
+                      options, source);
+#endif
+       while (*cp) {
+               /* skip leading and inner runs of spaces */
+               while (*cp == ' ' || *cp == '\t')
+                       cp++;
+               /* search for and process individual options */
+               if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) {
+                       i = atoi(cp + sizeof("ndots:") - 1);
+                       if (i <= RES_MAXNDOTS)
+                               statp->ndots = i;
+                       else
+                               statp->ndots = RES_MAXNDOTS;
+#ifdef DEBUG
+                       if (statp->options & RES_DEBUG)
+                               printf(";;\tndots=%d\n", statp->ndots);
+#endif
+               } else if (!strncmp(cp, "timeout:", sizeof("timeout:") - 1)) {
+                       i = atoi(cp + sizeof("timeout:") - 1);
+                       if (i <= RES_MAXRETRANS)
+                               statp->retrans = i;
+                       else
+                               statp->retrans = RES_MAXRETRANS;
+               } else if (!strncmp(cp, "attempts:", sizeof("attempts:") - 1)){
+                       i = atoi(cp + sizeof("attempts:") - 1);
+                       if (i <= RES_MAXRETRY)
+                               statp->retry = i;
+                       else
+                               statp->retry = RES_MAXRETRY;
+               } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) {
+#ifdef DEBUG
+                       if (!(statp->options & RES_DEBUG)) {
+                               printf(";; res_setoptions(\"%s\", \"%s\")..\n",
+                                      options, source);
+                               statp->options |= RES_DEBUG;
+                       }
+                       printf(";;\tdebug\n");
+#endif
+               } else if (!strncmp(cp, "no_tld_query",
+                                   sizeof("no_tld_query") - 1) ||
+                          !strncmp(cp, "no-tld-query",
+                                   sizeof("no-tld-query") - 1)) {
+                       statp->options |= RES_NOTLDQUERY;
+               } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) {
+                       statp->options |= RES_USE_INET6;
+               } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) {
+                       statp->options |= RES_ROTATE;
+               } else if (!strncmp(cp, "no-check-names",
+                                   sizeof("no-check-names") - 1)) {
+                       statp->options |= RES_NOCHECKNAME;
+               }
+#ifdef RES_USE_EDNS0
+               else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) {
+                       statp->options |= RES_USE_EDNS0;
+               }
+#endif
+               else if (!strncmp(cp, "a6", sizeof("a6") - 1)) {
+                       statp->options |= RES_USE_A6;
+               }
+               else if (!strncmp(cp, "dname", sizeof("dname") - 1)) {
+                       statp->options |= RES_USE_DNAME;
+               }
+               else if (!strncmp(cp, "nibble:", sizeof("nibble:") - 1)) {
+                       if (ext == NULL)
+                               goto skip;
+                       cp += sizeof("nibble:") - 1;
+                       i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix) - 1);
+                       strncpy(ext->nsuffix, cp, i);
+                       ext->nsuffix[i] = '\0';
+               }
+               else if (!strncmp(cp, "nibble2:", sizeof("nibble2:") - 1)) {
+                       if (ext == NULL)
+                               goto skip;
+                       cp += sizeof("nibble2:") - 1;
+                       i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix2) - 1);
+                       strncpy(ext->nsuffix2, cp, i);
+                       ext->nsuffix2[i] = '\0';
+               }
+               else if (!strncmp(cp, "bitstring:", sizeof("bitstring:") - 1)) {
+                       if (ext == NULL)
+                               goto skip;
+                       cp += sizeof("bitstring:") - 1;
+                       i = MIN(strcspn(cp, " \t"), sizeof(ext->bsuffix) - 1);
+                       strncpy(ext->bsuffix, cp, i);
+                       ext->bsuffix[i] = '\0';
+               }
+               else if (!strncmp(cp, "v6revmode:", sizeof("v6revmode:") - 1)) {
+                       cp += sizeof("v6revmode:") - 1;
+                       /* "nibble" and "bitstring" used to be valid */
+                       if (!strncmp(cp, "single", sizeof("single") - 1)) {
+                               statp->options |= RES_NO_NIBBLE2;
+                       } else if (!strncmp(cp, "both", sizeof("both") - 1)) {
+                               statp->options &=
+                                        ~RES_NO_NIBBLE2;
+                       }
+               }
+               else {
+                       /* XXX - print a warning here? */
+               }
+   skip:
+               /* skip to next run of spaces */
+               while (*cp && *cp != ' ' && *cp != '\t')
+                       cp++;
+       }
+}
+
+/* This function has to be reachable by res_data.c but not publically. */
+int
+__res_vinit(res_state statp, int preinit)
+{
+       dns_config_t *sc_dns;
+       dns_resolver_t *sc_res;
+       char val[256], *p, *x;
+       int i, n;
+       uint32_t nsearch, total_timeout, send_timeout;
+       uint16_t port;
+
+       nsearch = 0;
+       send_timeout = 0;
+
+       sc_dns = dns_configuration_copy();
+       if (sc_dns == NULL) return res_vinit_from_file(statp, preinit, _PATH_RESCONF);
+
+       sc_res = sc_dns->resolver[0];
+       port = sc_res->port;
+       if (port == 0) port = NS_DEFAULTPORT;
+
+       if ((statp->options & RES_INIT) != 0) res_ndestroy(statp);
+
+       /* N.B. res_build_start will allocate statp->_u._ext.ext for us */
+       statp = res_build_start(statp);
+
+       p = getenv("RES_RETRY_TIMEOUT");
+       if (p != NULL) send_timeout = atoi(p);
+
+       p = getenv("RES_RETRY");
+       if (p != NULL) statp->retry= atoi(p);
+
+       if (sc_res->n_nameserver > MAXNS) sc_res->n_nameserver = MAXNS;
+       for (i = 0; i < sc_res->n_nameserver; i++)
+       {
+               if (sc_res->nameserver[i]->sa_family == AF_INET)
+               {
+                       memset(val, 0, sizeof(val));
+                       inet_ntop(AF_INET, (char *)(sc_res->nameserver[i]) + INET_NTOP_AF_INET_OFFSET, val, sizeof(val));
+                       res_build(statp, port, &nsearch, "nameserver", val);
+               }
+               else if (sc_res->nameserver[i]->sa_family == AF_INET6)
+               {
+                       memset(val, 0, sizeof(val));
+                       inet_ntop(AF_INET6, (char *)(sc_res->nameserver[i]) + INET_NTOP_AF_INET6_OFFSET, val, sizeof(val));
+                       res_build(statp, port, &nsearch, "nameserver", val);
+               }
+       }
+
+       if (sc_res->n_search > MAXDNSRCH) sc_res->n_search = MAXDNSRCH;
+       for (i = 0; i < sc_res->n_search; i++)
+       {
+               res_build(statp, port, &nsearch, "search", sc_res->search[i]);
+       }
+
+       /* If there was no search list, use "domain" */
+       if ((sc_res->n_search == 0) && (sc_res->domain != NULL))
+       {
+               /* Count dots */
+               n = 0;
+               for (p = sc_res->domain; *p != '\0'; p++) 
+               {
+                       if (*p == '.') n++;
+               }
+
+               /* Back up over any trailing dots and cut them out of the name */
+               for (p--; (p >= sc_res->domain) && (*p == '.'); p--)
+               {
+                       *p = '\0';
+                       n--;
+               }
+
+               if (p >= sc_res->domain) res_build(statp, port, &nsearch, "search", sc_res->domain);
+
+               /* dots are separators, so number of components is one larger */
+               n++;
+
+               /* Include parent domains with at least LOCALDOMAINPARTS components */
+               p = sc_res->domain;
+               while ((n > LOCALDOMAINPARTS) && (nsearch < MAXDFLSRCH))
+               {
+                       /* Find next component */
+                       while (*p != '.') p++;
+                       if (*p == '\0') break;
+                       p++;
+
+                       n--;
+                       res_build(statp, port, &nsearch, "search", p);
+               }
+       }
+
+       total_timeout = sc_res->timeout;
+       
+       snprintf(val, sizeof(val), "%d", sc_res->search_order);
+       res_build(statp, port, &nsearch, "search_order", val);
+       
+       if (sc_res->n_sortaddr > MAXRESOLVSORT) sc_res->n_sortaddr = MAXRESOLVSORT;
+       for (i = 0; i < sc_res->n_sortaddr; i++)
+       {
+               res_build_sortlist(statp, sc_res->sortaddr[i]->address, sc_res->sortaddr[i]->mask);
+       }
+       
+       p = sc_res->options;
+       while (NULL != (x = res_next_word(&p)))
+       {
+               if (!strncmp(x, "ndots:", 6))
+               {
+                       res_build(statp, port, &nsearch, "ndots", x+6);
+               }
+               
+               else if (!strncmp(x, "nibble:", 7))
+               {
+                       res_build(statp, port, &nsearch, "nibble", x+7);
+               }
+               
+               else if (!strncmp(x, "nibble2:", 8))
+               {
+                       res_build(statp, port, &nsearch, "nibble2", x+8);
+               }
+               
+               else if (!strncmp(x, "timeout:", 8))
+               {
+                       send_timeout = atoi(x+8);
+               }
+               
+               else if (!strncmp(x, "attempts:", 9))
+               {
+                       res_build(statp, port, &nsearch, "attempts", x+9);
+               }
+               
+               else if (!strncmp(x, "bitstring:", 10))
+               {
+                       res_build(statp, port, &nsearch, "bitstring", x+10);
+               }
+               
+               else if (!strncmp(x, "v6revmode:", 10))
+               {
+                       res_build(statp, port, &nsearch, "v6revmode", x+10);
+               }
+               
+               else if (!strcmp(x, "debug"))
+               {
+                       res_build(statp, port, &nsearch, "debug", NULL);
+               }
+               
+               else if (!strcmp(x, "no_tld_query"))
+               {
+                       res_build(statp, port, &nsearch, "no_tld_query", NULL);
+               }
+               
+               else if (!strcmp(x, "inet6"))
+               {
+                       res_build(statp, port, &nsearch, "inet6", NULL);
+               }
+               
+               else if (!strcmp(x, "rotate"))
+               {
+                       res_build(statp, port, &nsearch, "rotate", NULL);
+               }
+               
+               else if (!strcmp(x, "no-check-names"))
+               {
+                       res_build(statp, port, &nsearch, "no-check-names", NULL);
+               }
+               
+#ifdef RES_USE_EDNS0
+               else if (!strcmp(x, "edns0"))
+               {
+                       res_build(statp, port, &nsearch, "edns0", NULL);
+               }               
+#endif
+               else if (!strcmp(x, "a6"))
+               {
+                       res_build(statp, port, &nsearch, "a6", NULL);
+               }
+               
+               else if (!strcmp(x, "dname"))
+               {
+                       res_build(statp, port, &nsearch, "dname", NULL);
+               }
+       }
+       
+       n = statp->nscount;
+       if (n == 0) n = 1;
+
+       if (total_timeout == 0)
+       {
+               if (send_timeout == 0) total_timeout = RES_MAXRETRANS;
+               else total_timeout = send_timeout * statp->retry * n;
+       }
+
+       dns_configuration_free(sc_dns);
+
+       res_build_finish(statp, total_timeout, port);
+
+       return 0;
+}
+
+int
+res_ninit(res_state statp)
+{
+       if (statp == NULL) return -1;
+       memset(statp, 0, sizeof(struct __res_state));
+       return __res_vinit(statp, 0);
+}
+
+#ifdef RESOLVSORT
+/* XXX - should really support CIDR which means explicit masks always. */
+static u_int32_t
+net_mask(in)           /* XXX - should really use system's version of this */
+       struct in_addr in;
+{
+       register u_int32_t i = ntohl(in.s_addr);
+
+       if (IN_CLASSA(i))
+               return (htonl(IN_CLASSA_NET));
+       else if (IN_CLASSB(i))
+               return (htonl(IN_CLASSB_NET));
+       return (htonl(IN_CLASSC_NET));
+}
+#endif
+
+u_int
+res_randomid(void) {
+       struct timeval now;
+#ifdef __APPLE__
+       int rf;
+       static u_int x = 0;
+
+       if (x == 0)
+       {
+               rf = open("/dev/random", O_RDONLY, 0);
+               if (rf >= 0)
+               {
+                       read(rf, &x, sizeof(u_int));
+                       close(rf);
+               }
+               else
+               {
+                       x = (getpid() << 10) + time(NULL);
+               }
+
+               srandom(x);
+       }
+
+       return random();
+#endif
+
+       gettimeofday(&now, NULL);
+       return (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid()));
+}
+
+/*
+ * This routine is for closing the socket if a virtual circuit is used and
+ * the program wants to close it.  This provides support for endhostent()
+ * which expects to close the socket.
+ *
+ * This routine is not expected to be user visible.
+ */
+void
+res_nclose(res_state statp)
+{
+       int ns;
+       
+       if (statp->_vcsock >= 0)
+       { 
+               close(statp->_vcsock);
+               statp->_vcsock = -1;
+               statp->_flags &= ~(RES_F_VC | RES_F_CONN);
+       }
+       
+       if (statp->_pad >= 9)
+       {
+               for (ns = 0; ns < statp->_u._ext.nscount; ns++)
+               {
+                       if (statp->_u._ext.nssocks[ns] != -1)
+                       {
+                               close(statp->_u._ext.nssocks[ns]);
+                               statp->_u._ext.nssocks[ns] = -1;
+                       }
+               }
+       }
+}
+
+void
+res_ndestroy(res_state statp)
+{
+       res_nclose(statp);
+       statp->options &= ~RES_INIT;
+
+       /*
+        * _pad (normally unused) is a version number.
+        * We use it to prevent BIND_9 from trashing memory
+        * if statp was created by BIND-8.
+        */
+       if (statp->_pad >= 9)
+       {
+               if (statp->_u._ext.ext != NULL) free(statp->_u._ext.ext);
+               statp->_u._ext.ext = NULL;
+               statp->_pad = 9;
+       }
+}
+
+const char *
+res_get_nibblesuffix(res_state statp) {
+       if (statp->_u._ext.ext)
+               return (statp->_u._ext.ext->nsuffix);
+       return ("ip6.arpa");
+}
+
+const char *
+res_get_nibblesuffix2(res_state statp) {
+       if (statp->_u._ext.ext)
+               return (statp->_u._ext.ext->nsuffix2);
+       return ("ip6.int");
+}
+
+const char *
+res_get_bitstringsuffix(res_state statp) {
+       if (statp->_u._ext.ext)
+               return (statp->_u._ext.ext->bsuffix);
+       return ("ip6.arpa");
+}
+
+void
+res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt) {
+       int i, nserv;
+       size_t size;
+
+       /* close open servers */
+       res_nclose(statp);
+
+       /* cause rtt times to be forgotten */
+       statp->_u._ext.nscount = 0;
+
+       nserv = 0;
+       for (i = 0; i < cnt && nserv < MAXNS; i++) {
+               switch (set->sin.sin_family) {
+               case AF_INET:
+                       size = sizeof(set->sin);
+                       if (statp->_u._ext.ext)
+                               memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
+                                       &set->sin, size);
+                       if (size <= sizeof(statp->nsaddr_list[nserv]))
+                               memcpy(&statp->nsaddr_list[nserv],
+                                       &set->sin, size);
+                       else
+                               statp->nsaddr_list[nserv].sin_family = 0;
+                       nserv++;
+                       break;
+
+               case AF_INET6:
+                       size = sizeof(set->sin6);
+                       if (statp->_u._ext.ext)
+                               memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
+                                       &set->sin6, size);
+                       if (size <= sizeof(statp->nsaddr_list[nserv]))
+                               memcpy(&statp->nsaddr_list[nserv],
+                                       &set->sin6, size);
+                       else
+                               statp->nsaddr_list[nserv].sin_family = 0;
+                       nserv++;
+                       break;
+
+               default:
+                       break;
+               }
+               set++;
+       }
+
+       statp->nscount = nserv;
+}
+
+int
+res_getservers(res_state statp, union res_sockaddr_union *set, int cnt) {
+       int i;
+       size_t size;
+       u_int16_t family;
+
+       for (i = 0; i < statp->nscount && i < cnt; i++) {
+               if (statp->_u._ext.ext)
+                       family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family;
+               else 
+                       family = statp->nsaddr_list[i].sin_family;
+
+               switch (family) {
+               case AF_INET:
+                       size = sizeof(set->sin);
+                       if (statp->_u._ext.ext)
+                               memcpy(&set->sin,
+                                      &statp->_u._ext.ext->nsaddrs[i],
+                                      size);
+                       else
+                               memcpy(&set->sin, &statp->nsaddr_list[i],
+                                      size);
+                       break;
+
+               case AF_INET6:
+                       size = sizeof(set->sin6);
+                       if (statp->_u._ext.ext)
+                               memcpy(&set->sin6,
+                                      &statp->_u._ext.ext->nsaddrs[i],
+                                      size);
+                       else
+                               memcpy(&set->sin6, &statp->nsaddr_list[i],
+                                      size);
+                       break;
+
+               default:
+                       set->sin.sin_family = 0;
+                       break;
+               }
+               set++;
+       }
+       return (statp->nscount);
+}
diff --git a/res_mkquery.c b/res_mkquery.c
new file mode 100644 (file)
index 0000000..09e1cf3
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 1985, 1993
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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
+ * CORPORATION 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.
+ */
+
+/*
+ * 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 const char sccsid[] = "@(#)res_mkquery.c        8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$Id: res_mkquery.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+#include "res_private.h"
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* Options.  Leave them on. */
+#define DEBUG
+
+extern const char *__res_opcodes[];
+
+/*
+ * Form all types of queries.
+ * Returns the size of the result or -1.
+ */
+int
+res_nmkquery(res_state statp,
+            int op,                    /* opcode of query */
+            const char *dname,         /* domain name */
+            int class, int type,       /* class and type of query */
+            const u_char *data,        /* resource record data */
+            int datalen,               /* length of data */
+            const u_char *newrr_in,    /* new rr for modify or append */
+            u_char *buf,               /* buffer to put query */
+            int buflen)                /* size of buffer */
+{
+       register HEADER *hp;
+       register u_char *cp;
+       register int n;
+       u_char *dnptrs[20], **dpp, **lastdnptr;
+
+#ifdef __APPLE__
+       n = (int)newrr_in; n = 0;
+#else
+       UNUSED(newrr_in);
+#endif
+
+#ifdef DEBUG
+       if (statp->options & RES_DEBUG)
+               printf(";; res_nmkquery(%s, %s, %s, %s)\n",
+                      __res_opcodes[op], dname, p_class(class), p_type(type));
+#endif
+       /*
+        * Initialize header fields.
+        */
+       if ((buf == NULL) || (buflen < NS_HFIXEDSZ))
+               return (-1);
+       memset(buf, 0, NS_HFIXEDSZ);
+       hp = (HEADER *) buf;
+#ifdef __APPLE__
+       hp->id = res_randomid();
+#else
+       hp->id = htons(++statp->id);
+#endif
+       hp->opcode = op;
+       hp->rd = (statp->options & RES_RECURSE) != 0;
+       hp->rcode = ns_r_noerror;
+       cp = buf + NS_HFIXEDSZ;
+       buflen -= NS_HFIXEDSZ;
+       dpp = dnptrs;
+       *dpp++ = buf;
+       *dpp++ = NULL;
+       lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+       /*
+        * perform opcode specific processing
+        */
+       switch (op) {
+       case ns_o_query:        /*FALLTHROUGH*/
+       case ns_o_update:
+               if ((buflen -= NS_QFIXEDSZ) < 0)
+                       return (-1);
+               if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+                       return (-1);
+               cp += n;
+               buflen -= n;
+               putshort(type, cp);
+               cp += NS_INT16SZ;
+               putshort(class, cp);
+               cp += NS_INT16SZ;
+               hp->qdcount = htons(1);
+               if (op == ns_o_query || data == NULL)
+                       break;
+               /*
+                * Make an additional record for completion domain.
+                */
+               buflen -= NS_RRFIXEDSZ;
+               n = dn_comp((const char *)data, cp, buflen, dnptrs, lastdnptr);
+               if (n < 0)
+                       return (-1);
+               cp += n;
+               buflen -= n;
+               putshort(ns_t_null, cp);
+               cp += NS_INT16SZ;
+               putshort(class, cp);
+               cp += NS_INT16SZ;
+               putlong(0, cp);
+               cp += NS_INT32SZ;
+               putshort(0, cp);
+               cp += NS_INT16SZ;
+               hp->arcount = htons(1);
+               break;
+
+       case ns_o_iquery:
+               /*
+                * Initialize answer section
+                */
+               if (buflen < 1 + NS_RRFIXEDSZ + datalen)
+                       return (-1);
+               *cp++ = '\0';   /* no domain name */
+               putshort(type, cp);
+               cp += NS_INT16SZ;
+               putshort(class, cp);
+               cp += NS_INT16SZ;
+               putlong(0, cp);
+               cp += NS_INT32SZ;
+               putshort(datalen, cp);
+               cp += NS_INT16SZ;
+               if (datalen) {
+                       memcpy(cp, data, datalen);
+                       cp += datalen;
+               }
+               hp->ancount = htons(1);
+               break;
+
+       default:
+               return (-1);
+       }
+       return (cp - buf);
+}
+
+#ifdef RES_USE_EDNS0
+/* attach OPT pseudo-RR, as documented in RFC2671 (EDNS0). */
+#ifndef T_OPT
+#define T_OPT  41
+#endif
+
+int
+res_nopt(statp, n0, buf, buflen, anslen)
+       res_state statp;
+       int n0;
+       u_char *buf;            /* buffer to put query */
+       int buflen;             /* size of buffer */
+       int anslen;             /* answer buffer length */
+{
+       register HEADER *hp;
+       register u_char *cp;
+       u_int16_t flags = 0;
+
+#ifdef DEBUG
+       if ((statp->options & RES_DEBUG) != 0)
+               printf(";; res_nopt()\n");
+#endif
+
+       hp = (HEADER *) buf;
+       cp = buf + n0;
+       buflen -= n0;
+
+       if (buflen < 1 + NS_RRFIXEDSZ)
+               return -1;
+
+       *cp++ = 0;      /* "." */
+       buflen--;
+
+       putshort(T_OPT, cp);    /* TYPE */
+       cp += NS_INT16SZ;
+       putshort(anslen & 0xffff, cp);  /* CLASS = UDP payload size */
+       cp += NS_INT16SZ;
+       *cp++ = ns_r_noerror;   /* extended RCODE */
+       *cp++ = 0;              /* EDNS version */
+       if (statp->options & RES_USE_DNSSEC) {
+#ifdef DEBUG
+               if (statp->options & RES_DEBUG)
+                       printf(";; res_opt()... ENDS0 DNSSEC\n");
+#endif
+               flags |= NS_OPT_DNSSEC_OK;
+       }
+       putshort(flags, cp);
+       cp += NS_INT16SZ;
+       putshort(0, cp);        /* RDLEN */
+       cp += NS_INT16SZ;
+       hp->arcount = htons(ntohs(hp->arcount) + 1);
+       buflen -= NS_RRFIXEDSZ;
+
+       return cp - buf;
+}
+#endif
diff --git a/res_mkupdate.c b/res_mkupdate.c
new file mode 100644 (file)
index 0000000..44fb3d3
--- /dev/null
@@ -0,0 +1,1124 @@
+/*
+ * 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.
+ */
+
+/*
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * <viraj_bais@ccm.fm.intel.com>
+ */
+
+#ifndef __APPLE__
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: res_mkupdate.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* not lint */
+#endif
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <res_update.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "res_private.h"
+
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* Options.  Leave them on. */
+#define DEBUG
+#define MAXPORT 1024
+
+static int getnum_str(u_char **, u_char *);
+static int gethexnum_str(u_char **, u_char *);
+static int getword_str(char *, int, u_char **, u_char *);
+static int getstr_str(char *, int, u_char **, u_char *);
+
+#define ShrinkBuffer(x)  if ((buflen -= x) < 0) return (-2);
+
+/* Forward. */
+
+#ifdef __APPLE__
+static
+#endif
+int res_protocolnumber(const char *);
+#ifdef __APPLE__
+static
+#endif
+int res_servicenumber(const char *);
+
+/*
+ * Form update packets.
+ * Returns the size of the resulting packet if no error
+ * On error,
+ *     returns -1 if error in reading a word/number in rdata
+ *                portion for update packets
+ *             -2 if length of buffer passed is insufficient
+ *             -3 if zone section is not the first section in
+ *                the linked list, or section order has a problem
+ *             -4 on a number overflow
+ *             -5 unknown operation or no records
+ */
+int
+res_nmkupdate(res_state statp, ns_updrec *rrecp_in, u_char *buf, int buflen) {
+       ns_updrec *rrecp_start = rrecp_in;
+       HEADER *hp;
+       u_char *cp, *sp1, *sp2, *startp, *endp;
+       int n, i, soanum, multiline;
+       ns_updrec *rrecp;
+       struct in_addr ina;
+       struct in6_addr in6a;
+        char buf2[NS_MAXDNAME];
+       u_char buf3[NS_MAXDNAME];
+       int section, numrrs = 0, counts[ns_s_max];
+       u_int16_t rtype, rclass;
+       u_int32_t n1, rttl;
+       u_char *dnptrs[20], **dpp, **lastdnptr;
+       int siglen, keylen, certlen;
+
+       /*
+        * Initialize header fields.
+        */
+       if ((buf == NULL) || (buflen < NS_HFIXEDSZ))
+               return (-1);
+       memset(buf, 0, NS_HFIXEDSZ);
+       hp = (HEADER *) buf;
+#ifdef __APPLE__
+       hp->id = res_randomid();
+#else
+       hp->id = htons(++statp->id);
+#endif
+       hp->opcode = ns_o_update;
+       hp->rcode = ns_r_noerror;
+       sp1 = buf + 2*NS_INT16SZ;  /* save pointer to zocount */
+       cp = buf + NS_HFIXEDSZ;
+       buflen -= NS_HFIXEDSZ;
+       dpp = dnptrs;
+       *dpp++ = buf;
+       *dpp++ = NULL;
+       lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+
+       if (rrecp_start == NULL)
+               return (-5);
+       else if (rrecp_start->r_section != ns_s_zn)
+               return (-3);
+
+       memset(counts, 0, sizeof counts);
+       for (rrecp = rrecp_start; rrecp; rrecp = NEXT(rrecp, r_glink)) {
+               numrrs++;
+                section = rrecp->r_section;
+               if (section < 0 || section >= ns_s_max)
+                       return (-1);
+               counts[section]++;
+               for (i = section + 1; i < ns_s_max; i++)
+                       if (counts[i])
+                               return (-3);
+               rtype = rrecp->r_type;
+               rclass = rrecp->r_class;
+               rttl = rrecp->r_ttl;
+               /* overload class and type */
+               if (section == ns_s_pr) {
+                       rttl = 0;
+                       switch (rrecp->r_opcode) {
+                       case ns_r_yxdomain:
+                               rclass = ns_c_any;
+                               rtype = ns_t_any;
+                               rrecp->r_size = 0;
+                               break;
+                       case ns_r_nxdomain:
+                               rclass = ns_c_none;
+                               rtype = ns_t_any;
+                               rrecp->r_size = 0;
+                               break;
+                       case ns_r_nxrrset:
+                               rclass = ns_c_none;
+                               rrecp->r_size = 0;
+                               break;
+                       case ns_r_yxrrset:
+                               if (rrecp->r_size == 0)
+                                       rclass = ns_c_any;
+                               break;
+                       default:
+                               fprintf(stderr,
+                                       "res_mkupdate: incorrect opcode: %d\n",
+                                       rrecp->r_opcode);
+                               fflush(stderr);
+                               return (-1);
+                       }
+               } else if (section == ns_s_ud) {
+                       switch (rrecp->r_opcode) {
+                       case ns_uop_delete:
+                               rclass = rrecp->r_size == 0 ? ns_c_any : ns_c_none;
+                               break;
+                       case ns_uop_add:
+                               break;
+                       default:
+                               fprintf(stderr,
+                                       "res_mkupdate: incorrect opcode: %d\n",
+                                       rrecp->r_opcode);
+                               fflush(stderr);
+                               return (-1);
+                       }
+               }
+
+               /*
+                * XXX  appending default domain to owner name is omitted,
+                *      fqdn must be provided
+                */
+               if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs,
+                                lastdnptr)) < 0)
+                       return (-1);
+               cp += n;
+               ShrinkBuffer(n + 2*NS_INT16SZ);
+               NS_PUT16(rtype, cp);
+               NS_PUT16(rclass, cp);
+               if (section == ns_s_zn) {
+                       if (numrrs != 1 || rrecp->r_type != ns_t_soa)
+                               return (-3);
+                       continue;
+               }
+               ShrinkBuffer(NS_INT32SZ + NS_INT16SZ);
+               NS_PUT32(rttl, cp);
+               sp2 = cp;  /* save pointer to length byte */
+               cp += NS_INT16SZ;
+               if (rrecp->r_size == 0) {
+                       if (section == ns_s_ud && rclass != ns_c_any)
+                               return (-1);
+                       else {
+                               NS_PUT16(0, sp2);
+                               continue;
+                       }
+               }
+               startp = rrecp->r_data;
+               endp = startp + rrecp->r_size - 1;
+               /* XXX this should be done centrally. */
+               switch (rrecp->r_type) {
+               case ns_t_a:
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       if (!inet_aton(buf2, &ina))
+                               return (-1);
+                       n1 = ntohl(ina.s_addr);
+                       ShrinkBuffer(NS_INT32SZ);
+                       NS_PUT32(n1, cp);
+                       break;
+               case ns_t_cname:
+               case ns_t_mb:
+               case ns_t_mg:
+               case ns_t_mr:
+               case ns_t_ns:
+               case ns_t_ptr:
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+                       if (n < 0)
+                               return (-1);
+                       cp += n;
+                       ShrinkBuffer(n);
+                       break;
+               case ns_t_minfo:
+               case ns_t_soa:
+               case ns_t_rp:
+                       for (i = 0; i < 2; i++) {
+                               if (!getword_str(buf2, sizeof buf2, &startp,
+                                                endp))
+                               return (-1);
+                               n = dn_comp(buf2, cp, buflen,
+                                           dnptrs, lastdnptr);
+                               if (n < 0)
+                                       return (-1);
+                               cp += n;
+                               ShrinkBuffer(n);
+                       }
+                       if (rrecp->r_type == ns_t_soa) {
+                               ShrinkBuffer(5 * NS_INT32SZ);
+                               while (isspace(*startp) || !*startp)
+                                       startp++;
+                               if (*startp == '(') {
+                                       multiline = 1;
+                                       startp++;
+                               } else
+                                       multiline = 0;
+                               /* serial, refresh, retry, expire, minimum */
+                               for (i = 0; i < 5; i++) {
+                                       soanum = getnum_str(&startp, endp);
+                                       if (soanum < 0)
+                                               return (-1);
+                                       NS_PUT32(soanum, cp);
+                               }
+                               if (multiline) {
+                                       while (isspace(*startp) || !*startp)
+                                               startp++;
+                                       if (*startp != ')')
+                                               return (-1);
+                               }
+                       }
+                       break;
+               case ns_t_mx:
+               case ns_t_afsdb:
+               case ns_t_rt:
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+                       if (n < 0)
+                               return (-1);
+                       cp += n;
+                       ShrinkBuffer(n);
+                       break;
+               case ns_t_srv:
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+                       if (n < 0)
+                               return (-1);
+                       cp += n;
+                       ShrinkBuffer(n);
+                       break;
+               case ns_t_px:
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       NS_PUT16(n, cp);
+                       ShrinkBuffer(NS_INT16SZ);
+                       for (i = 0; i < 2; i++) {
+                               if (!getword_str(buf2, sizeof buf2, &startp,
+                                                endp))
+                                       return (-1);
+                               n = dn_comp(buf2, cp, buflen, dnptrs,
+                                           lastdnptr);
+                               if (n < 0)
+                                       return (-1);
+                               cp += n;
+                               ShrinkBuffer(n);
+                       }
+                       break;
+               case ns_t_wks: {
+                       char bm[MAXPORT/8];
+                       unsigned int maxbm = 0;
+
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       if (!inet_aton(buf2, &ina))
+                               return (-1);
+                       n1 = ntohl(ina.s_addr);
+                       ShrinkBuffer(NS_INT32SZ);
+                       NS_PUT32(n1, cp);
+
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       if ((i = res_protocolnumber(buf2)) < 0)
+                               return (-1);
+                       ShrinkBuffer(1);
+                       *cp++ = i & 0xff;
+                        
+                       for (i = 0; i < MAXPORT/8 ; i++)
+                               bm[i] = 0;
+
+                       while (getword_str(buf2, sizeof buf2, &startp, endp)) {
+                               if ((n1 = res_servicenumber(buf2)) <= 0)
+                                       return (-1);
+
+                               if (n1 < MAXPORT) {
+                                       bm[n1/8] |= (0x80>>(n1%8));
+                                       if (n1 > maxbm)
+                                               maxbm = n1;
+                               } else
+                                       return (-1);
+                       }
+                       maxbm = maxbm/8 + 1;
+                       ShrinkBuffer(maxbm);
+                       memcpy(cp, bm, maxbm);
+                       cp += maxbm;
+                       break;
+               }
+               case ns_t_hinfo:
+                       for (i = 0; i < 2; i++) {
+                               if ((n = getstr_str(buf2, sizeof buf2,
+                                               &startp, endp)) < 0)
+                                       return (-1);
+                               if (n > 255)
+                                       return (-1);
+                               ShrinkBuffer(n+1);
+                               *cp++ = n;
+                               memcpy(cp, buf2, n);
+                               cp += n;
+                       }
+                       break;
+               case ns_t_txt:
+                       while (1) {
+                               if ((n = getstr_str(buf2, sizeof buf2,
+                                               &startp, endp)) < 0) {
+                                       if (cp != (sp2 + NS_INT16SZ))
+                                               break;
+                                       return (-1);
+                               }
+                               if (n > 255)
+                                       return (-1);
+                               ShrinkBuffer(n+1);
+                               *cp++ = n;
+                               memcpy(cp, buf2, n);
+                               cp += n;
+                       }
+                       break;
+               case ns_t_x25:
+                       /* RFC 1183 */
+                       if ((n = getstr_str(buf2, sizeof buf2, &startp,
+                                        endp)) < 0)
+                               return (-1);
+                       if (n > 255)
+                               return (-1);
+                       ShrinkBuffer(n+1);
+                       *cp++ = n;
+                       memcpy(cp, buf2, n);
+                       cp += n;
+                       break;
+               case ns_t_isdn:
+                       /* RFC 1183 */
+                       if ((n = getstr_str(buf2, sizeof buf2, &startp,
+                                        endp)) < 0)
+                               return (-1);
+                       if ((n > 255) || (n == 0))
+                               return (-1);
+                       ShrinkBuffer(n+1);
+                       *cp++ = n;
+                       memcpy(cp, buf2, n);
+                       cp += n;
+                       if ((n = getstr_str(buf2, sizeof buf2, &startp,
+                                        endp)) < 0)
+                               n = 0;
+                       if (n > 255)
+                               return (-1);
+                       ShrinkBuffer(n+1);
+                       *cp++ = n;
+                       memcpy(cp, buf2, n);
+                       cp += n;
+                       break;
+               case ns_t_nsap:
+                       if ((n = inet_nsap_addr((char *)startp, (u_char *)buf2, sizeof(buf2))) != 0) {
+                               ShrinkBuffer(n);
+                               memcpy(cp, buf2, n);
+                               cp += n;
+                       } else {
+                               return (-1);
+                       }
+                       break;
+               case ns_t_loc:
+                       if ((n = loc_aton((char *)startp, (u_char *)buf2)) != 0) {
+                               ShrinkBuffer(n);
+                               memcpy(cp, buf2, n);
+                               cp += n;
+                       } else
+                               return (-1);
+                       break;
+               case ns_t_sig:
+                   {
+                       int sig_type, success, dateerror;
+                       u_int32_t exptime, timesigned;
+
+                       /* type */
+                       if ((n = getword_str(buf2, sizeof buf2,
+                                            &startp, endp)) < 0)
+                               return (-1);
+                       sig_type = sym_ston(__p_type_syms, buf2, &success);
+                       if (!success || sig_type == ns_t_any)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(sig_type, cp);
+                       /* alg */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(1);
+                       *cp++ = n;
+                       /* labels */
+                       n = getnum_str(&startp, endp);
+                       if (n <= 0 || n > 255)
+                               return (-1);
+                       ShrinkBuffer(1);
+                       *cp++ = n;
+                       /* ottl  & expire */
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       exptime = ns_datetosecs(buf2, &dateerror);
+                       if (!dateerror) {
+                               ShrinkBuffer(NS_INT32SZ);
+                               NS_PUT32(rttl, cp);
+                       }
+                       else {
+                               char *ulendp;
+                               u_int32_t ottl;
+
+                               ottl = strtoul(buf2, &ulendp, 10);
+                               if (ulendp != NULL && *ulendp != '\0')
+                                       return (-1);
+                               ShrinkBuffer(NS_INT32SZ);
+                               NS_PUT32(ottl, cp);
+                               if (!getword_str(buf2, sizeof buf2, &startp,
+                                                endp))
+                                       return (-1);
+                               exptime = ns_datetosecs(buf2, &dateerror);
+                               if (dateerror)
+                                       return (-1);
+                       }
+                       /* expire */
+                       ShrinkBuffer(NS_INT32SZ);
+                       NS_PUT32(exptime, cp);
+                       /* timesigned */
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       timesigned = ns_datetosecs(buf2, &dateerror);
+                       if (!dateerror) {
+                               ShrinkBuffer(NS_INT32SZ);
+                               NS_PUT32(timesigned, cp);
+                       }
+                       else
+                               return (-1);
+                       /* footprint */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+                       /* signer name */
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+                       if (n < 0)
+                               return (-1);
+                       cp += n;
+                       ShrinkBuffer(n);
+                       /* sig */
+                       if ((n = getword_str(buf2, sizeof buf2,
+                                            &startp, endp)) < 0)
+                               return (-1);
+                       siglen = b64_pton(buf2, buf3, sizeof(buf3));
+                       if (siglen < 0)
+                               return (-1);
+                       ShrinkBuffer(siglen);
+                       memcpy(cp, buf3, siglen);
+                       cp += siglen;
+                       break;
+                   }
+               case ns_t_key:
+                       /* flags */
+                       n = gethexnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+                       /* proto */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(1);
+                       *cp++ = n;
+                       /* alg */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(1);
+                       *cp++ = n;
+                       /* key */
+                       if ((n = getword_str(buf2, sizeof buf2,
+                                            &startp, endp)) < 0)
+                               return (-1);
+                       keylen = b64_pton(buf2, buf3, sizeof(buf3));
+                       if (keylen < 0)
+                               return (-1);
+                       ShrinkBuffer(keylen);
+                       memcpy(cp, buf3, keylen);
+                       cp += keylen;
+                       break;
+               case ns_t_nxt:
+                   {
+                       int success, nxt_type;
+                       u_char data[32];
+                       int maxtype;
+
+                       /* next name */
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+                       if (n < 0)
+                               return (-1);
+                       cp += n;
+                       ShrinkBuffer(n);
+                       maxtype = 0;
+                       memset(data, 0, sizeof data);
+                       while (1) {
+                               if (!getword_str(buf2, sizeof buf2, &startp,
+                                                endp))
+                                       break;
+                               nxt_type = sym_ston(__p_type_syms, buf2,
+                                                   &success);
+                               if (!success || !ns_t_rr_p(nxt_type))
+                                       return (-1);
+                               NS_NXT_BIT_SET(nxt_type, data);
+                               if (nxt_type > maxtype)
+                                       maxtype = nxt_type;
+                       }
+                       n = maxtype/NS_NXT_BITS+1;
+                       ShrinkBuffer(n);
+                       memcpy(cp, data, n);
+                       cp += n;
+                       break;
+                   }
+               case ns_t_cert:
+                       /* type */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+                       /* key tag */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(NS_INT16SZ);
+                       NS_PUT16(n, cp);
+                       /* alg */
+                       n = getnum_str(&startp, endp);
+                       if (n < 0)
+                               return (-1);
+                       ShrinkBuffer(1);
+                       *cp++ = n;
+                       /* cert */
+                       if ((n = getword_str(buf2, sizeof buf2,
+                                            &startp, endp)) < 0)
+                               return (-1);
+                       certlen = b64_pton(buf2, buf3, sizeof(buf3));
+                       if (certlen < 0)
+                               return (-1);
+                       ShrinkBuffer(certlen);
+                       memcpy(cp, buf3, certlen);
+                       cp += certlen;
+                       break;
+               case ns_t_aaaa:
+                       if (!getword_str(buf2, sizeof buf2, &startp, endp))
+                               return (-1);
+                       if (inet_pton(AF_INET6, buf2, &in6a) <= 0)
+                               return (-1);
+                       ShrinkBuffer(NS_IN6ADDRSZ);
+                       memcpy(cp, &in6a, NS_IN6ADDRSZ);
+                       cp += NS_IN6ADDRSZ;
+                       break;
+               default:
+                       return (-1);
+               } /*switch*/
+               n = (u_int16_t)((cp - sp2) - NS_INT16SZ);
+               NS_PUT16(n, sp2);
+       } /*for*/
+               
+       hp->qdcount = htons(counts[0]);
+       hp->ancount = htons(counts[1]);
+       hp->nscount = htons(counts[2]);
+       hp->arcount = htons(counts[3]);
+       return (cp - buf);
+}
+
+/*
+ * Get a whitespace delimited word from a string (not file)
+ * into buf. modify the start pointer to point after the
+ * word in the string.
+ */
+static int
+getword_str(char *buf, int size, u_char **startpp, u_char *endp) {
+        char *cp;
+        int c;
+        for (cp = buf; *startpp <= endp; ) {
+                c = **startpp;
+                if (isspace(c) || c == '\0') {
+                        if (cp != buf) /* trailing whitespace */
+                                break;
+                        else { /* leading whitespace */
+                                (*startpp)++;
+                                continue;
+                        }
+                }
+                (*startpp)++;
+                if (cp >= buf+size-1)
+                        break;
+                *cp++ = (u_char)c;
+        }
+        *cp = '\0';
+        return (cp != buf);
+}
+
+/*
+ * get a white spae delimited string from memory.  Process quoted strings
+ * and \DDD escapes.  Return length or -1 on error.  Returned string may
+ * contain nulls.
+ */
+static char digits[] = "0123456789";
+static int
+getstr_str(char *buf, int size, u_char **startpp, u_char *endp) {
+        char *cp;
+        int c, c1 = 0;
+       int inquote = 0;
+       int seen_quote = 0;
+       int escape = 0;
+       int dig = 0;
+       for (cp = buf; *startpp <= endp; ) {
+                if ((c = **startpp) == '\0')
+                       break;
+               /* leading white space */
+               if ((cp == buf) && !seen_quote && isspace(c)) {
+                       (*startpp)++;
+                       continue;
+               }
+
+               switch (c) {
+               case '\\':
+                       if (!escape)  {
+                               escape = 1;
+                               dig = 0;
+                               c1 = 0;
+                               (*startpp)++;
+                               continue;
+                       } 
+                       goto do_escape;
+               case '"':
+                       if (!escape) {
+                               inquote = !inquote;
+                               seen_quote = 1;
+                               (*startpp)++;
+                               continue;
+                       }
+                       /* fall through */
+               default:
+               do_escape:
+                       if (escape) {
+                               switch (c) {
+                               case '0':
+                               case '1':
+                               case '2':
+                               case '3':
+                               case '4':
+                               case '5':
+                               case '6':
+                               case '7':
+                               case '8':
+                               case '9':
+                                       c1 = c1 * 10 + 
+                                               (strchr(digits, c) - digits);
+
+                                       if (++dig == 3) {
+                                               c = c1 &0xff;
+                                               break;
+                                       }
+                                       (*startpp)++;
+                                       continue;
+                               }
+                               escape = 0;
+                       } else if (!inquote && isspace(c))
+                               goto done;
+                       if (cp >= buf+size-1)
+                               goto done;
+                       *cp++ = (u_char)c;
+                       (*startpp)++;
+               }
+       }
+ done:
+       *cp = '\0';
+       return ((cp == buf)?  (seen_quote? 0: -1): (cp - buf));
+}
+/*
+ * Get a whitespace delimited base 16 number from a string (not file) into buf
+ * update the start pointer to point after the number in the string.
+ */
+static int
+gethexnum_str(u_char **startpp, u_char *endp) {
+        int c, n;
+        int seendigit = 0;
+        int m = 0;
+
+       if (*startpp + 2 >= endp || strncasecmp((char *)*startpp, "0x", 2) != 0)
+               return getnum_str(startpp, endp);
+       (*startpp)+=2;
+        for (n = 0; *startpp <= endp; ) {
+                c = **startpp;
+                if (isspace(c) || c == '\0') {
+                        if (seendigit) /* trailing whitespace */
+                                break;
+                        else { /* leading whitespace */
+                                (*startpp)++;
+                                continue;
+                        }
+                }
+                if (c == ';') {
+                        while ((*startpp <= endp) &&
+                              ((c = **startpp) != '\n'))
+                                       (*startpp)++;
+                        if (seendigit)
+                                break;
+                        continue;
+                }
+                if (!isxdigit(c)) {
+                        if (c == ')' && seendigit) {
+                                (*startpp)--;
+                                break;
+                        }
+                       return (-1);
+                }        
+                (*startpp)++;
+               if (isdigit(c))
+                       n = n * 16 + (c - '0');
+               else
+                       n = n * 16 + (tolower(c) - 'a' + 10);
+                seendigit = 1;
+        }
+        return (n + m);
+}
+
+/*
+ * Get a whitespace delimited base 16 number from a string (not file) into buf
+ * update the start pointer to point after the number in the string.
+ */
+static int
+getnum_str(u_char **startpp, u_char *endp) {
+        int c, n;
+        int seendigit = 0;
+        int m = 0;
+
+        for (n = 0; *startpp <= endp; ) {
+                c = **startpp;
+                if (isspace(c) || c == '\0') {
+                        if (seendigit) /* trailing whitespace */
+                                break;
+                        else { /* leading whitespace */
+                                (*startpp)++;
+                                continue;
+                        }
+                }
+                if (c == ';') {
+                        while ((*startpp <= endp) &&
+                              ((c = **startpp) != '\n'))
+                                       (*startpp)++;
+                        if (seendigit)
+                                break;
+                        continue;
+                }
+                if (!isdigit(c)) {
+                        if (c == ')' && seendigit) {
+                                (*startpp)--;
+                                break;
+                        }
+                       return (-1);
+                }        
+                (*startpp)++;
+                n = n * 10 + (c - '0');
+                seendigit = 1;
+        }
+        return (n + m);
+}
+
+/*
+ * Allocate a resource record buffer & save rr info.
+ */
+ns_updrec *
+res_mkupdrec(int section, const char *dname,
+            u_int class, u_int type, u_long ttl) {
+       ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec));
+
+       if (!rrecp || !(rrecp->r_dname = strdup(dname))) {
+               if (rrecp)
+                       free((char *)rrecp);
+               return (NULL);
+       }
+       INIT_LINK(rrecp, r_link);
+       INIT_LINK(rrecp, r_glink);
+       rrecp->r_class = class;
+       rrecp->r_type = type;
+       rrecp->r_ttl = ttl;
+       rrecp->r_section = section;
+       return (rrecp);
+}
+
+/*
+ * Free a resource record buffer created by res_mkupdrec.
+ */
+void
+res_freeupdrec(ns_updrec *rrecp) {
+       /* Note: freeing r_dp is the caller's responsibility. */
+       if (rrecp->r_dname != NULL)
+               free(rrecp->r_dname);
+       free(rrecp);
+}
+
+struct valuelist {
+       struct valuelist *      next;
+       struct valuelist *      prev;
+       char *                  name;
+       char *                  proto;
+       int                     port;
+};
+static struct valuelist *servicelist, *protolist;
+
+static void
+res_buildservicelist() {
+       struct servent *sp;
+       struct valuelist *slp;
+
+#ifdef MAYBE_HESIOD
+       setservent(0);
+#else
+       setservent(1);
+#endif
+       while ((sp = getservent()) != NULL) {
+               slp = (struct valuelist *)malloc(sizeof(struct valuelist));
+               if (!slp)
+                       break;
+               slp->name = strdup(sp->s_name);
+               slp->proto = strdup(sp->s_proto);
+               if ((slp->name == NULL) || (slp->proto == NULL)) {
+                       if (slp->name) free(slp->name);
+                       if (slp->proto) free(slp->proto);
+                       free(slp);
+                       break;
+               }
+               slp->port = ntohs((u_int16_t)sp->s_port);  /* host byt order */
+               slp->next = servicelist;
+               slp->prev = NULL;
+               if (servicelist)
+                       servicelist->prev = slp;
+               servicelist = slp;
+       }
+       endservent();
+}
+
+void
+res_destroyservicelist() {
+       struct valuelist *slp, *slp_next;
+
+       for (slp = servicelist; slp != NULL; slp = slp_next) {
+               slp_next = slp->next;
+               free(slp->name);
+               free(slp->proto);
+               free(slp);
+       }
+       servicelist = (struct valuelist *)0;
+}
+
+void
+res_buildprotolist(void) {
+       struct protoent *pp;
+       struct valuelist *slp;
+
+#ifdef MAYBE_HESIOD
+       setprotoent(0);
+#else
+       setprotoent(1);
+#endif
+       while ((pp = getprotoent()) != NULL) {
+               slp = (struct valuelist *)malloc(sizeof(struct valuelist));
+               if (!slp)
+                       break;
+               slp->name = strdup(pp->p_name);
+               if (slp->name == NULL) {
+                       free(slp);
+                       break;
+               }
+               slp->port = pp->p_proto;        /* host byte order */
+               slp->next = protolist;
+               slp->prev = NULL;
+               if (protolist)
+                       protolist->prev = slp;
+               protolist = slp;
+       }
+       endprotoent();
+}
+
+void
+res_destroyprotolist(void) {
+       struct valuelist *plp, *plp_next;
+
+       for (plp = protolist; plp != NULL; plp = plp_next) {
+               plp_next = plp->next;
+               free(plp->name);
+               free(plp);
+       }
+       protolist = (struct valuelist *)0;
+}
+
+static int
+findservice(const char *s, struct valuelist **list) {
+       struct valuelist *lp = *list;
+       int n;
+
+       for (; lp != NULL; lp = lp->next)
+               if (strcasecmp(lp->name, s) == 0) {
+                       if (lp != *list) {
+                               lp->prev->next = lp->next;
+                               if (lp->next)
+                                       lp->next->prev = lp->prev;
+                               (*list)->prev = lp;
+                               lp->next = *list;
+                               *list = lp;
+                       }
+                       return (lp->port);      /* host byte order */
+               }
+       if (sscanf(s, "%d", &n) != 1 || n <= 0)
+               n = -1;
+       return (n);
+}
+
+/*
+ * Convert service name or (ascii) number to int.
+ */
+#ifdef __APPLE__
+static
+#endif
+int
+res_servicenumber(const char *p) {
+       if (servicelist == (struct valuelist *)0)
+               res_buildservicelist();
+       return (findservice(p, &servicelist));
+}
+
+/*
+ * Convert protocol name or (ascii) number to int.
+ */
+#ifdef __APPLE__
+static
+#endif
+int
+res_protocolnumber(const char *p) {
+       if (protolist == (struct valuelist *)0)
+               res_buildprotolist();
+       return (findservice(p, &protolist));
+}
+
+static struct servent *
+cgetservbyport(u_int16_t port, const char *proto) {    /* Host byte order. */
+       struct valuelist **list = &servicelist;
+       struct valuelist *lp = *list;
+       static struct servent serv;
+
+       port = ntohs(port);
+       for (; lp != NULL; lp = lp->next) {
+               if (port != (u_int16_t)lp->port)        /* Host byte order. */
+                       continue;
+               if (strcasecmp(lp->proto, proto) == 0) {
+                       if (lp != *list) {
+                               lp->prev->next = lp->next;
+                               if (lp->next)
+                                       lp->next->prev = lp->prev;
+                               (*list)->prev = lp;
+                               lp->next = *list;
+                               *list = lp;
+                       }
+                       serv.s_name = lp->name;
+                       serv.s_port = htons((u_int16_t)lp->port);
+                       serv.s_proto = lp->proto;
+                       return (&serv);
+               }
+       }
+       return (0);
+}
+
+static struct protoent *
+cgetprotobynumber(int proto) {                         /* Host byte order. */
+       struct valuelist **list = &protolist;
+       struct valuelist *lp = *list;
+       static struct protoent prot;
+
+       for (; lp != NULL; lp = lp->next)
+               if (lp->port == proto) {                /* Host byte order. */
+                       if (lp != *list) {
+                               lp->prev->next = lp->next;
+                               if (lp->next)
+                                       lp->next->prev = lp->prev;
+                               (*list)->prev = lp;
+                               lp->next = *list;
+                               *list = lp;
+                       }
+                       prot.p_name = lp->name;
+                       prot.p_proto = lp->port;        /* Host byte order. */
+                       return (&prot);
+               }
+       return (0);
+}
+
+const char *
+res_protocolname(int num) {
+       static char number[8];
+       struct protoent *pp;
+
+       if (protolist == (struct valuelist *)0)
+               res_buildprotolist();
+       pp = cgetprotobynumber(num);
+       if (pp == 0)  {
+               (void) sprintf(number, "%d", num);
+               return (number);
+       }
+       return (pp->p_name);
+}
+
+const char *
+res_servicename(u_int16_t port, const char *proto) {   /* Host byte order. */
+       static char number[8];
+       struct servent *ss;
+
+       if (servicelist == (struct valuelist *)0)
+               res_buildservicelist();
+       ss = cgetservbyport(htons(port), proto);
+       if (ss == 0)  {
+               (void) sprintf(number, "%d", port);
+               return (number);
+       }
+       return (ss->s_name);
+}
diff --git a/res_private.h b/res_private.h
new file mode 100644 (file)
index 0000000..562d5ab
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef res_9_private_h
+#define res_9_private_h
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <stdint.h>
+
+/*
+ * status codes from dns_res_xxx SPIs
+ * positive numbers are ns_rcode values.
+ */
+#define DNS_RES_STATUS_TIMEOUT -1001
+#define DNS_RES_STATUS_CANCELLED -1002
+#define DNS_RES_STATUS_INVALID_QUERY -1003
+#define DNS_RES_STATUS_INVALID_ARGUMENT -1004
+#define DNS_RES_STATUS_INVALID_RES_STATE -1005
+#define DNS_RES_STATUS_INVALID_REPLY -1006
+#define DNS_RES_STATUS_CONNECTION_REFUSED -1007
+#define DNS_RES_STATUS_SEND_FAILED -1008
+#define DNS_RES_STATUS_CONNECTION_FAILED -1009
+#define DNS_RES_STATUS_SYSTEM_ERROR -1010
+
+#define RES_EXT_SUFFIX_LEN 64
+
+typedef struct {
+       unsigned        id :16;         /* query identification number */
+#if BYTE_ORDER == BIG_ENDIAN
+                       /* fields in third byte */
+       unsigned        qr: 1;          /* response flag */
+       unsigned        opcode: 4;      /* purpose of message */
+       unsigned        aa: 1;          /* authoritive answer */
+       unsigned        tc: 1;          /* truncated message */
+       unsigned        rd: 1;          /* recursion desired */
+                       /* fields in fourth byte */
+       unsigned        ra: 1;          /* recursion available */
+       unsigned        unused :3;      /* unused bits (MBZ as of 4.9.3a3) */
+       unsigned        rcode :4;       /* response code */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
+                       /* fields in third byte */
+       unsigned        rd :1;          /* recursion desired */
+       unsigned        tc :1;          /* truncated message */
+       unsigned        aa :1;          /* authoritive answer */
+       unsigned        opcode :4;      /* purpose of message */
+       unsigned        qr :1;          /* response flag */
+                       /* fields in fourth byte */
+       unsigned        rcode :4;       /* response code */
+       unsigned        unused :3;      /* unused bits (MBZ as of 4.9.3a3) */
+       unsigned        ra :1;          /* recursion available */
+#endif
+                       /* remaining bytes */
+       unsigned        qdcount :16;    /* number of question entries */
+       unsigned        ancount :16;    /* number of answer entries */
+       unsigned        nscount :16;    /* number of authority entries */
+       unsigned        arcount :16;    /* number of resource entries */
+} HEADER;
+
+#ifndef __res_state_ext
+#define __res_state_ext __res_9_res_state_ext
+#endif
+
+struct __res_state_ext {
+       union res_sockaddr_union nsaddrs[MAXNS];
+       struct sort_list {
+               int af;
+               union {
+                       struct in_addr  ina;
+                       struct in6_addr in6a;
+               } addr, mask;
+       } sort_list[MAXRESOLVSORT];
+       char nsuffix[64];
+       char bsuffix[64];
+       char nsuffix2[64];
+};
+
+#define get_nsaddr res_9_get_nsaddr
+struct sockaddr *get_nsaddr __P((res_state, size_t));
+
+#define res_nsend_2 res_9_nsend_2
+int res_nsend_2(res_state, const u_char *, int, u_char *, int, struct sockaddr *, int *);
+
+#define res_ourserver_p res_9_ourserver_p
+int res_ourserver_p(const res_state, const struct sockaddr *);
+
+res_state res_state_new();
+void res_client_close(res_state res);
+
+/*
+ * From lookupd Thread.h.  We use this to signal threads to quit, since pthread_cancel() doesn't work.
+ */
+#define ThreadStateExitRequested 4
+
+/*
+ * notification SPI
+ */
+extern uint32_t notify_register_plain(const char *name, int *out_token);
+
+__private_extern__ int res_query_mDNSResponder(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen);
+
+int dns_res_once(struct sockaddr *server, struct timeval *timeout, int options, const char *name, int class, int type, u_char *res, int *reslen);
+
+#endif
diff --git a/res_query.c b/res_query.c
new file mode 100644 (file)
index 0000000..27217cc
--- /dev/null
@@ -0,0 +1,987 @@
+/*
+ * Copyright (c) 1988, 1993
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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
+ * CORPORATION 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.
+ */
+
+/*
+ * 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 const char sccsid[] = "@(#)res_query.c  8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$Id: res_query.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "res_private.h"
+#include <dns_sd.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#ifndef __APPLE__
+#include "port_after.h"
+#endif
+
+/* Options.  Leave them on. */
+#define DEBUG
+
+#if PACKETSZ > 1024
+#define MAXPACKET      PACKETSZ
+#else
+#define MAXPACKET      1024
+#endif
+
+#define BILLION 1000000000
+
+/* length of a reverse DNS IPv6 address query name, e.g. "9.4.a.f.c.e.e.f.e.e.1.5.4.1.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa" */
+#define IPv6_REVERSE_LEN 72
+
+/* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
+#define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
+
+/* index of low-order nibble of embedded scope id */
+#define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
+
+const static uint8_t hexval[128] =
+{
+       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,          /*  0 - 15 */
+       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,          /* 16 - 31 */
+       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,          /* 32 - 47 */
+       0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0,  0,  0,  0,  0,          /* 48 - 63 */
+       0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,          /* 64 - 79 */
+       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,          /* 80 - 95 */
+       0, 10, 11, 12, 13, 14, 15,  0,  0,  0,  0,  0,  0,  0,  0,  0,          /* 96 - 111 */
+       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0           /* 112 - 127 */
+};
+
+struct res_query_context
+{
+       u_char *answer;
+       size_t anslen;
+       size_t ansmaxlen;
+       uint32_t ifnum;
+       DNSServiceFlags flags;
+       DNSServiceErrorType error;
+};
+
+static void
+res_query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *ctx)
+{
+       struct res_query_context *context;
+       int n;
+       size_t buflen;
+       u_char *dnlist[2], *cp;
+       HEADER *ans;
+       struct in6_addr a6;
+
+       context = (struct res_query_context *)ctx;
+
+       context->flags = flags;
+       context->error = errorCode;
+
+       if (errorCode != kDNSServiceErr_NoError) return;
+
+       buflen = context->ansmaxlen - context->anslen;
+       if (buflen < NS_HFIXEDSZ) return;
+
+       dnlist[0] = context->answer + NS_HFIXEDSZ;
+       dnlist[1] = NULL;
+
+       cp = context->answer + context->anslen;
+
+       n = dn_comp((char *)fullname, cp, buflen, dnlist, &dnlist[1]);
+       if (n < 0) return;
+
+       /*
+        * Check that there is enough space in the buffer for the resource name (n),
+        * the resource record data (rdlen) and the resource record header (10).
+        */
+       if (buflen < n + rdlen + 10) return;
+
+       cp += n;
+       buflen -= n;
+
+       putshort(rrtype, cp);
+       cp += sizeof(uint16_t);
+
+       putshort(rrclass, cp);
+       cp += sizeof(uint16_t);
+
+       putlong(ttl, cp);
+       cp += sizeof(uint32_t);
+
+       putshort(rdlen, cp);
+       cp += sizeof(uint16_t);
+
+       memcpy(cp, rdata, rdlen);
+       cp += rdlen;
+
+       ans = (HEADER *)context->answer;
+       ans->ancount = htons(ntohs(ans->ancount) + 1);
+
+       context->anslen = (size_t)(cp - context->answer);
+
+       /* 
+        * Save the interface number for the first AAAA record for link-local addresses.
+        * It's used by getaddrinfo to set the scope id.
+        */
+       if ((context->ifnum == 0) && (rrtype == ns_t_aaaa))
+       {
+               memset(&a6, 0, sizeof(struct in6_addr));
+               memcpy(&a6, rdata, sizeof(struct in6_addr));
+               if (IN6_IS_ADDR_LINKLOCAL(&a6)) context->ifnum = ifIndex;
+       }
+}
+
+static void
+h_errno_for_dnssd_err(DNSServiceErrorType dnssd_err, int *h_errno_err)
+{
+       switch (dnssd_err)
+       {
+               case kDNSServiceErr_NoError:
+                       *h_errno_err = NETDB_SUCCESS;
+                       break;
+               case kDNSServiceErr_Unknown:
+                       *h_errno_err = NO_RECOVERY;
+                       break;
+               case kDNSServiceErr_NoSuchRecord:
+                       *h_errno_err = NO_DATA;
+                       break;
+               case kDNSServiceErr_NoSuchName:
+                       *h_errno_err = HOST_NOT_FOUND;
+                       break;
+               case kDNSServiceErr_NoMemory:
+               default:
+                       *h_errno_err = NETDB_INTERNAL;
+                       break;
+       }
+}
+
+static int
+_is_rev_link_local(const char *name)
+{
+       int len, i;
+
+       if (name == NULL) return 0;
+
+       len = strlen(name);
+       if (len == 0) return 0;
+
+       /* check for trailing '.' */
+       if (name[len - 1] == '.') len--;
+
+       if (len != IPv6_REVERSE_LEN) return 0;
+
+       i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR;
+       if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0;
+
+       i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1;
+       if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0;
+
+       for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2)
+       {
+               if (name[i] < '0') return 0;
+               if ((name[i] > '9') && (name[i] < 'A')) return 0;
+               if ((name[i] > 'F') && (name[i] < 'a')) return 0;
+               if (name[i] > 'f') return 0;
+               if (name[i + 1] != '.') return 0;
+       }
+
+       return 1;
+}
+
+__private_extern__ int
+res_query_mDNSResponder(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen)
+{
+       DNSServiceRef sdRef;
+       DNSServiceErrorType result;
+       struct res_query_context context;
+       int i, kq, n, wait;
+       struct kevent kv;
+       struct timeval ctv;
+       struct timespec now, finish, timeout;
+       HEADER *ans;
+       uint32_t iface;
+       uint16_t nibble;
+       char *qname;
+       result = 0;
+       kq = -1;
+       ans = (HEADER *)answer;
+
+       ans->rcode = 0;
+
+       memset(&context, 0, sizeof(struct res_query_context));
+
+       /* Build a dummy DNS header with question for the answer */
+       context.answer = answer;
+       context.ansmaxlen = anslen;
+       context.anslen = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, answer, anslen);
+       if (context.anslen <= 0) return 0;
+
+       /* Mark DNS packet as a response */
+       ans->qr = 1;
+       ans->qr = htons(ans->qr);
+
+       /* Pull out Scope ID in link-local reverse queries */
+       qname = (char *)name;
+       iface = 0;
+       if (_is_rev_link_local(name))
+       {
+               /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
+               i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
+               nibble = hexval[(uint32_t)name[i]];
+               iface = nibble;
+
+               i += 2;
+               nibble = hexval[(uint32_t)name[i]];
+               iface += (nibble << 4);
+
+               i += 2;
+               nibble = hexval[(uint32_t)name[i]];
+               iface += (nibble << 8);
+
+               i += 2;
+               nibble = hexval[(uint32_t)name[i]];
+               iface += (nibble << 12);
+
+               if (iface != 0)
+               {
+                       qname = strdup(name);
+                       if (qname == NULL)
+                       {
+                               h_errno = NO_RECOVERY;
+                               errno = ENOMEM;
+                               return -1;
+                       }
+
+                       i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
+                       qname[i] = '0';
+                       qname[i + 2] = '0';
+                       qname[i + 4] = '0';
+                       qname[i + 6] = '0';
+               }
+       }
+
+       result = DNSServiceQueryRecord(&sdRef, kDNSServiceFlagsReturnIntermediates, iface, qname, type, class, res_query_callback, &context);
+       if (iface != 0) free(qname);
+
+       if (result != 0) return 0;
+
+       /* Use a kqueue to wait for a response from mDNSResponder */
+       kq = kqueue();
+
+       /* determine the maximum time to wait for a result */
+       gettimeofday(&ctv, NULL);
+
+       /* N.B. statp->retrans is actually the total timeount in seconds */
+       timeout.tv_sec = statp->retrans;
+       timeout.tv_nsec = 0;
+
+       finish.tv_sec = ctv.tv_sec + statp->retrans;
+       finish.tv_nsec = ctv.tv_usec * 1000;
+
+       EV_SET(&kv, DNSServiceRefSockFD(sdRef), EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
+
+       wait = 1;
+       while (wait == 1)
+       {
+               n = kevent(kq, &kv, 1, &kv, 1, &timeout);
+
+               if (n < 0)
+               {
+                       if (errno == EINTR) goto keep_waiting;
+                       h_errno = NO_RECOVERY;
+                       wait = 0;
+               }
+               else if ((n == 0) && (ans->ancount == 0))
+               {
+                       h_errno = TRY_AGAIN;
+                       wait = 0;
+               }
+               else
+               {
+                       result = DNSServiceProcessResult(sdRef);
+                       if ((result != 0) || (context.error != 0))
+                       {
+                               if (result == 0) result = context.error;
+                               h_errno_for_dnssd_err(result, &h_errno);
+                               wait = 0;
+                       }
+
+                       if ((ans->ancount > 0) && ((context.flags & kDNSServiceFlagsMoreComing) == 0)) wait = 0;
+               }
+
+       keep_waiting:
+
+               if (wait == 1)
+               {
+                       /* calculate remaining timeout */
+                       gettimeofday(&ctv, NULL);
+
+                       now.tv_sec = ctv.tv_sec;
+                       now.tv_nsec = ctv.tv_usec * 1000;
+
+                       timeout.tv_sec = finish.tv_sec - now.tv_sec;
+                       if (finish.tv_nsec >= now.tv_nsec)
+                       {
+                               timeout.tv_nsec = finish.tv_nsec - now.tv_nsec;
+                       }
+                       else
+                       {
+                               timeout.tv_nsec = BILLION - now.tv_nsec + finish.tv_nsec;
+                               timeout.tv_sec--;
+                       }
+               }
+       }
+
+       DNSServiceRefDeallocate(sdRef);
+       close(kq);
+
+       if (ans->ancount == 0) context.anslen = -1;
+
+       if ((from != NULL) && (fromlen != NULL) && (context.ifnum != 0))
+       {
+               ((struct sockaddr_in6 *)from)->sin6_len = sizeof(struct sockaddr_in6);
+               ((struct sockaddr_in6 *)from)->sin6_family = AF_INET6;
+               ((struct sockaddr_in6 *)from)->sin6_addr.__u6_addr.__u6_addr8[15] = 1;
+               ((struct sockaddr_in6 *)from)->sin6_scope_id = context.ifnum;
+               *fromlen = sizeof(struct sockaddr_in6);
+       }
+
+       return context.anslen;
+}
+
+static int
+res_soa_minimum(const u_char *msg, int len)
+{
+       ns_msg handle;
+       const u_char *eom;
+       uint32_t i, b;
+       int min, soa_min;
+
+       eom = msg + len;
+
+       handle._msg = msg;
+       handle._eom = eom;
+
+       if (msg + NS_INT16SZ > eom) return -1;
+       NS_GET16(handle._id, msg);
+
+       if (msg + NS_INT16SZ > eom) return -1;
+       NS_GET16(handle._flags, msg);
+
+       for (i = 0; i < ns_s_max; i++)
+       {
+               if (msg + NS_INT16SZ > eom) return -1;
+               NS_GET16(handle._counts[i], msg);
+       }
+
+       if (handle._counts[ns_s_ns] == 0) return -1;
+
+       /* Skip forward to nameserver section */
+       for (i = 0; i < ns_s_ns; i++)
+       {
+               if (handle._counts[i] == 0) handle._sections[i] = NULL;
+               else
+               {
+                       b = ns_skiprr(msg, eom, (ns_sect)i, handle._counts[i]);
+                       if (b < 0) return -1;
+
+                       handle._sections[i] = msg;
+                       msg += b;
+               }
+       }
+
+       min = -1;
+       for (i = 0; i < handle._counts[ns_s_ns]; i++)
+       {
+               b = ns_skiprr(msg, eom, ns_s_ns, 1);
+               if (b < 0) return -1;
+
+               memcpy(&soa_min, msg + b - sizeof(int32_t), sizeof(int32_t));
+               soa_min = ntohl(soa_min);
+               if ((i == 0) || (soa_min < min)) min = soa_min;
+               msg += b;
+       }
+
+       return min;
+}
+
+/*
+ * Formulate a normal query, send, and await answer.
+ * Returned answer is placed in supplied buffer "answer".
+ * Perform preliminary check of answer, returning success only
+ * if no error is indicated and the answer count is nonzero.
+ * Return the size of the response on success, -1 on error.
+ * Error number is left in H_ERRNO.
+ *
+ * Caller must parse answer and determine whether it answers the question.
+ */
+__private_extern__ int
+res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min)
+{
+       u_char buf[MAXPACKET];
+       HEADER *hp = (HEADER *) answer;
+       int n;
+       u_int oflags;
+
+       if (min != NULL) *min = -1;
+
+       oflags = statp->_flags;
+
+again:
+       __h_errno_set(statp, 0);
+       hp->rcode = ns_r_noerror;       /* default */
+
+#ifdef DEBUG
+       if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type);
+#endif
+
+       n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf));
+#ifdef RES_USE_EDNS0
+       if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
+               n = res_nopt(statp, n, buf, sizeof(buf), anslen);
+#endif
+       if (n <= 0)
+       {
+#ifdef DEBUG
+               if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n");
+#endif
+               __h_errno_set(statp, NO_RECOVERY);
+               return (n);
+       }
+
+       n = res_nsend_2(statp, buf, n, answer, anslen, from, fromlen);
+       if (n < 0)
+       {
+#ifdef RES_USE_EDNS0
+               /* if the query choked with EDNS0, retry without EDNS0 */
+               if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0)
+               {
+                       statp->_flags |= RES_F_EDNS0ERR;
+                       if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n");
+                       goto again;
+               }
+#endif
+#ifdef DEBUG
+               if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
+#endif
+               __h_errno_set(statp, TRY_AGAIN);
+               return (n);
+       }
+
+       if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0)
+       {
+               if (min != NULL)
+               {
+                       *min = res_soa_minimum(answer, anslen);
+                       if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum = %d\n", *min);
+               }
+
+#ifdef DEBUG
+               if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount));
+#endif
+               switch (hp->rcode)
+               {
+                       case ns_r_nxdomain:
+                               __h_errno_set(statp, HOST_NOT_FOUND);
+                               break;
+                       case ns_r_servfail:
+                               __h_errno_set(statp, TRY_AGAIN);
+                               break;
+                       case ns_r_noerror:
+                               __h_errno_set(statp, NO_DATA);
+                               break;
+                       case ns_r_formerr:
+                       case ns_r_notimpl:
+                       case ns_r_refused:
+                       default:
+                               __h_errno_set(statp, NO_RECOVERY);
+                               break;
+               }
+
+               return (-1);
+       }
+
+       return (n);
+}
+
+int
+res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
+{
+       int unused = 0;
+
+       return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused);
+}
+
+int
+res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
+{
+       struct sockaddr_storage f;
+       int l;
+
+       l = sizeof(struct sockaddr_storage);
+
+       return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
+}
+
+/*
+ * Perform a call on res_query on the concatenation of name and domain,
+ * removing a trailing dot from name if domain is NULL.
+ */
+int
+res_nquerydomain_2(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
+{
+       char nbuf[NS_MAXDNAME];
+       const char *longname = nbuf;
+       int n, d;
+
+#ifdef DEBUG
+       if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type);
+#endif
+       if (domain == NULL)
+       {
+               /*
+                * Check for trailing '.';
+                * copy without '.' if present.
+                */
+               n = strlen(name);
+               if (n >= NS_MAXDNAME)
+               {
+                       __h_errno_set(statp, NO_RECOVERY);
+                       return (-1);
+               }
+
+               n--;
+               if (n >= 0 && name[n] == '.')
+               {
+                       strncpy(nbuf, name, n);
+                       nbuf[n] = '\0';
+               }
+               else
+               {
+                       longname = name;
+               }
+       }
+       else
+       {
+               n = strlen(name);
+               d = strlen(domain);
+               if (n + d + 1 >= NS_MAXDNAME)
+               {
+                       __h_errno_set(statp, NO_RECOVERY);
+                       return (-1);
+               }
+
+               sprintf(nbuf, "%s.%s", name, domain);
+       }
+
+       return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen));
+}
+
+int
+res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen)
+{
+       struct sockaddr_storage f;
+       int l;
+
+       l = sizeof(struct sockaddr_storage);
+
+       return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l);
+}
+
+/*
+ * Formulate a normal query, send, and retrieve answer in supplied buffer.
+ * Return the size of the response on success, -1 on error.
+ * If enabled, implement search rules until answer or unrecoverable failure
+ * is detected.  Error code, if any, is left in H_ERRNO.
+ */
+int
+res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen)
+{
+       const char *cp, * const *domain;
+       HEADER *hp = (HEADER *) answer;
+       char tmp[NS_MAXDNAME];
+       u_int dots;
+       int trailing_dot, ret, saved_herrno;
+       int got_nodata = 0, got_servfail = 0, root_on_list = 0;
+       int tried_as_is = 0;
+       int searched = 0;
+
+       errno = 0;
+       __h_errno_set(statp, HOST_NOT_FOUND);  /* True if we never query. */
+
+       dots = 0;
+       for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
+
+       trailing_dot = 0;
+       if (cp > name && *--cp == '.') trailing_dot++;
+
+       /* If there aren't any dots, it could be a user-level alias. */
+       if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
+               return (res_nquery(statp, cp, class, type, answer, anslen));
+
+       /*
+        * If there are enough dots in the name, let's just give it a
+        * try 'as is'. The threshold can be set with the "ndots" option.
+        * Also, query 'as is', if there is a trailing dot in the name.
+        */
+       saved_herrno = -1;
+       if (dots >= statp->ndots || trailing_dot)
+       {
+               ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
+               if (ret > 0 || trailing_dot) return (ret);
+               saved_herrno = h_errno;
+               tried_as_is++;
+       }
+
+       /*
+        * We do at least one level of search if
+        *      - there is no dot and RES_DEFNAME is set, or
+        *      - there is at least one dot, there is no trailing dot,
+        *        and RES_DNSRCH is set.
+        */
+       if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
+       {
+               int done = 0;
+
+               for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++)
+               {
+                       searched = 1;
+
+                       if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++;
+
+                       ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen);
+                       if (ret > 0) return (ret);
+
+                       /*
+                        * If no server present, give up.
+                        * If name isn't found in this domain,
+                        * keep trying higher domains in the search list
+                        * (if that's enabled).
+                        * On a NO_DATA error, keep trying, otherwise
+                        * a wildcard entry of another type could keep us
+                        * from finding this entry higher in the domain.
+                        * If we get some other error (negative answer or
+                        * server failure), then stop searching up,
+                        * but try the input name below in case it's
+                        * fully-qualified.
+                        */
+                       if (errno == ECONNREFUSED)
+                       {
+                               __h_errno_set(statp, TRY_AGAIN);
+                               return (-1);
+                       }
+
+                       switch (statp->res_h_errno)
+                       {
+                               case NO_DATA:
+                                       got_nodata++;
+                                       /* FALLTHROUGH */
+                               case HOST_NOT_FOUND:
+                                       /* keep trying */
+                                       break;
+                               case TRY_AGAIN:
+                                       if (hp->rcode == ns_r_refused)
+                                       {
+                                               /* try next search element, if any */
+                                               got_servfail++;
+                                               break;
+                                       }
+                                       /* FALLTHROUGH */
+                               default:
+                                       /* anything else implies that we're done */
+                                       done++;
+                       }
+
+                       /* if we got here for some reason other than DNSRCH,
+                        * we only wanted one iteration of the loop, so stop.
+                        */
+                       if ((statp->options & RES_DNSRCH) == 0) done++;
+               }
+       }
+
+       /*
+        * If the query has not already been tried as is then try it
+        * unless RES_NOTLDQUERY is set and there were no dots.
+        */
+       if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
+       {
+               ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
+               if (ret > 0) return (ret);
+       }
+
+       /* if we got here, we didn't satisfy the search.
+        * if we did an initial full query, return that query's H_ERRNO
+        * (note that we wouldn't be here if that query had succeeded).
+        * else if we ever got a nodata, send that back as the reason.
+        * else send back meaningless H_ERRNO, that being the one from
+        * the last DNSRCH we did.
+        */
+       if (saved_herrno != -1)
+               __h_errno_set(statp, saved_herrno);
+       else if (got_nodata)
+               __h_errno_set(statp, NO_DATA);
+       else if (got_servfail)
+               __h_errno_set(statp, TRY_AGAIN);
+       return (-1);
+}
+
+int
+__res_nsearch_list_2(res_state statp, const char *name,        int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int nsearch, char **search)
+{
+       const char *cp, *domain;
+       HEADER *hp = (HEADER *) answer;
+       char tmp[NS_MAXDNAME];
+       u_int dots;
+       int trailing_dot, ret, saved_herrno, i;
+       int got_nodata = 0, got_servfail = 0, root_on_list = 0;
+       int tried_as_is = 0;
+       int searched = 0;
+
+       errno = 0;
+       __h_errno_set(statp, HOST_NOT_FOUND);  /* True if we never query. */
+
+       dots = 0;
+       for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.');
+
+       trailing_dot = 0;
+       if (cp > name && *--cp == '.') trailing_dot++;
+
+       /* If there aren't any dots, it could be a user-level alias. */
+       if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL)
+               return (res_nquery(statp, cp, class, type, answer, anslen));
+
+       /*
+        * If there are enough dots in the name, let's just give it a
+        * try 'as is'. The threshold can be set with the "ndots" option.
+        * Also, query 'as is', if there is a trailing dot in the name.
+        */
+       saved_herrno = -1;
+       if (dots >= statp->ndots || trailing_dot)
+       {
+               ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen);
+               if (ret > 0 || trailing_dot) return ret;
+               saved_herrno = h_errno;
+               tried_as_is++;
+       }
+
+       /*
+        * We do at least one level of search if
+        *      - there is no dot and RES_DEFNAME is set, or
+        *      - there is at least one dot, there is no trailing dot,
+        *        and RES_DNSRCH is set.
+        */
+       if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0))
+       {
+               int done = 0;
+
+               for (i = 0; i < nsearch; i++)
+               {
+                       domain = search[i];
+                       searched = 1;
+
+                       if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++;
+
+                       ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen);
+                       if (ret > 0) return ret;
+
+                       /*
+                        * If no server present, give up.
+                        * If name isn't found in this domain,
+                        * keep trying higher domains in the search list
+                        * (if that's enabled).
+                        * On a NO_DATA error, keep trying, otherwise
+                        * a wildcard entry of another type could keep us
+                        * from finding this entry higher in the domain.
+                        * If we get some other error (negative answer or
+                                                                                  * server failure), then stop searching up,
+                        * but try the input name below in case it's
+                        * fully-qualified.
+                        */
+                       if (errno == ECONNREFUSED)
+                       {
+                               __h_errno_set(statp, TRY_AGAIN);
+                               return -1;
+                       }
+
+                       switch (statp->res_h_errno)
+                       {
+                               case NO_DATA:
+                                       got_nodata++;
+                                       /* FALLTHROUGH */
+                               case HOST_NOT_FOUND:
+                                       /* keep trying */
+                                       break;
+                               case TRY_AGAIN:
+                                       if (hp->rcode == ns_r_refused)
+                                       {
+                                               /* try next search element, if any */
+                                               got_servfail++;
+                                               break;
+                                       }
+                                       /* FALLTHROUGH */
+                               default:
+                                       /* anything else implies that we're done */
+                                       done++;
+                       }
+
+                       /*
+                        * if we got here for some reason other than DNSRCH,
+                        * we only wanted one iteration of the loop, so stop.
+                        */
+                       if ((statp->options & RES_DNSRCH) == 0) done++;
+               }
+       }
+
+       /*
+        * If the query has not already been tried as is then try it
+        * unless RES_NOTLDQUERY is set and there were no dots.
+        */
+       if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list))
+       {
+               ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen);
+               if (ret > 0) return ret;
+       }
+
+       /*
+        * we got here, we didn't satisfy the search.
+        * if we did an initial full query, return that query's H_ERRNO
+        * (note that we wouldn't be here if that query had succeeded).
+        * else if we ever got a nodata, send that back as the reason.
+        * else send back meaningless H_ERRNO, that being the one from
+        * the last DNSRCH we did.
+        */
+       if (saved_herrno != -1) __h_errno_set(statp, saved_herrno);
+       else if (got_nodata) __h_errno_set(statp, NO_DATA);
+       else if (got_servfail) __h_errno_set(statp, TRY_AGAIN);
+       return -1;
+}
+                         
+int
+res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen)
+{
+       struct sockaddr_storage f;
+       int l;
+
+       l = sizeof(struct sockaddr_storage);
+
+       return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l);
+}
+
+const char *
+res_hostalias(const res_state statp, const char *name, char *dst, size_t siz)
+{
+       char *file, *cp1, *cp2;
+       char buf[BUFSIZ];
+       FILE *fp;
+
+       if (statp->options & RES_NOALIASES) return (NULL);
+
+       file = getenv("HOSTALIASES");
+       if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL);
+
+       setbuf(fp, NULL);
+       buf[sizeof(buf) - 1] = '\0';
+       while (fgets(buf, sizeof(buf), fp))
+       {
+               for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ;
+
+               if (!*cp1) break;
+               *cp1 = '\0';
+
+               if (ns_samename(buf, name) == 1)
+               {
+                       while (isspace((unsigned char)*++cp1)) ;
+
+                       if (!*cp1) break;
+
+                       for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ;
+
+                       *cp2 = '\0';
+                       strncpy(dst, cp1, siz - 1);
+                       dst[siz - 1] = '\0';
+                       fclose(fp);
+                       return (dst);
+               }
+       }
+
+       fclose(fp);
+       return (NULL);
+}
diff --git a/res_send.c b/res_send.c
new file mode 100644 (file)
index 0000000..44f40ed
--- /dev/null
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 1985, 1989, 1993
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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
+ * CORPORATION 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.
+ */
+
+/*
+ * 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 const char sccsid[] = "@(#)res_send.c   8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$Id: res_send.c,v 1.1 2006/03/01 19:01:38 majka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Send query to name server and wait for reply.
+ */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#include "fd_setsize.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <notify.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include "res_private.h"
+
+#ifndef __APPLE__
+#include <isc/eventlib.h>
+#include "port_after.h"
+#endif
+
+#ifdef __APPLE__
+#define ISC_SOCKLEN_T unsigned int
+#endif
+
+/* Options.  Leave them on. */
+#define DEBUG
+#define CANNOT_CONNECT_DGRAM
+#ifdef __APPLE__
+#define MULTICAST
+#endif
+
+#include "res_debug.h"
+#include "res_private.h"
+
+#define EXT(res) ((res)->_u._ext)
+
+static const int highestFD = FD_SETSIZE - 1;
+
+#define MAX_HOOK_RETRIES 42
+
+/* Forward. */
+
+static int             get_salen __P((const struct sockaddr *));
+static int             send_vc(res_state, const u_char *, int, u_char *, int *, int *, int, struct sockaddr *, int *, int);
+static int             send_dg(res_state, const u_char *, int, u_char *, int *, int *, int, int *, int *, struct sockaddr *, int *, int);
+static void            Aerror(const res_state, FILE *, const char *, int, const struct sockaddr *, int);
+static void            Perror(const res_state, FILE *, const char *, int);
+static int             sock_eq(struct sockaddr *, struct sockaddr *);
+#ifdef NEED_PSELECT
+static int             pselect(int, void *, void *, void *, struct timespec *, const sigset_t *);
+#endif
+
+static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+
+#ifdef __APPLE__
+static struct iovec
+evConsIovec(void *buf, size_t cnt)
+{
+       struct iovec ret;
+
+       memset(&ret, 0xf5, sizeof ret);
+       ret.iov_base = buf;
+       ret.iov_len = cnt;
+       return (ret);
+}
+
+static struct timespec
+evConsTime(time_t sec, long nsec)
+{
+       struct timespec x;
+
+       x.tv_sec = sec;
+       x.tv_nsec = nsec;
+       return (x);
+}
+
+static struct timespec
+evTimeSpec(struct timeval tv)
+{
+       struct timespec ts;
+
+       ts.tv_sec = tv.tv_sec;
+       ts.tv_nsec = tv.tv_usec * 1000;
+       return (ts);
+}
+
+static struct timespec
+evNowTime()
+{
+       struct timeval now;
+
+       if (gettimeofday(&now, NULL) < 0) return (evConsTime(0, 0));
+       return (evTimeSpec(now));
+}
+
+#ifdef NEED_PSELECT
+static struct timeval
+evTimeVal(struct timespec ts)
+{
+       struct timeval tv;
+
+       tv.tv_sec = ts.tv_sec;
+       tv.tv_usec = ts.tv_nsec / 1000;
+       return (tv);
+}
+#endif
+
+#define BILLION 1000000000
+static struct timespec
+evAddTime(struct timespec addend1, struct timespec addend2)
+{
+       struct timespec x;
+
+       x.tv_sec = addend1.tv_sec + addend2.tv_sec;
+       x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec;
+       if (x.tv_nsec >= BILLION)
+       {
+               x.tv_sec++;
+               x.tv_nsec -= BILLION;
+       }
+
+       return (x);
+}
+
+static struct timespec
+evSubTime(struct timespec minuend, struct timespec subtrahend)
+{
+       struct timespec x;
+
+       x.tv_sec = minuend.tv_sec - subtrahend.tv_sec;
+       if (minuend.tv_nsec >= subtrahend.tv_nsec)
+       {
+               x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec;
+       }
+       else
+       {
+               x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec;
+               x.tv_sec--;
+       }
+
+       return (x);
+}
+
+static int
+evCmpTime(struct timespec a, struct timespec b)
+{
+       long x = a.tv_sec - b.tv_sec;
+
+       if (x == 0L) x = a.tv_nsec - b.tv_nsec;
+       return (x < 0L ? (-1) : x > 0L ? (1) : (0));
+}
+
+#endif /* __APPLE__ */
+
+/* Public. */
+
+/* int
+ * res_isourserver(ina)
+ *     looks up "ina" in _res.ns_addr_list[]
+ * returns:
+ *     0  : not found
+ *     >0 : found
+ * author:
+ *     paul vixie, 29may94
+ */
+int
+res_ourserver_p(const res_state statp, const struct sockaddr *sa)
+{
+       const struct sockaddr_in *inp, *srv;
+       const struct sockaddr_in6 *in6p, *srv6;
+       int ns;
+
+       switch (sa->sa_family)
+       {
+               case AF_INET:
+                       inp = (const struct sockaddr_in *)sa;
+                       for (ns = 0;  ns < statp->nscount;  ns++)
+                       {
+                               srv = (struct sockaddr_in *)get_nsaddr(statp, ns);
+                               if (srv->sin_family == inp->sin_family &&
+                                       srv->sin_port == inp->sin_port &&
+                                       (srv->sin_addr.s_addr == INADDR_ANY ||
+                                        srv->sin_addr.s_addr == inp->sin_addr.s_addr))
+                                       return (1);
+                       }
+                       break;
+               case AF_INET6:
+                       if (EXT(statp).ext == NULL) break;
+                       in6p = (const struct sockaddr_in6 *)sa;
+                       for (ns = 0;  ns < statp->nscount;  ns++)
+                       {
+                               srv6 = (struct sockaddr_in6 *)get_nsaddr(statp, ns);
+                               if (srv6->sin6_family == in6p->sin6_family &&
+                                       srv6->sin6_port == in6p->sin6_port &&
+                                       (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
+                                        IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr)))
+                                       return (1);
+                       }
+                       break;
+               default:
+                       break;
+       }
+       return (0);
+}
+
+/* int
+ * res_nameinquery(name, type, class, buf, eom)
+ *     look for (name,type,class) in the query section of packet (buf,eom)
+ * requires:
+ *     buf + NS_HFIXEDSZ <= eom
+ * returns:
+ *     -1 : format error
+ *     0  : not found
+ *     >0 : found
+ * author:
+ *     paul vixie, 29may94
+ */
+int
+res_nameinquery(const char *name, int type, int class, const u_char *buf, const u_char *eom)
+{
+       const u_char *cp = buf + NS_HFIXEDSZ;
+       int qdcount = ntohs(((const HEADER*)buf)->qdcount);
+
+       while (qdcount-- > 0)
+       {
+               char tname[NS_MAXDNAME+1];
+               int n, ttype, tclass;
+
+               n = dn_expand(buf, eom, cp, tname, sizeof tname);
+               if (n < 0) return (-1);
+
+               cp += n;
+               if (cp + 2 * NS_INT16SZ > eom) return (-1);
+
+               ttype = ns_get16(cp); cp += NS_INT16SZ;
+               tclass = ns_get16(cp); cp += NS_INT16SZ;
+               if (ttype == type && tclass == class && ns_samename(tname, name) == 1) return (1);
+       }
+
+       return (0);
+}
+
+/* int
+ * res_queriesmatch(buf1, eom1, buf2, eom2)
+ *     is there a 1:1 mapping of (name,type,class)
+ *     in (buf1,eom1) and (buf2,eom2)?
+ * returns:
+ *     -1 : format error
+ *     0  : not a 1:1 mapping
+ *     >0 : is a 1:1 mapping
+ * author:
+ *     paul vixie, 29may94
+ */
+int
+res_queriesmatch(const u_char *buf1, const u_char *eom1, const u_char *buf2, const u_char *eom2)
+{
+       const u_char *cp = buf1 + NS_HFIXEDSZ;
+       int qdcount = ntohs(((const HEADER*)buf1)->qdcount);
+
+       if (buf1 + NS_HFIXEDSZ > eom1 || buf2 + NS_HFIXEDSZ > eom2)
+               return (-1);
+
+       /*
+        * Only header section present in replies to
+        * dynamic update packets.
+        */
+       if ((((const HEADER *)buf1)->opcode == ns_o_update) &&
+           (((const HEADER *)buf2)->opcode == ns_o_update))
+               return (1);
+
+       if (qdcount != ntohs(((const HEADER*)buf2)->qdcount)) return (0);
+
+       while (qdcount-- > 0)
+       {
+               char tname[NS_MAXDNAME+1];
+               int n, ttype, tclass;
+
+               n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
+               if (n < 0) return (-1);
+
+               cp += n;
+               if (cp + 2 * NS_INT16SZ > eom1) return (-1);
+
+               ttype = ns_get16(cp);   cp += NS_INT16SZ;
+               tclass = ns_get16(cp); cp += NS_INT16SZ;
+               if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) return (0);
+       }
+
+       return (1);
+}
+
+int
+dns_res_send(res_state statp, const u_char *buf, int buflen, u_char *ans, int *anssiz, struct sockaddr *from, int *fromlen)
+{
+       int gotsomewhere, terrno, try, v_circuit, resplen, ns;
+       char abuf[NI_MAXHOST];
+       char *notify_name;
+       int notify_token, status, send_status, reply_buf_size;
+       uint64_t exit_requested;
+
+       if (statp->nscount == 0)
+       {
+               errno = ESRCH;
+               return DNS_RES_STATUS_INVALID_RES_STATE;
+       }
+
+       reply_buf_size = *anssiz;
+       if (reply_buf_size < NS_HFIXEDSZ)
+       {
+               errno = EINVAL;
+               return DNS_RES_STATUS_INVALID_ARGUMENT;
+       }
+
+       DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), (stdout, ";; res_send()\n"), buf, buflen);
+
+       v_circuit = (statp->options & RES_USEVC) || (buflen > NS_PACKETSZ);
+       gotsomewhere = 0;
+       send_status = 0;
+       terrno = ETIMEDOUT;
+
+       /*
+        * If the ns_addr_list in the resolver context has changed, then
+        * invalidate our cached copy and the associated timing data.
+        */
+       if (EXT(statp).nscount != 0)
+       {
+               int needclose = 0;
+               struct sockaddr_storage peer;
+               ISC_SOCKLEN_T peerlen;
+
+               if (EXT(statp).nscount != statp->nscount)
+               {
+                       needclose++;
+               }
+               else
+               {
+                       for (ns = 0; ns < statp->nscount; ns++)
+                       {
+                               if ((statp->nsaddr_list[ns].sin_family) && (EXT(statp).ext != NULL) && (!sock_eq((struct sockaddr *)&statp->nsaddr_list[ns], (struct sockaddr *)&EXT(statp).ext->nsaddrs[ns])))
+                               {
+                                       needclose++;
+                                       break;
+                               }
+
+                               if (EXT(statp).nssocks[ns] == -1) continue;
+
+                               peerlen = sizeof(peer);
+                               if (getsockname(EXT(statp).nssocks[ns], (struct sockaddr *)&peer, &peerlen) < 0)
+                               {
+                                       needclose++;
+                                       break;
+                               }
+
+                               if (!sock_eq((struct sockaddr *)&peer, get_nsaddr(statp, ns)))
+                               {
+                                       needclose++;
+                                       break;
+                               }
+                       }
+               }
+
+               if (needclose)
+               {
+                       res_nclose(statp);
+                       EXT(statp).nscount = 0;
+               }
+       }
+
+       /*
+        * Maybe initialize our private copy of the ns_addr_list.
+        */
+       if (EXT(statp).nscount == 0)
+       {
+               for (ns = 0; ns < statp->nscount; ns++)
+               {
+                       EXT(statp).nstimes[ns] = RES_MAXTIME;
+                       EXT(statp).nssocks[ns] = -1;
+                       if (!statp->nsaddr_list[ns].sin_family) continue;
+                       if (EXT(statp).ext != NULL) EXT(statp).ext->nsaddrs[ns].sin = statp->nsaddr_list[ns];
+               }
+
+               EXT(statp).nscount = statp->nscount;
+       }
+
+       /*
+        * Some resolvers want to even out the load on their nameservers.
+        * Note that RES_BLAST overrides RES_ROTATE.
+        */
+       if (((statp->options & RES_ROTATE) != 0) && ((statp->options & RES_BLAST) == 0))
+       {
+               union res_sockaddr_union inu;
+               struct sockaddr_in ina;
+               int lastns = statp->nscount - 1;
+               int fd;
+               u_int16_t nstime;
+
+               if (EXT(statp).ext != NULL) inu = EXT(statp).ext->nsaddrs[0];
+               ina = statp->nsaddr_list[0];
+               fd = EXT(statp).nssocks[0];
+               nstime = EXT(statp).nstimes[0];
+
+               for (ns = 0; ns < lastns; ns++)
+               {
+                       if (EXT(statp).ext != NULL)
+                       {
+                               EXT(statp).ext->nsaddrs[ns] =EXT(statp).ext->nsaddrs[ns + 1];
+                       }
+
+                       statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
+                       EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1];
+                       EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1];
+               }
+
+               if (EXT(statp).ext != NULL) EXT(statp).ext->nsaddrs[lastns] = inu;
+               statp->nsaddr_list[lastns] = ina;
+               EXT(statp).nssocks[lastns] = fd;
+               EXT(statp).nstimes[lastns] = nstime;
+       }
+
+       /*
+        * Get notification token
+        * we use a self-notification token to allow a caller
+        * to signal the thread doing this DNS query to quit.
+        */
+       notify_name = NULL;
+       notify_token = -1;
+
+       asprintf(&notify_name, "self.thread.%lu", (unsigned long)pthread_self());
+       if (notify_name != NULL) 
+       {
+               status = notify_register_plain(notify_name, &notify_token);
+               free(notify_name);
+       }
+
+       /*
+        * Send request, RETRY times, or until successful.
+        */
+       for (try = 0; try < statp->retry; try++)
+       {
+           for (ns = 0; ns < statp->nscount; ns++)
+               {
+                       struct sockaddr *nsap;
+                       int nsaplen;
+                       nsap = get_nsaddr(statp, ns);
+                       nsaplen = get_salen(nsap);
+
+send_same_ns:
+
+                       if (statp->qhook)
+                       {
+                               int done = 0, loops = 0;
+
+                               do
+                               {
+                                       res_sendhookact act;
+
+                                       act = (*statp->qhook)(&nsap, &buf, &buflen, ans, reply_buf_size, &resplen);
+                                       switch (act)
+                                       {
+                                               case res_goahead:
+                                                       done = 1;
+                                                       break;
+                                               case res_nextns:
+                                                       res_nclose(statp);
+                                                       goto send_next_ns;
+                                               case res_done:
+                                                       if (notify_token != -1) notify_cancel(notify_token);
+                                                       return DNS_RES_STATUS_CANCELLED;
+                                               case res_modified:
+                                                       /* give the hook another try */
+                                                       if (++loops < MAX_HOOK_RETRIES) break;
+                                                       /*FALLTHROUGH*/
+                                               case res_error:
+                                                       /*FALLTHROUGH*/
+                                               default:
+                                                       if (notify_token != -1) notify_cancel(notify_token);
+                                                       return DNS_RES_STATUS_CANCELLED;
+                                       }
+                               } while (!done);
+                       }
+
+                       if (notify_token != -1)
+                       {
+                               exit_requested = 0;
+                               status = notify_get_state(notify_token, &exit_requested);
+                               if (exit_requested == ThreadStateExitRequested)
+                               {
+                                       res_nclose(statp);
+                                       notify_cancel(notify_token);
+                                       return DNS_RES_STATUS_CANCELLED;
+                               }
+                       }
+
+                       Dprint(((statp->options & RES_DEBUG) && getnameinfo(nsap, nsaplen, abuf, sizeof(abuf), NULL, 0, niflags) == 0), (stdout, ";; Querying server (# %d) address = %s\n", ns + 1, abuf));
+
+                       send_status = ns_r_noerror;
+
+                       if (v_circuit != 0)
+                       {
+                               /* Use VC; at most one attempt per server. */
+                               try = statp->retry;
+
+                               *anssiz = reply_buf_size;
+                               send_status = send_vc(statp, buf, buflen, ans, anssiz, &terrno, ns, from, fromlen, notify_token);
+                       }
+                       else
+                       {
+                               /* Use datagrams. */
+                               send_status = send_dg(statp, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit, &gotsomewhere, from, fromlen, notify_token);
+                               if (v_circuit != 0) goto send_same_ns;
+                       }
+
+                       if ((send_status == DNS_RES_STATUS_SYSTEM_ERROR) || (send_status == DNS_RES_STATUS_CANCELLED))
+                       {
+                               res_nclose(statp);
+                               if (notify_token != -1) notify_cancel(notify_token);
+                               return send_status;
+                       }
+
+                       if (send_status != ns_r_noerror) goto send_next_ns;
+
+                       Dprint((statp->options & RES_DEBUG) || ((statp->pfcode & RES_PRF_REPLY) && (statp->pfcode & RES_PRF_HEAD1)), (stdout, ";; got answer:\n"));
+                       DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, "%s", ""), ans, (*anssiz > reply_buf_size) ? reply_buf_size : *anssiz);
+
+                       /*
+                        * If we have temporarily opened a virtual circuit,
+                        * or if we haven't been asked to keep a socket open,
+                        * close the socket.
+                        */
+                       if (((v_circuit != 0) && (statp->options & RES_USEVC) == 0) || (statp->options & RES_STAYOPEN) == 0)  res_nclose(statp);
+
+                       if (statp->rhook)
+                       {
+                               int done = 0, loops = 0;
+
+                               do
+                               {
+                                       res_sendhookact act;
+
+                                       act = (*statp->rhook)(nsap, buf, buflen, ans, *anssiz, &resplen);
+                                       switch (act)
+                                       {
+                                               case res_goahead:
+                                               case res_done:
+                                                       done = 1;
+                                                       break;
+                                               case res_nextns:
+                                                       res_nclose(statp);
+                                                       goto send_next_ns;
+                                               case res_modified:
+                                                       /* give the hook another try */
+                                                       if (++loops < MAX_HOOK_RETRIES) break;
+                                                       /*FALLTHROUGH*/
+                                               case res_error:
+                                                       /*FALLTHROUGH*/
+                                               default:
+                                                       res_nclose(statp);
+                                                       if (notify_token != -1) notify_cancel(notify_token);
+                                                       return DNS_RES_STATUS_CANCELLED;
+                                       }
+                               } while (!done);
+
+                       }
+
+                       if (notify_token != -1) notify_cancel(notify_token);
+                       return ns_r_noerror;
+
+send_next_ns: ;
+               } /* foreach ns */
+       } /* foreach retry */
+
+       res_nclose(statp);
+       if (notify_token != -1) notify_cancel(notify_token);
+
+       if (v_circuit == 0)
+       {
+               /* used datagrams */
+               if (gotsomewhere != 0)
+               {
+                       errno = ECONNREFUSED;
+                       return DNS_RES_STATUS_CONNECTION_REFUSED;
+               }
+
+               errno = ETIMEDOUT;
+               return DNS_RES_STATUS_TIMEOUT;
+       }
+
+       /* used v_circuit */
+       errno = terrno;
+       return send_status;
+}
+
+int
+res_nsend_2(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz, struct sockaddr *from, int *fromlen)
+{
+       int len, status;
+
+       len = anssiz;
+       status = dns_res_send(statp, buf, buflen, ans, &len, from, fromlen);
+       if (status != ns_r_noerror) len = -1;
+       return len;
+}
+
+int
+res_nsend(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz)
+{
+       struct sockaddr_storage from;
+       int fromlen;
+
+       fromlen = sizeof(struct sockaddr_storage);
+
+       return res_nsend_2(statp, buf, buflen, ans, anssiz, (struct sockaddr *)&from, &fromlen);
+}
+
+/* Private */
+
+static int
+get_salen(const struct sockaddr *sa)
+{
+#ifdef HAVE_SA_LEN
+       /* There are people do not set sa_len.  Be forgiving to them. */
+       if (sa->sa_len) return (sa->sa_len);
+#endif
+
+       if (sa->sa_family == AF_INET) return (sizeof(struct sockaddr_in));
+       else if (sa->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6));
+       else return (0);        /* unknown, die on connect */
+}
+
+/*
+ * pick appropriate nsaddr_list for use.  see res_init() for initialization.
+ */
+struct sockaddr *
+get_nsaddr(res_state statp, size_t n)
+{
+       if ((!statp->nsaddr_list[n].sin_family) && (EXT(statp).ext != NULL))
+       {
+               /*
+                * - EXT(statp).ext->nsaddrs[n] holds an address that is larger
+                *   than struct sockaddr, and
+                * - user code did not update statp->nsaddr_list[n].
+                */
+               return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n];
+       }
+       else
+       {
+               /*
+                * - user code updated statp->nsaddr_list[n], or
+                * - statp->nsaddr_list[n] has the same content as
+                *   EXT(statp).ext->nsaddrs[n].
+                */
+               return (struct sockaddr *)(void *)&statp->nsaddr_list[n];
+       }
+}
+
+static int
+send_vc(res_state statp, const u_char *buf, int buflen, u_char *ans, int *anssiz, int *terrno, int ns, struct sockaddr *from, int *fromlen, int notify_token)
+{
+       const HEADER *hp = (const HEADER *) buf;
+       HEADER *anhp = (HEADER *) ans;
+       struct sockaddr *nsap;
+       int nsaplen;
+       int truncating, connreset, resplen, n;
+       struct iovec iov[2];
+       u_short len;
+       u_char *cp;
+       void *tmp;
+       int status;
+       uint64_t exit_requested;
+
+       nsap = get_nsaddr(statp, ns);
+       nsaplen = get_salen(nsap);
+
+       connreset = 0;
+vc_same_ns:
+
+       if (notify_token != -1)
+       {
+               exit_requested = 0;
+               status = notify_get_state(notify_token, &exit_requested);
+               if (exit_requested == ThreadStateExitRequested)
+               {
+                       *terrno = EINTR;
+                       return DNS_RES_STATUS_CANCELLED;
+               }
+       }
+
+       truncating = 0;
+
+       /* Are we still talking to whom we want to talk? */
+       if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0)
+       {
+               struct sockaddr_storage peer;
+               ISC_SOCKLEN_T size = sizeof peer;
+
+               if (getpeername(statp->_vcsock, (struct sockaddr *)&peer, &size) < 0 || !sock_eq((struct sockaddr *)&peer, nsap))
+               {
+                       res_nclose(statp);
+                       statp->_flags &= ~RES_F_VC;
+               }
+       }
+
+       if ((statp->_vcsock < 0) || ((statp->_flags & RES_F_VC) == 0))
+       {
+               if (statp->_vcsock >= 0) res_nclose(statp);
+
+               statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0);
+               if (statp->_vcsock > highestFD)
+               {
+                       res_nclose(statp);
+                       errno = ENOTSOCK;
+               }
+
+               if (statp->_vcsock < 0)
+               {
+                       *terrno = errno;
+                       Perror(statp, stderr, "socket(vc)", errno);
+                       return DNS_RES_STATUS_SYSTEM_ERROR;
+               }
+
+               errno = 0;
+               if (connect(statp->_vcsock, nsap, nsaplen) < 0)
+               {
+                       *terrno = errno;
+                       Aerror(statp, stderr, "connect(vc)", errno, nsap, nsaplen);
+                       res_nclose(statp);
+                       return DNS_RES_STATUS_CONNECTION_REFUSED;
+               }
+
+               statp->_flags |= RES_F_VC;
+       }
+
+       /*
+        * Send length & message
+        */
+       putshort((u_short)buflen, (u_char*)&len);
+       iov[0] = evConsIovec(&len, NS_INT16SZ);
+#ifdef __APPLE__
+       tmp = (char *)buf;
+#else
+       DE_CONST(buf, tmp);
+#endif
+       iov[1] = evConsIovec(tmp, buflen);
+       if (writev(statp->_vcsock, iov, 2) != (NS_INT16SZ + buflen))
+       {
+               *terrno = errno;
+               Perror(statp, stderr, "write failed", errno);
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_FAILED;
+       }
+
+       /*
+        * Receive length & response
+        */
+ read_len:
+
+       if (notify_token != -1)
+       {
+               exit_requested = 0;
+               status = notify_get_state(notify_token, &exit_requested);
+               if (exit_requested == ThreadStateExitRequested)
+               {
+                       *terrno = EINTR;
+                       return DNS_RES_STATUS_CANCELLED;
+               }
+       }
+       
+       cp = ans;
+       len = NS_INT16SZ;
+       while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0)
+       {
+               cp += n;
+               if ((len -= n) <= 0) break;
+       }
+
+       if (n <= 0)
+       {
+               *terrno = errno;
+               Perror(statp, stderr, "read failed", errno);
+               res_nclose(statp);
+
+               /*
+                * A long running process might get its TCP
+                * connection reset if the remote server was
+                * restarted.  Requery the server instead of
+                * trying a new one.  When there is only one
+                * server, this means that a query might work
+                * instead of failing.  We only allow one reset
+                * per query to prevent looping.
+                */
+               if (*terrno == ECONNRESET && !connreset)
+               {
+                       connreset = 1;
+                       res_nclose(statp);
+                       goto vc_same_ns;
+               }
+
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_FAILED;
+       }
+
+       resplen = ns_get16(ans);
+       if (resplen > *anssiz)
+       {
+               Dprint(statp->options & RES_DEBUG, (stdout, ";; response truncated\n"));
+               truncating = 1;
+               len = *anssiz;
+       }
+       else
+       {
+               len = resplen;
+       }
+
+       if (len < NS_HFIXEDSZ)
+       {
+               /*
+                * Undersized message.
+                */
+               Dprint(statp->options & RES_DEBUG, (stdout, ";; undersized: %d\n", len));
+               *terrno = EMSGSIZE;
+               res_nclose(statp);
+               *anssiz = 0;
+               return DNS_RES_STATUS_INVALID_REPLY;
+       }
+
+       cp = ans;
+       while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0)
+       {
+               cp += n;
+               len -= n;
+       }
+
+       if (n <= 0)
+       {
+               *terrno = errno;
+               Perror(statp, stderr, "read(vc)", errno);
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_FAILED;
+       }
+
+       if (truncating)
+       {
+               /*
+                * Flush rest of answer so connection stays in synch.
+                */
+               anhp->tc = 1;
+               len = resplen - *anssiz;
+               while (len != 0)
+               {
+                       char junk[NS_PACKETSZ];
+
+                       n = read(statp->_vcsock, junk, (len > sizeof junk) ? sizeof junk : len);
+                       if (n > 0) len -= n;
+                       else break;
+               }
+       }
+
+       /*
+        * If the calling applicating has bailed out of
+        * a previous call and failed to arrange to have
+        * the circuit closed or the server has got
+        * itself confused, then drop the packet and
+        * wait for the correct one.
+        */
+       if (hp->id != anhp->id)
+       {
+               DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer (unexpected):\n"), ans, (resplen > *anssiz) ? *anssiz : resplen);
+               goto read_len;
+       }
+
+       /*
+        * All is well, or the error is fatal.  Signal that the
+        * next nameserver ought not be tried.
+        */
+
+       *fromlen = sizeof(nsap);
+       memcpy(from, &nsap, *fromlen);
+       *anssiz = resplen;
+       return ns_r_noerror;
+}
+
+static ssize_t
+internal_recvfrom(int s, void *buf, size_t len, struct sockaddr *from, int *fromlen, int *iface)
+{
+       struct sockaddr_dl *sdl;
+       struct iovec databuffers = { buf, len };
+       struct msghdr msg;
+       ssize_t n;
+       struct cmsghdr *cmp;
+       char ancillary[1024], ifname[IF_NAMESIZE];
+       struct in6_pktinfo *ip6_info;
+       struct sockaddr_in *s4;
+       struct sockaddr_in6 *s6;
+
+       memset(&msg, 0, sizeof(struct msghdr));
+       msg.msg_name = (caddr_t)from;
+       msg.msg_namelen = *fromlen;
+       msg.msg_iov = &databuffers;
+       msg.msg_iovlen = 1;
+       msg.msg_control = (caddr_t)&ancillary;
+       msg.msg_controllen = sizeof(ancillary);
+
+       /* Receive the data */
+       n = recvmsg(s, &msg, 0);
+       if ((n < 0) || (msg.msg_controllen < sizeof(struct cmsghdr)) || (msg.msg_flags & MSG_CTRUNC))
+       {
+               return n;
+       }
+
+       *fromlen = msg.msg_namelen;
+
+       s4 = (struct sockaddr_in *)from;
+       s6 = (struct sockaddr_in6 *)from;
+
+       for (cmp = CMSG_FIRSTHDR(&msg); cmp; cmp = CMSG_NXTHDR(&msg, cmp))
+       {
+               if ((cmp->cmsg_level == IPPROTO_IP) && (cmp->cmsg_type == IP_RECVIF))
+               {
+                       sdl = (struct sockaddr_dl *)CMSG_DATA(cmp);
+                       if (sdl->sdl_nlen < IF_NAMESIZE)
+                       {
+                               memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
+                               ifname[sdl->sdl_nlen] = 0;
+                               *iface = if_nametoindex(ifname);
+                       }
+               }
+               else if ((cmp->cmsg_level == IPPROTO_IPV6) && (cmp->cmsg_type == IPV6_PKTINFO))
+               {
+                       ip6_info = (struct in6_pktinfo *)CMSG_DATA(cmp);
+                       *iface = ip6_info->ipi6_ifindex;
+               }
+       }
+
+       return n;
+}
+
+static int
+send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans, int *anssiz, int *terrno, int ns, int *v_circuit, int *gotsomewhere, struct sockaddr *from, int *fromlen, int notify_token)
+{
+       const HEADER *hp = (const HEADER *) buf;
+       HEADER *anhp = (HEADER *) ans;
+       const struct sockaddr *nsap;
+       int nsaplen;
+       struct timespec now, timeout, finish;
+       fd_set dsmask;
+       int iface, rif, status;
+       uint64_t exit_requested;
+#ifndef __APPLE__
+       struct sockaddr_storage from;
+       ISC_SOCKLEN_T fromlen;
+#endif
+       int resplen, seconds, ntry, n, s;
+#ifdef MULTICAST
+       int multicast;
+#endif
+
+       nsap = get_nsaddr(statp, ns);
+       nsaplen = get_salen(nsap);
+       if (EXT(statp).nssocks[ns] == -1)
+       {
+               EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0);
+               if (EXT(statp).nssocks[ns] > highestFD)
+               {
+                       res_nclose(statp);
+                       errno = ENOTSOCK;
+               }
+
+               if (EXT(statp).nssocks[ns] < 0)
+               {
+                       *terrno = errno;
+                       Perror(statp, stderr, "socket(dg)", errno);
+                       return DNS_RES_STATUS_SYSTEM_ERROR;
+               }
+
+#ifndef CANNOT_CONNECT_DGRAM
+               /*
+                * On a 4.3BSD+ machine (client and server,
+                * actually), sending to a nameserver datagram
+                * port with no nameserver will cause an
+                * ICMP port unreachable message to be returned.
+                * If our datagram socket is "connected" to the
+                * server, we get an ECONNREFUSED error on the next
+                * socket operation, and select returns if the
+                * error message is received.  We can thus detect
+                * the absence of a nameserver without timing out.
+                */
+               if (connect(EXT(statp).nssocks[ns], nsap, nsaplen) < 0)
+               {
+                       Aerror(statp, stderr, "connect(dg)", errno, nsap, nsaplen);
+                       res_nclose(statp);
+                       return DNS_RES_STATUS_CONNECTION_REFUSED;
+               }
+
+#endif /* !CANNOT_CONNECT_DGRAM */
+               Dprint(statp->options & RES_DEBUG, (stdout, ";; new DG socket\n"))
+       }
+
+       s = EXT(statp).nssocks[ns];
+       rif = 1;
+       setsockopt(s, IPPROTO_IP, IP_RECVIF, &rif, sizeof(int));
+       setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &rif, sizeof(int));
+
+#ifdef MULTICAST
+       multicast = 0;
+
+       if ((nsap->sa_family == AF_INET) && (IN_MULTICAST(ntohl(((struct sockaddr_in *)nsap)->sin_addr.s_addr)))) multicast = AF_INET;
+       else if ((nsap->sa_family == AF_INET6) && (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)nsap)->sin6_addr))) multicast = AF_INET6;
+
+       if (multicast != 0)
+       {
+               struct ifaddrs *ifa, *p;
+               struct sockaddr_in *sin4;
+               struct sockaddr_in6 *sin6;
+               int i, ifnum;
+
+               if (getifaddrs(&ifa) < 0)
+               {
+                               Aerror(statp, stderr, "getifaddrs", errno, nsap, nsaplen);
+                               res_nclose(statp);
+                               return DNS_RES_STATUS_SYSTEM_ERROR;
+               }
+
+               for (p = ifa; p != NULL; p = p->ifa_next)
+               {
+                       if (p->ifa_addr == NULL) continue;
+                       if ((p->ifa_flags & IFF_UP) == 0) continue;
+                       if (p->ifa_addr->sa_family != multicast) continue;
+                       if ((p->ifa_flags & IFF_MULTICAST) == 0) continue;
+                       if ((p->ifa_flags & IFF_POINTOPOINT) != 0)
+                       {
+                               if ((multicast == AF_INET) && (ntohl(((struct sockaddr_in *)nsap)->sin_addr.s_addr) <= INADDR_MAX_LOCAL_GROUP)) continue;
+                       }
+
+                       sin4 = (struct sockaddr_in *)p->ifa_addr;
+                       sin6 = (struct sockaddr_in6 *)p->ifa_addr;
+                       i = -1;
+                       if (multicast == AF_INET) i = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &sin4->sin_addr, sizeof(sin4->sin_addr));
+                       else if (multicast == AF_INET6)
+                       {
+                               ifnum = if_nametoindex(p->ifa_name);
+                               ((struct sockaddr_in6 *)nsap)->sin6_scope_id = ifnum;
+                               i = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifnum, sizeof(ifnum));
+                       }
+
+                       if (i < 0)
+                       {
+                               Aerror(statp, stderr, "setsockopt", errno, nsap, nsaplen);
+                               if (multicast == AF_INET6) ((struct sockaddr_in6 *)nsap)->sin6_scope_id = 0;
+
+                               continue;
+                       }
+
+                       if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen)
+                       {
+                               Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
+                               if (multicast == AF_INET6) ((struct sockaddr_in6 *)nsap)->sin6_scope_id = 0;
+                               continue;
+                       }
+
+                       if (multicast == AF_INET6) ((struct sockaddr_in6 *)nsap)->sin6_scope_id = 0;
+               }
+
+
+               freeifaddrs(ifa);   
+       }
+       else
+       {
+#endif /* MULTICAST */
+
+#ifndef CANNOT_CONNECT_DGRAM
+       if (send(s, (const char*)buf, buflen, 0) != buflen)
+       {
+               Perror(statp, stderr, "send", errno);
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_FAILED;
+       }
+
+#else /* !CANNOT_CONNECT_DGRAM */
+       if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen)
+       {
+               Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_FAILED;
+       }
+#endif /* !CANNOT_CONNECT_DGRAM */
+
+#ifdef MULTICAST
+       }
+#endif /* MULTICAST */
+
+       /*
+        * Wait for reply.
+        */
+#ifdef __APPLE__
+       ntry = statp->nscount * statp->retry;
+       seconds = statp->retrans / ntry;
+       if (seconds <= 0) seconds = 1;
+       timeout.tv_sec = seconds;
+       timeout.tv_nsec = ((statp->retrans - (seconds * ntry)) * 1000) / ntry;
+       timeout.tv_nsec *= 1000000;
+       now = evNowTime();
+       finish = evAddTime(now, timeout);
+#else
+       seconds = (statp->retrans << ns);
+       if (ns > 0) seconds /= statp->nscount;
+       if (seconds <= 0) seconds = 1;
+       now = evNowTime();
+       timeout = evConsTime(seconds, 0);
+       finish = evAddTime(now, timeout);
+#endif /* __APPLE__ */
+       goto nonow;
+ wait:
+
+       now = evNowTime();
+
+ nonow:
+       
+       if (notify_token != -1)
+       {
+               exit_requested = 0;
+               status = notify_get_state(notify_token, &exit_requested);
+               if (exit_requested == ThreadStateExitRequested) return DNS_RES_STATUS_CANCELLED;
+       }
+
+       FD_ZERO(&dsmask);
+       FD_SET(s, &dsmask);
+       if (evCmpTime(finish, now) > 0) timeout = evSubTime(finish, now);
+       else timeout = evConsTime(0, 0);
+
+       n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
+       if (n == 0)
+       {
+               Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
+               *gotsomewhere = 1;
+               return DNS_RES_STATUS_TIMEOUT;
+       }
+
+       if (n < 0)
+       {
+               if (errno == EINTR) goto wait;
+               Perror(statp, stderr, "select", errno);
+               res_nclose(statp);
+               return DNS_RES_STATUS_SYSTEM_ERROR;
+       }
+
+       errno = 0;
+       iface = 0;
+       resplen = internal_recvfrom(s, (char *)ans, *anssiz, from, fromlen, &iface);
+       if (resplen <= 0)
+       {
+               Perror(statp, stderr, "recvfrom", errno);
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_FAILED;
+       }
+
+       if (nsap->sa_family == AF_INET) memcpy(((struct sockaddr_in *)from)->sin_zero, &iface, 4);
+       else if (nsap->sa_family == AF_INET6) ((struct sockaddr_in6 *)from)->sin6_scope_id = iface;
+
+       *gotsomewhere = 1;
+       if (resplen < NS_HFIXEDSZ)
+       {
+               /*
+                * Undersized message.
+                */
+               Dprint(statp->options & RES_DEBUG, (stdout, ";; undersized: %d\n", resplen));
+               *terrno = EMSGSIZE;
+               res_nclose(statp);
+               return DNS_RES_STATUS_INVALID_REPLY;
+       }
+
+       if (hp->id != anhp->id)
+       {
+               /*
+                * response from old query, ignore it.
+                * XXX - potential security hazard could
+                *       be detected here.
+                */
+               DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer:\n"), ans, (resplen > *anssiz) ? *anssiz : resplen);
+               goto wait;
+       }
+
+#ifdef MULTICAST
+       if (multicast == 0)
+       {
+#endif /* MULTICAST */
+
+       if (!(statp->options & RES_INSECURE1) && !res_ourserver_p(statp, from))
+       {
+               /*
+                * response from wrong server? ignore it.
+                * XXX - potential security hazard could
+                *       be detected here.
+                */
+               DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; not our server:\n"), ans, (resplen > *anssiz) ? *anssiz : resplen);
+               goto wait;
+       }
+
+#ifdef MULTICAST
+       }
+#endif /* MULTICAST */
+
+#ifdef RES_USE_EDNS0
+       if (anhp->rcode == ns_r_formerr && (statp->options & RES_USE_EDNS0) != 0)
+       {
+               /*
+                * Do not retry if the server do not understand EDNS0.
+                * The case has to be captured here, as FORMERR packet do not
+                * carry query section, hence res_queriesmatch() returns 0.
+                */
+               DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query with EDNS0:\n"), ans, (resplen > *anssiz) ? *anssiz : resplen);
+               /* record the error */
+               statp->_flags |= RES_F_EDNS0ERR;
+               res_nclose(statp);
+               return DNS_RES_STATUS_CONNECTION_REFUSED;
+       }
+#endif
+
+       if (!(statp->options & RES_INSECURE2) && !res_queriesmatch(buf, buf + buflen, ans, ans + *anssiz))
+       {
+               /*
+                * response contains wrong query? ignore it.
+                * XXX - potential security hazard could
+                *       be detected here.
+                */
+               DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; wrong query name:\n"), ans, (resplen > *anssiz) ? *anssiz : resplen);
+               res_nclose(statp);
+               return DNS_RES_STATUS_INVALID_REPLY;
+       }
+
+       if (anhp->rcode == ns_r_servfail || anhp->rcode == ns_r_notimpl || anhp->rcode == ns_r_refused)
+       {
+               DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query:\n"), ans, (resplen > *anssiz) ? *anssiz : resplen);
+               res_nclose(statp);
+               /* don't retry if called from dig */
+               if (!statp->pfcode) return anhp->rcode;
+       }
+
+       if (!(statp->options & RES_IGNTC) && anhp->tc)
+       {
+               /*
+                * To get the rest of answer,
+                * use TCP with same server.
+                */
+               Dprint(statp->options & RES_DEBUG, (stdout, ";; truncated answer\n"));
+               *v_circuit = 1;
+               res_nclose(statp);
+               return ns_r_noerror;
+       }
+
+       /*
+        * All is well, or the error is fatal.  Signal that the
+        * next nameserver ought not be tried.
+        */
+       *anssiz = resplen;
+       return ns_r_noerror;
+}
+
+static void
+Aerror(const res_state statp, FILE *file, const char *string, int error, const struct sockaddr *address, int alen)
+{
+       int save = errno;
+       char hbuf[NI_MAXHOST];
+       char sbuf[NI_MAXSERV];
+
+       alen = alen;
+
+       if ((statp->options & RES_DEBUG) != 0)
+       {
+               if (getnameinfo(address, alen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), niflags))
+               {
+                       strncpy(hbuf, "?", sizeof(hbuf) - 1);
+                       hbuf[sizeof(hbuf) - 1] = '\0';
+                       strncpy(sbuf, "?", sizeof(sbuf) - 1);
+                       sbuf[sizeof(sbuf) - 1] = '\0';
+               }
+
+               fprintf(file, "res_send: %s ([%s].%s): %s\n", string, hbuf, sbuf, strerror(error));
+       }
+
+       errno = save;
+}
+
+static void
+Perror(const res_state statp, FILE *file, const char *string, int error)
+{
+       int save = errno;
+
+       if ((statp->options & RES_DEBUG) != 0) fprintf(file, "res_send: %s: %s\n", string, strerror(error));
+       errno = save;
+}
+
+static int
+sock_eq(struct sockaddr *a, struct sockaddr *b)
+{
+       struct sockaddr_in *a4, *b4;
+       struct sockaddr_in6 *a6, *b6;
+
+       if (a->sa_family != b->sa_family) return 0;
+       
+       switch (a->sa_family)
+       {
+               case AF_INET:
+                       a4 = (struct sockaddr_in *)a;
+                       b4 = (struct sockaddr_in *)b;
+                       return a4->sin_port == b4->sin_port &&  a4->sin_addr.s_addr == b4->sin_addr.s_addr;
+               case AF_INET6:
+                       a6 = (struct sockaddr_in6 *)a;
+                       b6 = (struct sockaddr_in6 *)b;
+                       return a6->sin6_port == b6->sin6_port &&
+#ifdef HAVE_SIN6_SCOPE_ID
+                               a6->sin6_scope_id == b6->sin6_scope_id &&
+#endif
+                               IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr);
+               default:
+                       return 0;
+       }
+}
+
+#ifdef NEED_PSELECT
+/* XXX needs to move to the porting library. */
+static int
+pselect(int nfds, void *rfds, void *wfds, void *efds, struct timespec *tsp, const sigset_t *sigmask)
+{
+       struct timeval tv, *tvp = NULL;
+       sigset_t sigs;
+       int n;
+
+       if (tsp)
+       {
+               tvp = &tv;
+               tv = evTimeVal(*tsp);
+       }
+
+       if (sigmask) sigprocmask(SIG_SETMASK, sigmask, &sigs);
+       n = select(nfds, rfds, wfds, efds, tvp);
+       if (sigmask) sigprocmask(SIG_SETMASK, &sigs, NULL);
+       if (tsp) *tsp = evTimeSpec(tv);
+       return n;
+}
+#endif
diff --git a/res_sendsigned.c b/res_sendsigned.c
new file mode 100644 (file)
index 0000000..a7a9c3a
--- /dev/null
@@ -0,0 +1,142 @@
+#ifndef __APPLE__
+#include "port_before.h"
+#include "fd_setsize.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef __APPLE__
+#include <isc/dst.h>
+#include "port_after.h"
+#else
+#include "dst.h"
+#endif
+
+#include "res_private.h"
+
+// #define DEBUG
+#include "res_debug.h"
+
+
+/* res_nsendsigned */
+int
+res_nsendsigned(res_state statp, const u_char *msg, int msglen,
+               ns_tsig_key *key, u_char *answer, int anslen)
+{
+       res_state nstatp;
+       DST_KEY *dstkey;
+       int usingTCP = 0;
+       u_char *newmsg;
+       int newmsglen, bufsize, siglen;
+       u_char sig[64];
+       HEADER *hp;
+       time_t tsig_time;
+       int ret;
+
+       dst_init();
+
+       nstatp = (res_state) malloc(sizeof(*statp));
+       if (nstatp == NULL) {
+               errno = ENOMEM;
+               return (-1);
+       }
+       memcpy(nstatp, statp, sizeof(*statp));
+       nstatp->_pad = 9;
+
+       bufsize = msglen + 1024;
+       newmsg = (u_char *) malloc(bufsize);
+       if (newmsg == NULL) {
+               errno = ENOMEM;
+               return (-1);
+       }
+       memcpy(newmsg, msg, msglen);
+       newmsglen = msglen;
+
+       if (ns_samename(key->alg, NS_TSIG_ALG_HMAC_MD5) != 1)
+               dstkey = NULL;
+       else
+               dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5,
+                                          NS_KEY_TYPE_AUTH_ONLY,
+                                          NS_KEY_PROT_ANY,
+                                          key->data, key->len);
+       if (dstkey == NULL) {
+               errno = EINVAL;
+               free(nstatp);
+               free(newmsg);
+               return (-1);
+       }
+
+       nstatp->nscount = 1;
+       siglen = sizeof(sig);
+       ret = ns_sign(newmsg, &newmsglen, bufsize, ns_r_noerror, dstkey, NULL, 0,
+                     sig, &siglen, 0);
+       if (ret < 0) {
+               free (nstatp);
+               free (newmsg);
+               dst_free_key(dstkey);
+               if (ret == NS_TSIG_ERROR_NO_SPACE)
+                       errno  = EMSGSIZE;
+               else if (ret == -1)
+                       errno  = EINVAL;
+               return (ret);
+       }
+
+       if (newmsglen > NS_PACKETSZ || (nstatp->options & RES_IGNTC))
+               usingTCP = 1;
+       if (usingTCP == 0)
+               nstatp->options |= RES_IGNTC;
+       else
+               nstatp->options |= RES_USEVC;
+
+retry:
+
+       ret = res_nsend(nstatp, newmsg, newmsglen, answer, anslen);
+       if (ret < 0) {
+               free (nstatp);
+               free (newmsg);
+               dst_free_key(dstkey);
+               return (ret);
+       }
+
+       anslen = ret;
+       ret = ns_verify(answer, &anslen, dstkey, sig, siglen,
+                       NULL, NULL, &tsig_time, nstatp->options & RES_KEEPTSIG);
+       if (ret != 0) {
+               Dprint(nstatp->pfcode & RES_PRF_REPLY,
+                      (stdout, ";; TSIG invalid (%s)\n", p_rcode(ret)));
+               free (nstatp);
+               free (newmsg);
+               dst_free_key(dstkey);
+               if (ret == -1)
+                       errno = EINVAL;
+               else
+                       errno = ENOTTY;
+               return (-1);
+       }
+       Dprint(nstatp->pfcode & RES_PRF_REPLY, (stdout, ";; TSIG ok\n"));
+
+       hp = (HEADER *) answer;
+       if (hp->tc && usingTCP == 0) {
+               nstatp->options &= ~RES_IGNTC;
+               usingTCP = 1;
+               goto retry;
+       }
+
+       free (nstatp);
+       free (newmsg);
+       dst_free_key(dstkey);
+       return (anslen);
+}
diff --git a/res_update.c b/res_update.c
new file mode 100644 (file)
index 0000000..dca7f30
--- /dev/null
@@ -0,0 +1,211 @@
+#ifndef __APPLE__
+#if !defined(lint) && !defined(SABER)
+static const char rcsid[] = "$Id: res_update.c,v 1.1 2006/03/01 19:01:39 majka Exp $";
+#endif /* not lint */
+#endif
+
+/*
+ * 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.
+ */
+
+/*
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * <viraj_bais@ccm.fm.intel.com>
+ */
+
+#ifndef __APPLE__
+#include "port_before.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <res_update.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <resolv.h>
+
+#ifndef __APPLE__
+#include <isc/list.h>
+#include "port_after.h"
+#endif
+#include "res_private.h"
+
+/*
+ * Separate a linked list of records into groups so that all records
+ * in a group will belong to a single zone on the nameserver.
+ * Create a dynamic update packet for each zone and send it to the
+ * nameservers for that zone, and await answer.
+ * Abort if error occurs in updating any zone.
+ * Return the number of zones updated on success, < 0 on error.
+ *
+ * On error, caller must deal with the unsynchronized zones
+ * eg. an A record might have been successfully added to the forward
+ * zone but the corresponding PTR record would be missing if error
+ * was encountered while updating the reverse zone.
+ */
+
+struct zonegrp {
+       char                    z_origin[NS_MAXDNAME];
+       ns_class                z_class;
+       union res_sockaddr_union z_nsaddrs[MAXNS];
+       int                     z_nscount;
+       int                     z_flags;
+       LIST(ns_updrec)         z_rrlist;
+       LINK(struct zonegrp)    z_link;
+};
+
+#define ZG_F_ZONESECTADDED     0x0001
+
+/* Forward. */
+
+static void    res_dprintf(const char *, ...);
+
+/* Macros. */
+
+#define DPRINTF(x) do {\
+               int save_errno = errno; \
+               if ((statp->options & RES_DEBUG) != 0) res_dprintf x; \
+               errno = save_errno; \
+       } while (0)
+
+/* Public. */
+
+int
+res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) {
+       ns_updrec *rrecp;
+       u_char answer[NS_PACKETSZ], packet[2*NS_PACKETSZ];
+       struct zonegrp *zptr, tgrp;
+       LIST(struct zonegrp) zgrps;
+       int nzones = 0, nscount = 0, n;
+       union res_sockaddr_union nsaddrs[MAXNS];
+
+       /* Thread all of the updates onto a list of groups. */
+       INIT_LIST(zgrps);
+       memset(&tgrp, 0, sizeof (tgrp));
+       for (rrecp = rrecp_in; rrecp;
+            rrecp = LINKED(rrecp, r_link) ? NEXT(rrecp, r_link) : NULL) {
+               int nscnt;
+               /* Find the origin for it if there is one. */
+               tgrp.z_class = rrecp->r_class;
+               nscnt = res_findzonecut2(statp, rrecp->r_dname, tgrp.z_class,
+                                        RES_EXHAUSTIVE, tgrp.z_origin,
+                                        sizeof tgrp.z_origin, 
+                                        tgrp.z_nsaddrs, MAXNS);
+               if (nscnt <= 0) {
+                       DPRINTF(("res_findzonecut failed (%d)", nscnt));
+                       goto done;
+               }
+               tgrp.z_nscount = nscnt;
+               /* Find the group for it if there is one. */
+               for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link))
+                       if (ns_samename(tgrp.z_origin, zptr->z_origin) == 1 &&
+                           tgrp.z_class == zptr->z_class)
+                               break;
+               /* Make a group for it if there isn't one. */
+               if (zptr == NULL) {
+                       zptr = malloc(sizeof *zptr);
+                       if (zptr == NULL) {
+                               DPRINTF(("malloc failed"));
+                               goto done;
+                       }
+                       *zptr = tgrp;
+                       zptr->z_flags = 0;
+                       INIT_LINK(zptr, z_link);
+                       INIT_LIST(zptr->z_rrlist);
+                       APPEND(zgrps, zptr, z_link);
+               }
+               /* Thread this rrecp onto the right group. */
+               APPEND(zptr->z_rrlist, rrecp, r_glink);
+       }
+
+       for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) {
+               /* Construct zone section and prepend it. */
+               rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin,
+                                    zptr->z_class, ns_t_soa, 0);
+               if (rrecp == NULL) {
+                       DPRINTF(("res_mkupdrec failed"));
+                       goto done;
+               }
+               PREPEND(zptr->z_rrlist, rrecp, r_glink);
+               zptr->z_flags |= ZG_F_ZONESECTADDED;
+
+               /* Marshall the update message. */
+               n = res_nmkupdate(statp, HEAD(zptr->z_rrlist),
+                                 packet, sizeof packet);
+               DPRINTF(("res_mkupdate -> %d", n));
+               if (n < 0)
+                       goto done;
+
+               /* Temporarily replace the resolver's nameserver set. */
+               nscount = res_getservers(statp, nsaddrs, MAXNS);
+               res_setservers(statp, zptr->z_nsaddrs, zptr->z_nscount);
+
+               /* Send the update and remember the result. */
+               if (key != NULL)
+                       n = res_nsendsigned(statp, packet, n, key,
+                                           answer, sizeof answer);
+               else
+                       n = res_nsend(statp, packet, n, answer, sizeof answer);
+               if (n < 0) {
+                       DPRINTF(("res_nsend: send error, n=%d (%s)\n",
+                                n, strerror(errno)));
+                       goto done;
+               }
+               if (((HEADER *)answer)->rcode == ns_r_noerror)
+                       nzones++;
+
+               /* Restore resolver's nameserver set. */
+               res_setservers(statp, nsaddrs, nscount);
+               nscount = 0;
+       }
+ done:
+       while (!EMPTY(zgrps)) {
+               zptr = HEAD(zgrps);
+               if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0)
+                       res_freeupdrec(HEAD(zptr->z_rrlist));
+               UNLINK(zgrps, zptr, z_link);
+               free(zptr);
+       }
+       if (nscount != 0)
+               res_setservers(statp, nsaddrs, nscount);
+
+       return (nzones);
+}
+
+/* Private. */
+
+static void
+res_dprintf(const char *fmt, ...) {
+       va_list ap;
+
+       va_start(ap, fmt);
+       fputs(";; res_nupdate: ", stderr);
+       vfprintf(stderr, fmt, ap);
+       fputc('\n', stderr);
+       va_end(ap);
+}
diff --git a/res_update.h b/res_update.h
new file mode 100644 (file)
index 0000000..1dbaed0
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * 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.
+ */
+
+/*
+ *     $Id: res_update.h,v 1.1 2006/03/01 19:01:39 majka Exp $
+ */
+
+#ifndef _RES_UPDATE_H_
+#define _RES_UPDATE_H_
+
+#include <sys/types.h>
+#include <arpa/nameser.h>
+#ifndef __APPLE__
+#include <isc/list.h>
+#else
+#define LIST(type) struct { type *head, *tail; }
+#define LINK(type) struct { type *prev, *next; }
+#define INIT_LIST(list) do { (list).head = NULL; (list).tail = NULL; } while (0)
+#define HEAD(list) ((list).head)
+#define TAIL(list) ((list).tail)
+#define EMPTY(list) ((list).head == NULL)
+#define PREV(elt, link) ((elt)->link.prev)
+#define NEXT(elt, link) ((elt)->link.next)
+#define INIT_LINK_TYPE(elt, link, type) \
+       do { \
+               (elt)->link.prev = (type *)(-1); \
+               (elt)->link.next = (type *)(-1); \
+       } while (0)
+#define INIT_LINK(elt, link) INIT_LINK_TYPE(elt, link, void)
+#define APPEND(list, elt, link) \
+       do { \
+               if ((list).tail != NULL) \
+                       (list).tail->link.next = (elt); \
+               else \
+                       (list).head = (elt); \
+               (elt)->link.prev = (list).tail; \
+               (elt)->link.next = NULL; \
+               (list).tail = (elt); \
+       } while (0)
+#define PREPEND(list, elt, link) \
+       do { \
+               if ((list).head != NULL) \
+                       (list).head->link.prev = (elt); \
+               else \
+                       (list).tail = (elt); \
+               (elt)->link.prev = NULL; \
+               (elt)->link.next = (list).head; \
+               (list).head = (elt); \
+       } while (0)
+#define UNLINK_TYPE(list, elt, link, type) \
+       do { \
+               if ((elt)->link.next != NULL) \
+                       (elt)->link.next->link.prev = (elt)->link.prev; \
+               else \
+                       (list).tail = (elt)->link.prev; \
+               if ((elt)->link.prev != NULL) \
+                       (elt)->link.prev->link.next = (elt)->link.next; \
+               else \
+                       (list).head = (elt)->link.next; \
+               INIT_LINK_TYPE(elt, link, type); \
+       } while (0)
+#define UNLINK(list, elt, link) \
+       UNLINK_TYPE(list, elt, link, void)
+#define LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1))
+#endif
+#include <resolv.h>
+
+/*
+ * This RR-like structure is particular to UPDATE.
+ */
+struct ns_updrec {
+       LINK(struct ns_updrec) r_link, r_glink;
+       ns_sect         r_section;      /* ZONE/PREREQUISITE/UPDATE */
+       char *          r_dname;        /* owner of the RR */
+       ns_class        r_class;        /* class number */
+       ns_type         r_type;         /* type number */
+       u_int32_t       r_ttl;          /* time to live */
+       u_char *        r_data;         /* rdata fields as text string */
+       u_int           r_size;         /* size of r_data field */
+       int             r_opcode;       /* type of operation */
+       /* following fields for private use by the resolver/server routines */
+       struct databuf *r_dp;           /* databuf to process */
+       struct databuf *r_deldp;        /* databuf's deleted/overwritten */
+       u_int           r_zone;         /* zone number on server */
+};
+typedef struct ns_updrec ns_updrec;
+typedef        LIST(ns_updrec) ns_updque;
+
+#define res_mkupdate           res_9_mkupdate
+#define res_update             res_9_update
+#define res_mkupdrec           res_9_mkupdrec
+#define res_freeupdrec res_9_freeupdrec
+#define res_nmkupdate  res_9_nmkupdate
+#define res_nupdate            res_9_nupdate
+
+int            res_mkupdate __P((ns_updrec *, u_char *, int));
+int            res_update __P((ns_updrec *));
+ns_updrec *    res_mkupdrec __P((int, const char *, u_int, u_int, u_long));
+void           res_freeupdrec __P((ns_updrec *));
+int            res_nmkupdate __P((res_state, ns_updrec *, u_char *, int));
+int            res_nupdate __P((res_state, ns_updrec *, ns_tsig_key *));
+
+#endif /*_RES_UPDATE_H_*/
diff --git a/resolv.h b/resolv.h
new file mode 100644 (file)
index 0000000..2a36c22
--- /dev/null
+++ b/resolv.h
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 1983, 1987, 1989
+ *    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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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) 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.
+ */
+
+/*
+ *     @(#)resolv.h    8.1 (Berkeley) 6/2/93
+ *     $Id: resolv.h,v 1.1 2006/03/01 19:01:39 majka Exp $
+ */
+
+/*
+ * Important note regarding the BIND-9 API on Mac OS
+ * -------------------------------------------------
+ * 
+ * Mac OS supports a DNS query routing API (see <dns.h>) which is used by
+ * most system services to access DNS.  The BIND-9 APIs described here are
+ * a lower-level that does not do query routing or search amongst multiple
+ * resolver clients.  The results of DNS queries from this API may differ
+ * significantly from the results of queries sent to the <dns.h> API.  We
+ * strongly encourage developers to use higher-level APIs where possible.
+ */
+
+#ifndef _RESOLV_9_H_
+#define        _RESOLV_9_H_
+
+#ifdef BIND_8_COMPAT
+#include <resolv8_compat.h>
+#else
+
+#include <sys/param.h>
+#if (!defined(BSD)) || (BSD < 199306)
+# include <sys/bitypes.h>
+#else
+# include <sys/types.h>
+#endif
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <arpa/nameser.h>
+
+
+/*
+ * Revision information.  This is the release date in YYYYMMDD format.
+ * It can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__RES > 19931104)".  Do not
+ * compare for equality; rather, use it to determine whether your resolver
+ * is new enough to contain a certain feature.
+ */
+
+#define        __RES   19991006
+
+#define __h_errno_set _res_9_h_errno_set
+#define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x)
+
+#define __res_state __res_9_state
+struct __res_state; /* forward */
+
+__BEGIN_DECLS
+void __h_errno_set(struct __res_state *res, int err);
+__END_DECLS
+
+/*
+ * Resolver configuration file.
+ * Normally not present, but may contain the address of the
+ * inital name server(s) to query and the domain search list.
+ *
+ * Apple Note:  The default DNS resolver client configuration
+ * is now stored in a system configuration database, not in
+ */
+
+#ifndef _PATH_RESCONF
+#define _PATH_RESCONF        "/etc/resolv.conf"
+#endif
+
+#define res_goahead res_9_goahead
+#define res_nextns res_9_nextns
+#define res_modified res_9_modified
+#define res_done res_9_done
+#define res_error res_9_error
+#define res_sendhookact res_9_sendhookact
+
+typedef enum
+{
+       res_goahead,
+       res_nextns,
+       res_modified,
+       res_done,
+       res_error
+} res_sendhookact;
+
+typedef res_sendhookact (*res_send_qhook)__P((struct sockaddr * const *ns,
+                                             const u_char **query,
+                                             int *querylen,
+                                             u_char *ans,
+                                             int anssiz,
+                                             int *resplen));
+
+typedef res_sendhookact (*res_send_rhook)__P((const struct sockaddr *ns,
+                                             const u_char *query,
+                                             int querylen,
+                                             u_char *ans,
+                                             int anssiz,
+                                             int *resplen));
+
+#define res_sym res_9_sym
+
+struct res_sym
+{
+       int             number;    /* Identifying number, like T_MX */
+       const char *    name;      /* Its symbolic name, like "MX" */
+       const char *    humanname; /* Its fun name, like "mail exchanger" */
+};
+
+/*
+ * Global defines and variables for resolver stub.
+ */
+#define        MAXNS                   3       /* max # name servers we'll track */
+#define        MAXDFLSRCH              3       /* # default domain levels to try */
+#define        MAXDNSRCH               6       /* max # domains in search path */
+#define        LOCALDOMAINPARTS        2       /* min levels in name that is "local" */
+
+#define        RES_TIMEOUT             5       /* min. seconds between retries */
+#define        MAXRESOLVSORT           10      /* number of net to sort on */
+#define        RES_MAXNDOTS            15      /* should reflect bit field size */
+#define        RES_MAXRETRANS          30      /* only for resolv.conf/RES_OPTIONS */
+#define        RES_MAXRETRY            5       /* only for resolv.conf/RES_OPTIONS */
+#define        RES_DFLRETRY            2       /* Default #/tries. */
+#define        RES_MAXTIME             65535   /* Infinity, in milliseconds. */
+
+#define __res_state_ext __res_9_state_ext
+struct __res_state_ext;
+
+#define __res_state __res_9_state
+struct __res_state {
+       int     retrans;                /* retransmition time interval */
+       int     retry;                  /* number of times to transmit (attempts, not retries) */
+#ifdef sun
+       u_int   options;                /* option flags - see below. */
+#else
+       u_long  options;                /* option flags - see below. */
+#endif
+       int     nscount;                /* number of name servers */
+       struct sockaddr_in
+               nsaddr_list[MAXNS];     /* address of name server */
+#define        nsaddr  nsaddr_list[0]          /* for backward compatibility */
+       u_short id;                     /* current message id */
+       char    *dnsrch[MAXDNSRCH+1];   /* components of domain to search */
+       char    defdname[256];          /* default domain (deprecated) */
+#ifdef sun
+       u_int   pfcode;                 /* RES_PRF_ flags - see below. */
+#else
+       u_long  pfcode;                 /* RES_PRF_ flags - see below. */
+#endif
+       unsigned ndots:4;               /* threshold for initial abs. query */
+       unsigned nsort:4;               /* number of elements in sort_list[] */
+       char    unused[3];
+       struct {
+               struct in_addr  addr;
+               u_int32_t       mask;
+       } sort_list[MAXRESOLVSORT];
+       res_send_qhook qhook;           /* query hook */
+       res_send_rhook rhook;           /* response hook */
+       int     res_h_errno;            /* last one set for this context */
+       int     _vcsock;                /* PRIVATE: for res_send VC i/o */
+       u_int   _flags;                 /* PRIVATE: see below */
+       u_int   _pad;                   /* make _u 64 bit aligned */
+       union {
+               /* On an 32-bit arch this means 512b total. */
+               char    pad[72 - 4*sizeof (int) - 2*sizeof (void *)];
+               struct {
+                       u_int16_t               nscount;
+                       u_int16_t               nstimes[MAXNS]; /* ms. */
+                       int                     nssocks[MAXNS];
+                       struct __res_state_ext *ext;    /* extention for IPv6 */
+               } _ext;
+       } _u;
+};
+
+#define res_state res_9_state
+
+typedef struct __res_state *res_state;
+
+#define res_sockaddr_union res_9_sockaddr_union
+
+union res_sockaddr_union {
+       struct sockaddr_in      sin;
+#ifdef IN6ADDR_ANY_INIT
+       struct sockaddr_in6     sin6;
+#endif
+#ifdef ISC_ALIGN64
+       int64_t                 __align64;      /* 64bit alignment */
+#else
+       int32_t                 __align32;      /* 32bit alignment */
+#endif
+       char                    __space[128];   /* max size */
+};
+
+/*
+ * Resolver flags (used to be discrete per-module statics ints).
+ */
+#define        RES_F_VC                        0x00000001      /* socket is TCP */
+#define        RES_F_CONN              0x00000002      /* socket is connected */
+#define        RES_F_EDNS0ERR  0x00000004      /* EDNS0 caused errors */
+
+/* res_findzonecut2() options */
+#define        RES_EXHAUSTIVE  0x00000001      /* always do all queries */
+#define        RES_IPV4ONLY            0x00000002      /* IPv4 only */
+#define        RES_IPV6ONLY            0x00000004      /* IPv6 only */
+
+/*
+ * Resolver options (keep these in synch with res_debug.c, please)
+ */
+#define RES_INIT                       0x00000001      /* address initialized */
+#define RES_DEBUG              0x00000002      /* print debug messages */
+#define RES_AAONLY             0x00000004      /* authoritative answers only (!IMPL)*/
+#define RES_USEVC              0x00000008      /* use virtual circuit */
+#define RES_PRIMARY            0x00000010      /* query primary server only (!IMPL) */
+#define RES_IGNTC              0x00000020      /* ignore trucation errors */
+#define RES_RECURSE            0x00000040      /* recursion desired */
+#define RES_DEFNAMES           0x00000080      /* use default domain name */
+#define RES_STAYOPEN           0x00000100      /* Keep TCP socket open */
+#define RES_DNSRCH             0x00000200      /* search up local domain tree */
+#define        RES_INSECURE1   0x00000400      /* type 1 security disabled */
+#define        RES_INSECURE2   0x00000800      /* type 2 security disabled */
+#define        RES_NOALIASES   0x00001000      /* shuts off HOSTALIASES feature */
+#define        RES_USE_INET6   0x00002000      /* use/map IPv6 in gethostbyname() */
+#define RES_ROTATE             0x00004000      /* rotate ns list after each query */
+#define        RES_NOCHECKNAME 0x00008000      /* do not check names for sanity. */
+#define        RES_KEEPTSIG            0x00010000      /* do not strip TSIG records */
+#define        RES_BLAST               0x00020000      /* blast all recursive servers */
+#define RES_NO_NIBBLE  0x00040000      /* disable IPv6 nibble mode reverse */
+#define RES_NO_BITSTRING 0x00080000    /* disable IPv6 bitstring mode reverse */
+#define RES_NOTLDQUERY 0x00100000      /* don't unqualified name as a tld */
+#define RES_USE_DNSSEC 0x00200000      /* use DNSSEC using OK bit in OPT */
+/* KAME extensions: use higher bit to avoid conflict with ISC use */
+#define RES_USE_DNAME  0x10000000      /* use DNAME */
+#define RES_USE_A6             0x20000000      /* use A6 */
+#define RES_USE_EDNS0  0x40000000      /* use EDNS0 if configured */
+#define RES_NO_NIBBLE2 0x80000000      /* disable alternate nibble lookup */
+
+#define RES_DEFAULT    (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_INSECURE1)
+
+/*
+ * Resolver "pfcode" values.  Used by dig.
+ */
+#define RES_PRF_STATS  0x00000001
+#define RES_PRF_UPDATE 0x00000002
+#define RES_PRF_CLASS   0x00000004
+#define RES_PRF_CMD            0x00000008
+#define RES_PRF_QUES           0x00000010
+#define RES_PRF_ANS            0x00000020
+#define RES_PRF_AUTH           0x00000040
+#define RES_PRF_ADD            0x00000080
+#define RES_PRF_HEAD1  0x00000100
+#define RES_PRF_HEAD2  0x00000200
+#define RES_PRF_TTLID  0x00000400
+#define RES_PRF_HEADX  0x00000800
+#define RES_PRF_QUERY  0x00001000
+#define RES_PRF_REPLY  0x00002000
+#define RES_PRF_INIT           0x00004000
+#define RES_PRF_TRUNC  0x00008000
+/*                     0x00010000      */
+
+/* Things involving an internal (static) resolver context. */
+#ifndef __BIND_NOSTATIC
+extern struct __res_state _res;
+#endif
+
+#ifndef __BIND_NOSTATIC
+#define fp_nquery              res_9_fp_nquery
+#define fp_query                       res_9_fp_query
+#define hostalias              res_9_hostalias_1
+#define p_query                        res_9_p_query
+#define res_close              res_9_close
+#define res_init                       res_9_init
+#define res_isourserver        res_9_isourserver
+#define res_mkquery            res_9_mkquery
+#define res_query              res_9_query
+#define res_querydomain        res_9_querydomain
+#define res_search             res_9_search
+#define res_send                       res_9_send
+#define res_sendsigned res_9_sendsigned
+
+__BEGIN_DECLS
+void           fp_nquery __P((const u_char *, int, FILE *));
+void           fp_query __P((const u_char *, FILE *));
+const char *hostalias __P((const char *));
+void           p_query __P((const u_char *));
+void           res_close __P((void));
+int            res_init __P((void));
+int            res_isourserver __P((const struct sockaddr_in *));
+int            res_mkquery __P((int, const char *, int, int, const u_char *,
+                                int, const u_char *, u_char *, int));
+int            res_query __P((const char *, int, int, u_char *, int));
+int            res_querydomain __P((const char *, const char *, int, int,
+                                    u_char *, int));
+int            res_search __P((const char *, int, int, u_char *, int));
+int            res_send __P((const u_char *, int, u_char *, int));
+int            res_sendsigned __P((const u_char *, int, ns_tsig_key *,
+                                   u_char *, int));
+__END_DECLS
+#endif
+
+#if !defined(SHARED_LIBBIND) || defined(LIB)
+/*
+ * If libbind is a shared object (well, DLL anyway)
+ * these externs break the linker when resolv.h is 
+ * included by a lib client (like named)
+ * Make them go away if a client is including this
+ *
+ */
+#define res_sym res_9_sym
+#define __p_key_syms __res_9_p_key_syms
+#define __p_cert_syms __res_9_p_cert_syms
+#define __p_class_syms __res_9_p_class_syms
+#define __p_type_syms __res_9_p_type_syms
+#define __p_rcode_syms __res_9_p_rcode_syms
+
+extern const struct res_sym __p_key_syms[];
+extern const struct res_sym __p_cert_syms[];
+extern const struct res_sym __p_class_syms[];
+extern const struct res_sym __p_type_syms[];
+extern const struct res_sym __p_rcode_syms[];
+#endif /* SHARED_LIBBIND */
+
+#define b64_ntop                                       res_9_b64_ntop
+#define b64_pton                                       res_9_b64_pton
+#define dn_comp                                        res_9_dn_comp
+#define dn_count_labels                        res_9_dn_count_labels
+#define dn_expand                              res_9_dn_expand
+#define dn_skipname                            res_9_dn_skipname
+#define fp_resstat                             res_9_fp_resstat
+#define loc_aton                                       res_9_loc_aton
+#define loc_ntoa                                       res_9_loc_ntoa
+#define p_cdname                                       res_9_p_cdname
+#define p_cdnname                              res_9_p_cdnname
+#define p_class                                        res_9_p_class
+#define p_fqname                                       res_9_p_fqname
+#define p_fqnname                              res_9_p_fqnname
+#define p_option                                       res_9_p_option
+#define p_secstodate                           res_9_p_secstodate
+#define p_section                              res_9_p_section
+#define p_time                                 res_9_p_time
+#define p_type                                 res_9_p_type
+#define p_rcode                                        res_9_p_rcode
+#define putlong                                        res_9_putlong
+#define putshort                                       res_9_putshort
+#define res_dnok                                       res_9_dnok
+#define res_findzonecut                        res_9_findzonecut
+#define res_findzonecut2                       res_9_findzonecut2
+#define res_hnok                                       res_9_hnok
+#define res_hostalias                  res_9_hostalias_2
+#define res_mailok                             res_9_mailok
+#define res_nameinquery                        res_9_nameinquery
+#define res_nclose                             res_9_nclose
+#define res_ninit                              res_9_ninit
+#define res_nmkquery                           res_9_nmkquery
+#define res_pquery                             res_9_pquery
+#define res_nquery                             res_9_nquery
+#define res_nquerydomain                       res_9_nquerydomain
+#define res_nsearch                            res_9_nsearch
+#define res_nsend                              res_9_nsend
+#define res_nsendsigned                        res_9_nsendsigned
+#define res_nisourserver                       res_9_nisourserver
+#define res_ownok                              res_9_ownok
+#define res_queriesmatch                       res_9_queriesmatch
+#define res_randomid                           res_9_randomid
+#define sym_ntop                                       res_9_sym_ntop
+#define sym_ntos                                       res_9_sym_ntos
+#define sym_ston                                       res_9_sym_ston
+#define res_nopt                                       res_9_nopt
+#define res_ndestroy                           res_9_ndestroy
+#define res_nametoclass                        res_9_nametoclass
+#define res_nametotype                 res_9_nametotype
+#define res_setservers                 res_9_setservers
+#define res_getservers                 res_9_getservers
+#define _getshort                              res_9_getshort
+#define _getlong                                       res_9_getlong
+#define __res_vinit                            res_9_vinit
+
+__BEGIN_DECLS
+int            res_hnok __P((const char *));
+int            res_ownok __P((const char *));
+int            res_mailok __P((const char *));
+int            res_dnok __P((const char *));
+int            sym_ston __P((const struct res_sym *, const char *, int *));
+const char *   sym_ntos __P((const struct res_sym *, int, int *));
+const char *sym_ntop __P((const struct res_sym *, int, int *));
+int            b64_ntop __P((u_char const *, size_t, char *, size_t));
+int            b64_pton __P((char const *, u_char *, size_t));
+int            loc_aton __P((const char *ascii, u_char *binary));
+const char *   loc_ntoa __P((const u_char *binary, char *ascii));
+int            dn_skipname __P((const u_char *, const u_char *));
+void           putlong __P((u_int32_t, u_char *));
+void           putshort __P((u_int16_t, u_char *));
+#ifndef __ultrix__
+u_int16_t      _getshort __P((const u_char *src));
+u_int32_t      _getlong __P((const u_char *src));
+#endif
+const char * p_class __P((int));
+const char * p_time __P((u_int32_t));
+const char * p_type __P((int));
+const char * p_rcode __P((int));
+const u_char * p_cdnname __P((const u_char *, const u_char *, int, FILE *));
+const u_char * p_cdname __P((const u_char *, const u_char *, FILE *));
+const u_char * p_fqnname __P((const u_char *cp, const u_char *msg, int, char *, int));
+const u_char * p_fqname __P((const u_char *, const u_char *, FILE *));
+const char * p_option __P((u_long option));
+char * p_secstodate __P((u_long));
+int            dn_count_labels __P((const char *));
+int            dn_comp __P((const char *, u_char *, int, u_char **, u_char **));
+int            dn_expand __P((const u_char *, const u_char *, const u_char *, char *, int));
+u_int          res_randomid __P((void));
+int            res_nameinquery __P((const char *, int, int, const u_char *, const u_char *));
+int            res_queriesmatch __P((const u_char *, const u_char *, const u_char *, const u_char *));
+const char * p_section __P((int section, int opcode));
+
+/* Things involving a resolver context. */
+int            res_ninit __P((res_state));
+int            res_nisourserver __P((const res_state, const struct sockaddr_in *));
+void           fp_resstat __P((const res_state, FILE *));
+void           res_pquery __P((const res_state, const u_char *, int, FILE *));
+const char *res_hostalias __P((const res_state, const char *, char *, size_t));
+int            res_nquery __P((res_state, const char *, int, int, u_char *, int));
+int            res_nsearch __P((res_state, const char *, int, int, u_char *, int));
+int            res_nquerydomain __P((res_state, const char *, const char *, int, int, u_char *, int));
+int            res_nmkquery __P((res_state, int, const char *, int, int, const u_char *, int, const u_char *, u_char *, int));
+int            res_nsend __P((res_state, const u_char *, int, u_char *, int));
+int            res_nsendsigned __P((res_state, const u_char *, int, ns_tsig_key *, u_char *, int));
+int            res_findzonecut __P((res_state, const char *, ns_class, int, char *, size_t, struct in_addr *, int));
+int            res_findzonecut2 __P((res_state, const char *, ns_class, int, char *, size_t, union res_sockaddr_union *, int));
+void           res_nclose __P((res_state));
+int            res_nopt __P((res_state, int, u_char *, int, int));
+void           res_send_setqhook __P((res_send_qhook hook));
+void           res_send_setrhook __P((res_send_rhook hook));
+int            __res_vinit __P((res_state, int));
+void           res_destroyservicelist __P((void));
+const char *   res_servicename __P((u_int16_t port, const char *proto));
+const char *   res_protocolname __P((int num));
+void           res_destroyprotolist __P((void));
+void           res_buildprotolist __P((void));
+const char *   res_get_nibblesuffix __P((res_state));
+const char *   res_get_nibblesuffix2 __P((res_state));
+const char *   res_get_bitstringsuffix __P((res_state));
+void           res_ndestroy __P((res_state));
+u_int16_t      res_nametoclass __P((const char *buf, int *success));
+u_int16_t      res_nametotype __P((const char *buf, int *success));
+void           res_setservers __P((res_state, const union res_sockaddr_union *, int));
+int            res_getservers __P((res_state, union res_sockaddr_union *, int));
+__END_DECLS
+
+#endif /* !BIND_8_COMPAT */
+#endif /* !_RESOLV_9_H_ */
diff --git a/resolver.3 b/resolver.3
new file mode 100644 (file)
index 0000000..cd03597
--- /dev/null
@@ -0,0 +1,422 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\"    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:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 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
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" 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.
+.\"
+.\"     @(#)resolver.3 8.1 (Berkeley) 6/4/93
+.\" $FreeBSD: src/lib/libc/net/resolver.3,v 1.21 2001/10/01 16:08:56 ru Exp $
+.\"
+.Dd June 4, 1993
+.Dt RESOLVER 3
+.Os
+.Sh NAME
+.Nm res_query ,
+.Nm res_search ,
+.Nm res_mkquery ,
+.Nm res_send ,
+.Nm res_init ,
+.Nm dn_comp ,
+.Nm dn_expand ,
+.Nm dn_skipname ,
+.Nm ns_get16 ,
+.Nm ns_get32 ,
+.Nm ns_put16 ,
+.Nm ns_put32
+.Nd resolver routines
+.Sh LIBRARY
+.Lb libresolv
+.Sh SYNOPSIS
+.In sys/types.h
+.In netinet/in.h
+.In arpa/nameser.h
+.In resolv.h
+.Ft int
+.Fo res_query
+.Fa "const char *dname"
+.Fa "int class"
+.Fa "int type"
+.Fa "u_char *answer"
+.Fa "int anslen"
+.Fc
+.Ft int
+.Fo res_search
+.Fa "const char *dname"
+.Fa "int class"
+.Fa "int type"
+.Fa "u_char *answer"
+.Fa "int anslen"
+.Fc
+.Ft int
+.Fo res_mkquery
+.Fa "int op"
+.Fa "const char *dname"
+.Fa "int class"
+.Fa "int type"
+.Fa "const u_char *data"
+.Fa "int datalen"
+.Fa "const u_char *newrr_in"
+.Fa "u_char *buf"
+.Fa "int buflen"
+.Fc
+.Ft int
+.Fo res_send
+.Fa "const u_char *msg"
+.Fa "int msglen"
+.Fa "u_char *answer"
+.Fa "int anslen"
+.Fc
+.Ft int
+.Fn res_init
+.Ft int
+.Fo dn_comp
+.Fa "const char *exp_dn"
+.Fa "u_char *comp_dn"
+.Fa "int length"
+.Fa "u_char **dnptrs"
+.Fa "u_char **lastdnptr"
+.Fc
+.Ft int
+.Fo dn_expand
+.Fa "const u_char *msg"
+.Fa "const u_char *eomorig"
+.Fa "const u_char *comp_dn"
+.Fa "char *exp_dn"
+.Fa "int length"
+.Fc
+.Ft int
+.Fn dn_skipname "const u_char *comp_dn" "const u_char *eom"
+.Ft u_int
+.Fn ns_get16 "const u_char *src"
+.Ft u_long
+.Fn ns_get32 "const u_char *src"
+.Ft void
+.Fn ns_put16 "u_int src" "u_char *dst"
+.Ft void
+.Fn ns_put32 "u_long src" "u_char *dst"
+.Sh DESCRIPTION
+These routines are used for making, sending and interpreting
+query and reply messages with Internet domain name servers.
+.Pp
+Global configuration and state information that is used by the
+resolver routines is kept in the structure
+.Em _res .
+Most of the values have reasonable defaults and can be ignored.
+Options
+stored in
+.Em _res.options
+are defined in
+.Pa resolv.h
+and are as follows.
+Options are stored as a simple bit mask containing the bitwise ``or''
+of the options enabled.
+.Bl -tag -width RES_USE_INET6
+.It Dv RES_INIT
+True if the initial name server address and default domain name are
+initialized (i.e.,
+.Fn res_init
+has been called).
+.It Dv RES_DEBUG
+Print debugging messages.
+.It Dv RES_AAONLY
+Accept authoritative answers only.
+With this option,
+.Fn res_send
+should continue until it finds an authoritative answer or finds an error.
+Currently this is not implemented.
+.It Dv RES_USEVC
+Use
+.Tn TCP
+connections for queries instead of
+.Tn UDP
+datagrams.
+.It Dv RES_STAYOPEN
+Used with
+.Dv RES_USEVC
+to keep the
+.Tn TCP
+connection open between
+queries.
+This is useful only in programs that regularly do many queries.
+.Tn UDP
+should be the normal mode used.
+.It Dv RES_IGNTC
+Unused currently (ignore truncation errors, i.e., don't retry with
+.Tn TCP ) .
+.It Dv RES_RECURSE
+Set the recursion-desired bit in queries.
+This is the default.
+.Pf ( Fn res_send
+does not do iterative queries and expects the name server
+to handle recursion.)
+.It Dv RES_DEFNAMES
+If set,
+.Fn res_search
+will append the default domain name to single-component names
+(those that do not contain a dot).
+This option is enabled by default.
+.It Dv RES_DNSRCH
+If this option is set,
+.Fn res_search
+will search for host names in the current domain and in parent domains; see
+.Xr hostname 7 .
+This is used by the standard host lookup routine
+.Xr gethostbyname 3 .
+This option is enabled by default.
+.It Dv RES_NOALIASES
+This option turns off the user level aliasing feature controlled by the
+.Dq Ev HOSTALIASES
+environment variable.  Network daemons should set this option.
+.It Dv RES_USE_INET6
+Enables support for IPv6-only applications.
+This causes IPv4 addresses to be returned as an IPv4 mapped address.
+For example,
+.Li 10.1.1.1
+will be returned as
+.Li ::ffff:10.1.1.1 .
+The option is meaningful with certain kernel configuration only.
+.It Dv RES_USE_EDNS0
+Enables support for OPT pseudo-RR for EDNS0 extension.
+With the option, resolver code will attach OPT pseudo-RR into DNS queries,
+to inform of our receive buffer size.
+The option will allow DNS servers to take advantage of non-default receive
+buffer size, and to send larger replies.
+DNS query packets with EDNS0 extension is not compatible with
+non-EDNS0 DNS servers.
+.El
+.Pp
+The
+.Fn res_init
+routine
+reads the configuration file (if any; see
+.Xr resolver 5 )
+to get the default domain name,
+search list and
+the Internet address of the local name server(s).
+If no server is configured, the host running
+the resolver is tried.
+The current domain name is defined by the hostname
+if not specified in the configuration file;
+it can be overridden by the environment variable
+.Ev LOCALDOMAIN .
+This environment variable may contain several blank-separated
+tokens if you wish to override the
+.Em "search list"
+on a per-process basis.  This is similar to the
+.Em search
+command in the configuration file.
+Another environment variable
+.Dq Ev RES_OPTIONS
+can be set to
+override certain internal resolver options which are otherwise
+set by changing fields in the
+.Em _res
+structure or are inherited from the configuration file's
+.Em options
+command.  The syntax of the
+.Dq Ev RES_OPTIONS
+environment variable is explained in
+.Xr resolver 5 .
+Initialization normally occurs on the first call
+to one of the following routines.
+.Pp
+The
+.Fn res_query
+function provides an interface to the server query mechanism.
+It constructs a query, sends it to the local server,
+awaits a response, and makes preliminary checks on the reply.
+The query requests information of the specified
+.Fa type
+and
+.Fa class
+for the specified fully-qualified domain name
+.Fa dname .
+The reply message is left in the
+.Fa answer
+buffer with length
+.Fa anslen
+supplied by the caller.
+.Pp
+The
+.Fn res_search
+routine makes a query and awaits a response like
+.Fn res_query ,
+but in addition, it implements the default and search rules
+controlled by the
+.Dv RES_DEFNAMES
+and
+.Dv RES_DNSRCH
+options.
+It returns the first successful reply.
+.Pp
+The remaining routines are lower-level routines used by
+.Fn res_query .
+The
+.Fn res_mkquery
+function
+constructs a standard query message and places it in
+.Fa buf .
+It returns the size of the query, or \-1 if the query is
+larger than
+.Fa buflen .
+The query type
+.Fa op
+is usually
+.Dv QUERY ,
+but can be any of the query types defined in
+.Aq Pa arpa/nameser.h .
+The domain name for the query is given by
+.Fa dname .
+.Fa Newrr
+is currently unused but is intended for making update messages.
+.Pp
+The
+.Fn res_send
+routine
+sends a pre-formatted query and returns an answer.
+It will call
+.Fn res_init
+if
+.Dv RES_INIT
+is not set, send the query to the local name server, and
+handle timeouts and retries.
+The length of the reply message is returned, or
+\-1 if there were errors.
+.Pp
+The
+.Fn dn_comp
+function
+compresses the domain name
+.Fa exp_dn
+and stores it in
+.Fa comp_dn .
+The size of the compressed name is returned or \-1 if there were errors.
+The size of the array pointed to by
+.Fa comp_dn
+is given by
+.Fa length .
+The compression uses
+an array of pointers
+.Fa dnptrs
+to previously-compressed names in the current message.
+The first pointer points to
+the beginning of the message and the list ends with
+.Dv NULL .
+The limit to the array is specified by
+.Fa lastdnptr .
+A side effect of
+.Fn dn_comp
+is to update the list of pointers for
+labels inserted into the message
+as the name is compressed.
+If
+.Em dnptr
+is
+.Dv NULL ,
+names are not compressed.
+If
+.Fa lastdnptr
+is
+.Dv NULL ,
+the list of labels is not updated.
+.Pp
+The
+.Fn dn_expand
+entry
+expands the compressed domain name
+.Fa comp_dn
+to a full domain name
+The compressed name is contained in a query or reply message;
+.Fa msg
+is a pointer to the beginning of the message.
+The uncompressed name is placed in the buffer indicated by
+.Fa exp_dn
+which is of size
+.Fa length .
+The size of compressed name is returned or \-1 if there was an error.
+.Pp
+The
+.Fn dn_skipname
+function skips over a compressed domain name, which starts at a location
+pointed to by
+.Fa comp_dn .
+The compressed name is contained in a query or reply message;
+.Fa eom
+is a pointer to the end of the message.
+The size of compressed name is returned or \-1 if there was
+an error.
+.Pp
+The
+.Fn ns_get16
+function gets a 16-bit quantity from a buffer pointed to by
+.Fa src .
+.Pp
+The
+.Fn ns_get32
+function gets a 32-bit quantity from a buffer pointed to by
+.Fa src .
+.Pp
+The
+.Fn ns_put16
+function puts a 16-bit quantity
+.Fa src
+to a buffer pointed to by
+.Fa dst .
+.Pp
+The
+.Fn ns_put32
+function puts a 32-bit quantity
+.Fa src
+to a buffer pointed to by
+.Fa dst .
+.Sh FILES
+.Bl -tag -width /etc/resolv.conf
+.It Pa /etc/resolv.conf
+The configuration file,
+see
+.Xr resolver 5 .
+.El
+.Sh SEE ALSO
+.Xr gethostbyname 3 ,
+.Xr resolver 5 ,
+.Xr hostname 7 ,
+.Xr named 8
+.Pp
+.%T RFC1032 ,
+.%T RFC1033 ,
+.%T RFC1034 ,
+.%T RFC1035 ,
+.%T RFC974
+.Rs
+.%T "Name Server Operations Guide for BIND"
+.Re
+.Sh HISTORY
+The
+.Nm
+function appeared in
+.Bx 4.3 .
diff --git a/resolver.5 b/resolver.5
new file mode 100644 (file)
index 0000000..8161807
--- /dev/null
@@ -0,0 +1,225 @@
+.\"    $OpenBSD: resolver.5,v 1.2 1997/03/12 10:42:19 downsj Exp $
+.\" Copyright (c) 1986 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley.  The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" Portions Copyright (c) 2003 by Apple Computer, Inc. 
+.\"
+.\"    @(#)resolver.5  5.9 (Berkeley) 12/14/89
+.\"    $From: resolver.5,v 8.3 1995/12/06 20:34:35 vixie Exp $
+.\"
+.Dd June 6, 2003
+.Dt resolver 5
+.Os "Mac OS X"
+.Sh NAME
+.Nm resolver
+.Nd resolver configuration file format
+.Sh DESCRIPTION
+The
+.Nm
+is a set of routines in the C library 
+.Xr resolv(3)
+that provide access to the Internet Domain Name System (DNS).
+A resolver configuration file contains information used to specify parameters
+for a DNS resolver client.
+The file contains a list of keywords with values that provide various types
+of resolver information.
+.Pp
+Mac OS X supports a DNS search strategy that may involve multiple
+DNS resolver clients.
+See the
+.Sx SEARCH STRATEGY
+section below for an overview of multi-client DNS
+search.
+.Pp
+Each DNS client is configured using the contents of a single configuration
+file of the format described below, or from a property list supplied from
+some other system configuration database.
+Note that the 
+.Pa /etc/resolv.conf
+file, which contains configuration for the default (or "primary") DNS resolver client,
+is maintained automatically by Mac OS X and should not be edited manually.
+Changes to the DNS configuration should be made by using the Network
+Preferences panel.
+.Pp
+The different configuration options are given below.
+.Ss nameserver
+Internet address (in dot notation for IPv4 or in colon notation for IPv6)
+of a name server that the resolver should query.
+The address may optionally have a trailing dot followed by a port number.
+For example, 
+.Li 10.0.0.17.55
+specifies that the nameserver at 10.0.0.17
+uses port 55.
+.Pp
+Up to
+.Va MAXNS 
+(currently 3) name servers may be listed,
+one per keyword.
+If there are multiple servers,
+the resolver library queries them in the order listed.
+The algorithm used is to try a name server, and if the query times out,
+try the next, until out of name servers,
+then repeat trying all the name servers
+until a maximum number of retries are made.
+.Ss port
+IP port number to be used for this resolver.
+The default port is 53.
+The port number for an individual nameserver may be specified as
+part of the nameserver address (see 
+.Sx nameserver 
+above) to override the default 
+or the port number specified as a value for this keyword.
+.Ss domain
+Domain name associated with this resolver configuration.
+This option is normally not required by the Mac OS X DNS search system
+when the resolver configuration is read from a file in the
+.Pa /etc/resolver
+directroy.
+In that case the file name is used as the domain name.
+However, 
+.Sx domain
+must be provided when there are 
+multiple resolver clients for the same domain name, since multiple
+files may not exist having the same name.
+See the 
+.Sx SEARCH STRATEGY
+section for more details.
+.Ss search
+Search list for host-name lookup.
+This parameter is only used by the "Super" DNS resolver, which
+manages the DNS search strategy amongst multiple DNS resolver clients.
+Unqualified queries will be attempted using each component
+of the search list in turn until a match is found.
+Note that this process may be slow and will generate a lot of network
+traffic if the servers for the listed domains are not local,
+and that queries will time out if no server is available
+for one of the domains.
+.Pp
+The search list is currently limited to six domains
+with a total of 256 characters.
+.Ss search_order
+Only required for those clients that share a domain name with other clients.
+Queries will be sent to these clients in order by ascending
+.Sx search_order
+value.
+For example, this allows two clients for the ".local"
+domain, which is used by Apple's multicast DNS, but which may
+also be used at some sites as private DNS domain name.
+.Ss sortlist
+Sortlist allows addresses returned by gethostbyname to be sorted.
+A sortlist is specified by IP address netmask pairs. The netmask is
+optional and defaults to the natural netmask of the net. The IP address
+and optional network pairs are separated by slashes. Up to 10 pairs may
+be specified. For example: 
+.Bd -literal -offset indent
+ sortlist 130.155.160.0/255.255.240.0 130.155.0.0
+.Ed
+.Ss timeout
+Specifies the total amount of time allowed for a name resolution.
+This time interval is divided by the number of nameservers and the number
+of retries allowed for each nameserver.
+.Ss options
+Options allows certain internal resolver variables to be modified.
+The syntax is:
+.Pp
+options
+.Ar option Li "..."
+.Pp
+where 
+.Ar option
+is one of the following:
+.Bl -tag -width -indent
+.It Ar debug
+sets
+.Va RES_DEBUG
+in the resolver options.
+.It Ar timeout:n
+sets the per-retry timeout for resolver queries.
+The total timeout allowed for a query depends on the number of retries and the
+number of nameservers.  This value is ignored if a total timeout is specified
+using the
+.Sx timeout
+keyword (see above).
+.It Ar ndots:n
+Sets a threshold for the number of dots which
+must appear in a name given to
+.Sx res_query
+(see 
+.Xr resolver(3))
+before an initial absolute query will be made.  The default for
+.Ar n
+is ``1'', meaning that if there are any dots in a name, the name
+will be tried first as an absolute name before any 
+.Sx search
+list elements are appended to it.
+.Pp
+The keyword and value must appear on a single line,
+and the keyword must start the line.
+The value follows the keyword, separated by white space.
+.El
+.Sh SEARCH STRATEGY
+Mac OS X uses a DNS search strategy that supports multiple DNS
+client configurations.
+Each DNS client has its own set of nameserver
+addresses and its own set of operational parameters.
+Each client can perform DNS queries and searches independent of other clients.
+Each client has a symbolic name which is of the same format as a
+domain name, e.g. "apple.com".
+A special meta-client, known as the
+"Super" DNS client acts as a router for DNS queries.
+The Super client chooses among all available clients by finding a best match
+between the domain name given in a query and the names of all known clients.
+.Pp
+Queries for qualified names
+are sent using a client configuration
+that best matches the domain name given in the query.
+For example, if there is a client named "apple.com", a search for
+"www.apple.com" would use the resolver configuration specified for that client.
+The matching algorithm chooses the client with the maximum number of matching
+domain components.
+For example, if there are clients named "a.b.c", and "b.c", a search for
+"x.a.b.c" would use the "a.b.c" resolver configuration, while a search
+for "x.y.b.c" would use the "b.c" client.
+If there are no matches, the configuration settings in the default client,
+generally corresponding to the
+.Pa /etc/resolv.conf
+file or to the "primary" DNS
+configuration on the system are used for the query.
+.Pp
+If multiple clients are available for the same domain name, the clients ordered
+according to a
+.Sx search_order
+value (see above).
+Queries are sent to these resolvers in sequence by ascending value of
+search_order.
+.Pp
+The configuration for a particular client may be read from a file
+having the format described in this man page.
+These are at present located by the system in the 
+.Pa /etc/resolv.conf
+file and
+in the files found in the 
+.Pa /etc/resolver
+directroy.
+However, client configurations are not limited to file storage.
+The implementation of the DNS multi-client search strategy may also locate
+client configuratins in other data sources, such as the System Configuration
+Database.
+Users of the DNS system should make no assumptions about the
+source of the configuration data.
+.Sh FILES
+/etc/resolv.conf, /etc/resolver/*
+.Sh SEE ALSO
+gethostbyname(2), getaddrinfo(3), resolver(3)
diff --git a/resolver_so.3 b/resolver_so.3
new file mode 100644 (file)
index 0000000..87a6d0e
--- /dev/null
@@ -0,0 +1 @@
+.so man3/resolver.3