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 #ifndef HAVE_STRDUP
46 # include "ares_str.h"
47 # define strdup(ptr) ares_strdup(ptr)
48 #endif
49
50 #ifndef HAVE_STRCASECMP
51 # include "ares_strcasecmp.h"
52 # define strcasecmp(p1, p2) ares_strcasecmp(p1, p2)
53 #endif
54
55 #ifndef HAVE_STRNCASECMP
56 # include "ares_strcasecmp.h"
57 # define strncasecmp(p1, p2, n) ares_strncasecmp(p1, p2, n)
58 #endif
59
60 static void callback(void *arg, int status, int timeouts, struct hostent *host);
61 static void ai_callback(void *arg, int status, int timeouts,
62 struct ares_addrinfo *result);
63 static void usage(void);
64 static void print_help_info_ahost(void);
65
main(int argc,char ** argv)66 int main(int argc, char **argv)
67 {
68 struct ares_options options;
69 int optmask = 0;
70 ares_channel_t *channel;
71 int status;
72 int nfds;
73 int c;
74 int addr_family = AF_UNSPEC;
75 fd_set read_fds;
76 fd_set write_fds;
77 struct timeval *tvp;
78 struct timeval tv;
79 struct in_addr addr4;
80 struct ares_in6_addr addr6;
81 ares_getopt_state_t state;
82 char *servers = NULL;
83
84 #ifdef USE_WINSOCK
85 WORD wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
86 WSADATA wsaData;
87 WSAStartup(wVersionRequested, &wsaData);
88 #endif
89
90 memset(&options, 0, sizeof(options));
91
92 status = ares_library_init(ARES_LIB_INIT_ALL);
93 if (status != ARES_SUCCESS) {
94 fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status));
95 return 1;
96 }
97
98 ares_getopt_init(&state, argc, (const char **)argv);
99 while ((c = ares_getopt(&state, "dt:h?D:s:")) != -1) {
100 switch (c) {
101 case 'd':
102 #ifdef WATT32
103 dbug_init();
104 #endif
105 break;
106 case 'D':
107 optmask |= ARES_OPT_DOMAINS;
108 options.ndomains++;
109 options.domains = (char **)realloc(
110 options.domains, (size_t)options.ndomains * sizeof(char *));
111 options.domains[options.ndomains - 1] = strdup(state.optarg);
112 break;
113 case 't':
114 if (!strcasecmp(state.optarg, "a")) {
115 addr_family = AF_INET;
116 } else if (!strcasecmp(state.optarg, "aaaa")) {
117 addr_family = AF_INET6;
118 } else if (!strcasecmp(state.optarg, "u")) {
119 addr_family = AF_UNSPEC;
120 } else {
121 usage();
122 }
123 break;
124 case 's':
125 if (state.optarg == NULL) {
126 fprintf(stderr, "%s", "missing servers");
127 usage();
128 break;
129 }
130 if (servers) {
131 free(servers);
132 }
133 servers = strdup(state.optarg);
134 break;
135 case 'h':
136 case '?':
137 print_help_info_ahost();
138 break;
139 default:
140 usage();
141 break;
142 }
143 }
144
145 argc -= state.optind;
146 argv += state.optind;
147 if (argc < 1) {
148 usage();
149 }
150
151 status = ares_init_options(&channel, &options, optmask);
152 if (status != ARES_SUCCESS) {
153 free(servers);
154 fprintf(stderr, "ares_init: %s\n", ares_strerror(status));
155 return 1;
156 }
157
158 if (servers) {
159 status = ares_set_servers_csv(channel, servers);
160 if (status != ARES_SUCCESS) {
161 fprintf(stderr, "ares_set_serveres_csv: %s\n", ares_strerror(status));
162 free(servers);
163 usage();
164 return 1;
165 }
166 free(servers);
167 }
168
169 /* Initiate the queries, one per command-line argument. */
170 for (; *argv; argv++) {
171 if (ares_inet_pton(AF_INET, *argv, &addr4) == 1) {
172 ares_gethostbyaddr(channel, &addr4, sizeof(addr4), AF_INET, callback,
173 *argv);
174 } else if (ares_inet_pton(AF_INET6, *argv, &addr6) == 1) {
175 ares_gethostbyaddr(channel, &addr6, sizeof(addr6), AF_INET6, callback,
176 *argv);
177 } else {
178 struct ares_addrinfo_hints hints;
179 memset(&hints, 0, sizeof(hints));
180 hints.ai_family = addr_family;
181 ares_getaddrinfo(channel, *argv, NULL, &hints, ai_callback, *argv);
182 }
183 }
184
185 /* Wait for all queries to complete. */
186 for (;;) {
187 int res;
188 FD_ZERO(&read_fds);
189 FD_ZERO(&write_fds);
190 nfds = ares_fds(channel, &read_fds, &write_fds);
191 if (nfds == 0) {
192 break;
193 }
194 tvp = ares_timeout(channel, NULL, &tv);
195 if (tvp == NULL) {
196 break;
197 }
198 res = select(nfds, &read_fds, &write_fds, NULL, tvp);
199 if (-1 == res) {
200 break;
201 }
202 ares_process(channel, &read_fds, &write_fds);
203 }
204
205 ares_destroy(channel);
206
207 ares_library_cleanup();
208
209 #ifdef USE_WINSOCK
210 WSACleanup();
211 #endif
212
213 return 0;
214 }
215
callback(void * arg,int status,int timeouts,struct hostent * host)216 static void callback(void *arg, int status, int timeouts, struct hostent *host)
217 {
218 char **p;
219
220 (void)timeouts;
221
222 if (status != ARES_SUCCESS) {
223 fprintf(stderr, "%s: %s\n", (char *)arg, ares_strerror(status));
224 return;
225 }
226
227 for (p = host->h_addr_list; *p; p++) {
228 char addr_buf[46] = "??";
229
230 ares_inet_ntop(host->h_addrtype, *p, addr_buf, sizeof(addr_buf));
231 printf("%-32s\t%s", host->h_name, addr_buf);
232 puts("");
233 }
234 }
235
ai_callback(void * arg,int status,int timeouts,struct ares_addrinfo * result)236 static void ai_callback(void *arg, int status, int timeouts,
237 struct ares_addrinfo *result)
238 {
239 struct ares_addrinfo_node *node = NULL;
240 (void)timeouts;
241
242
243 if (status != ARES_SUCCESS) {
244 fprintf(stderr, "%s: %s\n", (char *)arg, ares_strerror(status));
245 return;
246 }
247
248 for (node = result->nodes; node != NULL; node = node->ai_next) {
249 char addr_buf[64] = "";
250 const void *ptr = NULL;
251 if (node->ai_family == AF_INET) {
252 const struct sockaddr_in *in_addr =
253 (const struct sockaddr_in *)((void *)node->ai_addr);
254 ptr = &in_addr->sin_addr;
255 } else if (node->ai_family == AF_INET6) {
256 const struct sockaddr_in6 *in_addr =
257 (const struct sockaddr_in6 *)((void *)node->ai_addr);
258 ptr = &in_addr->sin6_addr;
259 } else {
260 continue;
261 }
262 ares_inet_ntop(node->ai_family, ptr, addr_buf, sizeof(addr_buf));
263 printf("%-32s\t%s\n", result->name, addr_buf);
264 }
265
266 ares_freeaddrinfo(result);
267 }
268
usage(void)269 static void usage(void)
270 {
271 fprintf(stderr, "usage: ahost [-h] [-d] [[-D {domain}] ...] [-s {server}] "
272 "[-t {a|aaaa|u}] {host|addr} ...\n");
273 exit(1);
274 }
275
276 /* Information from the man page. Formatting taken from man -h */
print_help_info_ahost(void)277 static void print_help_info_ahost(void)
278 {
279 printf("ahost, version %s\n\n", ARES_VERSION_STR);
280 printf(
281 "usage: ahost [-h] [-d] [-D domain] [-s server] [-t a|aaaa|u] host|addr "
282 "...\n\n"
283 " -h : Display this help and exit.\n"
284 " -d : Print some extra debugging output.\n\n"
285 " -D domain : Specify the domain to search instead of using the default "
286 "values\n"
287 " -s server : Connect to the specified DNS server, instead of the\n"
288 " system's default one(s). Servers are tried in round-robin,\n"
289 " if the previous one failed.\n"
290 " -t type : If type is \"a\", print the A record.\n"
291 " If type is \"aaaa\", print the AAAA record.\n"
292 " If type is \"u\" (default), print both A and AAAA records.\n"
293 "\n");
294 exit(0);
295 }
296