1 /*        $NetBSD: getaddrinfo.c,v 1.6 2025/02/06 19:35:28 christos Exp $       */
2 
3 /*-
4  * Copyright (c) 2013 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Taylor R. Campbell.
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 the
17  *    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 LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: getaddrinfo.c,v 1.6 2025/02/06 19:35:28 christos Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 
38 #include <assert.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <netdb.h>
43 #include <stdbool.h>
44 #include <stdint.h>
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <util.h>
50 
51 #include "tables.h"
52 #include "support.h"
53 
54 static void         usage(void) __dead;
55 static void         printaddrinfo(struct addrinfo *);
56 
57 int
main(int argc,char ** argv)58 main(int argc, char **argv)
59 {
60           static const struct addrinfo zero_addrinfo;
61           struct addrinfo hints = zero_addrinfo;
62           struct addrinfo *addrinfo;
63           const char *hostname = NULL;
64           char *service = NULL;
65           int ch;
66           int error;
67 
68           setprogname(argv[0]);
69 
70           hints.ai_family = AF_UNSPEC;
71           hints.ai_socktype = 0;
72           hints.ai_protocol = 0;
73           hints.ai_flags = 0;
74 
75           while ((ch = getopt(argc, argv, "cf:nNp:Ps:t:")) != -1) {
76                     switch (ch) {
77                     case 'c':
78                               hints.ai_flags |= AI_CANONNAME;
79                               break;
80                     case 'f':
81                               if (!parse_af(optarg, &hints.ai_family)) {
82                                         warnx("invalid address family: %s", optarg);
83                                         usage();
84                               }
85                               break;
86                     case 'n':
87                               hints.ai_flags |= AI_NUMERICHOST;
88                               break;
89                     case 'N':
90                               hints.ai_flags |= AI_NUMERICSERV;
91                               break;
92                     case 's':
93                               service = optarg;
94                               break;
95                     case 'p':
96                               if (!parse_protocol(optarg, &hints.ai_protocol)) {
97                                         warnx("invalid protocol: %s", optarg);
98                                         usage();
99                               }
100                               break;
101                     case 'P':
102                               hints.ai_flags |= AI_PASSIVE;
103                               break;
104                     case 't':
105                               if (!parse_socktype(optarg, &hints.ai_socktype)) {
106                                         warnx("invalid socket type: %s", optarg);
107                                         usage();
108                               }
109                               break;
110                     case '?':
111                     default:
112                               usage();
113                     }
114           }
115 
116           argc -= optind;
117           argv += optind;
118 
119           if (!((argc == 1) || ((argc == 0) && (hints.ai_flags & AI_PASSIVE))))
120                     usage();
121           if (argc == 1)
122                     hostname = argv[0];
123 
124           if (service != NULL) {
125                     char *p;
126 
127                     if ((p = strchr(service, '/')) != NULL) {
128                               if (hints.ai_protocol != 0) {
129                                         warnx("protocol already specified");
130                                         usage();
131                               }
132                               *p = '\0';
133                               p++;
134 
135                               if (!parse_protocol(p, &hints.ai_protocol)) {
136                                         warnx("invalid protocol: %s", p);
137                                         usage();
138                               }
139                     }
140           }
141 
142           error = getaddrinfo(hostname, service, &hints, &addrinfo);
143           if (error)
144                     errx(1, "%s", gai_strerror(error));
145 
146           if ((hints.ai_flags & AI_CANONNAME) && (addrinfo != NULL)) {
147                     if (printf("canonname %s\n", addrinfo->ai_canonname) < 0)
148                               err(1, "printf");
149           }
150 
151           printaddrinfo(addrinfo);
152 
153           freeaddrinfo(addrinfo);
154 
155           return 0;
156 }
157 
158 static void __dead
usage(void)159 usage(void)
160 {
161 
162           (void)fprintf(stderr, "Usage: %s", getprogname());
163           (void)fprintf(stderr,
164               " [-f <family>] [-p <protocol>] [-t <socktype>] [-s <service>]\n");
165           (void)fprintf(stderr, "   [-cnNP] [<hostname>]\n");
166           exit(1);
167 }
168 
169 static void
printaddrinfo(struct addrinfo * addrinfo)170 printaddrinfo(struct addrinfo *addrinfo)
171 {
172           struct addrinfo *ai;
173           char buf[1024];
174           int n;
175           struct protoent *protoent;
176 
177           for (ai = addrinfo; ai != NULL; ai = ai->ai_next) {
178                     /* Print the socket type.  */
179                     if ((ai->ai_socktype >= 0) &&
180                         ((size_t)ai->ai_socktype < __arraycount(socket_types)) &&
181                         (socket_types[ai->ai_socktype] != NULL))
182                               n = printf("%s", socket_types[ai->ai_socktype]);
183                     else
184                               n = printf("%d", ai->ai_socktype);
185                     if (n < 0)
186                               err(1, "printf");
187 
188                     /* Print the address family.  */
189                     if ((ai->ai_family >= 0) &&
190                         ((size_t)ai->ai_family < __arraycount(address_families)) &&
191                         (address_families[ai->ai_family] != NULL))
192                               n = printf(" %s", address_families[ai->ai_family]);
193                     else
194                               n = printf(" %d", ai->ai_family);
195                     if (n < 0)
196                               err(1, "printf");
197 
198                     /* Print the protocol number.  */
199                     protoent = getprotobynumber(ai->ai_protocol);
200                     if (protoent == NULL)
201                               n = printf(" %d", ai->ai_protocol);
202                     else
203                               n = printf(" %s", protoent->p_name);
204                     if (n < 0)
205                               err(1, "printf");
206 
207                     /* Format the sockaddr.  */
208                     switch (ai->ai_family) {
209                     case AF_INET:
210                     case AF_INET6:
211                               n = sockaddr_snprintf(buf, sizeof(buf), " %a %p",
212                                   ai->ai_addr);
213                               break;
214                     default:
215                               n = sockaddr_snprintf(buf, sizeof(buf),
216                                   "%a %p %I %F %R %S", ai->ai_addr);
217                     }
218 
219                     /*
220                      * Check for sockaddr_snprintf failure.
221                      *
222                      * XXX sockaddr_snprintf's error reporting is botched
223                      * -- man page says it sets errno, but if getnameinfo
224                      * fails, errno is not where it reports the error...
225                      */
226                     if (n < 0) {
227                               warnx("sockaddr_snprintf failed");
228                               continue;
229                     }
230                     if (sizeof(buf) <= (size_t)n)
231                               warnx("truncated sockaddr_snprintf output");
232 
233                     /* Print the formatted sockaddr.  */
234                     if (printf("%s\n", buf) < 0)
235                               err(1, "printf");
236           }
237 }
238