1 /* MIT License
2 *
3 * Copyright (c) 1998 Massachusetts Institute of Technology
4 * Copyright (c) The c-ares project and its contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * SPDX-License-Identifier: MIT
26 */
27
28 #include "ares_setup.h"
29
30 #if !defined(_WIN32) || defined(WATT32)
31 # include <netinet/in.h>
32 # include <arpa/inet.h>
33 # include <netdb.h>
34 #endif
35
36 #ifdef HAVE_STRINGS_H
37 # include <strings.h>
38 #endif
39
40 #include "ares.h"
41 #include "ares_dns.h"
42 #include "ares_getopt.h"
43 #include "ares_ipv6.h"
44
45 #include "ares_str.h"
46
47 static void callback(void *arg, int status, int timeouts, struct hostent *host);
48 static void ai_callback(void *arg, int status, int timeouts,
49 struct ares_addrinfo *result);
50 static void usage(void);
51 static void print_help_info_ahost(void);
52
main(int argc,char ** argv)53 int main(int argc, char **argv)
54 {
55 struct ares_options options;
56 int optmask = 0;
57 ares_channel_t *channel;
58 int status;
59 int nfds;
60 int c;
61 int addr_family = AF_UNSPEC;
62 fd_set read_fds;
63 fd_set write_fds;
64 struct timeval *tvp;
65 struct timeval tv;
66 struct in_addr addr4;
67 struct ares_in6_addr addr6;
68 ares_getopt_state_t state;
69 char *servers = NULL;
70
71 #ifdef USE_WINSOCK
72 WORD wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
73 WSADATA wsaData;
74 WSAStartup(wVersionRequested, &wsaData);
75 #endif
76
77 memset(&options, 0, sizeof(options));
78
79 status = ares_library_init(ARES_LIB_INIT_ALL);
80 if (status != ARES_SUCCESS) {
81 fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status));
82 return 1;
83 }
84
85 ares_getopt_init(&state, argc, (const char * const *)argv);
86 while ((c = ares_getopt(&state, "dt:h?D:s:")) != -1) {
87 switch (c) {
88 case 'd':
89 #ifdef WATT32
90 dbug_init();
91 #endif
92 break;
93 case 'D':
94 optmask |= ARES_OPT_DOMAINS;
95 options.ndomains++;
96 options.domains = (char **)realloc(
97 options.domains, (size_t)options.ndomains * sizeof(char *));
98 options.domains[options.ndomains - 1] = strdup(state.optarg);
99 break;
100 case 't':
101 if (ares_strcaseeq(state.optarg, "a")) {
102 addr_family = AF_INET;
103 } else if (ares_strcaseeq(state.optarg, "aaaa")) {
104 addr_family = AF_INET6;
105 } else if (ares_strcaseeq(state.optarg, "u")) {
106 addr_family = AF_UNSPEC;
107 } else {
108 usage();
109 }
110 break;
111 case 's':
112 if (state.optarg == NULL) {
113 fprintf(stderr, "%s", "missing servers");
114 usage();
115 break;
116 }
117 if (servers) {
118 free(servers);
119 }
120 servers = strdup(state.optarg);
121 break;
122 case 'h':
123 case '?':
124 print_help_info_ahost();
125 break;
126 default:
127 usage();
128 break;
129 }
130 }
131
132 argc -= state.optind;
133 argv += state.optind;
134 if (argc < 1) {
135 usage();
136 }
137
138 status = ares_init_options(&channel, &options, optmask);
139 if (status != ARES_SUCCESS) {
140 free(servers);
141 fprintf(stderr, "ares_init: %s\n", ares_strerror(status));
142 return 1;
143 }
144
145 if (servers) {
146 status = ares_set_servers_csv(channel, servers);
147 if (status != ARES_SUCCESS) {
148 fprintf(stderr, "ares_set_serveres_csv: %s\n", ares_strerror(status));
149 free(servers);
150 usage();
151 return 1;
152 }
153 free(servers);
154 }
155
156 /* Initiate the queries, one per command-line argument. */
157 for (; *argv; argv++) {
158 if (ares_inet_pton(AF_INET, *argv, &addr4) == 1) {
159 ares_gethostbyaddr(channel, &addr4, sizeof(addr4), AF_INET, callback,
160 *argv);
161 } else if (ares_inet_pton(AF_INET6, *argv, &addr6) == 1) {
162 ares_gethostbyaddr(channel, &addr6, sizeof(addr6), AF_INET6, callback,
163 *argv);
164 } else {
165 struct ares_addrinfo_hints hints;
166 memset(&hints, 0, sizeof(hints));
167 hints.ai_family = addr_family;
168 ares_getaddrinfo(channel, *argv, NULL, &hints, ai_callback, *argv);
169 }
170 }
171
172 /* Wait for all queries to complete. */
173 for (;;) {
174 int res;
175 FD_ZERO(&read_fds);
176 FD_ZERO(&write_fds);
177 nfds = ares_fds(channel, &read_fds, &write_fds);
178 if (nfds == 0) {
179 break;
180 }
181 tvp = ares_timeout(channel, NULL, &tv);
182 if (tvp == NULL) {
183 break;
184 }
185 res = select(nfds, &read_fds, &write_fds, NULL, tvp);
186 if (-1 == res) {
187 break;
188 }
189 ares_process(channel, &read_fds, &write_fds);
190 }
191
192 ares_destroy(channel);
193
194 ares_library_cleanup();
195
196 #ifdef USE_WINSOCK
197 WSACleanup();
198 #endif
199
200 return 0;
201 }
202
callback(void * arg,int status,int timeouts,struct hostent * host)203 static void callback(void *arg, int status, int timeouts, struct hostent *host)
204 {
205 char **p;
206
207 (void)timeouts;
208
209 if (status != ARES_SUCCESS) {
210 fprintf(stderr, "%s: %s\n", (char *)arg, ares_strerror(status));
211 return;
212 }
213
214 for (p = host->h_addr_list; *p; p++) {
215 char addr_buf[46] = "??";
216
217 ares_inet_ntop(host->h_addrtype, *p, addr_buf, sizeof(addr_buf));
218 printf("%-32s\t%s", host->h_name, addr_buf);
219 puts("");
220 }
221 }
222
ai_callback(void * arg,int status,int timeouts,struct ares_addrinfo * result)223 static void ai_callback(void *arg, int status, int timeouts,
224 struct ares_addrinfo *result)
225 {
226 struct ares_addrinfo_node *node = NULL;
227 (void)timeouts;
228
229
230 if (status != ARES_SUCCESS) {
231 fprintf(stderr, "%s: %s\n", (char *)arg, ares_strerror(status));
232 return;
233 }
234
235 for (node = result->nodes; node != NULL; node = node->ai_next) {
236 char addr_buf[64] = "";
237 const void *ptr = NULL;
238 if (node->ai_family == AF_INET) {
239 const struct sockaddr_in *in_addr =
240 (const struct sockaddr_in *)((void *)node->ai_addr);
241 ptr = &in_addr->sin_addr;
242 } else if (node->ai_family == AF_INET6) {
243 const struct sockaddr_in6 *in_addr =
244 (const struct sockaddr_in6 *)((void *)node->ai_addr);
245 ptr = &in_addr->sin6_addr;
246 } else {
247 continue;
248 }
249 ares_inet_ntop(node->ai_family, ptr, addr_buf, sizeof(addr_buf));
250 printf("%-32s\t%s\n", result->name, addr_buf);
251 }
252
253 ares_freeaddrinfo(result);
254 }
255
usage(void)256 static void usage(void)
257 {
258 fprintf(stderr, "usage: ahost [-h] [-d] [[-D {domain}] ...] [-s {server}] "
259 "[-t {a|aaaa|u}] {host|addr} ...\n");
260 exit(1);
261 }
262
263 /* Information from the man page. Formatting taken from man -h */
print_help_info_ahost(void)264 static void print_help_info_ahost(void)
265 {
266 /* Split due to maximum c89 string literal of 509 bytes */
267 printf("ahost, version %s\n\n", ARES_VERSION_STR);
268 printf(
269 "usage: ahost [-h] [-d] [-D domain] [-s server] [-t a|aaaa|u] host|addr "
270 "...\n\n");
271 printf(
272 " -h : Display this help and exit.\n"
273 " -d : Print some extra debugging output.\n\n"
274 " -D domain : Specify the domain to search instead of using the default "
275 "values\n");
276 printf(
277 " -s server : Connect to the specified DNS server, instead of the\n"
278 " system's default one(s). Servers are tried in round-robin,\n"
279 " if the previous one failed.\n"
280 " -t type : If type is \"a\", print the A record.\n");
281 printf(
282 " If type is \"aaaa\", print the AAAA record.\n"
283 " If type is \"u\" (default), print both A and AAAA records.\n"
284 "\n");
285 exit(0);
286 }
287