X-Git-Url: https://git.distorted.org.uk/~mdw/misc/blobdiff_plain/99acf5cd9f964d9d42eebed4a27215cda3c57178..d3fafed3ea3c8937802eaae33ae3f5b8f2b45a62:/gai.c diff --git a/gai.c b/gai.c new file mode 100644 index 0000000..5891c55 --- /dev/null +++ b/gai.c @@ -0,0 +1,181 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct nameval { const char *name; int val; }; +const char *prog = ""; + +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); +}