diff mbox series

network/lib6/getaddrinfo01: rewrite with the new API + use static hostnames

Message ID 20210517085637.16866-1-aleksei.kodanev@bell-sw.com
State Superseded
Headers show
Series network/lib6/getaddrinfo01: rewrite with the new API + use static hostnames | expand

Commit Message

Alexey Kodanev May 17, 2021, 8:56 a.m. UTC
The test is now independent of various machine settings
regarding the test host name as it adds predefined names
and aliases to /etc/hosts file and restores it to its
original state after completing the test.

This should fix the following failures:
* when gethostname() returns an alias name that doesn't
  match canonical name;
* No AAAA record for the returned name from gethostname().

Addresses and names added to /etc/hosts are more or less
unique, so that there are no conflicts with the existing
configuration.

Also most of the duplicate code is now gone.

Signed-off-by: Alexey Kodanev <aleksei.kodanev@bell-sw.com>
---
 testcases/network/lib6/getaddrinfo_01.c | 1140 +++++------------------
 1 file changed, 235 insertions(+), 905 deletions(-)

Comments

Petr Vorel May 17, 2021, 7:57 p.m. UTC | #1
Hi Alexey,

> The test is now independent of various machine settings
> regarding the test host name as it adds predefined names
> and aliases to /etc/hosts file and restores it to its
> original state after completing the test.

> This should fix the following failures:
> * when gethostname() returns an alias name that doesn't
>   match canonical name;
> * No AAAA record for the returned name from gethostname().

> Addresses and names added to /etc/hosts are more or less
> unique, so that there are no conflicts with the existing
> configuration.

> Also most of the duplicate code is now gone.

Thanks a lot. On a first look looks very nice, I'll try to review tomorrow.
I'd be for merging this before release as the old code is broken.

> Signed-off-by: Alexey Kodanev <aleksei.kodanev@bell-sw.com>
Congratulations to a new job. That explains why NFS related patches didn't
reached you. FYI nfs-utils related "Fix NFSv4 export of tmpfs filesystems" patch
from Neil Brown [1], fix for my bug report (with naive attempt to fix it) found
with LTP NFS tests [2].

Kind regards,
Petr

[1] https://lore.kernel.org/linux-nfs/162122673178.19062.96081788305923933@noble.neil.brown.name/
[2] https://lore.kernel.org/linux-nfs/YILQip3nAxhpXP9+@pevik/T/#t
Petr Vorel May 18, 2021, 6:02 a.m. UTC | #2
Hi Alexey,

Reviewed-by: Petr Vorel <pvorel@suse.cz>

Moving everything to /etc/hosts based setup is a great idea
as we get rid of network setup related failures.

Do we lost any getaddrinfo() test coverage for bypassing DNS?

Just a few unimportant nits below (feel free to ignore them).
Again, I'd be for merging this before release.

> The test is now independent of various machine settings
> regarding the test host name as it adds predefined names
> and aliases to /etc/hosts file and restores it to its
> original state after completing the test.

> This should fix the following failures:
> * when gethostname() returns an alias name that doesn't
>   match canonical name;
> * No AAAA record for the returned name from gethostname().

> Addresses and names added to /etc/hosts are more or less
> unique, so that there are no conflicts with the existing
> configuration.
We might want to put this into docparse documentation, e.g:

/*\
 * [Description]
 *
 * Basic getaddrinfo() tests.
 *
 * Test use LTP specific addresses and names added to /etc/hosts to avoid
 * problems with DNS and hostname setup or conflicts with existing
 * configuration.
 */

> Also most of the duplicate code is now gone.

> Signed-off-by: Alexey Kodanev <aleksei.kodanev@bell-sw.com>
> ---
>  testcases/network/lib6/getaddrinfo_01.c | 1140 +++++------------------
>  1 file changed, 235 insertions(+), 905 deletions(-)

> diff --git a/testcases/network/lib6/getaddrinfo_01.c b/testcases/network/lib6/getaddrinfo_01.c
> index db252a998..23a279ed1 100644

...
> +static void gaiv(void)
> +{
> +	check_addrinfo(1, "basic lookup", hostname, 0, NULL, 0, 0, 0, NULL);
> +	check_addrinfo_name("canonical name");

...
> +	check_addrinfo(1, "host+service", hostname, 7, "echo", 0, 0, 0, NULL);
> +
> +	check_addrinfo(1, "host+service, AI_PASSIVE", hostname, 9462, "9462",
> +		       AI_PASSIVE, SOCK_STREAM, 0, test_passive);
> +
> +	check_addrinfo(0, "host+service, AI_NUMERICHOST", hostname, 7, "echo",
> +		       AI_NUMERICHOST, SOCK_STREAM, 0, NULL);
> +	if (TST_RET != EAI_NONAME)
> +		tst_brk(TFAIL, "AI_NUMERICHOST: ret %ld exp %d (EAI_NONAME)",
> +			TST_RET, EAI_NONAME);
> +	tst_res(TPASS, "AI_NUMERICHOST: exp %ld (EAI_NONAME)", TST_RET);
> +
> +	check_addrinfo(1, "0+service, AI_PASSIVE", NULL, 9462, "9462",
> +		       AI_PASSIVE, SOCK_STREAM, 0, test_passive_no_host);
> +
> +	check_addrinfo(0, "0+service", NULL, 9462, "9462",
> +		       0, SOCK_STREAM, 0, test_loopback);
> +	if (TST_RET == EAI_BADFLAGS) {
> +		tst_res(TPASS, "0+service ('', '9462') returns %ld '%s'",
> +			TST_RET, gai_strerror(TST_RET));
> +	} else if (TST_RET) {
> +		tst_brk(TFAIL, "0+service ('', '9462') returns %ld '%s'",
> +			TST_RET, gai_strerror(TST_RET));
>  	}
nit: Maybe having check_addrinfo_badflags() which would do the verification
would safe few lines of code duplicity.

...
> -	/* test 16, IPv6 host+service w/ AI_NUMERICHOST */
> -	memset(&hints, 0, sizeof(hints));
> -	strcpy(service, "echo");
> -	servnum = 7;
> -	hints.ai_family = AF_INET6;
> -	hints.ai_flags = AI_NUMERICHOST;
> -	TEST(getaddrinfo(hostname, service, &hints, &aires));
> -	if (TEST_RETURN != EAI_NONAME) {
> -		tst_resm(TFAIL, "getaddrinfo IPv6 AI_NUMERICHOST w/ hostname: "
> -			 "returns %ld expected %d (EAI_NONAME)",
> -			 TEST_RETURN, EAI_NONAME);
> -		if (!TEST_RETURN)
> -			freeaddrinfo(aires);
> -		return;
> +	check_addrinfo(0, "SOCK_STREAM/IPPROTO_UDP", NULL, 0, NULL, 0,
> +		       SOCK_STREAM, IPPROTO_UDP, NULL);
> +	if (!TST_RET)
> +		tst_brk(TFAIL, "SOCK_STREAM/IPPROTO_UDP: unexpected pass");
> +	tst_res(TPASS, "SOCK_STREAM/IPPROTO_UDP: failed as expected");
> +
> +	check_addrinfo(0, "socktype 0, 513", NULL, 513, "513", 0, 0, 0, NULL);
And here also check_addrinfo_badflags() (if implemented)
and nit: "socktype 0,513" (remove space)

> +	if (TST_RET == EAI_BADFLAGS) {
> +		tst_res(TPASS, "socktype 0,513 returns %ld '%s'",
> +			TST_RET, gai_strerror(TST_RET));
> +	} else if (TST_RET) {
> +		tst_brk(TFAIL, "socktype 0,513 returns %ld '%s'",
> +			TST_RET, gai_strerror(TST_RET));
>  	}

Kind regards,
Petr
Alexey Kodanev May 18, 2021, 8:30 a.m. UTC | #3
On 18.05.2021 09:02, Petr Vorel wrote:
> Hi Alexey,
> 
> Reviewed-by: Petr Vorel <pvorel@suse.cz>
> 

Hi Petr,

Thank you for review!

> Moving everything to /etc/hosts based setup is a great idea
> as we get rid of network setup related failures.
> 
> Do we lost any getaddrinfo() test coverage for bypassing DNS?

We may lose some DNS lookups queries because the files almost always
will be checked first by nss, and I think this is covered by another
test, dns-stress{01|02} (focus more on the server than the client).

> Just a few unimportant nits below (feel free to ignore them).
> Again, I'd be for merging this before release.
> 

OK, I'll post v2 today.

>> The test is now independent of various machine settings
>> regarding the test host name as it adds predefined names
>> and aliases to /etc/hosts file and restores it to its
>> original state after completing the test.
> 
>> This should fix the following failures:
>> * when gethostname() returns an alias name that doesn't
>>   match canonical name;
>> * No AAAA record for the returned name from gethostname().
> 
>> Addresses and names added to /etc/hosts are more or less
>> unique, so that there are no conflicts with the existing
>> configuration.
> We might want to put this into docparse documentation, e.g:
> 
> /*\
>  * [Description]
>  *
>  * Basic getaddrinfo() tests.
>  *
>  * Test use LTP specific addresses and names added to /etc/hosts to avoid
>  * problems with DNS and hostname setup or conflicts with existing
>  * configuration.
>  */
> 
>> Also most of the duplicate code is now gone.
> 
>> Signed-off-by: Alexey Kodanev <aleksei.kodanev@bell-sw.com>
>> ---
>>  testcases/network/lib6/getaddrinfo_01.c | 1140 +++++------------------
>>  1 file changed, 235 insertions(+), 905 deletions(-)
> 
>> diff --git a/testcases/network/lib6/getaddrinfo_01.c b/testcases/network/lib6/getaddrinfo_01.c
>> index db252a998..23a279ed1 100644
> 
> ...
>> +static void gaiv(void)
>> +{
>> +	check_addrinfo(1, "basic lookup", hostname, 0, NULL, 0, 0, 0, NULL);
>> +	check_addrinfo_name("canonical name");
> 
> ...
>> +	check_addrinfo(1, "host+service", hostname, 7, "echo", 0, 0, 0, NULL);
>> +
>> +	check_addrinfo(1, "host+service, AI_PASSIVE", hostname, 9462, "9462",
>> +		       AI_PASSIVE, SOCK_STREAM, 0, test_passive);
>> +
>> +	check_addrinfo(0, "host+service, AI_NUMERICHOST", hostname, 7, "echo",
>> +		       AI_NUMERICHOST, SOCK_STREAM, 0, NULL);
>> +	if (TST_RET != EAI_NONAME)
>> +		tst_brk(TFAIL, "AI_NUMERICHOST: ret %ld exp %d (EAI_NONAME)",
>> +			TST_RET, EAI_NONAME);
>> +	tst_res(TPASS, "AI_NUMERICHOST: exp %ld (EAI_NONAME)", TST_RET);
>> +
>> +	check_addrinfo(1, "0+service, AI_PASSIVE", NULL, 9462, "9462",
>> +		       AI_PASSIVE, SOCK_STREAM, 0, test_passive_no_host);
>> +
>> +	check_addrinfo(0, "0+service", NULL, 9462, "9462",
>> +		       0, SOCK_STREAM, 0, test_loopback);
>> +	if (TST_RET == EAI_BADFLAGS) {
>> +		tst_res(TPASS, "0+service ('', '9462') returns %ld '%s'",
>> +			TST_RET, gai_strerror(TST_RET));
>> +	} else if (TST_RET) {
>> +		tst_brk(TFAIL, "0+service ('', '9462') returns %ld '%s'",
>> +			TST_RET, gai_strerror(TST_RET));
>>  	}
> nit: Maybe having check_addrinfo_badflags() which would do the verification
> would safe few lines of code duplicity.
> 
> ...
>> -	/* test 16, IPv6 host+service w/ AI_NUMERICHOST */
>> -	memset(&hints, 0, sizeof(hints));
>> -	strcpy(service, "echo");
>> -	servnum = 7;
>> -	hints.ai_family = AF_INET6;
>> -	hints.ai_flags = AI_NUMERICHOST;
>> -	TEST(getaddrinfo(hostname, service, &hints, &aires));
>> -	if (TEST_RETURN != EAI_NONAME) {
>> -		tst_resm(TFAIL, "getaddrinfo IPv6 AI_NUMERICHOST w/ hostname: "
>> -			 "returns %ld expected %d (EAI_NONAME)",
>> -			 TEST_RETURN, EAI_NONAME);
>> -		if (!TEST_RETURN)
>> -			freeaddrinfo(aires);
>> -		return;
>> +	check_addrinfo(0, "SOCK_STREAM/IPPROTO_UDP", NULL, 0, NULL, 0,
>> +		       SOCK_STREAM, IPPROTO_UDP, NULL);
>> +	if (!TST_RET)
>> +		tst_brk(TFAIL, "SOCK_STREAM/IPPROTO_UDP: unexpected pass");
>> +	tst_res(TPASS, "SOCK_STREAM/IPPROTO_UDP: failed as expected");
>> +
>> +	check_addrinfo(0, "socktype 0, 513", NULL, 513, "513", 0, 0, 0, NULL);
> And here also check_addrinfo_badflags() (if implemented)
> and nit: "socktype 0,513" (remove space)
> 
>> +	if (TST_RET == EAI_BADFLAGS) {
>> +		tst_res(TPASS, "socktype 0,513 returns %ld '%s'",
>> +			TST_RET, gai_strerror(TST_RET));
>> +	} else if (TST_RET) {
>> +		tst_brk(TFAIL, "socktype 0,513 returns %ld '%s'",
>> +			TST_RET, gai_strerror(TST_RET));
>>  	}
> 
> Kind regards,
> Petr
>
Cyril Hrubis May 18, 2021, 9:59 a.m. UTC | #4
Hi!
> >> The test is now independent of various machine settings
> >> regarding the test host name as it adds predefined names
> >> and aliases to /etc/hosts file and restores it to its
> >> original state after completing the test.
> > 
> >> This should fix the following failures:
> >> * when gethostname() returns an alias name that doesn't
> >>   match canonical name;
> >> * No AAAA record for the returned name from gethostname().
> > 
> >> Addresses and names added to /etc/hosts are more or less
> >> unique, so that there are no conflicts with the existing
> >> configuration.
> > 
> >> Also most of the duplicate code is now gone.
> > 
> > Thanks a lot. On a first look looks very nice, I'll try to review tomorrow.
> > I'd be for merging this before release as the old code is broken.
> 
> Yeah, I'm for it too.

I would be a bit more cautious now as it's fairly late in the release
process. We already started pre-release testing and got some reports in.
Merging this would invalidate results for this test and would require
re-testing.
Alexey Kodanev May 18, 2021, 10:19 a.m. UTC | #5
On 17.05.2021 22:57, Petr Vorel wrote:
> Hi Alexey,
> 
>> The test is now independent of various machine settings
>> regarding the test host name as it adds predefined names
>> and aliases to /etc/hosts file and restores it to its
>> original state after completing the test.
> 
>> This should fix the following failures:
>> * when gethostname() returns an alias name that doesn't
>>   match canonical name;
>> * No AAAA record for the returned name from gethostname().
> 
>> Addresses and names added to /etc/hosts are more or less
>> unique, so that there are no conflicts with the existing
>> configuration.
> 
>> Also most of the duplicate code is now gone.
> 
> Thanks a lot. On a first look looks very nice, I'll try to review tomorrow.
> I'd be for merging this before release as the old code is broken.

Yeah, I'm for it too.

> 
>> Signed-off-by: Alexey Kodanev <aleksei.kodanev@bell-sw.com>
> Congratulations to a new job. That explains why NFS related patches didn't
> reached you. FYI nfs-utils related "Fix NFSv4 export of tmpfs filesystems" patch
> from Neil Brown [1], fix for my bug report (with naive attempt to fix it) found
> with LTP NFS tests [2].

Thanks Petr! Nice catch!

> 
> Kind regards,
> Petr
> 
> [1] https://lore.kernel.org/linux-nfs/162122673178.19062.96081788305923933@noble.neil.brown.name/
> [2] https://lore.kernel.org/linux-nfs/YILQip3nAxhpXP9+@pevik/T/#t
>
Petr Vorel May 18, 2021, 3:25 p.m. UTC | #6
Hi Alexey,

> Hi Petr,

> Thank you for review!
Thank you for finding a time to cleanup this, it'd take long time to me to
rewrite it.

> > Moving everything to /etc/hosts based setup is a great idea
> > as we get rid of network setup related failures.

> > Do we lost any getaddrinfo() test coverage for bypassing DNS?

> We may lose some DNS lookups queries because the files almost always
> will be checked first by nss, and I think this is covered by another
> test, dns-stress{01|02} (focus more on the server than the client).

+1, we don't need to worry.

Kind regards,
Petr
diff mbox series

Patch

diff --git a/testcases/network/lib6/getaddrinfo_01.c b/testcases/network/lib6/getaddrinfo_01.c
index db252a998..23a279ed1 100644
--- a/testcases/network/lib6/getaddrinfo_01.c
+++ b/testcases/network/lib6/getaddrinfo_01.c
@@ -1,979 +1,309 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
+ * Copyright (c) 2021, BELLSOFT. All rights reserved.
  * Copyright (c) 2015 Fujitsu Ltd.
  * Copyright (c) International Business Machines  Corp., 2001
  *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
  * Author: David L Stevens
  */
 
 #include <unistd.h>
 #include <errno.h>
+#include <stdlib.h>
 
 #include <sys/socket.h>
 #include <netdb.h>
 #include <arpa/inet.h>
 #include <sys/param.h>
 
-#include "test.h"
+#include "tst_safe_stdio.h"
+#include "tst_test.h"
+#include "tst_safe_net.h"
 
 #ifndef AI_V4MAPPED
-#define AI_V4MAPPED    0x0008	/* IPv4 mapped addresses are acceptable.  */
+# define AI_V4MAPPED    0x0008	/* IPv4 mapped addresses are acceptable.  */
 #endif
 
-static void setup(void);
-static void gaiv4(void);
-static void gaiv6(void);
+static const char *const host_file = "/etc/hosts";
+static const char *hostname;
+static const char *shortname;
+static sa_family_t family;
 
-char *TCID = "getaddrinfo_01";
-int TST_TOTAL = 22;
-
-int main(int argc, char *argv[])
+static void verify_res(struct addrinfo *res, int sock_type, in_port_t servnum,
+		       int (*test_cb)(struct addrinfo *))
 {
-	int lc;
-
-	tst_parse_opts(argc, argv, NULL, NULL);
+	sa_family_t sin_family = 0;
+	in_port_t sin_port = 0;
+	struct addrinfo *p = res;
+	int got_tcp = 0;
+	int got_udp = 0;
+	int ret = 0;
+
+	size_t exp_addrlen = (family == AF_INET) ? sizeof(struct sockaddr_in) :
+			     sizeof(struct sockaddr_in6);
+
+	for (; p; p = p->ai_next) {
+		ret |= p->ai_family != family;
+		ret |= p->ai_addrlen != exp_addrlen;
+		ret |= p->ai_addr == 0;
+		got_tcp |= p->ai_socktype == SOCK_STREAM;
+		got_udp |= p->ai_socktype == SOCK_DGRAM;
+
+		if (p->ai_addr) {
+
+			if (test_cb)
+				ret |= test_cb(p);
+
+			if (p->ai_family == AF_INET) {
+				struct sockaddr_in *psin;
+
+				psin = (struct sockaddr_in *)p->ai_addr;
+				sin_family = psin->sin_family;
+				sin_port = psin->sin_port;
+			} else {
+				struct sockaddr_in6 *psin6;
+
+				psin6 = (struct sockaddr_in6 *)p->ai_addr;
+				sin_family = psin6->sin6_family;
+				sin_port = psin6->sin6_port;
+			}
 
-	setup();
+			ret |= sin_family != family;
+			ret |= sin_port != htons(servnum);
+		}
 
-	for (lc = 0; TEST_LOOPING(lc); ++lc) {
-		tst_count = 0;
+		if (ret)
+			break;
+	}
 
-		gaiv4();
-		gaiv6();
+	if (!sock_type && (!got_tcp || !got_udp)) {
+		tst_brk(TFAIL, "socktype 0,%d TCP %d UDP %d",
+			htons(sin_port), got_tcp, got_udp);
 	}
 
-	tst_exit();
+	if (ret) {
+		tst_brk(TFAIL, "family %d alen %d sin family %d port %d",
+			p->ai_family, p->ai_addrlen, sin_family,
+			htons(sin_port));
+	}
 }
 
-static void setup(void)
+static void print_test_family(const char *name)
 {
-	TEST_PAUSE;
+	tst_res(TINFO, "test %s: %s", (family == AF_INET) ? "IPv4" : "IPv6",
+		name);
 }
 
-/* getaddrinfo tests (v4) */
-static void gaiv4(void)
+static void check_addrinfo(int safe, const char *name, const char *host,
+			   in_port_t servnum, const char *service,
+			   int flags, int type, int proto,
+			   int (*test_cb)(struct addrinfo *))
 {
-	struct addrinfo *aires, hints, *pai;
-	char hostname[MAXHOSTNAMELEN + 1];
-	char shortname[MAXHOSTNAMELEN + 1];
-	char service[NI_MAXSERV + 1];
-	int servnum;
-	char *p;
-
-	if (gethostname(hostname, sizeof(hostname)) < 0)
-		tst_brkm(TBROK | TERRNO, NULL, "gethostname failed");
-	strncpy(shortname, hostname, MAXHOSTNAMELEN);
-	shortname[MAXHOSTNAMELEN] = '\0';
-	p = strchr(shortname, '.');
-	if (p)
-		*p = '\0';
-
-	/* test 1, IPv4 basic lookup */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	TEST(getaddrinfo(hostname, 0, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != 0;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 basic lookup: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 basic lookup");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv4 basic "
-			 "lookup (\"%s\") returns %ld (\"%s\")", hostname,
-			 TEST_RETURN, gai_strerror(TEST_RETURN));
-		return;
-	}
+	struct addrinfo *res = NULL;
+	struct addrinfo hints;
 
-	/* test 2, IPv4 canonical name */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_flags = AI_CANONNAME;
-	TEST(getaddrinfo(shortname, 0, &hints, &aires));
-	if (!TEST_RETURN) {
-		for (pai = aires; pai; pai = pai->ai_next)
-			if (pai->ai_canonname)
-				break;
-		if (!pai) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 canonical name: no "
-				 "entries with canonical name set");
-			freeaddrinfo(aires);
-			return;
-		} else if (strcasecmp(hostname, pai->ai_canonname)) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 canonical name "
-				 "(\"%s\") doesn't match hostname (\"%s\")",
-				 pai->ai_canonname, hostname);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 canonical name");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv4 "
-			 "canonical name (\"%s\") returns %ld (\"%s\")",
-			 shortname, TEST_RETURN, gai_strerror(TEST_RETURN));
-		return;
-	}
+	print_test_family(name);
 
-	/* test 3, IPv4 host+service name */
 	memset(&hints, 0, sizeof(hints));
-	/*
-	 * These are hard-coded for echo/7 to avoid using getservbyname(),
-	 * since it isn't thread-safe and these tests may be re-used
-	 * multithreaded. Sigh.
-	 */
-	strcpy(service, "echo");
-	servnum = 7;
-	hints.ai_family = AF_INET;
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != htons(servnum);
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 host+service: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 host+service");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv4 host+"
-			 "service returns %ld (\"%s\")", TEST_RETURN,
-			 gai_strerror(TEST_RETURN));
-		return;
+	hints.ai_family = family;
+	hints.ai_flags = flags;
+	hints.ai_socktype = type;
+	hints.ai_protocol = proto;
+
+	if (safe)
+		SAFE_GETADDRINFO(host, service, &hints, &res);
+	else
+		TEST(getaddrinfo(host, service, &hints, &res));
+
+	if (res) {
+		verify_res(res, type, servnum, test_cb);
+		freeaddrinfo(res);
+		tst_res(TPASS, "%s", name);
 	}
+}
 
-	/* test 4, IPv4 hostname+service, AI_PASSIVE */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_socktype = SOCK_STREAM;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-				/* AI_PASSIVE is ignored if hostname is
-				 * non-null; address must be set
-				 */
-				err |= psin->sin_addr.s_addr == 0;
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 host+service, PASSIVE"
-				 ": fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 host+service PASSIVE");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv4 host+"
-			 "service, PASSIVE (\"%s\", \"%s\") returns %ld (\"%s\")",
-			 hostname, service, TEST_RETURN,
-			 gai_strerror(TEST_RETURN));
-		return;
-	}
+static void check_addrinfo_name(const char *name)
+{
+	struct addrinfo *p, *res;
+	struct addrinfo hints;
 
-	/* test 5, IPv4 host+service w/ AI_NUMERICHOST */
-	memset(&hints, 0, sizeof(hints));
-	strcpy(service, "echo");
-	servnum = 7;
-	hints.ai_family = AF_INET;
-	hints.ai_flags = AI_NUMERICHOST;
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (TEST_RETURN != EAI_NONAME) {
-		tst_resm(TFAIL, "getaddrinfo IPv4 AI_NUMERICHOST w/ hostname: "
-			 "returns %ld expected %d (EAI_NONAME)",
-			 TEST_RETURN, EAI_NONAME);
-		if (!TEST_RETURN)
-			freeaddrinfo(aires);
-		return;
-	}
-	tst_resm(TPASS, "getaddrinfo IPv4 AI_NUMERICHOST w/ hostname");
-	if (!TEST_RETURN)
-		freeaddrinfo(aires);
+	print_test_family(name);
 
-	/* test 6, IPv4 0+service, AI_PASSIVE */
 	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_socktype = SOCK_STREAM;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-
-				/* AI_PASSIVE means addr must be INADDR_ANY */
-				err |= psin->sin_addr.s_addr != 0;
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 0+service, PASSIVE:"
-				 " fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 0+service, PASSIVE");
-		freeaddrinfo(aires);
-	} else {
-		if (TEST_RETURN == EAI_BADFLAGS) {
-			tst_resm(TPASS, "getaddrinfo IPv4 0+service,"
-				" PASSIVE (\"\", \"%s\") returns %ld (\"%s\")",
-				service, TEST_RETURN,
-				gai_strerror(TEST_RETURN));
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv4 0+service,"
-				" PASSIVE (\"\", \"%s\") returns %ld (\"%s\")",
-				service, TEST_RETURN,
-				gai_strerror(TEST_RETURN));
-			return;
-		}
-	}
+	hints.ai_family = family;
+	hints.ai_flags = AI_CANONNAME;
 
-	/* test 7, IPv4 0+service */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_socktype = SOCK_STREAM;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-				/* hostname not set; addr should be loopback */
-				err |= psin->sin_addr.s_addr !=
-				    htonl(INADDR_LOOPBACK);
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 0+service: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 0+service");
-		freeaddrinfo(aires);
-	} else {
-		if (TEST_RETURN == EAI_BADFLAGS) {
-			tst_resm(TPASS, "getaddrinfo IPv4 "
-				"0+service (\"\", \"%s\") returns %ld (\"%s\")",
-				service, TEST_RETURN,
-				gai_strerror(TEST_RETURN));
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv4 "
-				"0+service (\"\", \"%s\") returns %ld (\"%s\")",
-				service, TEST_RETURN,
-				gai_strerror(TEST_RETURN));
-			return;
-		}
-	}
+	SAFE_GETADDRINFO(shortname, 0, &hints, &res);
 
-	/* test 8, IPv4 host+service, AI_NUMERICSERV */
-#ifndef AI_NUMERICSERV
-	tst_resm(TCONF, "getaddrinfo IPv4 host+service, AI_NUMERICSERV: flag "
-		 "not implemented");
-#else
-	memset(&hints, 0, sizeof(hints));
-	strcpy(service, "echo");
-	servnum = 7;
-	hints.ai_family = AF_INET;
-	hints.ai_flags = AI_NUMERICSERV;
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (TEST_RETURN != EAI_NONAME) {
-		tst_resm(TFAIL,
-			 "getaddrinfo IPv4 host+service, AI_NUMERICSERV: "
-			 "returns %ld (\"%s\") expected %d (EAI_NONAME)",
-			 TEST_RETURN, gai_strerror(TEST_RETURN), EAI_NONAME);
-		if (!TEST_RETURN)
-			freeaddrinfo(aires);
-		return;
+	for (p = res; p; p = p->ai_next) {
+		if (p->ai_canonname)
+			break;
 	}
-	tst_resm(TPASS, "getaddrinfo IPv4 host+service, AI_NUMERICSERV");
-	if (!TEST_RETURN)
-		freeaddrinfo(aires);
-#endif /* AI_NUMERICSERV */
+	if (!p)
+		tst_brk(TFAIL, "%s: no entries with canonical name set", name);
+	else if (strcasecmp(hostname, p->ai_canonname))
+		tst_brk(TFAIL, "%s: ai_canonname '%s' doesn't match hostname '%s'",
+			name, p->ai_canonname, hostname);
+
+	tst_res(TPASS, "%s: ai_canonname '%s'", name, p->ai_canonname);
+	freeaddrinfo(res);
+}
 
-	/* test 9, IPv4 SOCK_STREAM/IPPROTO_UDP hints */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_protocol = IPPROTO_UDP;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		tst_resm(TFAIL, "getaddrinfo IPv4 SOCK_STREAM/IPPROTO_UDP "
-			 "hints");
-		freeaddrinfo(aires);
-		return;
-	}
-	tst_resm(TPASS, "getaddrinfo IPv4 SOCK_STREAM/IPPROTO_UDP hints");
+static int test_loopback(struct addrinfo *p)
+{
+	/* hostname not set; addr should be loopback */
+	if (family == AF_INET) {
+		struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr;
 
-	/* test 10, IPv4 socktype 0, 513 */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_socktype = 0;
-	strcpy(service, "513");
-	servnum = htons(513);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int got_tcp, got_udp;
-		int err = 0;
-
-		got_tcp = got_udp = 0;
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			got_tcp |= pai->ai_socktype == SOCK_STREAM;
-			got_udp |= pai->ai_socktype == SOCK_DGRAM;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-				/* hostname not set; addr should be loopback */
-				err |= psin->sin_addr.s_addr !=
-				    htonl(INADDR_LOOPBACK);
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 socktype 0,513: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		} else if (got_tcp && got_udp) {
-			tst_resm(TPASS, "getaddrinfo IPv4 socktype 0,513");
-			freeaddrinfo(aires);
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv4 socktype 0,513 TCP %d"
-				 " UDP %d", got_tcp, got_udp);
-			freeaddrinfo(aires);
-			return;
-		}
+		return psin->sin_addr.s_addr != htonl(INADDR_LOOPBACK);
 	} else {
-		if (TEST_RETURN == EAI_BADFLAGS) {
-			tst_resm(TPASS, "getaddrinfo IPv4 socktype 0,513"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv4 socktype 0,513"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-			return;
-		}
-	}
+		struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr;
 
-	/* test 11, IPv4 AI_V4MAPPED */
-	/* AI_V4MAPPED should be ignored because family != AF_INET6 */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_flags = AI_V4MAPPED;
-	TEST(getaddrinfo(hostname, 0, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in *psin = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in);
-			err |= pai->ai_addr == 0;
-			psin = (struct sockaddr_in *)pai->ai_addr;
-			if (pai->ai_addr) {
-				err |= psin->sin_family != AF_INET;
-				err |= psin->sin_port != 0;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv4 AI_V4MAPPED: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin,
-				 psin ? psin->sin_family : 0,
-				 psin ? psin->sin_port : 0,
-				 psin ? htons(psin->sin_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv4 AI_V4MAPPED");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv4 "
-			 "AI_V4MAPPED (\"%s\") returns %ld (\"%s\")", hostname,
-			 TEST_RETURN, gai_strerror(TEST_RETURN));
-		return;
+		return memcmp(&psin6->sin6_addr, &in6addr_loopback,
+		       sizeof(struct in6_addr)) != 0;
 	}
 }
 
-/* getaddrinfo tests (v6) */
-static void gaiv6(void)
+static int test_passive(struct addrinfo *p)
 {
-	struct addrinfo *aires, hints, *pai;
-	char hostname[MAXHOSTNAMELEN + 1];
-	char shortname[MAXHOSTNAMELEN + 1];
-	char service[NI_MAXSERV + 1];
-	int servnum;
-	char *p;
-
-	if (gethostname(hostname, sizeof(hostname)) < 0)
-		tst_brkm(TBROK, NULL, "gethostname failed - %s",
-			 strerror(errno));
-	strncpy(shortname, hostname, MAXHOSTNAMELEN);
-	shortname[MAXHOSTNAMELEN] = '\0';
-	p = strchr(shortname, '.');
-	if (p)
-		*p = '\0';
-
-	/* test 12, IPv6 basic lookup */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	TEST(getaddrinfo(hostname, 0, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != 0;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 basic lookup: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 basic lookup");
-		freeaddrinfo(aires);
+	if (family == AF_INET) {
+		struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr;
+
+		return psin->sin_addr.s_addr == 0;
 	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv6 basic "
-			 "lookup (\"%s\") returns %ld (\"%s\")", hostname,
-			 TEST_RETURN, gai_strerror(TEST_RETURN));
-		return;
+		struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr;
+
+		return memcmp(&psin6->sin6_addr, &in6addr_any,
+			      sizeof(struct in6_addr)) == 0;
 	}
+}
 
-	/* test 13, IPv6 canonical name */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_flags = AI_CANONNAME;
-	TEST(getaddrinfo(shortname, 0, &hints, &aires));
-	if (!TEST_RETURN) {
-		for (pai = aires; pai; pai = pai->ai_next)
-			if (pai->ai_canonname)
-				break;
-		if (!pai) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 canonical name: no "
-				 "entries with canonical name set");
-			freeaddrinfo(aires);
-			return;
-		} else if (strcasecmp(hostname, pai->ai_canonname)) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 canonical name "
-				 "(\"%s\") doesn't match hostname (\"%s\")",
-				 pai->ai_canonname, hostname);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 canonical name");
-		freeaddrinfo(aires);
+static int test_passive_no_host(struct addrinfo *p)
+{
+	if (family == AF_INET) {
+		struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr;
+
+		return psin->sin_addr.s_addr != 0;
 	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv6 "
-			 "canonical name (\"%s\") returns %ld (\"%s\")",
-			 shortname, TEST_RETURN, gai_strerror(TEST_RETURN));
-		return;
+		struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr;
+
+		return memcmp(&psin6->sin6_addr, &in6addr_any,
+			      sizeof(struct in6_addr));
 	}
+}
+
+static void gaiv(void)
+{
+	check_addrinfo(1, "basic lookup", hostname, 0, NULL, 0, 0, 0, NULL);
+	check_addrinfo_name("canonical name");
 
-	/* test 14, IPv6 host+service name */
-	memset(&hints, 0, sizeof(hints));
 	/*
 	 * These are hard-coded for echo/7 to avoid using getservbyname(),
 	 * since it isn't thread-safe and these tests may be re-used
 	 * multithreaded. Sigh.
 	 */
-	strcpy(service, "echo");
-	servnum = 7;
-	hints.ai_family = AF_INET6;
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != htons(servnum);
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 host+service: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 host+service");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv6 host+"
-			 "service returns %ld (\"%s\")", TEST_RETURN,
-			 gai_strerror(TEST_RETURN));
-		return;
+	check_addrinfo(1, "host+service", hostname, 7, "echo", 0, 0, 0, NULL);
+
+	check_addrinfo(1, "host+service, AI_PASSIVE", hostname, 9462, "9462",
+		       AI_PASSIVE, SOCK_STREAM, 0, test_passive);
+
+	check_addrinfo(0, "host+service, AI_NUMERICHOST", hostname, 7, "echo",
+		       AI_NUMERICHOST, SOCK_STREAM, 0, NULL);
+	if (TST_RET != EAI_NONAME)
+		tst_brk(TFAIL, "AI_NUMERICHOST: ret %ld exp %d (EAI_NONAME)",
+			TST_RET, EAI_NONAME);
+	tst_res(TPASS, "AI_NUMERICHOST: exp %ld (EAI_NONAME)", TST_RET);
+
+	check_addrinfo(1, "0+service, AI_PASSIVE", NULL, 9462, "9462",
+		       AI_PASSIVE, SOCK_STREAM, 0, test_passive_no_host);
+
+	check_addrinfo(0, "0+service", NULL, 9462, "9462",
+		       0, SOCK_STREAM, 0, test_loopback);
+	if (TST_RET == EAI_BADFLAGS) {
+		tst_res(TPASS, "0+service ('', '9462') returns %ld '%s'",
+			TST_RET, gai_strerror(TST_RET));
+	} else if (TST_RET) {
+		tst_brk(TFAIL, "0+service ('', '9462') returns %ld '%s'",
+			TST_RET, gai_strerror(TST_RET));
 	}
 
-	/* test 15, IPv6 hostname+service, AI_PASSIVE */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_socktype = SOCK_STREAM;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-				/* AI_PASSIVE is ignored if hostname is
-				 * non-null; address must be set
-				 */
-				err |= memcmp(&psin6->sin6_addr, &in6addr_any,
-					      sizeof(struct in6_addr)) == 0;
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 host+service, PASSIVE"
-				 ": fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 host+service PASSIVE");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv6 host+"
-			 "service, PASSIVE (\"%s\", \"%s\") returns %ld (\"%s\")",
-			 hostname, service, TEST_RETURN,
-			 gai_strerror(TEST_RETURN));
-		return;
-	}
+#ifdef AI_NUMERICSERV
+	check_addrinfo(0, "host+service, AI_NUMERICSERV", hostname, 7, "echo",
+		       AI_NUMERICSERV, 0, 0, NULL);
+	if (TST_RET != EAI_NONAME)
+		tst_brk(TFAIL, "AI_NUMERICSERV: returns %ld '%s', expected %d (EAI_NONAME)",
+			TST_RET, gai_strerror(TST_RET), EAI_NONAME);
+	tst_res(TPASS, "AI_NUMERICSERV: returns %ld (EAI_NONAME)", TST_RET);
+#else
+	tst_res(TCONF, "AI_NUMERICSERV: flag not implemented");
+#endif
 
-	/* test 16, IPv6 host+service w/ AI_NUMERICHOST */
-	memset(&hints, 0, sizeof(hints));
-	strcpy(service, "echo");
-	servnum = 7;
-	hints.ai_family = AF_INET6;
-	hints.ai_flags = AI_NUMERICHOST;
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (TEST_RETURN != EAI_NONAME) {
-		tst_resm(TFAIL, "getaddrinfo IPv6 AI_NUMERICHOST w/ hostname: "
-			 "returns %ld expected %d (EAI_NONAME)",
-			 TEST_RETURN, EAI_NONAME);
-		if (!TEST_RETURN)
-			freeaddrinfo(aires);
-		return;
+	check_addrinfo(0, "SOCK_STREAM/IPPROTO_UDP", NULL, 0, NULL, 0,
+		       SOCK_STREAM, IPPROTO_UDP, NULL);
+	if (!TST_RET)
+		tst_brk(TFAIL, "SOCK_STREAM/IPPROTO_UDP: unexpected pass");
+	tst_res(TPASS, "SOCK_STREAM/IPPROTO_UDP: failed as expected");
+
+	check_addrinfo(0, "socktype 0, 513", NULL, 513, "513", 0, 0, 0, NULL);
+	if (TST_RET == EAI_BADFLAGS) {
+		tst_res(TPASS, "socktype 0,513 returns %ld '%s'",
+			TST_RET, gai_strerror(TST_RET));
+	} else if (TST_RET) {
+		tst_brk(TFAIL, "socktype 0,513 returns %ld '%s'",
+			TST_RET, gai_strerror(TST_RET));
 	}
-	tst_resm(TPASS, "getaddrinfo IPv6 AI_NUMERICHOST w/ hostname");
-	if (!TEST_RETURN)
-		freeaddrinfo(aires);
 
-	/* test 17, IPv6 0+service, AI_PASSIVE */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_socktype = SOCK_STREAM;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-
-				/* AI_PASSIVE means addr must be INADDR_ANY */
-				err |= memcmp(&psin6->sin6_addr, &in6addr_any,
-					      sizeof(struct in6_addr)) != 0;
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 0+service, PASSIVE:"
-				 " fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 0+service, PASSIVE");
-		freeaddrinfo(aires);
-	} else {
-		if (TEST_RETURN == EAI_BADFLAGS) {
-			tst_resm(TPASS, "getaddrinfo IPv6 0+service, PASSIVE"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv6 0+service, PASSIVE"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-			return;
-		}
-	}
+	check_addrinfo(1, "AI_V4MAPPED", NULL, 513, "513",
+		       AI_V4MAPPED, 0, 0, NULL);
+}
 
-	/* test 18, IPv6 0+service */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_socktype = SOCK_STREAM;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-				/* hostname not set; addr should be loopback */
-				err |= memcmp(&psin6->sin6_addr,
-					      &in6addr_loopback,
-					      sizeof(struct in6_addr)) != 0;
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 0+service: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 0+service");
-		freeaddrinfo(aires);
-	} else {
-		if (TEST_RETURN == EAI_BADFLAGS) {
-			tst_resm(TPASS, "getaddrinfo IPv6 0+service"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv6 0+service"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-			return;
-		}
-	}
+static struct tcase {
+	sa_family_t family;
+	const char *const addr;
+	const char *const name;
+	const char *const alias;
+} tcases[] = {
+	{ AF_INET, "127.0.127.1", "getaddrinfo01.ltp", "getaddrinfo01-ipv4" },
+	{ AF_INET6, "::127", "getaddrinfo01.ipv6.ltp", "getaddrinfo01-ipv6" }
+};
 
-	/* test 19, IPv6 host+service, AI_NUMERICSERV */
-#ifndef AI_NUMERICSERV
-	tst_resm(TCONF, "getaddrinfo IPv6 host+service, AI_NUMERICSERV: flag "
-		 "not implemented");
-#else
-	memset(&hints, 0, sizeof(hints));
-	strcpy(service, "echo");
-	servnum = 7;
-	hints.ai_family = AF_INET6;
-	hints.ai_flags = AI_NUMERICSERV;
-	TEST(getaddrinfo(hostname, service, &hints, &aires));
-	if (TEST_RETURN != EAI_NONAME) {
-		tst_resm(TFAIL,
-			 "getaddrinfo IPv6 host+service, AI_NUMERICSERV: "
-			 "returns %ld (\"%s\") expected %d (EAI_NONAME)",
-			 TEST_RETURN, gai_strerror(TEST_RETURN), EAI_NONAME);
-		if (!TEST_RETURN)
-			freeaddrinfo(aires);
-		return;
-	}
-	tst_resm(TPASS, "getaddrinfo IPv6 host+service, AI_NUMERICSERV");
-	if (!TEST_RETURN)
-		freeaddrinfo(aires);
-#endif /* AI_NUMERICSERV */
+static void setup(void)
+{
+	unsigned int i;
+	int fd;
 
-	/* test 20, IPv6 SOCK_STREAM/IPPROTO_UDP hints */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_protocol = IPPROTO_UDP;
-	strcpy(service, "9462");
-	servnum = htons(9462);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		tst_resm(TFAIL, "getaddrinfo IPv6 SOCK_STREAM/IPPROTO_UDP "
-			 "hints");
-		freeaddrinfo(aires);
-		return;
-	}
-	tst_resm(TPASS, "getaddrinfo IPv6 SOCK_STREAM/IPPROTO_UDP hints");
+	SAFE_CP(host_file, "hosts");
+	fd = SAFE_OPEN(host_file, O_WRONLY|O_APPEND);
 
-	/* test 21, IPv6 socktype 0, 513 */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_socktype = 0;
-	strcpy(service, "513");
-	servnum = htons(513);
-	TEST(getaddrinfo(0, service, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int got_tcp, got_udp;
-		int err = 0;
-
-		got_tcp = got_udp = 0;
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			got_tcp |= pai->ai_socktype == SOCK_STREAM;
-			got_udp |= pai->ai_socktype == SOCK_DGRAM;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-				/* hostname not set; addr should be loopback */
-				err |= memcmp(&psin6->sin6_addr,
-					      &in6addr_loopback,
-					      sizeof(struct in6_addr)) != 0;
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != servnum;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 socktype 0,513: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		} else if (got_tcp && got_udp) {
-			tst_resm(TPASS, "getaddrinfo IPv6 socktype 0,513");
-			freeaddrinfo(aires);
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv6 socktype 0,513 TCP %d"
-				 " UDP %d", got_tcp, got_udp);
-			freeaddrinfo(aires);
-			return;
-		}
-	} else {
-		if (TEST_RETURN == EAI_BADFLAGS) {
-			tst_resm(TPASS, "getaddrinfo IPv6 socktype 0,513"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-		} else {
-			tst_resm(TFAIL, "getaddrinfo IPv6 socktype 0,513"
-				" (\"\", \"%s\") returns %ld (\"%s\")", service,
-				TEST_RETURN, gai_strerror(TEST_RETURN));
-			return;
-		}
-	}
+	for (i = 0; i < ARRAY_SIZE(tcases); ++i) {
+		char *entry;
 
-	/* test 22, IPv6 AI_V4MAPPED */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET6;
-	hints.ai_flags = AI_V4MAPPED;
-	TEST(getaddrinfo(hostname, 0, &hints, &aires));
-	if (!TEST_RETURN) {
-		struct sockaddr_in6 *psin6 = 0;
-		int err = 0;
-
-		for (pai = aires; pai; pai = pai->ai_next) {
-			err |= pai->ai_family != AF_INET6;
-			err |= pai->ai_addrlen != sizeof(struct sockaddr_in6);
-			err |= pai->ai_addr == 0;
-			psin6 = (struct sockaddr_in6 *)pai->ai_addr;
-			if (pai->ai_addr) {
-				err |= psin6->sin6_family != AF_INET6;
-				err |= psin6->sin6_port != 0;
-			}
-			if (err)
-				break;
-		}
-		if (err) {
-			tst_resm(TFAIL, "getaddrinfo IPv6 AI_V4MAPPED: "
-				 "fam %d alen %d addr 0x%p addr/fam %d "
-				 "addr/port %d H[%d]",
-				 pai->ai_family, pai->ai_addrlen, psin6,
-				 psin6 ? psin6->sin6_family : 0,
-				 psin6 ? psin6->sin6_port : 0,
-				 psin6 ? htons(psin6->sin6_port) : 0);
-			freeaddrinfo(aires);
-			return;
-		}
-		tst_resm(TPASS, "getaddrinfo IPv6 AI_V4MAPPED");
-		freeaddrinfo(aires);
-	} else {
-		tst_resm(TFAIL, "getaddrinfo IPv6 "
-			 "AI_V4MAPPED (\"%s\") returns %ld (\"%s\")", hostname,
-			 TEST_RETURN, gai_strerror(TEST_RETURN));
-		return;
+		SAFE_ASPRINTF(&entry, "%s %s %s\n",
+			      tcases[i].addr, tcases[i].name, tcases[i].alias);
+		SAFE_WRITE(0, fd, entry, strlen(entry));
+		free(entry);
 	}
+	SAFE_CLOSE(fd);
+}
+
+static void cleanup(void)
+{
+	SAFE_CP("hosts", host_file);
 }
+
+static void do_test(unsigned int i)
+{
+	family = tcases[i].family;
+	hostname = tcases[i].name;
+	shortname = tcases[i].alias;
+	gaiv();
+}
+
+static struct tst_test test = {
+	.needs_root = 1,
+	.needs_tmpdir = 1,
+	.setup = setup,
+	.cleanup = cleanup,
+	.tcnt = ARRAY_SIZE(tcases),
+	.test = do_test,
+};