mirror of
https://github.com/curl/curl.git
synced 2025-09-14 08:02:44 +03:00
urldata: move async resolver state from easy handle to connectdata
- resolving is done for a connection, not for every transfer - save create/dup/free of a cares channel for each transfer - check values of setopt calls against a local channel if no connection has been attached yet, when needed. Closes #12198
This commit is contained in:
parent
910f740ce2
commit
56a4db2e4e
119
lib/asyn-ares.c
119
lib/asyn-ares.c
|
@ -228,9 +228,9 @@ static void destroy_async_data(struct Curl_async *async);
|
||||||
void Curl_resolver_cancel(struct Curl_easy *data)
|
void Curl_resolver_cancel(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
if(data->state.async.resolver)
|
if(data->conn->resolve_async.resolver)
|
||||||
ares_cancel((ares_channel)data->state.async.resolver);
|
ares_cancel((ares_channel)data->conn->resolve_async.resolver);
|
||||||
destroy_async_data(&data->state.async);
|
destroy_async_data(&data->conn->resolve_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -278,14 +278,14 @@ int Curl_resolver_getsock(struct Curl_easy *data,
|
||||||
struct timeval timebuf;
|
struct timeval timebuf;
|
||||||
struct timeval *timeout;
|
struct timeval *timeout;
|
||||||
long milli;
|
long milli;
|
||||||
int max = ares_getsock((ares_channel)data->state.async.resolver,
|
int max = ares_getsock((ares_channel)data->conn->resolve_async.resolver,
|
||||||
(ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
|
(ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
|
||||||
|
|
||||||
maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
|
maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
|
||||||
maxtime.tv_usec = 0;
|
maxtime.tv_usec = 0;
|
||||||
|
|
||||||
timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
|
timeout = ares_timeout((ares_channel)data->conn->resolve_async.resolver,
|
||||||
&timebuf);
|
&maxtime, &timebuf);
|
||||||
milli = (long)curlx_tvtoms(timeout);
|
milli = (long)curlx_tvtoms(timeout);
|
||||||
if(milli == 0)
|
if(milli == 0)
|
||||||
milli += 10;
|
milli += 10;
|
||||||
|
@ -313,8 +313,8 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
|
||||||
int i;
|
int i;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
|
|
||||||
bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
|
bitmask = ares_getsock((ares_channel)data->conn->resolve_async.resolver,
|
||||||
ARES_GETSOCK_MAXNUM);
|
socks, ARES_GETSOCK_MAXNUM);
|
||||||
|
|
||||||
for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
|
for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
|
||||||
pfd[i].events = 0;
|
pfd[i].events = 0;
|
||||||
|
@ -344,12 +344,12 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
|
||||||
if(!nfds)
|
if(!nfds)
|
||||||
/* Call ares_process() unconditionally here, even if we simply timed out
|
/* Call ares_process() unconditionally here, even if we simply timed out
|
||||||
above, as otherwise the ares name resolve won't timeout! */
|
above, as otherwise the ares name resolve won't timeout! */
|
||||||
ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
|
ares_process_fd((ares_channel)data->conn->resolve_async.resolver,
|
||||||
ARES_SOCKET_BAD);
|
ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
||||||
else {
|
else {
|
||||||
/* move through the descriptors and ask for processing on them */
|
/* move through the descriptors and ask for processing on them */
|
||||||
for(i = 0; i < num; i++)
|
for(i = 0; i < num; i++)
|
||||||
ares_process_fd((ares_channel)data->state.async.resolver,
|
ares_process_fd((ares_channel)data->conn->resolve_async.resolver,
|
||||||
(pfd[i].revents & (POLLRDNORM|POLLIN))?
|
(pfd[i].revents & (POLLRDNORM|POLLIN))?
|
||||||
pfd[i].fd:ARES_SOCKET_BAD,
|
pfd[i].fd:ARES_SOCKET_BAD,
|
||||||
(pfd[i].revents & (POLLWRNORM|POLLOUT))?
|
(pfd[i].revents & (POLLWRNORM|POLLOUT))?
|
||||||
|
@ -368,7 +368,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
|
||||||
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
||||||
struct Curl_dns_entry **dns)
|
struct Curl_dns_entry **dns)
|
||||||
{
|
{
|
||||||
struct thread_data *res = data->state.async.tdata;
|
struct thread_data *res = data->conn->resolve_async.tdata;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
DEBUGASSERT(dns);
|
DEBUGASSERT(dns);
|
||||||
|
@ -397,7 +397,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
||||||
ARES_ECANCELLED synchronously for all pending responses. This will
|
ARES_ECANCELLED synchronously for all pending responses. This will
|
||||||
leave us with res->num_pending == 0, which is perfect for the next
|
leave us with res->num_pending == 0, which is perfect for the next
|
||||||
block. */
|
block. */
|
||||||
ares_cancel((ares_channel)data->state.async.resolver);
|
ares_cancel((ares_channel)data->conn->resolve_async.resolver);
|
||||||
DEBUGASSERT(res->num_pending == 0);
|
DEBUGASSERT(res->num_pending == 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -408,12 +408,12 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
||||||
them */
|
them */
|
||||||
res->temp_ai = NULL;
|
res->temp_ai = NULL;
|
||||||
|
|
||||||
if(!data->state.async.dns)
|
if(!data->conn->resolve_async.dns)
|
||||||
result = Curl_resolver_error(data);
|
result = Curl_resolver_error(data);
|
||||||
else
|
else
|
||||||
*dns = data->state.async.dns;
|
*dns = data->conn->resolve_async.dns;
|
||||||
|
|
||||||
destroy_async_data(&data->state.async);
|
destroy_async_data(&data->conn->resolve_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -464,7 +464,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
||||||
store.tv_sec = itimeout/1000;
|
store.tv_sec = itimeout/1000;
|
||||||
store.tv_usec = (itimeout%1000)*1000;
|
store.tv_usec = (itimeout%1000)*1000;
|
||||||
|
|
||||||
tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
|
tvp = ares_timeout((ares_channel)data->conn->resolve_async.resolver,
|
||||||
|
&store, &tv);
|
||||||
|
|
||||||
/* use the timeout period ares returned to us above if less than one
|
/* 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
|
second is left, otherwise just use 1000ms to make sure the progress
|
||||||
|
@ -478,7 +479,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
||||||
return CURLE_UNRECOVERABLE_POLL;
|
return CURLE_UNRECOVERABLE_POLL;
|
||||||
result = Curl_resolver_is_resolved(data, entry);
|
result = Curl_resolver_is_resolved(data, entry);
|
||||||
|
|
||||||
if(result || data->state.async.done)
|
if(result || data->conn->resolve_async.done)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(Curl_pgrsUpdate(data))
|
if(Curl_pgrsUpdate(data))
|
||||||
|
@ -499,12 +500,12 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
if(result)
|
if(result)
|
||||||
/* failure, so we cancel the ares operation */
|
/* failure, so we cancel the ares operation */
|
||||||
ares_cancel((ares_channel)data->state.async.resolver);
|
ares_cancel((ares_channel)data->conn->resolve_async.resolver);
|
||||||
|
|
||||||
/* Operation complete, if the lookup was successful we now have the entry
|
/* Operation complete, if the lookup was successful we now have the entry
|
||||||
in the cache. */
|
in the cache. */
|
||||||
if(entry)
|
if(entry)
|
||||||
*entry = data->state.async.dns;
|
*entry = data->conn->resolve_async.dns;
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
/* close the connection, since we can't return failure here without
|
/* close the connection, since we can't return failure here without
|
||||||
|
@ -571,12 +572,13 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */
|
||||||
be valid so only defer it when we know the 'status' says its fine! */
|
be valid so only defer it when we know the 'status' says its fine! */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
res = data->state.async.tdata;
|
res = data->conn->resolve_async.tdata;
|
||||||
if(res) {
|
if(res) {
|
||||||
res->num_pending--;
|
res->num_pending--;
|
||||||
|
|
||||||
if(CURL_ASYNC_SUCCESS == status) {
|
if(CURL_ASYNC_SUCCESS == status) {
|
||||||
struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
|
struct Curl_addrinfo *ai = Curl_he2ai(hostent,
|
||||||
|
data->conn->resolve_async.port);
|
||||||
if(ai) {
|
if(ai) {
|
||||||
compound_results(res, ai);
|
compound_results(res, ai);
|
||||||
}
|
}
|
||||||
|
@ -727,7 +729,7 @@ static void addrinfo_cb(void *arg, int status, int timeouts,
|
||||||
struct ares_addrinfo *result)
|
struct ares_addrinfo *result)
|
||||||
{
|
{
|
||||||
struct Curl_easy *data = (struct Curl_easy *)arg;
|
struct Curl_easy *data = (struct Curl_easy *)arg;
|
||||||
struct thread_data *res = data->state.async.tdata;
|
struct thread_data *res = data->conn->resolve_async.tdata;
|
||||||
(void)timeouts;
|
(void)timeouts;
|
||||||
if(ARES_SUCCESS == status) {
|
if(ARES_SUCCESS == status) {
|
||||||
res->temp_ai = ares2addr(result->nodes);
|
res->temp_ai = ares2addr(result->nodes);
|
||||||
|
@ -758,12 +760,12 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
||||||
res = calloc(sizeof(struct thread_data) + namelen, 1);
|
res = calloc(sizeof(struct thread_data) + namelen, 1);
|
||||||
if(res) {
|
if(res) {
|
||||||
strcpy(res->hostname, hostname);
|
strcpy(res->hostname, hostname);
|
||||||
data->state.async.hostname = res->hostname;
|
data->conn->resolve_async.hostname = res->hostname;
|
||||||
data->state.async.port = port;
|
data->conn->resolve_async.port = port;
|
||||||
data->state.async.done = FALSE; /* not done */
|
data->conn->resolve_async.done = FALSE; /* not done */
|
||||||
data->state.async.status = 0; /* clear */
|
data->conn->resolve_async.status = 0; /* clear */
|
||||||
data->state.async.dns = NULL; /* clear */
|
data->conn->resolve_async.dns = NULL; /* clear */
|
||||||
data->state.async.tdata = res;
|
data->conn->resolve_async.tdata = res;
|
||||||
|
|
||||||
/* initial status - failed */
|
/* initial status - failed */
|
||||||
res->last_status = ARES_ENOTFOUND;
|
res->last_status = ARES_ENOTFOUND;
|
||||||
|
@ -793,8 +795,8 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
||||||
hints.ai_flags = ARES_AI_NUMERICSERV;
|
hints.ai_flags = ARES_AI_NUMERICSERV;
|
||||||
msnprintf(service, sizeof(service), "%d", port);
|
msnprintf(service, sizeof(service), "%d", port);
|
||||||
res->num_pending = 1;
|
res->num_pending = 1;
|
||||||
ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
|
ares_getaddrinfo((ares_channel)data->conn->resolve_async.resolver,
|
||||||
service, &hints, addrinfo_cb, data);
|
hostname, service, &hints, addrinfo_cb, data);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@ -804,10 +806,10 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
||||||
res->num_pending = 2;
|
res->num_pending = 2;
|
||||||
|
|
||||||
/* areschannel is already setup in the Curl_open() function */
|
/* areschannel is already setup in the Curl_open() function */
|
||||||
ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
|
ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
|
||||||
PF_INET, query_completed_cb, data);
|
hostname, PF_INET, query_completed_cb, data);
|
||||||
ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
|
ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
|
||||||
PF_INET6, query_completed_cb, data);
|
hostname, PF_INET6, query_completed_cb, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
@ -815,7 +817,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
||||||
res->num_pending = 1;
|
res->num_pending = 1;
|
||||||
|
|
||||||
/* areschannel is already setup in the Curl_open() function */
|
/* areschannel is already setup in the Curl_open() function */
|
||||||
ares_gethostbyname((ares_channel)data->state.async.resolver,
|
ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
|
||||||
hostname, PF_INET,
|
hostname, PF_INET,
|
||||||
query_completed_cb, data);
|
query_completed_cb, data);
|
||||||
}
|
}
|
||||||
|
@ -829,6 +831,7 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
||||||
char *servers)
|
char *servers)
|
||||||
{
|
{
|
||||||
CURLcode result = CURLE_NOT_BUILT_IN;
|
CURLcode result = CURLE_NOT_BUILT_IN;
|
||||||
|
ares_channel channel, lchannel = NULL;
|
||||||
int ares_result;
|
int ares_result;
|
||||||
|
|
||||||
/* If server is NULL or empty, this would purge all DNS servers
|
/* If server is NULL or empty, this would purge all DNS servers
|
||||||
|
@ -841,11 +844,23 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
|
|
||||||
#ifdef HAVE_CARES_SERVERS_CSV
|
#ifdef HAVE_CARES_SERVERS_CSV
|
||||||
|
if(data->conn)
|
||||||
|
channel = data->conn->resolve_async.resolver;
|
||||||
|
else {
|
||||||
|
/* we are called by setopt on a data without a connection (yet). In that
|
||||||
|
* case we set the value on a local instance for checking.
|
||||||
|
* The configured data options are set when the connection for this
|
||||||
|
* transfer is created. */
|
||||||
|
result = Curl_resolver_init(data, (void **)&lchannel);
|
||||||
|
if(result)
|
||||||
|
goto out;
|
||||||
|
channel = lchannel;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_CARES_PORTS_CSV
|
#ifdef HAVE_CARES_PORTS_CSV
|
||||||
ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
|
ares_result = ares_set_servers_ports_csv(channel, servers);
|
||||||
servers);
|
|
||||||
#else
|
#else
|
||||||
ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
|
ares_result = ares_set_servers_csv(channel, servers);
|
||||||
#endif
|
#endif
|
||||||
switch(ares_result) {
|
switch(ares_result) {
|
||||||
case ARES_SUCCESS:
|
case ARES_SUCCESS:
|
||||||
|
@ -861,6 +876,9 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
||||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
|
if(lchannel)
|
||||||
|
Curl_resolver_cleanup(lchannel);
|
||||||
#else /* too old c-ares version! */
|
#else /* too old c-ares version! */
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)(ares_result);
|
(void)(ares_result);
|
||||||
|
@ -872,11 +890,14 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data,
|
||||||
const char *interf)
|
const char *interf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_CARES_LOCAL_DEV
|
#ifdef HAVE_CARES_LOCAL_DEV
|
||||||
if(!interf)
|
if(data->conn) {
|
||||||
interf = "";
|
/* not a setopt test run, set the value */
|
||||||
|
if(!interf)
|
||||||
ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
|
interf = "";
|
||||||
|
|
||||||
|
ares_set_local_dev((ares_channel)data->conn->resolve_async.resolver,
|
||||||
|
interf);
|
||||||
|
}
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
#else /* c-ares version too old! */
|
#else /* c-ares version too old! */
|
||||||
(void)data;
|
(void)data;
|
||||||
|
@ -900,8 +921,11 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ares_set_local_ip4((ares_channel)data->state.async.resolver,
|
if(data->conn) {
|
||||||
ntohl(a4.s_addr));
|
/* not a setopt test run, set the value */
|
||||||
|
ares_set_local_ip4((ares_channel)data->conn->resolve_async.resolver,
|
||||||
|
ntohl(a4.s_addr));
|
||||||
|
}
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
#else /* c-ares version too old! */
|
#else /* c-ares version too old! */
|
||||||
|
@ -927,7 +951,10 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
|
if(data->conn) {
|
||||||
|
/* not a setopt test run, set the value */
|
||||||
|
ares_set_local_ip6((ares_channel)data->conn->resolve_async.resolver, a6);
|
||||||
|
}
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
#else /* c-ares version too old! */
|
#else /* c-ares version too old! */
|
||||||
|
|
|
@ -136,7 +136,7 @@ static void destroy_async_data(struct Curl_async *);
|
||||||
*/
|
*/
|
||||||
void Curl_resolver_cancel(struct Curl_easy *data)
|
void Curl_resolver_cancel(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
destroy_async_data(&data->state.async);
|
destroy_async_data(&data->conn->resolve_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is used to init a threaded resolve */
|
/* This function is used to init a threaded resolve */
|
||||||
|
@ -173,7 +173,7 @@ struct thread_data {
|
||||||
|
|
||||||
static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
|
static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
return &(data->state.async.tdata->tsd);
|
return &(data->conn->resolve_async.tdata->tsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destroy resolver thread synchronization data */
|
/* Destroy resolver thread synchronization data */
|
||||||
|
@ -428,9 +428,9 @@ static bool init_resolve_thread(struct Curl_easy *data,
|
||||||
{
|
{
|
||||||
struct thread_data *td = calloc(1, sizeof(struct thread_data));
|
struct thread_data *td = calloc(1, sizeof(struct thread_data));
|
||||||
int err = ENOMEM;
|
int err = ENOMEM;
|
||||||
struct Curl_async *asp = &data->state.async;
|
struct Curl_async *asp = &data->conn->resolve_async;
|
||||||
|
|
||||||
data->state.async.tdata = td;
|
data->conn->resolve_async.tdata = td;
|
||||||
if(!td)
|
if(!td)
|
||||||
goto errno_exit;
|
goto errno_exit;
|
||||||
|
|
||||||
|
@ -488,7 +488,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
td = data->state.async.tdata;
|
td = data->conn->resolve_async.tdata;
|
||||||
DEBUGASSERT(td);
|
DEBUGASSERT(td);
|
||||||
DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
|
DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
|
||||||
|
|
||||||
|
@ -500,18 +500,18 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
|
||||||
else
|
else
|
||||||
DEBUGASSERT(0);
|
DEBUGASSERT(0);
|
||||||
|
|
||||||
data->state.async.done = TRUE;
|
data->conn->resolve_async.done = TRUE;
|
||||||
|
|
||||||
if(entry)
|
if(entry)
|
||||||
*entry = data->state.async.dns;
|
*entry = data->conn->resolve_async.dns;
|
||||||
|
|
||||||
if(!data->state.async.dns && report)
|
if(!data->conn->resolve_async.dns && report)
|
||||||
/* a name was not resolved, report error */
|
/* a name was not resolved, report error */
|
||||||
result = Curl_resolver_error(data);
|
result = Curl_resolver_error(data);
|
||||||
|
|
||||||
destroy_async_data(&data->state.async);
|
destroy_async_data(&data->conn->resolve_async);
|
||||||
|
|
||||||
if(!data->state.async.dns && report)
|
if(!data->conn->resolve_async.dns && report)
|
||||||
connclose(data->conn, "asynch resolve failed");
|
connclose(data->conn, "asynch resolve failed");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -524,7 +524,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
|
||||||
*/
|
*/
|
||||||
void Curl_resolver_kill(struct Curl_easy *data)
|
void Curl_resolver_kill(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct thread_data *td = data->state.async.tdata;
|
struct thread_data *td = data->conn->resolve_async.tdata;
|
||||||
|
|
||||||
/* If we're still resolving, we must wait for the threads to fully clean up,
|
/* If we're still resolving, we must wait for the threads to fully clean up,
|
||||||
unfortunately. Otherwise, we can simply cancel to clean up any resolver
|
unfortunately. Otherwise, we can simply cancel to clean up any resolver
|
||||||
|
@ -563,7 +563,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
||||||
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
||||||
struct Curl_dns_entry **entry)
|
struct Curl_dns_entry **entry)
|
||||||
{
|
{
|
||||||
struct thread_data *td = data->state.async.tdata;
|
struct thread_data *td = data->conn->resolve_async.tdata;
|
||||||
int done = 0;
|
int done = 0;
|
||||||
|
|
||||||
DEBUGASSERT(entry);
|
DEBUGASSERT(entry);
|
||||||
|
@ -581,13 +581,13 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
||||||
if(done) {
|
if(done) {
|
||||||
getaddrinfo_complete(data);
|
getaddrinfo_complete(data);
|
||||||
|
|
||||||
if(!data->state.async.dns) {
|
if(!data->conn->resolve_async.dns) {
|
||||||
CURLcode result = Curl_resolver_error(data);
|
CURLcode result = Curl_resolver_error(data);
|
||||||
destroy_async_data(&data->state.async);
|
destroy_async_data(&data->conn->resolve_async);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
destroy_async_data(&data->state.async);
|
destroy_async_data(&data->conn->resolve_async);
|
||||||
*entry = data->state.async.dns;
|
*entry = data->conn->resolve_async.dns;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* poll for name lookup done with exponential backoff up to 250ms */
|
/* poll for name lookup done with exponential backoff up to 250ms */
|
||||||
|
@ -619,9 +619,9 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
|
||||||
int ret_val = 0;
|
int ret_val = 0;
|
||||||
timediff_t milli;
|
timediff_t milli;
|
||||||
timediff_t ms;
|
timediff_t ms;
|
||||||
struct resdata *reslv = (struct resdata *)data->state.async.resolver;
|
struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
|
||||||
#ifndef CURL_DISABLE_SOCKETPAIR
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
||||||
struct thread_data *td = data->state.async.tdata;
|
struct thread_data *td = data->conn->resolve_async.tdata;
|
||||||
#else
|
#else
|
||||||
(void)socks;
|
(void)socks;
|
||||||
#endif
|
#endif
|
||||||
|
@ -662,7 +662,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
||||||
int port,
|
int port,
|
||||||
int *waitp)
|
int *waitp)
|
||||||
{
|
{
|
||||||
struct resdata *reslv = (struct resdata *)data->state.async.resolver;
|
struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
|
||||||
|
|
||||||
*waitp = 0; /* default to synchronous response */
|
*waitp = 0; /* default to synchronous response */
|
||||||
|
|
||||||
|
@ -691,7 +691,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
||||||
{
|
{
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
int pf = PF_INET;
|
int pf = PF_INET;
|
||||||
struct resdata *reslv = (struct resdata *)data->state.async.resolver;
|
struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
|
||||||
|
|
||||||
*waitp = 0; /* default to synchronous response */
|
*waitp = 0; /* default to synchronous response */
|
||||||
|
|
||||||
|
|
|
@ -901,6 +901,7 @@ UNITTEST void de_cleanup(struct dohentry *d)
|
||||||
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
|
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
|
||||||
struct Curl_dns_entry **dnsp)
|
struct Curl_dns_entry **dnsp)
|
||||||
{
|
{
|
||||||
|
struct connectdata *conn = data->conn;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
struct dohdata *dohp = data->req.doh;
|
struct dohdata *dohp = data->req.doh;
|
||||||
*dnsp = NULL; /* defaults to no response */
|
*dnsp = NULL; /* defaults to no response */
|
||||||
|
@ -909,7 +910,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
|
||||||
|
|
||||||
if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
|
if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
|
||||||
!dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
|
!dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
|
||||||
failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
|
failf(data, "Could not DoH-resolve: %s", conn->resolve_async.hostname);
|
||||||
return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
|
return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
|
||||||
CURLE_COULDNT_RESOLVE_HOST;
|
CURLE_COULDNT_RESOLVE_HOST;
|
||||||
}
|
}
|
||||||
|
@ -970,7 +971,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
|
||||||
Curl_freeaddrinfo(ai);
|
Curl_freeaddrinfo(ai);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data->state.async.dns = dns;
|
conn->resolve_async.dns = dns;
|
||||||
*dnsp = dns;
|
*dnsp = dns;
|
||||||
result = CURLE_OK; /* address resolution OK */
|
result = CURLE_OK; /* address resolution OK */
|
||||||
}
|
}
|
||||||
|
|
27
lib/easy.c
27
lib/easy.c
|
@ -968,33 +968,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
|
||||||
(void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
|
(void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Clone the resolver handle, if present, for the new handle */
|
|
||||||
if(Curl_resolver_duphandle(outcurl,
|
|
||||||
&outcurl->state.async.resolver,
|
|
||||||
data->state.async.resolver))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
#ifdef USE_ARES
|
|
||||||
{
|
|
||||||
CURLcode rc;
|
|
||||||
|
|
||||||
rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
|
|
||||||
if(rc && rc != CURLE_NOT_BUILT_IN)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
|
|
||||||
if(rc && rc != CURLE_NOT_BUILT_IN)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
|
|
||||||
if(rc && rc != CURLE_NOT_BUILT_IN)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
|
|
||||||
if(rc && rc != CURLE_NOT_BUILT_IN)
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
#endif /* USE_ARES */
|
|
||||||
|
|
||||||
Curl_initinfo(outcurl);
|
Curl_initinfo(outcurl);
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,11 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
|
||||||
int status,
|
int status,
|
||||||
struct Curl_addrinfo *ai)
|
struct Curl_addrinfo *ai)
|
||||||
{
|
{
|
||||||
|
struct connectdata *conn = data->conn;
|
||||||
struct Curl_dns_entry *dns = NULL;
|
struct Curl_dns_entry *dns = NULL;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
data->state.async.status = status;
|
conn->resolve_async.status = status;
|
||||||
|
|
||||||
if(CURL_ASYNC_SUCCESS == status) {
|
if(CURL_ASYNC_SUCCESS == status) {
|
||||||
if(ai) {
|
if(ai) {
|
||||||
|
@ -78,8 +79,8 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
|
||||||
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
||||||
|
|
||||||
dns = Curl_cache_addr(data, ai,
|
dns = Curl_cache_addr(data, ai,
|
||||||
data->state.async.hostname, 0,
|
conn->resolve_async.hostname, 0,
|
||||||
data->state.async.port);
|
conn->resolve_async.port);
|
||||||
if(data->share)
|
if(data->share)
|
||||||
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
|
||||||
|
@ -94,12 +95,12 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->state.async.dns = dns;
|
conn->resolve_async.dns = dns;
|
||||||
|
|
||||||
/* Set async.done TRUE last in this function since it may be used multi-
|
/* Set async.done TRUE last in this function since it may be used multi-
|
||||||
threaded and once this is TRUE the other thread may read fields from the
|
threaded and once this is TRUE the other thread may read fields from the
|
||||||
async struct */
|
async struct */
|
||||||
data->state.async.done = TRUE;
|
conn->resolve_async.done = TRUE;
|
||||||
|
|
||||||
/* IPv4: The input hostent struct will be freed by ares when we return from
|
/* IPv4: The input hostent struct will be freed by ares when we return from
|
||||||
this function */
|
this function */
|
||||||
|
|
12
lib/hostip.c
12
lib/hostip.c
|
@ -741,7 +741,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
|
||||||
Curl_set_in_callback(data, true);
|
Curl_set_in_callback(data, true);
|
||||||
st = data->set.resolver_start(
|
st = data->set.resolver_start(
|
||||||
#ifdef USE_CURL_ASYNC
|
#ifdef USE_CURL_ASYNC
|
||||||
data->state.async.resolver,
|
conn->resolve_async.resolver,
|
||||||
#else
|
#else
|
||||||
NULL,
|
NULL,
|
||||||
#endif
|
#endif
|
||||||
|
@ -1413,9 +1413,9 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
|
||||||
struct connectdata *conn = data->conn;
|
struct connectdata *conn = data->conn;
|
||||||
|
|
||||||
#ifdef USE_CURL_ASYNC
|
#ifdef USE_CURL_ASYNC
|
||||||
if(data->state.async.dns) {
|
if(conn->resolve_async.dns) {
|
||||||
conn->dns_entry = data->state.async.dns;
|
conn->dns_entry = conn->resolve_async.dns;
|
||||||
data->state.async.dns = NULL;
|
conn->resolve_async.dns = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1437,11 +1437,11 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
|
||||||
#ifdef USE_CURL_ASYNC
|
#ifdef USE_CURL_ASYNC
|
||||||
CURLcode Curl_resolver_error(struct Curl_easy *data)
|
CURLcode Curl_resolver_error(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
|
struct connectdata *conn = data->conn;
|
||||||
const char *host_or_proxy;
|
const char *host_or_proxy;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
struct connectdata *conn = data->conn;
|
|
||||||
if(conn->bits.httpproxy) {
|
if(conn->bits.httpproxy) {
|
||||||
host_or_proxy = "proxy";
|
host_or_proxy = "proxy";
|
||||||
result = CURLE_COULDNT_RESOLVE_PROXY;
|
result = CURLE_COULDNT_RESOLVE_PROXY;
|
||||||
|
@ -1454,7 +1454,7 @@ CURLcode Curl_resolver_error(struct Curl_easy *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
failf(data, "Could not resolve %s: %s", host_or_proxy,
|
failf(data, "Could not resolve %s: %s", host_or_proxy,
|
||||||
data->state.async.hostname);
|
conn->resolve_async.hostname);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1986,8 +1986,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||||
|
|
||||||
if(dns) {
|
if(dns) {
|
||||||
#ifdef CURLRES_ASYNCH
|
#ifdef CURLRES_ASYNCH
|
||||||
data->state.async.dns = dns;
|
conn->resolve_async.dns = dns;
|
||||||
data->state.async.done = TRUE;
|
conn->resolve_async.done = TRUE;
|
||||||
#endif
|
#endif
|
||||||
result = CURLE_OK;
|
result = CURLE_OK;
|
||||||
infof(data, "Hostname '%s' was found in DNS cache", hostname);
|
infof(data, "Hostname '%s' was found in DNS cache", hostname);
|
||||||
|
|
|
@ -339,8 +339,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
|
||||||
|
|
||||||
if(dns) {
|
if(dns) {
|
||||||
#ifdef CURLRES_ASYNCH
|
#ifdef CURLRES_ASYNCH
|
||||||
data->state.async.dns = dns;
|
conn->resolve_async.dns = dns;
|
||||||
data->state.async.done = TRUE;
|
conn->resolve_async.done = TRUE;
|
||||||
#endif
|
#endif
|
||||||
infof(data, "Hostname '%s' was found", sx->hostname);
|
infof(data, "Hostname '%s' was found", sx->hostname);
|
||||||
sxstate(sx, data, CONNECT_RESOLVED);
|
sxstate(sx, data, CONNECT_RESOLVED);
|
||||||
|
@ -806,8 +806,8 @@ CONNECT_REQ_INIT:
|
||||||
|
|
||||||
if(dns) {
|
if(dns) {
|
||||||
#ifdef CURLRES_ASYNCH
|
#ifdef CURLRES_ASYNCH
|
||||||
data->state.async.dns = dns;
|
conn->resolve_async.dns = dns;
|
||||||
data->state.async.done = TRUE;
|
conn->resolve_async.done = TRUE;
|
||||||
#endif
|
#endif
|
||||||
infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
|
infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
|
||||||
}
|
}
|
||||||
|
|
42
lib/url.c
42
lib/url.c
|
@ -422,10 +422,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
||||||
Curl_safefree(data->info.contenttype);
|
Curl_safefree(data->info.contenttype);
|
||||||
Curl_safefree(data->info.wouldredirect);
|
Curl_safefree(data->info.wouldredirect);
|
||||||
|
|
||||||
/* this destroys the channel and we cannot use it anymore after this */
|
|
||||||
Curl_resolver_cancel(data);
|
|
||||||
Curl_resolver_cleanup(data->state.async.resolver);
|
|
||||||
|
|
||||||
data_priority_cleanup(data);
|
data_priority_cleanup(data);
|
||||||
|
|
||||||
/* No longer a dirty share, if it exists */
|
/* No longer a dirty share, if it exists */
|
||||||
|
@ -652,13 +648,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
|
||||||
|
|
||||||
data->magic = CURLEASY_MAGIC_NUMBER;
|
data->magic = CURLEASY_MAGIC_NUMBER;
|
||||||
|
|
||||||
result = Curl_resolver_init(data, &data->state.async.resolver);
|
|
||||||
if(result) {
|
|
||||||
DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
|
|
||||||
free(data);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = Curl_init_userdefined(data);
|
result = Curl_init_userdefined(data);
|
||||||
if(!result) {
|
if(!result) {
|
||||||
Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
|
Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
|
||||||
|
@ -675,7 +664,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
Curl_resolver_cleanup(data->state.async.resolver);
|
|
||||||
Curl_dyn_free(&data->state.headerb);
|
Curl_dyn_free(&data->state.headerb);
|
||||||
Curl_freeset(data);
|
Curl_freeset(data);
|
||||||
free(data);
|
free(data);
|
||||||
|
@ -709,6 +697,7 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn)
|
||||||
Curl_conn_cf_discard_all(data, conn, (int)i);
|
Curl_conn_cf_discard_all(data, conn, (int)i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Curl_resolver_cleanup(conn->resolve_async.resolver);
|
||||||
Curl_free_idnconverted_hostname(&conn->host);
|
Curl_free_idnconverted_hostname(&conn->host);
|
||||||
Curl_free_idnconverted_hostname(&conn->conn_to_host);
|
Curl_free_idnconverted_hostname(&conn->conn_to_host);
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
|
@ -809,6 +798,7 @@ void Curl_disconnect(struct Curl_easy *data,
|
||||||
conn->handler->disconnect(data, conn, dead_connection);
|
conn->handler->disconnect(data, conn, dead_connection);
|
||||||
|
|
||||||
conn_shutdown(data);
|
conn_shutdown(data);
|
||||||
|
Curl_resolver_cancel(data);
|
||||||
|
|
||||||
/* detach it again */
|
/* detach it again */
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
|
@ -3791,7 +3781,35 @@ static CURLcode create_conn(struct Curl_easy *data,
|
||||||
* This is a brand new connection, so let's store it in the connection
|
* This is a brand new connection, so let's store it in the connection
|
||||||
* cache of ours!
|
* cache of ours!
|
||||||
*/
|
*/
|
||||||
|
result = Curl_resolver_init(data, &conn->resolve_async.resolver);
|
||||||
|
if(result) {
|
||||||
|
DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(data, conn);
|
||||||
|
|
||||||
|
#ifdef USE_ARES
|
||||||
|
result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]);
|
||||||
|
if(result && result != CURLE_NOT_BUILT_IN)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = Curl_set_dns_interface(data,
|
||||||
|
data->set.str[STRING_DNS_INTERFACE]);
|
||||||
|
if(result && result != CURLE_NOT_BUILT_IN)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = Curl_set_dns_local_ip4(data,
|
||||||
|
data->set.str[STRING_DNS_LOCAL_IP4]);
|
||||||
|
if(result && result != CURLE_NOT_BUILT_IN)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = Curl_set_dns_local_ip6(data,
|
||||||
|
data->set.str[STRING_DNS_LOCAL_IP6]);
|
||||||
|
if(result && result != CURLE_NOT_BUILT_IN)
|
||||||
|
goto out;
|
||||||
|
#endif /* USE_ARES */
|
||||||
|
|
||||||
result = Curl_conncache_add_conn(data);
|
result = Curl_conncache_add_conn(data);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -917,6 +917,9 @@ struct connectdata {
|
||||||
multi_done(). This entry will be NULL if the connection is reused as then
|
multi_done(). This entry will be NULL if the connection is reused as then
|
||||||
there is no name resolve done. */
|
there is no name resolve done. */
|
||||||
struct Curl_dns_entry *dns_entry;
|
struct Curl_dns_entry *dns_entry;
|
||||||
|
#ifdef USE_CURL_ASYNC
|
||||||
|
struct Curl_async resolve_async; /* asynchronous name resolver data */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* 'remote_addr' is the particular IP we connected to. it is owned, set
|
/* 'remote_addr' is the particular IP we connected to. it is owned, set
|
||||||
* and NULLed by the connected socket filter (if there is one). */
|
* and NULLed by the connected socket filter (if there is one). */
|
||||||
|
@ -1374,9 +1377,6 @@ struct UrlState {
|
||||||
#endif
|
#endif
|
||||||
struct auth authhost; /* auth details for host */
|
struct auth authhost; /* auth details for host */
|
||||||
struct auth authproxy; /* auth details for proxy */
|
struct auth authproxy; /* auth details for proxy */
|
||||||
#ifdef USE_CURL_ASYNC
|
|
||||||
struct Curl_async async; /* asynchronous name resolver data */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(USE_OPENSSL)
|
#if defined(USE_OPENSSL)
|
||||||
/* void instead of ENGINE to avoid bleeding OpenSSL into this header */
|
/* void instead of ENGINE to avoid bleeding OpenSSL into this header */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user