pennmush-1.8.3p3/game/data/
pennmush-1.8.3p3/game/log/
pennmush-1.8.3p3/game/save/
pennmush-1.8.3p3/game/txt/evt/
pennmush-1.8.3p3/game/txt/nws/
pennmush-1.8.3p3/po/
pennmush-1.8.3p3/win32/msvc.net/
pennmush-1.8.3p3/win32/msvc6/
/**
 * \file mysocket.c
 *
 * \brief Socket routines for PennMUSH.
 *
 *
 */
#include "copyrite.h"

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif

#ifdef I_SYS_SOCKET
#include <sys/socket.h>
#endif

#ifdef I_NETINET_IN
#ifdef WIN32
#undef EINTR
#endif
#include <netinet/in.h>
#else
#ifdef I_SYS_IN
#include <sys/in.h>
#endif
#endif

#ifdef I_NETINET_TCP
#include <netinet/tcp.h>
#endif

#ifdef I_ARPA_INET
#include <arpa/inet.h>
#endif

#ifdef I_UNISTD
#include <unistd.h>
#endif

#ifndef HAS_GETADDRINFO
#ifdef I_ARPA_NAMESER
#include <arpa/nameser.h>
#endif
#ifndef WIN32
#ifdef __APPLE__
#ifdef __APPLE_CC__
#include <nameser.h>
#endif
#endif
#ifndef __CYGWIN__
#include <resolv.h>
#endif
#endif
#endif

#ifdef I_NETDB
#include <netdb.h>
#endif

#ifndef HAVE_H_ERRNO
extern int h_errno;
#endif

#include <errno.h>

#ifdef I_FCNTL
#include <fcntl.h>
#endif

#ifdef I_SYS_FILE
#include <sys/file.h>
#endif

#ifdef I_SYS_TIME
#include <sys/time.h>
#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#endif
#else
#include <time.h>
#endif

#include "conf.h"
#include "externs.h"
#include "mymalloc.h"
#include "mysocket.h"
#include "ident.h"
#include "confmagic.h"

#ifndef INFOSLAVE
/** Given a sockaddr structure, try to look up and return hostname info.
 * If we can't get a hostname from DNS (or if we're not using DNS),
 * we settle for the IP address.
 * \param host pointer to a sockaddr structure.
 * \param len length of host structure.
 * \return static hostname_info structure with hostname and port.
 */
struct hostname_info *
hostname_convert(struct sockaddr *host, int len)
{
  static struct hostname_info hi;
  static char hostname[NI_MAXHOST];
  static char port[NI_MAXSERV];

  if (getnameinfo(host, len, hostname, sizeof hostname, port, sizeof port,
                  (USE_DNS ? 0 : NI_NUMERICHOST) | NI_NUMERICSERV) != 0) {
    return NULL;
  }
  hi.hostname = hostname;
  hi.port = port;
  return &hi;
}

/** Given a sockaddr structure, try to look up and return ip address info.
 * \param host pointer to a sockaddr structure.
 * \param len length of host structure.
 * \return static hostname_info structure with ip address and port.
 */
struct hostname_info *
ip_convert(struct sockaddr *host, int len)
{
  static struct hostname_info hi;
  static char hostname[NI_MAXHOST];
  static char port[NI_MAXSERV];

  if (getnameinfo(host, len, hostname, sizeof hostname, port, sizeof port,
                  NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
    return NULL;
  }
  hi.hostname = hostname;
  hi.port = port;
  return &hi;
}
#endif                          /* INFOSLAVE */

/** Open a connection to a given host and port. Basically
 * tcp_connect from UNPv1
 * \param host hostname or ip address to connect to, as a string.
 * \param socktype The type of socket - SOCK_STREAM or SOCK_DGRAM
 * \param myiterface pointer to sockaddr structure for specific interface.
 * \param myilen length of myiterface
 * \param port port to connect to.
 * \param timeout pointer to timeout for connection.
 * \return file descriptor for connected socket, or -1 for failure.
 */
int
make_socket_conn(const char *host, int socktype, struct sockaddr *myiterface,
                 socklen_t myilen, Port_t port, int *timeout)
{
  struct addrinfo hints, *server, *save;
  char cport[NI_MAXSERV];
  int s;
  int res;
  int err;

  memset(&hints, 0, sizeof(struct addrinfo));
  hints.ai_family = AF_UNSPEC;  /* Try to use IPv6 if available */
  hints.ai_socktype = socktype;

  sprintf(cport, "%hu", port);

  if ((res = getaddrinfo(host, cport, &hints, &server)) != 0) {
    fprintf(stderr, "In getaddrinfo: %s\n", gai_strerror(res));
    fprintf(stderr, "Host: %s Port %hu\n", host, port);
    fflush(stderr);
    return -1;
  }

  if (!server) {
    fprintf(stderr, "Couldn't get address for host %s port %hu\n", host, port);
    fflush(stderr);
    return -1;
  }

  save = server;

  do {
    s = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
    if (s < 0)
      continue;

    if (myiterface && myilen > 0 && myiterface->sa_family == server->ai_family) {
      /* Bind to a specific interface. Don't even try for the case of
       * an IPv4 socket and an IPv6 interface. Happens with ident, which
       * seems to work okay without the bind(). */
      if (bind(s, myiterface, myilen) < 0)
        perror("bind failed (Possibly harmless)");
    }

    if ((err = connect_nonb(s, server->ai_addr, server->ai_addrlen, timeout)) ==
        0)
      break;

#ifdef DEBUG
    perror("connect failed (Probably harmless)");
#endif

    closesocket(s);

  } while ((server = server->ai_next) != NULL);

  freeaddrinfo(save);

  if (server == NULL) {
#ifndef DEBUG
    /* Usually, we don't want to show this for failed connect to
     * an ident server, as that just means they're not running one
     */

    if (port != IDPORT) {
#endif
      fprintf(stderr, "Couldn't connect to %s on port %hu\n", host, port);
      fflush(stderr);
#ifndef DEBUG
    }
#endif
    return -1;
  }
  return s;
}


/** Start listening on a given port. Basically tcp_listen
 * from UNPv1
 * \param port port to listen on.
 * \param socktype the type of socket - SOCK_STREAM or SOCK_DGRAM
 * \param addr pointer to sockaddr to copy address data to.
 * \param len length of addr.
 * \param host hostname or address to listen on, as a string.
 * \return file descriptor of listening socket, or -1 for failure.
 */
int
make_socket(Port_t port, int socktype, union sockaddr_u *addr, socklen_t * len,
            const char *host)
{
  int s, opt, ipv = 4;
  /* Use getaddrinfo() to fill in the sockaddr fields. This
   * effectively makes us IPv6 capable if the host is. Much of this is
   * lifted from listing 11.9 (tcp_listen()) in W. Richard Steven's
   * Unix Network Programming, vol 1.  If getaddrinfo() isn't
   * present on the system, we'll use our own version, also from UNPv1. */
  struct addrinfo *server, *save, hints;
  char portbuf[NI_MAXSERV], *cport;
  int res;

  memset(&hints, 0, sizeof(struct addrinfo));
  hints.ai_flags = AI_PASSIVE;
#ifdef FORCE_IPV4
  hints.ai_family = AF_INET;    /* OpenBSD apparently doesn't properly
                                   map IPv4 connections to IPv6 servers. */
#else
  hints.ai_family = AF_UNSPEC;  /* Try to use IPv6 if available */
#endif
  hints.ai_socktype = socktype;

  if (port > 0) {
    sprintf(portbuf, "%hu", port);
    cport = portbuf;
  } else
    cport = NULL;

  if (strlen(host) == 0)
    host = NULL;

  if ((res = getaddrinfo(host, cport, &hints, &server)) != 0) {
    fprintf(stderr, "In getaddrinfo: %s\n", gai_strerror(res));
    fprintf(stderr, "Host: %s Port %hu\n", host, port);
    exit(3);
  }

  save = server;

  if (!server) {
    fprintf(stderr, "Couldn't get address for host %s port %hu\n", host, port);
    exit(3);
  }

  do {
    s = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
    if (s < 0)
      continue;

    opt = 1;
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) {
      perror("setsockopt (Possibly ignorable)");
      continue;
    }

    if (bind(s, server->ai_addr, server->ai_addrlen) == 0)
      break;                    /* Success */

    perror("binding stream socket (Possibly ignorable)");
    closesocket(s);
  } while ((server = server->ai_next) != NULL);

  if (server == NULL) {
    fprintf(stderr, "Couldn't bind to port %d\n", port);
    exit(4);
  }

  ipv = (server->ai_family == AF_INET) ? 4 : 6;
  if (addr) {
    memcpy(addr->data, server->ai_addr, server->ai_addrlen);
    if (len)
      *len = server->ai_addrlen;
  }

  freeaddrinfo(save);
  fprintf(stderr, "Listening on port %d using IPv%d.\n", port, ipv);
  fflush(stderr);
  listen(s, 5);
  return s;
}


/** Make a socket do nonblocking i/o.
 * \param s file descriptor of socket.
 */
void
make_nonblocking(int s)
{
#ifdef WIN32
  unsigned long arg = 1;
  if (ioctlsocket(s, FIONBIO, &arg) == -1) {
#else
  int flags;

  if ((flags = fcntl(s, F_GETFL, 0)) == -1) {
    perror("make_nonblocking: fcntl");
#ifndef INFOSLAVE
    mush_panic("F_GETFL fcntl failed");
#else
    exit(1);
#endif
  }

  if (fcntl(s, F_SETFL, flags | O_NDELAY) == -1) {
#endif                          /* WIN32 */
    perror("make_nonblocking: fcntl");
#ifndef INFOSLAVE
    mush_panic("O_NDELAY fcntl failed");
#else
    exit(1);
#endif
  }
}


#ifndef INFOSLAVE
/** Enable TCP keepalive on the given socket if we can.
 * \param s socket.
 */
/* ARGSUSED */
void
set_keepalive(int s __attribute__ ((__unused__)))
{
#ifdef SO_KEEPALIVE
  int keepalive = 1;
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
  int keepidle = options.keepalive_timeout;
#endif

  /* enable TCP keepalive */
  if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
                 (void *) &keepalive, sizeof(keepalive)) == -1)
    fprintf(stderr, "[%d] could not set SO_KEEPALIVE: errno %d", s, errno);

  /* And set the ping time to something reasonable instead of the
     default 2 hours. Linux, NetBSD and o thers use TCP_KEEPIDLE to do
     this. OS X and possibly others use TCP_KEEPALIVE. */
#if defined(TCP_KEEPIDLE)
  if (setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE,
                 (void *) &keepidle, sizeof(keepidle)) == -1)
    fprintf(stderr, "[%d] could not set TCP_KEEPIDLE: errno %d", s, errno);
#elif defined(TCP_KEEPALIVE)
  if (setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,
                 (void *) &keepidle, sizeof(keepidle)) == -1)
    fprintf(stderr, "[%d] could not set TCP_KEEPALIVE: errno %d", s, errno);
#endif
#endif
  return;
}
#endif


/** Connect a socket, possibly making it nonblocking first.
 * From UNP, with changes. If nsec > 0, we set the socket
 * nonblocking and connect with timeout. The socket is STILL
 * nonblocking after it returns. If nsec == 0, a normal blocking
 * connect is done. 
 * \param sockfd file descriptor of socket.
 * \param saptr pointer to sockaddr structure with connection data.
 * \param salen length of saptr.
 * \param timeout pointer to timeout value. If given, nonblocking connect.
 * \retval 0 success.
 * \retval -1 failure.
 */
int
connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen,
             int *timeout)
{
  int n, error;
  time_t start, end;
  socklen_t len;
  fd_set rset, wset;
  struct timeval tval;

  if (timeout && *timeout)
    make_nonblocking(sockfd);

  error = 0;
  if ((n = connect(sockfd, (const struct sockaddr *) saptr, salen)) < 0)
#ifdef WIN32
    if (n == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
#else
    if (errno != EINPROGRESS)
#endif
      return -1;

  /* connect completed immediately or we did a blocking connect  */
  if (!timeout || !*timeout || n == 0)
    goto done;

  FD_ZERO(&rset);
  FD_SET(sockfd, &rset);
  wset = rset;
  tval.tv_sec = *timeout;
  tval.tv_usec = 0;
  time(&start);

  if ((n = select(sockfd + 1, &rset, &wset, NULL, &tval)) == 0) {
    closesocket(sockfd);        /* timeout */
    errno = ETIMEDOUT;
    return -1;
  }

  time(&end);
  *timeout -= end - start;
  *timeout = *timeout < 0 ? 0 : *timeout;

  if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
    len = sizeof(error);
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *) &error, &len) < 0)
      return -1;                /* Solaris pending error */
  } else
    return -1;

done:

  if (error) {
    closesocket(sockfd);        /* just in case */
    errno = error;
    return -1;
  }
  return 0;
}


/* The following functions are from W. Ridhard Steven's libgai,
 * modified by Shawn Wagner for PennMUSH. These arn't full
 * implementations- they don't handle unix-domain sockets or named
 * services, because we don't need them for what we're doing.  */

#ifdef AF_INET
#define IPv4
#endif
#ifdef AF_INET6
#define IPv6
#endif

#ifndef HAS_INET_PTON
/* This is from the BIND 4.9.4 release, modified to compile by itself */

/* Copyright (c) 1996 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */


#define IN6ADDRSZ       16
#define INADDRSZ         4
#define INT16SZ          2

#ifndef AF_INET6
#define AF_INET6        AF_MAX+1        /* just to let this compile */
#endif

/*
 * WARNING: Don't even consider trying to compile this on a system where
 * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
 */

static const char *inet_ntop4(const unsigned char *src, char *dst, size_t size);
static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size);

/* char *
 * inet_ntop(af, src, dst, size)
 *      convert a network format address to presentation format.
 * return:
 *      pointer to presentation format address (`dst'), or NULL (see errno).
 * author:
 *      Paul Vixie, 1996.
 */
const char *
inet_ntop(int af, const void *src, char *dst, size_t size)
{
  switch (af) {
  case AF_INET:
    return (inet_ntop4(src, dst, size));
  case AF_INET6:
    return (inet_ntop6(src, dst, size));
  default:
    errno = EAFNOSUPPORT;
    return (NULL);
  }
  /* NOTREACHED */
}

/* const char *
 * inet_ntop4(src, dst, size)
 *      format an IPv4 address, more or less like inet_ntoa()
 * return:
 *      `dst' (as a const)
 * notes:
 *      (1) uses no statics
 *      (2) takes a unsigned char* not an in_addr as input
 * author:
 *      Paul Vixie, 1996.
 */
static const char *
inet_ntop4(const unsigned char *src, char *dst, size_t size)
{
  static const char fmt[] = "%u.%u.%u.%u";
  char tmp[sizeof "255.255.255.255"];

  sprintf(tmp, fmt, src[0], src[1], src[2], src[3]);
  if (strlen(tmp) > size) {
    errno = ENOSPC;
    return (NULL);
  }
  strcpy(dst, tmp);
  return (dst);
}

/* const char *
 * inet_ntop6(src, dst, size)
 *      convert IPv6 binary address into presentation (printable) format
 * author:
 *      Paul Vixie, 1996.
 */
static const char *
inet_ntop6(const unsigned char *src, char *dst, size_t size)
{
  /*
   * Note that int32_t and int16_t need only be "at least" large enough
   * to contain a value of the specified size.  On some systems, like
   * Crays, there is no such thing as an integer variable with 16 bits.
   * Keep this in mind if you think this function should have been coded
   * to use pointer overlays.  All the world's not a VAX.
   */
  char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
  struct {
    int base, len;
  } best, cur;
  unsigned int words[IN6ADDRSZ / INT16SZ];
  int i;

  /*
   * Preprocess:
   *    Copy the input (bytewise) array into a wordwise array.
   *    Find the longest run of 0x00's in src[] for :: shorthanding.
   */
  memset(words, 0, sizeof words);
  for (i = 0; i < IN6ADDRSZ; i++)
    words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
  best.base = -1;
  cur.base = -1;
  for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
    if (words[i] == 0) {
      if (cur.base == -1)
        cur.base = i, cur.len = 1;
      else
        cur.len++;
    } else {
      if (cur.base != -1) {
        if (best.base == -1 || cur.len > best.len)
          best = cur;
        cur.base = -1;
      }
    }
  }
  if (cur.base != -1) {
    if (best.base == -1 || cur.len > best.len)
      best = cur;
  }
  if (best.base != -1 && best.len < 2)
    best.base = -1;

  /*
   * Format the result.
   */
  tp = tmp;
  for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
    /* Are we inside the best run of 0x00's? */
    if (best.base != -1 && i >= best.base && i < (best.base + best.len)) {
      if (i == best.base)
        *tp++ = ':';
      continue;
    }
    /* Are we following an initial run of 0x00s or any real hex? */
    if (i != 0)
      *tp++ = ':';
    /* Is this address an encapsulated IPv4? */
    if (i == 6 && best.base == 0 &&
        (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
      if (!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp)))
        return (NULL);
      tp += strlen(tp);
      break;
    }
    sprintf(tp, "%x", words[i]);
    tp += strlen(tp);
  }
  /* Was it a trailing run of 0x00's? */
  if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
    *tp++ = ':';
  *tp++ = '\0';

  /*
   * Check for overflow, copy, and we're done.
   */
  if ((size_t) (tp - tmp) > size) {
    errno = ENOSPC;
    return (NULL);
  }
  strcpy(dst, tmp);
  return (dst);
}


/* This is from the BIND 4.9.4 release, modified to compile by itself */

/* Copyright (c) 1996 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */


/*
 * WARNING: Don't even consider trying to compile this on a system where
 * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
 */

static int inet_pton4(const char *src, unsigned char *dst);
static int inet_pton6(const char *src, unsigned char *dst);

/* int
 * inet_pton(af, src, dst)
 *      convert from presentation format (which usually means ASCII printable)
 *      to network format (which is usually some kind of binary format).
 * return:
 *      1 if the address was valid for the specified address family
 *      0 if the address wasn't valid (`dst' is untouched in this case)
 *      -1 if some other error occurred (`dst' is untouched in this case, too)
 * author:
 *      Paul Vixie, 1996.
 */
int
inet_pton(int af, const char *src, void *dst)
{
  switch (af) {
  case AF_INET:
    return (inet_pton4(src, dst));
  case AF_INET6:
    return (inet_pton6(src, dst));
  default:
    errno = EAFNOSUPPORT;
    return (-1);
  }
  /* NOTREACHED */
}

/* int
 * inet_pton4(src, dst)
 *      like inet_aton() but without all the hexadecimal and shorthand.
 * return:
 *      1 if `src' is a valid dotted quad, else 0.
 * notice:
 *      does not touch `dst' unless it's returning 1.
 * author:
 *      Paul Vixie, 1996.
 */
static int
inet_pton4(const char *src, unsigned char *dst)
{
  static const char digits[] = "0123456789";
  int saw_digit, octets, ch;
  unsigned char tmp[INADDRSZ], *tp;

  saw_digit = 0;
  octets = 0;
  *(tp = tmp) = 0;
  while ((ch = *src++) != '\0') {
    const char *pch;

    if ((pch = strchr(digits, ch)) != NULL) {
      unsigned int new = *tp * 10 + (pch - digits);

      if (new > 255)
        return (0);
      *tp = new;
      if (!saw_digit) {
        if (++octets > 4)
          return (0);
        saw_digit = 1;
      }
    } else if (ch == '.' && saw_digit) {
      if (octets == 4)
        return (0);
      *++tp = 0;
      saw_digit = 0;
    } else
      return (0);
  }
  if (octets < 4)
    return (0);
  /* bcopy(tmp, dst, INADDRSZ); */
  memcpy(dst, tmp, INADDRSZ);
  return (1);
}

/* int
 * inet_pton6(src, dst)
 *      convert presentation level address to network order binary form.
 * return:
 *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
 * notice:
 *      (1) does not touch `dst' unless it's returning 1.
 *      (2) :: in a full address is silently ignored.
 * credit:
 *      inspired by Mark Andrews.
 * author:
 *      Paul Vixie, 1996.
 */
static int
inet_pton6(const char *src, unsigned char *dst)
{
  static const char xdigits_l[] = "0123456789abcdef",
    xdigits_u[] = "0123456789ABCDEF";
  unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
  const char *xdigits, *curtok;
  int ch, saw_xdigit;
  unsigned int val;

  memset((tp = tmp), 0, IN6ADDRSZ);
  endp = tp + IN6ADDRSZ;
  colonp = NULL;
  /* Leading :: requires some special handling. */
  if (*src == ':')
    if (*++src != ':')
      return (0);
  curtok = src;
  saw_xdigit = 0;
  val = 0;
  while ((ch = *src++) != '\0') {
    const char *pch;

    if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
      pch = strchr((xdigits = xdigits_u), ch);
    if (pch != NULL) {
      val <<= 4;
      val |= (pch - xdigits);
      if (val > 0xffff)
        return (0);
      saw_xdigit = 1;
      continue;
    }
    if (ch == ':') {
      curtok = src;
      if (!saw_xdigit) {
        if (colonp)
          return (0);
        colonp = tp;
        continue;
      }
      if (tp + INT16SZ > endp)
        return (0);
      *tp++ = (unsigned char) (val >> 8) & 0xff;
      *tp++ = (unsigned char) val & 0xff;
      saw_xdigit = 0;
      val = 0;
      continue;
    }
    if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) {
      tp += INADDRSZ;
      saw_xdigit = 0;
      break;                    /* '\0' was seen by inet_pton4(). */
    }
    return (0);
  }
  if (saw_xdigit) {
    if (tp + INT16SZ > endp)
      return (0);
    *tp++ = (unsigned char) (val >> 8) & 0xff;
    *tp++ = (unsigned char) val & 0xff;
  }
  if (colonp != NULL) {
    /*
     * Since some memmove()'s erroneously fail to handle
     * overlapping regions, we'll do the shift by hand.
     */
    const int n = tp - colonp;
    int i;

    for (i = 1; i <= n; i++) {
      endp[-i] = colonp[n - i];
      colonp[n - i] = 0;
    }
    tp = endp;
  }
  if (tp != endp)
    return (0);
  /* bcopy(tmp, dst, IN6ADDRSZ); */
  memcpy(dst, tmp, IN6ADDRSZ);
  return (1);
}
#endif

#ifndef HAS_GETNAMEINFO
static int gn_ipv46(char *, size_t, char *, size_t, void *, size_t,
                    int, int, int);

/*
 * Handle either an IPv4 or an IPv6 address and port.
 */

/* include gn_ipv46 */
static int
gn_ipv46(char *host, size_t hostlen, char *serv, size_t servlen,
         void *aptr, size_t alen, int family, int port, int flags)
{
  char *ptr;
  struct hostent *hptr;

  if (hostlen > 0) {
    if (flags & NI_NUMERICHOST) {
      if (inet_ntop(family, aptr, host, hostlen) == NULL)
        return (1);
    } else {
      hptr = gethostbyaddr(aptr, alen, family);
      if (hptr != NULL && hptr->h_name != NULL) {
        if (flags & NI_NOFQDN) {
          if ((ptr = strchr(hptr->h_name, '.')) != NULL)
            *ptr = 0;           /* overwrite first dot */
        }
#ifdef HAS_SNPRINTF
        snprintf(host, hostlen, "%s", hptr->h_name);
#else
        strncpy(host, hptr->h_name, hostlen);
        host[hostlen - 1] = '\0';
#endif
      } else {
        if (flags & NI_NAMEREQD)
          return (1);
        if (inet_ntop(family, aptr, host, hostlen) == NULL)
          return (1);
      }
    }
  }

  if (servlen > 0) {
    if (flags & NI_NUMERICSERV) {
#ifdef HAS_SNPRINTF
      snprintf(serv, servlen, "%hu", ntohs((unsigned short) port));
#else
      sprintf(serv, "%hu", ntohs((unsigned short) port));
#endif

    } else {
      /* We're not bothering with getservbyport */
#ifdef HAS_SNPRINTF
      snprintf(serv, servlen, "%hu", ntohs((unsigned short) port));
#else
      sprintf(serv, "%hu", ntohs((unsigned short) port));
#endif
    }
  }
  return (0);
}

/* end gn_ipv46 */


/* include getnameinfo */
int
getnameinfo(const struct sockaddr *sa, socklen_t salen,
            char *host, size_t hostlen, char *serv, size_t servlen, int flags)
{

  switch (sa->sa_family) {
#ifdef  IPv4
  case AF_INET:{
      struct sockaddr_in *sain = (struct sockaddr_in *) sa;

      return (gn_ipv46(host, hostlen, serv, servlen,
                       &sain->sin_addr, sizeof(struct in_addr),
                       AF_INET, sain->sin_port, flags));
    }
#endif

#ifdef  HAVE_SOCKADDR_IN6
  case AF_INET6:{
      struct sockaddr_in6 *sain = (struct sockaddr_in6 *) sa;

      return (gn_ipv46(host, hostlen, serv, servlen,
                       &sain->sin6_addr, sizeof(struct in6_addr),
                       AF_INET6, sain->sin6_port, flags));
    }
#endif

  default:
    return (1);
  }
}

/* end getnameinfo */

#endif

#ifndef HAS_GETADDRINFO

/* include ga1 */
struct search {
  const char *host;             /* hostname or address string */
  int family;                   /* AF_xxx */
};

static int ga_aistruct(struct addrinfo ***, const struct addrinfo *,
                       const void *, int);
static struct addrinfo *ga_clone(struct addrinfo *);
static int ga_echeck(const char *, const char *, int, int, int, int);
static int ga_nsearch(const char *, const struct addrinfo *, struct search *);
static int ga_port(struct addrinfo *, int, int);
static int ga_serv(struct addrinfo *, const struct addrinfo *, const char *);


int
getaddrinfo(const char *hostname, const char *servname,
            const struct addrinfo *hintsp, struct addrinfo **result)
{
  int rc, error, nsearch;
  char **ap, *canon;
  struct hostent *hptr;
  struct search search[3], *sptr;
  struct addrinfo hints, *aihead, **aipnext;

  /*
   * If we encounter an error we want to free() any dynamic memory
   * that we've allocated.  This is our hack to simplify the code.
   */
#define error(e) { error = (e); goto bad; }

  aihead = NULL;                /* initialize automatic variables */
  aipnext = &aihead;
  canon = NULL;

  if (hintsp == NULL) {
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
  } else
    hints = *hintsp;            /* struct copy */

  /* 4first some basic error checking */
  if ((rc = ga_echeck(hostname, servname, hints.ai_flags, hints.ai_family,
                      hints.ai_socktype, hints.ai_protocol)) != 0)
    error(rc);

  /* end ga1 */

  /* include ga3 */
  /* 4remainder of function for IPv4/IPv6 */
  nsearch = ga_nsearch(hostname, &hints, &search[0]);
  for (sptr = &search[0]; sptr < &search[nsearch]; sptr++) {
#ifdef  IPv4
    /* 4check for an IPv4 dotted-decimal string */
    if (isdigit((unsigned char) sptr->host[0])) {
      struct in_addr inaddr;

      if (inet_pton(AF_INET, sptr->host, &inaddr) == 1) {
        if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET)
          error(EAI_ADDRFAMILY);
        if (sptr->family != AF_INET)
          continue;             /* ignore */
        rc = ga_aistruct(&aipnext, &hints, &inaddr, AF_INET);
        if (rc != 0)
          error(rc);
        continue;
      }
    }
#endif

#ifdef  HAVE_SOCKADDR_IN6
    /* 4check for an IPv6 hex string */
    if ((isxdigit((unsigned char) sptr->host[0]) || sptr->host[0] == ':') &&
        (strchr(sptr->host, ':') != NULL)) {
      struct in6_addr in6addr;

      if (inet_pton(AF_INET6, sptr->host, &in6addr) == 1) {
        if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6)
          error(EAI_ADDRFAMILY);
        if (sptr->family != AF_INET6)
          continue;             /* ignore */
        rc = ga_aistruct(&aipnext, &hints, &in6addr, AF_INET6);
        if (rc != 0)
          error(rc);
        continue;
      }
    }
#endif
/* end ga3 */
/* include ga4 */
    /* 4remainder of for() to look up hostname */
#ifdef  HAVE_SOCKADDR_IN6
    if ((_res.options & RES_INIT) == 0)
      res_init();               /* need this to set _res.options */
#endif

    if (nsearch == 2) {
#ifdef  HAVE_SOCKADDR_IN6
      _res.options &= ~RES_USE_INET6;
#endif
      hptr = gethostbyname2(sptr->host, sptr->family);
    } else {
#ifdef  HAVE_SOCKADDR_IN6
      if (sptr->family == AF_INET6)
        _res.options |= RES_USE_INET6;
      else
        _res.options &= ~RES_USE_INET6;
#endif
      hptr = gethostbyname(sptr->host);
    }
    if (hptr == NULL) {
      if (nsearch == 2)
        continue;               /* failure OK if multiple searches */

      switch (h_errno) {
      case HOST_NOT_FOUND:
        error(EAI_NONAME);
      case TRY_AGAIN:
        error(EAI_AGAIN);
      case NO_RECOVERY:
        error(EAI_FAIL);
      case NO_DATA:
        error(EAI_NODATA);
      default:
        error(EAI_NONAME);
      }
    }

    /* 4check for address family mismatch if one specified */
    if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype)
      error(EAI_ADDRFAMILY);

    /* 4save canonical name first time */
    if (hostname != NULL && hostname[0] != '\0' &&
        (hints.ai_flags & AI_CANONNAME) && canon == NULL) {
      if ((canon = strdup(hptr->h_name)) == NULL)
        error(EAI_MEMORY);
    }

    /* 4create one addrinfo{} for each returned address */
    for (ap = hptr->h_addr_list; *ap != NULL; ap++) {
      rc = ga_aistruct(&aipnext, &hints, *ap, hptr->h_addrtype);
      if (rc != 0)
        error(rc);
    }
  }
  if (aihead == NULL)
    error(EAI_NONAME);          /* nothing found */
  /* end ga4 */

/* include ga5 */
  /* 4return canonical name */
  if (hostname != NULL && hostname[0] != '\0' && hints.ai_flags & AI_CANONNAME) {
    if (canon != NULL)
      aihead->ai_canonname = canon;     /* strdup'ed earlier */
    else {
      if ((aihead->ai_canonname = strdup(search[0].host)) == NULL)
        error(EAI_MEMORY);
    }
  }

  /* 4now process the service name */
  if (servname != NULL && servname[0] != '\0') {
    if ((rc = ga_serv(aihead, &hints, servname)) != 0)
      error(rc);
  }

  *result = aihead;             /* pointer to first structure in linked list */
  return (0);

bad:
  freeaddrinfo(aihead);         /* free any alloc'ed memory */
  return (error);
}

/* end ga5 */

/*
 * Basic error checking of flags, family, socket type, and protocol.
 */

/* include ga_echeck */
static int
ga_echeck(const char *hostname, const char *servname,
          int flags, int family, int socktype, int protocol)
{
  if (flags & ~(AI_PASSIVE | AI_CANONNAME))
    return (EAI_BADFLAGS);      /* unknown flag bits */

  if (hostname == NULL || hostname[0] == '\0') {
    if (servname == NULL || servname[0] == '\0')
      return (EAI_NONAME);      /* host or service must be specified */
  }

  switch (family) {
  case AF_UNSPEC:
    break;
#ifdef  IPv4
  case AF_INET:
    if (socktype != 0 &&
        (socktype != SOCK_STREAM &&
         socktype != SOCK_DGRAM && socktype != SOCK_RAW))
      return (EAI_SOCKTYPE);    /* invalid socket type */
    break;
#endif
#ifdef  HAVE_SOCKADDR_IN6
  case AF_INET6:
    if (socktype != 0 &&
        (socktype != SOCK_STREAM &&
         socktype != SOCK_DGRAM && socktype != SOCK_RAW))
      return (EAI_SOCKTYPE);    /* invalid socket type */
    break;
#endif
  default:
    return (EAI_FAMILY);        /* unknown protocol family */
  }
  return (0);
}

/* end ga_echeck */


/*
 * Set up the search[] array with the hostnames and address families
 * that we are to look up.
 */

/* include ga_nsearch1 */
static int
ga_nsearch(const char *hostname, const struct addrinfo *hintsp,
           struct search *search)
{
  int nsearch = 0;

  if (hostname == NULL || hostname[0] == '\0') {
    if (hintsp->ai_flags & AI_PASSIVE) {
      /* 4no hostname and AI_PASSIVE: implies wildcard bind */
      switch (hintsp->ai_family) {
#ifdef  IPv4
      case AF_INET:
        search[nsearch].host = "0.0.0.0";
        search[nsearch].family = AF_INET;
        nsearch++;
        break;
#endif
#ifdef  HAVE_SOCKADDR_IN6
      case AF_INET6:
        search[nsearch].host = "0::0";
        search[nsearch].family = AF_INET6;
        nsearch++;
        break;
#endif
      case AF_UNSPEC:
#ifdef  HAVE_SOCKADDR_IN6
        search[nsearch].host = "0::0";  /* IPv6 first, then IPv4 */
        search[nsearch].family = AF_INET6;
        nsearch++;
#endif
#ifdef  IPv4
        search[nsearch].host = "0.0.0.0";
        search[nsearch].family = AF_INET;
        nsearch++;
#endif
        break;
      }
      /* end ga_nsearch1 */
      /* include ga_nsearch2 */
    } else {
      /* 4no host and not AI_PASSIVE: connect to local host */
      switch (hintsp->ai_family) {
#ifdef  IPv4
      case AF_INET:
        search[nsearch].host = "localhost";     /* 127.0.0.1 */
        search[nsearch].family = AF_INET;
        nsearch++;
        break;
#endif
#ifdef  HAVE_SOCKADDR_IN6
      case AF_INET6:
        search[nsearch].host = "0::1";
        search[nsearch].family = AF_INET6;
        nsearch++;
        break;
#endif
      case AF_UNSPEC:
#ifdef  HAVE_SOCKADDR_IN6
        search[nsearch].host = "0::1";  /* IPv6 first, then IPv4 */
        search[nsearch].family = AF_INET6;
        nsearch++;
#endif
#ifdef  IPv4
        search[nsearch].host = "localhost";
        search[nsearch].family = AF_INET;
        nsearch++;
#endif
        break;
      }
    }
    /* end ga_nsearch2 */
    /* include ga_nsearch3 */
  } else {                      /* host is specified */
    switch (hintsp->ai_family) {
#ifdef  IPv4
    case AF_INET:
      search[nsearch].host = hostname;
      search[nsearch].family = AF_INET;
      nsearch++;
      break;
#endif
#ifdef  HAVE_SOCKADDR_IN6
    case AF_INET6:
      search[nsearch].host = hostname;
      search[nsearch].family = AF_INET6;
      nsearch++;
      break;
#endif
    case AF_UNSPEC:
#ifdef  HAVE_SOCKADDR_IN6
      search[nsearch].host = hostname;
      search[nsearch].family = AF_INET6;        /* IPv6 first */
      nsearch++;
#endif
#ifdef  IPv4
      search[nsearch].host = hostname;
      search[nsearch].family = AF_INET; /* then IPv4 */
      nsearch++;
#endif
      break;
    }
  }
  if (nsearch < 1 || nsearch > 2)
    return -1;
  return (nsearch);
}

/* end ga_nsearch3 */


/*
 * Create and fill in an addrinfo{}.
 */

#define AI_CLONE 4

/* include ga_aistruct1 */
int
ga_aistruct(struct addrinfo ***paipnext, const struct addrinfo *hintsp,
            const void *addr, int family)
{
  struct addrinfo *ai;

  if ((ai = calloc(1, sizeof(struct addrinfo))) == NULL)
    return (EAI_MEMORY);
  ai->ai_next = NULL;
  ai->ai_canonname = NULL;
  **paipnext = ai;
  *paipnext = &ai->ai_next;

  if ((ai->ai_socktype = hintsp->ai_socktype) == 0)
    ai->ai_flags |= AI_CLONE;

  ai->ai_protocol = hintsp->ai_protocol;
/* end ga_aistruct1 */

/* include ga_aistruct2 */
  switch ((ai->ai_family = family)) {
#ifdef  IPv4
  case AF_INET:{
      struct sockaddr_in *sinptr;

      /* 4allocate sockaddr_in{} and fill in all but port */
      if ((sinptr = calloc(1, sizeof(struct sockaddr_in))) == NULL)
        return (EAI_MEMORY);
#ifdef  HAVE_SOCKADDR_SA_LEN
      sinptr->sin_len = sizeof(struct sockaddr_in);
#endif
      sinptr->sin_family = AF_INET;
      memcpy(&sinptr->sin_addr, addr, sizeof(struct in_addr));
      ai->ai_addr = (struct sockaddr *) sinptr;
      ai->ai_addrlen = sizeof(struct sockaddr_in);
      break;
    }
#endif                          /* IPV4 */
#ifdef  HAVE_SOCKADDR_IN6
  case AF_INET6:{
      struct sockaddr_in6 *sin6ptr;

      /* 4allocate sockaddr_in6{} and fill in all but port */
      if ((sin6ptr = calloc(1, sizeof(struct sockaddr_in6))) == NULL)
        return (EAI_MEMORY);
#ifdef  HAVE_SOCKADDR_SA_LEN
      sin6ptr->sin6_len = sizeof(struct sockaddr_in6);
#endif
      sin6ptr->sin6_family = AF_INET6;
      memcpy(&sin6ptr->sin6_addr, addr, sizeof(struct in6_addr));
      ai->ai_addr = (struct sockaddr *) sin6ptr;
      ai->ai_addrlen = sizeof(struct sockaddr_in6);
      break;
    }
#endif                          /* IPV6 */

  }
  return (0);
}

/* end ga_aistruct2 */

/*
 * This function handles the service string.
 */

/* include ga_serv */
int
ga_serv(struct addrinfo *aihead, const struct addrinfo *hintsp,
        const char *serv)
{
  int port, rc, nfound;

  nfound = 0;
  if (isdigit((unsigned char) serv[0])) {       /* check for port number string first */
    port = (int) htons((unsigned short) atoi(serv));
    if (hintsp->ai_socktype) {
      /* 4caller specifies socket type */
      if ((rc = ga_port(aihead, port, hintsp->ai_socktype)) < 0)
        return (EAI_MEMORY);
      nfound += rc;
    } else {
      /* 4caller does not specify socket type */
      if ((rc = ga_port(aihead, port, SOCK_STREAM)) < 0)
        return (EAI_MEMORY);
      nfound += rc;
      if ((rc = ga_port(aihead, port, SOCK_DGRAM)) < 0)
        return (EAI_MEMORY);
      nfound += rc;
    }
  }

  if (nfound == 0) {
    if (hintsp->ai_socktype == 0)
      return (EAI_NONAME);      /* all calls to getservbyname() failed */
    else
      return (EAI_SERVICE);     /* service not supported for socket type */
  }
  return (0);
}

/* end ga_serv */


/*
 * Go through all the addrinfo structures, checking for a match of the
 * socket type and filling in the socket type, and then the port number
 * in the corresponding socket address structures.
 *
 * The AI_CLONE flag works as follows.  Consider a multihomed host with
 * two IP addresses and no socket type specified by the caller.  After
 * the "host" search there are two addrinfo structures, one per IP address.
 * Assuming a service supported by both TCP and UDP (say the daytime
 * service) we need to return *four* addrinfo structures:
 *              IP#1, SOCK_STREAM, TCP port,
 *              IP#1, SOCK_DGRAM, UDP port,
 *              IP#2, SOCK_STREAM, TCP port,
 *              IP#2, SOCK_DGRAM, UDP port.
 * To do this, when the "host" loop creates an addrinfo structure, if the
 * caller has not specified a socket type (hintsp->ai_socktype == 0), the
 * AI_CLONE flag is set.  When the following function finds an entry like
 * this it is handled as follows: If the entry's ai_socktype is still 0,
 * this is the first use of the structure, and the ai_socktype field is set.
 * But, if the entry's ai_socktype is nonzero, then we clone a new addrinfo
 * structure and set it's ai_socktype to the new value.  Although we only
 * need two socket types today (SOCK_STREAM and SOCK_DGRAM) this algorithm
 * will handle any number.  Also notice that Posix.1g requires all socket
 * types to be nonzero.
 */

/* include ga_port */
int
ga_port(struct addrinfo *aihead, int port, int socktype)
                /* port must be in network byte order */
{
  int nfound = 0;
  struct addrinfo *ai;

  for (ai = aihead; ai != NULL; ai = ai->ai_next) {
    if (ai->ai_flags & AI_CLONE) {
      if (ai->ai_socktype != 0) {
        if ((ai = ga_clone(ai)) == NULL)
          return (-1);          /* memory allocation error */
        /* ai points to newly cloned entry, which is what we want */
      }
    } else if (ai->ai_socktype != socktype)
      continue;                 /* ignore if mismatch on socket type */

    ai->ai_socktype = socktype;

    switch (ai->ai_family) {
#ifdef  IPv4
    case AF_INET:
      ((struct sockaddr_in *) ai->ai_addr)->sin_port = port;
      nfound++;
      break;
#endif
#ifdef  HAVE_SOCKADDR_IN6
    case AF_INET6:
      ((struct sockaddr_in6 *) ai->ai_addr)->sin6_port = port;
      nfound++;
      break;
#endif
    }
  }
  return (nfound);
}

/* end ga_port */

/*
 * Clone a new addrinfo structure from an existing one.
 */

/* include ga_clone */
struct addrinfo *
ga_clone(struct addrinfo *ai)
{
  struct addrinfo *new;

  if ((new = calloc(1, sizeof(struct addrinfo))) == NULL)
    return (NULL);

  new->ai_next = ai->ai_next;
  ai->ai_next = new;

  new->ai_flags = 0;            /* make sure AI_CLONE is off */
  new->ai_family = ai->ai_family;
  new->ai_socktype = ai->ai_socktype;
  new->ai_protocol = ai->ai_protocol;
  new->ai_canonname = NULL;
  new->ai_addrlen = ai->ai_addrlen;
  if ((new->ai_addr = malloc(ai->ai_addrlen)) == NULL)
    return (NULL);
  memcpy(new->ai_addr, ai->ai_addr, ai->ai_addrlen);

  return (new);
}

/* end ga_clone */
#endif                          /* HAS_GETADDRINFO */

/*
 * Return a string containing some additional information after an
 * error from getaddrinfo().
 */
#ifndef HAS_GAI_STRERROR

const char *
gai_strerror(int err)
{
  switch (err) {
  case EAI_ADDRFAMILY:
    return ("address family for host not supported");
  case EAI_AGAIN:
    return ("temporary failure in name resolution");
  case EAI_BADFLAGS:
    return ("invalid flags value");
  case EAI_FAIL:
    return ("non-recoverable failure in name resolution");
  case EAI_FAMILY:
    return ("address family not supported");
  case EAI_MEMORY:
    return ("memory allocation failure");
  case EAI_NODATA:
    return ("no address associated with host");
  case EAI_NONAME:
    return ("host nor service provided, or not known");
  case EAI_SERVICE:
    return ("service not supported for socket type");
  case EAI_SOCKTYPE:
    return ("socket type not supported");
  case EAI_SYSTEM:
    return ("system error");
  default:
    return ("unknown getaddrinfo() error");
  }
}
#endif                          /* HAS_GAI_STRERROR */

#ifndef HAS_GETADDRINFO

/* include freeaddrinfo */
void
freeaddrinfo(struct addrinfo *aihead)
{
  struct addrinfo *ai, *ainext;

  for (ai = aihead; ai != NULL; ai = ainext) {
    if (ai->ai_addr != NULL)
      free(ai->ai_addr);        /* socket address structure */

    if (ai->ai_canonname != NULL)
      free(ai->ai_canonname);

    ainext = ai->ai_next;       /* can't fetch ai_next after free() */
    free(ai);                   /* the addrinfo{} itself */
  }
}

/* end freeaddrinfo */

#endif                          /* HAS_GETADDRINFO */