#include "unp.h"
/*
* Test program for getaddrinfo() and getnameinfo().
*/
/* function prototypes for internal functions */
static void do_errtest(void);
static void do_funccall(const char *, const char *, int, int, int, int, int);
static int do_onetest(int);
static char *str_fam(int);
static char *str_sock(int);
static void usage(const char *);
/* globals */
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
int doerrtest;
int loopcount = 1;
int vflag;
struct addrinfo hints; /* set by command-line options */
int
main(int argc, char **argv)
{
int c, i;
if (argc < 2)
usage("");
memset(&hints, 0, sizeof(struct addrinfo));
opterr = 0; /* don't want getopt() writing to stderr */
while ( (c = getopt(argc, argv, "cef:h:l:pr:s:t:v")) != -1) {
switch (c) {
case 'c':
hints.ai_flags |= AI_CANONNAME;
break;
case 'e':
doerrtest = 1;
break;
case 'f': /* address family */
#ifdef IPv4
if (strcmp(optarg, "inet") == 0) {
hints.ai_family = AF_INET;
break;
}
#endif
#ifdef IPv6
if (strcmp(optarg, "inet6") == 0) {
hints.ai_family = AF_INET6;
break;
}
#endif
#ifdef UNIXdomain
if (strcmp(optarg, "unix") == 0) {
hints.ai_family = AF_LOCAL;
break;
}
#endif
usage("invalid -f option");
case 'h': /* host */
strncpy(host, optarg, NI_MAXHOST-1);
break;
case 'l': /* loop count */
loopcount = atoi(optarg);
break;
case 'p':
hints.ai_flags |= AI_PASSIVE;
break;
case 'r': /* protocol */
usage("-r option not yet implemented");
case 's':
strncpy(serv, optarg, NI_MAXSERV-1);
break;
case 't': /* socket type */
if (strcmp(optarg, "stream") == 0) {
hints.ai_socktype = SOCK_STREAM;
break;
}
if (strcmp(optarg, "dgram") == 0) {
hints.ai_socktype = SOCK_DGRAM;
break;
}
#ifdef SOCK_RAW
if (strcmp(optarg, "raw") == 0) {
hints.ai_socktype = SOCK_RAW;
break;
}
#endif
#ifdef SOCK_RDM
if (strcmp(optarg, "rdm") == 0) {
hints.ai_socktype = SOCK_RDM;
break;
}
#endif
#ifdef SOCK_SEQPACKET
if (strcmp(optarg, "seqpacket") == 0) {
hints.ai_socktype = SOCK_SEQPACKET;
break;
}
#endif
usage("invalid -f option");
case 'v':
vflag = 1;
break;
case '?':
usage("unrecognized option");
}
}
if (doerrtest) {
do_errtest();
exit(0);
}
for (i = 1; i <= loopcount; i++) {
if (do_onetest(i) > 0)
exit(1);
if (i % 1000 == 0) {
printf(" %d", i);
fflush(stdout);
}
}
exit(0);
}
/*
* Check that the right error codes are returned for invalid input.
* Test all the errors that are easy to test for.
*/
static void
do_errtest(void)
{
/* passive open with no hostname and no address family */
do_funccall(NULL, "ftp", AI_PASSIVE, 0, 0, 0, 0);
/* kind of hard to automatically test EAI_AGAIN ??? */
/* invalid flags */
do_funccall("localhost", NULL, 999999, 0, 0, 0, EAI_BADFLAGS);
/* how to test EAI_FAIL ??? */
/* invalid address family */
do_funccall("localhost", NULL, 0, AF_SNA, 0, 0, EAI_FAMILY);
/* hard to test for EAI_MEMORY: would have to malloc() until
failure, then give some back, then call getaddrinfo and
hope that its memory requests would not be satisfied. */
/* to test for EAI_NODATA: would have to know of a host with
no A record in the DNS */
#ifdef notdef /* following depends on resolver, sigh */
/* believe it or not, there is a registered domain "bar.com",
so the following should generate NO_DATA from the DNS */
do_funccall("foo.bar.foo.bar.foo.bar.com", NULL, 0, 0, 0, 0, EAI_NODATA);
#endif
/* no hostname, no service name */
do_funccall(NULL, NULL, 0, 0, 0, 0, EAI_NONAME);
/* invalid hostname (should be interpreted in local default domain) */
do_funccall("lkjjkhjhghgfgfd", NULL, 0, 0, 0, 0, EAI_NONAME);
/* invalid service name */
do_funccall(NULL, "nosuchservice", 0, 0, 0, 0, EAI_NONAME);
/* service valid but not supported for socket type */
do_funccall("localhost", "telnet", 0, 0, SOCK_DGRAM, 0, EAI_SERVICE);
/* service valid but not supported for socket type */
do_funccall("localhost", "tftp", 0, 0, SOCK_STREAM, 0, EAI_SERVICE);
/* invalid socket type */
do_funccall("localhost", NULL, 0, AF_INET, SOCK_SEQPACKET, 0, EAI_SOCKTYPE);
/* EAI_SYSTEM not generated by my implementation */
}
static void
do_funccall(const char *host, const char *serv,
int flags, int family, int socktype, int protocol, int exprc)
{
int rc;
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = flags;
hints.ai_family = family;
hints.ai_socktype = socktype;
hints.ai_protocol = protocol;
rc = getaddrinfo(host, serv, &hints, &res);
if (rc != exprc) {
printf("expected return = %d (%s),\nactual return = %d (%s)\n",
exprc, gai_strerror(exprc), rc, gai_strerror(rc));
if (host != NULL)
printf(" host = %s\n", host);
if (serv != NULL)
printf(" serv = %s\n", serv);
printf(" flags = %d, family = %s, socktype = %s, protocol = %d\n",
flags, str_fam(family), str_sock(socktype), protocol);
exit(2);
}
}
static int
do_onetest(int loopcount)
{
int rc, fd, verbose;
struct addrinfo *res, *rescopy;
char rhost[NI_MAXHOST], rserv[NI_MAXSERV];
verbose = vflag && (loopcount == 1); /* only first time */
if (host[0] != 0 && verbose)
printf("host = %s\n", host);
if (serv[0] != 0 && verbose)
printf("serv = %s\n", serv);
rc = getaddrinfo(host, serv, &hints, &res);
if (rc != 0) {
printf("getaddrinfo return code = %d (%s)\n", rc, gai_strerror(rc));
return(1);
}
rescopy = res;
do {
if (loopcount == 1) { /* always print results first time */
printf("\nsocket(%s, %s, %d)", str_fam(res->ai_family),
str_sock(res->ai_socktype), res->ai_protocol);
/* canonname should be set only in first addrinfo{} */
if (hints.ai_flags & AI_CANONNAME) {
if (res->ai_canonname)
printf(", ai_canonname = %s", res->ai_canonname);
}
printf("\n");
printf("\taddress: %s\n",
Sock_ntop(res->ai_addr, res->ai_addrlen));
}
/* Call socket() to make sure return values are valid */
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0)
printf("call to socket() failed!\n");
else
close(fd);
/*
* Call getnameinfo() to check the reverse mapping.
*/
rc = getnameinfo(res->ai_addr, res->ai_addrlen,
rhost, NI_MAXHOST, rserv, NI_MAXSERV,
(res->ai_socktype == SOCK_DGRAM) ? NI_DGRAM: 0);
if (rc == 0) {
if (verbose)
printf("\tgetnameinfo: host = %s, serv = %s\n",
rhost, rserv);
} else
printf("getnameinfo returned %d\n", rc);
} while ( (res = res->ai_next) != NULL);
freeaddrinfo(rescopy);
return(0);
}
static void
usage(const char *msg)
{
printf(
"usage: testaddrinfo [ options ]\n"
"options: -h <host> (can be hostname or address string)\n"
" -s <service> (can be service name or decimal port number)\n"
" -c AI_CANONICAL flag\n"
" -p AI_PASSIVE flag\n"
" -l N loop N times (check for memory leaks with ps)\n"
" -f X address family, X = inet, inet6, unix\n"
" -r X protocol (not yet implemented)\n"
" -t X socket type, X = stream, dgram, raw, rdm, seqpacket\n"
" -v verbose\n"
" -e only do test of error returns (no options required)\n"
" without -e, one or both of <host> and <service> must be specified.\n"
);
if (msg[0] != 0)
printf("%s\n", msg);
exit(1);
}
static char *
str_fam(int family)
{
#ifdef IPv4
if (family == AF_INET)
return("AF_INET");
#endif
#ifdef IPv6
if (family == AF_INET6)
return("AF_INET6");
#endif
#ifdef UNIXdomain
if (family == AF_LOCAL)
return("AF_LOCAL");
#endif
return("<unknown family>");
}
static char *
str_sock(int socktype)
{
if (socktype == SOCK_STREAM)
return("SOCK_STREAM");
if (socktype == SOCK_DGRAM)
return("SOCK_DGRAM");
if (socktype == SOCK_RAW)
return("SOCK_RAW");
return("<unknown socktype>");
}
syntax highlighted by Code2HTML, v. 0.9.1