auto import from //depot/cupcake/@135843
diff --git a/libc/netbsd/resolv/__dn_comp.c b/libc/netbsd/resolv/__dn_comp.c
new file mode 100644
index 0000000..93d3f19
--- /dev/null
+++ b/libc/netbsd/resolv/__dn_comp.c
@@ -0,0 +1,38 @@
+/*	$NetBSD: __dn_comp.c,v 1.4 2005/09/13 01:44:10 christos Exp $	*/
+
+/*
+ * written by matthew green, 22/04/97.
+ * public domain.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: __dn_comp.c,v 1.4 2005/09/13 01:44:10 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#if defined(__indr_reference)
+__indr_reference(__dn_comp,dn_comp)
+#else
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+
+/* XXX THIS IS A MESS!  SEE <resolv.h> XXX */
+
+#undef dn_comp
+int	dn_comp(const char *, u_char *, int, u_char **, u_char **);
+
+int
+dn_comp(const char *exp_dn, u_char *comp_dn, u_char **dnptrs,
+    u_char **lastdnptr, int length)
+{
+
+	return __dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr);
+}
+
+#endif
diff --git a/libc/netbsd/resolv/__res_close.c b/libc/netbsd/resolv/__res_close.c
new file mode 100644
index 0000000..3af50b0
--- /dev/null
+++ b/libc/netbsd/resolv/__res_close.c
@@ -0,0 +1,33 @@
+/*	$NetBSD: __res_close.c,v 1.4 2005/09/13 01:44:10 christos Exp $	*/
+
+/*
+ * written by matthew green, 22/04/97.
+ * public domain.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: __res_close.c,v 1.4 2005/09/13 01:44:10 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#if defined(__indr_reference)
+__indr_reference(__res_close, res_close)
+#else
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include "resolv_private.h"
+
+/* XXX THIS IS A MESS!  SEE <resolv.h> XXX */
+
+#undef res_close
+void	res_close(void);
+
+void
+res_close(void)
+{
+
+	__res_close();
+}
+
+#endif
diff --git a/libc/netbsd/resolv/__res_send.c b/libc/netbsd/resolv/__res_send.c
new file mode 100644
index 0000000..198b05c
--- /dev/null
+++ b/libc/netbsd/resolv/__res_send.c
@@ -0,0 +1,37 @@
+/*	$NetBSD: __res_send.c,v 1.4 2005/09/13 01:44:10 christos Exp $	*/
+
+/*
+ * written by matthew green, 22/04/97.
+ * public domain.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: __res_send.c,v 1.4 2005/09/13 01:44:10 christos Exp $");
+#endif
+
+#if defined(__indr_reference)
+__indr_reference(__res_send, res_send)
+#else
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+
+/* XXX THIS IS A MESS!  SEE <resolv.h> XXX */
+
+#undef res_send
+int	res_send(const u_char *, int, u_char *, int);
+
+int
+res_send(const u_char *buf, int buflen, u_char *ans, int anssiz)
+{
+
+	return __res_send(buf, buflen, ans, anssiz);
+}
+
+#endif
diff --git a/libc/netbsd/resolv/herror.c b/libc/netbsd/resolv/herror.c
new file mode 100644
index 0000000..e90e641
--- /dev/null
+++ b/libc/netbsd/resolv/herror.c
@@ -0,0 +1,133 @@
+/*	$NetBSD: herror.c,v 1.4 2004/05/23 05:09:52 christos Exp $	*/
+
+/*
+ * Copyright (c) 1987, 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) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)herror.c	8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "Id: herror.c,v 1.2.206.1 2004/03/09 08:33:54 marka Exp";
+#else
+__RCSID("$NetBSD: herror.c,v 1.4 2004/05/23 05:09:52 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include "arpa_nameser.h"
+
+#include <netdb.h>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+#ifndef DE_CONST
+#define DE_CONST(c,v)   v = ((c) ? \
+    strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL)
+#endif
+
+const char * const h_errlist[] = {
+	"Resolver Error 0 (no error)",
+	"Unknown host",				/* 1 HOST_NOT_FOUND */
+	"Host name lookup failure",		/* 2 TRY_AGAIN */
+	"Unknown server error",			/* 3 NO_RECOVERY */
+	"No address associated with name",	/* 4 NO_ADDRESS */
+};
+const int	h_nerr = { sizeof h_errlist / sizeof h_errlist[0] };
+
+/*
+ * herror --
+ *	print the error indicated by the h_errno value.
+ */
+void
+herror(const char *s) {
+	struct iovec iov[4], *v = iov;
+	char *t;
+
+	if (s != NULL && *s != '\0') {
+		DE_CONST(s, t);
+		v->iov_base = t;
+		v->iov_len = strlen(t);
+		v++;
+		DE_CONST(": ", t);
+		v->iov_base = t;
+		v->iov_len = 2;
+		v++;
+	}
+	DE_CONST(hstrerror(h_errno), t);
+	v->iov_base = t;
+	v->iov_len = strlen(v->iov_base);
+	v++;
+	DE_CONST("\n", t);
+	v->iov_base = t;
+	v->iov_len = 1;
+	writev(STDERR_FILENO, iov, (v - iov) + 1);
+}
+
+/*
+ * hstrerror --
+ *	return the string associated with a given "host" errno value.
+ */
+const char *
+hstrerror(int err) {
+	if (err < 0)
+		return ("Resolver internal error");
+	else if (err < h_nerr)
+		return (h_errlist[err]);
+	return ("Unknown resolver error");
+}
diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c
new file mode 100644
index 0000000..2c912de
--- /dev/null
+++ b/libc/netbsd/resolv/res_cache.c
@@ -0,0 +1,1461 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ */
+
+#include "resolv_cache.h"
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "pthread.h"
+
+/* This code implements a small and *simple* DNS resolver cache.
+ *
+ * It is only used to cache DNS answers for a maximum of CONFIG_SECONDS seconds
+ * in order to reduce DNS traffic. It is not supposed to be a full DNS cache,
+ * since we plan to implement that in the future in a dedicated process running
+ * on the system.
+ *
+ * Note that its design is kept simple very intentionally, i.e.:
+ *
+ *  - it takes raw DNS query packet data as input, and returns raw DNS
+ *    answer packet data as output
+ *
+ *    (this means that two similar queries that encode the DNS name
+ *     differently will be treated distinctly).
+ *
+ *  - the TTLs of answer RRs are ignored. our DNS resolver library does not use
+ *    them anyway, but it means that records with a TTL smaller than
+ *    CONFIG_SECONDS will be kept in the cache anyway.
+ *
+ *    this is bad, but we absolutely want to avoid parsing the answer packets
+ *    (and should be solved by the later full DNS cache process).
+ *
+ *  - the implementation is just a (query-data) => (answer-data) hash table
+ *    with a trivial least-recently-used expiration policy.
+ *
+ * Doing this keeps the code simple and avoids to deal with a lot of things
+ * that a full DNS cache is expected to do.
+ *
+ * The API is also very simple:
+ *
+ *   - the client calls _resolv_cache_get() to obtain a handle to the cache.
+ *     this will initialize the cache on first usage. the result can be NULL
+ *     if the cache is disabled.
+ *
+ *   - the client calls _resolv_cache_lookup() before performing a query
+ *
+ *     if the function returns RESOLV_CACHE_FOUND, a copy of the answer data
+ *     has been copied into the client-provided answer buffer.
+ *
+ *     if the function returns RESOLV_CACHE_NOTFOUND, the client should perform
+ *     a request normally, *then* call _resolv_cache_add() to add the received
+ *     answer to the cache.
+ *
+ *     if the function returns RESOLV_CACHE_UNSUPPORTED, the client should
+ *     perform a request normally, and *not* call _resolv_cache_add()
+ *
+ *     note that RESOLV_CACHE_UNSUPPORTED is also returned if the answer buffer
+ *     is too short to accomodate the cached result.
+ *
+ *  - when network settings change, the cache must be flushed since the list
+ *    of DNS servers probably changed. this is done by calling
+ *    _resolv_cache_reset()
+ *
+ *    the parameter to this function must be an ever-increasing generation
+ *    number corresponding to the current network settings state.
+ *
+ *    This is done because several threads could detect the same network
+ *    settings change (but at different times) and will all end up calling the
+ *    same function. Comparing with the last used generation number ensures
+ *    that the cache is only flushed once per network change.
+ */
+
+/* the name of an environment variable that will be checked the first time
+ * this code is called if its value is "0", then the resolver cache is
+ * disabled.
+ */
+#define  CONFIG_ENV  "BIONIC_DNSCACHE"
+
+/* entries older than CONFIG_SECONDS seconds are always discarded.
+ */
+#define  CONFIG_SECONDS    (60*10)    /* 10 minutes */
+
+/* maximum number of entries kept in the cache. This value has been
+ * determined by browsing through various sites and counting the number
+ * of corresponding requests. Keep in mind that our framework is currently
+ * performing two requests per name lookup (one for IPv4, the other for IPv6)
+ *
+ *    www.google.com      4
+ *    www.ysearch.com     6
+ *    www.amazon.com      8
+ *    www.nytimes.com     22
+ *    www.espn.com        28
+ *    www.msn.com         28
+ *    www.lemonde.fr      35
+ *
+ * (determined in 2009-2-17 from Paris, France, results may vary depending
+ *  on location)
+ *
+ * most high-level websites use lots of media/ad servers with different names
+ * but these are generally reused when browsing through the site.
+ *
+ * As such, a valud of 64 should be relatively conformtable at the moment.
+ */
+#define  CONFIG_MAX_ENTRIES    64
+
+/****************************************************************************/
+/****************************************************************************/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/****************************************************************************/
+/****************************************************************************/
+
+/* set to 1 to debug cache operations */
+#define  DEBUG       0
+
+/* set to 1 to debug query data */
+#define  DEBUG_DATA  0
+
+#if DEBUG
+#  include <logd.h>
+#  define  XLOG(...)   \
+    __libc_android_log_print(ANDROID_LOG_DEBUG,"libc",__VA_ARGS__)
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/** BOUNDED BUFFER FORMATTING
+ **/
+
+/* technical note:
+ *
+ *   the following debugging routines are used to append data to a bounded
+ *   buffer they take two parameters that are:
+ *
+ *   - p : a pointer to the current cursor position in the buffer
+ *         this value is initially set to the buffer's address.
+ *
+ *   - end : the address of the buffer's limit, i.e. of the first byte
+ *           after the buffer. this address should never be touched.
+ *
+ *           IMPORTANT: it is assumed that end > buffer_address, i.e.
+ *                      that the buffer is at least one byte.
+ *
+ *   the _bprint_() functions return the new value of 'p' after the data
+ *   has been appended, and also ensure the following:
+ *
+ *   - the returned value will never be strictly greater than 'end'
+ *
+ *   - a return value equal to 'end' means that truncation occured
+ *     (in which case, end[-1] will be set to 0)
+ *
+ *   - after returning from a _bprint_() function, the content of the buffer
+ *     is always 0-terminated, even in the event of truncation.
+ *
+ *  these conventions allow you to call _bprint_ functions multiple times and
+ *  only check for truncation at the end of the sequence, as in:
+ *
+ *     char  buff[1000], *p = buff, *end = p + sizeof(buff);
+ *
+ *     p = _bprint_c(p, end, '"');
+ *     p = _bprint_s(p, end, my_string);
+ *     p = _bprint_c(p, end, '"');
+ *
+ *     if (p >= end) {
+ *        // buffer was too small
+ *     }
+ *
+ *     printf( "%s", buff );
+ */
+
+/* add a char to a bounded buffer */
+static char*
+_bprint_c( char*  p, char*  end, int  c )
+{
+    if (p < end) {
+        if (p+1 == end)
+            *p++ = 0;
+        else {
+            *p++ = (char) c;
+            *p   = 0;
+        }
+    }
+    return p;
+}
+
+/* add a sequence of bytes to a bounded buffer */
+static char*
+_bprint_b( char*  p, char*  end, const char*  buf, int  len )
+{
+    int  avail = end - p;
+
+    if (avail <= 0 || len <= 0)
+        return p;
+
+    if (avail > len)
+        avail = len;
+
+    memcpy( p, buf, avail );
+    p += avail;
+
+    if (p < end)
+        p[0] = 0;
+    else
+        end[-1] = 0;
+
+    return p;
+}
+
+/* add a string to a bounded buffer */
+static char*
+_bprint_s( char*  p, char*  end, const char*  str )
+{
+    return _bprint_b(p, end, str, strlen(str));
+}
+
+/* add a formatted string to a bounded buffer */
+static char*
+_bprint( char*  p, char*  end, const char*  format, ... )
+{
+    int      avail, n;
+    va_list  args;
+
+    avail = end - p;
+
+    if (avail <= 0)
+        return p;
+
+    va_start(args, format);
+    n = snprintf( p, avail, format, args);
+    va_end(args);
+
+    /* certain C libraries return -1 in case of truncation */
+    if (n < 0 || n > avail)
+        n = avail;
+
+    p += n;
+    /* certain C libraries do not zero-terminate in case of truncation */
+    if (p == end)
+        p[-1] = 0;
+
+    return p;
+}
+
+/* add a hex value to a bounded buffer, up to 8 digits */
+static char*
+_bprint_hex( char*  p, char*  end, unsigned  value, int  numDigits )
+{
+    char   text[sizeof(unsigned)*2];
+    int    nn = 0;
+
+    while (numDigits-- > 0) {
+        text[nn++] = "0123456789abcdef"[(value >> (numDigits*4)) & 15];
+    }
+    return _bprint_b(p, end, text, nn);
+}
+
+/* add the hexadecimal dump of some memory area to a bounded buffer */
+static char*
+_bprint_hexdump( char*  p, char*  end, const uint8_t*  data, int  datalen )
+{
+    int   lineSize = 16;
+
+    while (datalen > 0) {
+        int  avail = datalen;
+        int  nn;
+
+        if (avail > lineSize)
+            avail = lineSize;
+
+        for (nn = 0; nn < avail; nn++) {
+            if (nn > 0)
+                p = _bprint_c(p, end, ' ');
+            p = _bprint_hex(p, end, data[nn], 2);
+        }
+        for ( ; nn < lineSize; nn++ ) {
+            p = _bprint_s(p, end, "   ");
+        }
+        p = _bprint_s(p, end, "  ");
+
+        for (nn = 0; nn < avail; nn++) {
+            int  c = data[nn];
+
+            if (c < 32 || c > 127)
+                c = '.';
+
+            p = _bprint_c(p, end, c);
+        }
+        p = _bprint_c(p, end, '\n');
+
+        data    += avail;
+        datalen -= avail;
+    }
+    return p;
+}
+
+/* dump the content of a query of packet to the log */
+static void
+XLOG_BYTES( const void*  base, int  len )
+{
+    char  buff[1024];
+    char*  p = buff, *end = p + sizeof(buff);
+
+    p = _bprint_hexdump(p, end, base, len);
+    XLOG("%s",buff);
+}
+
+#else /* !DEBUG */
+#  define  XLOG(...)        ((void)0)
+#  define  XLOG_BYTES(a,b)  ((void)0)
+#endif
+
+static time_t
+_time_now( void )
+{
+    struct timeval  tv;
+
+    gettimeofday( &tv, NULL );
+    return tv.tv_sec;
+}
+
+/* reminder: the general format of a DNS packet is the following:
+ *
+ *    HEADER  (12 bytes)
+ *    QUESTION  (variable)
+ *    ANSWER (variable)
+ *    AUTHORITY (variable)
+ *    ADDITIONNAL (variable)
+ *
+ * the HEADER is made of:
+ *
+ *   ID     : 16 : 16-bit unique query identification field
+ *
+ *   QR     :  1 : set to 0 for queries, and 1 for responses
+ *   Opcode :  4 : set to 0 for queries
+ *   AA     :  1 : set to 0 for queries
+ *   TC     :  1 : truncation flag, will be set to 0 in queries
+ *   RD     :  1 : recursion desired
+ *
+ *   RA     :  1 : recursion available (0 in queries)
+ *   Z      :  3 : three reserved zero bits
+ *   RCODE  :  4 : response code (always 0=NOERROR in queries)
+ *
+ *   QDCount: 16 : question count
+ *   ANCount: 16 : Answer count (0 in queries)
+ *   NSCount: 16: Authority Record count (0 in queries)
+ *   ARCount: 16: Additionnal Record count (0 in queries)
+ *
+ * the QUESTION is made of QDCount Question Record (QRs)
+ * the ANSWER is made of ANCount RRs
+ * the AUTHORITY is made of NSCount RRs
+ * the ADDITIONNAL is made of ARCount RRs
+ *
+ * Each Question Record (QR) is made of:
+ *
+ *   QNAME   : variable : Query DNS NAME
+ *   TYPE    : 16       : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255)
+ *   CLASS   : 16       : class of query (IN=1)
+ *
+ * Each Resource Record (RR) is made of:
+ *
+ *   NAME    : variable : DNS NAME
+ *   TYPE    : 16       : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255)
+ *   CLASS   : 16       : class of query (IN=1)
+ *   TTL     : 32       : seconds to cache this RR (0=none)
+ *   RDLENGTH: 16       : size of RDDATA in bytes
+ *   RDDATA  : variable : RR data (depends on TYPE)
+ *
+ * Each QNAME contains a domain name encoded as a sequence of 'labels'
+ * terminated by a zero. Each label has the following format:
+ *
+ *    LEN  : 8     : lenght of label (MUST be < 64)
+ *    NAME : 8*LEN : label length (must exclude dots)
+ *
+ * A value of 0 in the encoding is interpreted as the 'root' domain and
+ * terminates the encoding. So 'www.android.com' will be encoded as:
+ *
+ *   <3>www<7>android<3>com<0>
+ *
+ * Where <n> represents the byte with value 'n'
+ *
+ * Each NAME reflects the QNAME of the question, but has a slightly more
+ * complex encoding in order to provide message compression. This is achieved
+ * by using a 2-byte pointer, with format:
+ *
+ *    TYPE   : 2  : 0b11 to indicate a pointer, 0b01 and 0b10 are reserved
+ *    OFFSET : 14 : offset to another part of the DNS packet
+ *
+ * The offset is relative to the start of the DNS packet and must point
+ * A pointer terminates the encoding.
+ *
+ * The NAME can be encoded in one of the following formats:
+ *
+ *   - a sequence of simple labels terminated by 0 (like QNAMEs)
+ *   - a single pointer
+ *   - a sequence of simple labels terminated by a pointer
+ *
+ * A pointer shall always point to either a pointer of a sequence of
+ * labels (which can themselves be terminated by either a 0 or a pointer)
+ *
+ * The expanded length of a given domain name should not exceed 255 bytes.
+ *
+ * NOTE: we don't parse the answer packets, so don't need to deal with NAME
+ *       records, only QNAMEs.
+ */
+
+#define  DNS_HEADER_SIZE  12
+
+#define  DNS_TYPE_A   "\00\01"   /* big-endian decimal 1 */
+#define  DNS_TYPE_PTR "\00\014"  /* big-endian decimal 12 */
+#define  DNS_TYPE_MX  "\00\017"  /* big-endian decimal 15 */
+#define  DNS_TYPE_AAAA "\00\034" /* big-endian decimal 28 */
+#define  DNS_TYPE_ALL "\00\0377" /* big-endian decimal 255 */
+
+#define  DNS_CLASS_IN "\00\01"   /* big-endian decimal 1 */
+
+typedef struct {
+    const uint8_t*  base;
+    const uint8_t*  end;
+    const uint8_t*  cursor;
+} DnsPacket;
+
+static void
+_dnsPacket_init( DnsPacket*  packet, const uint8_t*  buff, int  bufflen )
+{
+    packet->base   = buff;
+    packet->end    = buff + bufflen;
+    packet->cursor = buff;
+}
+
+static void
+_dnsPacket_rewind( DnsPacket*  packet )
+{
+    packet->cursor = packet->base;
+}
+
+static void
+_dnsPacket_skip( DnsPacket*  packet, int  count )
+{
+    const uint8_t*  p = packet->cursor + count;
+
+    if (p > packet->end)
+        p = packet->end;
+
+    packet->cursor = p;
+}
+
+static int
+_dnsPacket_readInt16( DnsPacket*  packet )
+{
+    const uint8_t*  p = packet->cursor;
+
+    if (p+2 > packet->end)
+        return -1;
+
+    packet->cursor = p+2;
+    return (p[0]<< 8) | p[1];
+}
+
+/** QUERY CHECKING
+ **/
+
+/* check bytes in a dns packet. returns 1 on success, 0 on failure.
+ * the cursor is only advanced in the case of success
+ */
+static int
+_dnsPacket_checkBytes( DnsPacket*  packet, int  numBytes, const void*  bytes )
+{
+    const uint8_t*  p = packet->cursor;
+
+    if (p + numBytes > packet->end)
+        return 0;
+
+    if (memcmp(p, bytes, numBytes) != 0)
+        return 0;
+
+    packet->cursor = p + numBytes;
+    return 1;
+}
+
+/* parse and skip a given QNAME stored in a query packet,
+ * from the current cursor position. returns 1 on success,
+ * or 0 for malformed data.
+ */
+static int
+_dnsPacket_checkQName( DnsPacket*  packet )
+{
+    const uint8_t*  p   = packet->cursor;
+    const uint8_t*  end = packet->end;
+
+    for (;;) {
+        int  c;
+
+        if (p >= end)
+            break;
+
+        c = *p++;
+
+        if (c == 0) {
+            packet->cursor = p;
+            return 1;
+        }
+
+        /* we don't expect label compression in QNAMEs */
+        if (c >= 64)
+            break;
+
+        p += c;
+        /* we rely on the bound check at the start
+         * of the loop here */
+    }
+    /* malformed data */
+    XLOG("malformed QNAME");
+    return 0;
+}
+
+/* parse and skip a given QR stored in a packet.
+ * returns 1 on success, and 0 on failure
+ */
+static int
+_dnsPacket_checkQR( DnsPacket*  packet )
+{
+    int  len;
+
+    if (!_dnsPacket_checkQName(packet))
+        return 0;
+
+    /* TYPE must be one of the things we support */
+    if (!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_A) &&
+        !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_PTR) &&
+        !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_MX) &&
+        !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_AAAA) &&
+        !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_ALL))
+    {
+        XLOG("unsupported TYPE");
+        return 0;
+    }
+    /* CLASS must be IN */
+    if (!_dnsPacket_checkBytes(packet, 2, DNS_CLASS_IN)) {
+        XLOG("unsupported CLASS");
+        return 0;
+    }
+
+    return 1;
+}
+
+/* check the header of a DNS Query packet, return 1 if it is one
+ * type of query we can cache, or 0 otherwise
+ */
+static int
+_dnsPacket_checkQuery( DnsPacket*  packet )
+{
+    const uint8_t*  p = packet->base;
+    int             qdCount, anCount, dnCount, arCount;
+
+    if (p + DNS_HEADER_SIZE > packet->end) {
+        XLOG("query packet too small");
+        return 0;
+    }
+
+    /* QR must be set to 0, opcode must be 0 and AA must be 0 */
+    /* RA, Z, and RCODE must be 0 */
+    if ((p[2] & 0xFC) != 0 || p[3] != 0) {
+        XLOG("query packet flags unsupported");
+        return 0;
+    }
+
+    /* Note that we ignore the TC and RD bits here for the
+     * following reasons:
+     *
+     * - there is no point for a query packet sent to a server
+     *   to have the TC bit set, but the implementation might
+     *   set the bit in the query buffer for its own needs
+     *   between a _resolv_cache_lookup and a
+     *   _resolv_cache_add. We should not freak out if this
+     *   is the case.
+     *
+     * - we consider that the result from a RD=0 or a RD=1
+     *   query might be different, hence that the RD bit
+     *   should be used to differentiate cached result.
+     *
+     *   this implies that RD is checked when hashing or
+     *   comparing query packets, but not TC
+     */
+
+    /* ANCOUNT, DNCOUNT and ARCOUNT must be 0 */
+    qdCount = (p[4] << 8) | p[5];
+    anCount = (p[6] << 8) | p[7];
+    dnCount = (p[8] << 8) | p[9];
+    arCount = (p[10]<< 8) | p[11];
+
+    if (anCount != 0 || dnCount != 0 || arCount != 0) {
+        XLOG("query packet contains non-query records");
+        return 0;
+    }
+
+    if (qdCount == 0) {
+        XLOG("query packet doesn't contain query record");
+        return 0;
+    }
+
+    /* Check QDCOUNT QRs */
+    packet->cursor = p + DNS_HEADER_SIZE;
+
+    for (;qdCount > 0; qdCount--)
+        if (!_dnsPacket_checkQR(packet))
+            return 0;
+
+    return 1;
+}
+
+/** QUERY DEBUGGING
+ **/
+#if DEBUG
+static char*
+_dnsPacket_bprintQName(DnsPacket*  packet, char*  bp, char*  bend)
+{
+    const uint8_t*  p   = packet->cursor;
+    const uint8_t*  end = packet->end;
+    int             first = 1;
+
+    for (;;) {
+        int  c;
+
+        if (p >= end)
+            break;
+
+        c = *p++;
+
+        if (c == 0) {
+            packet->cursor = p;
+            return bp;
+        }
+
+        /* we don't expect label compression in QNAMEs */
+        if (c >= 64)
+            break;
+
+        if (first)
+            first = 0;
+        else
+            bp = _bprint_c(bp, bend, '.');
+
+        bp = _bprint_b(bp, bend, (const char*)p, c);
+
+        p += c;
+        /* we rely on the bound check at the start
+         * of the loop here */
+    }
+    /* malformed data */
+    bp = _bprint_s(bp, bend, "<MALFORMED>");
+    return bp;
+}
+
+static char*
+_dnsPacket_bprintQR(DnsPacket*  packet, char*  p, char*  end)
+{
+#define  QQ(x)   { DNS_TYPE_##x, #x }
+    static const struct { 
+        const char*  typeBytes;
+        const char*  typeString; 
+    } qTypes[] =
+    {
+        QQ(A), QQ(PTR), QQ(MX), QQ(AAAA), QQ(ALL),
+        { NULL, NULL }
+    };
+    int          nn;
+    const char*  typeString = NULL;
+
+    /* dump QNAME */
+    p = _dnsPacket_bprintQName(packet, p, end);
+
+    /* dump TYPE */
+    p = _bprint_s(p, end, " (");
+
+    for (nn = 0; qTypes[nn].typeBytes != NULL; nn++) {
+        if (_dnsPacket_checkBytes(packet, 2, qTypes[nn].typeBytes)) {
+            typeString = qTypes[nn].typeString;
+            break;
+        }
+    }
+
+    if (typeString != NULL)
+        p = _bprint_s(p, end, typeString);
+    else {
+        int  typeCode = _dnsPacket_readInt16(packet);
+        p = _bprint(p, end, "UNKNOWN-%d", typeCode);
+    }
+
+    p = _bprint_c(p, end, ')');
+
+    /* skip CLASS */
+    _dnsPacket_skip(packet, 2);
+    return p;
+}
+
+/* this function assumes the packet has already been checked */
+static char*
+_dnsPacket_bprintQuery( DnsPacket*  packet, char*  p, char*  end )
+{
+    int   qdCount;
+
+    if (packet->base[2] & 0x1) {
+        p = _bprint_s(p, end, "RECURSIVE ");
+    }
+
+    _dnsPacket_skip(packet, 4);
+    qdCount = _dnsPacket_readInt16(packet);
+    _dnsPacket_skip(packet, 6);
+
+    for ( ; qdCount > 0; qdCount-- ) {
+        p = _dnsPacket_bprintQR(packet, p, end);
+    }
+    return p;
+}
+#endif
+
+
+/** QUERY HASHING SUPPORT
+ **
+ ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKET HAS ALREADY
+ ** BEEN SUCCESFULLY CHECKED.
+ **/
+
+/* use 32-bit FNV hash function */
+#define  FNV_MULT   16777619U
+#define  FNV_BASIS  2166136261U
+
+static unsigned
+_dnsPacket_hashBytes( DnsPacket*  packet, int  numBytes, unsigned  hash )
+{
+    const uint8_t*  p   = packet->cursor;
+    const uint8_t*  end = packet->end;
+
+    while (numBytes > 0 && p < end) {
+        hash = hash*FNV_MULT ^ *p++;
+    }
+    packet->cursor = p;
+    return hash;
+}
+
+
+static unsigned
+_dnsPacket_hashQName( DnsPacket*  packet, unsigned  hash )
+{
+    const uint8_t*  p   = packet->cursor;
+    const uint8_t*  end = packet->end;
+
+    for (;;) {
+        int  c;
+
+        if (p >= end) {  /* should not happen */
+            XLOG("%s: INTERNAL_ERROR: read-overflow !!\n", __FUNCTION__);
+            break;
+        }
+
+        c = *p++;
+
+        if (c == 0)
+            break;
+
+        if (c >= 64) {
+            XLOG("%s: INTERNAL_ERROR: malformed domain !!\n", __FUNCTION__);
+            break;
+        }
+        if (p + c >= end) {
+            XLOG("%s: INTERNAL_ERROR: simple label read-overflow !!\n",
+                    __FUNCTION__);
+            break;
+        }
+        while (c > 0) {
+            hash = hash*FNV_MULT ^ *p++;
+            c   -= 1;
+        }
+    }
+    packet->cursor = p;
+    return hash;
+}
+
+static unsigned
+_dnsPacket_hashQR( DnsPacket*  packet, unsigned  hash )
+{
+    int   len;
+
+    hash = _dnsPacket_hashQName(packet, hash);
+    hash = _dnsPacket_hashBytes(packet, 4, hash); /* TYPE and CLASS */
+    return hash;
+}
+
+static unsigned
+_dnsPacket_hashQuery( DnsPacket*  packet )
+{
+    unsigned  hash = FNV_BASIS;
+    int       count;
+    _dnsPacket_rewind(packet);
+
+    /* we ignore the TC bit for reasons explained in
+     * _dnsPacket_checkQuery().
+     *
+     * however we hash the RD bit to differentiate
+     * between answers for recursive and non-recursive
+     * queries.
+     */
+    hash = hash*FNV_MULT ^ (packet->base[2] & 1);
+
+    /* assume: other flags are 0 */
+    _dnsPacket_skip(packet, 4);
+
+    /* read QDCOUNT */
+    count = _dnsPacket_readInt16(packet);
+
+    /* assume: ANcount, NScount, ARcount are 0 */
+    _dnsPacket_skip(packet, 6);
+
+    /* hash QDCOUNT QRs */
+    for ( ; count > 0; count-- )
+        hash = _dnsPacket_hashQR(packet, hash);
+
+    return hash;
+}
+
+
+/** QUERY COMPARISON
+ **
+ ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKETS HAVE ALREADY
+ ** BEEN SUCCESFULLY CHECKED.
+ **/
+
+static int
+_dnsPacket_isEqualDomainName( DnsPacket*  pack1, DnsPacket*  pack2 )
+{
+    const uint8_t*  p1   = pack1->cursor;
+    const uint8_t*  end1 = pack1->end;
+    const uint8_t*  p2   = pack2->cursor;
+    const uint8_t*  end2 = pack2->end;
+
+    for (;;) {
+        int  c1, c2;
+
+        if (p1 >= end1 || p2 >= end2) {
+            XLOG("%s: INTERNAL_ERROR: read-overflow !!\n", __FUNCTION__);
+            break;
+        }
+        c1 = *p1++;
+        c2 = *p2++;
+        if (c1 != c2)
+            break;
+
+        if (c1 == 0) {
+            pack1->cursor = p1;
+            pack2->cursor = p2;
+            return 1;
+        }
+        if (c1 >= 64) {
+            XLOG("%s: INTERNAL_ERROR: malformed domain !!\n", __FUNCTION__);
+            break;
+        }
+        if ((p1+c1 > end1) || (p2+c1 > end2)) {
+            XLOG("%s: INTERNAL_ERROR: simple label read-overflow !!\n",
+                    __FUNCTION__);
+            break;
+        }
+        if (memcmp(p1, p2, c1) != 0)
+            break;
+        p1 += c1;
+        p2 += c1;
+        /* we rely on the bound checks at the start of the loop */
+    }
+    /* not the same, or one is malformed */
+    XLOG("different DN");
+    return 0;
+}
+
+static int
+_dnsPacket_isEqualBytes( DnsPacket*  pack1, DnsPacket*  pack2, int  numBytes )
+{
+    const uint8_t*  p1 = pack1->cursor;
+    const uint8_t*  p2 = pack2->cursor;
+
+    if ( p1 + numBytes > pack1->end || p2 + numBytes > pack2->end )
+        return 0;
+
+    if ( memcmp(p1, p2, numBytes) != 0 )
+        return 0;
+
+    pack1->cursor += numBytes;
+    pack2->cursor += numBytes;
+    return 1;
+}
+
+static int
+_dnsPacket_isEqualQR( DnsPacket*  pack1, DnsPacket*  pack2 )
+{
+    /* compare domain name encoding + TYPE + CLASS */
+    if ( !_dnsPacket_isEqualDomainName(pack1, pack2) ||
+         !_dnsPacket_isEqualBytes(pack1, pack2, 2+2) )
+        return 0;
+
+    return 1;
+}
+
+static int
+_dnsPacket_isEqualQuery( DnsPacket*  pack1, DnsPacket*  pack2 )
+{
+    int  count1, count2;
+
+    /* compare the headers, ignore most fields */
+    _dnsPacket_rewind(pack1);
+    _dnsPacket_rewind(pack2);
+
+    /* compare RD, ignore TC, see comment in _dnsPacket_checkQuery */
+    if ((pack1->base[2] & 1) != (pack2->base[2] & 1)) {
+        XLOG("different RD");
+        return 0;
+    }
+
+    /* assume: other flags are all 0 */
+    _dnsPacket_skip(pack1, 4);
+    _dnsPacket_skip(pack2, 4);
+
+    /* compare QDCOUNT */
+    count1 = _dnsPacket_readInt16(pack1);
+    count2 = _dnsPacket_readInt16(pack2);
+    if (count1 != count2 || count1 < 0) {
+        XLOG("different QDCOUNT");
+        return 0;
+    }
+
+    /* assume: ANcount, NScount and ARcount are all 0 */
+    _dnsPacket_skip(pack1, 6);
+    _dnsPacket_skip(pack2, 6);
+
+    /* compare the QDCOUNT QRs */
+    for ( ; count1 > 0; count1-- ) {
+        if (!_dnsPacket_isEqualQR(pack1, pack2)) {
+            XLOG("different QR");
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/****************************************************************************/
+/****************************************************************************/
+
+/* cache entry. for simplicity, 'hash' and 'hlink' are inlined in this
+ * structure though they are conceptually part of the hash table.
+ *
+ * similarly, mru_next and mru_prev are part of the global MRU list
+ */
+typedef struct Entry {
+    unsigned int     hash;   /* hash value */
+    struct Entry*    hlink;  /* next in collision chain */
+    struct Entry*    mru_prev;
+    struct Entry*    mru_next;
+
+    const uint8_t*   query;
+    int              querylen;
+    const uint8_t*   answer;
+    int              answerlen;
+    time_t           when;   /* time_t when entry was added to table */
+    int              id;     /* for debugging purpose */
+} Entry;
+
+
+static void
+entry_free( Entry*  e )
+{
+    /* everything is allocated in a single memory block */
+    if (e) {
+        free(e);
+    }
+}
+
+static __inline__ void
+entry_mru_remove( Entry*  e )
+{
+    e->mru_prev->mru_next = e->mru_next;
+    e->mru_next->mru_prev = e->mru_prev;
+}
+
+static __inline__ void
+entry_mru_add( Entry*  e, Entry*  list )
+{
+    Entry*  first = list->mru_next;
+
+    e->mru_next = first;
+    e->mru_prev = list;
+
+    list->mru_next  = e;
+    first->mru_prev = e;
+}
+
+/* compute the hash of a given entry, this is a hash of most
+ * data in the query (key) */
+static unsigned
+entry_hash( const Entry*  e )
+{
+    DnsPacket  pack[1];
+
+    _dnsPacket_init(pack, e->query, e->querylen);
+    return _dnsPacket_hashQuery(pack);
+}
+
+/* initialize an Entry as a search key, this also checks the input query packet
+ * returns 1 on success, or 0 in case of unsupported/malformed data */
+static int
+entry_init_key( Entry*  e, const void*  query, int  querylen )
+{
+    DnsPacket  pack[1];
+
+    memset(e, 0, sizeof(*e));
+
+    e->query    = query;
+    e->querylen = querylen;
+    e->hash     = entry_hash(e);
+
+    _dnsPacket_init(pack, query, querylen);
+
+    return _dnsPacket_checkQuery(pack);
+}
+
+/* allocate a new entry as a cache node */
+static Entry*
+entry_alloc( const Entry*  init, const void*  answer, int  answerlen )
+{
+    Entry*  e;
+    int     size;
+
+    size = sizeof(*e) + init->querylen + answerlen;
+    e    = calloc(size, 1);
+    if (e == NULL)
+        return e;
+
+    e->hash     = init->hash;
+    e->query    = (const uint8_t*)(e+1);
+    e->querylen = init->querylen;
+
+    memcpy( (char*)e->query, init->query, e->querylen );
+
+    e->answer    = e->query + e->querylen;
+    e->answerlen = answerlen;
+
+    memcpy( (char*)e->answer, answer, e->answerlen );
+
+    e->when  = _time_now();
+
+    return e;
+}
+
+static int
+entry_equals( const Entry*  e1, const Entry*  e2 )
+{
+    DnsPacket  pack1[1], pack2[1];
+
+    if (e1->querylen != e2->querylen) {
+        return 0;
+    }
+    _dnsPacket_init(pack1, e1->query, e1->querylen);
+    _dnsPacket_init(pack2, e2->query, e2->querylen);
+
+    return _dnsPacket_isEqualQuery(pack1, pack2);
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/****************************************************************************/
+/****************************************************************************/
+
+/* We use a simple hash table with external collision lists
+ * for simplicity, the hash-table fields 'hash' and 'hlink' are
+ * inlined in the Entry structure.
+ */
+#define  MAX_HASH_ENTRIES   (2*CONFIG_MAX_ENTRIES)
+
+typedef struct resolv_cache {
+    int              num_entries;
+    Entry            mru_list;
+    pthread_mutex_t  lock;
+    unsigned         generation;
+    int              last_id;
+    Entry*           entries[ MAX_HASH_ENTRIES ];
+} Cache;
+
+
+#define  HTABLE_VALID(x)  ((x) != NULL && (x) != HTABLE_DELETED)
+
+static void
+_cache_flush_locked( Cache*  cache )
+{
+    int     nn;
+    time_t  now = _time_now();
+
+    for (nn = 0; nn < MAX_HASH_ENTRIES; nn++) 
+    {
+        Entry**  pnode = &cache->entries[nn];
+
+        while (*pnode != NULL) {
+            Entry*  node = *pnode;
+            *pnode = node->hlink;
+            entry_free(node);
+        }
+    }
+
+    cache->mru_list.mru_next = cache->mru_list.mru_prev = &cache->mru_list;
+    cache->num_entries       = 0;
+    cache->last_id           = 0;
+
+    XLOG("*************************\n"
+         "*** DNS CACHE FLUSHED ***\n"
+         "*************************");
+}
+
+struct resolv_cache*
+_resolv_cache_create( void )
+{
+    struct resolv_cache*  cache;
+
+    cache = calloc(sizeof(*cache), 1);
+    if (cache) {
+        cache->generation = ~0U;
+        pthread_mutex_init( &cache->lock, NULL );
+        cache->mru_list.mru_prev = cache->mru_list.mru_next = &cache->mru_list;
+        XLOG("%s: cache created\n", __FUNCTION__);
+    }
+    return cache;
+}
+
+
+#if DEBUG
+static void
+_dump_query( const uint8_t*  query, int  querylen )
+{
+    char       temp[256], *p=temp, *end=p+sizeof(temp);
+    DnsPacket  pack[1];
+
+    _dnsPacket_init(pack, query, querylen);
+    p = _dnsPacket_bprintQuery(pack, p, end);
+    XLOG("QUERY: %s", temp);
+}
+
+static void
+_cache_dump_mru( Cache*  cache )
+{
+    char    temp[512], *p=temp, *end=p+sizeof(temp);
+    Entry*  e;
+
+    p = _bprint(temp, end, "MRU LIST (%2d): ", cache->num_entries);
+    for (e = cache->mru_list.mru_next; e != &cache->mru_list; e = e->mru_next)
+        p = _bprint(p, end, " %d", e->id);
+
+    XLOG("%s", temp);
+}
+#endif
+
+#if DEBUG
+#  define  XLOG_QUERY(q,len)   _dump_query((q), (len))
+#else
+#  define  XLOG_QUERY(q,len)   ((void)0)
+#endif
+
+/* This function tries to find a key within the hash table
+ * In case of success, it will return a *pointer* to the hashed key.
+ * In case of failure, it will return a *pointer* to NULL
+ *
+ * So, the caller must check '*result' to check for success/failure.
+ *
+ * The main idea is that the result can later be used directly in
+ * calls to _resolv_cache_add or _resolv_cache_remove as the 'lookup'
+ * parameter. This makes the code simpler and avoids re-searching
+ * for the key position in the htable.
+ *
+ * The result of a lookup_p is only valid until you alter the hash
+ * table.
+ */
+static Entry**
+_cache_lookup_p( Cache*   cache,
+                 Entry*   key )
+{
+    int      index = key->hash % MAX_HASH_ENTRIES;
+    Entry**  pnode = &cache->entries[ key->hash % MAX_HASH_ENTRIES ];
+
+    while (*pnode != NULL) {
+        Entry*  node = *pnode;
+
+        if (node == NULL)
+            break;
+
+        if (node->hash == key->hash && entry_equals(node, key))
+            break;
+
+        pnode = &node->hlink;
+    }
+    return pnode; 
+}
+
+/* Add a new entry to the hash table. 'lookup' must be the
+ * result of an immediate previous failed _lookup_p() call
+ * (i.e. with *lookup == NULL), and 'e' is the pointer to the
+ * newly created entry
+ */
+static void
+_cache_add_p( Cache*   cache,
+              Entry**  lookup,
+              Entry*   e )
+{
+    *lookup = e;
+    e->id = ++cache->last_id;
+    entry_mru_add(e, &cache->mru_list);
+    cache->num_entries += 1;
+
+    XLOG("%s: entry %d added (count=%d)", __FUNCTION__,
+         e->id, cache->num_entries);
+}
+
+/* Remove an existing entry from the hash table,
+ * 'lookup' must be the result of an immediate previous
+ * and succesful _lookup_p() call.
+ */
+static void
+_cache_remove_p( Cache*   cache,
+                 Entry**  lookup )
+{
+    Entry*  e  = *lookup;
+
+    XLOG("%s: entry %d removed (count=%d)", __FUNCTION__,
+         e->id, cache->num_entries-1);
+
+    entry_mru_remove(e);
+    *lookup = e->hlink;
+    entry_free(e);
+    cache->num_entries -= 1;
+}
+
+/* Remove the oldest entry from the hash table.
+ */
+static void
+_cache_remove_oldest( Cache*  cache )
+{
+    Entry*   oldest = cache->mru_list.mru_prev;
+    Entry**  lookup = _cache_lookup_p(cache, oldest);
+
+    if (*lookup == NULL) { /* should not happen */
+        XLOG("%s: OLDEST NOT IN HTABLE ?", __FUNCTION__);
+        return;
+    }
+    _cache_remove_p(cache, lookup);
+}
+
+
+ResolvCacheStatus
+_resolv_cache_lookup( struct resolv_cache*  cache,
+                      const void*           query,
+                      int                   querylen,
+                      void*                 answer,
+                      int                   answersize,
+                      int                  *answerlen )
+{
+    DnsPacket  pack[1];
+    Entry      key[1];
+    int        index;
+    Entry**    lookup;
+    Entry*     e;
+    time_t     now;
+
+    ResolvCacheStatus  result = RESOLV_CACHE_NOTFOUND;
+
+    XLOG("%s: lookup", __FUNCTION__);
+    XLOG_QUERY(query, querylen);
+
+    /* we don't cache malformed queries */
+    if (!entry_init_key(key, query, querylen)) {
+        XLOG("%s: unsupported query", __FUNCTION__);
+        return RESOLV_CACHE_UNSUPPORTED;
+    }
+    /* lookup cache */
+    pthread_mutex_lock( &cache->lock );
+
+    /* see the description of _lookup_p to understand this.
+     * the function always return a non-NULL pointer.
+     */
+    lookup = _cache_lookup_p(cache, key);
+    e      = *lookup;
+
+    if (e == NULL) {
+        XLOG( "NOT IN CACHE");
+        goto Exit;
+    }
+
+    now = _time_now();
+
+    /* remove stale entries here */
+    if ( (unsigned)(now - e->when) >= CONFIG_SECONDS ) {
+        XLOG( " NOT IN CACHE (STALE ENTRY %p DISCARDED)", *lookup );
+        _cache_remove_p(cache, lookup);
+        goto Exit;
+    }
+
+    *answerlen = e->answerlen;
+    if (e->answerlen > answersize) {
+        /* NOTE: we return UNSUPPORTED if the answer buffer is too short */
+        result = RESOLV_CACHE_UNSUPPORTED;
+        XLOG(" ANSWER TOO LONG");
+        goto Exit;
+    }
+
+    memcpy( answer, e->answer, e->answerlen );
+
+    /* bump up this entry to the top of the MRU list */
+    if (e != cache->mru_list.mru_next) {
+        entry_mru_remove( e );
+        entry_mru_add( e, &cache->mru_list );
+    }
+
+    XLOG( "FOUND IN CACHE entry=%p", e );
+    result = RESOLV_CACHE_FOUND;
+
+Exit:
+    pthread_mutex_unlock( &cache->lock );
+    return result;
+}
+
+
+void
+_resolv_cache_add( struct resolv_cache*  cache,
+                   const void*           query,
+                   int                   querylen,
+                   const void*           answer,
+                   int                   answerlen )
+{
+    Entry    key[1];
+    Entry*   e;
+    Entry**  lookup;
+
+    /* don't assume that the query has already been cached
+     */
+    if (!entry_init_key( key, query, querylen )) {
+        XLOG( "%s: passed invalid query ?", __FUNCTION__);
+        return;
+    }
+
+    pthread_mutex_lock( &cache->lock );
+
+    XLOG( "%s: query:", __FUNCTION__ );
+    XLOG_QUERY(query,querylen);
+#if DEBUG_DATA
+    XLOG( "answer:");
+    XLOG_BYTES(answer,answerlen);
+#endif
+
+    lookup = _cache_lookup_p(cache, key);
+    e      = *lookup;
+
+    if (e != NULL) { /* should not happen */
+        XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD",
+             __FUNCTION__, e);
+        goto Exit;
+    }
+
+    if (cache->num_entries >= CONFIG_MAX_ENTRIES) {
+        _cache_remove_oldest(cache);
+        /* need to lookup again */
+        lookup = _cache_lookup_p(cache, key);
+        e      = *lookup;
+        if (e != NULL) {
+            XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD",
+                __FUNCTION__, e);
+            goto Exit;
+        }
+    }
+
+    e = entry_alloc( key, answer, answerlen );
+    if (e != NULL) {
+        _cache_add_p(cache, lookup, e);
+    }
+#if DEBUG
+    _cache_dump_mru(cache);
+#endif
+Exit:
+    pthread_mutex_unlock( &cache->lock );
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/*****                                                                  *****/
+/****************************************************************************/
+/****************************************************************************/
+
+static struct resolv_cache*  _res_cache;
+static pthread_once_t        _res_cache_once;
+
+static void
+_res_cache_init( void )
+{
+    const char*  env = getenv(CONFIG_ENV);
+
+    if (env && atoi(env) == 0) {
+        /* the cache is disabled */
+        return;
+    }
+
+    _res_cache = _resolv_cache_create();
+}
+
+
+struct resolv_cache*
+__get_res_cache( void )
+{
+    pthread_once( &_res_cache_once, _res_cache_init );
+    return _res_cache;
+}
+
+void
+_resolv_cache_reset( unsigned  generation )
+{
+    XLOG("%s: generation=%d", __FUNCTION__, generation);
+
+    if (_res_cache == NULL)
+        return;
+
+    pthread_mutex_lock( &_res_cache->lock );
+    if (_res_cache->generation != generation) {
+        _cache_flush_locked(_res_cache);
+        _res_cache->generation = generation;
+    }
+    pthread_mutex_unlock( &_res_cache->lock );
+}
diff --git a/libc/netbsd/resolv/res_comp.c b/libc/netbsd/resolv/res_comp.c
new file mode 100644
index 0000000..6991e8b
--- /dev/null
+++ b/libc/netbsd/resolv/res_comp.c
@@ -0,0 +1,259 @@
+/*	$NetBSD: res_comp.c,v 1.6 2004/05/22 23:47:09 christos Exp $	*/
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)res_comp.c	8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "Id: res_comp.c,v 1.1.2.1.4.1 2004/03/09 08:33:54 marka Exp";
+#else
+__RCSID("$NetBSD: res_comp.c,v 1.6 2004/05/22 23:47:09 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include "arpa_nameser.h"
+#include <ctype.h>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Expand compressed domain name 'src' to full domain name.
+ * 'msg' is a pointer to the begining of the message,
+ * 'eom' points to the first location after the message,
+ * 'dst' is a pointer to a buffer of size 'dstsiz' 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 pch = PERIOD, ch = *dn++;
+
+	while (ch != '\0') {
+		int nch = *dn++;
+
+		if (periodchar(ch)) {
+			;
+		} 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);
+		}
+		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 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*/
diff --git a/libc/netbsd/resolv/res_compat.c b/libc/netbsd/resolv/res_compat.c
new file mode 100644
index 0000000..19a1d5f
--- /dev/null
+++ b/libc/netbsd/resolv/res_compat.c
@@ -0,0 +1,87 @@
+/*	$NetBSD: res_compat.c,v 1.1 2004/06/09 18:07:03 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: res_compat.c,v 1.1 2004/06/09 18:07:03 christos Exp $");
+#endif
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include "arpa_nameser.h"
+#include <netdb.h>
+#include <string.h>
+#define __OLD_RES_STATE
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include "resolv.h"
+#endif
+
+#undef _res
+
+/*
+ * Binary Compatibility; this symbol does not appear in a header file
+ * Most userland programs use this to set res_options before res_init()
+ * is called. There are hooks to res_init() to consult the data in this
+ * structure. The hooks are provided indirectly by the two functions below.
+ * We depend on the fact the the first 440 [32 bit machines] bytes are
+ * shared between the two structures.
+ */
+#ifndef __BIND_NOSTATIC
+struct __res_state _res
+#if defined(__BIND_RES_TEXT)
+	= { RES_TIMEOUT, }      /* Motorola, et al. */
+# endif
+;
+
+void *__res_get_old_state(void);
+void __res_put_old_state(void *);
+
+void *
+__res_get_old_state(void)
+{
+	return &_res;
+}
+
+void
+__res_put_old_state(void *res)
+{
+	(void)memcpy(&_res, res, sizeof(_res));
+}
+#endif
diff --git a/libc/netbsd/resolv/res_data.c b/libc/netbsd/resolv/res_data.c
new file mode 100644
index 0000000..e60ecfd
--- /dev/null
+++ b/libc/netbsd/resolv/res_data.c
@@ -0,0 +1,332 @@
+/*	$NetBSD: res_data.c,v 1.8 2004/06/09 18:07:03 christos Exp $	*/
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char rcsid[] = "Id: res_data.c,v 1.1.206.2 2004/03/16 12:34:18 marka Exp";
+#else
+__RCSID("$NetBSD: res_data.c,v 1.8 2004/06/09 18:07:03 christos Exp $");
+#endif
+#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_private.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+const char * const _res_opcodes[] = {
+	"QUERY",
+	"IQUERY",
+	"CQUERYM",
+	"CQUERYU",	/* experimental */
+	"NOTIFY",	/* experimental */
+	"UPDATE",
+	"6",
+	"7",
+	"8",
+	"9",
+	"10",
+	"11",
+	"12",
+	"13",
+	"ZONEINIT",
+	"ZONEREF",
+};
+
+#ifdef BIND_UPDATE
+const char * const _res_sectioncodes[] = {
+	"ZONE",
+	"PREREQUISITES",
+	"UPDATE",
+	"ADDITIONAL",
+};
+#endif
+
+#ifndef __BIND_NOSTATIC
+extern struct __res_state _nres;
+
+/* Proto. */
+
+int  res_ourserver_p(const res_state, const struct sockaddr *);
+
+#ifdef ANDROID_CHANGES
+int res_need_init() {
+	return ((_nres.options & RES_INIT) == 0U) || res_get_dns_changed();
+}
+#else
+#define res_need_init()   ((_nres.options & RES_INIT) == 0U)
+#endif
+
+int
+res_init(void) {
+	int rv;
+	extern int __res_vinit(res_state, int);
+#ifdef COMPAT__RES
+	/*
+	 * Compatibility with program that were accessing _res directly
+	 * to set options. We keep another struct res that is the same
+	 * size as the original res structure, and then copy fields to
+	 * it so that we achieve the same initialization
+	 */
+	extern void *__res_get_old_state(void);
+	extern void __res_put_old_state(void *);
+	res_state ores = __res_get_old_state();
+
+	if (ores->options != 0)
+		_nres.options = ores->options;
+	if (ores->retrans != 0)
+		_nres.retrans = ores->retrans;
+	if (ores->retry != 0)
+		_nres.retry = ores->retry;
+#endif
+
+	/*
+	 * These three fields used to be statically initialized.  This made
+	 * it hard to use this code in a shared library.  It is necessary,
+	 * now that we're doing dynamic initialization here, that we preserve
+	 * the old semantics: if an application modifies one of these three
+	 * fields of _res before res_init() is called, res_init() will not
+	 * alter them.  Of course, if an application is setting them to
+	 * _zero_ before calling res_init(), hoping to override what used
+	 * to be the static default, we can't detect it and unexpected results
+	 * will follow.  Zero for any of these fields would make no sense,
+	 * so one can safely assume that the applications were already getting
+	 * unexpected results.
+	 *
+	 * _nres.options is tricky since some apps were known to diddle the bits
+	 * before res_init() was first called. We can't replicate that semantic
+	 * with dynamic initialization (they may have turned bits off that are
+	 * set in RES_DEFAULT).  Our solution is to declare such applications
+	 * "broken".  They could fool us by setting RES_INIT but none do (yet).
+	 */
+	if (!_nres.retrans)
+		_nres.retrans = RES_TIMEOUT;
+	if (!_nres.retry)
+		_nres.retry = 4;
+	if (!(_nres.options & RES_INIT))
+		_nres.options = RES_DEFAULT;
+
+	/*
+	 * This one used to initialize implicitly to zero, so unless the app
+	 * has set it to something in particular, we can randomize it now.
+	 */
+	if (!_nres.id)
+		_nres.id = res_randomid();
+
+	rv = __res_vinit(&_nres, 1);
+#ifdef COMPAT__RES
+	__res_put_old_state(&_nres);
+#endif
+	return rv;
+}
+
+void
+p_query(const u_char *msg) {
+	fp_query(msg, stdout);
+}
+
+void
+fp_query(const u_char *msg, FILE *file) {
+	fp_nquery(msg, PACKETSZ, file);
+}
+
+void
+fp_nquery(const u_char *msg, int len, FILE *file) {
+	if (res_need_init() && res_init() == -1)
+		return;
+
+	res_pquery(&_nres, msg, len, file);
+}
+
+int
+res_mkquery(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 */
+{
+	if (res_need_init() && res_init() == -1) {
+		RES_SET_H_ERRNO(&_nres, NETDB_INTERNAL);
+		return (-1);
+	}
+	return (res_nmkquery(&_nres, op, dname, class, type,
+			     data, datalen,
+			     newrr_in, buf, buflen));
+}
+
+#ifdef _LIBRESOLV
+int
+res_mkupdate(ns_updrec *rrecp_in, u_char *buf, int buflen) {
+	if (res_need_init() && res_init() == -1) {
+		RES_SET_H_ERRNO(&_nres, NETDB_INTERNAL);
+		return (-1);
+	}
+
+	return (res_nmkupdate(&_nres, rrecp_in, buf, buflen));
+}
+#endif
+
+int
+res_query(const char *name,	/* domain name */
+	  int class, int type,	/* class and type of query */
+	  u_char *answer,	/* buffer to put answer */
+	  int anslen)		/* size of answer buffer */
+{
+	if (res_need_init() && res_init() == -1) {
+		RES_SET_H_ERRNO(&_nres, NETDB_INTERNAL);
+		return (-1);
+	}
+	return (res_nquery(&_nres, name, class, type, answer, anslen));
+}
+
+void
+res_send_setqhook(res_send_qhook hook) {
+	_nres.qhook = hook;
+}
+
+void
+res_send_setrhook(res_send_rhook hook) {
+	_nres.rhook = hook;
+}
+
+int
+res_isourserver(const struct sockaddr_in *inp) {
+	return (res_ourserver_p(&_nres, (const struct sockaddr *)(const void *)inp));
+}
+
+int
+res_send(const u_char *buf, int buflen, u_char *ans, int anssiz) {
+	if (res_need_init() && res_init() == -1) {
+		/* errno should have been set by res_init() in this case. */
+		return (-1);
+	}
+
+	return (res_nsend(&_nres, buf, buflen, ans, anssiz));
+}
+
+#ifdef _LIBRESOLV
+int
+res_sendsigned(const u_char *buf, int buflen, ns_tsig_key *key,
+	       u_char *ans, int anssiz)
+{
+	if (res_need_init() && res_init() == -1) {
+		/* errno should have been set by res_init() in this case. */
+		return (-1);
+	}
+
+	return (res_nsendsigned(&_nres, buf, buflen, key, ans, anssiz));
+}
+#endif
+
+void
+res_close(void) {
+	res_nclose(&_nres);
+}
+
+#ifdef _LIBRESOLV
+int
+res_update(ns_updrec *rrecp_in) {
+	if (res_need_init() && res_init() == -1) {
+		RES_SET_H_ERRNO(&_nres, NETDB_INTERNAL);
+		return (-1);
+	}
+
+	return (res_nupdate(&_nres, rrecp_in, NULL));
+}
+#endif
+
+int
+res_search(const char *name,	/* domain name */
+	   int class, int type,	/* class and type of query */
+	   u_char *answer,	/* buffer to put answer */
+	   int anslen)		/* size of answer */
+{
+	if (res_need_init() && res_init() == -1) {
+		RES_SET_H_ERRNO(&_nres, NETDB_INTERNAL);
+		return (-1);
+	}
+
+	return (res_nsearch(&_nres, name, class, type, answer, anslen));
+}
+
+int
+res_querydomain(const char *name,
+		const char *domain,
+		int class, int type,	/* class and type of query */
+		u_char *answer,		/* buffer to put answer */
+		int anslen)		/* size of answer */
+{
+	if (res_need_init() && res_init() == -1) {
+		RES_SET_H_ERRNO(&_nres, NETDB_INTERNAL);
+		return (-1);
+	}
+
+	return (res_nquerydomain(&_nres, name, domain,
+				 class, type,
+				 answer, anslen));
+}
+
+int
+res_opt(int a, u_char *b, int c, int d)
+{
+	return res_nopt(&_nres, a, b, c, d);
+}
+
+const char *
+hostalias(const char *name) {
+	return NULL;
+}
+
+#ifdef ultrix
+int
+local_hostname_length(const char *hostname) {
+	int len_host, len_domain;
+
+	if (!*_nres.defdname)
+		res_init();
+	len_host = strlen(hostname);
+	len_domain = strlen(_nres.defdname);
+	if (len_host > len_domain &&
+	    !strcasecmp(hostname + len_host - len_domain, _nres.defdname) &&
+	    hostname[len_host - len_domain - 1] == '.')
+		return (len_host - len_domain - 1);
+	return (0);
+}
+#endif /*ultrix*/
+
+#endif
diff --git a/libc/netbsd/resolv/res_debug.c b/libc/netbsd/resolv/res_debug.c
new file mode 100644
index 0000000..84c6afc
--- /dev/null
+++ b/libc/netbsd/resolv/res_debug.c
@@ -0,0 +1,1174 @@
+/*	$NetBSD: res_debug.c,v 1.7 2004/11/07 02:25:01 christos Exp $	*/
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)res_debug.c	8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "Id: res_debug.c,v 1.3.2.5.4.5 2004/07/28 20:16:46 marka Exp";
+#else
+__RCSID("$NetBSD: res_debug.c,v 1.7 2004/11/07 02:25:01 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "arpa_nameser.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <netdb.h>
+#include "resolv_private.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
+
+static const char *precsize_ntoa(u_int32_t);
+
+extern const char * const _res_opcodes[];
+extern const char * const _res_sectioncodes[];
+
+#ifndef _LIBC
+/*
+ * Print the current options.
+ */
+void
+fp_resstat(const res_state statp, FILE *file) {
+	u_long mask;
+
+	fprintf(file, ";; res options:");
+	for (mask = 1;  mask != 0U;  mask <<= 1)
+		if (statp->options & mask)
+			fprintf(file, " %s", p_option(mask));
+	putc('\n', file);
+}
+#endif
+
+static void
+do_section(const res_state statp,
+	   ns_msg *handle, ns_sect section,
+	   int pflag, FILE *file)
+{
+	int n, sflag, rrnum;
+	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((size_t)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, (u_int)buflen);
+			if (n < 0) {
+				if (errno == ENOSPC) {
+					free(buf);
+					buf = NULL;
+					if (buflen < 131072)
+						buf = malloc((size_t)(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((int)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, (int)opcode), qdcount);
+		fprintf(file, ", %s: %d",
+			p_section(ns_s_an, (int)opcode), ancount);
+		fprintf(file, ", %s: %d",
+			p_section(ns_s_ns, (int)opcode), nscount);
+		fprintf(file, ", %s: %d",
+			p_section(ns_s_ar, (int)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[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, 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[MAXDNAME];
+	const u_char *n;
+
+	n = p_fqnname(cp, msg, 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 C_ANY is a qclass but not a class.  (You can ask for records of class
+ * C_ANY, but you can't have any records of that class in the database.)
+ */
+const struct res_sym __p_class_syms[] = {
+	{C_IN,		"IN",		(char *)0},
+	{C_CHAOS,	"CH",		(char *)0},
+	{C_CHAOS,	"CHAOS",	(char *)0},
+	{C_HS,		"HS",		(char *)0},
+	{C_HS,		"HESIOD",	(char *)0},
+	{C_ANY,		"ANY",		(char *)0},
+	{C_NONE,	"NONE",		(char *)0},
+	{C_IN, 		(char *)0,	(char *)0}
+};
+
+/*
+ * Names of message sections.
+ */
+const struct res_sym __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}
+};
+
+const struct res_sym __p_update_section_syms[] = {
+	{S_ZONE,	"ZONE",		(char *)0},
+	{S_PREREQ,	"PREREQUISITE",	(char *)0},
+	{S_UPDATE,	"UPDATE",	(char *)0},
+	{S_ADDT,	"ADDITIONAL",	(char *)0},
+	{0,             (char *)0,	(char *)0}
+};
+
+const struct res_sym __p_key_syms[] = {
+	{NS_ALG_MD5RSA,		"RSA",		"RSA KEY with MD5 hash"},
+	{NS_ALG_DH,		"DH",		"Diffie Hellman"},
+	{NS_ALG_DSA,		"DSA",		"Digital Signature Algorithm"},
+	{NS_ALG_EXPIRE_ONLY,	"EXPIREONLY",	"No algorithm"},
+	{NS_ALG_PRIVATE_OID,	"PRIVATE",	"Algorithm obtained from OID"},
+	{0,			NULL,		NULL}
+};
+
+const struct res_sym __p_cert_syms[] = {
+	{cert_t_pkix,	"PKIX",		"PKIX (X.509v3) Certificate"},
+	{cert_t_spki,	"SPKI",		"SPKI certificate"},
+	{cert_t_pgp,	"PGP",		"PGP certificate"},
+	{cert_t_url,	"URL",		"URL Private"},
+	{cert_t_oid,	"OID",		"OID Private"},
+	{0,		NULL,		NULL}
+};
+
+/*
+ * 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 __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 (; 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 (; 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 (; 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 > 0xffff)
+		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 = __p_update_section_syms;
+		break;
+	default:
+		symbols = __p_default_section_syms;
+		break;
+	}
+	return (sym_ntos(symbols, section, (int *)0));
+}
+
+/*
+ * Return a mnemonic for class.
+ */
+const char *
+p_class(int class) {
+	int success;
+	const char *result;
+	static char classbuf[20];
+
+	result = sym_ntos(__p_class_syms, class, &success);
+	if (success)
+		return (result);
+	if (class < 0 || class > 0xffff)
+		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_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
+#ifdef RES_NO_NIBBLE2
+	case RES_NO_NIBBLE2:	return "no-nibble2";
+#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((u_long)value, nbuf, sizeof nbuf) < 0)
+		sprintf(nbuf, "%u", value);
+	return (nbuf);
+}
+
+/*
+ * Return a string for the rcode.
+ */
+const char *
+p_rcode(int rcode) {
+	return (sym_ntos(__p_rcode_syms, rcode, (int *)0));
+}
+
+/*
+ * Return a string for a res_sockaddr_union.
+ */
+const char *
+p_sockun(union res_sockaddr_union u, char *buf, size_t size) {
+	char ret[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:123.123.123.123"];
+
+	switch (u.sin.sin_family) {
+	case AF_INET:
+		inet_ntop(AF_INET, &u.sin.sin_addr, ret, sizeof ret);
+		break;
+#ifdef HAS_INET6_STRUCTS
+	case AF_INET6:
+		inet_ntop(AF_INET6, &u.sin6.sin6_addr, ret, sizeof ret);
+		break;
+#endif
+	default:
+		sprintf(ret, "[af%d]", u.sin.sin_family);
+		break;
+	}
+	if (size > 0U) {
+		strncpy(buf, ret, size - 1);
+		buf[size - 1] = '0';
+	}
+	return (buf);
+}
+
+/*
+ * 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 const 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_int32_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;
+	PUTLONG(latit,bcp);
+	PUTLONG(longit,bcp);
+	PUTLONG(alt,bcp);
+
+	return (16);		/* size of RR in octets */
+}
+
+/* takes an on-the-wire LOC RR and formats it in a human readable format. */
+const char *
+loc_ntoa(binary, ascii)
+	const u_char *binary;
+	char *ascii;
+{
+	static 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++;
+
+	GETLONG(templ, cp);
+	latval = (templ - ((unsigned)1<<31));
+
+	GETLONG(templ, cp);
+	longval = (templ - ((unsigned)1<<31));
+
+	GETLONG(templ, cp);
+	if (templ < referencealt) { /* below WGS 84 spheroid */
+		altval = referencealt - templ;
+		altsign = "-";
+	} 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((u_int32_t)sizeval));
+	hpstr = strdup(precsize_ntoa((u_int32_t)hpval));
+	vpstr = strdup(precsize_ntoa((u_int32_t)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 myclock = secs;
+	struct tm *mytime;
+#ifdef HAVE_TIME_R
+	struct tm res;
+
+	mytime = gmtime_r(&myclock, &res);
+#else
+	mytime = gmtime(&myclock);
+#endif
+	mytime->tm_year += 1900;
+	mytime->tm_mon += 1;
+	sprintf(output, "%04d%02d%02d%02d%02d%02d",
+		mytime->tm_year, mytime->tm_mon, mytime->tm_mday,
+		mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
+	return (output);
+}
+
+u_int16_t
+res_nametoclass(const char *buf, int *successp) {
+	unsigned long result;
+	char *endptr;
+	int success;
+
+	result = sym_ston(__p_class_syms, buf, &success);
+	if (success)
+		goto done;
+
+	if (strncasecmp(buf, "CLASS", 5) != 0 ||
+	    !isdigit((unsigned char)buf[5]))
+		goto done;
+	errno = 0;
+	result = strtoul(buf + 5, &endptr, 10);
+	if (errno == 0 && *endptr == '\0' && result <= 0xffffU)
+		success = 1;
+ done:
+	if (successp)
+		*successp = success;
+	return (u_int16_t)(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;
+	errno = 0;
+	result = strtoul(buf + 4, &endptr, 10);
+	if (errno == 0 && *endptr == '\0' && result <= 0xffffU)
+		success = 1;
+ done:
+	if (successp)
+		*successp = success;
+	return (u_int16_t)(result);
+}
diff --git a/libc/netbsd/resolv/res_debug.h b/libc/netbsd/resolv/res_debug.h
new file mode 100644
index 0000000..4341c5a
--- /dev/null
+++ b/libc/netbsd/resolv/res_debug.h
@@ -0,0 +1,36 @@
+/*	$NetBSD: res_debug.h,v 1.1.1.1 2004/05/20 17:18:55 christos Exp $	*/
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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/libc/netbsd/resolv/res_init.c b/libc/netbsd/resolv/res_init.c
new file mode 100644
index 0000000..fc0272c
--- /dev/null
+++ b/libc/netbsd/resolv/res_init.c
@@ -0,0 +1,863 @@
+/*	$NetBSD: res_init.c,v 1.8 2006/03/19 03:10:08 christos Exp $	*/
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)res_init.c	8.1 (Berkeley) 6/7/93";
+static const char rcsid[] = "Id: res_init.c,v 1.9.2.5.4.2 2004/03/16 12:34:18 marka Exp";
+#else
+__RCSID("$NetBSD: res_init.c,v 1.8 2006/03/19 03:10:08 christos Exp $");
+#endif
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#ifdef ANDROID_CHANGES
+#include <sys/system_properties.h>
+#endif /* ANDROID_CHANGES */
+
+#ifndef MIN
+#define	MIN(x,y)	((x)<(y)?(x):(y))
+#endif
+
+/* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#define MAX_DNS_PROPERTIES 8
+#define DNS_PROP_NAME_PREFIX "net.dns"
+#define DNS_CHANGE_PROP_NAME "net.dnschange"
+const prop_info *dns_change_prop;
+int dns_last_change_counter;
+static int _get_dns_change_count();
+#else
+#include <resolv.h>
+#endif
+
+#include "res_private.h"
+
+/* Options.  Should all be left alone. */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+static void res_setoptions __P((res_state, const char *, const char *));
+
+static const char sort_mask[] = "/&";
+#define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL)
+static u_int32_t net_mask __P((struct in_addr));
+
+#if !defined(isascii)	/* XXX - could be a function */
+# define isascii(c) (!(c & 0200))
+#endif
+
+/*
+ * Resolver state default settings.
+ */
+
+/*
+ * 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_ninit(res_state statp) {
+	extern int __res_vinit(res_state, int);
+
+	return (__res_vinit(statp, 0));
+}
+
+/* This function has to be reachable by res_data.c but not publicly. */
+int
+__res_vinit(res_state statp, int preinit) {
+	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 nsort = 0;
+	char *net;
+	int dots;
+	union res_sockaddr_union u[2];
+#ifdef ANDROID_CHANGES
+        pid_t mypid = getpid();
+        int use_proc_props = 0;
+        int found_prop;
+	char dnsProperty[PROP_VALUE_MAX];
+#endif
+
+	if (!preinit) {
+		statp->retrans = RES_TIMEOUT;
+		statp->retry = RES_DFLRETRY;
+		statp->options = RES_DEFAULT;
+		statp->id = res_randomid();
+	}
+
+	if ((statp->options & RES_INIT) != 0U)
+		res_ndestroy(statp);
+
+	memset(u, 0, sizeof(u));
+#ifdef USELOOPBACK
+	u[nserv].sin.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
+#else
+	u[nserv].sin.sin_addr.s_addr = INADDR_ANY;
+#endif
+	u[nserv].sin.sin_family = AF_INET;
+	u[nserv].sin.sin_port = htons(NAMESERVER_PORT);
+#ifdef HAVE_SA_LEN
+	u[nserv].sin.sin_len = sizeof(struct sockaddr_in);
+#endif
+	nserv++;
+#ifdef HAS_INET6_STRUCTS
+#ifdef USELOOPBACK
+	u[nserv].sin6.sin6_addr = in6addr_loopback;
+#else
+	u[nserv].sin6.sin6_addr = in6addr_any;
+#endif
+	u[nserv].sin6.sin6_family = AF_INET6;
+	u[nserv].sin6.sin6_port = htons(NAMESERVER_PORT);
+#ifdef HAVE_SA_LEN
+	u[nserv].sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+	nserv++;
+#endif
+	statp->nscount = 0;
+	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 = malloc(sizeof(*statp->_u._ext.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");
+	}
+	statp->nsort = 0;
+	res_setservers(statp, u, nserv);
+
+#if 0 /* IGNORE THE ENVIRONMENT */
+	/* Allow user to override the local domain definition */
+	if ((cp = getenv("LOCALDOMAIN")) != NULL) {
+		(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')	/* silly backwards compat */
+				break;
+			else if (*cp == ' ' || *cp == '\t') {
+				*cp = 0;
+				n = 1;
+			} else if (n) {
+				*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;
+	}
+	if (nserv > 0)
+		statp->nscount = nserv;
+#endif
+#ifdef ANDROID_CHANGES /* READ FROM SYSTEM PROPERTIES */
+	dns_last_change_counter = _get_dns_change_count();
+
+	nserv = 0;
+	for(n = 1; n <= MAX_DNS_PROPERTIES && nserv < MAXNS; n++) {
+		char propname[PROP_NAME_MAX];
+		char propvalue[PROP_VALUE_MAX];
+
+		struct addrinfo hints, *ai;
+		char sbuf[NI_MAXSERV];
+		const size_t minsiz = sizeof(statp->_u._ext.ext->nsaddrs[0]);
+
+		/*
+		 * Check first for process-specific properties, and if those don't
+		 * exist, try the generic properties.
+		 */
+		found_prop = 0;
+		if (n == 1 || use_proc_props) {
+			snprintf(propname, sizeof(propname), "%s%d.%d", DNS_PROP_NAME_PREFIX, n, mypid);
+			if(__system_property_get(propname, propvalue) < 1) {
+				if (use_proc_props) {
+					break;
+				}
+			} else {
+				found_prop = 1;
+				use_proc_props = 1;
+			}
+		}
+		if (!found_prop) {
+			snprintf(propname, sizeof(propname), "%s%d", DNS_PROP_NAME_PREFIX, n);
+			if(__system_property_get(propname, propvalue) < 1) {
+				break;
+			}
+		}
+
+		cp = propvalue;
+
+		while (*cp == ' ' || *cp == '\t')
+			cp++;
+		cp[strcspn(cp, ";# \t\n")] = '\0';
+		if ((*cp != '\0') && (*cp != '\n')) {
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_family = PF_UNSPEC;
+			hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
+			hints.ai_flags = AI_NUMERICHOST;
+			sprintf(sbuf, "%u", NAMESERVER_PORT);
+			if (getaddrinfo(cp, sbuf, &hints, &ai) == 0 &&
+			    (size_t)ai->ai_addrlen <= minsiz) {
+				if (statp->_u._ext.ext != NULL) {
+					memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
+					       ai->ai_addr, ai->ai_addrlen);
+				}
+				if ((size_t)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++;
+			}
+		}
+	}
+#else /* IGNORE resolv.conf */
+#define	MATCH(line, name) \
+	(!strncmp(line, name, sizeof(name) - 1) && \
+	(line[sizeof(name) - 1] == ' ' || \
+	 line[sizeof(name) - 1] == '\t'))
+
+	nserv = 0;
+	if ((fp = fopen(_PATH_RESCONF, "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")) {
+		    if (haveenv)	/* skip if have from environ */
+			    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';
+		    if ((cp = strpbrk(statp->defdname, " \t\n")) != NULL)
+			    *cp = '\0';
+		    havesearch = 0;
+		    continue;
+		}
+		/* set search list */
+		if (MATCH(buf, "search")) {
+		    if (haveenv)	/* skip if have from environ */
+			    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';
+		    if ((cp = strchr(statp->defdname, '\n')) != 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) {
+				    *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) {
+		    struct addrinfo hints, *ai;
+		    char sbuf[NI_MAXSERV];
+		    const size_t minsiz =
+		        sizeof(statp->_u._ext.ext->nsaddrs[0]);
+
+		    cp = buf + sizeof("nameserver") - 1;
+		    while (*cp == ' ' || *cp == '\t')
+			cp++;
+		    cp[strcspn(cp, ";# \t\n")] = '\0';
+		    if ((*cp != '\0') && (*cp != '\n')) {
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_family = PF_UNSPEC;
+			hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
+			hints.ai_flags = AI_NUMERICHOST;
+			sprintf(sbuf, "%u", NAMESERVER_PORT);
+			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++;
+			}
+		    }
+		    continue;
+		}
+		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;
+		}
+		if (MATCH(buf, "options")) {
+		    res_setoptions(statp, buf + sizeof("options") - 1, "conf");
+		    continue;
+		}
+	    }
+	    if (nserv > 0)
+		statp->nscount = nserv;
+	    statp->nsort = nsort;
+	    (void) fclose(fp);
+	}
+#endif /* ANDROID_CHANGES */
+/*
+ * 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 (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++)
+			dots += (*cp == '.');
+
+		cp = statp->defdname;
+		while (pp < statp->dnsrch + MAXDFLSRCH) {
+			if (dots < LOCALDOMAINPARTS)
+				break;
+			cp = strchr(cp, '.') + 1;    /* we know there is one */
+			*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
+	}
+
+	if ((cp = getenv("RES_OPTIONS")) != NULL)
+		res_setoptions(statp, cp, "env");
+	if (nserv > 0) {
+		statp->nscount = nserv;
+		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;
+#ifdef DEBUG
+			if (statp->options & RES_DEBUG)
+				printf(";;\ttimeout=%d\n", statp->retrans);
+#endif
+		} 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;
+#ifdef DEBUG
+			if (statp->options & RES_DEBUG)
+				printf(";;\tattempts=%d\n", statp->retry);
+#endif
+		} 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, "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, (size_t)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, (size_t)i);
+			ext->nsuffix2[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++;
+	}
+}
+
+/* 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));
+}
+
+u_int
+res_randomid(void) {
+	struct timeval now;
+
+	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) {
+		(void) close(statp->_vcsock);
+		statp->_vcsock = -1;
+		statp->_flags &= ~(RES_F_VC | RES_F_CONN);
+	}
+	for (ns = 0; ns < statp->_u._ext.nscount; ns++) {
+		if (statp->_u._ext.nssocks[ns] != -1) {
+			(void) close(statp->_u._ext.nssocks[ns]);
+			statp->_u._ext.nssocks[ns] = -1;
+		}
+	}
+}
+
+void
+res_ndestroy(res_state statp) {
+	res_nclose(statp);
+	if (statp->_u._ext.ext != NULL)
+		free(statp->_u._ext.ext);
+	statp->options &= ~RES_INIT;
+	statp->_u._ext.ext = NULL;
+}
+
+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");
+}
+
+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);
+#ifdef notdef
+			else
+				statp->nsaddr_list[nserv].sin_family = 0;
+#endif
+			nserv++;
+			break;
+
+#ifdef HAS_INET6_STRUCTS
+		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;
+#endif
+
+		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;
+
+#ifdef HAS_INET6_STRUCTS
+		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;
+#endif
+
+		default:
+			set->sin.sin_family = 0;
+			break;
+		}
+		set++;
+	}
+	return (statp->nscount);
+}
+
+#ifdef ANDROID_CHANGES
+static int _get_dns_change_count()
+{
+	if (dns_change_prop == NULL) {
+		dns_change_prop = __system_property_find(DNS_CHANGE_PROP_NAME);
+	}
+	if (dns_change_prop != NULL) {
+		char propvalue[PROP_VALUE_MAX];
+		if (__system_property_read(dns_change_prop, NULL, propvalue) >= 1) {
+			return atoi(propvalue);
+		}
+	}
+	return -1;
+}
+
+int res_get_dns_changed()
+{
+	int change_count;
+
+	change_count = _get_dns_change_count();
+	if (change_count != dns_last_change_counter) {
+		if (change_count != -1) {
+			dns_last_change_counter = change_count;
+		}
+		return 1;
+	} else {
+		return 0;
+	}
+}
+#endif /* ANDROID_CHANGES */
diff --git a/libc/netbsd/resolv/res_mkquery.c b/libc/netbsd/resolv/res_mkquery.c
new file mode 100644
index 0000000..fb4de7f
--- /dev/null
+++ b/libc/netbsd/resolv/res_mkquery.c
@@ -0,0 +1,276 @@
+/*	$NetBSD: res_mkquery.c,v 1.6 2006/01/24 17:40:32 christos Exp $	*/
+
+/*
+ * Copyright (c) 2008  Android Open Source Project (query id randomization)
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)res_mkquery.c	8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "Id: res_mkquery.c,v 1.1.2.2.4.2 2004/03/16 12:34:18 marka Exp";
+#else
+__RCSID("$NetBSD: res_mkquery.c,v 1.6 2006/01/24 17:40:32 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include "arpa_nameser.h"
+#include <netdb.h>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+/* Options.  Leave them on. */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+#ifndef lint
+#define UNUSED(a)	(void)&a
+#else
+#define UNUSED(a)	a = a
+#endif
+
+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, *ep;
+	register int n;
+	u_char *dnptrs[20], **dpp, **lastdnptr;
+
+	UNUSED(newrr_in);
+
+#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 < HFIXEDSZ))
+		return (-1);
+	memset(buf, 0, HFIXEDSZ);
+	hp = (HEADER *)(void *)buf;
+	hp->id = htons(res_randomid());
+	hp->opcode = op;
+	hp->rd = (statp->options & RES_RECURSE) != 0U;
+	hp->rcode = NOERROR;
+	cp = buf + HFIXEDSZ;
+	ep = buf + buflen;
+	dpp = dnptrs;
+	*dpp++ = buf;
+	*dpp++ = NULL;
+	lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+	/*
+	 * perform opcode specific processing
+	 */
+	switch (op) {
+	case QUERY:	/*FALLTHROUGH*/
+	case NS_NOTIFY_OP:
+		if (ep - cp < QFIXEDSZ)
+			return (-1);
+		if ((n = dn_comp(dname, cp, ep - cp - QFIXEDSZ, dnptrs,
+		    lastdnptr)) < 0)
+			return (-1);
+		cp += n;
+		ns_put16(type, cp);
+		cp += INT16SZ;
+		ns_put16(class, cp);
+		cp += INT16SZ;
+		hp->qdcount = htons(1);
+		if (op == QUERY || data == NULL)
+			break;
+		/*
+		 * Make an additional record for completion domain.
+		 */
+		if ((ep - cp) < RRFIXEDSZ)
+			return (-1);
+		n = dn_comp((const char *)data, cp, ep - cp - RRFIXEDSZ,
+			    dnptrs, lastdnptr);
+		if (n < 0)
+			return (-1);
+		cp += n;
+		ns_put16(T_NULL, cp);
+		cp += INT16SZ;
+		ns_put16(class, cp);
+		cp += INT16SZ;
+		ns_put32(0, cp);
+		cp += INT32SZ;
+		ns_put16(0, cp);
+		cp += INT16SZ;
+		hp->arcount = htons(1);
+		break;
+
+	case IQUERY:
+		/*
+		 * Initialize answer section
+		 */
+		if (ep - cp < 1 + RRFIXEDSZ + datalen)
+			return (-1);
+		*cp++ = '\0';	/* no domain name */
+		ns_put16(type, cp);
+		cp += INT16SZ;
+		ns_put16(class, cp);
+		cp += INT16SZ;
+		ns_put32(0, cp);
+		cp += INT32SZ;
+		ns_put16(datalen, cp);
+		cp += INT16SZ;
+		if (datalen) {
+			memcpy(cp, data, (size_t)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(res_state statp,
+	 int n0,		/* current offset in buffer */
+	 u_char *buf,		/* buffer to put query */
+	 int buflen,		/* size of buffer */
+	 int anslen)		/* UDP answer buffer size */
+{
+	register HEADER *hp;
+	register u_char *cp, *ep;
+	u_int16_t flags = 0;
+
+#ifdef DEBUG
+	if ((statp->options & RES_DEBUG) != 0U)
+		printf(";; res_nopt()\n");
+#endif
+
+	hp = (HEADER *)(void *)buf;
+	cp = buf + n0;
+	ep = buf + buflen;
+
+	if ((ep - cp) < 1 + RRFIXEDSZ)
+		return (-1);
+
+	*cp++ = 0;	/* "." */
+
+	ns_put16(T_OPT, cp);	/* TYPE */
+	cp += INT16SZ;
+	ns_put16(anslen & 0xffff, cp);	/* CLASS = UDP payload size */
+	cp += INT16SZ;
+	*cp++ = 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;
+	}
+	ns_put16(flags, cp);
+	cp += INT16SZ;
+	ns_put16(0, cp);	/* RDLEN */
+	cp += INT16SZ;
+	hp->arcount = htons(ntohs(hp->arcount) + 1);
+
+	return (cp - buf);
+}
+#endif
diff --git a/libc/netbsd/resolv/res_private.h b/libc/netbsd/resolv/res_private.h
new file mode 100644
index 0000000..8341913
--- /dev/null
+++ b/libc/netbsd/resolv/res_private.h
@@ -0,0 +1,22 @@
+/*	$NetBSD: res_private.h,v 1.1.1.1 2004/05/20 17:18:54 christos Exp $	*/
+
+#ifndef res_private_h
+#define res_private_h
+
+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 nsuffix2[64];
+};
+
+extern int
+res_ourserver_p(const res_state statp, const struct sockaddr *sa);
+
+#endif
diff --git a/libc/netbsd/resolv/res_query.c b/libc/netbsd/resolv/res_query.c
new file mode 100644
index 0000000..8e1321e
--- /dev/null
+++ b/libc/netbsd/resolv/res_query.c
@@ -0,0 +1,415 @@
+/*	$NetBSD: res_query.c,v 1.7 2006/01/24 17:41:25 christos Exp $	*/
+
+/*
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)res_query.c	8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "Id: res_query.c,v 1.2.2.3.4.2 2004/03/16 12:34:19 marka Exp";
+#else
+__RCSID("$NetBSD: res_query.c,v 1.7 2006/01/24 17:41:25 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+
+
+#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>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define DISABLE_HOST_ALIAS 1
+
+/* Options.  Leave them on. */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+#if PACKETSZ > 1024
+#define MAXPACKET	PACKETSZ
+#else
+#define MAXPACKET	1024
+#endif
+
+/*
+ * 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.
+ */
+int
+res_nquery(res_state statp,
+	   const char *name,	/* domain name */
+	   int class, int type,	/* class and type of query */
+	   u_char *answer,	/* buffer to put answer */
+	   int anslen)		/* size of answer buffer */
+{
+	u_char buf[MAXPACKET];
+	HEADER *hp = (HEADER *)(void *)answer;
+	int n;
+	u_int oflags;
+
+	oflags = statp->_flags;
+
+again:
+	hp->rcode = NOERROR;	/* default */
+
+#ifdef DEBUG
+	if (statp->options & RES_DEBUG)
+		printf(";; res_query(%s, %d, %d)\n", name, class, type);
+#endif
+
+	n = res_nmkquery(statp, 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)) != 0U)
+		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
+		RES_SET_H_ERRNO(statp, NO_RECOVERY);
+		return (n);
+	}
+	n = res_nsend(statp, buf, n, answer, anslen);
+	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)) != 0U &&
+		    ((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
+		RES_SET_H_ERRNO(statp, TRY_AGAIN);
+		return (n);
+	}
+
+	if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+#ifdef DEBUG
+		if (statp->options & RES_DEBUG)
+			printf(";; rcode = (%s), counts = an:%d ns:%d ar:%d\n",
+			       p_rcode(hp->rcode),
+			       ntohs(hp->ancount),
+			       ntohs(hp->nscount),
+			       ntohs(hp->arcount));
+#endif
+		switch (hp->rcode) {
+		case NXDOMAIN:
+			RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
+			break;
+		case SERVFAIL:
+			RES_SET_H_ERRNO(statp, TRY_AGAIN);
+			break;
+		case NOERROR:
+			RES_SET_H_ERRNO(statp, NO_DATA);
+			break;
+		case FORMERR:
+		case NOTIMP:
+		case REFUSED:
+		default:
+			RES_SET_H_ERRNO(statp, NO_RECOVERY);
+			break;
+		}
+		return (-1);
+	}
+	return (n);
+}
+
+/*
+ * 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(res_state statp,
+	    const char *name,	/* domain name */
+	    int class, int type,	/* class and type of query */
+	    u_char *answer,	/* buffer to put answer */
+	    int anslen)		/* size of answer */
+{
+	const char *cp, * const *domain;
+	HEADER *hp = (HEADER *)(void *)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;
+	RES_SET_H_ERRNO(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 = statp->res_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) != 0U) ||
+	    (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0U)) {
+		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(statp, name, *domain,
+					       class, type,
+					       answer, anslen);
+			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) {
+				RES_SET_H_ERRNO(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 == SERVFAIL) {
+					/* 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) == 0U)
+				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) == 0U) &&
+	    !(tried_as_is || root_on_list)) {
+		ret = res_nquerydomain(statp, name, NULL, class, type,
+				       answer, anslen);
+		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)
+		RES_SET_H_ERRNO(statp, saved_herrno);
+	else if (got_nodata)
+		RES_SET_H_ERRNO(statp, NO_DATA);
+	else if (got_servfail)
+		RES_SET_H_ERRNO(statp, TRY_AGAIN);
+	return (-1);
+}
+
+/*
+ * 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(res_state statp,
+	    const char *name,
+	    const char *domain,
+	    int class, int type,	/* class and type of query */
+	    u_char *answer,		/* buffer to put answer */
+	    int anslen)		/* size of answer */
+{
+	char nbuf[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 >= MAXDNAME) {
+			RES_SET_H_ERRNO(statp, NO_RECOVERY);
+			return (-1);
+		}
+		n--;
+		if (n >= 0 && name[n] == '.') {
+			strncpy(nbuf, name, (size_t)n);
+			nbuf[n] = '\0';
+		} else
+			longname = name;
+	} else {
+		n = strlen(name);
+		d = strlen(domain);
+		if (n + d + 1 >= MAXDNAME) {
+			RES_SET_H_ERRNO(statp, NO_RECOVERY);
+			return (-1);
+		}
+		sprintf(nbuf, "%s.%s", name, domain);
+	}
+	return (res_nquery(statp, longname, class, type, answer, anslen));
+}
+
+const char *
+res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
+	return (NULL);
+}
diff --git a/libc/netbsd/resolv/res_random.c b/libc/netbsd/resolv/res_random.c
new file mode 100644
index 0000000..4570c4f
--- /dev/null
+++ b/libc/netbsd/resolv/res_random.c
@@ -0,0 +1,275 @@
+/* $OpenBSD: res_random.c,v 1.17 2008/04/13 00:28:35 djm Exp $ */
+
+/*
+ * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
+ * Copyright 2008 Damien Miller <djm@openbsd.org>
+ * Copyright 2008 Android Open Source Project (thread-safety)
+ * All rights reserved.
+ *
+ * Theo de Raadt <deraadt@openbsd.org> came up with the idea of using
+ * such a mathematical system to generate more random (yet non-repeating)
+ * ids to solve the resolver/named problem.  But Niels designed the
+ * actual system based on the constraints.
+ *
+ * Later modified by Damien Miller to wrap the LCG output in a 15-bit
+ * permutation generator based on a Luby-Rackoff block cipher. This
+ * ensures the output is non-repeating and preserves the MSB twiddle
+ * trick, but makes it more resistant to LCG prediction.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * seed = random 15bit
+ * n = prime, g0 = generator to n,
+ * j = random so that gcd(j,n-1) == 1
+ * g = g0^j mod n will be a generator again.
+ *
+ * X[0] = random seed.
+ * X[n] = a*X[n-1]+b mod m is a Linear Congruential Generator
+ * with a = 7^(even random) mod m,
+ *      b = random with gcd(b,m) == 1
+ *      m = 31104 and a maximal period of m-1.
+ *
+ * The transaction id is determined by:
+ * id[n] = seed xor (g^X[n] mod n)
+ *
+ * Effectivly the id is restricted to the lower 15 bits, thus
+ * yielding two different cycles by toggling the msb on and off.
+ * This avoids reuse issues caused by reseeding.
+ *
+ * The output of this generator is then randomly permuted though a
+ * custom 15 bit Luby-Rackoff block cipher.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include "resolv_private.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* BIONIC-BEGIN */
+static pthread_mutex_t         _res_random_lock = PTHREAD_MUTEX_INITIALIZER;
+#define  _RES_RANDOM_LOCK()    pthread_mutex_lock(&_res_random_lock)
+#define  _RES_RANDOM_UNLOCK()  pthread_mutex_unlock(&_res_random_lock)
+/* BIONIC-END */
+
+#define RU_OUT  	180	/* Time after wich will be reseeded */
+#define RU_MAX		30000	/* Uniq cycle, avoid blackjack prediction */
+#define RU_GEN		2	/* Starting generator */
+#define RU_N		32749	/* RU_N-1 = 2*2*3*2729 */
+#define RU_AGEN		7	/* determine ru_a as RU_AGEN^(2*rand) */
+#define RU_M		31104	/* RU_M = 2^7*3^5 - don't change */
+#define RU_ROUNDS	11	/* Number of rounds for permute (odd) */
+
+struct prf_ctx {
+	/* PRF lookup table for odd rounds (7 bits input to 8 bits output) */
+	u_char prf7[(RU_ROUNDS / 2) * (1 << 7)];
+
+	/* PRF lookup table for even rounds (8 bits input to 7 bits output) */
+	u_char prf8[((RU_ROUNDS + 1) / 2) * (1 << 8)];
+};
+
+#define PFAC_N 3
+const static u_int16_t pfacts[PFAC_N] = {
+	2,
+	3,
+	2729
+};
+
+static u_int16_t ru_x;
+static u_int16_t ru_seed, ru_seed2;
+static u_int16_t ru_a, ru_b;
+static u_int16_t ru_g;
+static u_int16_t ru_counter = 0;
+static u_int16_t ru_msb = 0;
+static struct prf_ctx *ru_prf = NULL;
+static long ru_reseed;
+
+static u_int16_t pmod(u_int16_t, u_int16_t, u_int16_t);
+static void res_initid(void);
+
+/*
+ * Do a fast modular exponation, returned value will be in the range
+ * of 0 - (mod-1)
+ */
+static u_int16_t
+pmod(u_int16_t gen, u_int16_t exp, u_int16_t mod)
+{
+	u_int16_t s, t, u;
+
+	s = 1;
+	t = gen;
+	u = exp;
+
+	while (u) {
+		if (u & 1)
+			s = (s * t) % mod;
+		u >>= 1;
+		t = (t * t) % mod;
+	}
+	return (s);
+}
+
+/*
+ * 15-bit permutation based on Luby-Rackoff block cipher
+ */
+u_int
+permute15(u_int in)
+{
+	int i;
+	u_int left, right, tmp;
+
+	if (ru_prf == NULL)
+		return in;
+
+	left = (in >> 8) & 0x7f;
+	right = in & 0xff;
+
+	/*
+	 * Each round swaps the width of left and right. Even rounds have
+	 * a 7-bit left, odd rounds have an 8-bit left.	Since this uses an
+	 * odd number of rounds, left is always 8 bits wide at the end.
+	 */
+	for (i = 0; i < RU_ROUNDS; i++) {
+		if ((i & 1) == 0)
+			tmp = ru_prf->prf8[(i << (8 - 1)) | right] & 0x7f;
+		else
+			tmp = ru_prf->prf7[((i - 1) << (7 - 1)) | right];
+		tmp ^= left;
+		left = right;
+		right = tmp;
+	}
+
+	return (right << 8) | left;
+}
+
+/*
+ * Initializes the seed and chooses a suitable generator. Also toggles
+ * the msb flag. The msb flag is used to generate two distinct
+ * cycles of random numbers and thus avoiding reuse of ids.
+ *
+ * This function is called from res_randomid() when needed, an
+ * application does not have to worry about it.
+ */
+static void
+res_initid(void)
+{
+	u_int16_t j, i;
+	u_int32_t tmp;
+	int noprime = 1;
+	struct timeval tv;
+
+	ru_x = arc4random_uniform(RU_M);
+
+	/* 15 bits of random seed */
+	tmp = arc4random();
+	ru_seed = (tmp >> 16) & 0x7FFF;
+	ru_seed2 = tmp & 0x7FFF;
+
+	/* Determine the LCG we use */
+	tmp = arc4random();
+	ru_b = (tmp & 0xfffe) | 1;
+	ru_a = pmod(RU_AGEN, (tmp >> 16) & 0xfffe, RU_M);
+	while (ru_b % 3 == 0)
+		ru_b += 2;
+
+	j = arc4random_uniform(RU_N);
+
+	/*
+	 * Do a fast gcd(j,RU_N-1), so we can find a j with
+	 * gcd(j, RU_N-1) == 1, giving a new generator for
+	 * RU_GEN^j mod RU_N
+	 */
+
+	while (noprime) {
+		for (i = 0; i < PFAC_N; i++)
+			if (j % pfacts[i] == 0)
+				break;
+
+		if (i >= PFAC_N)
+			noprime = 0;
+		else
+			j = (j + 1) % RU_N;
+	}
+
+	ru_g = pmod(RU_GEN, j, RU_N);
+	ru_counter = 0;
+
+	/* Initialise PRF for Luby-Rackoff permutation */
+	if (ru_prf == NULL)
+		ru_prf = malloc(sizeof(*ru_prf));
+	if (ru_prf != NULL)
+		arc4random_buf(ru_prf, sizeof(*ru_prf));
+
+	gettimeofday(&tv, NULL);
+	ru_reseed = tv.tv_sec + RU_OUT;
+	ru_msb = ru_msb == 0x8000 ? 0 : 0x8000;
+}
+
+u_int
+res_randomid(void)
+{
+	struct timeval tv;
+        u_int  result;
+
+        _RES_RANDOM_LOCK()
+	gettimeofday(&tv, NULL);
+	if (ru_counter >= RU_MAX || tv.tv_sec > ru_reseed)
+		res_initid();
+
+	/* Linear Congruential Generator */
+	ru_x = (ru_a * ru_x + ru_b) % RU_M;
+	ru_counter++;
+
+	result = permute15(ru_seed ^ pmod(ru_g, ru_seed2 + ru_x, RU_N)) | ru_msb;
+        _RES_RANDOM_UNLOCK()
+        return result;
+}
+
+#if 0
+int
+main(int argc, char **argv)
+{
+	int i, n;
+	u_int16_t wert;
+
+	res_initid();
+
+	printf("Generator: %u\n", ru_g);
+	printf("Seed: %u\n", ru_seed);
+	printf("Reseed at %ld\n", ru_reseed);
+	printf("Ru_X: %u\n", ru_x);
+	printf("Ru_A: %u\n", ru_a);
+	printf("Ru_B: %u\n", ru_b);
+
+	n = argc > 1 ? atoi(argv[1]) : 60001;
+	for (i=0;i<n;i++) {
+		wert = res_randomid();
+		printf("%u\n", wert);
+	}
+	return 0;
+}
+#endif
+
diff --git a/libc/netbsd/resolv/res_send.c b/libc/netbsd/resolv/res_send.c
new file mode 100644
index 0000000..3aca760
--- /dev/null
+++ b/libc/netbsd/resolv/res_send.c
@@ -0,0 +1,1168 @@
+/*	$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $	*/
+
+/*
+ * Copyright 2008  Android Open Source Project (source port randomization)
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#ifdef notdef
+static const char sccsid[] = "@(#)res_send.c	8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "Id: res_send.c,v 1.5.2.2.4.5 2004/08/10 02:19:56 marka Exp";
+#else
+__RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/* set to 1 to use our small/simple/limited DNS cache */
+#define  USE_RESOLV_CACHE  1
+
+/*
+ * Send query to name server and wait for reply.
+ */
+
+#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 <netinet/in6.h>
+#include "arpa_nameser.h"
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#ifdef ANDROID_CHANGES
+#include "resolv_private.h"
+#else
+#include <resolv.h>
+#endif
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <isc/eventlib.h>
+
+#if USE_RESOLV_CACHE
+#  include <resolv_cache.h>
+#endif
+
+#ifndef DE_CONST
+#define DE_CONST(c,v)   v = ((c) ? \
+    strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL)
+#endif
+
+/* Options.  Leave them on. */
+#ifndef DEBUG
+#define DEBUG
+#endif
+#include "res_debug.h"
+#include "res_private.h"
+
+#define EXT(res) ((res)->_u._ext)
+
+static const int highestFD = FD_SETSIZE - 1;
+
+/* Forward. */
+
+static int		get_salen __P((const struct sockaddr *));
+static struct sockaddr * get_nsaddr __P((res_state, size_t));
+static int		send_vc(res_state, const u_char *, int,
+				u_char *, int, int *, int);
+static int		send_dg(res_state, const u_char *, int,
+				u_char *, int, int *, int,
+				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
+void res_pquery(const res_state, const u_char *, int, FILE *);
+
+
+/* BIONIC-BEGIN: implement source port randomization */
+typedef union {
+    struct sockaddr      sa;
+    struct sockaddr_in   sin;
+    struct sockaddr_in6  sin6;
+} _sockaddr_union;
+
+static int
+random_bind( int  s, int  family )
+{
+    _sockaddr_union  u;
+    int              j;
+    socklen_t        slen;
+
+    /* clear all, this also sets the IP4/6 address to 'any' */
+    memset( &u, 0, sizeof u );
+
+    switch (family) {
+        case AF_INET:
+            u.sin.sin_family = family;
+            slen             = sizeof u.sin;
+            break;
+        case AF_INET6:
+            u.sin6.sin6_family = family;
+            slen               = sizeof u.sin6;
+            break;
+        default:
+            errno = EPROTO;
+            return -1;
+    }
+
+    /* first try to bind to a random source port a few times */
+    for (j = 0; j < 10; j++) {
+        /* find a random port between 1025 .. 65534 */
+        int  port = 1025 + (res_randomid() % (65535-1025));
+        if (family == AF_INET)
+            u.sin.sin_port = htons(port);
+        else
+            u.sin6.sin6_port = htons(port);
+
+        if ( !bind( s, &u.sa, slen ) )
+            return 0;
+    }
+
+    /* nothing after 10 tries, our network table is probably busy */
+    /* let the system decide which port is best */
+    if (family == AF_INET)
+        u.sin.sin_port = 0;
+    else
+        u.sin6.sin6_port = 0;
+
+    return bind( s, &u.sa, slen );
+}
+/* BIONIC-END */
+
+static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+
+/* 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 *)(const void *)sa;
+		for (ns = 0;  ns < statp->nscount;  ns++) {
+			srv = (struct sockaddr_in *)(void *)get_nsaddr(statp, (size_t)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 *)(const void *)sa;
+		for (ns = 0;  ns < statp->nscount;  ns++) {
+			srv6 = (struct sockaddr_in6 *)(void *)get_nsaddr(statp, (size_t)ns);
+			if (srv6->sin6_family == in6p->sin6_family &&
+			    srv6->sin6_port == in6p->sin6_port &&
+#ifdef HAVE_SIN6_SCOPE_ID
+			    (srv6->sin6_scope_id == 0 ||
+			     srv6->sin6_scope_id == in6p->sin6_scope_id) &&
+#endif
+			    (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 + 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 + HFIXEDSZ;
+	int qdcount = ntohs(((const HEADER*)(const void *)buf)->qdcount);
+
+	while (qdcount-- > 0) {
+		char tname[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 * INT16SZ > eom)
+			return (-1);
+		ttype = ns_get16(cp); cp += INT16SZ;
+		tclass = ns_get16(cp); cp += 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 + HFIXEDSZ;
+	int qdcount = ntohs(((const HEADER*)(const void *)buf1)->qdcount);
+
+	if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
+		return (-1);
+
+	/*
+	 * Only header section present in replies to
+	 * dynamic update packets.
+	 */
+	if ((((const HEADER *)(const void *)buf1)->opcode == ns_o_update) &&
+	    (((const HEADER *)(const void *)buf2)->opcode == ns_o_update))
+		return (1);
+
+	if (qdcount != ntohs(((const HEADER*)(const void *)buf2)->qdcount))
+		return (0);
+	while (qdcount-- > 0) {
+		char tname[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 * INT16SZ > eom1)
+			return (-1);
+		ttype = ns_get16(cp);	cp += INT16SZ;
+		tclass = ns_get16(cp); cp += INT16SZ;
+		if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
+			return (0);
+	}
+	return (1);
+}
+
+
+int
+res_nsend(res_state statp,
+	  const u_char *buf, int buflen, u_char *ans, int anssiz)
+{
+	int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
+	char abuf[NI_MAXHOST];
+#if USE_RESOLV_CACHE
+        struct resolv_cache*  cache;
+        ResolvCacheStatus     cache_status = RESOLV_CACHE_UNSUPPORTED;
+#endif
+
+	if (statp->nscount == 0) {
+		errno = ESRCH;
+		return (-1);
+	}
+	if (anssiz < HFIXEDSZ) {
+		errno = EINVAL;
+		return (-1);
+	}
+	DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
+		(stdout, ";; res_send()\n"), buf, buflen);
+	v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
+	gotsomewhere = 0;
+	terrno = ETIMEDOUT;
+
+#if USE_RESOLV_CACHE
+        cache = __get_res_cache();
+        if (cache != NULL) {
+            int  anslen = 0;
+            cache_status = _resolv_cache_lookup(
+                                cache, buf, buflen,
+                                ans, anssiz, &anslen);
+
+            if (cache_status == RESOLV_CACHE_FOUND) {
+                return anslen;
+            }
+        }
+#endif
+
+	/*
+	 * 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;
+		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 &&
+				    !sock_eq((struct sockaddr *)(void *)&statp->nsaddr_list[ns],
+					     (struct sockaddr *)(void *)&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 *)(void *)&peer, &peerlen) < 0) {
+					needclose++;
+					break;
+				}
+				if (!sock_eq((struct sockaddr *)(void *)&peer,
+				    get_nsaddr(statp, (size_t)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;
+			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) != 0U &&
+	    (statp->options & RES_BLAST) == 0U) {
+		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;
+	}
+
+	/*
+	 * 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, (size_t)ns);
+		nsaplen = get_salen(nsap);
+		statp->_flags &= ~RES_F_LASTMASK;
+		statp->_flags |= (ns << RES_F_LASTSHIFT);
+ same_ns:
+		if (statp->qhook) {
+			int done = 0, loops = 0;
+
+			do {
+				res_sendhookact act;
+
+				act = (*statp->qhook)(&nsap, &buf, &buflen,
+						      ans, anssiz, &resplen);
+				switch (act) {
+				case res_goahead:
+					done = 1;
+					break;
+				case res_nextns:
+					res_nclose(statp);
+					goto next_ns;
+				case res_done:
+					return (resplen);
+				case res_modified:
+					/* give the hook another try */
+					if (++loops < 42) /*doug adams*/
+						break;
+					/*FALLTHROUGH*/
+				case res_error:
+					/*FALLTHROUGH*/
+				default:
+					goto fail;
+				}
+			} while (!done);
+		}
+
+		Dprint(((statp->options & RES_DEBUG) &&
+			getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf),
+				    NULL, 0, niflags) == 0),
+		       (stdout, ";; Querying server (# %d) address = %s\n",
+			ns + 1, abuf));
+
+
+		if (v_circuit) {
+			/* Use VC; at most one attempt per server. */
+			try = statp->retry;
+			n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
+				    ns);
+			if (n < 0)
+				goto fail;
+			if (n == 0)
+				goto next_ns;
+			resplen = n;
+		} else {
+			/* Use datagrams. */
+			n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
+				    ns, &v_circuit, &gotsomewhere);
+			if (n < 0)
+				goto fail;
+			if (n == 0)
+				goto next_ns;
+			if (v_circuit)
+				goto same_ns;
+			resplen = n;
+		}
+
+		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, (resplen > anssiz) ? anssiz : resplen);
+
+#if USE_RESOLV_CACHE
+                if (cache_status == RESOLV_CACHE_NOTFOUND) {
+                    _resolv_cache_add(cache, buf, buflen,
+                                      ans, resplen);
+                }
+#endif
+		/*
+		 * 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 && (statp->options & RES_USEVC) == 0U) ||
+		    (statp->options & RES_STAYOPEN) == 0U) {
+			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 next_ns;
+				case res_modified:
+					/* give the hook another try */
+					if (++loops < 42) /*doug adams*/
+						break;
+					/*FALLTHROUGH*/
+				case res_error:
+					/*FALLTHROUGH*/
+				default:
+					goto fail;
+				}
+			} while (!done);
+
+		}
+		return (resplen);
+ next_ns: ;
+	   } /*foreach ns*/
+	} /*foreach retry*/
+	res_nclose(statp);
+	if (!v_circuit) {
+		if (!gotsomewhere)
+			errno = ECONNREFUSED;	/* no nameservers found */
+		else
+			errno = ETIMEDOUT;	/* no answer obtained */
+	} else
+		errno = terrno;
+	return (-1);
+ fail:
+	res_nclose(statp);
+	return (-1);
+}
+
+/* Private */
+
+static int
+get_salen(sa)
+	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.
+ */
+static struct sockaddr *
+get_nsaddr(statp, n)
+	res_state statp;
+	size_t n;
+{
+
+	if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) {
+		/*
+		 * - 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)
+{
+	const HEADER *hp = (const HEADER *)(const void *)buf;
+	HEADER *anhp = (HEADER *)(void *)ans;
+	struct sockaddr *nsap;
+	int nsaplen;
+	int truncating, connreset, resplen, n;
+	struct iovec iov[2];
+	u_short len;
+	u_char *cp;
+	void *tmp;
+
+	nsap = get_nsaddr(statp, (size_t)ns);
+	nsaplen = get_salen(nsap);
+
+	connreset = 0;
+ same_ns:
+	truncating = 0;
+
+	/* Are we still talking to whom we want to talk to? */
+	if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
+		struct sockaddr_storage peer;
+		socklen_t size = sizeof peer;
+
+		if (getpeername(statp->_vcsock,
+				(struct sockaddr *)(void *)&peer, &size) < 0 ||
+		    !sock_eq((struct sockaddr *)(void *)&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) {
+			switch (errno) {
+			case EPROTONOSUPPORT:
+#ifdef EPFNOSUPPORT
+			case EPFNOSUPPORT:
+#endif
+			case EAFNOSUPPORT:
+				Perror(statp, stderr, "socket(vc)", errno);
+				return (0);
+			default:
+				*terrno = errno;
+				Perror(statp, stderr, "socket(vc)", errno);
+				return (-1);
+			}
+		}
+		errno = 0;
+		if (random_bind(statp->_vcsock,nsap->sa_family) < 0) {
+			*terrno = errno;
+			Aerror(statp, stderr, "bind/vc", errno, nsap,
+			    nsaplen);
+			res_nclose(statp);
+			return (0);
+		}
+		if (connect(statp->_vcsock, nsap, (socklen_t)nsaplen) < 0) {
+			*terrno = errno;
+			Aerror(statp, stderr, "connect/vc", errno, nsap,
+			    nsaplen);
+			res_nclose(statp);
+			return (0);
+		}
+		statp->_flags |= RES_F_VC;
+	}
+
+	/*
+	 * Send length & message
+	 */
+	ns_put16((u_short)buflen, (u_char*)(void *)&len);
+	iov[0] = evConsIovec(&len, INT16SZ);
+	DE_CONST(buf, tmp);
+	iov[1] = evConsIovec(tmp, (size_t)buflen);
+	if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
+		*terrno = errno;
+		Perror(statp, stderr, "write failed", errno);
+		res_nclose(statp);
+		return (0);
+	}
+	/*
+	 * Receive length & response
+	 */
+ read_len:
+	cp = ans;
+	len = INT16SZ;
+	while ((n = read(statp->_vcsock, (char *)cp, (size_t)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 same_ns;
+		}
+		res_nclose(statp);
+		return (0);
+	}
+	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 < HFIXEDSZ) {
+		/*
+		 * Undersized message.
+		 */
+		Dprint(statp->options & RES_DEBUG,
+		       (stdout, ";; undersized: %d\n", len));
+		*terrno = EMSGSIZE;
+		res_nclose(statp);
+		return (0);
+	}
+	cp = ans;
+	while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0){
+		cp += n;
+		len -= n;
+	}
+	if (n <= 0) {
+		*terrno = errno;
+		Perror(statp, stderr, "read(vc)", errno);
+		res_nclose(statp);
+		return (0);
+	}
+	if (truncating) {
+		/*
+		 * Flush rest of answer so connection stays in synch.
+		 */
+		anhp->tc = 1;
+		len = resplen - anssiz;
+		while (len != 0) {
+			char junk[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.
+	 */
+	return (resplen);
+}
+
+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)
+{
+	const HEADER *hp = (const HEADER *)(const void *)buf;
+	HEADER *anhp = (HEADER *)(void *)ans;
+	const struct sockaddr *nsap;
+	int nsaplen;
+	struct timespec now, timeout, finish;
+	fd_set dsmask;
+	struct sockaddr_storage from;
+	socklen_t fromlen;
+	int resplen, seconds, n, s;
+
+	nsap = get_nsaddr(statp, (size_t)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) {
+			switch (errno) {
+			case EPROTONOSUPPORT:
+#ifdef EPFNOSUPPORT
+			case EPFNOSUPPORT:
+#endif
+			case EAFNOSUPPORT:
+				Perror(statp, stderr, "socket(dg)", errno);
+				return (0);
+			default:
+				*terrno = errno;
+				Perror(statp, stderr, "socket(dg)", errno);
+				return (-1);
+			}
+		}
+#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 (random_bind(EXT(statp).nssocks[ns], nsap->sa_family) < 0) {
+			Aerror(statp, stderr, "bind(dg)", errno, nsap,
+			    nsaplen);
+			res_nclose(statp);
+			return (0);
+		}
+		if (connect(EXT(statp).nssocks[ns], nsap, (socklen_t)nsaplen) < 0) {
+			Aerror(statp, stderr, "connect(dg)", errno, nsap,
+			    nsaplen);
+			res_nclose(statp);
+			return (0);
+		}
+#endif /* !CANNOT_CONNECT_DGRAM */
+		Dprint(statp->options & RES_DEBUG,
+		       (stdout, ";; new DG socket\n"))
+	}
+	s = EXT(statp).nssocks[ns];
+#ifndef CANNOT_CONNECT_DGRAM
+	if (send(s, (const char*)buf, (size_t)buflen, 0) != buflen) {
+		Perror(statp, stderr, "send", errno);
+		res_nclose(statp);
+		return (0);
+	}
+#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 (0);
+	}
+#endif /* !CANNOT_CONNECT_DGRAM */
+
+	/*
+	 * Wait for reply.
+	 */
+	seconds = (statp->retrans << ns);
+	if (ns > 0)
+		seconds /= statp->nscount;
+	if (seconds <= 0)
+		seconds = 1;
+	now = evNowTime();
+	timeout = evConsTime((long)seconds, 0L);
+	finish = evAddTime(now, timeout);
+	goto nonow;
+ wait:
+	now = evNowTime();
+ nonow:
+	FD_ZERO(&dsmask);
+	FD_SET(s, &dsmask);
+	if (evCmpTime(finish, now) > 0)
+		timeout = evSubTime(finish, now);
+	else
+		timeout = evConsTime(0L, 0L);
+	n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
+	if (n == 0) {
+		Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
+		*gotsomewhere = 1;
+		return (0);
+	}
+	if (n < 0) {
+		if (errno == EINTR)
+			goto wait;
+		Perror(statp, stderr, "select", errno);
+		res_nclose(statp);
+		return (0);
+	}
+	errno = 0;
+	fromlen = sizeof(from);
+	resplen = recvfrom(s, (char*)ans, (size_t)anssiz,0,
+			   (struct sockaddr *)(void *)&from, &fromlen);
+	if (resplen <= 0) {
+		Perror(statp, stderr, "recvfrom", errno);
+		res_nclose(statp);
+		return (0);
+	}
+	*gotsomewhere = 1;
+	if (resplen < HFIXEDSZ) {
+		/*
+		 * Undersized message.
+		 */
+		Dprint(statp->options & RES_DEBUG,
+		       (stdout, ";; undersized: %d\n",
+			resplen));
+		*terrno = EMSGSIZE;
+		res_nclose(statp);
+		return (0);
+	}
+	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;
+	}
+	if (!(statp->options & RES_INSECURE1) &&
+	    !res_ourserver_p(statp, (struct sockaddr *)(void *)&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 RES_USE_EDNS0
+	if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) {
+		/*
+		 * 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 (0);
+	}
+#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);
+		goto wait;
+	}
+	if (anhp->rcode == SERVFAIL ||
+	    anhp->rcode == NOTIMP ||
+	    anhp->rcode == 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 (0);
+	}
+	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 (1);
+	}
+	/*
+	 * All is well, or the error is fatal.  Signal that the
+	 * next nameserver ought not be tried.
+	 */
+	return (resplen);
+}
+
+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) != 0U) {
+		if (getnameinfo(address, (socklen_t)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) != 0U)
+		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 *)(void *)a;
+		b4 = (struct sockaddr_in *)(void *)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 *)(void *)a;
+		b6 = (struct sockaddr_in6 *)(void *)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;
+	sigset_t sigs;
+	int n;
+
+	if (tsp) {
+		tvp = &tv;
+		tv = evTimeVal(*tsp);
+	} else
+		tvp = NULL;
+	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/libc/netbsd/resolv/res_state.c b/libc/netbsd/resolv/res_state.c
new file mode 100644
index 0000000..3a2301d
--- /dev/null
+++ b/libc/netbsd/resolv/res_state.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ */
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include "arpa_nameser.h"
+#include <netdb.h>
+#include "resolv_private.h"
+#include "resolv_cache.h"
+#include <pthread.h>
+#include <stdlib.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static pthread_key_t   _res_key;
+static pthread_once_t  _res_once;
+
+typedef struct {
+    int                    _h_errno;
+    struct __res_state     _nres[1];
+    unsigned               _serial;
+    struct prop_info*      _pi;
+    struct res_static      _rstatic[1];
+} _res_thread;
+
+static _res_thread*
+_res_thread_alloc(void)
+{
+    _res_thread*  rt = malloc(sizeof(*rt));
+
+    if (rt) {
+        rt->_h_errno = 0;
+        /* Special system property which tracks any changes to 'net.*'. */
+        rt->_serial = 0;
+        rt->_pi = (struct prop_info*) __system_property_find("net.change");
+        if (rt->_pi) {
+            rt->_serial = rt->_pi->serial;
+        }
+        if ( res_ninit( rt->_nres ) < 0 ) {
+            free(rt);
+            rt = NULL;
+        } else {
+            memset(rt->_rstatic, 0, sizeof rt->_rstatic);
+        }
+    }
+    return rt;
+}
+
+static void
+_res_static_done( res_static  rs )
+{
+    /* fortunately, there is nothing to do here, since the
+     * points in h_addr_ptrs and host_aliases should all
+     * point to 'hostbuf'
+     */
+    if (rs->hostf) {  /* should not happen in theory, but just be safe */
+        fclose(rs->hostf);
+        rs->hostf = NULL;
+    }
+    free(rs->servent.s_aliases);
+}
+
+static void
+_res_thread_free( void*  _rt )
+{
+    _res_thread*  rt = _rt;
+
+    _res_static_done(rt->_rstatic);
+    res_ndestroy(rt->_nres);
+    free(rt);
+}
+
+static void
+_res_init_key( void )
+{
+    pthread_key_create( &_res_key, _res_thread_free );
+}
+
+static _res_thread*
+_res_thread_get(void)
+{
+    _res_thread*  rt;
+    pthread_once( &_res_once, _res_init_key );
+    rt = pthread_getspecific( _res_key );
+    if (rt == NULL) {
+        if ((rt = _res_thread_alloc()) == NULL) {
+            return NULL;
+        }
+        rt->_h_errno = 0;
+        rt->_serial = 0;
+        pthread_setspecific( _res_key, rt );
+    }
+    /* Check the serial value for any chanes to net.* properties. */
+    if (rt->_pi == NULL) {
+        rt->_pi = (struct prop_info*) __system_property_find("net.change");
+    }
+    if (rt->_pi == NULL || rt->_serial == rt->_pi->serial) {
+        return rt;
+    }
+    rt->_serial = rt->_pi->serial;
+    /* Reload from system properties. */
+    if ( res_ninit( rt->_nres ) < 0 ) {
+        free(rt);
+        rt = NULL;
+        pthread_setspecific( _res_key, rt );
+    }
+    _resolv_cache_reset(rt->_serial);
+    return rt;
+}
+
+struct __res_state _nres;
+
+#if 0
+struct resolv_cache*
+__get_res_cache(void)
+{
+    _res_thread*  rt = _res_thread_get();
+
+    if (!rt)
+        return NULL;
+
+    if (!rt->_cache) {
+        rt->_cache = _resolv_cache_create();
+    }
+    return rt->_cache;
+}
+#endif
+
+int*
+__get_h_errno(void)
+{
+    _res_thread*  rt    = _res_thread_get();
+    static int    panic = NETDB_INTERNAL;
+
+    return rt ? &rt->_h_errno : &panic;
+}
+
+res_state
+__res_get_state(void)
+{
+    _res_thread*  rt = _res_thread_get();
+
+    return rt ? rt->_nres : NULL;
+}
+
+void
+__res_put_state(res_state res)
+{
+    /* nothing to do */
+    res=res;
+}
+
+res_static
+__res_get_static(void)
+{
+    _res_thread*  rt = _res_thread_get();
+
+    return rt ? rt->_rstatic : NULL;
+}