/*-
 * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stringlist.h>
#include <unistd.h>

#include <atf-c.h>

#include "freebsd_test_suite/macros.h"
#include "testutil.h"

enum test_methods {
	TEST_GETHOSTBYNAME2,
	TEST_GETHOSTBYADDR,
	TEST_GETHOSTBYNAME2_GETADDRINFO,
	TEST_GETHOSTBYADDR_GETNAMEINFO,
	TEST_BUILD_SNAPSHOT,
	TEST_BUILD_ADDR_SNAPSHOT
};

static int ipnode_flags = 0;
static int af_type = AF_INET;
static bool use_ipnode_functions;

DECLARE_TEST_DATA(hostent)
DECLARE_TEST_FILE_SNAPSHOT(hostent)
DECLARE_1PASS_TEST(hostent)
DECLARE_2PASS_TEST(hostent)

/* These stubs will use gethostby***() or getipnodeby***() functions,
 * depending on the use_ipnode_functions global variable value */
static struct hostent *__gethostbyname2(const char *, int);
static struct hostent *__gethostbyaddr(const void *, socklen_t, int);
static void __freehostent(struct hostent *);

static void clone_hostent(struct hostent *, struct hostent const *);
static int compare_hostent(struct hostent *, struct hostent *, void *);
static void dump_hostent(struct hostent *);
static void free_hostent(struct hostent *);

static int is_hostent_equal(struct hostent *, struct addrinfo *);

static void sdump_hostent(struct hostent *, char *, size_t);
static int hostent_read_hostlist_func(struct hostent *, char *);
static int hostent_read_snapshot_addr(char *, unsigned char *, size_t);
static int hostent_read_snapshot_func(struct hostent *, char *);

static int hostent_test_correctness(struct hostent *, void *);
static int hostent_test_gethostbyaddr(struct hostent *, void *);
static int hostent_test_getaddrinfo_eq(struct hostent *, void *);
static int hostent_test_getnameinfo_eq(struct hostent *, void *);

IMPLEMENT_TEST_DATA(hostent)
IMPLEMENT_TEST_FILE_SNAPSHOT(hostent)
IMPLEMENT_1PASS_TEST(hostent)
IMPLEMENT_2PASS_TEST(hostent)

static struct hostent *
__gethostbyname2(const char *name, int af)
{
	struct hostent *he;
	int error;

	if (use_ipnode_functions) {
		error = 0;
		he = getipnodebyname(name, af, ipnode_flags, &error);
		if (he == NULL)
			errno = error;
	} else
		he = gethostbyname2(name, af);

	return (he);
}

static struct hostent *
__gethostbyaddr(const void *addr, socklen_t len, int af)
{
	struct hostent *he;
	int error;

	if (use_ipnode_functions) {
		error = 0;
		he = getipnodebyaddr(addr, len, af, &error);
		if (he == NULL)
			errno = error;
	} else
		he = gethostbyaddr(addr, len, af);

	return (he);
}

static void
__freehostent(struct hostent *he)
{

	/* NOTE: checking for he != NULL - just in case */
	if (use_ipnode_functions && he != NULL)
		freehostent(he);
}

static void
clone_hostent(struct hostent *dest, struct hostent const *src)
{
	ATF_REQUIRE(dest != NULL);
	ATF_REQUIRE(src != NULL);

	char **cp;
	int aliases_num;
	int addrs_num;
	size_t offset;

	memset(dest, 0, sizeof(struct hostent));

	if (src->h_name != NULL) {
		dest->h_name = strdup(src->h_name);
		ATF_REQUIRE(dest->h_name != NULL);
	}

	dest->h_addrtype = src->h_addrtype;
	dest->h_length = src->h_length;

	if (src->h_aliases != NULL) {
		aliases_num = 0;
		for (cp = src->h_aliases; *cp; ++cp)
			++aliases_num;

		dest->h_aliases = calloc(aliases_num + 1, sizeof(char *));
		ATF_REQUIRE(dest->h_aliases != NULL);

		for (cp = src->h_aliases; *cp; ++cp) {
			dest->h_aliases[cp - src->h_aliases] = strdup(*cp);
			ATF_REQUIRE(dest->h_aliases[cp - src->h_aliases] != NULL);
		}
	}

	if (src->h_addr_list != NULL) {
		addrs_num = 0;
		for (cp = src->h_addr_list; *cp; ++cp)
			++addrs_num;

		dest->h_addr_list = calloc(addrs_num + 1, sizeof(char *));
		ATF_REQUIRE(dest->h_addr_list != NULL);

		for (cp = src->h_addr_list; *cp; ++cp) {
			offset = cp - src->h_addr_list;
			dest->h_addr_list[offset] = malloc(src->h_length);
			ATF_REQUIRE(dest->h_addr_list[offset] != NULL);
			memcpy(dest->h_addr_list[offset],
			    src->h_addr_list[offset], src->h_length);
		}
	}
}

static void
free_hostent(struct hostent *ht)
{
	char **cp;

	ATF_REQUIRE(ht != NULL);

	free(ht->h_name);

	if (ht->h_aliases != NULL) {
		for (cp = ht->h_aliases; *cp; ++cp)
			free(*cp);
		free(ht->h_aliases);
	}

	if  (ht->h_addr_list != NULL) {
		for (cp = ht->h_addr_list; *cp; ++cp)
			free(*cp);
		free(ht->h_addr_list);
	}
}

static  int
compare_hostent(struct hostent *ht1, struct hostent *ht2, void *mdata)
{
	char **c1, **c2, **ct, **cb;
	int b;

	if (ht1 == ht2)
		return 0;

	if (ht1 == NULL || ht2 == NULL)
		goto errfin;

	if (ht1->h_name == NULL || ht2->h_name == NULL)
		goto errfin;

	if (ht1->h_addrtype != ht2->h_addrtype ||
	    ht1->h_length != ht2->h_length ||
	    strcmp(ht1->h_name, ht2->h_name) != 0)
		goto errfin;

	c1 = ht1->h_aliases;
	c2 = ht2->h_aliases;

	if ((ht1->h_aliases == NULL || ht2->h_aliases == NULL) &&
	    ht1->h_aliases != ht2->h_aliases)
		goto errfin;

	if (c1 != NULL && c2 != NULL) {
		cb = c1;
		for (;*c1; ++c1) {
			b = 0;
			for (ct = c2; *ct; ++ct) {
				if (strcmp(*c1, *ct) == 0) {
					b = 1;
					break;
				}
			}
			if (b == 0) {
				printf("h1 aliases item can't be found in h2 "
				    "aliases\n");
				goto errfin;
			}
		}

		c1 = cb;
		for (;*c2; ++c2) {
			b = 0;
			for (ct = c1; *ct; ++ct) {
				if (strcmp(*c2, *ct) == 0) {
					b = 1;
					break;
				}
			}
			if (b == 0) {
				printf("h2 aliases item can't be found in h1 "
				    "aliases\n");
				goto errfin;
			}
		}
	}

	c1 = ht1->h_addr_list;
	c2 = ht2->h_addr_list;

	if ((ht1->h_addr_list == NULL || ht2->h_addr_list== NULL) &&
	    ht1->h_addr_list != ht2->h_addr_list)
		goto errfin;

	if (c1 != NULL && c2 != NULL) {
		cb = c1;
		for (; *c1; ++c1) {
			b = 0;
			for (ct = c2; *ct; ++ct) {
				if (memcmp(*c1, *ct, ht1->h_length) == 0) {
					b = 1;
					break;
				}
			}
			if (b == 0) {
				printf("h1 addresses item can't be found in "
				    "h2 addresses\n");
				goto errfin;
			}
		}

		c1 = cb;
		for (; *c2; ++c2) {
			b = 0;
			for (ct = c1; *ct; ++ct) {
				if (memcmp(*c2, *ct, ht1->h_length) == 0) {
					b = 1;
					break;
				}
			}
			if (b == 0) {
				printf("h2 addresses item can't be found in "
				    "h1 addresses\n");
				goto errfin;
			}
		}
	}

	return 0;

errfin:
	if (mdata == NULL) {
		printf("following structures are not equal:\n");
		dump_hostent(ht1);
		dump_hostent(ht2);
	}

	return (-1);
}

static int
check_addrinfo_for_name(struct addrinfo *ai, char const *name)
{
	struct addrinfo *ai2;

	for (ai2 = ai; ai2 != NULL; ai2 = ai2->ai_next) {
		if (strcmp(ai2->ai_canonname, name) == 0)
			return (0);
	}

	return (-1);
}

static int
check_addrinfo_for_addr(struct addrinfo *ai, char const *addr,
	socklen_t addrlen, int af)
{
	struct addrinfo *ai2;

	for (ai2 = ai; ai2 != NULL; ai2 = ai2->ai_next) {
		if (af != ai2->ai_family)
			continue;

		switch (af) {
		case AF_INET:
			if (memcmp(addr,
			    (void *)&((struct sockaddr_in *)ai2->ai_addr)->sin_addr,
			    MIN(addrlen, ai2->ai_addrlen)) == 0)
				return (0);
			break;
		case AF_INET6:
			if (memcmp(addr,
			    (void *)&((struct sockaddr_in6 *)ai2->ai_addr)->sin6_addr,
			    MIN(addrlen, ai2->ai_addrlen)) == 0)
				return (0);
			break;
		default:
			break;
		}
	}

	return (-1);
}

static int
is_hostent_equal(struct hostent *he, struct addrinfo *ai)
{
	char **cp;
	int rv;

#ifdef DEBUG
	printf("checking equality of he and ai\n");
#endif

	rv = check_addrinfo_for_name(ai, he->h_name);
	if (rv != 0) {
		printf("not equal - he->h_name couldn't be found\n");
		return (rv);
	}

	for (cp = he->h_addr_list; *cp; ++cp) {
		rv = check_addrinfo_for_addr(ai, *cp, he->h_length,
			he->h_addrtype);
		if (rv != 0) {
			printf("not equal - one of he->h_addr_list couldn't be found\n");
			return (rv);
		}
	}

#ifdef DEBUG
	printf("equal\n");
#endif

	return (0);
}

static void
sdump_hostent(struct hostent *ht, char *buffer, size_t buflen)
{
	char **cp;
	size_t i;
	int written;

	written = snprintf(buffer, buflen, "%s %d %d",
		ht->h_name, ht->h_addrtype, ht->h_length);
	buffer += written;
	if (written > (int)buflen)
		return;
	buflen -= written;

	if (ht->h_aliases != NULL) {
		if (*(ht->h_aliases) != NULL) {
			for (cp = ht->h_aliases; *cp; ++cp) {
				written = snprintf(buffer, buflen, " %s",*cp);
				buffer += written;
				if (written > (int)buflen)
					return;
				buflen -= written;

				if (buflen == 0)
					return;
			}
		} else {
			written = snprintf(buffer, buflen, " noaliases");
			buffer += written;
			if (written > (int)buflen)
				return;
			buflen -= written;
		}
	} else {
		written = snprintf(buffer, buflen, " (null)");
		buffer += written;
		if (written > (int)buflen)
			return;
		buflen -= written;
	}

	written = snprintf(buffer, buflen, " : ");
	buffer += written;
	if (written > (int)buflen)
		return;
	buflen -= written;

	if (ht->h_addr_list != NULL) {
		if (*(ht->h_addr_list) != NULL) {
			for (cp = ht->h_addr_list; *cp; ++cp) {
				for (i = 0; i < (size_t)ht->h_length; ++i) {
					written = snprintf(buffer, buflen,
					    i + 1 != (size_t)ht->h_length ?
					        "%d." : "%d",
					    (unsigned char)(*cp)[i]);
					buffer += written;
					if (written > (int)buflen)
						return;
					buflen -= written;

					if (buflen == 0)
						return;
				}

				if (*(cp + 1)) {
					written = snprintf(buffer, buflen,
					    " ");
					buffer += written;
					if (written > (int)buflen)
						return;
					buflen -= written;
				}
			}
		} else {
			written = snprintf(buffer, buflen, " noaddrs");
			buffer += written;
			if (written > (int)buflen)
				return;
			buflen -= written;
		}
	} else {
		written = snprintf(buffer, buflen, " (null)");
		buffer += written;
		if (written > (int)buflen)
			return;
		buflen -= written;
	}
}

static int
hostent_read_hostlist_func(struct hostent *he, char *line)
{
	struct hostent *result;
	int rv;

#ifdef DEBUG
	printf("resolving %s: ", line);
#endif
	result = __gethostbyname2(line, af_type);
	if (result != NULL) {
#ifdef DEBUG
		printf("found\n");
#endif

		rv = hostent_test_correctness(result, NULL);
		if (rv != 0) {
			__freehostent(result);
			return (rv);
		}

		clone_hostent(he, result);
		__freehostent(result);
	} else {
#ifdef DEBUG
		printf("not found\n");
#endif
 		memset(he, 0, sizeof(struct hostent));
		he->h_name = strdup(line);
		ATF_REQUIRE(he->h_name != NULL);
	}
	return (0);
}

static int
hostent_read_snapshot_addr(char *addr, unsigned char *result, size_t len)
{
	char *s, *ps, *ts;

	ps = addr;
	while ( (s = strsep(&ps, ".")) != NULL) {
		if (len == 0)
			return (-1);

		*result = (unsigned char)strtol(s, &ts, 10);
		++result;
		if (*ts != '\0')
			return (-1);

		--len;
	}
	if (len != 0)
		return (-1);
	else
		return (0);
}

static int
hostent_read_snapshot_func(struct hostent *ht, char *line)
{
	StringList *sl1, *sl2;
	char *s, *ps, *ts;
	int i, rv;

#ifdef DEBUG
	printf("1 line read from snapshot:\n%s\n", line);
#endif

	rv = 0;
	i = 0;
	sl1 = sl2 = NULL;
	ps = line;
	memset(ht, 0, sizeof(struct hostent));
	while ((s = strsep(&ps, " ")) != NULL) {
		switch (i) {
		case 0:
			ht->h_name = strdup(s);
			ATF_REQUIRE(ht->h_name != NULL);
			break;

		case 1:
			ht->h_addrtype = (int)strtol(s, &ts, 10);
			if (*ts != '\0')
				goto fin;
			break;

		case 2:
			ht->h_length = (int)strtol(s, &ts, 10);
			if (*ts != '\0')
				goto fin;
			break;

		case 3:
			if (sl1 == NULL) {
				if (strcmp(s, "(null)") == 0)
					return (0);

				sl1 = sl_init();
				ATF_REQUIRE(sl1 != NULL);

				if (strcmp(s, "noaliases") != 0) {
					ts = strdup(s);
					ATF_REQUIRE(ts != NULL);
					sl_add(sl1, ts);
				}
			} else {
				if (strcmp(s, ":") == 0)
					++i;
				else {
					ts = strdup(s);
					ATF_REQUIRE(ts != NULL);
					sl_add(sl1, ts);
				}
			}
			break;

		case 4:
			if (sl2 == NULL) {
				if (strcmp(s, "(null)") == 0)
					return (0);

				sl2 = sl_init();
				ATF_REQUIRE(sl2 != NULL);

				if (strcmp(s, "noaddrs") != 0) {
					ts = calloc(1, ht->h_length);
					ATF_REQUIRE(ts != NULL);
					rv = hostent_read_snapshot_addr(s,
					    (unsigned char *)ts,
					    ht->h_length);
					sl_add(sl2, ts);
					if (rv != 0)
						goto fin;
				}
			} else {
				ts = calloc(1, ht->h_length);
				ATF_REQUIRE(ts != NULL);
				rv = hostent_read_snapshot_addr(s,
				    (unsigned char *)ts, ht->h_length);
				sl_add(sl2, ts);
				if (rv != 0)
					goto fin;
			}
			break;
		default:
			break;
		}

		if (i != 3 && i != 4)
			++i;
	}

fin:
	if (sl1 != NULL) {
		sl_add(sl1, NULL);
		ht->h_aliases = sl1->sl_str;
	}
	if (sl2 != NULL) {
		sl_add(sl2, NULL);
		ht->h_addr_list = sl2->sl_str;
	}

	if ((i != 4) || (rv != 0)) {
		free_hostent(ht);
		memset(ht, 0, sizeof(struct hostent));
		return (-1);
	}

	/* NOTE: is it a dirty hack or not? */
	free(sl1);
	free(sl2);
	return (0);
}

static void
dump_hostent(struct hostent *result)
{
	if (result != NULL) {
		char buffer[1024];
		sdump_hostent(result, buffer, sizeof(buffer));
		printf("%s\n", buffer);
	} else
		printf("(null)\n");
}

static int
hostent_test_correctness(struct hostent *ht, void *mdata __unused)
{

#ifdef DEBUG
	printf("testing correctness with the following data:\n");
	dump_hostent(ht);
#endif

	if (ht == NULL)
		goto errfin;

	if (ht->h_name == NULL)
		goto errfin;

	if (!((ht->h_addrtype >= 0) && (ht->h_addrtype < AF_MAX)))
		goto errfin;

	if ((ht->h_length != sizeof(struct in_addr)) &&
		(ht->h_length != sizeof(struct in6_addr)))
		goto errfin;

	if (ht->h_aliases == NULL)
		goto errfin;

	if (ht->h_addr_list == NULL)
		goto errfin;

#ifdef DEBUG
	printf("correct\n");
#endif

	return (0);
errfin:
	printf("incorrect\n");

	return (-1);
}

static int
hostent_test_gethostbyaddr(struct hostent *he, void *mdata)
{
	struct hostent *result;
	struct hostent_test_data *addr_test_data;
	int rv;

	addr_test_data = (struct hostent_test_data *)mdata;

	/* We should omit unresolved hostents */
	if (he->h_addr_list != NULL) {
		char **cp;
		for (cp = he->h_addr_list; *cp; ++cp) {
#ifdef DEBUG
			printf("doing reverse lookup for %s\n", he->h_name);
#endif

			result = __gethostbyaddr(*cp, he->h_length,
			    he->h_addrtype);
			if (result == NULL) {
#ifdef DEBUG
				printf("%s: warning: reverse lookup failed "
				    "for %s: %s\n", __func__, he->h_name,
				    strerror(errno));
#endif
				continue;
			}
			rv = hostent_test_correctness(result, NULL);
			if (rv != 0) {
				__freehostent(result);
				return (rv);
			}

			if (addr_test_data != NULL)
				TEST_DATA_APPEND(hostent, addr_test_data,
				    result);

			__freehostent(result);
		}
	}

	return (0);
}

static int
hostent_test_getaddrinfo_eq(struct hostent *he, void *mdata __unused)
{
	struct addrinfo *ai, hints;
	int rv;

	ai = NULL;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = af_type;
	hints.ai_flags = AI_CANONNAME;

	printf("using getaddrinfo() to resolve %s\n", he->h_name);

	/* struct hostent *he was not resolved */
	if (he->h_addr_list == NULL) {
		/* We can be sure that he->h_name is not NULL */
		rv = getaddrinfo(he->h_name, NULL, &hints, &ai);
		if (rv == 0) {
			printf("not ok - shouldn't have been resolved\n");
			rv = -1;
		} else
			rv = 0;
	} else {
		rv = getaddrinfo(he->h_name, NULL, &hints, &ai);
		if (rv != 0) {
			printf("not ok - should have been resolved\n");
			rv = -1;
			goto done;
		}
		rv = is_hostent_equal(he, ai);
		if (rv != 0) {
			printf("not ok - addrinfo and hostent are not equal\n");
			rv = -1;
		}
	}
done:
	if (ai != NULL)
		freeaddrinfo(ai);
	return (rv);
}

static int
hostent_test_getnameinfo_eq(struct hostent *he, void *mdata __unused)
{
	char **cp;
	char buffer[NI_MAXHOST];
	struct sockaddr_in sin;
	struct sockaddr_in6 sin6;
	struct sockaddr *saddr;
	struct hostent *result;
	int i, rv;

	if (he->h_addr_list == NULL)
		return (0);

	for (cp = he->h_addr_list; *cp; ++cp) {
#ifdef DEBUG
		printf("doing reverse lookup for %s\n", he->h_name);
#endif
		result = __gethostbyaddr(*cp, he->h_length,
		    he->h_addrtype);
		if (result != NULL) {
			rv = hostent_test_correctness(result, NULL);
			if (rv != 0) {
				__freehostent(result);
				return (rv);
			}
		} else
			printf("%s: warning: reverse lookup failed "
			    "for %s: %s\n", __func__, he->h_name,
			    strerror(errno));

		switch (he->h_addrtype) {
		case AF_INET:
			memset(&sin, 0, sizeof(struct sockaddr_in));
			sin.sin_len = sizeof(struct sockaddr_in);
			sin.sin_family = AF_INET;
			memcpy(&sin.sin_addr, *cp, he->h_length);

			saddr = (struct sockaddr *)&sin;
			break;
		case AF_INET6:
			memset(&sin6, 0, sizeof(struct sockaddr_in6));
			sin6.sin6_len = sizeof(struct sockaddr_in6);
			sin6.sin6_family = AF_INET6;
			memcpy(&sin6.sin6_addr, *cp, he->h_length);

			saddr = (struct sockaddr *)&sin6;
			break;
		default:
			printf("warning: %d family is unsupported\n",
			    he->h_addrtype);
			continue;
		}

		ATF_REQUIRE(saddr != NULL);
		rv = getnameinfo(saddr, saddr->sa_len, buffer,
			sizeof(buffer), NULL, 0, NI_NAMEREQD);

		if (rv != 0 && result != NULL) {
			printf("getnameinfo() didn't make the reverse "
			    "lookup, when it should have (%s)\n",
			    gai_strerror(rv));
			return (rv);
		}

		if (rv == 0 && result == NULL) {
			printf("getnameinfo() made the "
			    "reverse lookup, when it shouldn't have\n");
			return (rv);
		}

		if (rv != 0 && result == NULL) {
#ifdef DEBUG
			printf("both getnameinfo() and ***byaddr() failed as "
			    "expected\n");
#endif
			continue;
		}

#ifdef DEBUG
		printf("comparing %s with %s\n", result->h_name,
		    buffer);
#endif

		/*
		 * An address might reverse resolve to hostname alias or the
		 * official hostname, e.g. moon.vub.ac.be.
		 */
		bool found_a_match = false;

		if (strcmp(result->h_name, buffer) == 0) {
			found_a_match = true;
#ifdef DEBUG
			printf("matched official hostname\n");
#endif
		} else {
			for (i = 0; result->h_aliases[i] != NULL; i++) {
				printf("[%d] resolved: %s\n", i,
				    result->h_aliases[i]);
				if (strcmp(result->h_aliases[i],
				    buffer) == 0) {
					printf("matched hostname alias\n");
					found_a_match = true;
					break;
				}
			}
		}
		__freehostent(result);

		if (found_a_match) {
#ifdef DEBUG
			printf("getnameinfo() and ***byaddr() results are "
			    "equal\n");
#endif
		} else {
			printf("getnameinfo() and ***byaddr() results are not "
			    "equal for %s\n", he->h_name);
			return (-1);
		}
	}

	return (0);
}

static int
run_tests(const char *hostlist_file, const char *snapshot_file, int _af_type,
    enum test_methods method, bool use_ipv6_mapping)
{
	char *snapshot_file_copy;
	struct hostent_test_data td, td_addr, td_snap;
	res_state statp;
	int rv = -2;

	if (snapshot_file == NULL)
		snapshot_file_copy = NULL;
	else {
		snapshot_file_copy = strdup(snapshot_file);
		ATF_REQUIRE(snapshot_file_copy != NULL);
	}
	snapshot_file = snapshot_file_copy;

	switch (_af_type) {
	case AF_INET:
		ATF_REQUIRE_FEATURE("inet");
		ATF_REQUIRE(!use_ipv6_mapping);
		break;
	case AF_INET6:
		ATF_REQUIRE_FEATURE("inet6");
		break;
	default:
		atf_tc_fail("unhandled address family: %d", _af_type);
		break;
	}

	if (!use_ipnode_functions) {
		statp = __res_state();
		if (statp == NULL || ((statp->options & RES_INIT) == 0 &&
		    res_ninit(statp) == -1)) {
			printf("error: can't init res_state\n");
			rv = -1;
			goto fin2;
		}

		if (use_ipv6_mapping)
			statp->options |= RES_USE_INET6;
		else
			statp->options &= ~RES_USE_INET6;
	}

	TEST_DATA_INIT(hostent, &td, clone_hostent, free_hostent);
	TEST_DATA_INIT(hostent, &td_addr, clone_hostent, free_hostent);
	TEST_DATA_INIT(hostent, &td_snap, clone_hostent, free_hostent);

	if (access(hostlist_file, R_OK) != 0) {
		printf("can't access the hostlist file %s\n", hostlist_file);
		rv = -1;
		goto fin;
	}

#ifdef DEBUG
	printf("building host lists from %s\n", hostlist_file);
#endif

	rv = TEST_SNAPSHOT_FILE_READ(hostent, hostlist_file, &td,
		hostent_read_hostlist_func);
	if (rv != 0) {
		printf("failed to read the host list file: %s\n",
		    hostlist_file);
		goto fin;
	}

	if (snapshot_file != NULL) {
		if (access(snapshot_file, W_OK | R_OK) != 0) {
			if (errno == ENOENT) {
				if (method != TEST_GETHOSTBYADDR)
					method = TEST_BUILD_SNAPSHOT;
				else
					method = TEST_BUILD_ADDR_SNAPSHOT;
			} else {
				printf("can't access the snapshot file %s\n",
				    snapshot_file);
				rv = -1;
				goto fin;
			}
		} else {
			rv = TEST_SNAPSHOT_FILE_READ(hostent, snapshot_file,
				&td_snap, hostent_read_snapshot_func);
			if (rv != 0) {
				printf("error reading snapshot file\n");
				goto fin;
			}
		}
	}

	switch (method) {
	case TEST_GETHOSTBYNAME2:
		if (snapshot_file != NULL)
			rv = DO_2PASS_TEST(hostent, &td, &td_snap,
			    compare_hostent, NULL);
		break;
	case TEST_GETHOSTBYADDR:
		rv = DO_1PASS_TEST(hostent, &td,
			hostent_test_gethostbyaddr, (void *)&td_addr);
		if (rv != 0)
			goto fin;

		if (snapshot_file != NULL)
			rv = DO_2PASS_TEST(hostent, &td_addr, &td_snap,
			    compare_hostent, NULL);
		break;
	case TEST_GETHOSTBYNAME2_GETADDRINFO:
		rv = DO_1PASS_TEST(hostent, &td,
			hostent_test_getaddrinfo_eq, NULL);
		break;
	case TEST_GETHOSTBYADDR_GETNAMEINFO:
		rv = DO_1PASS_TEST(hostent, &td,
			hostent_test_getnameinfo_eq, NULL);
		break;
	case TEST_BUILD_SNAPSHOT:
		if (snapshot_file != NULL) {
			rv = TEST_SNAPSHOT_FILE_WRITE(hostent, snapshot_file,
			    &td, sdump_hostent);
		}
		break;
	case TEST_BUILD_ADDR_SNAPSHOT:
		if (snapshot_file != NULL) {
			rv = DO_1PASS_TEST(hostent, &td,
			    hostent_test_gethostbyaddr, (void *)&td_addr);
			if (rv != 0)
				goto fin;
			rv = TEST_SNAPSHOT_FILE_WRITE(hostent, snapshot_file,
			    &td_addr, sdump_hostent);
		}
		break;
	default:
		rv = 0;
		break;
	}

fin:
	TEST_DATA_DESTROY(hostent, &td_snap);
	TEST_DATA_DESTROY(hostent, &td_addr);
	TEST_DATA_DESTROY(hostent, &td);

fin2:
	free(snapshot_file_copy);

	return (rv);
}

#define	HOSTLIST_FILE	"mach"

#define	_RUN_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping) \
do {									\
	char *_hostlist_file;						\
	ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s",		\
	    atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE));	\
	ATF_REQUIRE(run_tests(_hostlist_file, snapshot_file, af_type,	\
	    method, use_ipv6_mapping) == 0);				\
	free(_hostlist_file);						\
} while (0)

#define	RUN_HOST_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping) \
do {									\
	use_ipnode_functions = false; 					\
	_RUN_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping); \
} while (0)

#define	RUN_IPNODE_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping) \
do {									\
	use_ipnode_functions = true; 					\
	_RUN_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping); \
} while (0)

ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv4);
ATF_TC_BODY(gethostbyaddr_ipv4, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv4_with_snapshot);
ATF_TC_BODY(gethostbyaddr_ipv4_with_snapshot, tc)
{

	RUN_HOST_TESTS(tc, "snapshot_htaddr4", AF_INET, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6);
ATF_TC_BODY(gethostbyaddr_ipv6, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6_AI_V4MAPPED);
ATF_TC_BODY(gethostbyaddr_ipv6_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6_with_snapshot);
ATF_TC_BODY(gethostbyaddr_ipv6_with_snapshot, tc)
{

	RUN_HOST_TESTS(tc, "snapshot_htaddr6", AF_INET6, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6_with_snapshot_AI_V4MAPPED);
ATF_TC_BODY(gethostbyaddr_ipv6_with_snapshot_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_HOST_TESTS(tc, "snapshot_htaddr6map", AF_INET6, TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_getaddrinfo_ipv4);
ATF_TC_BODY(gethostbyname2_getaddrinfo_ipv4, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_getaddrinfo_ipv6);
ATF_TC_BODY(gethostbyname2_getaddrinfo_ipv6, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_getnameinfo_ipv4);
ATF_TC_BODY(gethostbyaddr_getnameinfo_ipv4, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyaddr_getnameinfo_ipv6);
ATF_TC_BODY(gethostbyaddr_getnameinfo_ipv6, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv4);
ATF_TC_BODY(gethostbyname2_ipv4, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv4_with_snapshot);
ATF_TC_BODY(gethostbyname2_ipv4_with_snapshot, tc)
{

	RUN_HOST_TESTS(tc, "snapshot_htname4", AF_INET, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6);
ATF_TC_BODY(gethostbyname2_ipv6, tc)
{

	RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6_AI_V4MAPPED);
ATF_TC_BODY(gethostbyname2_ipv6_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6_with_snapshot);
ATF_TC_BODY(gethostbyname2_ipv6_with_snapshot, tc)
{

	RUN_HOST_TESTS(tc, "snapshot_htname6", AF_INET6, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6_with_snapshot_AI_V4MAPPED);
ATF_TC_BODY(gethostbyname2_ipv6_with_snapshot_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_HOST_TESTS(tc, "snapshot_htname6map", AF_INET6, TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv4);
ATF_TC_BODY(getipnodebyaddr_ipv4, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv4_with_snapshot);
ATF_TC_BODY(getipnodebyaddr_ipv4_with_snapshot, tc)
{

	RUN_IPNODE_TESTS(tc, "snapshot_ipnodeaddr4", AF_INET, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_getnameinfo_ipv4);
ATF_TC_BODY(getipnodebyaddr_getnameinfo_ipv4, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6);
ATF_TC_BODY(getipnodebyaddr_ipv6, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_AI_V4MAPPED);
ATF_TC_BODY(getipnodebyaddr_ipv6_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG);
ATF_TC_BODY(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG_AI_ALL);
ATF_TC_BODY(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG_AI_ALL, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot);
ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot, tc)
{

	RUN_IPNODE_TESTS(tc, "snapshot_ipnodeaddr6", AF_INET6, TEST_GETHOSTBYADDR, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED);
ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodeaddr6_AI_V4MAPPED", AF_INET6,
	    TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG);
ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodeaddr6_AI_V4MAPPED_CFG", AF_INET6,
	    TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodeaddr6_AI_V4MAPPED_CFG_AI_ALL", AF_INET6,
	    TEST_GETHOSTBYADDR, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyaddr_getnameinfo_ipv6);
ATF_TC_BODY(getipnodebyaddr_getnameinfo_ipv6, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4);
ATF_TC_BODY(getipnodebyname_ipv4, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4_with_snapshot);
ATF_TC_BODY(getipnodebyname_ipv4_with_snapshot, tc)
{

	RUN_IPNODE_TESTS(tc, "snapshot_ipnodename4", AF_INET, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4_AI_ADDRCONFIG);
ATF_TC_BODY(getipnodebyname_ipv4_AI_ADDRCONFIG, tc)
{

	ipnode_flags = AI_ADDRCONFIG;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4_with_snapshot_AI_ADDRCONFIG);
ATF_TC_BODY(getipnodebyname_ipv4_with_snapshot_AI_ADDRCONFIG, tc)
{

	ipnode_flags = AI_ADDRCONFIG;
	RUN_IPNODE_TESTS(tc, "snapshot_ipnodename4_AI_ADDRCONFIG", AF_INET,
	    TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_getaddrinfo_ipv4);
ATF_TC_BODY(getipnodebyname_getaddrinfo_ipv4, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6);
ATF_TC_BODY(getipnodebyname_ipv6, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot);
ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot, tc)
{

	RUN_IPNODE_TESTS(tc, "snapshot_ipnodename6", AF_INET6, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_ADDRCONFIG);
ATF_TC_BODY(getipnodebyname_ipv6_AI_ADDRCONFIG, tc)
{

	ipnode_flags = AI_ADDRCONFIG;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED);
ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED_CFG);
ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED_CFG, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ADDRCONFIG, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG | AI_ADDRCONFIG;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ALL);
ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ALL, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED);
ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED, tc)
{

	ipnode_flags = AI_V4MAPPED;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodename6_AI_V4MAPPED", AF_INET6,
	    TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG);
ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodename6_AI_V4MAPPED_CFG", AF_INET6,
	    TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ADDRCONFIG, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG | AI_ADDRCONFIG;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodename6_AI_V4MAPPED_CFG_AI_ADDRCONFIG", AF_INET6,
	    TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL, tc)
{

	ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
	RUN_IPNODE_TESTS(tc,
	    "snapshot_ipnodename6_AI_V4MAPPED_CFG_AI_ALL", AF_INET6,
	    TEST_GETHOSTBYNAME2, true);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_ADDRCONFIG);
ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_ADDRCONFIG, tc)
{

	ipnode_flags = AI_ADDRCONFIG;
	RUN_IPNODE_TESTS(tc, "snapshot_ipnodename6_AI_ADDRCONFIG", AF_INET6,
	    TEST_GETHOSTBYNAME2, false);
}

ATF_TC_WITHOUT_HEAD(getipnodebyname_getaddrinfo_ipv6);
ATF_TC_BODY(getipnodebyname_getaddrinfo_ipv6, tc)
{

	RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
}

ATF_TP_ADD_TCS(tp)
{

	/* gethostbyaddr */
	ATF_TP_ADD_TC(tp, gethostbyaddr_ipv4);
	ATF_TP_ADD_TC(tp, gethostbyaddr_ipv4_with_snapshot);
	ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6);
	ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6_AI_V4MAPPED); /* XXX */
	ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6_with_snapshot);
	ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6_with_snapshot_AI_V4MAPPED);
	ATF_TP_ADD_TC(tp, gethostbyaddr_getnameinfo_ipv4);
	ATF_TP_ADD_TC(tp, gethostbyaddr_getnameinfo_ipv6);

	/* gethostbyname2 */
	ATF_TP_ADD_TC(tp, gethostbyname2_getaddrinfo_ipv4);
	ATF_TP_ADD_TC(tp, gethostbyname2_getaddrinfo_ipv6);
	ATF_TP_ADD_TC(tp, gethostbyname2_ipv4);
	ATF_TP_ADD_TC(tp, gethostbyname2_ipv4_with_snapshot);
	ATF_TP_ADD_TC(tp, gethostbyname2_ipv6);
	ATF_TP_ADD_TC(tp, gethostbyname2_ipv6_AI_V4MAPPED);
	ATF_TP_ADD_TC(tp, gethostbyname2_ipv6_with_snapshot);
	ATF_TP_ADD_TC(tp, gethostbyname2_ipv6_with_snapshot_AI_V4MAPPED);

	/* getipnodebyaddr */
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv4);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv4_with_snapshot);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_getnameinfo_ipv4);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_AI_V4MAPPED);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_AI_V4MAPPED_CFG);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_AI_V4MAPPED_CFG_AI_ALL);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
	ATF_TP_ADD_TC(tp, getipnodebyaddr_getnameinfo_ipv6);

	/* getipnodebyname */
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv4);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv4_with_snapshot);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv4_AI_ADDRCONFIG);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv4_with_snapshot_AI_ADDRCONFIG);
	ATF_TP_ADD_TC(tp, getipnodebyname_getaddrinfo_ipv4);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_ADDRCONFIG);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED_CFG);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ALL);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
	ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_ADDRCONFIG);
	ATF_TP_ADD_TC(tp, getipnodebyname_getaddrinfo_ipv6);

	return (atf_no_error());
}