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