#include "setup.h"
#include <string.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#include <stdlib.h>
#endif
#ifdef HAVE_PROCESS_H
#include <process.h>
#endif
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
#undef in_addr_t
#define in_addr_t unsigned long
#endif
#include "urldata.h"
#include "sendf.h"
#include "hostip.h"
#include "hash.h"
#include "share.h"
#include "strerror.h"
#include "url.h"
#include "multiif.h"
#include "inet_pton.h"
#include "connect.h"
#include "select.h"
#include "progress.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "curl_memory.h"
#include "memdebug.h"
#ifdef CURLRES_ARES
int Curl_resolv_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
struct timeval maxtime;
struct timeval timebuf;
struct timeval *timeout;
int max = ares_getsock(conn->data->state.areschannel,
(ares_socket_t *)socks, numsocks);
maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
maxtime.tv_usec = 0;
timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
Curl_expire(conn->data,
(timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
return max;
}
static int waitperform(struct connectdata *conn, int timeout_ms)
{
struct SessionHandle *data = conn->data;
int nfds;
int bitmask;
ares_socket_t socks[ARES_GETSOCK_MAXNUM];
struct pollfd pfd[ARES_GETSOCK_MAXNUM];
int i;
int num = 0;
bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
pfd[i].events = 0;
pfd[i].revents = 0;
if(ARES_GETSOCK_READABLE(bitmask, i)) {
pfd[i].fd = socks[i];
pfd[i].events |= POLLRDNORM|POLLIN;
}
if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
pfd[i].fd = socks[i];
pfd[i].events |= POLLWRNORM|POLLOUT;
}
if(pfd[i].events != 0)
num++;
else
break;
}
if(num)
nfds = Curl_poll(pfd, num, timeout_ms);
else
nfds = 0;
if(!nfds)
ares_process_fd(data->state.areschannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
else {
for(i=0; i < num; i++)
ares_process_fd(data->state.areschannel,
pfd[i].revents & (POLLRDNORM|POLLIN)?
pfd[i].fd:ARES_SOCKET_BAD,
pfd[i].revents & (POLLWRNORM|POLLOUT)?
pfd[i].fd:ARES_SOCKET_BAD);
}
return nfds;
}
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) {
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;
}
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();
timeout = Curl_timeleft(data, &now, TRUE);
if(!timeout)
timeout = CURL_TIMEOUT_RESOLVE * 1000;
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(data->state.areschannel, &store, &tv);
if(!tvp->tv_sec)
timeout_ms = (int)(tvp->tv_usec/1000);
else
timeout_ms = 1000;
waitperform(conn, timeout_ms);
if(conn->async.done)
break;
if(Curl_pgrsUpdate(conn)) {
rc = CURLE_ABORTED_BY_CALLBACK;
timeout = -1;
}
else {
struct timeval now2 = Curl_tvnow();
timediff = Curl_tvdiff(now2, now);
timeout -= timediff?timediff:1;
now = now2;
}
if(timeout < 0) {
ares_cancel(data->state.areschannel);
break;
}
}
if(entry)
*entry = conn->async.dns;
if(!conn->async.dns) {
if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
if (conn->bits.httpproxy) {
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.httpproxy) {
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;
conn->bits.close = TRUE;
}
return rc;
}
static void ares_query_completed_cb(void *arg,
int status,
#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
int timeouts,
#endif
struct hostent *hostent)
{
struct connectdata *conn = (struct connectdata *)arg;
struct Curl_addrinfo * ai = NULL;
#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
(void)timeouts;
#endif
switch(status) {
case CURL_ASYNC_SUCCESS:
ai = Curl_he2ai(hostent, conn->async.port);
break;
case ARES_EDESTRUCTION:
return;
default:
break;
}
(void)Curl_addrinfo_callback(arg, status, ai);
}
Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
const char *hostname,
int port,
int *waitp)
{
char *bufp;
struct SessionHandle *data = conn->data;
struct in_addr in;
int family = PF_INET;
#ifdef ENABLE_IPV6
struct in6_addr in6;
#endif
*waitp = 0;
if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
return Curl_ip2addr(AF_INET, &in, hostname, port);
}
#ifdef ENABLE_IPV6
if (Curl_inet_pton (AF_INET6, hostname, &in6) > 0) {
return Curl_ip2addr(AF_INET6, &in6, hostname, port);
}
switch(conn->ip_version) {
default:
#if ARES_VERSION >= 0x010601
family = PF_UNSPEC;
break;
#endif
case CURL_IPRESOLVE_V4:
family = PF_INET;
break;
case CURL_IPRESOLVE_V6:
family = PF_INET6;
break;
}
#endif
bufp = strdup(hostname);
if(bufp) {
Curl_safefree(conn->async.hostname);
conn->async.hostname = bufp;
conn->async.port = port;
conn->async.done = FALSE;
conn->async.status = 0;
conn->async.dns = NULL;
conn->async.temp_ai = NULL;
#ifdef ENABLE_IPV6
if(family == PF_UNSPEC) {
conn->async.num_pending = 2;
ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
ares_query_completed_cb, conn);
ares_gethostbyname(data->state.areschannel, hostname, PF_INET6,
ares_query_completed_cb, conn);
}
else
#endif
{
conn->async.num_pending = 1;
ares_gethostbyname(data->state.areschannel, hostname, family,
ares_query_completed_cb, conn);
}
*waitp = 1;
}
return NULL;
}
#endif