getnameinfo.c revision 1.8 1 /* $NetBSD: getnameinfo.c,v 1.8 2025/02/06 20:59:00 christos Exp $ */
2
3 /*
4 * Copyright (c) 2025 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Attaullah Ansari.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: getnameinfo.c,v 1.8 2025/02/06 20:59:00 christos Exp $");
35 #endif
36
37 #include <sys/types.h>
38
39 #include <sys/socket.h>
40
41 #include <arpa/inet.h>
42 #include <netinet/in.h>
43 #include <netatalk/at.h>
44 #include <sys/un.h>
45 #include <net/if_dl.h>
46 #include <arpa/inet.h>
47
48 #include <assert.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <netdb.h>
52 #include <stdbool.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 #include "support.h"
59
60 /*
61 * getnameinfo: Resolve IP addresses and ports to hostnames and service names,
62 * similar to the getnameinfo function in the standard library.
63 *
64 * usage:
65 * getnameinfo [-46FHNnrSu] [-f family] [-p port] <IP-address>
66 *
67 * -4: Restrict lookup to IPv4 addresses only
68 * -6: Restrict lookup to IPv6 addresses only
69 * -F: Suppress the fully-qualified domain name (FQDN)
70 * -f: Specify address family to look up
71 * -H: Display only the hostname, omitting the service name
72 * -N: Display the numeric service name instead of the service name
73 * -n: Display the numeric host address instead of the hostname
74 * -p: Specify the port number to be used in the lookup
75 * -r: Ensure that the name is returned (error if no name is found)
76 * -S: Display only the service name, omitting the hostname
77 * -u: Use UDP instead of the default TCP
78 */
79
80 static void usage(void) __dead;
81 static void print_result(bool, bool, char *, char *);
82 static in_port_t get_port(const char *);
83 static uint8_t get_family_from_address(const char *);
84 static uint8_t get_family(const char *);
85 static void parse_atalk(const char *, struct sockaddr_at *);
86
87 int
88 main(int argc, char **argv)
89 {
90 socklen_t hostlen = NI_MAXHOST, servlen = NI_MAXSERV, addrlen;
91 char hostname[NI_MAXHOST], service[NI_MAXSERV];
92 bool hostname_only = false, service_only = false;
93 uint8_t family = AF_UNSPEC;
94 int flags = 0;
95 char *address = NULL;
96 in_port_t port = 0;
97 struct sockaddr_storage addr_st;
98 struct sockaddr_in *addr_in;
99 struct sockaddr_in6 *addr_in6;
100 struct sockaddr_un *addr_un;
101 struct sockaddr_dl *addr_dl;
102 int ch;
103 int error;
104
105 setprogname(argv[0]);
106
107 while ((ch = getopt(argc, argv, "46Ff:HNnp:rSu")) != -1) {
108 switch (ch) {
109 case '4':
110 if (family != AF_UNSPEC)
111 goto opt46f;
112 family = AF_INET;
113 break;
114 case '6':
115 if (family != AF_UNSPEC)
116 goto opt46f;
117 family = AF_INET6;
118 break;
119 case 'r':
120 flags |= NI_NAMEREQD;
121 break;
122 case 'u':
123 flags |= NI_DGRAM;
124 break;
125 case 'f':
126 if (family != AF_UNSPEC)
127 goto opt46f;
128 family = get_family(optarg);
129 break;
130 case 'F':
131 flags |= NI_NOFQDN;
132 break;
133 case 'n':
134 flags |= NI_NUMERICHOST;
135 break;
136 case 'N':
137 flags |= NI_NUMERICSERV;
138 break;
139 case 'H':
140 if (service_only)
141 goto optHS;
142 hostname_only = true;
143 break;
144 case 'S':
145 if (hostname_only)
146 goto optHS;
147 service_only = true;
148 break;
149 case 'p':
150 port = get_port(optarg);
151 break;
152 case '?':
153 default:
154 usage();
155 }
156 }
157
158 argc -= optind;
159 argv += optind;
160
161 if (argc > 1) {
162 warnx("Too many addresses");
163 usage();
164 }
165
166 if (argc == 0 && (hostname_only ||
167 (!hostname_only && !service_only))) {
168 warnx("No IP address provided");
169 usage();
170 }
171
172 if (port == 0 && (service_only ||
173 (!hostname_only && !service_only))) {
174 warnx("No port number provided");
175 usage();
176 }
177
178 if (argc == 1) {
179 address = argv[0];
180 if (family == AF_UNSPEC)
181 family = get_family_from_address(address);
182 }
183
184 memset(&addr_st, 0, sizeof(addr_st));
185
186 switch (family) {
187 case AF_APPLETALK:
188 parse_atalk(address, (struct sockaddr_at *)&addr_st);
189 addrlen = sizeof(struct sockaddr_at *);
190 break;
191 case AF_INET:
192 addr_in = (struct sockaddr_in *)&addr_st;
193 addr_in->sin_family = family;
194 addr_in->sin_port = htons(port);
195 if (address == NULL) {
196 addr_in->sin_addr.s_addr = INADDR_ANY;
197 } else if (inet_pton(family, address, &addr_in->sin_addr)
198 == 0)
199 errx(EXIT_FAILURE, "Invalid IPv4 address: %s", address);
200 addrlen = sizeof(*addr_in);
201 break;
202 case AF_INET6:
203 addr_in6 = (struct sockaddr_in6 *)&addr_st;
204 addr_in6->sin6_family = family;
205 addr_in6->sin6_port = htons(port);
206 if (address == NULL) {
207 addr_in6->sin6_addr =
208 (struct in6_addr)IN6ADDR_ANY_INIT;
209 } else if (inet_pton(family, address, &addr_in6->sin6_addr)
210 == 0)
211 errx(EXIT_FAILURE, "Invalid IPv6 address: %s", address);
212 addrlen = sizeof(*addr_in6);
213 break;
214 case AF_LINK:
215 addr_dl = (struct sockaddr_dl *)&addr_st;
216 addr_dl->sdl_len = sizeof(addr_st);
217 link_addr(address, addr_dl);
218 addrlen = addr_dl->sdl_len;
219 break;
220 case AF_LOCAL:
221 addr_un = (struct sockaddr_un *)&addr_st;
222 addr_un->sun_family = family;
223 if (strlen(address) >= sizeof(addr_un->sun_path))
224 errx(EXIT_FAILURE, "Invalid AF_LOCAL address: %s",
225 address);
226 (void)strncpy(addr_un->sun_path, address,
227 sizeof(addr_un->sun_path));
228 addrlen = sizeof(*addr_un);
229 break;
230 default:
231 errx(EXIT_FAILURE, "Unsupported family %d", family);
232 }
233
234 if (hostname_only)
235 servlen = 0;
236 else if (service_only)
237 hostlen = 0;
238
239 error = getnameinfo((struct sockaddr *)&addr_st, addrlen,
240 hostname, hostlen, service, servlen, flags);
241 if (error)
242 errx(EXIT_FAILURE, "%s", gai_strerror(error));
243
244 print_result(hostname_only, service_only, hostname, service);
245
246 fflush(stdout);
247 return ferror(stdout) ? EXIT_FAILURE : EXIT_SUCCESS;
248 opt46f:
249 warnx("Options -4, -6, -f cannot be used together");
250 usage();
251 optHS:
252 warnx("Options -H and -S cannot be used together");
253 usage();
254 }
255
256 static void
257 parse_atalk(const char *address, struct sockaddr_at *addr_at)
258 {
259 int net, node, port;
260
261 if (sscanf(address, "%d:%d:%d", &net, &node, &port) != 3)
262 badat: errx(EXIT_FAILURE, "Invalid appletalk address: %s", address);
263
264 if (net < 0 || net > 0xFFFF || node < 0 || node > 0xFF ||
265 port < 0 || port > 0xFFFF)
266 goto badat;
267 addr_at->sat_family = AF_APPLETALK;
268 addr_at->sat_addr.s_net = htons((u_short)net);
269 addr_at->sat_addr.s_node = (u_char)node;
270 addr_at->sat_port = htons((u_short)port);
271 }
272
273 static uint8_t
274 get_family_from_address(const char *address)
275 {
276 struct in_addr ipv4_addr;
277 struct in6_addr ipv6_addr;
278
279 if (inet_pton(AF_INET, address, &ipv4_addr) == 1)
280 return AF_INET;
281
282 if (inet_pton(AF_INET6, address, &ipv6_addr) == 1)
283 return AF_INET6;
284
285 errx(EXIT_FAILURE, "Invalid addrsss %s", address);
286 }
287
288 static uint8_t
289 get_family(const char* str)
290 {
291 int fam;
292 if (!parse_af(str, &fam) || fam <= 0 || fam > 255)
293 errx(EXIT_FAILURE, "Invalid family %s", str);
294 return (int8_t)fam;
295 }
296
297 static in_port_t
298 get_port(const char *port_str)
299 {
300 int r;
301
302 const intmax_t port = strtoi(port_str, NULL, 0, 0, 65535, &r);
303 if (r)
304 errc(EXIT_FAILURE, r, "Invalid port number %s", port_str);
305
306 return (in_port_t)port;
307 }
308
309 static void
310 print_result(bool hostname_only, bool service_only,
311 char *hostname, char *service)
312 {
313
314 if (hostname_only)
315 printf("%s\n", hostname);
316 else if (service_only)
317 printf("%s\n", service);
318 else
319 printf("%s %s\n", hostname, service);
320 }
321
322 static void __dead
323 usage(void)
324 {
325
326 (void)fprintf(stderr, "Usage: %s", getprogname());
327 (void)fprintf(stderr, " [-46fHNnrSu] [-p port] [<IP-address>]\n");
328 exit(EXIT_FAILURE);
329 }
330