• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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