From 44c59a91c3f1c6a95592ce9ed92bb1bd3ac5838e Mon Sep 17 00:00:00 2001 From: Brad House Date: Mon, 18 Dec 2023 10:54:13 -0500 Subject: [PATCH] ahost should use ares_getaddrinfo() these days (#669) ahost wasn't printing both ipv4 and ipv6 addresses. This day and age, it really should. This PR also adds the ability to specify the servers to use. Fix By: Brad House (@bradh352) --- docs/ahost.1 | 9 ++-- src/tools/ahost.c | 112 +++++++++++++++++++++++++++++++++------------- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/docs/ahost.1 b/docs/ahost.1 index 3cad8b77..19bde6b4 100644 --- a/docs/ahost.1 +++ b/docs/ahost.1 @@ -39,11 +39,14 @@ Print some extra debugging output. Display this help and exit. .TP \fB\-t\fR type -If type is "a", print the A record (default). +If type is "a", print the A record. If type is "aaaa", print the AAAA record. -If type is "u", look for either AAAA or A record (in that order). +If type is "u", look for both AAAA and A records (default). .TP -\fB\-s\fR \fIdomain\fP +\fB\-s\fR server +Set the server list to use for DNS lookups. +.TP +\fB\-D\fR \fIdomain\fP Specify the \fIdomain\fP to search instead of using the default values from .br /etc/resolv.conf. This option only has an effect on platforms that use diff --git a/src/tools/ahost.c b/src/tools/ahost.c index 1f293fc6..113a639c 100644 --- a/src/tools/ahost.c +++ b/src/tools/ahost.c @@ -58,6 +58,8 @@ #endif static void callback(void *arg, int status, int timeouts, struct hostent *host); +static void ai_callback(void *arg, int status, int timeouts, + struct ares_addrinfo *result); static void usage(void); static void print_help_info_ahost(void); @@ -69,7 +71,7 @@ int main(int argc, char **argv) int status; int nfds; int c; - int addr_family = AF_INET; + int addr_family = AF_UNSPEC; fd_set read_fds; fd_set write_fds; struct timeval *tvp; @@ -77,6 +79,7 @@ int main(int argc, char **argv) struct in_addr addr4; struct ares_in6_addr addr6; ares_getopt_state_t state; + char *servers = NULL; #ifdef USE_WINSOCK WORD wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); @@ -93,14 +96,14 @@ int main(int argc, char **argv) } ares_getopt_init(&state, argc, (const char **)argv); - while ((c = ares_getopt(&state, "dt:h?s:")) != -1) { + while ((c = ares_getopt(&state, "dt:h?D:s:")) != -1) { switch (c) { case 'd': #ifdef WATT32 dbug_init(); #endif break; - case 's': + case 'D': optmask |= ARES_OPT_DOMAINS; options.ndomains++; options.domains = (char **)realloc( @@ -118,9 +121,18 @@ int main(int argc, char **argv) usage(); } break; - case 'h': - print_help_info_ahost(); + case 's': + if (state.optarg == NULL) { + fprintf(stderr, "%s", "missing servers"); + usage(); + break; + } + if (servers) { + free(servers); + } + servers = strdup(state.optarg); break; + case 'h': case '?': print_help_info_ahost(); break; @@ -138,10 +150,22 @@ int main(int argc, char **argv) status = ares_init_options(&channel, &options, optmask); if (status != ARES_SUCCESS) { + free(servers); fprintf(stderr, "ares_init: %s\n", ares_strerror(status)); return 1; } + if (servers) { + status = ares_set_servers_csv(channel, servers); + if (status != ARES_SUCCESS) { + fprintf(stderr, "ares_set_serveres_csv: %s\n", ares_strerror(status)); + free(servers); + usage(); + return 1; + } + free(servers); + } + /* Initiate the queries, one per command-line argument. */ for (; *argv; argv++) { if (ares_inet_pton(AF_INET, *argv, &addr4) == 1) { @@ -151,7 +175,10 @@ int main(int argc, char **argv) ares_gethostbyaddr(channel, &addr6, sizeof(addr6), AF_INET6, callback, *argv); } else { - ares_gethostbyname(channel, *argv, addr_family, callback, *argv); + struct ares_addrinfo_hints hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = addr_family; + ares_getaddrinfo(channel, *argv, NULL, &hints, ai_callback, *argv); } } @@ -202,25 +229,47 @@ static void callback(void *arg, int status, int timeouts, struct hostent *host) ares_inet_ntop(host->h_addrtype, *p, addr_buf, sizeof(addr_buf)); printf("%-32s\t%s", host->h_name, addr_buf); -#if 0 - if (host->h_aliases[0]) - { - int i; - - printf (", Aliases: "); - for (i = 0; host->h_aliases[i]; i++) - printf("%s ", host->h_aliases[i]); - } -#endif puts(""); } } +static void ai_callback(void *arg, int status, int timeouts, + struct ares_addrinfo *result) +{ + struct ares_addrinfo_node *node = NULL; + (void)timeouts; + + + if (status != ARES_SUCCESS) { + fprintf(stderr, "%s: %s\n", (char *)arg, ares_strerror(status)); + return; + } + + for (node = result->nodes; node != NULL; node = node->ai_next) { + char addr_buf[64] = ""; + const void *ptr = NULL; + if (node->ai_family == AF_INET) { + struct sockaddr_in *in_addr = + (struct sockaddr_in *)((void *)node->ai_addr); + ptr = &in_addr->sin_addr; + } else if (node->ai_family == AF_INET6) { + struct sockaddr_in6 *in_addr = + (struct sockaddr_in6 *)((void *)node->ai_addr); + ptr = &in_addr->sin6_addr; + } else { + continue; + } + ares_inet_ntop(node->ai_family, ptr, addr_buf, sizeof(addr_buf)); + printf("%-32s\t%s\n", result->name, addr_buf); + } + + ares_freeaddrinfo(result); +} + static void usage(void) { - fprintf( - stderr, - "usage: ahost [-h] [-d] [-s {domain}] [-t {a|aaaa|u}] {host|addr} ...\n"); + fprintf(stderr, "usage: ahost [-h] [-d] [[-D {domain}] ...] [-s {server}] " + "[-t {a|aaaa|u}] {host|addr} ...\n"); exit(1); } @@ -229,19 +278,18 @@ static void print_help_info_ahost(void) { printf("ahost, version %s\n\n", ARES_VERSION_STR); printf( - "usage: ahost [-h] [-d] [[-s domain] ...] [-t a|aaaa|u] host|addr ...\n\n" - " h : Display this help and exit.\n" - " d : Print some extra debugging output.\n\n" - " s domain : Specify the domain to search instead of using the default " + "usage: ahost [-h] [-d] [-D domain] [-s server] [-t a|aaaa|u] host|addr " + "...\n\n" + " -h : Display this help and exit.\n" + " -d : Print some extra debugging output.\n\n" + " -D domain : Specify the domain to search instead of using the default " "values\n" - " from /etc/resolv.conf. This option only has an effect on\n" - " platforms that use /etc/resolv.conf for DNS " - "configuration;\n" - " it has no effect on other platforms (such as Win32 or " - "Android).\n\n" - " t type : If type is \"a\", print the A record (default).\n" - " If type is \"aaaa\", print the AAAA record.\n" - " If type is \"u\", look for either AAAA or A record (in " - "that order).\n\n"); + " -s server : Connect to the specified DNS server, instead of the\n" + " system's default one(s). Servers are tried in round-robin,\n" + " if the previous one failed.\n" + " -t type : If type is \"a\", print the A record.\n" + " If type is \"aaaa\", print the AAAA record.\n" + " If type is \"u\" (default), print both A and AAAA records.\n" + "\n"); exit(0); }