Sync with upstream for gethnamaddr.c.
authorYabin Cui <yabinc@google.com>
Wed, 17 Dec 2014 01:03:44 +0000 (17:03 -0800)
committerYabin Cui <yabinc@google.com>
Thu, 18 Dec 2014 00:19:27 +0000 (16:19 -0800)
Bug: 18566967
Change-Id: I37e7410226b49eec67614e20b2c1d5e3e47817a5

libc/dns/gethnamaddr.c
libc/dns/include/hostent.h [new file with mode: 0644]
libc/dns/net/sethostent.c [new file with mode: 0644]
libc/include/netdb.h
tests/netdb_test.cpp

index 736858ff2ae06c620b769562d3092fc86e600d8a..63a6a60d3e218f4f7d6bbe1ba5cac7cd6844a302 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: gethnamaddr.c,v 1.70 2006/03/22 00:03:51 christos Exp $        */
+/*     $NetBSD: gethnamaddr.c,v 1.91 2014/06/19 15:08:18 christos Exp $        */
 
 /*
  * ++Copyright++ 1985, 1988, 1993
 #include <stdlib.h>
 #include <string.h>
 
+#include "hostent.h"
+
+#define maybe_ok(res, nm, ok) (((res)->options & RES_NOCHECKNAME) != 0U || \
+                               (ok)(nm) != 0)
+#define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok)
+#define maybe_dnok(res, dn) maybe_ok((res), (dn), res_dnok)
+
+#define addalias(d, s, arr, siz) do {                  \
+       if (d >= &arr[siz]) {                           \
+               char **xptr = realloc(arr, (siz + 10) * sizeof(*arr)); \
+               if (xptr == NULL)                       \
+                       goto nospc;                     \
+               d = xptr + (d - arr);                   \
+               arr = xptr;                             \
+               siz += 10;                              \
+       }                                               \
+       *d++ = s;                                       \
+} while (/*CONSTCOND*/0)
+
+#define setup(arr, siz) do {                           \
+       arr = malloc((siz = 10) * sizeof(*arr));        \
+       if (arr == NULL)                                \
+               goto nospc;                             \
+} while (/*CONSTCOND*/0)
+
 // This should be synchronized to ResponseCode.h
 static const int DnsProxyQueryResult = 222;
 
@@ -107,18 +132,15 @@ typedef union {
 } align;
 
 #ifdef DEBUG
-static void dprintf(const char *, res_state, ...)
+static void debugprintf(const char *, res_state, ...)
        __attribute__((__format__(__printf__, 1, 3)));
 #endif
 static struct hostent *getanswer(const querybuf *, int, const char *, int,
-    res_state);
+    res_state, struct hostent *, char *, size_t, int *);
 static void map_v4v6_address(const char *, char *);
 static void map_v4v6_hostent(struct hostent *, char **, char *);
 static void addrsort(char **, int, res_state);
 
-static void _sethtent(int);
-static void _endhtent(void);
-static struct hostent *_gethtent(void);
 void ht_sethostent(int);
 void ht_endhostent(void);
 struct hostent *ht_gethostbyname(char *);
@@ -126,13 +148,13 @@ struct hostent *ht_gethostbyaddr(const char *, int, int);
 void dns_service(void);
 #undef dn_skipname
 int dn_skipname(const u_char *, const u_char *);
-static int _gethtbyaddr(void *, void *, va_list);
-static int _gethtbyname(void *, void *, va_list);
-static struct hostent *_gethtbyname2(const char *, int);
 static int _dns_gethtbyaddr(void *, void *, va_list);
 static int _dns_gethtbyname(void *, void *, va_list);
 
-static struct hostent *gethostbyname_internal(const char *, int, res_state, unsigned, unsigned);
+static struct hostent *gethostbyname_internal(const char *, int, res_state,
+    struct hostent *, char *, size_t, int *, unsigned, unsigned);
+static struct hostent* android_gethostbyaddrfornet_proxy_internal(const void*, socklen_t,
+    int, struct hostent *, char *, size_t, int *, unsigned, unsigned);
 
 static const ns_src default_dns_files[] = {
        { NSSRC_FILES,  NS_SUCCESS },
@@ -143,9 +165,9 @@ static const ns_src default_dns_files[] = {
 
 #ifdef DEBUG
 static void
-dprintf(const char *msg, res_state res, ...)
+debugprintf(const char *msg, res_state res, ...)
 {
-       assert(msg != NULL);
+       _DIAGASSERT(msg != NULL);
 
        if (res->options & RES_DEBUG) {
                int save = errno;
@@ -159,48 +181,47 @@ dprintf(const char *msg, res_state res, ...)
        }
 }
 #else
-# define dprintf(msg, res, num) ((void)0) /*nada*/
+# define debugprintf(msg, res, num) /*nada*/
 #endif
 
 #define BOUNDED_INCR(x) \
        do { \
                cp += (x); \
-               if (cp > eom) { \
-                       h_errno = NO_RECOVERY; \
-                       return NULL; \
-               } \
+               if (cp > eom) \
+                       goto no_recovery; \
        } while (/*CONSTCOND*/0)
 
 #define BOUNDS_CHECK(ptr, count) \
        do { \
-               if ((ptr) + (count) > eom) { \
-                       h_errno = NO_RECOVERY; \
-                       return NULL; \
-               } \
+               if ((ptr) + (count) > eom) \
+                       goto no_recovery; \
        } while (/*CONSTCOND*/0)
 
 static struct hostent *
 getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
-    res_state res)
+    res_state res, struct hostent *hent, char *buf, size_t buflen, int *he)
 {
        const HEADER *hp;
        const u_char *cp;
        int n;
+       size_t qlen;
        const u_char *eom, *erdata;
        char *bp, **ap, **hap, *ep;
        int type, class, ancount, qdcount;
        int haveanswer, had_error;
        int toobig = 0;
        char tbuf[MAXDNAME];
+       char **aliases;
+       size_t maxaliases;
+       char *addr_ptrs[MAXADDRS];
        const char *tname;
        int (*name_ok)(const char *);
-       res_static  rs = __res_get_static();
 
-       assert(answer != NULL);
-       assert(qname != NULL);
+       _DIAGASSERT(answer != NULL);
+       _DIAGASSERT(qname != NULL);
 
        tname = qname;
-       rs->host.h_name = NULL;
+       hent->h_name = NULL;
        eom = answer->buf + anslen;
        switch (qtype) {
        case T_A:
@@ -211,54 +232,51 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                name_ok = res_dnok;
                break;
        default:
+         *he = NO_RECOVERY;
                return NULL;    /* XXX should be abort(); */
        }
+
+       setup(aliases, maxaliases);
        /*
         * find first satisfactory answer
         */
        hp = &answer->hdr;
        ancount = ntohs(hp->ancount);
        qdcount = ntohs(hp->qdcount);
-       bp = rs->hostbuf;
-       ep = rs->hostbuf + sizeof rs->hostbuf;
+       bp = buf;
+       ep = buf + buflen;
        cp = answer->buf;
        BOUNDED_INCR(HFIXEDSZ);
-       if (qdcount != 1) {
-               h_errno = NO_RECOVERY;
-               return NULL;
-       }
-       n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
-       if ((n < 0) || !(*name_ok)(bp)) {
-               h_errno = NO_RECOVERY;
-               return NULL;
-       }
+       if (qdcount != 1)
+               goto no_recovery;
+
+       n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));
+       if ((n < 0) || !maybe_ok(res, bp, name_ok))
+               goto no_recovery;
+
        BOUNDED_INCR(n + QFIXEDSZ);
        if (qtype == T_A || qtype == T_AAAA) {
                /* res_send() has already verified that the query name is the
                 * same as the one we sent; this just gets the expanded name
                 * (i.e., with the succeeding search-domain tacked on).
                 */
-               n = strlen(bp) + 1;             /* for the \0 */
-               if (n >= MAXHOSTNAMELEN) {
-                       h_errno = NO_RECOVERY;
-                       return NULL;
-               }
-               rs->host.h_name = bp;
+               n = (int)strlen(bp) + 1;                /* for the \0 */
+               if (n >= MAXHOSTNAMELEN)
+                       goto no_recovery;
+               hent->h_name = bp;
                bp += n;
                /* The qname can be abbreviated, but h_name is now absolute. */
-               qname = rs->host.h_name;
+               qname = hent->h_name;
        }
-       ap = rs->host_aliases;
+       hent->h_aliases = ap = aliases;
+       hent->h_addr_list = hap = addr_ptrs;
        *ap = NULL;
-       rs->host.h_aliases = rs->host_aliases;
-       hap = rs->h_addr_ptrs;
        *hap = NULL;
-       rs->host.h_addr_list = rs->h_addr_ptrs;
        haveanswer = 0;
        had_error = 0;
        while (ancount-- > 0 && cp < eom && !had_error) {
-               n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
-               if ((n < 0) || !(*name_ok)(bp)) {
+               n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));
+               if ((n < 0) || !maybe_ok(res, bp, name_ok)) {
                        had_error++;
                        continue;
                }
@@ -278,50 +296,46 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                        continue;               /* XXX - had_error++ ? */
                }
                if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
-                       if (ap >= &rs->host_aliases[MAXALIASES-1])
-                               continue;
-                       n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
-                       if ((n < 0) || !(*name_ok)(tbuf)) {
+                       n = dn_expand(answer->buf, eom, cp, tbuf,
+                           (int)sizeof tbuf);
+                       if ((n < 0) || !maybe_ok(res, tbuf, name_ok)) {
                                had_error++;
                                continue;
                        }
                        cp += n;
-                       if (cp != erdata) {
-                               h_errno = NO_RECOVERY;
-                               return NULL;
-                       }
+                       if (cp != erdata)
+                               goto no_recovery;
                        /* Store alias. */
-                       *ap++ = bp;
-                       n = strlen(bp) + 1;     /* for the \0 */
+                       addalias(ap, bp, aliases, maxaliases);
+                       n = (int)strlen(bp) + 1;        /* for the \0 */
                        if (n >= MAXHOSTNAMELEN) {
                                had_error++;
                                continue;
                        }
                        bp += n;
                        /* Get canonical name. */
-                       n = strlen(tbuf) + 1;   /* for the \0 */
+                       n = (int)strlen(tbuf) + 1;      /* for the \0 */
                        if (n > ep - bp || n >= MAXHOSTNAMELEN) {
                                had_error++;
                                continue;
                        }
                        strlcpy(bp, tbuf, (size_t)(ep - bp));
-                       rs->host.h_name = bp;
+                       hent->h_name = bp;
                        bp += n;
                        continue;
                }
                if (qtype == T_PTR && type == T_CNAME) {
-                       n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
-                       if (n < 0 || !res_dnok(tbuf)) {
+                       n = dn_expand(answer->buf, eom, cp, tbuf,
+                           (int)sizeof tbuf);
+                       if (n < 0 || !maybe_dnok(res, tbuf)) {
                                had_error++;
                                continue;
                        }
                        cp += n;
-                       if (cp != erdata) {
-                               h_errno = NO_RECOVERY;
-                               return NULL;
-                       }
+                       if (cp != erdata)
+                               goto no_recovery;
                        /* Get canonical name. */
-                       n = strlen(tbuf) + 1;   /* for the \0 */
+                       n = (int)strlen(tbuf) + 1;      /* for the \0 */
                        if (n > ep - bp || n >= MAXHOSTNAMELEN) {
                                had_error++;
                                continue;
@@ -348,25 +362,21 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                                cp += n;
                                continue;       /* XXX - had_error++ ? */
                        }
-                       n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
-                       if ((n < 0) || !res_hnok(bp)) {
+                       n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));
+                       if ((n < 0) || !maybe_hnok(res, bp)) {
                                had_error++;
                                break;
                        }
 #if MULTI_PTRS_ARE_ALIASES
                        cp += n;
-                       if (cp != erdata) {
-                               h_errno = NO_RECOVERY;
-                               return NULL;
-                       }
+                       if (cp != erdata)
+                               goto no_recovery;
                        if (!haveanswer)
-                               rs->host.h_name = bp;
-                       else if (ap < &rs->host_aliases[MAXALIASES-1])
-                               *ap++ = bp;
+                               hent->h_name = bp;
                        else
-                               n = -1;
+                               addalias(ap, bp, aliases, maxaliases);
                        if (n != -1) {
-                               n = strlen(bp) + 1;     /* for the \0 */
+                               n = (int)strlen(bp) + 1;        /* for the \0 */
                                if (n >= MAXHOSTNAMELEN) {
                                        had_error++;
                                        break;
@@ -375,7 +385,7 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                        }
                        break;
 #else
-                       rs->host.h_name = bp;
+                       hent->h_name = bp;
                        if (res->options & RES_USE_INET6) {
                                n = strlen(bp) + 1;     /* for the \0 */
                                if (n >= MAXHOSTNAMELEN) {
@@ -383,26 +393,25 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                                        break;
                                }
                                bp += n;
-                               map_v4v6_hostent(&rs->host, &bp, ep);
+                               map_v4v6_hostent(hent, &bp, ep);
                        }
-                       h_errno = NETDB_SUCCESS;
-                       return &rs->host;
+                       goto success;
 #endif
                case T_A:
                case T_AAAA:
-                       if (strcasecmp(rs->host.h_name, bp) != 0) {
+                       if (strcasecmp(hent->h_name, bp) != 0) {
                                syslog(LOG_NOTICE|LOG_AUTH,
-                                      AskedForGot, rs->host.h_name, bp);
+                                      AskedForGot, hent->h_name, bp);
                                cp += n;
                                continue;       /* XXX - had_error++ ? */
                        }
-                       if (n != rs->host.h_length) {
+                       if (n != hent->h_length) {
                                cp += n;
                                continue;
                        }
                        if (type == T_AAAA) {
                                struct in6_addr in6;
-                               memcpy(&in6, cp, IN6ADDRSZ);
+                               memcpy(&in6, cp, NS_IN6ADDRSZ);
                                if (IN6_IS_ADDR_V4MAPPED(&in6)) {
                                        cp += n;
                                        continue;
@@ -411,33 +420,32 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                        if (!haveanswer) {
                                int nn;
 
-                               rs->host.h_name = bp;
-                               nn = strlen(bp) + 1;    /* for the \0 */
+                               hent->h_name = bp;
+                               nn = (int)strlen(bp) + 1;       /* for the \0 */
                                bp += nn;
                        }
 
                        bp += sizeof(align) -
                            (size_t)((u_long)bp % sizeof(align));
 
-                       if (bp + n >= &rs->hostbuf[sizeof rs->hostbuf]) {
-                               dprintf("size (%d) too big\n", res, n);
+                       if (bp + n >= ep) {
+                               debugprintf("size (%d) too big\n", res, n);
                                had_error++;
                                continue;
                        }
-                       if (hap >= &rs->h_addr_ptrs[MAXADDRS-1]) {
-                               if (!toobig++)
-                                       dprintf("Too many addresses (%d)\n",
+                       if (hap >= &addr_ptrs[MAXADDRS - 1]) {
+                               if (!toobig++) {
+                                       debugprintf("Too many addresses (%d)\n",
                                                res, MAXADDRS);
+                               }
                                cp += n;
                                continue;
                        }
                        (void)memcpy(*hap++ = bp, cp, (size_t)n);
                        bp += n;
                        cp += n;
-                       if (cp != erdata) {
-                               h_errno = NO_RECOVERY;
-                               return NULL;
-                       }
+                       if (cp != erdata)
+                               goto no_recovery;
                        break;
                default:
                        abort();
@@ -454,83 +462,103 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
                 * address in that case, not some random one
                 */
                if (res->nsort && haveanswer > 1 && qtype == T_A)
-                       addrsort(rs->h_addr_ptrs, haveanswer, res);
-               if (!rs->host.h_name) {
-                       n = strlen(qname) + 1;  /* for the \0 */
+                       addrsort(addr_ptrs, haveanswer, res);
+               if (!hent->h_name) {
+                       n = (int)strlen(qname) + 1;     /* for the \0 */
                        if (n > ep - bp || n >= MAXHOSTNAMELEN)
                                goto no_recovery;
                        strlcpy(bp, qname, (size_t)(ep - bp));
-                       rs->host.h_name = bp;
+                       hent->h_name = bp;
                        bp += n;
                }
                if (res->options & RES_USE_INET6)
-                       map_v4v6_hostent(&rs->host, &bp, ep);
-               h_errno = NETDB_SUCCESS;
-               return &rs->host;
+                       map_v4v6_hostent(hent, &bp, ep);
+         goto success;
        }
- no_recovery:
-       h_errno = NO_RECOVERY;
+no_recovery:
+       free(aliases);
+       *he = NO_RECOVERY;
+       return NULL;
+success:
+       bp = (char *)ALIGN(bp);
+       n = (int)(ap - aliases);
+       qlen = (n + 1) * sizeof(*hent->h_aliases);
+       if ((size_t)(ep - bp) < qlen)
+               goto nospc;
+       hent->h_aliases = (void *)bp;
+       memcpy(bp, aliases, qlen);
+       free(aliases);
+       aliases = NULL;
+
+       bp += qlen;
+       n = (int)(hap - addr_ptrs);
+       qlen = (n + 1) * sizeof(*hent->h_addr_list);
+       if ((size_t)(ep - bp) < qlen)
+               goto nospc;
+       hent->h_addr_list = (void *)bp;
+       memcpy(bp, addr_ptrs, qlen);
+       *he = NETDB_SUCCESS;
+       return hent;
+nospc:
+       free(aliases);
+       errno = ENOSPC;
+       *he = NETDB_INTERNAL;
        return NULL;
 }
 
+/* The prototype of gethostbyname_r is from glibc, not that in netbsd. */
 int
 gethostbyname_r(const char *name, struct hostent *hp, char *buf, size_t buflen,
-    struct hostent**result, int *errorp)
-{
-        struct hostent *res;
-
-        res = gethostbyname(name);
-        *errorp = h_errno;
-        if (res == NULL) {
-                *result = NULL;
-                return -1;
-        }
-        memcpy(hp, res, sizeof *hp);
-        *result = hp;
-        return 0;
-}
-
-struct hostent *
-gethostbyname(const char *name)
+    struct hostent **result, int *errorp)
 {
-       struct hostent *hp;
        res_state res = __res_get_state();
 
-       if (res == NULL)
-               return NULL;
+       if (res == NULL) {
+         *result = NULL;
+               *errorp = NETDB_INTERNAL;
+               return -1;
+       }
 
-       assert(name != NULL);
+       _DIAGASSERT(name != NULL);
 
-       /* try IPv6 first - if that fails do IPv4 */
        if (res->options & RES_USE_INET6) {
-               hp = gethostbyname_internal(name, AF_INET6, res, NETID_UNSET, MARK_UNSET);
-               if (hp) {
+               *result = gethostbyname_internal(name, AF_INET6, res, hp, buf, buflen, errorp, NETID_UNSET,
+                                                MARK_UNSET);
+               if (*result) {
                        __res_put_state(res);
-                       return hp;
+                       return 0;
                }
        }
-       hp = gethostbyname_internal(name, AF_INET, res, NETID_UNSET, MARK_UNSET);
+       *result = gethostbyname_internal(name, AF_INET, res, hp, buf, buflen, errorp, NETID_UNSET,
+                                        MARK_UNSET);
        __res_put_state(res);
-       return hp;
-}
-
-struct hostent *
-gethostbyname2(const char *name, int af)
-{
-       return android_gethostbynamefornet(name, af, NETID_UNSET, MARK_UNSET);
+       if (!*result && errno == ENOSPC) {
+         errno = ERANGE;
+         return ERANGE; /* Return error as in linux manual page. */
+       }
+       return (*result) ? 0 : -1;
 }
 
-struct hostent *
-android_gethostbynamefornet(const char *name, int af, unsigned netid, unsigned mark)
+/* The prototype of gethostbyname2_r is from glibc, not that in netbsd. */
+int
+gethostbyname2_r(const char *name, int af, struct hostent *hp, char *buf,
+    size_t buflen, struct hostent **result, int *errorp)
 {
-       struct hostent *hp;
        res_state res = __res_get_state();
 
-       if (res == NULL)
-               return NULL;
-       hp = gethostbyname_internal(name, af, res, netid, mark);
+       if (res == NULL) {
+               *result = NULL;
+               *errorp = NETDB_INTERNAL;
+               return -1;
+       }
+       *result = gethostbyname_internal(name, af, res, hp, buf, buflen, errorp, NETID_UNSET,
+                                        MARK_UNSET);
        __res_put_state(res);
-       return hp;
+       if (!*result && errno == ENOSPC) {
+               errno = ERANGE;
+               return ERANGE;
+       }
+       return (*result) ? 0 : -1;
 }
 
 __LIBC_HIDDEN__ FILE* android_open_proxy() {
@@ -562,7 +590,7 @@ __LIBC_HIDDEN__ FILE* android_open_proxy() {
 }
 
 static struct hostent *
-android_read_hostent(FILE* proxy)
+android_read_hostent(FILE* proxy, struct hostent* hp, char* hbuf, size_t hbuflen, int *he)
 {
        uint32_t size;
        char buf[4];
@@ -573,21 +601,27 @@ android_read_hostent(FILE* proxy)
        int result_code = strtol(buf, NULL, 10);
        if (result_code != DnsProxyQueryResult) {
                fread(&size, 1, sizeof(size), proxy);
+               *he = HOST_NOT_FOUND;
                return NULL;
        }
 
        if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
        size = ntohl(size);
-       res_static rs = __res_get_static();
-       memset(&rs->host, 0, sizeof(rs->host));
-       char *ptr = rs->hostbuf;
 
+       memset(hp, 0, sizeof(*hp));
+       char *ptr = hbuf;
+       char *hbuf_end = hbuf + hbuflen;
+
+       if (ptr + size > hbuf_end) {
+               goto nospc;
+       }
        if (fread(ptr, 1, size, proxy) != size) return NULL;
+       hp->h_name = ptr;
        ptr += size;
-       rs->host.h_name = rs->hostbuf;
 
-       char **aliases = rs->host_aliases;
-       rs->host.h_aliases = rs->host_aliases;
+       char *aliases_ptrs[MAXALIASES];
+       char **aliases = &aliases_ptrs[0];
+
        while (1) {
                if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
                size = ntohl(size);
@@ -596,74 +630,107 @@ android_read_hostent(FILE* proxy)
                        *aliases = NULL;
                        break;
                }
+               if (ptr + size > hbuf_end) {
+                 goto nospc;
+               }
                if (fread(ptr, 1, size, proxy) != size) return NULL;
-               *aliases++ = ptr;
+               if (aliases < &aliases_ptrs[MAXALIASES - 1]) {
+                 *aliases++ = ptr;
+               }
                ptr += size;
        }
 
+       int aliases_len = ((int)(aliases - aliases_ptrs) + 1) * sizeof(*hp->h_aliases);
+       if (ptr + aliases_len > hbuf_end) {
+               goto nospc;
+       }
+       hp->h_aliases = (void*)ptr;
+       memcpy(ptr, aliases_ptrs, aliases_len);
+       ptr += aliases_len;
+
        if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
-       rs->host.h_addrtype = ntohl(size);
+       hp->h_addrtype = ntohl(size);
 
        if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
-       rs->host.h_length = ntohl(size);
+       hp->h_length = ntohl(size);
+
+       char *addr_ptrs[MAXADDRS];
+       char **addr_p = &addr_ptrs[0];
 
-       char **addrs = rs->h_addr_ptrs;
-       rs->host.h_addr_list = rs->h_addr_ptrs;
        while (1) {
                if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
                size = ntohl(size);
                if (size == 0) {
-                       *addrs = NULL;
+                       *addr_p = NULL;
                        break;
                }
+               if (ptr + size > hbuf_end) {
+                 goto nospc;
+               }
                if (fread(ptr, 1, size, proxy) != size) return NULL;
-               *addrs++ = ptr;
+               if (addr_p < &addr_ptrs[MAXADDRS - 1]) {
+                 *addr_p++ = ptr;
+               }
                ptr += size;
        }
 
-       return &rs->host;
-}
+       int addrs_len = ((int)(addr_p - addr_ptrs) + 1) * sizeof(*hp->h_addr_list);
+       if (ptr + addrs_len > hbuf_end) {
+               goto nospc;
+       }
+       hp->h_addr_list = (void*)ptr;
+       memcpy(ptr, addr_ptrs, addrs_len);
+       *he = NETDB_SUCCESS;
+       return hp;
 
+nospc:
+       *he = NETDB_INTERNAL;
+       errno = ENOSPC;
+       return NULL;
+}
 
 static struct hostent *
-gethostbyname_internal_real(const char *name, int af, res_state res)
+gethostbyname_internal_real(const char *name, int af, res_state res, struct hostent *hp, char *buf,
+                            size_t buflen, int *he)
 {
        const char *cp;
-       char *bp, *ep;
-       int size;
-       struct hostent *hp;
-       res_static rs = __res_get_static();
-
+       struct getnamaddr info;
+       char hbuf[MAXHOSTNAMELEN];
+       size_t size;
        static const ns_dtab dtab[] = {
-               NS_FILES_CB(_gethtbyname, NULL)
+               NS_FILES_CB(_hf_gethtbyname, NULL)
                { NSSRC_DNS, _dns_gethtbyname, NULL },  /* force -DHESIOD */
-               { 0, 0, 0 }
+               NS_NIS_CB(_yp_gethtbyname, NULL)
+               NS_NULL_CB
        };
 
-       assert(name != NULL);
+       _DIAGASSERT(name != NULL);
 
        switch (af) {
        case AF_INET:
-               size = INADDRSZ;
+               size = NS_INADDRSZ;
                break;
        case AF_INET6:
-               size = IN6ADDRSZ;
+               size = NS_IN6ADDRSZ;
                break;
        default:
-               h_errno = NETDB_INTERNAL;
+               *he = NETDB_INTERNAL;
                errno = EAFNOSUPPORT;
                return NULL;
        }
+       if (buflen < size)
+               goto nospc;
 
-       rs->host.h_addrtype = af;
-       rs->host.h_length = size;
+       hp->h_addrtype = af;
+       hp->h_length = (int)size;
 
        /*
         * if there aren't any dots, it could be a user-level alias.
         * this is also done in res_nquery() since we are not the only
         * function that looks up host names.
         */
-       if (!strchr(name, '.') && (cp = __hostalias(name)))
+       if (!strchr(name, '.') && (cp = res_hostalias(res, name,
+           hbuf, sizeof(hbuf))))
                name = cp;
 
        /*
@@ -680,25 +747,7 @@ gethostbyname_internal_real(const char *name, int af, res_state res)
                                 * Fake up a hostent as if we'd actually
                                 * done a lookup.
                                 */
-                               if (inet_pton(af, name,
-                                   (char *)(void *)rs->host_addr) <= 0) {
-                                       h_errno = HOST_NOT_FOUND;
-                                       return NULL;
-                               }
-                               strncpy(rs->hostbuf, name, MAXDNAME);
-                               rs->hostbuf[MAXDNAME] = '\0';
-                               bp = rs->hostbuf + MAXDNAME;
-                               ep = rs->hostbuf + sizeof rs->hostbuf;
-                               rs->host.h_name = rs->hostbuf;
-                               rs->host.h_aliases = rs->host_aliases;
-                               rs->host_aliases[0] = NULL;
-                               rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
-                               rs->h_addr_ptrs[1] = NULL;
-                               rs->host.h_addr_list = rs->h_addr_ptrs;
-                               if (res->options & RES_USE_INET6)
-                                       map_v4v6_hostent(&rs->host, &bp, ep);
-                               h_errno = NETDB_SUCCESS;
-                               return &rs->host;
+                               goto fake;
                        }
                        if (!isdigit((u_char) *cp) && *cp != '.')
                                break;
@@ -714,49 +763,60 @@ gethostbyname_internal_real(const char *name, int af, res_state res)
                                 * Fake up a hostent as if we'd actually
                                 * done a lookup.
                                 */
-                               if (inet_pton(af, name,
-                                   (char *)(void *)rs->host_addr) <= 0) {
-                                       h_errno = HOST_NOT_FOUND;
-                                       return NULL;
-                               }
-                               strncpy(rs->hostbuf, name, MAXDNAME);
-                               rs->hostbuf[MAXDNAME] = '\0';
-                               bp = rs->hostbuf + MAXDNAME;
-                               ep = rs->hostbuf + sizeof rs->hostbuf;
-                               rs->host.h_name = rs->hostbuf;
-                               rs->host.h_aliases = rs->host_aliases;
-                               rs->host_aliases[0] = NULL;
-                               rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
-                               rs->h_addr_ptrs[1] = NULL;
-                               rs->host.h_addr_list = rs->h_addr_ptrs;
-                               h_errno = NETDB_SUCCESS;
-                               return &rs->host;
+                               goto fake;
                        }
                        if (!isxdigit((u_char) *cp) && *cp != ':' && *cp != '.')
                                break;
                }
 
-       hp = NULL;
-       h_errno = NETDB_INTERNAL;
-       if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyname",
-           default_dns_files, name, strlen(name), af) != NS_SUCCESS) {
+       *he = NETDB_INTERNAL;
+       info.hp = hp;
+       info.buf = buf;
+       info.buflen = buflen;
+       info.he = he;
+       if (nsdispatch(&info, dtab, NSDB_HOSTS, "gethostbyname",
+           default_dns_files, name, strlen(name), af) != NS_SUCCESS)
+               return NULL;
+       *he = NETDB_SUCCESS;
+       return hp;
+nospc:
+       *he = NETDB_INTERNAL;
+       errno = ENOSPC;
+       return NULL;
+fake:
+       HENT_ARRAY(hp->h_addr_list, 1, buf, buflen);
+       HENT_ARRAY(hp->h_aliases, 0, buf, buflen);
+
+       hp->h_aliases[0] = NULL;
+       if (size > buflen)
+               goto nospc;
+
+       if (inet_pton(af, name, buf) <= 0) {
+               *he = HOST_NOT_FOUND;
                return NULL;
        }
-       h_errno = NETDB_SUCCESS;
+       hp->h_addr_list[0] = buf;
+       hp->h_addr_list[1] = NULL;
+       buf += size;
+       buflen -= size;
+       HENT_SCOPY(hp->h_name, name, buf, buflen);
+       if (res->options & RES_USE_INET6)
+               map_v4v6_hostent(hp, &buf, buf + buflen);
+       *he = NETDB_SUCCESS;
        return hp;
 }
 
-
 // very similar in proxy-ness to android_getaddrinfo_proxy
 static struct hostent *
-gethostbyname_internal(const char *name, int af, res_state res, unsigned netid, unsigned mark)
+gethostbyname_internal(const char *name, int af, res_state res, struct hostent *hp, char *hbuf,
+                       size_t hbuflen, int *errorp, unsigned netid, unsigned mark)
 {
        FILE* proxy = android_open_proxy();
        if (proxy == NULL) {
                // Either we're not supposed to be using the proxy or the proxy is unavailable.
                res_setnetid(res, netid);
                res_setmark(res, mark);
-               return gethostbyname_internal_real(name, af, res);
+               return gethostbyname_internal_real(name, af, res, hp, hbuf, hbuflen, errorp);
        }
 
        netid = __netdClientDispatch.netIdForResolv(netid);
@@ -776,29 +836,44 @@ gethostbyname_internal(const char *name, int af, res_state res, unsigned netid,
                return NULL;
        }
 
-       struct hostent* result = android_read_hostent(proxy);
+       struct hostent* result = android_read_hostent(proxy, hp, hbuf, hbuflen, errorp);
        fclose(proxy);
        return result;
 }
 
+/* The prototype of gethostbyaddr_r is from glibc, not that in netbsd. */
+int gethostbyaddr_r(const void *addr, socklen_t len, int af, struct hostent *hp, char *buf,
+                    size_t buflen, struct hostent **result, int *h_errnop)
+{
+       *result = android_gethostbyaddrfornet_proxy_internal(addr, len, af, hp, buf, buflen, h_errnop,
+                                                       NETID_UNSET, MARK_UNSET);
+       if (!*result && errno == ENOSPC) {
+               errno = ERANGE;
+               return ERANGE;
+       }
+       return (*result) ? 0 : -1;
+}
 
 static struct hostent *
-android_gethostbyaddrfornet_real(const void *addr, socklen_t len, int af, unsigned netid, unsigned mark) {
+android_gethostbyaddrfornet_real(const void *addr, socklen_t len, int af, struct hostent *hp,
+                                 char *buf, size_t buflen, int *he, unsigned netid, unsigned mark)
+{
        const u_char *uaddr = (const u_char *)addr;
        socklen_t size;
-       struct hostent *hp;
+       struct getnamaddr info;
        static const ns_dtab dtab[] = {
-               NS_FILES_CB(_gethtbyaddr, NULL)
+               NS_FILES_CB(_hf_gethtbyaddr, NULL)
                { NSSRC_DNS, _dns_gethtbyaddr, NULL },  /* force -DHESIOD */
-               { 0, 0, 0 }
+               NS_NIS_CB(_yp_gethtbyaddr, NULL)
+               NS_NULL_CB
        };
 
-       assert(addr != NULL);
+       _DIAGASSERT(addr != NULL);
 
        if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
            (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)addr) ||
             IN6_IS_ADDR_SITELOCAL((const struct in6_addr *)addr))) {
-               h_errno = HOST_NOT_FOUND;
+               *he = HOST_NOT_FOUND;
                return NULL;
        }
        if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
@@ -819,29 +894,35 @@ android_gethostbyaddrfornet_real(const void *addr, socklen_t len, int af, unsign
                break;
        default:
                errno = EAFNOSUPPORT;
-               h_errno = NETDB_INTERNAL;
+               *he = NETDB_INTERNAL;
                return NULL;
        }
        if (size != len) {
                errno = EINVAL;
-               h_errno = NETDB_INTERNAL;
+               *he = NETDB_INTERNAL;
                return NULL;
        }
-       hp = NULL;
-       h_errno = NETDB_INTERNAL;
-       if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyaddr",
-               default_dns_files, uaddr, len, af, netid, mark) != NS_SUCCESS)
+       info.hp = hp;
+       info.buf = buf;
+       info.buflen = buflen;
+       info.he = he;
+       *he = NETDB_INTERNAL;
+       if (nsdispatch(&info, dtab, NSDB_HOSTS, "gethostbyaddr",
+           default_dns_files, uaddr, len, af, netid, mark) != NS_SUCCESS)
                return NULL;
-       h_errno = NETDB_SUCCESS;
+       *he = NETDB_SUCCESS;
        return hp;
 }
 
-__LIBC_HIDDEN__ struct hostent*
-android_gethostbyaddrfornet_proxy(const void* addr, socklen_t len, int af, unsigned netid, unsigned mark) {
+static struct hostent*
+android_gethostbyaddrfornet_proxy_internal(const void* addr, socklen_t len, int af,
+                             struct hostent *hp, char *hbuf, size_t hbuflen, int *he,
+                             unsigned netid, unsigned mark)
+{
        FILE* proxy = android_open_proxy();
        if (proxy == NULL) {
                // Either we're not supposed to be using the proxy or the proxy is unavailable.
-               return android_gethostbyaddrfornet_real(addr,len, af, netid, mark);
+               return android_gethostbyaddrfornet_real(addr,len, af, hp, hbuf, hbuflen, he, netid, mark);
        }
 
        char buf[INET6_ADDRSTRLEN];  //big enough for IPv4 and IPv6
@@ -864,302 +945,144 @@ android_gethostbyaddrfornet_proxy(const void* addr, socklen_t len, int af, unsig
                return NULL;
        }
 
-       struct hostent *result = android_read_hostent(proxy);
+       struct hostent *result = android_read_hostent(proxy, hp, hbuf, hbuflen, he);
        fclose(proxy);
        return result;
 }
 
-struct hostent *
-android_gethostbyaddrfornet(const void *addr, socklen_t len, int af, unsigned netid, unsigned mark)
+struct hostent*
+netbsd_gethostent_r(FILE *hf, struct hostent *hent, char *buf, size_t buflen, int *he)
 {
-       return android_gethostbyaddrfornet_proxy(addr, len, af, netid, mark);
-}
-
-struct hostent *
-gethostbyaddr(const void *addr, socklen_t len, int af)
-{
-       return android_gethostbyaddrfornet(addr, len, af, NETID_UNSET, MARK_UNSET);
-}
-
-
-static void
-_sethtent(int f)
-{
-    res_static  rs = __res_get_static();
-    if (rs == NULL) return;
-       if (!rs->hostf)
-               rs->hostf = fopen(_PATH_HOSTS, "re" );
-       else
-               rewind(rs->hostf);
-       rs->stayopen = f;
-}
-
-static void
-_endhtent(void)
-{
-    res_static  rs = __res_get_static();
-    if (rs == NULL) return;
-
-       if (rs->hostf && !rs->stayopen) {
-               (void) fclose(rs->hostf);
-               rs->hostf = NULL;
-       }
-}
-
-static struct hostent *
-_gethtent(void)
-{
-       char *p;
+       char *p, *name;
        char *cp, **q;
        int af, len;
-       res_static  rs = __res_get_static();
+       size_t anum;
+       char **aliases;
+       size_t maxaliases;
+       struct in6_addr host_addr;
 
-       if (!rs->hostf && !(rs->hostf = fopen(_PATH_HOSTS, "re" ))) {
-               h_errno = NETDB_INTERNAL;
+       if (hf == NULL) {
+               *he = NETDB_INTERNAL;
+               errno = EINVAL;
                return NULL;
        }
- again:
-       if (!(p = fgets(rs->hostbuf, sizeof rs->hostbuf, rs->hostf))) {
-               h_errno = HOST_NOT_FOUND;
-               return NULL;
+       p = NULL;
+       setup(aliases, maxaliases);
+
+       /* Allocate a new space to read file lines like upstream does.
+        * To keep reentrancy we cannot use __res_get_static()->hostbuf here,
+        * as the buffer may be used to store content for a previous hostent
+        * returned by non-reentrant functions like gethostbyname().
+        */
+       const size_t line_buf_size = sizeof(__res_get_static()->hostbuf);
+       if ((p = malloc(line_buf_size)) == NULL) {
+         goto nospc;
        }
-       if (*p == '#')
-               goto again;
-       if (!(cp = strpbrk(p, "#\n")))
-               goto again;
-       *cp = '\0';
-       if (!(cp = strpbrk(p, " \t")))
-               goto again;
-       *cp++ = '\0';
-       if (inet_pton(AF_INET6, p, (char *)(void *)rs->host_addr) > 0) {
-               af = AF_INET6;
-               len = IN6ADDRSZ;
-       } else if (inet_pton(AF_INET, p, (char *)(void *)rs->host_addr) > 0) {
-               res_state res = __res_get_state();
-               if (res == NULL)
+       for (;;) {
+               if (!fgets(p, line_buf_size, hf)) {
+                       free(p);
+                       free(aliases);
+                       *he = HOST_NOT_FOUND;
                        return NULL;
-               if (res->options & RES_USE_INET6) {
-                       map_v4v6_address((char *)(void *)rs->host_addr,
-                           (char *)(void *)rs->host_addr);
-                       af = AF_INET6;
-                       len = IN6ADDRSZ;
-               } else {
-                       af = AF_INET;
-                       len = INADDRSZ;
-               }
-               __res_put_state(res);
-       } else {
-               goto again;
-       }
-       /* if this is not something we're looking for, skip it. */
-       if (rs->host.h_addrtype != 0 && rs->host.h_addrtype != af)
-               goto again;
-       if (rs->host.h_length != 0 && rs->host.h_length != len)
-               goto again;
-       rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
-       rs->h_addr_ptrs[1] = NULL;
-       rs->host.h_addr_list = rs->h_addr_ptrs;
-       rs->host.h_length = len;
-       rs->host.h_addrtype = af;
-       while (*cp == ' ' || *cp == '\t')
-               cp++;
-       rs->host.h_name = cp;
-       q = rs->host.h_aliases = rs->host_aliases;
-       if ((cp = strpbrk(cp, " \t")) != NULL)
-               *cp++ = '\0';
-       while (cp && *cp) {
-               if (*cp == ' ' || *cp == '\t') {
-                       cp++;
+               }
+               if (*p == '#') {
                        continue;
                }
-               if (q < &rs->host_aliases[MAXALIASES - 1])
-                       *q++ = cp;
-               if ((cp = strpbrk(cp, " \t")) != NULL)
-                       *cp++ = '\0';
-       }
-       *q = NULL;
-       h_errno = NETDB_SUCCESS;
-       return &rs->host;
-}
-
-/*ARGSUSED*/
-int
-_gethtbyname(void *rv, void *cb_data, va_list ap)
-{
-       struct hostent *hp;
-       const char *name;
-       int af;
-
-       assert(rv != NULL);
-
-       name = va_arg(ap, char *);
-       /* NOSTRICT skip len */(void)va_arg(ap, int);
-       af = va_arg(ap, int);
-
-       hp = NULL;
-#if 0
-       {
-               res_state res = __res_get_state();
-               if (res == NULL)
-                       return NS_NOTFOUND;
-               if (res->options & RES_USE_INET6)
-                       hp = _gethtbyname2(name, AF_INET6);
-               if (hp==NULL)
-                       hp = _gethtbyname2(name, AF_INET);
-               __res_put_state(res);
-       }
-#else
-       hp = _gethtbyname2(name, af);
-#endif
-       *((struct hostent **)rv) = hp;
-       if (hp == NULL) {
-               h_errno = HOST_NOT_FOUND;
-               return NS_NOTFOUND;
-       }
-       return NS_SUCCESS;
-}
-
-static struct hostent *
-_gethtbyname2(const char *name, int af)
-{
-       struct hostent *p;
-       char *tmpbuf, *ptr, **cp;
-       int num;
-       size_t len;
-       res_static rs = __res_get_static();
-
-       assert(name != NULL);
-
-       _sethtent(rs->stayopen);
-       ptr = tmpbuf = NULL;
-       num = 0;
-       while ((p = _gethtent()) != NULL && num < MAXADDRS) {
-               if (p->h_addrtype != af)
+               if (!(cp = strpbrk(p, "#\n"))) {
                        continue;
-               if (strcasecmp(p->h_name, name) != 0) {
-                       for (cp = p->h_aliases; *cp != NULL; cp++)
-                               if (strcasecmp(*cp, name) == 0)
-                                       break;
-                       if (*cp == NULL) continue;
                }
+               *cp = '\0';
+               if (!(cp = strpbrk(p, " \t")))
+                       continue;
+               *cp++ = '\0';
+               if (inet_pton(AF_INET6, p, &host_addr) > 0) {
+                       af = AF_INET6;
+                       len = NS_IN6ADDRSZ;
+               } else {
+                       if (inet_pton(AF_INET, p, &host_addr) <= 0)
+                               continue;
 
-               if (num == 0) {
-                       size_t bufsize;
-                       char *src;
-
-                       bufsize = strlen(p->h_name) + 2 +
-                                 MAXADDRS * p->h_length +
-                                 ALIGNBYTES;
-                       for (cp = p->h_aliases; *cp != NULL; cp++)
-                               bufsize += strlen(*cp) + 1;
-
-                       if ((tmpbuf = malloc(bufsize)) == NULL) {
-                               h_errno = NETDB_INTERNAL;
-                               return NULL;
-                       }
-
-                       ptr = tmpbuf;
-                       src = p->h_name;
-                       while ((*ptr++ = *src++) != '\0');
-                       for (cp = p->h_aliases; *cp != NULL; cp++) {
-                               src = *cp;
-                               while ((*ptr++ = *src++) != '\0');
+                       res_state res = __res_get_state();
+                       if (res == NULL)
+                               goto nospc;
+                       if (res->options & RES_USE_INET6) {
+                               map_v4v6_address(buf, buf);
+                               af = AF_INET6;
+                               len = NS_IN6ADDRSZ;
+                       } else {
+                               af = AF_INET;
+                               len = NS_INADDRSZ;
                        }
-                       *ptr++ = '\0';
-
-                       ptr = (char *)(void *)ALIGN(ptr);
+                       __res_put_state(res);
                }
 
-               (void)memcpy(ptr, p->h_addr_list[0], (size_t)p->h_length);
-               ptr += p->h_length;
-               num++;
-       }
-       _endhtent();
-       if (num == 0) return NULL;
-
-       len = ptr - tmpbuf;
-       if (len > (sizeof(rs->hostbuf) - ALIGNBYTES)) {
-               free(tmpbuf);
-               errno = ENOSPC;
-               h_errno = NETDB_INTERNAL;
-               return NULL;
-       }
-       ptr = memcpy((void *)ALIGN(rs->hostbuf), tmpbuf, len);
-       free(tmpbuf);
-
-       rs->host.h_name = ptr;
-       while (*ptr++);
-
-       cp = rs->host_aliases;
-       while (*ptr) {
-               *cp++ = ptr;
-               while (*ptr++);
-       }
-       ptr++;
-       *cp = NULL;
-
-       ptr = (char *)(void *)ALIGN(ptr);
-       cp = rs->h_addr_ptrs;
-       while (num--) {
-               *cp++ = ptr;
-               ptr += rs->host.h_length;
-       }
-       *cp = NULL;
-
-       return &rs->host;
-}
-
-/*ARGSUSED*/
-static int
-_gethtbyaddr(void *rv, void *cb_data, va_list ap)
-{
-       struct hostent *p;
-       const unsigned char *addr;
-       int len, af;
-       res_static  rs = __res_get_static();
-
-       assert(rv != NULL);
-
-       addr = va_arg(ap, unsigned char *);
-       len = va_arg(ap, int);
-       af = va_arg(ap, int);
-
-       rs->host.h_length = len;
-       rs->host.h_addrtype = af;
+               /* if this is not something we're looking for, skip it. */
+               if (hent->h_addrtype != 0 && hent->h_addrtype != af)
+                       continue;
+               if (hent->h_length != 0 && hent->h_length != len)
+                       continue;
 
-       _sethtent(rs->stayopen);
-       while ((p = _gethtent()) != NULL)
-               if (p->h_addrtype == af && !memcmp(p->h_addr, addr,
-                   (size_t)len))
-                       break;
-       _endhtent();
-       *((struct hostent **)rv) = p;
-       if (p==NULL) {
-               h_errno = HOST_NOT_FOUND;
-               return NS_NOTFOUND;
+               while (*cp == ' ' || *cp == '\t')
+                       cp++;
+               if ((cp = strpbrk(name = cp, " \t")) != NULL)
+                       *cp++ = '\0';
+               q = aliases;
+               while (cp && *cp) {
+                       if (*cp == ' ' || *cp == '\t') {
+                               cp++;
+                               continue;
+                       }
+                       addalias(q, cp, aliases, maxaliases);
+                       if ((cp = strpbrk(cp, " \t")) != NULL)
+                               *cp++ = '\0';
+               }
+               break;
        }
-       return NS_SUCCESS;
+       hent->h_length = len;
+       hent->h_addrtype = af;
+       HENT_ARRAY(hent->h_addr_list, 1, buf, buflen);
+       anum = (size_t)(q - aliases);
+       HENT_ARRAY(hent->h_aliases, anum, buf, buflen);
+       HENT_COPY(hent->h_addr_list[0], &host_addr, hent->h_length, buf,
+           buflen);
+       hent->h_addr_list[1] = NULL;
+
+       HENT_SCOPY(hent->h_name, name, buf, buflen);
+       for (size_t i = 0; i < anum; i++)
+               HENT_SCOPY(hent->h_aliases[i], aliases[i], buf, buflen);
+       hent->h_aliases[anum] = NULL;
+
+       *he = NETDB_SUCCESS;
+       free(p);
+       free(aliases);
+       return hent;
+nospc:
+       free(p);
+       free(aliases);
+       errno = ENOSPC;
+       *he = NETDB_INTERNAL;
+       return NULL;
 }
 
 static void
 map_v4v6_address(const char *src, char *dst)
 {
        u_char *p = (u_char *)dst;
-       char tmp[INADDRSZ];
+       char tmp[NS_INADDRSZ];
        int i;
 
-       assert(src != NULL);
-       assert(dst != NULL);
+       _DIAGASSERT(src != NULL);
+       _DIAGASSERT(dst != NULL);
 
        /* Stash a temporary copy so our caller can update in place. */
-       (void)memcpy(tmp, src, INADDRSZ);
+       (void)memcpy(tmp, src, NS_INADDRSZ);
        /* Mark this ipv6 addr as a mapped ipv4. */
        for (i = 0; i < 10; i++)
                *p++ = 0x00;
        *p++ = 0xff;
        *p++ = 0xff;
        /* Retrieve the saved copy and we're done. */
-       (void)memcpy((void *)p, tmp, INADDRSZ);
+       (void)memcpy(p, tmp, NS_INADDRSZ);
 }
 
 static void
@@ -1167,18 +1090,19 @@ map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep)
 {
        char **ap;
 
-       assert(hp != NULL);
-       assert(bpp != NULL);
-       assert(ep != NULL);
+       _DIAGASSERT(hp != NULL);
+       _DIAGASSERT(bpp != NULL);
+       _DIAGASSERT(ep != NULL);
 
-       if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
+       if (hp->h_addrtype != AF_INET || hp->h_length != NS_INADDRSZ)
                return;
        hp->h_addrtype = AF_INET6;
-       hp->h_length = IN6ADDRSZ;
+       hp->h_length = NS_IN6ADDRSZ;
        for (ap = hp->h_addr_list; *ap; ap++) {
-               int i = sizeof(align) - (size_t)((u_long)*bpp % sizeof(align));
+               int i = (int)(sizeof(align) -
+                   (size_t)((u_long)*bpp % sizeof(align)));
 
-               if (ep - *bpp < (i + IN6ADDRSZ)) {
+               if (ep - *bpp < (i + NS_IN6ADDRSZ)) {
                        /* Out of memory.  Truncate address list here.  XXX */
                        *ap = NULL;
                        return;
@@ -1186,7 +1110,7 @@ map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep)
                *bpp += i;
                map_v4v6_address(*ap, *bpp);
                *ap = *bpp;
-               *bpp += IN6ADDRSZ;
+               *bpp += NS_IN6ADDRSZ;
        }
 }
 
@@ -1198,7 +1122,7 @@ addrsort(char **ap, int num, res_state res)
        short aval[MAXADDRS];
        int needsort = 0;
 
-       assert(ap != NULL);
+       _DIAGASSERT(ap != NULL);
 
        p = ap;
        for (i = 0; i < num; i++, p++) {
@@ -1233,15 +1157,6 @@ addrsort(char **ap, int num, res_state res)
        }
 }
 
-struct hostent *
-gethostent(void)
-{
-    res_static  rs = __res_get_static();
-       rs->host.h_addrtype = 0;
-       rs->host.h_length = 0;
-       return _gethtent();
-}
-
 /*ARGSUSED*/
 static int
 _dns_gethtbyname(void *rv, void *cb_data, va_list ap)
@@ -1250,20 +1165,22 @@ _dns_gethtbyname(void *rv, void *cb_data, va_list ap)
        int n, type;
        struct hostent *hp;
        const char *name;
-       int af;
        res_state res;
+       struct getnamaddr *info = rv;
 
-       assert(rv != NULL);
+       _DIAGASSERT(rv != NULL);
 
        name = va_arg(ap, char *);
-       /* NOSTRICT skip len */(void)va_arg(ap, int);
-       af = va_arg(ap, int);
+       /* NOSTRICT skip string len */(void)va_arg(ap, int);
+       info->hp->h_addrtype = va_arg(ap, int);
 
-       switch (af) {
+       switch (info->hp->h_addrtype) {
        case AF_INET:
+               info->hp->h_length = NS_INADDRSZ;
                type = T_A;
                break;
        case AF_INET6:
+               info->hp->h_length = NS_IN6ADDRSZ;
                type = T_AAAA;
                break;
        default:
@@ -1271,7 +1188,7 @@ _dns_gethtbyname(void *rv, void *cb_data, va_list ap)
        }
        buf = malloc(sizeof(*buf));
        if (buf == NULL) {
-               h_errno = NETDB_INTERNAL;
+               *info->he = NETDB_INTERNAL;
                return NS_NOTFOUND;
        }
        res = __res_get_state();
@@ -1279,14 +1196,15 @@ _dns_gethtbyname(void *rv, void *cb_data, va_list ap)
                free(buf);
                return NS_NOTFOUND;
        }
-       n = res_nsearch(res, name, C_IN, type, buf->buf, sizeof(buf->buf));
+       n = res_nsearch(res, name, C_IN, type, buf->buf, (int)sizeof(buf->buf));
        if (n < 0) {
                free(buf);
-               dprintf("res_nsearch failed (%d)\n", res, n);
+               debugprintf("res_nsearch failed (%d)\n", res, n);
                __res_put_state(res);
                return NS_NOTFOUND;
        }
-       hp = getanswer(buf, n, name, type, res);
+       hp = getanswer(buf, n, name, type, res, info->hp, info->buf,
+           info->buflen, info->he);
        free(buf);
        __res_put_state(res);
        if (hp == NULL)
@@ -1298,7 +1216,6 @@ _dns_gethtbyname(void *rv, void *cb_data, va_list ap)
                default:
                        return NS_UNAVAIL;
                }
-       *((struct hostent **)rv) = hp;
        return NS_SUCCESS;
 }
 
@@ -1311,20 +1228,22 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap)
        querybuf *buf;
        struct hostent *hp;
        const unsigned char *uaddr;
-       int len, af, advance;
+       int advance;
        res_state res;
+       char *bf;
+       size_t blen;
+       struct getnamaddr *info = rv;
        unsigned netid, mark;
-       res_static rs = __res_get_static();
 
-       assert(rv != NULL);
+       _DIAGASSERT(rv != NULL);
 
        uaddr = va_arg(ap, unsigned char *);
-       len = va_arg(ap, int);
-       af = va_arg(ap, int);
+       info->hp->h_length = va_arg(ap, int);
+       info->hp->h_addrtype = va_arg(ap, int);
        netid = va_arg(ap, unsigned);
        mark = va_arg(ap, unsigned);
 
-       switch (af) {
+       switch (info->hp->h_addrtype) {
        case AF_INET:
                (void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa",
                    (uaddr[3] & 0xff), (uaddr[2] & 0xff),
@@ -1334,29 +1253,29 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap)
        case AF_INET6:
                qp = qbuf;
                ep = qbuf + sizeof(qbuf) - 1;
-               for (n = IN6ADDRSZ - 1; n >= 0; n--) {
+               for (n = NS_IN6ADDRSZ - 1; n >= 0; n--) {
                        advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.",
                            uaddr[n] & 0xf,
                            ((unsigned int)uaddr[n] >> 4) & 0xf);
                        if (advance > 0 && qp + advance < ep)
                                qp += advance;
                        else {
-                               h_errno = NETDB_INTERNAL;
+                               *info->he = NETDB_INTERNAL;
                                return NS_NOTFOUND;
                        }
                }
                if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
-                       h_errno = NETDB_INTERNAL;
+                       *info->he = NETDB_INTERNAL;
                        return NS_NOTFOUND;
                }
                break;
        default:
-               abort();
+               return NS_UNAVAIL;
        }
 
        buf = malloc(sizeof(*buf));
        if (buf == NULL) {
-               h_errno = NETDB_INTERNAL;
+               *info->he = NETDB_INTERNAL;
                return NS_NOTFOUND;
        }
        res = __res_get_state();
@@ -1366,18 +1285,19 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap)
        }
        res_setnetid(res, netid);
        res_setmark(res, mark);
-       n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, sizeof(buf->buf));
+       n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf));
        if (n < 0) {
                free(buf);
-               dprintf("res_nquery failed (%d)\n", res, n);
+               debugprintf("res_nquery failed (%d)\n", res, n);
                __res_put_state(res);
                return NS_NOTFOUND;
        }
-       hp = getanswer(buf, n, qbuf, T_PTR, res);
+       hp = getanswer(buf, n, qbuf, T_PTR, res, info->hp, info->buf,
+           info->buflen, info->he);
        free(buf);
        if (hp == NULL) {
                __res_put_state(res);
-               switch (h_errno) {
+               switch (*info->he) {
                case HOST_NOT_FOUND:
                        return NS_NOTFOUND;
                case TRY_AGAIN:
@@ -1386,20 +1306,325 @@ _dns_gethtbyaddr(void *rv, void        *cb_data, va_list ap)
                        return NS_UNAVAIL;
                }
        }
-       hp->h_addrtype = af;
-       hp->h_length = len;
-       (void)memcpy(rs->host_addr, uaddr, (size_t)len);
-       rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
-       rs->h_addr_ptrs[1] = NULL;
-       if (af == AF_INET && (res->options & RES_USE_INET6)) {
-               map_v4v6_address((char *)(void *)rs->host_addr,
-                   (char *)(void *)rs->host_addr);
+
+       bf = (void *)(hp->h_addr_list + 2);
+       blen = (size_t)(bf - info->buf);
+       if (blen + info->hp->h_length > info->buflen)
+               goto nospc;
+       hp->h_addr_list[0] = bf;
+       hp->h_addr_list[1] = NULL;
+       (void)memcpy(bf, uaddr, (size_t)info->hp->h_length);
+       if (info->hp->h_addrtype == AF_INET && (res->options & RES_USE_INET6)) {
+               if (blen + NS_IN6ADDRSZ > info->buflen)
+                       goto nospc;
+               map_v4v6_address(bf, bf);
                hp->h_addrtype = AF_INET6;
-               hp->h_length = IN6ADDRSZ;
+               hp->h_length = NS_IN6ADDRSZ;
        }
 
        __res_put_state(res);
-       *((struct hostent **)rv) = hp;
-       h_errno = NETDB_SUCCESS;
+       *info->he = NETDB_SUCCESS;
+       return NS_SUCCESS;
+nospc:
+       *info->he = NETDB_INTERNAL;
+       return NS_UNAVAIL;
+}
+
+#ifdef YP
+/*ARGSUSED*/
+static struct hostent *
+_yp_hostent(char *line, int af, struct getnamaddr *info)
+{
+       struct in6_addr host_addrs[MAXADDRS];
+       char **aliases;
+       size_t maxaliases;
+       char *p = line;
+       char *cp, **q, *ptr;
+       size_t len, anum, i;
+       int addrok;
+       int more;
+       size_t naddrs;
+       struct hostent *hp = info->hp;
+
+       _DIAGASSERT(line != NULL);
+
+       hp->h_name = NULL;
+       hp->h_addrtype = af;
+       switch (af) {
+       case AF_INET:
+               hp->h_length = NS_INADDRSZ;
+               break;
+       case AF_INET6:
+               hp->h_length = NS_IN6ADDRSZ;
+               break;
+       default:
+               return NULL;
+       }
+       setup(aliases, maxaliases);
+       naddrs = 0;
+       q = aliases;
+
+nextline:
+       /* check for host_addrs overflow */
+       if (naddrs >= __arraycount(host_addrs))
+               goto done;
+
+       more = 0;
+       cp = strpbrk(p, " \t");
+       if (cp == NULL)
+               goto done;
+       *cp++ = '\0';
+
+       /* p has should have an address */
+       addrok = inet_pton(af, p, &host_addrs[naddrs]);
+       if (addrok != 1) {
+               /* skip to the next line */
+               while (cp && *cp) {
+                       if (*cp == '\n') {
+                               cp++;
+                               goto nextline;
+                       }
+                       cp++;
+               }
+               goto done;
+       }
+       naddrs++;
+
+       while (*cp == ' ' || *cp == '\t')
+               cp++;
+       p = cp;
+       cp = strpbrk(p, " \t\n");
+       if (cp != NULL) {
+               if (*cp == '\n')
+                       more = 1;
+               *cp++ = '\0';
+       }
+       if (!hp->h_name)
+               hp->h_name = p;
+       else if (strcmp(hp->h_name, p) == 0)
+               ;
+       else
+               addalias(q, p, aliases, maxaliases);
+       p = cp;
+       if (more)
+               goto nextline;
+
+       while (cp && *cp) {
+               if (*cp == ' ' || *cp == '\t') {
+                       cp++;
+                       continue;
+               }
+               if (*cp == '\n') {
+                       cp++;
+                       goto nextline;
+               }
+               addalias(q, cp, aliases, maxaliases);
+               cp = strpbrk(cp, " \t");
+               if (cp != NULL)
+                       *cp++ = '\0';
+       }
+
+done:
+       if (hp->h_name == NULL) {
+               free(aliases);
+               return NULL;
+       }
+
+       ptr = info->buf;
+       len = info->buflen;
+
+       anum = (size_t)(q - aliases);
+       HENT_ARRAY(hp->h_addr_list, naddrs, ptr, len);
+       HENT_ARRAY(hp->h_aliases, anum, ptr, len);
+
+       for (i = 0; i < naddrs; i++)
+               HENT_COPY(hp->h_addr_list[i], &host_addrs[i], hp->h_length,
+                   ptr, len);
+       hp->h_addr_list[naddrs] = NULL;
+
+       HENT_SCOPY(hp->h_name, hp->h_name, ptr, len);
+
+       for (i = 0; i < anum; i++)
+               HENT_SCOPY(hp->h_aliases[i], aliases[i], ptr, len);
+       hp->h_aliases[anum] = NULL;
+       free(aliases);
+
+       return hp;
+nospc:
+       free(aliases);
+       *info->he = NETDB_INTERNAL;
+       errno = ENOSPC;
+       return NULL;
+}
+
+/*ARGSUSED*/
+int
+_yp_gethtbyaddr(void *rv, void *cb_data, va_list ap)
+{
+       struct hostent *hp = NULL;
+       char *ypcurrent;
+       int ypcurrentlen, r;
+       char name[INET6_ADDRSTRLEN];    /* XXX enough? */
+       const unsigned char *uaddr;
+       int af;
+       const char *map;
+       struct getnamaddr *info = rv;
+
+       _DIAGASSERT(rv != NULL);
+
+       uaddr = va_arg(ap, unsigned char *);
+       /* NOSTRICT skip len */(void)va_arg(ap, int);
+       af = va_arg(ap, int);
+
+       if (!__ypdomain) {
+               if (_yp_check(&__ypdomain) == 0)
+                       return NS_UNAVAIL;
+       }
+       /*
+        * XXX unfortunately, we cannot support IPv6 extended scoped address
+        * notation here.  gethostbyaddr() is not scope-aware.  too bad.
+        */
+       if (inet_ntop(af, uaddr, name, (socklen_t)sizeof(name)) == NULL)
+               return NS_UNAVAIL;
+       switch (af) {
+       case AF_INET:
+               map = "hosts.byaddr";
+               break;
+       default:
+               map = "ipnodes.byaddr";
+               break;
+       }
+       ypcurrent = NULL;
+       r = yp_match(__ypdomain, map, name,
+               (int)strlen(name), &ypcurrent, &ypcurrentlen);
+       if (r == 0)
+               hp = _yp_hostent(ypcurrent, af, info);
+       else
+               hp = NULL;
+       free(ypcurrent);
+       if (hp == NULL) {
+               *info->he = HOST_NOT_FOUND;
+               return NS_NOTFOUND;
+       }
+       return NS_SUCCESS;
+}
+
+/*ARGSUSED*/
+int
+_yp_gethtbyname(void *rv, void *cb_data, va_list ap)
+{
+       struct hostent *hp;
+       char *ypcurrent;
+       int ypcurrentlen, r;
+       const char *name;
+       int af;
+       const char *map;
+       struct getnamaddr *info = rv;
+
+       _DIAGASSERT(rv != NULL);
+
+       name = va_arg(ap, char *);
+       /* NOSTRICT skip string len */(void)va_arg(ap, int);
+       af = va_arg(ap, int);
+
+       if (!__ypdomain) {
+               if (_yp_check(&__ypdomain) == 0)
+                       return NS_UNAVAIL;
+       }
+       switch (af) {
+       case AF_INET:
+               map = "hosts.byname";
+               break;
+       default:
+               map = "ipnodes.byname";
+               break;
+       }
+       ypcurrent = NULL;
+       r = yp_match(__ypdomain, map, name,
+               (int)strlen(name), &ypcurrent, &ypcurrentlen);
+       if (r == 0)
+               hp = _yp_hostent(ypcurrent, af, info);
+       else
+               hp = NULL;
+       free(ypcurrent);
+       if (hp == NULL) {
+               *info->he = HOST_NOT_FOUND;
+               return NS_NOTFOUND;
+       }
        return NS_SUCCESS;
 }
+#endif
+
+/*
+ * Non-reentrant versions.
+ */
+
+struct hostent *
+gethostbyname(const char *name)
+{
+       struct hostent *result = NULL;
+       res_static rs = __res_get_static(); /* Use res_static to provide thread-safety. */
+
+       gethostbyname_r(name, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), &result, &h_errno);
+       return result;
+}
+
+struct hostent *
+gethostbyname2(const char *name, int af)
+{
+       struct hostent *result = NULL;
+       res_static rs = __res_get_static(); /* Use res_static to provide thread-safety. */
+
+       gethostbyname2_r(name, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), &result, &h_errno);
+       return result;
+}
+
+struct hostent *
+android_gethostbynamefornet(const char *name, int af, unsigned netid, unsigned mark)
+{
+       struct hostent *hp;
+       res_state res = __res_get_state();
+       if (res == NULL)
+               return NULL;
+       res_static rs = __res_get_static(); /* Use res_static to provide thread-safety. */
+       hp = gethostbyname_internal(name, af, res, &rs->host, rs->hostbuf, sizeof(rs->hostbuf),
+                                   &h_errno, netid, mark);
+       __res_put_state(res);
+       return hp;
+}
+
+struct hostent *
+gethostbyaddr(const void *addr, socklen_t len, int af)
+{
+       return android_gethostbyaddrfornet_proxy(addr, len, af, NETID_UNSET, MARK_UNSET);
+}
+
+struct hostent *
+android_gethostbyaddrfornet(const void *addr, socklen_t len, int af, unsigned netid, unsigned mark)
+{
+       return android_gethostbyaddrfornet_proxy(addr, len, af, netid, mark);
+}
+
+__LIBC_HIDDEN__ struct hostent*
+android_gethostbyaddrfornet_proxy(const void* addr, socklen_t len, int af,
+                                  unsigned netid, unsigned mark)
+{
+       res_static rs = __res_get_static(); /* Use res_static to provide thread-safety. */
+       return android_gethostbyaddrfornet_proxy_internal(addr, len, af, &rs->host, rs->hostbuf,
+                                                    sizeof(rs->hostbuf), &h_errno, netid, mark);
+}
+
+struct hostent *
+gethostent(void)
+{
+  res_static  rs = __res_get_static();
+       if (!rs->hostf) {
+         sethostent_r(&rs->hostf);
+         if (!rs->hostf) {
+           h_errno = NETDB_INTERNAL;
+           return NULL;
+         }
+       }
+       memset(&rs->host, 0, sizeof(rs->host));
+       return netbsd_gethostent_r(rs->hostf, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), &h_errno);
+}
diff --git a/libc/dns/include/hostent.h b/libc/dns/include/hostent.h
new file mode 100644 (file)
index 0000000..8b9a637
--- /dev/null
@@ -0,0 +1,93 @@
+/*     $NetBSD: hostent.h,v 1.2 2013/08/27 09:56:12 christos Exp $     */
+
+/*-
+ * Copyright (c) 2013 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.
+ *
+ * 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.
+ */
+#ifndef _DNS_NET_HOSTENT_H
+#define _DNS_NET_HOSTENT_H
+
+#include <stdio.h>
+#include <netdb.h>
+#include <stdarg.h>
+
+/*
+ * These are not being advertised because the interfaces are non-standard.
+ * There are versions by linux, aix, qnx, sun, etc. Our versions are used
+ * internally to provide thread safety; they mostly resemble qnx.
+ */
+void sethostent_r(FILE **);
+struct hostent *netbsd_gethostent_r(FILE *, struct hostent *, char *, size_t, int *);
+void endhostent_r(FILE **);
+
+/*
+ * The following are internal API's and are used only for testing.
+ */
+struct getnamaddr {
+       struct hostent *hp;
+       char *buf;
+       size_t buflen;
+       int *he;
+};
+
+/* /etc/hosts lookup */
+int _hf_gethtbyaddr(void *, void *, va_list);
+int _hf_gethtbyname(void *, void *, va_list);
+
+#ifdef YP
+/* NIS lookup */
+int _yp_gethtbyaddr(void *, void *, va_list);
+int _yp_gethtbyname(void *, void *, va_list);
+#endif
+
+#define HENT_ARRAY(dst, anum, ptr, len) \
+       do { \
+               size_t _len = (anum + 1) * sizeof(*dst); \
+               if (_len > len) \
+                       goto nospc; \
+               dst = (void *)ptr; \
+               ptr += _len; \
+               len -= _len; \
+       } while (/*CONSTCOND*/0)
+
+#define HENT_COPY(dst, src, slen, ptr, len) \
+       do { \
+               if ((size_t)slen > len) \
+                       goto nospc; \
+               memcpy(ptr, src, (size_t)slen); \
+               dst = ptr; \
+               ptr += slen; \
+               len -= slen; \
+       } while (/* CONSTCOND */0)
+
+#define HENT_SCOPY(dst, src, ptr, len) \
+       do { \
+               size_t _len = strlen(src) + 1; \
+               HENT_COPY(dst, src, _len, ptr, len); \
+       } while (/* CONSTCOND */0)
+
+#endif /* _DNS_NET_HOSTENT_H */
diff --git a/libc/dns/net/sethostent.c b/libc/dns/net/sethostent.c
new file mode 100644 (file)
index 0000000..f501c8b
--- /dev/null
@@ -0,0 +1,266 @@
+/*     $NetBSD: sethostent.c,v 1.20 2014/03/17 13:24:23 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)sethostent.c       8.1 (Berkeley) 6/4/93";
+static char rcsid[] = "Id: sethostent.c,v 8.5 1996/09/28 06:51:07 vixie Exp ";
+#else
+__RCSID("$NetBSD: sethostent.c,v 1.20 2014/03/17 13:24:23 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include "namespace.h"
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <string.h>
+#include <nsswitch.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "hostent.h"
+#include "resolv_private.h"
+
+#define ALIGNBYTES (sizeof(uintptr_t) - 1)
+#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+
+#ifndef _REENTRANT
+void   res_close(void);
+#endif
+
+static struct hostent *_hf_gethtbyname2(const char *, int, struct getnamaddr *);
+
+static const char *_h_hosts = _PATH_HOSTS;
+
+void
+sethostent_r(FILE **hf)
+{
+       if (!*hf)
+               *hf = fopen(_h_hosts, "re");
+       else
+               rewind(*hf);
+}
+
+void
+endhostent_r(FILE **hf)
+{
+       if (*hf) {
+               (void)fclose(*hf);
+               *hf = NULL;
+       }
+}
+
+/*ARGSUSED*/
+int
+_hf_gethtbyname(void *rv, void *cb_data, va_list ap)
+{
+       struct hostent *hp;
+       const char *name;
+       int af;
+       struct getnamaddr *info = rv;
+
+       _DIAGASSERT(rv != NULL);
+
+       name = va_arg(ap, char *);
+       /* NOSTRICT skip string len */(void)va_arg(ap, int);
+       af = va_arg(ap, int);
+
+#if 0
+       {
+               res_state res = __res_get_state();
+               if (res == NULL)
+                       return NS_NOTFOUND;
+               if (res->options & RES_USE_INET6)
+                       hp = _hf_gethtbyname2(name, AF_INET6, info);
+               else
+                       hp = NULL;
+               if (hp == NULL)
+                       hp = _hf_gethtbyname2(name, AF_INET, info);
+               __res_put_state(res);
+       }
+#else
+       hp = _hf_gethtbyname2(name, af, info);
+#endif
+       if (hp == NULL) {
+               *info->he = HOST_NOT_FOUND;
+               return NS_NOTFOUND;
+       }
+       return NS_SUCCESS;
+}
+
+static struct hostent *
+_hf_gethtbyname2(const char *name, int af, struct getnamaddr *info)
+{
+       struct hostent *hp, hent;
+       char *buf, *ptr;
+       size_t len, anum, num, i;
+       FILE *hf;
+       char *aliases[MAXALIASES];
+       char *addr_ptrs[MAXADDRS];
+
+       _DIAGASSERT(name != NULL);
+
+       hf = NULL;
+       sethostent_r(&hf);
+       if (hf == NULL) {
+               errno = EINVAL;
+               *info->he = NETDB_INTERNAL;
+               return NULL;
+       }
+
+       if ((ptr = buf = malloc(len = info->buflen)) == NULL) {
+               endhostent_r(&hf);
+               *info->he = NETDB_INTERNAL;
+               return NULL;
+       }
+
+       anum = 0;               /* XXX: gcc */
+       hent.h_name = NULL;     /* XXX: gcc */
+       hent.h_addrtype = 0;    /* XXX: gcc */
+       hent.h_length = 0;      /* XXX: gcc */
+
+       for (num = 0; num < MAXADDRS;) {
+               info->hp->h_addrtype = af;
+               info->hp->h_length = 0;
+
+               hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen,
+                   info->he);
+               if (hp == NULL)
+                       break;
+
+               if (strcasecmp(hp->h_name, name) != 0) {
+                       char **cp;
+                       for (cp = hp->h_aliases; *cp != NULL; cp++)
+                               if (strcasecmp(*cp, name) == 0)
+                                       break;
+                       if (*cp == NULL) continue;
+               }
+
+               if (num == 0) {
+                       hent.h_addrtype = af = hp->h_addrtype;
+                       hent.h_length = hp->h_length;
+
+                       HENT_SCOPY(hent.h_name, hp->h_name, ptr, len);
+                       for (anum = 0; hp->h_aliases[anum]; anum++) {
+                               if (anum >= MAXALIASES)
+                                       goto nospc;
+                               HENT_SCOPY(aliases[anum], hp->h_aliases[anum],
+                                   ptr, len);
+                       }
+                       ptr = (void *)ALIGN(ptr);
+                       if ((size_t)(ptr - buf) >= info->buflen)
+                               goto nospc;
+               }
+
+               if (num >= MAXADDRS)
+                       goto nospc;
+               HENT_COPY(addr_ptrs[num], hp->h_addr_list[0], hp->h_length, ptr,
+                   len);
+               num++;
+       }
+       endhostent_r(&hf);
+
+       if (num == 0) {
+               *info->he = HOST_NOT_FOUND;
+               free(buf);
+               return NULL;
+       }
+
+       hp = info->hp;
+       ptr = info->buf;
+       len = info->buflen;
+
+       hp->h_addrtype = hent.h_addrtype;
+       hp->h_length = hent.h_length;
+
+       HENT_ARRAY(hp->h_aliases, anum, ptr, len);
+       HENT_ARRAY(hp->h_addr_list, num, ptr, len);
+
+       for (i = 0; i < num; i++)
+               HENT_COPY(hp->h_addr_list[i], addr_ptrs[i], hp->h_length, ptr,
+                   len);
+       hp->h_addr_list[num] = NULL;
+
+       HENT_SCOPY(hp->h_name, hent.h_name, ptr, len);
+
+       for (i = 0; i < anum; i++)
+               HENT_SCOPY(hp->h_aliases[i], aliases[i], ptr, len);
+       hp->h_aliases[anum] = NULL;
+
+       free(buf);
+       return hp;
+nospc:
+       endhostent_r(&hf);
+       *info->he = NETDB_INTERNAL;
+       free(buf);
+       errno = ENOSPC;
+       return NULL;
+}
+
+/*ARGSUSED*/
+int
+_hf_gethtbyaddr(void *rv, void *cb_data, va_list ap)
+{
+       struct hostent *hp;
+       const unsigned char *addr;
+       struct getnamaddr *info = rv;
+       FILE *hf;
+
+       _DIAGASSERT(rv != NULL);
+
+       addr = va_arg(ap, unsigned char *);
+       info->hp->h_length = va_arg(ap, int);
+       info->hp->h_addrtype = va_arg(ap, int);
+
+       hf = NULL;
+       sethostent_r(&hf);
+       if (hf == NULL) {
+               *info->he = NETDB_INTERNAL;
+               return NS_UNAVAIL;
+       }
+       while ((hp = netbsd_gethostent_r(hf, info->hp, info->buf, info->buflen,
+           info->he)) != NULL)
+               if (!memcmp(hp->h_addr_list[0], addr, (size_t)hp->h_length))
+                       break;
+       endhostent_r(&hf);
+
+       if (hp == NULL) {
+               *info->he = HOST_NOT_FOUND;
+               return NS_NOTFOUND;
+       }
+       return NS_SUCCESS;
+}
index 527d5c1964e80e905358fd2a769e5c753bce2fff..e16537629a22303ae1509da9f10e26cf4a40e37a 100644 (file)
@@ -209,7 +209,7 @@ void endprotoent(void);
 void endservent(void);
 void freehostent(struct hostent *);
 struct hostent *gethostbyaddr(const void *, socklen_t, int);
-int gethostbyaddr_r(const void *, int, int, struct hostent *, char *, size_t, struct hostent **, int *);
+int gethostbyaddr_r(const void *, socklen_t, int, struct hostent *, char *, size_t, struct hostent **, int *);
 struct hostent *gethostbyname(const char *);
 int gethostbyname_r(const char *, struct hostent *, char *, size_t, struct hostent **, int *);
 struct hostent *gethostbyname2(const char *, int);
index 0cebe4e8e9a8d16b163b01843b049a98faf6cbb8..ab5b48755453cb2e00829fc5caa73b04f62fe4f5 100644 (file)
  * limitations under the License.
  */
 
+#include <netdb.h>
+
 #include <gtest/gtest.h>
 
+#include <arpa/inet.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <netdb.h>
 #include <netinet/in.h>
 
 TEST(netdb, getaddrinfo_NULL_host) {
@@ -114,8 +116,7 @@ TEST(netdb, getnameinfo_salen) {
   ASSERT_EQ(EAI_FAMILY, getnameinfo(sa, too_little, tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST));
 }
 
-TEST(netdb, gethostbyname) {
-  hostent* hent = gethostbyname("localhost");
+void VerifyLocalhost(hostent *hent) {
   ASSERT_TRUE(hent != NULL);
   ASSERT_EQ(hent->h_addrtype, AF_INET);
   ASSERT_EQ(hent->h_addr[0], 127);
@@ -124,6 +125,125 @@ TEST(netdb, gethostbyname) {
   ASSERT_EQ(hent->h_addr[3], 1);
 }
 
+TEST(netdb, gethostbyname) {
+  hostent* hp = gethostbyname("localhost");
+  VerifyLocalhost(hp);
+}
+
+TEST(netdb, gethostbyname2) {
+  hostent* hp = gethostbyname2("localhost", AF_INET);
+  VerifyLocalhost(hp);
+}
+
+TEST(netdb, gethostbyname_r) {
+  hostent hent;
+  hostent *hp;
+  char buf[512];
+  int err;
+  int result = gethostbyname_r("localhost", &hent, buf, sizeof(buf), &hp, &err);
+  ASSERT_EQ(0, result);
+  VerifyLocalhost(hp);
+
+  // Change hp->h_addr to test reentrancy.
+  hp->h_addr[0] = 0;
+
+  hostent hent2;
+  hostent *hp2;
+  char buf2[512];
+  result = gethostbyname_r("localhost", &hent2, buf2, sizeof(buf2), &hp2, &err);
+  ASSERT_EQ(0, result);
+  VerifyLocalhost(hp2);
+
+  ASSERT_EQ(0, hp->h_addr[0]);
+}
+
+TEST(netdb, gethostbyname2_r) {
+  hostent hent;
+  hostent *hp;
+  char buf[512];
+  int err;
+  int result = gethostbyname2_r("localhost", AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+  ASSERT_EQ(0, result);
+  VerifyLocalhost(hp);
+
+  // Change hp->h_addr to test reentrancy.
+  hp->h_addr[0] = 0;
+
+  hostent hent2;
+  hostent *hp2;
+  char buf2[512];
+  result = gethostbyname2_r("localhost", AF_INET, &hent2, buf2, sizeof(buf2), &hp2, &err);
+  ASSERT_EQ(0, result);
+  VerifyLocalhost(hp2);
+
+  ASSERT_EQ(0, hp->h_addr[0]);
+}
+
+TEST(netdb, gethostbyaddr) {
+  char addr[4];
+  ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
+  hostent *hp = gethostbyaddr(addr, sizeof(addr), AF_INET);
+  VerifyLocalhost(hp);
+}
+
+TEST(netdb, gethostbyaddr_r) {
+  char addr[4];
+  ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
+
+  hostent hent;
+  hostent *hp;
+  char buf[512];
+  int err;
+  int result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+  ASSERT_EQ(0, result);
+  VerifyLocalhost(hp);
+
+  // Change hp->h_addr to test reentrancy.
+  hp->h_addr[0] = 0;
+
+  hostent hent2;
+  hostent *hp2;
+  char buf2[512];
+  result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent2, buf2, sizeof(buf2), &hp2, &err);
+  ASSERT_EQ(0, result);
+  VerifyLocalhost(hp2);
+
+  ASSERT_EQ(0, hp->h_addr[0]);
+}
+
+TEST(netdb, gethostbyname_r_ERANGE) {
+  hostent hent;
+  hostent *hp;
+  char buf[4]; // Use too small buffer.
+  int err;
+  int result = gethostbyname_r("localhost", &hent, buf, sizeof(buf), &hp, &err);
+  ASSERT_EQ(ERANGE, result);
+  ASSERT_EQ(NULL, hp);
+}
+
+TEST(netdb, gethostbyname2_r_ERANGE) {
+  hostent hent;
+  hostent *hp;
+  char buf[4]; // Use too small buffer.
+  int err;
+  int result = gethostbyname2_r("localhost", AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+  ASSERT_EQ(ERANGE, result);
+  ASSERT_EQ(NULL, hp);
+}
+
+TEST(netdb, gethostbyaddr_r_ERANGE) {
+  char addr[4];
+  ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
+
+  hostent hent;
+  hostent *hp;
+  char buf[4]; // Use too small buffer.
+  int err;
+  int result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+  ASSERT_EQ(ERANGE, result);
+  ASSERT_EQ(NULL, hp);
+}
+
 TEST(netdb, getservbyname) {
   // smtp is TCP-only, so we know we'll get 25/tcp back.
   servent* s = getservbyname("smtp", NULL);