--- /dev/null
+#define _GNU_SOURCE
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include <netdb.h>
+
+struct nameval { const char *name; int val; };
+const char *prog = "<unset>";
+
+static int parseint(const char *s, const char *what)
+{
+ char *q;
+ unsigned long i;
+ int e = errno;
+
+ if (!isdigit((unsigned char)*s)) goto fail;
+ errno = 0;
+ i = strtoul(s, &q, 0);
+ if (errno || *q || i > INT_MAX) goto fail;
+ errno = e;
+ return ((int)i);
+
+fail:
+ fprintf(stderr, "%s: bad numeric %s `%s'\n", prog, what, s);
+ exit(1);
+}
+
+static int lookup(const char *name, const char *what,
+ const struct nameval *nv)
+{
+ if (isdigit((unsigned char)*name))
+ return (parseint(name, what));
+ for (; nv->name; nv++)
+ if (strcmp(nv->name, name) == 0) return nv->val;
+ fprintf(stderr, "%s: unknown %s `%s'\n", prog, what, name);
+ exit(1);
+}
+
+static const char *rlookup(int val, const char *what,
+ const struct nameval *nv)
+{
+ static char buf[32];
+ for (; nv->name; nv++)
+ if (nv->val == val) return (nv->name);
+ sprintf(buf, "%s#%d", what, val);
+ return (buf);
+}
+
+static const struct nameval familymap[] = {
+ { "unspec", AF_UNSPEC },
+ { "inet", AF_INET },
+ { "inet6", AF_INET6 },
+ { 0 }
+};
+
+static const struct nameval typemap[] = {
+ { "any", 0 },
+ { "stream", SOCK_STREAM },
+ { "dgram", SOCK_DGRAM },
+ { "raw", SOCK_RAW },
+ { 0 }
+};
+
+static void usage(FILE *fp)
+{
+ fprintf(fp,
+ "Usage: %s "
+ "[-CNacgimnsuv] [-f FAMILY] [-p PROTOCOL] [-t TYPE] "
+ "NAME [SERVICE]\n",
+ prog);
+}
+
+static void help(FILE *fp)
+{
+ usage(fp); fputs("\n\
+Options:\n\
+\n\
+ -a plain v4 also (with -m) -n numeric host\n\
+ -c canonify name -N numeric service\n\
+ -C canonify to IDN (with -c) -s allow unassigned codepoints\n\
+ -g families with configured addrs -u check result is STD3 hostname\n\
+ -i convert to IDN -v passive\n\
+ -m v6-mapped v4 addresses\n\
+\n\
+ -f unspec|inet|inet6 NAME -- hostname or `-' for none\n\
+ -p PROTOCOL -- /etc/protocols SERVICE -- number or /etc/services\n\
+ -t any|stream|dgram|raw\n\
+", fp);
+}
+
+int main(int argc, char *argv[])
+{
+ struct addrinfo *ai, hint = { .ai_family = AF_UNSPEC };
+ const char *name;
+ const char *serv = 0;
+ char namebuf[NI_MAXHOST], servbuf[NI_MAXSERV];
+ int err;
+
+ prog = strrchr(argv[0], '/');
+ if (prog) prog++;
+ else prog = argv[0];
+
+ for (;;) {
+ int opt = getopt(argc, argv, "hf:t:p:nNcvgamiCus");
+ if (opt < 0) break;
+ switch (opt) {
+ case 'h': help(stdout); exit(0);
+ case 'f':
+ hint.ai_family = lookup(optarg, "family", familymap);
+ break;
+ case 't':
+ hint.ai_socktype = lookup(optarg, "type", typemap);
+ break;
+ case 'p':
+ if (isdigit((unsigned char)*optarg))
+ hint.ai_protocol = parseint(optarg, "protocol");
+ else {
+ struct protoent *p = getprotobyname(optarg);
+ if (!p) {
+ fprintf(stderr, "%s: unknown protocol `%s'\n", prog, optarg);
+ exit(1);
+ }
+ hint.ai_protocol = p->p_proto;
+ }
+ break;
+ case 'C': hint.ai_flags |= AI_CANONIDN; break;
+ case 'N': hint.ai_flags |= AI_NUMERICSERV; break;
+ case 'a': hint.ai_flags |= AI_ALL; break;
+ case 'c': hint.ai_flags |= AI_CANONNAME; break;
+ case 'g': hint.ai_flags |= AI_ADDRCONFIG; break;
+ case 'i': hint.ai_flags |= AI_IDN; break;
+ case 'm': hint.ai_flags |= AI_V4MAPPED; break;
+ case 'n': hint.ai_flags |= AI_NUMERICHOST; break;
+ case 's': hint.ai_flags |= AI_IDN_USE_STD3_ASCII_RULES; break;
+ case 'u': hint.ai_flags |= AI_IDN_ALLOW_UNASSIGNED; break;
+ case 'v': hint.ai_flags |= AI_PASSIVE; break;
+ default: usage(stderr); exit(1);
+ }
+ }
+
+ argv += optind;
+ if (!*argv) { usage(stderr); exit(1); } name = *argv++;
+ if (*argv) { serv = *argv++; }
+ if (*argv) { usage(stderr); exit(1); }
+
+ if (strcmp(name, "-") == 0) name = 0;
+
+ if ((err = getaddrinfo(name, serv, &hint, &ai)) != 0) {
+ fprintf(stderr, "%s: %s\n", prog, gai_strerror(err));
+ exit(1);
+ }
+
+ for (; ai; ai = ai->ai_next) {
+ if (ai->ai_canonname) printf("[%s]\n", ai->ai_canonname);
+ fputs(rlookup(ai->ai_family, "family", familymap), stdout);
+ fputc(' ', stdout);
+ fputs(rlookup(ai->ai_socktype, "type", typemap), stdout);
+ fputc(' ', stdout);
+ if ((err = getnameinfo(ai->ai_addr, ai->ai_addrlen,
+ namebuf, sizeof(namebuf),
+ servbuf, sizeof(servbuf),
+ (NI_NUMERICHOST | NI_NUMERICSERV |
+ (ai->ai_socktype == SOCK_DGRAM ?
+ NI_DGRAM : 0)))) != 0)
+ printf("(error: %s)", gai_strerror(err));
+ else
+ printf("%s %s", namebuf, servbuf);
+ fputc('\n', stdout);
+ }
+
+ freeaddrinfo(ai);
+
+ return (0);
+}