Thu May 23 00:01:10 1996 Ulrich Drepper <drepper@cygnus.com>
[kopensolaris-gnu/glibc.git] / resolv / gethnamaddr.c
index a451cb8..b80595b 100644 (file)
@@ -1,6 +1,8 @@
 /*
- * Copyright (c) 1985, 1988 Regents of the University of California.
- * All rights reserved.
+ * ++Copyright++ 1985, 1988, 1993
+ * -
+ * Copyright (c) 1985, 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
@@ -12,8 +14,8 @@
  *    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.
+ *     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.
  * 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--
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)gethostnamadr.c    6.45 (Berkeley) 2/24/91";
+static char sccsid[] = "@(#)gethostnamadr.c    8.1 (Berkeley) 6/4/93";
+static char rcsid[] = "$Id$";
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/param.h>
@@ -40,27 +63,54 @@ static char sccsid[] = "@(#)gethostnamadr.c 6.45 (Berkeley) 2/24/91";
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
+
+#include <stdio.h>
 #include <netdb.h>
 #include <resolv.h>
-#include <stdio.h>
 #include <ctype.h>
 #include <errno.h>
-#include <string.h>
+#include <syslog.h>
+
+#include "res_hconf.h"
+
+#ifndef LOG_AUTH
+# define LOG_AUTH 0
+#endif
+
+#define MULTI_PTRS_ARE_ALIASES 1       /* XXX - experimental */
+
+#if defined(BSD) && (BSD >= 199103) && defined(AF_INET6)
+# include <string.h>
+#else
+# include "../conf/portability.h"
+#endif
+#if defined(USE_OPTIONS_H)
+# include <../conf/options.h>
+#endif
 
 #define        MAXALIASES      35
 #define        MAXADDRS        35
+#define        MAXADDRBUFSIZE  8192
+
+static const char AskedForGot[] =
+                         "gethostby*.getanswer: asked for \"%s\", got \"%s\"";
 
 static char *h_addr_ptrs[MAXADDRS + 1];
+static struct hostent *gethostbyname_ipv4 __P((const char *));
 
 static struct hostent host;
 static char *host_aliases[MAXALIASES];
-static char hostbuf[BUFSIZ+1];
-static struct in_addr host_addr;
+static char hostbuf[MAXADDRBUFSIZE];
+static u_char host_addr[16];   /* IPv4 or IPv6 */
 static FILE *hostf = NULL;
-static char hostaddr[MAXADDRS];
-static char *host_addrs[2];
 static int stayopen = 0;
-char *strpbrk();
+
+static void map_v4v6_address __P((const char *src, char *dst));
+static void map_v4v6_hostent __P((struct hostent *hp, char **bp, int *len));
+
+#ifdef RESOLVSORT
+static void addrsort __P((char **, int));
+#endif
 
 #if PACKETSZ > 1024
 #define        MAXPACKET       PACKETSZ
@@ -74,28 +124,62 @@ typedef union {
 } querybuf;
 
 typedef union {
-    long al;
+    int32_t al;
     char ac;
 } align;
 
-int h_errno;
+extern int h_errno;
+
+#ifdef DEBUG
+static void
+dprintf(msg, num)
+       char *msg;
+       int num;
+{
+       if (_res.options & RES_DEBUG) {
+               int save = errno;
+
+               printf(msg, num);
+               errno = save;
+       }
+}
+#else
+# define dprintf(msg, num) /*nada*/
+#endif
 
 static struct hostent *
-getanswer(answer, anslen, iquery)
-       querybuf *answer;
+getanswer(answer, anslen, qname, qtype)
+       const querybuf *answer;
        int anslen;
-       int iquery;
+       const char *qname;
+       int qtype;
 {
-       register HEADER *hp;
-       register u_char *cp;
+       register const HEADER *hp;
+       register const u_char *cp;
        register int n;
-       u_char *eom;
-       char *bp, **ap;
+       const u_char *eom;
+       char *bp, **ap, **hap;
        int type, class, buflen, ancount, qdcount;
-       int haveanswer, getclass = C_ANY;
-       char **hap;
+       int haveanswer, had_error;
+       int toobig = 0;
+       char tbuf[MAXDNAME+1];
+       const char *tname;
+       int (*name_ok) __P((const char *));
 
+       tname = qname;
+       host.h_name = NULL;
        eom = answer->buf + anslen;
+       switch (qtype) {
+       case T_A:
+       case T_AAAA:
+               name_ok = res_hnok;
+               break;
+       case T_PTR:
+               name_ok = res_dnok;
+               break;
+       default:
+               return (NULL);  /* XXX should be abort(); */
+       }
        /*
         * find first satisfactory answer
         */
@@ -103,137 +187,279 @@ getanswer(answer, anslen, iquery)
        ancount = ntohs(hp->ancount);
        qdcount = ntohs(hp->qdcount);
        bp = hostbuf;
-       buflen = sizeof(hostbuf);
-       cp = answer->buf + sizeof(HEADER);
-       if (qdcount) {
-               if (iquery) {
-                       if ((n = dn_expand((u_char *)answer->buf,
-                           (u_char *)eom, (u_char *)cp, (u_char *)bp,
-                           buflen)) < 0) {
-                               h_errno = NO_RECOVERY;
-                               return ((struct hostent *) NULL);
-                       }
-                       cp += n + QFIXEDSZ;
-                       host.h_name = bp;
-                       n = strlen(bp) + 1;
-                       bp += n;
-                       buflen -= n;
-               } else
-                       cp += __dn_skipname(cp, eom) + QFIXEDSZ;
-               while (--qdcount > 0)
-                       cp += __dn_skipname(cp, eom) + QFIXEDSZ;
-       } else if (iquery) {
-               if (hp->aa)
-                       h_errno = HOST_NOT_FOUND;
-               else
-                       h_errno = TRY_AGAIN;
-               return ((struct hostent *) NULL);
+       buflen = sizeof hostbuf;
+       cp = answer->buf + HFIXEDSZ;
+       if (qdcount != 1) {
+               h_errno = NO_RECOVERY;
+               return (NULL);
+       }
+       n = dn_expand(answer->buf, eom, cp, bp, buflen);
+       if ((n < 0) || !(*name_ok)(bp)) {
+               h_errno = NO_RECOVERY;
+               return (NULL);
+       }
+       cp += 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 */
+               host.h_name = bp;
+               bp += n;
+               buflen -= n;
+               /* The qname can be abbreviated, but h_name is now absolute. */
+               qname = host.h_name;
        }
        ap = host_aliases;
        *ap = NULL;
        host.h_aliases = host_aliases;
        hap = h_addr_ptrs;
        *hap = NULL;
-#if BSD >= 43 || defined(h_addr)       /* new-style hostent structure */
        host.h_addr_list = h_addr_ptrs;
-#endif
        haveanswer = 0;
-       while (--ancount >= 0 && cp < eom) {
-               if ((n = dn_expand((u_char *)answer->buf, (u_char *)eom,
-                   (u_char *)cp, (u_char *)bp, buflen)) < 0)
-                       break;
-               cp += n;
+       had_error = 0;
+       while (ancount-- > 0 && cp < eom && !had_error) {
+               n = dn_expand(answer->buf, eom, cp, bp, buflen);
+               if ((n < 0) || !(*name_ok)(bp)) {
+                       had_error++;
+                       continue;
+               }
+               cp += n;                        /* name */
                type = _getshort(cp);
-               cp += sizeof(u_short);
+               cp += INT16SZ;                  /* type */
                class = _getshort(cp);
-               cp += sizeof(u_short) + sizeof(u_long);
+               cp += INT16SZ + INT32SZ;        /* class, TTL */
                n = _getshort(cp);
-               cp += sizeof(u_short);
-               if (type == T_CNAME) {
+               cp += INT16SZ;                  /* len */
+               if (class != C_IN) {
+                       /* XXX - debug? syslog? */
                        cp += n;
+                       continue;               /* XXX - had_error++ ? */
+               }
+               if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
                        if (ap >= &host_aliases[MAXALIASES-1])
                                continue;
+                       n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
+                       if ((n < 0) || !(*name_ok)(tbuf)) {
+                               had_error++;
+                               continue;
+                       }
+                       cp += n;
+                       /* Store alias. */
                        *ap++ = bp;
-                       n = strlen(bp) + 1;
+                       n = strlen(bp) + 1;     /* for the \0 */
+                       bp += n;
+                       buflen -= n;
+                       /* Get canonical name. */
+                       n = strlen(tbuf) + 1;   /* for the \0 */
+                       if (n > buflen) {
+                               had_error++;
+                               continue;
+                       }
+                       strcpy(bp, tbuf);
+                       host.h_name = bp;
                        bp += n;
                        buflen -= n;
                        continue;
                }
-               if (iquery && type == T_PTR) {
-                       if ((n = dn_expand((u_char *)answer->buf,
-                           (u_char *)eom, (u_char *)cp, (u_char *)bp,
-                           buflen)) < 0) {
-                               cp += n;
+               if (qtype == T_PTR && type == T_CNAME) {
+                       n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
+                       if ((n < 0) || !res_hnok(tbuf)) {
+                               had_error++;
                                continue;
                        }
                        cp += n;
-                       host.h_name = bp;
-                       return(&host);
+                       /* Get canonical name. */
+                       n = strlen(tbuf) + 1;   /* for the \0 */
+                       if (n > buflen) {
+                               had_error++;
+                               continue;
+                       }
+                       strcpy(bp, tbuf);
+                       tname = bp;
+                       bp += n;
+                       buflen -= n;
+                       continue;
                }
-               if (iquery || type != T_A)  {
-#ifdef DEBUG
-                       if (_res.options & RES_DEBUG)
-                               printf("unexpected answer type %d, size %d\n",
-                                       type, n);
-#endif
+               if (type != qtype) {
+                       syslog(LOG_NOTICE|LOG_AUTH,
+              "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
+                              qname, p_class(C_IN), p_type(qtype),
+                              p_type(type));
                        cp += n;
-                       continue;
+                       continue;               /* XXX - had_error++ ? */
                }
-               if (haveanswer) {
-                       if (n != host.h_length) {
+               switch (type) {
+               case T_PTR:
+                       if (strcasecmp(tname, bp) != 0) {
+                               syslog(LOG_NOTICE|LOG_AUTH,
+                                      AskedForGot, qname, bp);
                                cp += n;
-                               continue;
+                               continue;       /* XXX - had_error++ ? */
+                       }
+                       n = dn_expand(answer->buf, eom, cp, bp, buflen);
+                       if ((n < 0) || !res_hnok(bp)) {
+                               had_error++;
+                               break;
+                       }
+#if MULTI_PTRS_ARE_ALIASES
+                       cp += n;
+                       if (!haveanswer)
+                               host.h_name = bp;
+                       else if (ap < &host_aliases[MAXALIASES-1])
+                               *ap++ = bp;
+                       else
+                               n = -1;
+                       if (n != -1) {
+                               n = strlen(bp) + 1;     /* for the \0 */
+                               bp += n;
+                               buflen -= n;
+                       }
+                       break;
+#else
+                       host.h_name = bp;
+                       if (_res.options & RES_USE_INET6) {
+                               n = strlen(bp) + 1;     /* for the \0 */
+                               bp += n;
+                               buflen -= n;
+                               map_v4v6_hostent(&host, &bp, &buflen);
                        }
-                       if (class != getclass) {
+                       h_errno = NETDB_SUCCESS;
+                       return (&host);
+#endif
+               case T_A:
+               case T_AAAA:
+                       if (strcasecmp(host.h_name, bp) != 0) {
+                               syslog(LOG_NOTICE|LOG_AUTH,
+                                      AskedForGot, host.h_name, bp);
                                cp += n;
-                               continue;
+                               continue;       /* XXX - had_error++ ? */
                        }
-               } else {
-                       host.h_length = n;
-                       getclass = class;
-                       host.h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC;
-                       if (!iquery) {
+                       if (haveanswer) {
+                               if (n != host.h_length) {
+                                       cp += n;
+                                       continue;
+                               }
+                       } else {
+                               register int nn;
+
                                host.h_name = bp;
-                               bp += strlen(bp) + 1;
+                               nn = strlen(bp) + 1;    /* for the \0 */
+                               bp += nn;
+                               buflen -= nn;
                        }
-               }
 
-               bp += sizeof(align) - ((u_long)bp % sizeof(align));
+                       bp += sizeof(align) - ((u_long)bp % sizeof(align));
 
-               if (bp + n >= &hostbuf[sizeof(hostbuf)]) {
-#ifdef DEBUG
-                       if (_res.options & RES_DEBUG)
-                               printf("size (%d) too big\n", n);
-#endif
+                       if (bp + n >= &hostbuf[sizeof hostbuf]) {
+                               dprintf("size (%d) too big\n", n);
+                               had_error++;
+                               continue;
+                       }
+                       if (hap >= &h_addr_ptrs[MAXADDRS-1]) {
+                               if (!toobig++)
+                                       dprintf("Too many addresses (%d)\n",
+                                               MAXADDRS);
+                               cp += n;
+                               continue;
+                       }
+                       bcopy(cp, *hap++ = bp, n);
+                       bp += n;
+                       cp += n;
                        break;
+               default:
+                       abort();
                }
-               bcopy(cp, *hap++ = bp, n);
-               bp +=n;
-               cp += n;
-               haveanswer++;
+               if (!had_error)
+                       haveanswer++;
        }
        if (haveanswer) {
                *ap = NULL;
-#if BSD >= 43 || defined(h_addr)       /* new-style hostent structure */
                *hap = NULL;
-#else
-               host.h_addr = h_addr_ptrs[0];
-#endif
+# if defined(RESOLVSORT)
+               /*
+                * Note: we sort even if host can take only one address
+                * in its return structures - should give it the "best"
+                * address in that case, not some random one
+                */
+               if (_res.nsort && haveanswer > 1 && qtype == T_A)
+                       addrsort(h_addr_ptrs, haveanswer);
+# endif /*RESOLVSORT*/
+               if (!host.h_name) {
+                       n = strlen(qname) + 1;  /* for the \0 */
+                       if (n > buflen)
+                               goto try_again;
+                       strcpy(bp, qname);
+                       host.h_name = bp;
+                       bp += n;
+                       buflen -= n;
+               }
+               if (_res.options & RES_USE_INET6)
+                       map_v4v6_hostent(&host, &bp, &buflen);
+               h_errno = NETDB_SUCCESS;
                return (&host);
-       } else {
-               h_errno = TRY_AGAIN;
-               return ((struct hostent *) NULL);
        }
+ try_again:
+       h_errno = TRY_AGAIN;
+       return (NULL);
 }
 
 struct hostent *
 gethostbyname(name)
-       char *name;
+       const char *name;
+{
+       struct hostent *hp;
+
+       if (_res.options & RES_USE_INET6) {
+               hp = gethostbyname2(name, AF_INET6);
+               if (hp)
+                       return (hp);
+       }
+       return (gethostbyname2(name, AF_INET));
+}
+
+struct hostent *
+gethostbyname2(name, af)
+       const char *name;
+       int af;
 {
        querybuf buf;
-       register char *cp;
-       int n;
-       extern struct hostent *_gethtbyname();
+       register const char *cp;
+       char *bp;
+       int i, n, size, type, len;
+       extern struct hostent *_gethtbyname2();
+
+       if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+               h_errno = NETDB_INTERNAL;
+               return (NULL);
+       }
+
+       switch (af) {
+       case AF_INET:
+               size = INADDRSZ;
+               type = T_A;
+               break;
+       case AF_INET6:
+               size = IN6ADDRSZ;
+               type = T_AAAA;
+               break;
+       default:
+               h_errno = NETDB_INTERNAL;
+               errno = EAFNOSUPPORT;
+               return (NULL);
+       }
+
+       host.h_addrtype = af;
+       host.h_length = size;
+
+       /*
+        * if there aren't any dots, it could be a user-level alias.
+        * this is also done in res_query() since we are not the only
+        * function that looks up host names.
+        */
+       if (!strchr(name, '.') && (cp = __hostalias(name)))
+               name = cp;
 
        /*
         * disallow names consisting only of digits/dots, unless
@@ -247,97 +473,270 @@ gethostbyname(name)
                                /*
                                 * All-numeric, no dot at the end.
                                 * Fake up a hostent as if we'd actually
-                                * done a lookup.  What if someone types
-                                * 255.255.255.255?  The test below will
-                                * succeed spuriously... ???
+                                * done a lookup.
                                 */
-                               if ((host_addr.s_addr = inet_addr(name)) == -1) {
+                               if (inet_pton(af, name, host_addr,
+                                              sizeof host_addr) <= 0) {
                                        h_errno = HOST_NOT_FOUND;
-                                       return((struct hostent *) NULL);
+                                       return (NULL);
                                }
-                               host.h_name = name;
+                               strncpy(hostbuf, name, MAXDNAME);
+                               hostbuf[MAXDNAME] = '\0';
+                               bp = hostbuf + MAXDNAME;
+                               len = sizeof hostbuf - MAXDNAME;
+                               host.h_name = hostbuf;
                                host.h_aliases = host_aliases;
                                host_aliases[0] = NULL;
-                               host.h_addrtype = AF_INET;
-                               host.h_length = sizeof(u_long);
                                h_addr_ptrs[0] = (char *)&host_addr;
-                               h_addr_ptrs[1] = (char *)0;
-#if BSD >= 43 || defined(h_addr)       /* new-style hostent structure */
+                               h_addr_ptrs[1] = NULL;
                                host.h_addr_list = h_addr_ptrs;
-#else
-                               host.h_addr = h_addr_ptrs[0];
-#endif
+                               if (_res.options & RES_USE_INET6)
+                                       map_v4v6_hostent(&host, &bp, &len);
+                               h_errno = NETDB_SUCCESS;
                                return (&host);
                        }
-                       if (!isdigit(*cp) && *cp != '.') 
+                       if (!isdigit(*cp) && *cp != '.')
                                break;
                }
 
-       if ((n = res_search(name, C_IN, T_A, buf.buf, sizeof(buf))) < 0) {
-#ifdef DEBUG
-               if (_res.options & RES_DEBUG)
-                       printf("res_search failed\n");
+       h_errno = HOST_NOT_FOUND;
+       for (i = 0; i < _res_hconf.num_services; ++i) {
+               struct hostent * hp;
+               char * cp = (char *) name;
+
+               if (_res_hconf.num_trimdomains > 0) {
+                       size_t name_len = strlen(name);
+
+                       cp = malloc(name_len + 1);
+                       memcpy(cp, name, name_len + 1);
+                       _res_hconf_trim_domain(cp);
+               }
+
+               hp = NULL;
+               switch (_res_hconf.service[i]) {
+                     case SERVICE_BIND:
+                       if ((n = res_search(cp, C_IN, type,
+                                           buf.buf, sizeof(buf))) < 0)
+                       {
+                               dprintf("res_search failed (%d)\n", n);
+                               break;
+                       }
+                       hp = getanswer(&buf, n, cp, type);
+                       break;
+
+                     case SERVICE_HOSTS:
+                       hp = _gethtbyname2(cp, af);
+                       break;
+#ifdef HAVE_NYS
+                     case SERVICE_NIS:
+                       hp = _getnishost(cp, "hosts.byname");
+                       break;
 #endif
-               if (errno == ECONNREFUSED)
-                       return (_gethtbyname(name));
-               else
-                       return ((struct hostent *) NULL);
+                     default:
+                       break;
+               }
+               if (cp != name)
+                       free(cp);
+               if (hp) {
+                       if ((_res_hconf.flags & HCONF_FLAG_REORDER)
+                           && hp->h_addr_list[0] && hp->h_addr_list[1])
+                               _res_hconf_reorder_addrs(hp);
+                       _res_hconf_trim_domains(hp);
+                       return hp;
+               }
        }
-       return (getanswer(&buf, n, 0));
+       if (h_errno == NETDB_SUCCESS)
+               h_errno = HOST_NOT_FOUND;
+       return NULL;
 }
 
 struct hostent *
-gethostbyaddr(addr, len, type)
-       const char *addr;
-       int len, type;
+gethostbyaddr(addr, len, af)
+       const char *addr;       /* XXX should have been def'd as u_char! */
+       int len, af;
 {
-       int n;
+       const u_char *uaddr = (const u_char *)addr;
+       static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
+       static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
+       int n, size;
        querybuf buf;
        register struct hostent *hp;
-       char qbuf[MAXDNAME];
+       char qbuf[MAXDNAME+1], *qp;
+       register struct hostent *rhp;
+       char **haddr;
+       u_long old_options;
+       char hname2[MAXDNAME+1];
+       int i, old_num_trimdomains;
        extern struct hostent *_gethtbyaddr();
-       
-       if (type != AF_INET)
-               return ((struct hostent *) NULL);
-       (void)sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa",
-               ((unsigned)addr[3] & 0xff),
-               ((unsigned)addr[2] & 0xff),
-               ((unsigned)addr[1] & 0xff),
-               ((unsigned)addr[0] & 0xff));
-       n = res_query(qbuf, C_IN, T_PTR, (char *)&buf, sizeof(buf));
-       if (n < 0) {
-#ifdef DEBUG
-               if (_res.options & RES_DEBUG)
-                       printf("res_query failed\n");
-#endif
-               if (errno == ECONNREFUSED)
-                       return (_gethtbyaddr(addr, len, type));
-               return ((struct hostent *) NULL);
+
+       if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+               h_errno = NETDB_INTERNAL;
+               return (NULL);
        }
-       hp = getanswer(&buf, n, 1);
-       if (hp == NULL)
-               return ((struct hostent *) NULL);
-       hp->h_addrtype = type;
-       hp->h_length = len;
-       h_addr_ptrs[0] = (char *)&host_addr;
-       h_addr_ptrs[1] = (char *)0;
-       host_addr = *(struct in_addr *)addr;
-#if BSD < 43 && !defined(h_addr)       /* new-style hostent structure */
-       hp->h_addr = h_addr_ptrs[0];
+       if (af == AF_INET6 && len == IN6ADDRSZ &&
+           (!bcmp(uaddr, mapped, sizeof mapped) ||
+            !bcmp(uaddr, tunnelled, sizeof tunnelled))) {
+               /* Unmap. */
+               addr += sizeof mapped;
+               uaddr += sizeof mapped;
+               af = AF_INET;
+               len = INADDRSZ;
+       }
+       switch (af) {
+       case AF_INET:
+               size = INADDRSZ;
+               break;
+       case AF_INET6:
+               size = IN6ADDRSZ;
+               break;
+       default:
+               errno = EAFNOSUPPORT;
+               h_errno = NETDB_INTERNAL;
+               return (NULL);
+       }
+       if (size != len) {
+               errno = EINVAL;
+               h_errno = NETDB_INTERNAL;
+               return (NULL);
+       }
+
+       h_errno = NETDB_SUCCESS;
+       for (i = 0; i < _res_hconf.num_services; ++i) {
+               hp = NULL;
+               switch (_res_hconf.service[i]) {
+               case SERVICE_BIND:
+                       switch (af) {
+                       case AF_INET:
+                               (void) sprintf(qbuf,
+                                              "%u.%u.%u.%u.in-addr.arpa",
+                                              (uaddr[3] & 0xff),
+                                              (uaddr[2] & 0xff),
+                                              (uaddr[1] & 0xff),
+                                              (uaddr[0] & 0xff));
+                               break;
+                       case AF_INET6:
+                               qp = qbuf;
+                               for (n = IN6ADDRSZ - 1; n >= 0; n--) {
+                                       qp += sprintf(qp, "%x.%x.",
+                                                     uaddr[n] & 0xf,
+                                                     (uaddr[n] >> 4) & 0xf);
+                               }
+                               strcpy(qp, "ip6.int");
+                               break;
+                       default:
+                               abort();
+                       }
+                       n = res_query(qbuf, C_IN, T_PTR,
+                                     (u_char *)buf.buf, sizeof buf.buf);
+                       if (n < 0) {
+                               dprintf("res_query failed (%d)\n", n);
+                               break;
+                       }
+                       hp = getanswer(&buf, n, qbuf, T_PTR);
+                       if (!hp)
+                               break;  /* h_errno was set by getanswer() */
+                       if (af == AF_INET
+                           && (_res_hconf.flags & HCONF_FLAG_SPOOF)) {
+                               /*
+                                * Turn off search as the name should
+                                * be absolute, 'localhost' should be
+                                * matched by defnames
+                                */
+                               strncpy(hname2, hp->h_name, MAXDNAME);
+                               hname2[MAXDNAME] = '\0';
+                               old_options = _res.options;
+                               /*
+                                * Also turn off domain trimming to prevent it
+                                * from causing the name comparison to fail.
+                                */
+                               old_num_trimdomains =
+                                       _res_hconf.num_trimdomains;
+                               _res.options &= ~RES_DNSRCH;
+                               _res.options |= RES_DEFNAMES;
+                               rhp = gethostbyname(hname2);
+                               _res.options = old_options;
+                               /* There must be an A record and
+                                  the names must match.  */
+                               if (!rhp || strcmp(hname2, rhp->h_name)) {
+                                       syslog(LOG_NOTICE|LOG_AUTH,
+                                              "gethostbyaddr: No A record for"
+                                              " %s (verifying [%s])",
+                                              hname2,
+                                              inet_ntoa(*((struct in_addr *)
+                                                          addr)));
+                                       h_errno = HOST_NOT_FOUND;
+                                       break;
+                               }
+                               for (haddr = rhp->h_addr_list; *haddr; haddr++)
+                                       if (!memcmp(*haddr, addr, INADDRSZ))
+                                               break;
+                               if (!*haddr) {
+                                       syslog(LOG_NOTICE|LOG_AUTH,
+                                              "gethostbyaddr: A record of %s"
+                                              " != PTR record [%s]",
+                                              hname2,
+                                              inet_ntoa(*((struct in_addr *)
+                                                          addr)));
+                                       h_errno = HOST_NOT_FOUND;
+                                       break;
+                               }
+                       }
+                       hp->h_addrtype = af;
+                       hp->h_length = len;
+                       bcopy(addr, host_addr, len);
+                       h_addr_ptrs[0] = (char *)host_addr;
+                       h_addr_ptrs[1] = NULL;
+                       if (af == AF_INET && (_res.options & RES_USE_INET6)) {
+                               map_v4v6_address((char*)host_addr,
+                                                (char*)host_addr);
+                               hp->h_addrtype = AF_INET6;
+                               hp->h_length = IN6ADDRSZ;
+                       }
+                       h_errno = NETDB_SUCCESS;
+                       break;
+
+               case SERVICE_HOSTS:
+                       hp = _gethtbyaddr(addr, len, af);
+                       break;
+
+#ifdef HAVE_NYS
+               case SERVICE_NIS:
+                       if (af == AF_INET) {
+                               sprintf(qbuf, "%u.%u.%u.%u",
+                                       (unsigned)addr[0] & 0xff,
+                                       (unsigned)addr[1] & 0xff,
+                                       (unsigned)addr[2] & 0xff,
+                                       (unsigned)addr[3] & 0xff);
+                               hp = _getnishost(qbuf, "hosts.byaddr");
+                       }
+                       break;
 #endif
-       return(hp);
+
+               default:
+                       break;
+               }
+               if (hp) {
+                       _res_hconf_trim_domains(hp);
+                       return hp;
+               }
+       }
+       if (h_errno == NETDB_SUCCESS)
+               h_errno = HOST_NOT_FOUND;
+       return NULL;
 }
 
+void
 _sethtent(f)
        int f;
 {
-       if (hostf == NULL)
+       if (!hostf)
                hostf = fopen(_PATH_HOSTS, "r" );
        else
                rewind(hostf);
-       stayopen |= f;
+       stayopen = f;
 }
 
+void
 _endhtent()
 {
        if (hostf && !stayopen) {
@@ -351,36 +750,51 @@ _gethtent()
 {
        char *p;
        register char *cp, **q;
+       int af, len;
 
-       if (hostf == NULL && (hostf = fopen(_PATH_HOSTS, "r" )) == NULL)
+       if (!hostf && !(hostf = fopen(_PATH_HOSTS, "r" ))) {
+               h_errno = NETDB_INTERNAL;
                return (NULL);
-again:
-       if ((p = fgets(hostbuf, BUFSIZ, hostf)) == NULL)
+       }
+ again:
+       if (!(p = fgets(hostbuf, sizeof hostbuf, hostf))) {
+               h_errno = HOST_NOT_FOUND;
                return (NULL);
+       }
        if (*p == '#')
                goto again;
-       cp = strpbrk(p, "#\n");
-       if (cp == NULL)
+       if (!(cp = strpbrk(p, "#\n")))
                goto again;
        *cp = '\0';
-       cp = strpbrk(p, " \t");
-       if (cp == NULL)
+       if (!(cp = strpbrk(p, " \t")))
                goto again;
        *cp++ = '\0';
-       /* THIS STUFF IS INTERNET SPECIFIC */
-#if BSD >= 43 || defined(h_addr)       /* new-style hostent structure */
-       host.h_addr_list = host_addrs;
-#endif
-       host.h_addr = hostaddr;
-       *((u_long *)host.h_addr) = inet_addr(p);
-       host.h_length = sizeof (u_long);
-       host.h_addrtype = AF_INET;
+       if ((_res.options & RES_USE_INET6) &&
+           inet_pton(AF_INET6, p, host_addr, sizeof host_addr) > 0) {
+               af = AF_INET6;
+               len = IN6ADDRSZ;
+       } else if (inet_pton(AF_INET, p, host_addr, sizeof host_addr) > 0) {
+               if (_res.options & RES_USE_INET6) {
+                       map_v4v6_address((char*)&host_addr, (char*)&host_addr);
+                       af = AF_INET6;
+                       len = IN6ADDRSZ;
+               } else {
+                       af = AF_INET;
+                       len = INADDRSZ;
+               }
+       } else {
+               goto again;
+       }
+       h_addr_ptrs[0] = (char *)&host_addr;
+       h_addr_ptrs[1] = NULL;
+       host.h_addr_list = h_addr_ptrs;
+       host.h_length = len;
+       host.h_addrtype = af;
        while (*cp == ' ' || *cp == '\t')
                cp++;
        host.h_name = cp;
        q = host.h_aliases = host_aliases;
-       cp = strpbrk(cp, " \t");
-       if (cp != NULL) 
+       if (cp = strpbrk(cp, " \t"))
                *cp++ = '\0';
        while (cp && *cp) {
                if (*cp == ' ' || *cp == '\t') {
@@ -389,45 +803,410 @@ again:
                }
                if (q < &host_aliases[MAXALIASES - 1])
                        *q++ = cp;
-               cp = strpbrk(cp, " \t");
-               if (cp != NULL)
+               if (cp = strpbrk(cp, " \t"))
                        *cp++ = '\0';
        }
        *q = NULL;
+       if (_res.options & RES_USE_INET6) {
+               char *bp = hostbuf;
+               int buflen = sizeof hostbuf;
+
+               map_v4v6_hostent(&host, &bp, &buflen);
+       }
+       h_errno = NETDB_SUCCESS;
        return (&host);
 }
 
+struct hstorage {
+       char    name[MAXHOSTNAMELEN + 1];       /* canonical name */
+       char ** alp;                            /* address list pointer */
+       char *  abp;                            /* address buffer pointer */
+       char *  addr_list[MAXADDRS + 1];        /* address list storage */
+       char    addr_buf[MAXADDRBUFSIZE];       /* addresses storage */
+};
+
+static void
+append_addr (struct hstorage * hs, struct hostent *p)
+{
+       if (hs->alp < hs->addr_list + MAXADDRS
+           && hs->abp + p->h_length < hs->addr_buf + MAXADDRBUFSIZE)
+       {
+               hs->alp[0] = hs->abp;
+               hs->alp[1] = 0;
+               memcpy(hs->abp, p->h_addr_list[0], p->h_length);
+               hs->abp += p->h_length;
+               ++hs->alp;
+       }
+}
+
+/*
+ * Lookup IP address and aliases for host NAME.  If multiaddress mode
+ * is enabled, the entire /etc/hosts file is searched and the union of
+ * all addresses found for NAME is returned (this may be slow with
+ * large /etc/hosts files, but is convenient for smallish sites that
+ * don't want to go through the complexity of running named locally).
+ * If multiaddress mode is enabled, the address returned in
+ * h_addr_list[0] is one that is on the same net as one of the host's
+ * local addresses (if such an address exists).  For compatibility
+ * with the BIND version of gethostbyname(), the alias field is empty
+ * unless the name being looked up is an alias itself.  In the latter
+ * case, the name field contains the canonical name and the alias
+ * field the name that is being looked up.  A difference from the BIND
+ * version is that this is true even if the alias applies only to one
+ * of the interfaces on the target host.o
+ */
 struct hostent *
 _gethtbyname(name)
-       char *name;
+       const char *name;
+{
+       extern struct hostent *_gethtbyname2 __P((const char *, int));
+       struct hostent *hp;
+
+       if (_res.options & RES_USE_INET6) {
+               hp = _gethtbyname2(name, AF_INET6);
+               if (hp)
+                       return (hp);
+       }
+       return (_gethtbyname2(name, AF_INET));
+}
+
+struct hostent *
+_gethtbyname2(name, af)
+       const char *name;
+       int af;
 {
        register struct hostent *p;
        register char **cp;
-       
+
        _sethtent(0);
-       while (p = _gethtent()) {
-               if (strcasecmp(p->h_name, name) == 0)
-                       break;
-               for (cp = p->h_aliases; *cp != 0; cp++)
-                       if (strcasecmp(*cp, name) == 0)
-                               goto found;
+
+       if (_res_hconf.flags & HCONF_FLAG_MULTI) {
+               /*
+                * More statics; not pretty, but it would require
+                * interface changes to make these functions
+                * reentrant.
+                */
+               static char * aliases[2];
+               static char alias[MAXHOSTNAMELEN + 1];
+               static struct hstorage self, target;
+               static struct hostent ht;
+               int found;
+
+               aliases[0] = aliases[1] = 0;
+
+               gethostname(self.name, sizeof(self.name));
+               self.alp = self.addr_list;
+               self.abp = self.addr_buf;
+
+               strncpy(target.name, name, MAXHOSTNAMELEN);
+               target.name[MAXHOSTNAMELEN] = '\0';
+               target.alp = target.addr_list;
+               target.abp = target.addr_buf;
+
+               _sethtent(0);
+               while ((p = _gethtent()) != 0) {
+                       found = 1;
+
+                       if (p->h_addrtype != af)
+                               continue;
+                       if (strcasecmp(p->h_name, name) != 0) {
+                               found = 0;
+                               for (cp = p->h_aliases; *cp; ++cp)
+                                       if (strcasecmp(*cp, name) == 0) {
+                                               found = 1;
+                                               if (!aliases[0]) {
+                                                       strcpy(target.name,
+                                                              p->h_name);
+                                                       strncpy(alias, name,
+                                                               MAXHOSTNAMELEN);
+                                                       alias[MAXHOSTNAMELEN]
+                                                               = '\0';
+                                                       aliases[0] = alias;
+                                               }
+                                               break;
+                                       }
+                       }
+                       if (found) {
+                               /* they better be all the same type and length! */
+                               ht.h_addrtype = p->h_addrtype;
+                               ht.h_length = p->h_length;
+                               append_addr(&target, p);
+                               /*
+                                * If the current hostentry is for the target host, we don't
+                                * check for the local host name.  This nicely optimizes the
+                                * case where NAME is a local name since address ordering
+                                * doesn't matter in that case.
+                                */
+                               continue;
+                       }
+                       found = 1;
+                       if (strcasecmp(p->h_name, self.name) != 0) {
+                               found = 0;
+                               for (cp = p->h_aliases; *cp; ++cp)
+                                       if (strcasecmp(*cp, self.name) == 0) {
+                                               found = 1;
+                                               break;
+                                       }
+                       }
+                       if (found) {
+                               append_addr(&self, p);
+                       }
+               }
+               _endhtent();
+
+               if (target.alp == target.addr_list)
+                       return NULL;            /* found nothing */
+
+               ht.h_aliases   = aliases;
+               ht.h_name      = target.name;
+               ht.h_addr_list = target.addr_list;
+               /*
+                * XXX (davidm) Isn't this subsumed by REORDER???
+                *
+                * Finding the `best' address is necessarily address
+                * specific.  For now, we do IPv4 addresses only.
+                */
+               if (af == AF_INET) {
+                       u_int32_t a1, a2, diff, mindiff = ~0;
+                       int i, j, bestaddr = 0;
+
+                       for (i = 0; self.addr_list[i]; ++i) {
+                               for (j = 0; target.addr_list[j]; ++j) {
+                                       memcpy(&a1, self.addr_list[i], 4);
+                                       memcpy(&a2, target.addr_list[j], 4);
+                                       a1 = ntohl(a1);
+                                       a2 = ntohl(a2);
+                                       diff = a1 ^ a2;
+                                       if (diff < mindiff) {
+                                               bestaddr = j;
+                                               mindiff = diff;
+                                       }
+                               }
+                       }
+                       if (bestaddr > 0) {
+                               char * tmp;
+
+                               tmp = target.addr_list[0];
+                               target.addr_list[0] = target.addr_list[bestaddr];
+                               target.addr_list[bestaddr] = tmp;
+                       }
+               } else if (af == AF_INET6) {
+                       /* XXX To do!!! */
+               }
+               ht.h_addr_list = target.addr_list;
+               return &ht;
+       } else {
+               _sethtent(0);
+               while (p = _gethtent()) {
+                       if (p->h_addrtype != af)
+                               continue;
+                       if (strcasecmp(p->h_name, name) == 0)
+                               break;
+                       for (cp = p->h_aliases; *cp != 0; cp++)
+                               if (strcasecmp(*cp, name) == 0)
+                                       goto found;
+               }
        }
-found:
+ found:
        _endhtent();
        return (p);
 }
 
 struct hostent *
-_gethtbyaddr(addr, len, type)
+_gethtbyaddr(addr, len, af)
        const char *addr;
-       int len, type;
+       int len, af;
 {
        register struct hostent *p;
 
        _sethtent(0);
        while (p = _gethtent())
-               if (p->h_addrtype == type && !bcmp(p->h_addr, addr, len))
+               if (p->h_addrtype == af && !bcmp(p->h_addr, addr, len))
                        break;
        _endhtent();
        return (p);
 }
+
+static void
+map_v4v6_address(src, dst)
+       const char *src;
+       char *dst;
+{
+       u_char *p = (u_char *)dst;
+       char tmp[INADDRSZ];
+       int i;
+
+       /* Stash a temporary copy so our caller can update in place. */
+       bcopy(src, tmp, 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. */
+       bcopy(tmp, (void*)p, INADDRSZ);
+}
+
+static void
+map_v4v6_hostent(hp, bpp, lenp)
+       struct hostent *hp;
+       char **bpp;
+       int *lenp;
+{
+       char **ap;
+
+       if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
+               return;
+       hp->h_addrtype = AF_INET6;
+       hp->h_length = IN6ADDRSZ;
+       for (ap = hp->h_addr_list; *ap; ap++) {
+               int i = sizeof(align) - ((u_long)*bpp % sizeof(align));
+
+               if (*lenp < (i + IN6ADDRSZ)) {
+                       /* Out of memory.  Truncate address list here.  XXX */
+                       *ap = NULL;
+                       return;
+               }
+               *bpp += i;
+               *lenp -= i;
+               map_v4v6_address(*ap, *bpp);
+               *ap = *bpp;
+               *bpp += IN6ADDRSZ;
+               *lenp -= IN6ADDRSZ;
+       }
+}
+
+#ifdef RESOLVSORT
+static void
+addrsort(ap, num)
+       char **ap;
+       int num;
+{
+       int i, j;
+       char **p;
+       short aval[MAXADDRS];
+       int needsort = 0;
+
+       p = ap;
+       for (i = 0; i < num; i++, p++) {
+           for (j = 0 ; (unsigned)j < _res.nsort; j++)
+               if (_res.sort_list[j].addr.s_addr ==
+                   (((struct in_addr *)(*p))->s_addr & _res.sort_list[j].mask))
+                       break;
+           aval[i] = j;
+           if (needsort == 0 && i > 0 && j < aval[i-1])
+               needsort = i;
+       }
+       if (!needsort)
+           return;
+
+       while (needsort < num) {
+           for (j = needsort - 1; j >= 0; j--) {
+               if (aval[j] > aval[j+1]) {
+                   char *hp;
+
+                   i = aval[j];
+                   aval[j] = aval[j+1];
+                   aval[j+1] = i;
+
+                   hp = ap[j];
+                   ap[j] = ap[j+1];
+                   ap[j+1] = hp;
+
+               } else
+                   break;
+           }
+           needsort++;
+       }
+}
+#endif
+
+#if defined(BSD43_BSD43_NFS) || defined(sun)
+/* some libc's out there are bound internally to these names (UMIPS) */
+void
+ht_sethostent(stayopen)
+       int stayopen;
+{
+       _sethtent(stayopen);
+}
+
+void
+ht_endhostent()
+{
+       _endhtent();
+}
+
+struct hostent *
+ht_gethostbyname(name)
+       char *name;
+{
+       return (_gethtbyname(name));
+}
+
+struct hostent *
+ht_gethostbyaddr(addr, len, af)
+       const char *addr;
+       int len, af;
+{
+       return (_gethtbyaddr(addr, len, af));
+}
+
+struct hostent *
+gethostent()
+{
+       struct hostent * hp;
+       int i;
+
+       if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+               h_errno = NETDB_INTERNAL;
+               return (NULL);
+       }
+
+       /*
+        * This doesn't look quite right.  Shouldn't we read one
+        * service until it returns 0, then move on to the next
+        * service?  If so, it requires adding some state and
+        * initializing that state in sethostent().
+        * (davidm@azstarnet.com)
+        */
+       for (i = 0; i < _res_hconf.num_services; ++i) {
+               hp = NULL;
+               switch (_res_hconf.service[i]) {
+                     case SERVICE_HOSTS:
+                       hp = _gethtent ();
+                       break;
+
+#ifdef HAVE_NYS
+                     case SERVICE_NIS:
+                       hp = _getnishost (NULL, "hosts.byname");
+                       break;
+#endif
+
+                     case SERVICE_BIND:
+                     default:
+                       break;
+               }
+               if (hp) {
+                       if ((_res_hconf.flags & HCONF_FLAG_REORDER)
+                           && hp->h_addr_list[0] && hp->h_addr_list[1])
+                               _res_hconf_reorder_addrs (hp);
+                       return hp;
+               }
+       }
+       h_errno = HOST_NOT_FOUND;
+       return NULL;
+}
+
+void
+dns_service()
+{
+       return;
+}
+
+#undef dn_skipname
+dn_skipname(comp_dn, eom)
+       const u_char *comp_dn, *eom;
+{
+       return (__dn_skipname(comp_dn, eom));
+}
+#endif /*old-style libc with yp junk in it*/