/* * Curl_resolver_is_resolved() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if * the operation seems to take too long. * * Returns normal CURLcode errors. */ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dns) { struct SessionHandle *data = conn->data; struct ResolverResults *res = (struct ResolverResults *) conn->async.os_specific; *dns = NULL; waitperform(conn, 0); if(res && !res->num_pending) { (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); /* temp_ai ownership is moved to the connection, so we need not free-up them */ res->temp_ai = NULL; destroy_async_data(&conn->async); if(!conn->async.dns) { failf(data, "Could not resolve %s: %s (%s)", conn->bits.proxy?"proxy":"host", conn->host.dispname, ares_strerror(conn->async.status)); return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } *dns = conn->async.dns; } return CURLE_OK; }
/* * Curl_resolver_is_resolved() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if * the operation seems to take too long. * * Returns normal CURLcode errors. */ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dns) { struct Curl_easy *data = conn->data; struct ResolverResults *res = (struct ResolverResults *) conn->async.os_specific; CURLcode result = CURLE_OK; if(dns) *dns = NULL; waitperform(conn, 0); /* Now that we've checked for any last minute results above, see if there are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer expires. */ if(res && res->num_pending /* This is only set to non-zero if the timer was started. */ && (res->happy_eyeballs_dns_time.tv_sec || res->happy_eyeballs_dns_time.tv_usec) && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time) >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer running. */ memset( &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time)); /* Cancel the raw c-ares request, which will fire query_completed_cb() with ARES_ECANCELLED synchronously for all pending responses. This will leave us with res->num_pending == 0, which is perfect for the next block. */ ares_cancel((ares_channel)data->state.resolver); DEBUGASSERT(res->num_pending == 0); } if(res && !res->num_pending) { if(dns) { (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); /* temp_ai ownership is moved to the connection, so we need not free-up them */ res->temp_ai = NULL; } if(!conn->async.dns) { failf(data, "Could not resolve: %s (%s)", conn->async.hostname, ares_strerror(conn->async.status)); result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } else if(dns) *dns = conn->async.dns; destroy_async_data(&conn->async); } return result; }
/* * Curl_is_resolved() is called repeatedly to check if a previous name resolve * request has completed. It should also make sure to time-out if the * operation seems to take too long. * * Returns normal CURLcode errors. */ CURLcode Curl_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dns) { struct SessionHandle *data = conn->data; *dns = NULL; waitperform(conn, 0); if(conn->async.done) { /* we're done, kill the ares handle */ if(!conn->async.dns) { failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, ares_strerror(conn->async.status)); return CURLE_COULDNT_RESOLVE_HOST; } *dns = conn->async.dns; } return CURLE_OK; }
/* * 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; }