/* test getaddrinfo(3) */

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <libgen.h>

static void usage(const char *command) {
	fprintf(stderr, "Usage: %s [-npd] <nodename> <servname>\n", command);
	fprintf(stderr, "  Calls getaddrinfo(3) and prints result.\n");
	fprintf(stderr, "  Use -p for AI_PASSIVE and -n for AI_NUMERICHOST.\n");
	fprintf(stderr, "  Use -d for SOCK_DGRAM instead of SOCK_STREAM.\n");
	fprintf(stderr, "  Empty string for <nodename> or <servname> will use NULL for call.\n");
}

int main(int argc, char **argv) {
	struct addrinfo hint;
	struct addrinfo *addrs = NULL, *addr;
	int rc, ip, i, j=0;
	char *nodename, *servname;
	unsigned short *ip6;

	memset(&hint, 0, sizeof(struct addrinfo));
	hint.ai_family = 0;
	hint.ai_flags = 0;
	hint.ai_socktype = SOCK_STREAM;

	opterr = 0;
	while (-1 != (i = getopt(argc, argv, "npd"))) {
		switch (i) {
			case 'n':
				hint.ai_flags |= AI_NUMERICHOST;
				break;
			case 'p':
				hint.ai_flags |= AI_PASSIVE;
				break;
			case 'd':
				hint.ai_socktype = SOCK_DGRAM;
				break;
			default:
				usage(basename(*argv));
				return 1;
		}
	}

	if (2 != (argc - optind)) {
		usage(basename(*argv));
		return 1;
	}
	nodename = argv[optind];
	if ('\0' == *nodename) nodename = NULL;
	servname = argv[optind + 1];
	if ('\0' == *servname) servname = NULL;

	rc = getaddrinfo(nodename, servname, &hint, &addrs);

	if (rc) {
		fprintf(stderr, "getaddrinfo returned %d (%s)\n", rc, gai_strerror(rc));
		return 1;
	}

	for (addr = addrs; addr; addr = addr->ai_next) {
		printf("Result #%d:\n", ++j);
		printf("  ai_flags: %d\n", addr->ai_flags);
		printf("  ai_family: %d\n", addr->ai_family);
		printf("  ai_socktype: %d\n", addr->ai_socktype);
		printf("  ai_protocol: %d\n", addr->ai_protocol);
		printf("  ai_canonname: %s\n", addr->ai_canonname);
		switch (addr->ai_family) {
			case AF_UNIX:
				printf("  UNIX socket\n");
				break;
			case AF_INET:
				printf("  ai_addr->sin_family: %d\n",
					((struct sockaddr_in *)(addr->ai_addr))->sin_family);
				printf("  ai_addr->sin_port: %d\n",
					ntohs(((struct sockaddr_in *)(addr->ai_addr))->sin_port));
				ip = ntohl(((struct sockaddr_in *)(addr->ai_addr))->sin_addr.s_addr);
				printf("  ai_addr->sin_addr.s_addr: %d.%d.%d.%d\n",
					(ip & 0xff000000)>>24, (ip & 0xff0000)>>16, (ip & 0xff00)>>8, ip & 0xff);
				break;
			case AF_INET6:
				printf("  ai_addr->sin6_family: %d\n",
					((struct sockaddr_in6 *)(addr->ai_addr))->sin6_family);
				printf("  ai_addr->sin6_flowinfo: %d\n",
					((struct sockaddr_in6 *)(addr->ai_addr))->sin6_flowinfo);
				printf("  ai_addr->sin6_port: %d\n",
					ntohs(((struct sockaddr_in6 *)(addr->ai_addr))->sin6_port));
				ip6 = ((struct sockaddr_in6 *)(addr->ai_addr))->sin6_addr.s6_addr16;
				printf("  ai_addr->sin6_addr.s6_addr16: ");
				for (i=0; i<8; ++i) {
					if (i)
						printf(":");
					printf("%x", ntohs(ip6[i]));
				}
				printf("\n");
				break;
		}
		printf("\n");
	}

	printf("%d results found.\n", j);
	return 0;
}
