int main(int argc, char **argv) { ares_channel channel; int c, i, optmask = ARES_OPT_FLAGS, dnsclass = C_IN, type = T_A; int status, nfds, count; struct ares_options options; struct hostent *hostent; fd_set read_fds, write_fds; struct timeval *tvp, tv; struct ares_addr_node *srvr, *servers = NULL; #ifdef USE_WINSOCK WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK); WSADATA wsaData; WSAStartup(wVersionRequested, &wsaData); #endif status = ares_library_init(ARES_LIB_INIT_ALL); if (status != ARES_SUCCESS) { fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status)); return 1; } options.flags = ARES_FLAG_NOCHECKRESP; options.servers = NULL; options.nservers = 0; while ((c = ares_getopt(argc, argv, "df:s:c:t:T:U:")) != -1) { switch (c) { case 'd': #ifdef WATT32 dbug_init(); #endif break; case 'f': /* Add a flag. */ for (i = 0; i < nflags; i++) { if (strcmp(flags[i].name, optarg) == 0) break; } if (i < nflags) options.flags |= flags[i].value; else usage(); break; case 's': /* User-specified name servers override default ones. */ srvr = malloc(sizeof(struct ares_addr_node)); if (!srvr) { fprintf(stderr, "Out of memory!\n"); destroy_addr_list(servers); return 1; } append_addr_list(&servers, srvr); if (ares_inet_pton(AF_INET, optarg, &srvr->addr.addr4) > 0) srvr->family = AF_INET; else if (ares_inet_pton(AF_INET6, optarg, &srvr->addr.addr6) > 0) srvr->family = AF_INET6; else { hostent = gethostbyname(optarg); if (!hostent) { fprintf(stderr, "adig: server %s not found.\n", optarg); destroy_addr_list(servers); return 1; } switch (hostent->h_addrtype) { case AF_INET: srvr->family = AF_INET; memcpy(&srvr->addr.addr4, hostent->h_addr, sizeof(srvr->addr.addr4)); break; case AF_INET6: srvr->family = AF_INET6; memcpy(&srvr->addr.addr6, hostent->h_addr, sizeof(srvr->addr.addr6)); break; default: fprintf(stderr, "adig: server %s unsupported address family.\n", optarg); destroy_addr_list(servers); return 1; } } /* Notice that calling ares_init_options() without servers in the * options struct and with ARES_OPT_SERVERS set simultaneously in * the options mask, results in an initialization with no servers. * When alternative name servers have been specified these are set * later calling ares_set_servers() overriding any existing server * configuration. To prevent initial configuration with default * servers that will be discarded later, ARES_OPT_SERVERS is set. * If this flag is not set here the result shall be the same but * ares_init_options() will do needless work. */ optmask |= ARES_OPT_SERVERS; break; case 'c': /* Set the query class. */ for (i = 0; i < nclasses; i++) { if (strcasecmp(classes[i].name, optarg) == 0) break; } if (i < nclasses) dnsclass = classes[i].value; else usage(); break; case 't': /* Set the query type. */ for (i = 0; i < ntypes; i++) { if (strcasecmp(types[i].name, optarg) == 0) break; } if (i < ntypes) type = types[i].value; else usage(); break; case 'T': /* Set the TCP port number. */ if (!ISDIGIT(*optarg)) usage(); options.tcp_port = (unsigned short)strtol(optarg, NULL, 0); optmask |= ARES_OPT_TCP_PORT; break; case 'U': /* Set the UDP port number. */ if (!ISDIGIT(*optarg)) usage(); options.udp_port = (unsigned short)strtol(optarg, NULL, 0); optmask |= ARES_OPT_UDP_PORT; break; } } argc -= optind; argv += optind; if (argc == 0) usage(); status = ares_init_options(&channel, &options, optmask); if (status != ARES_SUCCESS) { fprintf(stderr, "ares_init_options: %s\n", ares_strerror(status)); return 1; } if(servers) { status = ares_set_servers(channel, servers); destroy_addr_list(servers); if (status != ARES_SUCCESS) { fprintf(stderr, "ares_init_options: %s\n", ares_strerror(status)); return 1; } } /* Initiate the queries, one per command-line argument. If there is * only one query to do, supply NULL as the callback argument; * otherwise, supply the query name as an argument so we can * distinguish responses for the user when printing them out. */ if (argc == 1) ares_query(channel, *argv, dnsclass, type, callback, (char *) NULL); else { for (; *argv; argv++) ares_query(channel, *argv, dnsclass, type, callback, *argv); } /* Wait for all queries to complete. */ for (;;) { FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(channel, &read_fds, &write_fds); if (nfds == 0) break; tvp = ares_timeout(channel, NULL, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); if (count < 0 && SOCKERRNO != EINVAL) { perror("select"); return 1; } ares_process(channel, &read_fds, &write_fds); } ares_destroy(channel); ares_library_cleanup(); #ifdef USE_WINSOCK WSACleanup(); #endif return 0; }
int dns_start_query(myconn_t *rec, char *targetserver) { struct ares_addr_node *srvr, *servers = NULL; struct ares_options options; int status, optmask; char *tdup, *tst; /* See what IP family we must use to communicate with the target DNS server */ srvr = malloc(sizeof(struct ares_addr_node)); append_addr_list(&servers, srvr); srvr->family = -1; #ifdef IPV4_SUPPORT if ((srvr->family == -1) && (inet_pton(AF_INET, targetserver, &srvr->addr.addr4) > 0)) srvr->family = AF_INET; #endif #ifdef IPV6_SUPPORT if ((srvr->family == -1) && (inet_pton(AF_INET6, targetserver, &srvr->addr.addr6) > 0)) srvr->family = AF_INET6; #endif if (srvr->family == -1) { errprintf("Unsupported IP family for DNS target IP %s, test %s\n", targetserver, rec->testspec); return 0; } /* Create a new ARES request channel */ rec->dnschannel = malloc(sizeof(ares_channel)); /* * The C-ARES timeout handling is a bit complicated. The timeout setting * here in the options only determines the timeout for the first query; * subsequent queries (up to the "tries" count) use a progressively * higher timeout setting. * So we cannot easily determine what combination of timeout/tries will * result in the full query timing out after the desired number of seconds. * Therefore, use a fixed set of values - the 2000 ms / 4 tries combination * results in a timeout after 23-24 seconds. */ optmask = ARES_OPT_FLAGS | ARES_OPT_SERVERS | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES; options.flags = ARES_FLAG_NOCHECKRESP; options.servers = NULL; options.nservers = 0; options.timeout = 2000; options.tries = 4; status = ares_init_options(rec->dnschannel, &options, optmask); if (status != ARES_SUCCESS) { errprintf("Cannot create ARES channel for DNS target %s, test %s\n", targetserver, rec->testspec); rec->dnsstatus = DNS_FINISHED; return 0; } /* Point the channel at the target server */ status = ares_set_servers(*((ares_channel *)rec->dnschannel), servers); destroy_addr_list(servers); if (status != ARES_SUCCESS) { errprintf("Cannot select ARES target DNS server %s, test %s\n", targetserver, rec->testspec); rec->dnsstatus = DNS_QUERY_COMPLETED; /* To reap the ARES channel later */ return 0; } /* Post the queries we want to perform */ tdup = strdup(rec->testspec); tst = strtok(tdup, ","); do { char *p, *tlookup; int atype = dns_atype; p = strchr(tst, ':'); tlookup = (p ? p+1 : tst); if (p) { *p = '\0'; atype = dns_name_type(tst); *p = ':'; } /* Use ares_query() here, since we dont want to get results from hosts file or other odd stuff. */ ares_query(*((ares_channel *)rec->dnschannel), tlookup, dns_aclass, atype, dns_query_callback, rec); tst = strtok(NULL, ","); } while (tst); xfree(tdup); rec->textlog = newstrbuffer(0); getntimer(&rec->dnsstarttime); rec->dnsstatus = DNS_QUERY_ACTIVE; return 1; }