Admin Panel

/*
 * Copyright (c) 1996 W. Richard Stevens.  All rights reserved. 
 *
 * Permission to use or modify this software for educational or
 * for commercial purposes, and without fee, is hereby granted,
 * provided that the above copyright notice appears in connection
 * with any and all uses, with clear indication as to any
 * modifications made.  The author RESERVES the sole rights of
 * reproduction, publication and distribution and hence permission
 * to print this source code in any book, reference manual,
 * magazine, or other type of publication, including any digital
 * medium, must be granted in writing by W. Richard Stevens.
 *
 * The author makes no representations about the suitability of this 
 * software for any purpose.  It is provided "as is" without express
 * or implied warranty. 
 */

/* tabs set for 4 spaces, not 8 */

#ifdef	__osf__
#define	_SOCKADDR_LEN		/* required for this implementation */
#define	INET6				/* required for this implementation */
#endif

#include	<sys/types.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<ctype.h>		/* isdigit() */
#include	<netdb.h>		/* hostent{}, servent{}, etc. */
#include	<arpa/inet.h>	/* inet_pton() */
#include	<arpa/nameser.h>
#if !defined(__AROS__) && !defined(__MORPHOS__) && !defined(__amigaos4__)
#include	<resolv.h>		/* DNS resolver */
#endif
#include	<stdlib.h>		/* malloc() and calloc() */
#include	<string.h>		/* strdup() and strncpy() */
#include	<unistd.h>		/* unlink() */
#ifdef __amigaos4__
#undef AF_UNIX
#else
#include	<sys/un.h>		/* for Unix domain socket stuff */
#endif

/* NOTE: this code assumes you have the inet_pton() function as defined
 * in the BIND-4.9.4 release or later (ftp://ftp.vix.com/pub/bind).
 * If you don't have this in your resolver library, in this tar file is
 * a copy of it, along with inet_ntop(). */

/* We need a way to determine if the compiling host supports IPv4/IPv6.
 * Cannot test for AF_INET6, as some vendors #define it, even though
 * they don't support it. */

#ifdef	AF_INET
#define	IPV4	/* this is tested throughout the code that follows */
#endif

			/* no one constant that all implementations define, sigh */
#if		defined(IPV6ADDR_ANY_INIT)
#define	IPV6	/* this is tested throughout the code that follows */
#elif	defined(IPV6_FLOWINFO_FLOWLABEL)
#define	IPV6	/* this is tested throughout the code that follows */
#endif

/* There is no requirement that getaddrinfo() support Unix domain sockets,
 * but what the heck, it doesn't take too much code, and it's a real good
 * test whether an application is *really* protocol-independent. */

#ifdef	AF_UNIX
#define	LOCAL	/* this is tested throughout the code that follows */

#ifndef	AF_LOCAL
#define	AF_LOCAL	AF_UNIX		/* the Posix.1g correct term */
#endif
#ifndef	PF_LOCAL
#define	PF_LOCAL	PF_UNIX		/* the Posix.1g correct term */
#endif

#endif	/* AF_UNIX */

/* Define REENTRANT if the reentrant versions of get{host|serv}byname
 * are to be used.
 * Note that getaddrinfo{} *must* be reentrant if the underlying
 * get{host|serv}byname_r functions are provided.
 * This program has only been tested with the Solaris 2.x functions.
 * (I have no idea if other vendors have the same functions or not.)
 *
 * Long diatribe: Don't define REENTRANT.  At least not until you know
 * what your vendor's gethostbyname_r() function does with regard to
 * IPv4/IPv6 addresses.  If you really need a reentrant version of this
 * function, because you call it from different threads, then use a
 * mutex lock to protect the calls.
 * The problem at the time of this writing is the handling of IPv4/IPv6
 * addresses.  BIND-4.9.4 does it the "right" way :-), but this won't
 * be documented until a later revision of the IPv6 BSD API spec.  Also
 * BIND-4.9.4 doesn't provide the reentrant _r() functions, and I have
 * no idea what the vendors like Sun have done with these functions.
 * The code far below that calls gethostbyname() sets the resolver
 * RES_USE_INET6 option if the caller specifies an ai_family of AF_INET6.
 * This causes 16-byte addresses to be returned, regardless, either
 * "true" IPv6 address from AAAA records, or IPv4-mapped IPv6 addresses
 * from A records.  If the caller specifies an ai_family of AF_INET6,
 * then we should return 16-byte addresses.
 * With BIND-4.9.4 the caller can also force the return of 16-byte addresses
 * by setting the environment variable RES_OPTIONS, as in
 *    % RES_OPTIONS=inet6 ./a.out arguments...
 * This way the caller need not pollute the code with code like
 * ai_family = AF_INET6, making the code protocol-dependent.
 */

/* #define	REENTRANT */

/* Define following function prototype if your headers don't define it */

int	 inet_pton(int, const char *, void *);
void freeaddrinfo(struct addrinfo *aihead);

#define	HENTBUFSIZ	8*1024
#define	HENTMAXADDR	    32	/* max binary address: 16 for IPv4, 24 for IPv6 */

		/* following internal flags cannot overlap with other AI_xxx flags */
#define	AI_CLONE	  4096	/* clone this entry for other socket types */

		/* function prototypes for our own internal functions */
static int	getaddrinfo_host(const char *, struct hostent *,
							 struct hostent **, char *, int,
							 const struct addrinfo *);
static int	getaddrinfo_serv(struct addrinfo *,
							 const struct addrinfo *, const char *,
							 struct servent *, char *, int);
static int	getaddrinfo_port(struct addrinfo *, int , int);
static int	addrinfo_local(const char *, struct addrinfo *,
						   struct addrinfo **);
static struct addrinfo	*getaddrinfo_clone(struct addrinfo *);

		/* globals for all functions in this file; these *must* be
		   read-only if this function is to be reentrant */
static struct addrinfo	hints_default;

int
getaddrinfo(const char *host, const char *serv,
			const struct addrinfo *hintsptr, struct addrinfo **result)
{
	int					rc, error;
	struct hostent		*hptr, hent;
	struct servent		sent;
	char				hentbuf[HENTBUFSIZ], hent_addr[HENTMAXADDR];
	char				*hent_aliases[1], *hent_addr_list[2];
	char				**ap;
	struct addrinfo		hints, *ai, *aihead, **aipnext;

#ifdef	IPV4
	struct sockaddr_in	*sinptr;
#endif

#ifdef	IPV6
	struct sockaddr_in6	*sin6ptr;
#endif

/* 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; }

	/*
	 * We must make a copy of the caller's hints structure, so we can
	 * modify ai_family.  If the caller doesn't provide a hints structure,
	 * use a default one.  This simplifies all the following code.
	 * In the default one, ai_flags, ai_socktype, and ai_protocol are all 0,
	 * but we have to set ai_family to AF_UNSPEC, which isn't guaranteed to
	 * be 0.
	 */ 
	if (hintsptr == NULL) {
		hints_default.ai_family = AF_UNSPEC;
		hints = hints_default;	/* struct copy */
	} else
		hints = *hintsptr;		/* struct copy */

	/*
	 * First some error checking.
	 */
	if (hints.ai_flags & ~(AI_PASSIVE | AI_CANONNAME))
		error(EAI_BADFLAGS);	/* unknown flag bits */

	/*
	 * Check that the family is valid, and if a socket type is also
	 * specified, check that it's valid for the family.
	 */
	if (hints.ai_family != 0) {
		switch(hints.ai_family) {
			case AF_UNSPEC:	break;
				/* Actually, AF_UNSPEC is normally defined as 0,
				   but Posix.1g does not require this. */
#ifdef	IPV4
			case AF_INET:
				if (hints.ai_socktype != 0 &&
					(hints.ai_socktype != SOCK_STREAM &&
					 hints.ai_socktype != SOCK_DGRAM &&
					 hints.ai_socktype != SOCK_RAW))
					error(EAI_SOCKTYPE);	/* invalid socket type */
				break;
#endif
#ifdef	IPV6
			case AF_INET6:
				if (hints.ai_socktype != 0 &&
					(hints.ai_socktype != SOCK_STREAM &&
					 hints.ai_socktype != SOCK_DGRAM &&
					 hints.ai_socktype != SOCK_RAW))
					error(EAI_SOCKTYPE);	/* invalid socket type */
				break;
#endif
#ifdef	LOCAL
			case AF_LOCAL:
				if (hints.ai_socktype != 0 &&
					(hints.ai_socktype != SOCK_STREAM &&
					 hints.ai_socktype != SOCK_DGRAM))
					error(EAI_SOCKTYPE);	/* invalid socket type */
				break;
#endif
		default:
			error(EAI_FAMILY);		/* unknown protocol family */
		}
	}

	if (host == NULL || host[0] == '\0') {
		if (serv == NULL || serv[0] == '\0')
			error(EAI_NONAME);	/* either host or serv must be specified */

		if (hints.ai_flags & AI_PASSIVE) {
			/*
			 * No "host" and AI_PASSIVE: the returned address must be
			 * ready for bind(): 0.0.0.0 for IPv4 or 0::0 for IPv6.
			 */
			switch (hints.ai_family) {
#ifdef	IPV4
			case AF_INET:	host = "0.0.0.0"; break;
#endif
#ifdef	IPV6
			case AF_INET6:	host = "0::0"; break;
#endif
#ifdef	LOCAL
			case AF_LOCAL:
				if (serv[0] != '/')		/* allow service to specify path */
					error(EAI_ADDRFAMILY);
				break;
#endif
			case 0:			error(EAI_ADDRFAMILY);
					/* How can we initialize a socket address structure
					   for a passive open if we don't even know the family? */
			}
		} else {
			/*
			 * No host and not AI_PASSIVE: caller implies connect() to
			 * local host.
			 */

			host = "localhost";
		}
	} else if (hints.ai_family == 0) {
		/*
		 * Caller specifies a host but no address family.
		 * If the host string is really a valid IPv4 dotted-decimal address,
		 * set family to IPv4.  Similarly for IPv6 strings.
		 * This allows server applications to be protocol independent
		 * (not having to hard code a protocol family), allowing the
		 * user who starts the program to specify either 0.0.0.0 or 0::0.
		 *
		 * Assumed below is that inet_pton() allows only "valid" strings,
		 * which Paul Vixie put into the BIND-4.9.4 version of this function.
		 */

		char	temp[16];

#ifdef	IPV4
		if (inet_pton(AF_INET, host, temp) == 1)
			hints.ai_family = AF_INET;
#endif
#ifdef	IPV6
		if (inet_pton(AF_INET6, host, temp) == 1)
			hints.ai_family = AF_INET6;
#endif
		/*
		 * Note that we could bypass some of the testing done in
		 * getaddrinfo_host(), but it doesn't seem worth complicating
		 * this (already long) function.
		 */
	}

#ifdef	LOCAL
	/*
	 * For a Unix domain socket only one string can be provided and we
	 * require it to be an absolute pathname.  (Using relative pathnames
	 * is asking for trouble.)  We allow this string to be specified as
	 * either the hostname or the service name, in which case we ignore
	 * the other string.  Notice that a slash is not allowed in a DNS
	 * hostname (see RFC 1912) and a slash does not appear in any of the
	 * service names in /etc/services either.  Hence no conflict.
	 * For example, often a protocol-independent server will allow an
	 * argument to specify the service (e.g., port number) and let the
	 * hostname be wildcarded.  Similarly, a protocol-independent client
	 * often allows only the hostname as a command-line argument, hardcoding
	 * a service name in the program (which we ignore).
	 */

	if ((host != NULL && host[0] == '/'))
		return(addrinfo_local(host, &hints, result));

	if ((serv != NULL && serv[0] == '/'))
		return(addrinfo_local(serv, &hints, result));
#endif

	/*
	 * Look up the host.  The code above guarantees that "host"
	 * is a nonnull pointer to a nonull string.
	 *
	 * We first initialize "hent" assuming "host" is an IPv4/IPv6 address
	 * (instead of a name).  This saves passing lots of additional
	 * arguments to getaddrinfo_host().
	 */

	hent.h_name = hentbuf;		/* char string specifying address goes here */
	hent.h_aliases = hent_aliases;
	hent_aliases[0] = NULL;			/* no aliases */
	hent.h_addr_list = hent_addr_list;
	hent_addr_list[0] = hent_addr;	/* one binary address in [0] */
	hent_addr_list[1] = NULL;
	hptr = &hent;

	if ( (rc = getaddrinfo_host(host, &hent, &hptr, hentbuf, HENTBUFSIZ,
								&hints)) != 0)
		error(rc);

	/*
	 * "hptr" now points to a filled in hostent{}.
	 * If "host" was an IPv4/IPv6 address, instead of a name, then
	 * "hptr" points to our own "hent" structure.
	 * If gethostbyname_r() was called, then "hptr" points to our own
	 * "hent" structure, which was passed as as an argument to the
	 * reentrant function.
	 * If gethostbyname() was called, then "hptr" points to the static
	 * hostent{} that it returned.
	 *
	 * Check for address family mismatch if the caller specified one.
	 * Note that Posix.1g assumes that AF_foo == PF_foo.
	 */
	if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype)
		error(EAI_ADDRFAMILY);

	/*
	 * Go through the list of returned addresses and create one
	 * addrinfo{} for each one, linking all the structures together.
	 * We still have not looked at the service--that comes after this.
	 */

	aihead = NULL;
	aipnext = &aihead;
	for (ap = hptr->h_addr_list; *ap != NULL; ap++) {
		if ( (ai = calloc(1, sizeof(struct addrinfo))) == NULL)
			error(EAI_MEMORY);
		*aipnext = ai;			/* prev points to this new one */
		aipnext = &ai->ai_next;	/* pointer to next one goes here */

			/* initialize from hints; could be 0 */
		if ( (ai->ai_socktype = hints.ai_socktype) == 0)
			ai->ai_flags |= AI_CLONE;
		ai->ai_protocol = hints.ai_protocol;

		ai->ai_family = hptr->h_addrtype;
		switch (ai->ai_family) {
			/*
			 * Allocate a socket address structure and fill it in.
			 * The port number will be filled in later, when the service
			 * is processed.
			 */
#ifdef	IPV4
			case AF_INET:
				if ( (sinptr = calloc(1, sizeof(struct sockaddr_in))) == NULL)
					error(EAI_MEMORY);
#ifdef	SIN_LEN
				sinptr->sin_len = sizeof(struct sockaddr_in);
#endif
				sinptr->sin_family = AF_INET;
				memcpy(&sinptr->sin_addr, *ap, sizeof(struct in_addr));
				ai->ai_addr = (struct sockaddr *) sinptr;
				ai->ai_addrlen = sizeof(struct sockaddr_in);
				break;
#endif	/* IPV4 */

#ifdef	IPV6
			case AF_INET6:
				if ( (sin6ptr = calloc(1, sizeof(struct sockaddr_in6))) == NULL)
					error(EAI_MEMORY);
#ifdef	SIN6_LEN
				sin6ptr->sin6_len = sizeof(struct sockaddr_in6);
#endif
				sin6ptr->sin6_family = AF_INET6;
				memcpy(&sin6ptr->sin6_addr, *ap, sizeof(struct in6_addr));
				ai->ai_addr = (struct sockaddr *) sin6ptr;
				ai->ai_addrlen = sizeof(struct sockaddr_in6);
				break;
#endif	/* IPV6 */
		}
	}
	/* "aihead" points to the first structure in the linked list */

	if (hints.ai_flags & AI_CANONNAME) {
		/*
		 * Posix.1g doesn't say what to do if this flag is set and
		 * multiple addrinfo structures are returned.
		 * We return the canonical name only in the first addrinfo{}.
		 */
		if (hptr->h_name != NULL) {
			if ( (aihead->ai_canonname = strdup(hptr->h_name)) == NULL)
				error(EAI_MEMORY);
		} else {
			/*
			 * Posix.1g says we can just set ai_canonname to point to the
			 * "host" argument, but that makes freeaddrinfo() harder.
			 * We dynamically allocate room for a copy of "host".
			 */
			if ( (aihead->ai_canonname = strdup(host)) == NULL)
				error(EAI_MEMORY);
		}
	}

	/*
	 * Now look up the service, if specified.
	 */
	if (serv != NULL && serv[0] != '\0') {
		if ( (rc = getaddrinfo_serv(aihead, &hints, serv, &sent,
									hentbuf, HENTBUFSIZ)) != 0)
			error(rc);
	}

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

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

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

static int
getaddrinfo_host(const char *host,
				 struct hostent *hptr, struct hostent **hptrptr,
				 char *buf, int bufsiz, const struct addrinfo *hintsptr)
{

#ifdef	REENTRANT
	int		h_errno;
#endif	/* REENTRANT */

#ifdef	IPV4
	/*
	 * We explicitly check for an IPv4 dotted-decimal string.
	 * Recent versions of gethostbyname(), starting around BIND 4.9.2
	 * do this too, but we have the check here so we don't depend on
	 * this newer feature.  (You wouldn't believe the ancient versions
	 * of BIND that some vendors ship.)
	 */
	if (isdigit(host[0])) {
		if (inet_pton(AF_INET, host, hptr->h_addr_list[0]) == 1) {
				/* Success.  Finish making up the hostent{} as though
				   we had called gethostbyname(). */
			strncpy(hptr->h_name, host, bufsiz-1);
			buf[bufsiz-1] = '\0';
			hptr->h_addrtype = AF_INET;
			hptr->h_length = sizeof(struct in_addr);
			return(0);
		}
	}
#endif	/* IPV4 */

#ifdef	IPV6
	/*
	 * Check for an IPv6 hex string.
	 */
	if (isxdigit(host[0]) || host[0] == ':') {
		if (inet_pton(AF_INET6, host, hptr->h_addr_list[0]) == 1) {
				/* Success.  Finish making up a hostent{} as though
				   we had called gethostbyname(). */
			strncpy(buf, host, bufsiz-1);
			buf[bufsiz-1] = '\0';
			hptr->h_addrtype = AF_INET6;
			hptr->h_length = sizeof(struct in6_addr);
			return(0);
		}
	}
#endif	/* IPV6 */

	if (hintsptr->ai_flags & AI_NUMERICHOST)
		return(EAI_NONAME);

	/*
	 * Not an address, must be a hostname, try the DNS.
	 * Initialize the resolver, if not already initialized.
	 */
#if !defined(__AROS__) && !defined(__MORPHOS__) && !defined(__amigaos4__)
	if ((_res.options & RES_INIT) == 0)
		res_init();			/* need this to set _res.options below */
#endif
#ifdef	IPV6
	/*
	 * Notice that the following might be considered optional, and
	 * could be #ifdef'ed out if your <resolv.h> does not define
	 * RES_USE_INET6.  But I am assuming you have BIND-4.9.4 installed
	 * and want the IPv4/IPv6 semantics that it defines for gethostbyname().
	 */

#ifndef	RES_USE_INET6
	/* This is a gross hack; following line from BIND-4.9.4 release ... */
	/* (if you're using 4.9.4, but have not installed the include files) */
#define	RES_USE_INET6	0x00002000	/* use/map IPv6 in gethostbyname() */
#endif
	if (hintsptr->ai_family == AF_INET6)
		_res.options |= RES_USE_INET6;
#endif	/* IPV6 */

#ifdef	REENTRANT
	hptr = gethostbyname_r(host, hptr, buf, bufsiz, &h_errno);
#else
	hptr = gethostbyname(host);
#endif	/* REENTRANT */
	if (hptr == NULL) {
		switch (h_errno) {
			case HOST_NOT_FOUND:	return(EAI_NONAME);
			case TRY_AGAIN:			return(EAI_AGAIN);
			case NO_RECOVERY:		return(EAI_FAIL);
			case NO_DATA:			return(EAI_NODATA);
			default:				return(EAI_NONAME);
		}
	}
	*hptrptr = hptr;
	return(0);
}

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

static int
getaddrinfo_serv(struct addrinfo *aihead,
				 const struct addrinfo *hintsptr, const char *serv,
				 struct servent *sptrarg, char *buf, int bufsiz)
{
	int				port, rc;
	int				nfound = 0;
	struct servent	*sptr;

	/*
	 * We allow the service to be a numeric string, which we
	 * interpret as a decimal port number.  Posix.1g doesn't
	 * explicitly say to do this, but it just makes sense.
	 * But to do this the caller must specify a socket type,
	 * else there's no way to return values for socket().
	 */

	if (isdigit(serv[0]) && hintsptr->ai_socktype != 0) {
		port = htons(atoi(serv));
		if ( (rc = getaddrinfo_port(aihead, port, hintsptr->ai_socktype)) == 0)
			return(EAI_NONAME);
		else if (rc < 0)
			return(EAI_MEMORY);
		else
			return(0);
	}

	if (hintsptr->ai_flags & AI_NUMERICSERV)
		return(EAI_NONAME);

	/*
	 * Not a special case, try the "/etc/services" file (or whatever).
	 * We first try TCP, if applicable.
	 */

	if (hintsptr->ai_socktype == 0 || hintsptr->ai_socktype == SOCK_STREAM) {
#ifdef	REENTRANT
		sptr = getservbyname_r(serv, "tcp", sptrarg, buf, bufsiz);
#else
		sptr = getservbyname(serv, "tcp");
#endif	/* REENTRANT */
		if (sptr != NULL) {
			rc = getaddrinfo_port(aihead, sptr->s_port, SOCK_STREAM);
			if (rc < 0)
				return(EAI_MEMORY);
			nfound += rc;
		}
	}

	/*
	 * Now try UDP, if applicable.
	 */
	if (hintsptr->ai_socktype == 0 || hintsptr->ai_socktype == SOCK_DGRAM) {
#ifdef	REENTRANT
		sptr = getservbyname_r(serv, "udp", sptrarg, buf, bufsiz);
#else
		sptr = getservbyname(serv, "udp");
#endif	/* REENTRANT */
		if (sptr != NULL) {
			rc = getaddrinfo_port(aihead, sptr->s_port, SOCK_DGRAM);
			if (rc < 0)
				return(EAI_MEMORY);
			nfound += rc;
		}
	}

	if (nfound == 0) {
			/* You could call getservbyname() one more time, with no
			   protocol specified, but "tcp" and "udp" are all that
			   are supported today. */

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

/*
 * 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 (hints->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.
 */

static int
getaddrinfo_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) {
		/*
		 * We set the socket type but not the protocol, because if a
		 * port number is specified, the protocol must be TCP or UDP,
		 * and a protocol of 0 for socket() is fine for TCP and UDP.
		 * The only time a nonzero protocol argument is required by
		 * socket() is for a raw socket, in which case a service will
		 * not be specified to getaddrinfo().
		 */

		if (ai->ai_flags & AI_CLONE) {
			if (ai->ai_socktype != 0) {
				if ( (ai = getaddrinfo_clone(ai)) == NULL)
					return(-1);	/* tell caller it's a 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	IPV6
			case AF_INET6:
				((struct sockaddr_in6 *) ai->ai_addr)->sin6_port = port;
				nfound++;
				break;
#endif
		}
	}
	return(nfound);
}

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

static struct addrinfo *
getaddrinfo_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);
}

#ifdef	LOCAL
/*
 * Do everything for a Unix domain socket.
 * Only one addrinfo{} is returned.
 */

static int
addrinfo_local(const char *path, struct addrinfo *hints,
			   struct addrinfo **result)
{
	struct addrinfo		*ai;
	struct sockaddr_un	*unp;

	if (hints->ai_socktype == 0)
		return(EAI_SOCKTYPE);	/* we cannot tell socket type from service */

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

	ai->ai_flags = 0;
	ai->ai_family = AF_LOCAL;
	ai->ai_socktype = hints->ai_socktype;
	ai->ai_protocol = 0;

		/* allocate and fill in a socket address structure */
	ai->ai_addrlen = sizeof(struct sockaddr_un);
	if ( (ai->ai_addr = malloc(ai->ai_addrlen)) == NULL)
		return(EAI_MEMORY);
	unp = (struct sockaddr_un *) ai->ai_addr;
	unp->sun_family = AF_UNIX;
	strncpy(unp->sun_path, path, sizeof(unp->sun_path));

	ai->ai_canonname = NULL;	/* maybe return the i-node number :-) */
	ai->ai_next = NULL;
	*result = ai;

	if (hints->ai_flags & AI_PASSIVE)
		unlink(path);		/* OK if this fails */

	return(0);		/* success */
}
#endif	/* LOCAL */

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);		/* the socket address structure */
		if (ai->ai_canonname != NULL)
			free(ai->ai_canonname);	/* the canonical name */
		ainext = ai->ai_next;		/* can't fetch ai_next after free() */
		free(ai);					/* the addrinfo{} itself */
	}
}