int Curl_resolver_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks) { struct timeval maxtime; struct timeval timebuf; struct timeval *timeout; long milli; int max = ares_getsock((ares_channel)conn->data->state.resolver, (ares_socket_t *)socks, numsocks); maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; maxtime.tv_usec = 0; timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime, &timebuf); milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); if(milli == 0) milli += 10; Curl_expire(conn->data, milli); return max; }
/* * Curl_resolver_wait_resolv() * * waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and * CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; long timeout; struct timeval now = Curl_tvnow(); struct Curl_dns_entry *temp_entry; timeout = Curl_timeleft(data, &now, TRUE); if(!timeout) timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ /* Wait for the name resolve query to complete. */ for(;;) { struct timeval *tvp, tv, store; long timediff; int itimeout; int timeout_ms; itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; store.tv_sec = itimeout/1000; store.tv_usec = (itimeout%1000)*1000; tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv); /* use the timeout period ares returned to us above if less than one second is left, otherwise just use 1000ms to make sure the progress callback gets called frequent enough */ if(!tvp->tv_sec) timeout_ms = (int)(tvp->tv_usec/1000); else timeout_ms = 1000; waitperform(conn, timeout_ms); Curl_resolver_is_resolved(conn,&temp_entry); if(conn->async.done) break; if(Curl_pgrsUpdate(conn)) { rc = CURLE_ABORTED_BY_CALLBACK; timeout = -1; /* trigger the cancel below */ } else { struct timeval now2 = Curl_tvnow(); timediff = Curl_tvdiff(now2, now); /* spent time */ timeout -= timediff?timediff:1; /* always deduct at least 1 */ now = now2; /* for next loop */ } if(timeout < 0) { /* our timeout, so we cancel the ares operation */ ares_cancel((ares_channel)data->state.resolver); break; } } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) { if(conn->bits.proxy) { failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname); rc = CURLE_COULDNT_RESOLVE_PROXY; } else { failf(data, "Resolving host timed out: %s", conn->host.dispname); rc = CURLE_COULDNT_RESOLVE_HOST; } } else if(conn->async.done) { if(conn->bits.proxy) { failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_PROXY; } else { failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } } else rc = CURLE_OPERATION_TIMEDOUT; /* close the connection, since we can't return failure here without cleaning up this connection properly */ conn->bits.close = TRUE; } return rc; }
/* * Curl_resolver_wait_resolv() * * waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and * CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; long timeout; struct timeval now = Curl_tvnow(); struct Curl_dns_entry *temp_entry; if(entry) *entry = NULL; /* clear on entry */ timeout = Curl_timeleft(data, &now, TRUE); if(timeout < 0) { /* already expired! */ connclose(conn, "Timed out before name resolve started"); return CURLE_OPERATION_TIMEDOUT; } if(!timeout) timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ /* Wait for the name resolve query to complete. */ while(!result) { struct timeval *tvp, tv, store; long timediff; int itimeout; int timeout_ms; itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; store.tv_sec = itimeout/1000; store.tv_usec = (itimeout%1000)*1000; tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv); /* use the timeout period ares returned to us above if less than one second is left, otherwise just use 1000ms to make sure the progress callback gets called frequent enough */ if(!tvp->tv_sec) timeout_ms = (int)(tvp->tv_usec/1000); else timeout_ms = 1000; waitperform(conn, timeout_ms); result = Curl_resolver_is_resolved(conn, &temp_entry); if(result || conn->async.done) break; if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; else { struct timeval now2 = Curl_tvnow(); timediff = Curl_tvdiff(now2, now); /* spent time */ timeout -= timediff?timediff:1; /* always deduct at least 1 */ now = now2; /* for next loop */ } if(timeout < 0) result = CURLE_OPERATION_TIMEDOUT; } if(result) /* failure, so we cancel the ares operation */ ares_cancel((ares_channel)data->state.resolver); /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(result) /* close the connection, since we can't return failure here without cleaning up this connection properly. TODO: remove this action from here, it is not a name resolver decision. */ connclose(conn, "c-ares resolve failed"); return result; }
int gethostbyaddr_with_ares(const void *addr, int addrlen, int af, struct hostent *ret_h, char *buf, size_t &buflen, struct hostent **result, int *h_errnop) { int err = 0; if (!h_errnop) h_errnop = &err; cares_async_context ctx; ctx.called = false; ctx.errnop = h_errnop; ctx.ret = 0; ctx.result = result; ctx.buf = buf; ctx.buflen = &buflen; ares_channel c; ares_init(&c); ares_gethostbyaddr(c, addr, addrlen, af, [](void * arg, int status, int timeouts, struct hostent *hostent) { cares_async_context *ctx = (cares_async_context *)arg; ctx->called = true; if (status != ARES_SUCCESS) { *ctx->result = nullptr; switch (status) { case ARES_ENODATA: *ctx->errnop = NO_DATA; break; case ARES_EBADNAME: *ctx->errnop = NO_RECOVERY; break; case ARES_ENOTFOUND: default: *ctx->errnop = HOST_NOT_FOUND; break; } ctx->ret = -1; return ; } if (!hostent_dup(hostent, *ctx->result, ctx->buf, *ctx->buflen)) { *ctx->result = nullptr; *ctx->errnop = ENOEXEC; // 奇怪的错误码. ctx->ret = -1; return ; } *ctx->errnop = 0; ctx->ret = 0; }, &ctx); fd_set readers, writers; FD_ZERO(&readers); FD_ZERO(&writers); int nfds = ares_fds(c, &readers, &writers); if (nfds) { struct timeval tv, *tvp; tvp = ares_timeout(c, NULL, &tv); int count = select(nfds, &readers, &writers, NULL, tvp); if (count > 0) { ares_process(c, &readers, &writers); } } if (ctx.called) return ctx.ret; // No yet invoke callback. *result = nullptr; *h_errnop = HOST_NOT_FOUND; // TODO return -1; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and * CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; long timeout; struct timeval now = Curl_tvnow(); /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ if(conn->data->set.connecttimeout) timeout = conn->data->set.connecttimeout; else if(conn->data->set.timeout) timeout = conn->data->set.timeout; else timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ /* Wait for the name resolve query to complete. */ while(1) { struct timeval *tvp, tv, store; long timediff; int itimeout; itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; store.tv_sec = itimeout/1000; store.tv_usec = (itimeout%1000)*1000; tvp = ares_timeout(data->state.areschannel, &store, &tv); /* use the timeout period ares returned to us above */ ares_waitperform(conn, (int)(tvp->tv_sec * 1000 + tvp->tv_usec/1000)); if(conn->async.done) break; timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */ timeout -= timediff?timediff:1; /* always deduct at least 1 */ if(timeout < 0) { /* our timeout, so we cancel the ares operation */ ares_cancel(data->state.areschannel); break; } } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) { failf(data, "Resolving host timed out: %s", conn->host.dispname); rc = CURLE_COULDNT_RESOLVE_HOST; } else if(conn->async.done) { failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; /* close the connection, since we can't return failure here without cleaning up this connection properly */ conn->bits.close = TRUE; } return rc; }
/* This is a function that locks and waits until the name resolve operation has completed. If 'entry' is non-NULL, make it point to the resolved dns entry Return CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; struct timeval now = Curl_tvnow(); bool timedout = FALSE; long timeout = 300; /* default name resolve timeout in seconds */ long elapsed = 0; /* time taken so far */ /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ if(conn->data->set.connecttimeout) timeout = conn->data->set.connecttimeout; else if(conn->data->set.timeout) timeout = conn->data->set.timeout; /* Wait for the name resolve query to complete. */ while (1) { int nfds=0; fd_set read_fds, write_fds; struct timeval *tvp, tv, store; int count; store.tv_sec = (int)(timeout - elapsed); store.tv_usec = 0; FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds); if (nfds == 0) break; tvp = ares_timeout(data->state.areschannel, &store, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); if (count < 0 && errno != EINVAL) break; else if(!count) { /* timeout */ timedout = TRUE; break; } ares_process(data->state.areschannel, &read_fds, &write_fds); elapsed = Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */ } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if(timedout || (conn->async.status == ARES_ETIMEOUT)) { failf(data, "Resolving host timed out: %s", conn->name); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s (%s)", conn->name, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; /* close the connection, since we can't return failure here without cleaning up this connection properly */ Curl_disconnect(conn); } return rc; }
int main(int argc, char **argv) { struct ares_options options; int optmask = 0; ares_channel channel; int status, nfds, c, addr_family = AF_INET; fd_set read_fds, write_fds; struct timeval *tvp, tv; struct in_addr addr4; struct ares_in6_addr addr6; #ifdef USE_WINSOCK WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK); WSADATA wsaData; WSAStartup(wVersionRequested, &wsaData); #endif memset(&options, 0, sizeof(options)); status = ares_library_init(ARES_LIB_INIT_ALL); if (status != ARES_SUCCESS) { fprintf(stderr, "ares_library_init: %s\n", ares_strerror(status)); return 1; } while ((c = ares_getopt(argc,argv,"dt:hs:")) != -1) { switch (c) { case 'd': #ifdef WATT32 dbug_init(); #endif break; case 's': optmask |= ARES_OPT_DOMAINS; options.ndomains++; options.domains = realloc(options.domains, options.ndomains * sizeof(char *)); options.domains[options.ndomains - 1] = strdup(optarg); break; case 't': if (!strcasecmp(optarg,"a")) addr_family = AF_INET; else if (!strcasecmp(optarg,"aaaa")) addr_family = AF_INET6; else if (!strcasecmp(optarg,"u")) addr_family = AF_UNSPEC; else usage(); break; case 'h': default: usage(); break; } } argc -= optind; argv += optind; if (argc < 1) usage(); status = ares_init_options(&channel, &options, optmask); if (status != ARES_SUCCESS) { fprintf(stderr, "ares_init: %s\n", ares_strerror(status)); return 1; } /* Initiate the queries, one per command-line argument. */ for ( ; *argv; argv++) { if (ares_inet_pton(AF_INET, *argv, &addr4) == 1) { ares_gethostbyaddr(channel, &addr4, sizeof(addr4), AF_INET, callback, *argv); } else if (ares_inet_pton(AF_INET6, *argv, &addr6) == 1) { ares_gethostbyaddr(channel, &addr6, sizeof(addr6), AF_INET6, callback, *argv); } else { ares_gethostbyname(channel, *argv, addr_family, callback, *argv); } } /* Wait for all queries to complete. */ for (;;) { int res; 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); res = select(nfds, &read_fds, &write_fds, NULL, tvp); if (-1 == res) break; ares_process(channel, &read_fds, &write_fds); } ares_destroy(channel); ares_library_cleanup(); #ifdef USE_WINSOCK WSACleanup(); #endif return 0; }
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; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and * CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; long timeout = CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ if(conn->data->set.connecttimeout) timeout = conn->data->set.connecttimeout; else if(conn->data->set.timeout) timeout = conn->data->set.timeout; /* We convert the number of seconds into number of milliseconds here: */ if(timeout < 2147483) /* maximum amount of seconds that can be multiplied with 1000 and still fit within 31 bits */ timeout *= 1000; else timeout = 0x7fffffff; /* ridiculous amount of time anyway */ /* Wait for the name resolve query to complete. */ while (1) { int nfds=0; fd_set read_fds, write_fds; struct timeval *tvp, tv, store; int count; struct timeval now = Curl_tvnow(); long timediff; store.tv_sec = (int)timeout/1000; store.tv_usec = (timeout%1000)*1000; FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds); if (nfds == 0) /* no file descriptors means we're done waiting */ break; tvp = ares_timeout(data->state.areschannel, &store, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); if (count < 0 && errno != EINVAL) break; ares_process(data->state.areschannel, &read_fds, &write_fds); timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */ timeout -= timediff?timediff:1; /* always deduct at least 1 */ if (timeout < 0) { /* our timeout, so we cancel the ares operation */ ares_cancel(data->state.areschannel); break; } } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) { failf(data, "Resolving host timed out: %s", conn->host.dispname); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; /* close the connection, since we can't return failure here without cleaning up this connection properly */ conn->bits.close = TRUE; } return rc; }
void DNSHandler::worker () { // FD sets used for selecting fd_set readable; fd_set writeable; // Maximum file descriptor to pass // to select int nfds; // The timeout for select struct timeval tv; // Loop until shutdown for (;;) { // Zero FDs FD_ZERO(&readable); FD_ZERO(&writeable); if (lock.Execute([&] () mutable { // Wait for there to be something actionable, // either pending queries, or a shutdown command while (!stop && (queries.size()==0)) wait.Sleep(lock); // If the command to shutdown has been given, // do that at once if (stop) return true; // Get the file descriptors from libcares nfds=ares_fds( channel, &readable, &writeable ); // Get the timeout from libcares ares_timeout( channel, nullptr, &tv ); return false; })) break; // If there's a message pending on // the worker end of the socket pair, // we want to be woken up, so // If there's a message pending // on the worker end of the socket // pair, we want to be woken up, // so we're checking for readability nfds=control.Add(readable,nfds); // Wait for something to happen if (select( nfds, &readable, &writeable, nullptr, &tv )== #ifdef ENVIRONMENT_WINDOWS SOCKET_ERROR #else -1 #endif ) raise_os(); // Did something happen with the // control socket? if (control.Is(readable)) { // Check to see if a shutdown command // is coming through the control socket, // if so end at once if (should_stop()) break; // Remove it in case it matters to // libcares what's in the fd_set control.Clear(readable); } // Call libcares lock.Execute([&] () mutable { ares_process( channel, &readable, &writeable ); }); } }
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; #ifdef USE_WINSOCK WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK); WSADATA wsaData; WSAStartup(wVersionRequested, &wsaData); #endif 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) usage(); options.flags |= flags[i].value; break; case 's': /* Add a server, and specify servers in the option mask. */ if (ares_inet_pton(AF_INET, optarg, &inaddr) <= 0) { hostent = gethostbyname(optarg); if (!hostent || hostent->h_addrtype != AF_INET) { fprintf(stderr, "adig: server %s not found.\n", optarg); return 1; } memcpy(&inaddr, hostent->h_addr, sizeof(struct in_addr)); } options.servers = realloc(options.servers, (options.nservers + 1) * sizeof(struct in_addr)); if (!options.servers) { fprintf(stderr, "Out of memory!\n"); return 1; } memcpy(&options.servers[options.nservers], &inaddr, sizeof(struct in_addr)); options.nservers++; 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) usage(); dnsclass = classes[i].value; break; case 't': /* Set the query type. */ for (i = 0; i < ntypes; i++) { if (strcasecmp(types[i].name, optarg) == 0) break; } if (i == ntypes) usage(); type = types[i].value; 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; } /* 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. */ while (1) { 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); #ifdef USE_WINSOCK WSACleanup(); #endif return 0; }
void mux_loop(void) { int i, nfds; fd_set inputs, outputs; struct timeval tv, *tvp; mux_end_loop_p = 0; for (;;) { /* * Exit if mux_end_loop_p has been set to true by a handler: */ if (mux_end_loop_p) break; tvp = NULL; tv.tv_sec = 0; if (have_tty) { #ifdef CMU_ZWGCPLUS tv.tv_sec = plus_timequeue_events(); if (tv.tv_sec > 10) tv.tv_sec = 10; #else tv.tv_sec = 10; #endif tv.tv_usec = 0; #ifdef CMU_ZWGCPLUS } else { tv.tv_sec = plus_timequeue_events(); tv.tv_usec = 0; #endif } if (tv.tv_sec) tvp = &tv; /* * Do a select on all the file descriptors we care about to * wait until at least one of them has input available: */ inputs = input_sources; FD_ZERO(&outputs); #ifdef HAVE_ARES nfds = ares_fds(achannel, &inputs, &outputs); if (nfds < max_source + 1) nfds = max_source + 1; tvp = ares_timeout(achannel, tvp, &tv); #else nfds = max_source + 1; #endif i = select(nfds, &inputs, &outputs, NULL, tvp); if (i == -1) { if (errno == EINTR) continue; /* on a signal restart checking mux_loop_end_p */ else FATAL_TRAP( errno, "while selecting" ); } else if (i == 0) { if (have_tty && !check_tty()) { mux_end_loop_p = 1; continue; } } #ifdef HAVE_ARES ares_process(achannel, &inputs, &outputs); #endif /* * Call all input handlers whose corresponding file descriptors have * input: */ for(i=0; i<=max_source; i++) if (FD_ISSET(i, &inputs) && input_handler[i]) { #ifdef DEBUG if (zwgc_debug) fprintf(stderr, "mux_loop...activity on fd %d, calling %lx(%lx)\n", i, (unsigned long)input_handler[i], (unsigned long)input_handler_arg[i]); #endif input_handler[i](input_handler_arg[i]); } } }
/// Waits for replies to outstanding DNS queries on the specified channel. void DnsCachedResolver::wait_for_replies(DnsChannel* channel) { // Wait until the expected number of results has been returned. while (channel->pending_queries > 0) { // Call into ares to get details of the sockets it's using. ares_socket_t scks[ARES_GETSOCK_MAXNUM]; int rw_bits = ares_getsock(channel->channel, scks, ARES_GETSOCK_MAXNUM); // Translate these sockets into pollfd structures. int num_fds = 0; struct pollfd fds[ARES_GETSOCK_MAXNUM]; for (int fd_idx = 0; fd_idx < ARES_GETSOCK_MAXNUM; fd_idx++) { struct pollfd* fd = &fds[fd_idx]; fd->fd = scks[fd_idx]; fd->events = 0; fd->revents = 0; if (ARES_GETSOCK_READABLE(rw_bits, fd_idx)) { fd->events |= POLLRDNORM | POLLIN; } if (ARES_GETSOCK_WRITABLE(rw_bits, fd_idx)) { fd->events |= POLLWRNORM | POLLOUT; } if (fd->events != 0) { num_fds++; } } // Calculate the timeout. struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; (void)ares_timeout(channel->channel, NULL, &tv); // Wait for events on these file descriptors. if (poll(fds, num_fds, tv.tv_sec * 1000 + tv.tv_usec / 1000) != 0) { // We got at least one event, so find which file descriptor(s) this was on. for (int fd_idx = 0; fd_idx < num_fds; fd_idx++) { struct pollfd* fd = &fds[fd_idx]; if (fd->revents != 0) { // Call into ares to notify it of the event. The interface requires // that we pass separate file descriptors for read and write events // or ARES_SOCKET_BAD if no event has occurred. ares_process_fd(channel->channel, fd->revents & (POLLRDNORM | POLLIN) ? fd->fd : ARES_SOCKET_BAD, fd->revents & (POLLWRNORM | POLLOUT) ? fd->fd : ARES_SOCKET_BAD); } } } else { // No events, so just call into ares with no file descriptor to let it handle timeouts. ares_process_fd(channel->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); } } }