chiark / gitweb /
mtimeout.1: Use correct dash for number ranges.
[misc] / gai.c
1 #define _GNU_SOURCE
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <limits.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <getopt.h>
11
12 #include <netdb.h>
13
14 struct nameval { const char *name; int val; };
15 const char *prog = "<unset>";
16
17 static int parseint(const char *s, const char *what)
18 {
19   char *q;
20   unsigned long i;
21   int e = errno;
22
23   if (!isdigit((unsigned char)*s)) goto fail;
24   errno = 0;
25   i = strtoul(s, &q, 0);
26   if (errno || *q || i > INT_MAX) goto fail;
27   errno = e;
28   return ((int)i);
29
30 fail:
31   fprintf(stderr, "%s: bad numeric %s `%s'\n", prog, what, s);
32   exit(1);
33 }
34
35 static int lookup(const char *name, const char *what,
36                   const struct nameval *nv)
37 {
38   if (isdigit((unsigned char)*name))
39     return (parseint(name, what));
40   for (; nv->name; nv++)
41     if (strcmp(nv->name, name) == 0) return nv->val;
42   fprintf(stderr, "%s: unknown %s `%s'\n", prog, what, name);
43   exit(1);
44 }
45
46 static const char *rlookup(int val, const char *what,
47                            const struct nameval *nv)
48 {
49   static char buf[32];
50   for (; nv->name; nv++)
51     if (nv->val == val) return (nv->name);
52   sprintf(buf, "%s#%d", what, val);
53   return (buf);
54 }
55
56 static const struct nameval familymap[] = {
57   { "unspec", AF_UNSPEC },
58   { "inet", AF_INET },
59   { "inet6", AF_INET6 },
60   { 0 }
61 };
62
63 static const struct nameval typemap[] = {
64   { "any", 0 },
65   { "stream", SOCK_STREAM },
66   { "dgram", SOCK_DGRAM },
67   { "raw", SOCK_RAW },
68   { 0 }
69 };
70
71 static void usage(FILE *fp)
72 {
73   fprintf(fp,
74           "Usage: %s "
75           "[-CNacgimnsuv] [-f FAMILY] [-p PROTOCOL] [-t TYPE] "
76           "NAME [SERVICE]\n",
77           prog);
78 }
79
80 static void help(FILE *fp)
81 {
82   usage(fp); fputs("\n\
83 Options:\n\
84 \n\
85   -a plain v4 also (with -m)            -n numeric host\n\
86   -c canonify name                      -N numeric service\n\
87   -C canonify to IDN (with -c)          -s allow unassigned codepoints\n\
88   -g families with configured addrs     -u check result is STD3 hostname\n\
89   -i convert to IDN                     -v passive\n\
90   -m v6-mapped v4 addresses\n\
91 \n\
92   -f unspec|inet|inet6                  NAME -- hostname or `-' for none\n\
93   -p PROTOCOL -- /etc/protocols         SERVICE -- number or /etc/services\n\
94   -t any|stream|dgram|raw\n\
95 ", fp);
96 }
97
98 int main(int argc, char *argv[])
99 {
100   struct addrinfo *ai, hint = { .ai_family = AF_UNSPEC };
101   const char *name;
102   const char *serv = 0;
103   char namebuf[NI_MAXHOST], servbuf[NI_MAXSERV];
104   int err;
105
106   prog = strrchr(argv[0], '/');
107   if (prog) prog++;
108   else prog = argv[0];
109
110   for (;;) {
111     int opt = getopt(argc, argv, "hf:t:p:nNcvgamiCus");
112     if (opt < 0) break;
113     switch (opt) {
114       case 'h': help(stdout); exit(0);
115       case 'f':
116         hint.ai_family = lookup(optarg, "family", familymap);
117         break;
118       case 't':
119         hint.ai_socktype = lookup(optarg, "type", typemap);
120         break;
121       case 'p':
122         if (isdigit((unsigned char)*optarg))
123           hint.ai_protocol = parseint(optarg, "protocol");
124         else {
125           struct protoent *p = getprotobyname(optarg);
126           if (!p) {
127             fprintf(stderr, "%s: unknown protocol `%s'\n", prog, optarg);
128             exit(1);
129           }
130           hint.ai_protocol = p->p_proto;
131         }
132         break;
133       case 'C': hint.ai_flags |= AI_CANONIDN; break;
134       case 'N': hint.ai_flags |= AI_NUMERICSERV; break;
135       case 'a': hint.ai_flags |= AI_ALL; break;
136       case 'c': hint.ai_flags |= AI_CANONNAME; break;
137       case 'g': hint.ai_flags |= AI_ADDRCONFIG; break;
138       case 'i': hint.ai_flags |= AI_IDN; break;
139       case 'm': hint.ai_flags |= AI_V4MAPPED; break;
140       case 'n': hint.ai_flags |= AI_NUMERICHOST; break;
141       case 's': hint.ai_flags |= AI_IDN_USE_STD3_ASCII_RULES; break;
142       case 'u': hint.ai_flags |= AI_IDN_ALLOW_UNASSIGNED; break;
143       case 'v': hint.ai_flags |= AI_PASSIVE; break;
144       default: usage(stderr); exit(1);
145     }
146   }
147
148   argv += optind;
149   if (!*argv) { usage(stderr); exit(1); } name = *argv++;
150   if (*argv) { serv = *argv++; }
151   if (*argv) { usage(stderr); exit(1); }
152
153   if (strcmp(name, "-") == 0) name = 0;
154
155   if ((err = getaddrinfo(name, serv, &hint, &ai)) != 0) {
156     fprintf(stderr, "%s: %s\n", prog, gai_strerror(err));
157     exit(1);
158   }
159
160   for (; ai; ai = ai->ai_next) {
161     if (ai->ai_canonname) printf("[%s]\n", ai->ai_canonname);
162     fputs(rlookup(ai->ai_family, "family", familymap), stdout);
163     fputc(' ', stdout);
164     fputs(rlookup(ai->ai_socktype, "type", typemap), stdout);
165     fputc(' ', stdout);
166     if ((err = getnameinfo(ai->ai_addr, ai->ai_addrlen,
167                            namebuf, sizeof(namebuf),
168                            servbuf, sizeof(servbuf),
169                            (NI_NUMERICHOST | NI_NUMERICSERV |
170                             (ai->ai_socktype == SOCK_DGRAM ?
171                              NI_DGRAM : 0)))) != 0)
172       printf("(error: %s)", gai_strerror(err));
173     else
174       printf("%s %s", namebuf, servbuf);
175     fputc('\n', stdout);
176   }
177
178   freeaddrinfo(ai);
179
180   return (0);
181 }