#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(char *, char *, struct addrinfo *, int); static const char *str_fam(int); static const char *str_sock(int); static void usage(const char *); /* globals */ int vflag; int main(int argc, char **argv) { int doerrtest = 0; int loopcount = 1; int c, i; char *host = NULL; char hostbuf[NI_MAXHOST]; char *serv = NULL; char servbuf[NI_MAXSERV]; struct protoent *proto; struct addrinfo hints; /* set by command-line options */ 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(hostbuf, optarg, NI_MAXHOST-1); host = hostbuf; break; case 'l': /* loop count */ loopcount = atoi(optarg); break; case 'p': hints.ai_flags |= AI_PASSIVE; break; case 'r': /* protocol */ if ((proto = getprotobyname(optarg)) == NULL) { hints.ai_protocol = atoi(optarg); } else { hints.ai_protocol = proto->p_proto; } break; case 's': strncpy(servbuf, optarg, NI_MAXSERV-1); serv = servbuf; 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; } if (strcmp(optarg, "raw") == 0) { hints.ai_socktype = SOCK_RAW; break; } #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 -t option"); case 'v': vflag = 1; break; case '?': usage("unrecognized option"); } } if (optind < argc) { usage("extra args"); } if (doerrtest) { do_errtest(); exit(0); } for (i = 1; i <= loopcount; i++) { if (do_onetest(host, serv, &hints, 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(char *host, char *serv, struct addrinfo *hints, int iteration) { int rc, fd, verbose; struct addrinfo *res, *rescopy; char rhost[NI_MAXHOST], rserv[NI_MAXSERV]; verbose = vflag && (iteration == 1); /* only first time */ if (host != NULL && verbose) printf("host = %s\n", host); if (serv != NULL && 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 (iteration == 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 (%s)\n", rc, gai_strerror(rc)); } while ( (res = res->ai_next) != NULL); freeaddrinfo(rescopy); return(0); } static void usage(const char *msg) { printf( "usage: testaddrinfo [ options ]\n" "options: -h (can be hostname or address string)\n" " -s (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, X = tcp, udp, ... or number e.g. 6, 17, ...\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 and must be specified.\n" ); if (msg[0] != 0) printf("%s\n", msg); exit(1); } static const 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(""); } static const char * str_sock(int socktype) { switch(socktype) { case SOCK_STREAM: return "SOCK_STREAM"; case SOCK_DGRAM: return "SOCK_DGRAM"; case SOCK_RAW: return "SOCK_RAW"; #ifdef SOCK_RDM case SOCK_RDM: return "SOCK_RDM"; #endif #ifdef SOCK_SEQPACKET case SOCK_SEQPACKET:return "SOCK_SEQPACKET"; #endif default: return ""; } }