xref: /netbsd-src/usr.bin/getaddrinfo/getaddrinfo.c (revision 679f6f2d1c7e897ba9d9b2b6b61d8227aedcea4c)
1*679f6f2dSriastradh /*	$NetBSD: getaddrinfo.c,v 1.5 2024/01/10 01:48:16 riastradh Exp $	*/
288bc4b53Sriastradh 
388bc4b53Sriastradh /*-
488bc4b53Sriastradh  * Copyright (c) 2013 The NetBSD Foundation, Inc.
588bc4b53Sriastradh  * All rights reserved.
688bc4b53Sriastradh  *
788bc4b53Sriastradh  * This code is derived from software contributed to The NetBSD Foundation
888bc4b53Sriastradh  * by Taylor R. Campbell.
988bc4b53Sriastradh  *
1088bc4b53Sriastradh  * Redistribution and use in source and binary forms, with or without
1188bc4b53Sriastradh  * modification, are permitted provided that the following conditions
1288bc4b53Sriastradh  * are met:
1388bc4b53Sriastradh  * 1. Redistributions of source code must retain the above copyright
1488bc4b53Sriastradh  *    notice, this list of conditions and the following disclaimer.
1588bc4b53Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
1688bc4b53Sriastradh  *    notice, this list of conditions and the following disclaimer in the
1788bc4b53Sriastradh  *    documentation and/or other materials provided with the distribution.
1888bc4b53Sriastradh  *
1988bc4b53Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2088bc4b53Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2188bc4b53Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2288bc4b53Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2388bc4b53Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2488bc4b53Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2588bc4b53Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2688bc4b53Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2788bc4b53Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2888bc4b53Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2988bc4b53Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
3088bc4b53Sriastradh  */
3188bc4b53Sriastradh 
3288bc4b53Sriastradh #include <sys/cdefs.h>
33*679f6f2dSriastradh __RCSID("$NetBSD: getaddrinfo.c,v 1.5 2024/01/10 01:48:16 riastradh Exp $");
34aa8e0936Sginsbach 
35aa8e0936Sginsbach #include <sys/types.h>
36aa8e0936Sginsbach #include <sys/socket.h>
3788bc4b53Sriastradh 
3888bc4b53Sriastradh #include <assert.h>
3988bc4b53Sriastradh #include <err.h>
4088bc4b53Sriastradh #include <errno.h>
4188bc4b53Sriastradh #include <limits.h>
4288bc4b53Sriastradh #include <netdb.h>
4388bc4b53Sriastradh #include <stdbool.h>
4488bc4b53Sriastradh #include <stdint.h>
4588bc4b53Sriastradh #include <stdlib.h>
46aa8e0936Sginsbach #include <stdio.h>
4788bc4b53Sriastradh #include <string.h>
4888bc4b53Sriastradh #include <unistd.h>
4988bc4b53Sriastradh #include <util.h>
5088bc4b53Sriastradh 
5188bc4b53Sriastradh #include "tables.h"
5288bc4b53Sriastradh 
5388bc4b53Sriastradh static void	usage(void) __dead;
5488bc4b53Sriastradh static void	printaddrinfo(struct addrinfo *);
5588bc4b53Sriastradh static bool	parse_af(const char *, int *);
5688bc4b53Sriastradh static bool	parse_protocol(const char *, int *);
5788bc4b53Sriastradh static bool	parse_socktype(const char *, int *);
5888bc4b53Sriastradh static bool	parse_numeric_tabular(const char *, int *, const char *const *,
5988bc4b53Sriastradh 		    size_t);
6088bc4b53Sriastradh 
6188bc4b53Sriastradh int
main(int argc,char ** argv)6288bc4b53Sriastradh main(int argc, char **argv)
6388bc4b53Sriastradh {
6488bc4b53Sriastradh 	static const struct addrinfo zero_addrinfo;
6588bc4b53Sriastradh 	struct addrinfo hints = zero_addrinfo;
6688bc4b53Sriastradh 	struct addrinfo *addrinfo;
6788bc4b53Sriastradh 	const char *hostname = NULL, *service = NULL;
6888bc4b53Sriastradh 	int ch;
6988bc4b53Sriastradh 	int error;
7088bc4b53Sriastradh 
7188bc4b53Sriastradh 	setprogname(argv[0]);
7288bc4b53Sriastradh 
7388bc4b53Sriastradh 	hints.ai_family = AF_UNSPEC;
7488bc4b53Sriastradh 	hints.ai_socktype = 0;
7588bc4b53Sriastradh 	hints.ai_protocol = 0;
7688bc4b53Sriastradh 	hints.ai_flags = 0;
7788bc4b53Sriastradh 
7888bc4b53Sriastradh 	while ((ch = getopt(argc, argv, "cf:nNp:Ps:t:")) != -1) {
7988bc4b53Sriastradh 		switch (ch) {
8088bc4b53Sriastradh 		case 'c':
8188bc4b53Sriastradh 			hints.ai_flags |= AI_CANONNAME;
8288bc4b53Sriastradh 			break;
8388bc4b53Sriastradh 		case 'f':
8488bc4b53Sriastradh 			if (!parse_af(optarg, &hints.ai_family)) {
8588bc4b53Sriastradh 				warnx("invalid address family: %s", optarg);
8688bc4b53Sriastradh 				usage();
8788bc4b53Sriastradh 			}
8888bc4b53Sriastradh 			break;
8988bc4b53Sriastradh 		case 'n':
9088bc4b53Sriastradh 			hints.ai_flags |= AI_NUMERICHOST;
9188bc4b53Sriastradh 			break;
9288bc4b53Sriastradh 		case 'N':
9388bc4b53Sriastradh 			hints.ai_flags |= AI_NUMERICSERV;
9488bc4b53Sriastradh 			break;
9588bc4b53Sriastradh 		case 's':
9688bc4b53Sriastradh 			service = optarg;
9788bc4b53Sriastradh 			break;
9888bc4b53Sriastradh 		case 'p':
9988bc4b53Sriastradh 			if (!parse_protocol(optarg, &hints.ai_protocol)) {
10088bc4b53Sriastradh 				warnx("invalid protocol: %s", optarg);
10188bc4b53Sriastradh 				usage();
10288bc4b53Sriastradh 			}
10355791dffSginsbach 			break;
10488bc4b53Sriastradh 		case 'P':
10588bc4b53Sriastradh 			hints.ai_flags |= AI_PASSIVE;
10688bc4b53Sriastradh 			break;
10788bc4b53Sriastradh 		case 't':
10888bc4b53Sriastradh 			if (!parse_socktype(optarg, &hints.ai_socktype)) {
10988bc4b53Sriastradh 				warnx("invalid socket type: %s", optarg);
11088bc4b53Sriastradh 				usage();
11188bc4b53Sriastradh 			}
11288bc4b53Sriastradh 			break;
11388bc4b53Sriastradh 		case '?':
11488bc4b53Sriastradh 		default:
11588bc4b53Sriastradh 			usage();
11688bc4b53Sriastradh 		}
11788bc4b53Sriastradh 	}
11888bc4b53Sriastradh 
11988bc4b53Sriastradh 	argc -= optind;
12088bc4b53Sriastradh 	argv += optind;
12188bc4b53Sriastradh 
12288bc4b53Sriastradh 	if (!((argc == 1) || ((argc == 0) && (hints.ai_flags & AI_PASSIVE))))
12388bc4b53Sriastradh 		usage();
12488bc4b53Sriastradh 	if (argc == 1)
12588bc4b53Sriastradh 		hostname = argv[0];
12688bc4b53Sriastradh 
127236104beSginsbach 	if (service != NULL) {
128236104beSginsbach 		char *p;
129236104beSginsbach 
130236104beSginsbach 		if ((p = strchr(service, '/')) != NULL) {
131236104beSginsbach 			if (hints.ai_protocol != 0) {
132236104beSginsbach 				warnx("protocol already specified");
133236104beSginsbach 				usage();
134236104beSginsbach 			}
135236104beSginsbach 			*p = '\0';
136236104beSginsbach 			p++;
137236104beSginsbach 
138236104beSginsbach 			if (!parse_protocol(p, &hints.ai_protocol)) {
139236104beSginsbach 				warnx("invalid protocol: %s", p);
140236104beSginsbach 				usage();
141236104beSginsbach 			}
142236104beSginsbach 		}
143236104beSginsbach 	}
144236104beSginsbach 
14588bc4b53Sriastradh 	error = getaddrinfo(hostname, service, &hints, &addrinfo);
14688bc4b53Sriastradh 	if (error)
14788bc4b53Sriastradh 		errx(1, "%s", gai_strerror(error));
14888bc4b53Sriastradh 
14988bc4b53Sriastradh 	if ((hints.ai_flags & AI_CANONNAME) && (addrinfo != NULL)) {
15088bc4b53Sriastradh 		if (printf("canonname %s\n", addrinfo->ai_canonname) < 0)
15188bc4b53Sriastradh 			err(1, "printf");
15288bc4b53Sriastradh 	}
15388bc4b53Sriastradh 
15488bc4b53Sriastradh 	printaddrinfo(addrinfo);
15588bc4b53Sriastradh 
15688bc4b53Sriastradh 	freeaddrinfo(addrinfo);
15788bc4b53Sriastradh 
15888bc4b53Sriastradh 	return 0;
15988bc4b53Sriastradh }
16088bc4b53Sriastradh 
16188bc4b53Sriastradh static void __dead
usage(void)16288bc4b53Sriastradh usage(void)
16388bc4b53Sriastradh {
16488bc4b53Sriastradh 
16588bc4b53Sriastradh 	(void)fprintf(stderr, "Usage: %s", getprogname());
16688bc4b53Sriastradh 	(void)fprintf(stderr,
16788bc4b53Sriastradh 	    " [-f <family>] [-p <protocol>] [-t <socktype>] [-s <service>]\n");
16888bc4b53Sriastradh 	(void)fprintf(stderr, "   [-cnNP] [<hostname>]\n");
16988bc4b53Sriastradh 	exit(1);
17088bc4b53Sriastradh }
17188bc4b53Sriastradh 
17288bc4b53Sriastradh static bool
parse_af(const char * string,int * afp)17388bc4b53Sriastradh parse_af(const char *string, int *afp)
17488bc4b53Sriastradh {
17588bc4b53Sriastradh 
17688bc4b53Sriastradh 	return parse_numeric_tabular(string, afp, address_families,
17788bc4b53Sriastradh 	    __arraycount(address_families));
17888bc4b53Sriastradh }
17988bc4b53Sriastradh 
18088bc4b53Sriastradh static bool
parse_protocol(const char * string,int * protop)18188bc4b53Sriastradh parse_protocol(const char *string, int *protop)
18288bc4b53Sriastradh {
18388bc4b53Sriastradh 	struct protoent *protoent;
18488bc4b53Sriastradh 	char *end;
18588bc4b53Sriastradh 	long value;
18688bc4b53Sriastradh 
18788bc4b53Sriastradh 	errno = 0;
18888bc4b53Sriastradh 	value = strtol(string, &end, 0);
18988bc4b53Sriastradh 	if ((string[0] == '\0') || (*end != '\0'))
19088bc4b53Sriastradh 		goto numeric_failed;
19188bc4b53Sriastradh 	if ((errno == ERANGE) && ((value == LONG_MAX) || (value == LONG_MIN)))
19288bc4b53Sriastradh 		goto numeric_failed;
19388bc4b53Sriastradh 	if ((value > INT_MAX) || (value < INT_MIN))
19488bc4b53Sriastradh 		goto numeric_failed;
19588bc4b53Sriastradh 
19688bc4b53Sriastradh 	*protop = value;
19788bc4b53Sriastradh 	return true;
19888bc4b53Sriastradh 
19988bc4b53Sriastradh numeric_failed:
20088bc4b53Sriastradh 	protoent = getprotobyname(string);
20188bc4b53Sriastradh 	if (protoent == NULL)
20288bc4b53Sriastradh 		goto protoent_failed;
20388bc4b53Sriastradh 
20488bc4b53Sriastradh 	*protop = protoent->p_proto;
20588bc4b53Sriastradh 	return true;
20688bc4b53Sriastradh 
20788bc4b53Sriastradh protoent_failed:
20888bc4b53Sriastradh 	return false;
20988bc4b53Sriastradh }
21088bc4b53Sriastradh 
21188bc4b53Sriastradh static bool
parse_socktype(const char * string,int * typep)21288bc4b53Sriastradh parse_socktype(const char *string, int *typep)
21388bc4b53Sriastradh {
21488bc4b53Sriastradh 
21588bc4b53Sriastradh 	return parse_numeric_tabular(string, typep, socket_types,
21688bc4b53Sriastradh 	    __arraycount(socket_types));
21788bc4b53Sriastradh }
21888bc4b53Sriastradh 
21988bc4b53Sriastradh static bool
parse_numeric_tabular(const char * string,int * valuep,const char * const * table,size_t n)22088bc4b53Sriastradh parse_numeric_tabular(const char *string, int *valuep,
22188bc4b53Sriastradh     const char *const *table, size_t n)
22288bc4b53Sriastradh {
22388bc4b53Sriastradh 	char *end;
22488bc4b53Sriastradh 	long value;
22588bc4b53Sriastradh 	size_t i;
22688bc4b53Sriastradh 
22788bc4b53Sriastradh 	assert((uintmax_t)n <= (uintmax_t)INT_MAX);
22888bc4b53Sriastradh 
22988bc4b53Sriastradh 	errno = 0;
23088bc4b53Sriastradh 	value = strtol(string, &end, 0);
23188bc4b53Sriastradh 	if ((string[0] == '\0') || (*end != '\0'))
23288bc4b53Sriastradh 		goto numeric_failed;
23388bc4b53Sriastradh 	if ((errno == ERANGE) && ((value == LONG_MAX) || (value == LONG_MIN)))
23488bc4b53Sriastradh 		goto numeric_failed;
23588bc4b53Sriastradh 	if ((value > INT_MAX) || (value < INT_MIN))
23688bc4b53Sriastradh 		goto numeric_failed;
23788bc4b53Sriastradh 
23888bc4b53Sriastradh 	*valuep = value;
23988bc4b53Sriastradh 	return true;
24088bc4b53Sriastradh 
24188bc4b53Sriastradh numeric_failed:
24288bc4b53Sriastradh 	for (i = 0; i < n; i++)
24388bc4b53Sriastradh 		if ((table[i] != NULL) && (strcmp(string, table[i]) == 0))
24488bc4b53Sriastradh 			break;
24588bc4b53Sriastradh 	if (i == n)
24688bc4b53Sriastradh 		goto table_failed;
24788bc4b53Sriastradh 	*valuep = i;
24888bc4b53Sriastradh 	return true;
24988bc4b53Sriastradh 
25088bc4b53Sriastradh table_failed:
25188bc4b53Sriastradh 	return false;
25288bc4b53Sriastradh }
25388bc4b53Sriastradh 
25488bc4b53Sriastradh static void
printaddrinfo(struct addrinfo * addrinfo)25588bc4b53Sriastradh printaddrinfo(struct addrinfo *addrinfo)
25688bc4b53Sriastradh {
25788bc4b53Sriastradh 	struct addrinfo *ai;
25888bc4b53Sriastradh 	char buf[1024];
25988bc4b53Sriastradh 	int n;
26088bc4b53Sriastradh 	struct protoent *protoent;
26188bc4b53Sriastradh 
26288bc4b53Sriastradh 	for (ai = addrinfo; ai != NULL; ai = ai->ai_next) {
26388bc4b53Sriastradh 		/* Print the socket type.  */
26488bc4b53Sriastradh 		if ((ai->ai_socktype >= 0) &&
26588bc4b53Sriastradh 		    ((size_t)ai->ai_socktype < __arraycount(socket_types)) &&
26688bc4b53Sriastradh 		    (socket_types[ai->ai_socktype] != NULL))
26788bc4b53Sriastradh 			n = printf("%s", socket_types[ai->ai_socktype]);
26888bc4b53Sriastradh 		else
26988bc4b53Sriastradh 			n = printf("%d", ai->ai_socktype);
27088bc4b53Sriastradh 		if (n < 0)
27188bc4b53Sriastradh 			err(1, "printf");
27288bc4b53Sriastradh 
27388bc4b53Sriastradh 		/* Print the address family.  */
27488bc4b53Sriastradh 		if ((ai->ai_family >= 0) &&
27588bc4b53Sriastradh 		    ((size_t)ai->ai_family < __arraycount(address_families)) &&
27688bc4b53Sriastradh 		    (address_families[ai->ai_family] != NULL))
27788bc4b53Sriastradh 			n = printf(" %s", address_families[ai->ai_family]);
27888bc4b53Sriastradh 		else
27988bc4b53Sriastradh 			n = printf(" %d", ai->ai_family);
28088bc4b53Sriastradh 		if (n < 0)
28188bc4b53Sriastradh 			err(1, "printf");
28288bc4b53Sriastradh 
28388bc4b53Sriastradh 		/* Print the protocol number.  */
28488bc4b53Sriastradh 		protoent = getprotobynumber(ai->ai_protocol);
28588bc4b53Sriastradh 		if (protoent == NULL)
28688bc4b53Sriastradh 			n = printf(" %d", ai->ai_protocol);
28788bc4b53Sriastradh 		else
28888bc4b53Sriastradh 			n = printf(" %s", protoent->p_name);
28988bc4b53Sriastradh 		if (n < 0)
29088bc4b53Sriastradh 			err(1, "printf");
29188bc4b53Sriastradh 
29288bc4b53Sriastradh 		/* Format the sockaddr.  */
29388bc4b53Sriastradh 		switch (ai->ai_family) {
29488bc4b53Sriastradh 		case AF_INET:
29588bc4b53Sriastradh 		case AF_INET6:
29688bc4b53Sriastradh 			n = sockaddr_snprintf(buf, sizeof(buf), " %a %p",
29788bc4b53Sriastradh 			    ai->ai_addr);
29888bc4b53Sriastradh 			break;
29988bc4b53Sriastradh 		default:
30088bc4b53Sriastradh 			n = sockaddr_snprintf(buf, sizeof(buf),
30188bc4b53Sriastradh 			    "%a %p %I %F %R %S", ai->ai_addr);
30288bc4b53Sriastradh 		}
30388bc4b53Sriastradh 
30488bc4b53Sriastradh 		/*
30588bc4b53Sriastradh 		 * Check for sockaddr_snprintf failure.
30688bc4b53Sriastradh 		 *
30788bc4b53Sriastradh 		 * XXX sockaddr_snprintf's error reporting is botched
30888bc4b53Sriastradh 		 * -- man page says it sets errno, but if getnameinfo
30988bc4b53Sriastradh 		 * fails, errno is not where it reports the error...
31088bc4b53Sriastradh 		 */
31188bc4b53Sriastradh 		if (n < 0) {
31288bc4b53Sriastradh 			warnx("sockaddr_snprintf failed");
31388bc4b53Sriastradh 			continue;
31488bc4b53Sriastradh 		}
31588bc4b53Sriastradh 		if (sizeof(buf) <= (size_t)n)
31688bc4b53Sriastradh 			warnx("truncated sockaddr_snprintf output");
31788bc4b53Sriastradh 
31888bc4b53Sriastradh 		/* Print the formatted sockaddr.  */
31988bc4b53Sriastradh 		if (printf("%s\n", buf) < 0)
32088bc4b53Sriastradh 			err(1, "printf");
32188bc4b53Sriastradh 	}
32288bc4b53Sriastradh }
323