Rework the event based handling of transfers and connections to be "localized" into a single source file with clearer dependencies. - add multi_ev.c and multi_ev.h - add docs/internal/MULTI-EV.md to explain the overall workings - only do event handling book keeping when the socket callback is set - add handling for "connection only" event tracking, when internal easy handles are used that are not really tied to a connection. Used in connection pool. - remove transfer member "last_poll" and connections "shutdown_poll" and keep all that internal to multi_ev.c - add CURL_TRC_M() for tracing of "multi" related things, including event handling and connection pool operations. Add new trace feature "multi" for trace config. multi traces will show exactly what is going on in regard to event handling. - multi: trace transfers "mstate" in every CURL_TRC_M() call - make internal trace buffer 2048 bytes and end the silliness with +n here -m there. Adjust test 1652 expectations of resulting length and input edge cases. - add trace feature "lib-ids" to perfix libcurl traces with transfer and connection ids. Useful for debugging libcurl applications. Closes #16308
777 lines
19 KiB
C
777 lines
19 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
*
|
|
* This software is licensed as described in the file COPYING, which
|
|
* you should have received as part of this distribution. The terms
|
|
* are also available at https://curl.se/docs/copyright.html.
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* SPDX-License-Identifier: curl
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "curl_setup.h"
|
|
#include "socketpair.h"
|
|
|
|
/***********************************************************************
|
|
* Only for threaded name resolves builds
|
|
**********************************************************************/
|
|
#ifdef CURLRES_THREADED
|
|
|
|
#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 __VMS
|
|
#include <in.h>
|
|
#include <inet.h>
|
|
#endif
|
|
|
|
#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
|
|
# include <pthread.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
# define RESOLVER_ENOMEM EAI_MEMORY
|
|
#else
|
|
# define RESOLVER_ENOMEM ENOMEM
|
|
#endif
|
|
|
|
#include "urldata.h"
|
|
#include "sendf.h"
|
|
#include "hostip.h"
|
|
#include "hash.h"
|
|
#include "share.h"
|
|
#include "url.h"
|
|
#include "multiif.h"
|
|
#include "inet_ntop.h"
|
|
#include "curl_threads.h"
|
|
#include "connect.h"
|
|
#include "strdup.h"
|
|
|
|
#ifdef USE_ARES
|
|
#include <ares.h>
|
|
#ifdef USE_HTTPSRR
|
|
#define USE_HTTPSRR_ARES 1 /* the combo */
|
|
#endif
|
|
#endif
|
|
|
|
/* The last 3 #include files should be in this order */
|
|
#include "curl_printf.h"
|
|
#include "curl_memory.h"
|
|
#include "memdebug.h"
|
|
|
|
|
|
/*
|
|
* Curl_resolver_global_init()
|
|
* Called from curl_global_init() to initialize global resolver environment.
|
|
* Does nothing here.
|
|
*/
|
|
int Curl_resolver_global_init(void)
|
|
{
|
|
return CURLE_OK;
|
|
}
|
|
|
|
/*
|
|
* Curl_resolver_global_cleanup()
|
|
* Called from curl_global_cleanup() to destroy global resolver environment.
|
|
* Does nothing here.
|
|
*/
|
|
void Curl_resolver_global_cleanup(void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Curl_resolver_init()
|
|
* Called from curl_easy_init() -> Curl_open() to initialize resolver
|
|
* URL-state specific environment ('resolver' member of the UrlState
|
|
* structure).
|
|
*/
|
|
CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
|
|
{
|
|
(void)easy;
|
|
(void)resolver;
|
|
return CURLE_OK;
|
|
}
|
|
|
|
/*
|
|
* Curl_resolver_cleanup()
|
|
* Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
|
|
* URL-state specific environment ('resolver' member of the UrlState
|
|
* structure).
|
|
*/
|
|
void Curl_resolver_cleanup(void *resolver)
|
|
{
|
|
(void)resolver;
|
|
}
|
|
|
|
/*
|
|
* Curl_resolver_duphandle()
|
|
* Called from curl_easy_duphandle() to duplicate resolver URL state-specific
|
|
* environment ('resolver' member of the UrlState structure).
|
|
*/
|
|
CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
|
|
{
|
|
(void)from;
|
|
return Curl_resolver_init(easy, to);
|
|
}
|
|
|
|
static void destroy_async_data(struct Curl_easy *);
|
|
|
|
/*
|
|
* Cancel all possibly still on-going resolves for this connection.
|
|
*/
|
|
void Curl_resolver_cancel(struct Curl_easy *data)
|
|
{
|
|
destroy_async_data(data);
|
|
}
|
|
|
|
/* This function is used to init a threaded resolve */
|
|
static bool init_resolve_thread(struct Curl_easy *data,
|
|
const char *hostname, int port,
|
|
const struct addrinfo *hints);
|
|
|
|
|
|
static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
|
|
{
|
|
return &(data->state.async.thdata.tsd);
|
|
}
|
|
|
|
/* Destroy resolver thread synchronization data */
|
|
static
|
|
void destroy_thread_sync_data(struct thread_sync_data *tsd)
|
|
{
|
|
Curl_mutex_destroy(&tsd->mutx);
|
|
|
|
free(tsd->hostname);
|
|
|
|
if(tsd->res)
|
|
Curl_freeaddrinfo(tsd->res);
|
|
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
/*
|
|
* close one end of the socket pair (may be done in resolver thread);
|
|
* the other end (for reading) is always closed in the parent thread.
|
|
*/
|
|
#ifndef HAVE_EVENTFD
|
|
if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
|
|
wakeup_close(tsd->sock_pair[1]);
|
|
}
|
|
#endif
|
|
#endif
|
|
memset(tsd, 0, sizeof(*tsd));
|
|
}
|
|
|
|
/* Initialize resolver thread synchronization data */
|
|
static
|
|
int init_thread_sync_data(struct thread_data *td,
|
|
const char *hostname,
|
|
int port,
|
|
const struct addrinfo *hints)
|
|
{
|
|
struct thread_sync_data *tsd = &td->tsd;
|
|
|
|
memset(tsd, 0, sizeof(*tsd));
|
|
|
|
td->init = TRUE;
|
|
tsd->port = port;
|
|
/* Treat the request as done until the thread actually starts so any early
|
|
* cleanup gets done properly.
|
|
*/
|
|
tsd->done = TRUE;
|
|
#ifdef HAVE_GETADDRINFO
|
|
DEBUGASSERT(hints);
|
|
tsd->hints = *hints;
|
|
#else
|
|
(void) hints;
|
|
#endif
|
|
|
|
Curl_mutex_init(&tsd->mutx);
|
|
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
/* create socket pair or pipe */
|
|
if(wakeup_create(tsd->sock_pair, FALSE) < 0) {
|
|
tsd->sock_pair[0] = CURL_SOCKET_BAD;
|
|
tsd->sock_pair[1] = CURL_SOCKET_BAD;
|
|
goto err_exit;
|
|
}
|
|
#endif
|
|
tsd->sock_error = CURL_ASYNC_SUCCESS;
|
|
|
|
/* Copying hostname string because original can be destroyed by parent
|
|
* thread during gethostbyname execution.
|
|
*/
|
|
tsd->hostname = strdup(hostname);
|
|
if(!tsd->hostname)
|
|
goto err_exit;
|
|
|
|
return 1;
|
|
|
|
err_exit:
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
|
|
wakeup_close(tsd->sock_pair[0]);
|
|
tsd->sock_pair[0] = CURL_SOCKET_BAD;
|
|
}
|
|
#endif
|
|
destroy_thread_sync_data(tsd);
|
|
return 0;
|
|
}
|
|
|
|
static CURLcode getaddrinfo_complete(struct Curl_easy *data)
|
|
{
|
|
struct thread_sync_data *tsd = conn_thread_sync_data(data);
|
|
CURLcode result;
|
|
|
|
result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
|
|
/* The tsd->res structure has been copied to async.dns and perhaps the DNS
|
|
cache. Set our copy to NULL so destroy_thread_sync_data does not free it.
|
|
*/
|
|
tsd->res = NULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
|
|
/*
|
|
* getaddrinfo_thread() resolves a name and then exits.
|
|
*
|
|
* For builds without ARES, but with USE_IPV6, create a resolver thread
|
|
* and wait on it.
|
|
*/
|
|
static
|
|
#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
|
|
DWORD
|
|
#else
|
|
unsigned int
|
|
#endif
|
|
CURL_STDCALL getaddrinfo_thread(void *arg)
|
|
{
|
|
struct thread_data *td = arg;
|
|
struct thread_sync_data *tsd = &td->tsd;
|
|
char service[12];
|
|
int rc;
|
|
|
|
msnprintf(service, sizeof(service), "%d", tsd->port);
|
|
|
|
rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
|
|
|
|
if(rc) {
|
|
tsd->sock_error = SOCKERRNO ? SOCKERRNO : rc;
|
|
if(tsd->sock_error == 0)
|
|
tsd->sock_error = RESOLVER_ENOMEM;
|
|
}
|
|
else {
|
|
Curl_addrinfo_set_port(tsd->res, tsd->port);
|
|
}
|
|
|
|
Curl_mutex_acquire(&tsd->mutx);
|
|
if(tsd->done) {
|
|
/* too late, gotta clean up the mess */
|
|
Curl_mutex_release(&tsd->mutx);
|
|
destroy_thread_sync_data(tsd);
|
|
}
|
|
else {
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
|
|
#ifdef HAVE_EVENTFD
|
|
const uint64_t buf[1] = { 1 };
|
|
#else
|
|
const char buf[1] = { 1 };
|
|
#endif
|
|
/* DNS has been resolved, signal client task */
|
|
if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
|
|
/* update sock_erro to errno */
|
|
tsd->sock_error = SOCKERRNO;
|
|
}
|
|
}
|
|
#endif
|
|
tsd->done = TRUE;
|
|
Curl_mutex_release(&tsd->mutx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else /* HAVE_GETADDRINFO */
|
|
|
|
/*
|
|
* gethostbyname_thread() resolves a name and then exits.
|
|
*/
|
|
static
|
|
#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
|
|
DWORD
|
|
#else
|
|
unsigned int
|
|
#endif
|
|
CURL_STDCALL gethostbyname_thread(void *arg)
|
|
{
|
|
struct thread_data *td = arg;
|
|
struct thread_sync_data *tsd = &td->tsd;
|
|
|
|
tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
|
|
|
|
if(!tsd->res) {
|
|
tsd->sock_error = SOCKERRNO;
|
|
if(tsd->sock_error == 0)
|
|
tsd->sock_error = RESOLVER_ENOMEM;
|
|
}
|
|
|
|
Curl_mutex_acquire(&tsd->mutx);
|
|
if(tsd->done) {
|
|
/* too late, gotta clean up the mess */
|
|
Curl_mutex_release(&tsd->mutx);
|
|
destroy_thread_sync_data(tsd);
|
|
}
|
|
else {
|
|
tsd->done = TRUE;
|
|
Curl_mutex_release(&tsd->mutx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_GETADDRINFO */
|
|
|
|
/*
|
|
* destroy_async_data() cleans up async resolver data and thread handle.
|
|
*/
|
|
static void destroy_async_data(struct Curl_easy *data)
|
|
{
|
|
struct Curl_async *async = &data->state.async;
|
|
struct thread_data *td = &async->thdata;
|
|
if(td->init) {
|
|
bool done;
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
curl_socket_t sock_rd = td->tsd.sock_pair[0];
|
|
#endif
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(data->state.async.thdata.channel) {
|
|
ares_destroy(data->state.async.thdata.channel);
|
|
data->state.async.thdata.channel = NULL;
|
|
}
|
|
#endif
|
|
/*
|
|
* if the thread is still blocking in the resolve syscall, detach it and
|
|
* let the thread do the cleanup...
|
|
*/
|
|
Curl_mutex_acquire(&td->tsd.mutx);
|
|
done = td->tsd.done;
|
|
td->tsd.done = TRUE;
|
|
Curl_mutex_release(&td->tsd.mutx);
|
|
|
|
if(!done) {
|
|
Curl_thread_destroy(td->thread_hnd);
|
|
}
|
|
else {
|
|
if(td->thread_hnd != curl_thread_t_null)
|
|
Curl_thread_join(&td->thread_hnd);
|
|
|
|
destroy_thread_sync_data(&td->tsd);
|
|
}
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
/*
|
|
* ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
|
|
* before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
|
|
*/
|
|
Curl_multi_will_close(data, sock_rd);
|
|
wakeup_close(sock_rd);
|
|
#endif
|
|
|
|
td->init = FALSE;
|
|
}
|
|
Curl_safefree(async->hostname);
|
|
}
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
static CURLcode resolve_httpsrr(struct Curl_easy *data,
|
|
struct Curl_async *async)
|
|
{
|
|
int status = ares_init_options(&async->thdata.channel, NULL, 0);
|
|
if(status != ARES_SUCCESS)
|
|
return CURLE_FAILED_INIT;
|
|
|
|
memset(&async->thdata.hinfo, 0, sizeof(struct Curl_https_rrinfo));
|
|
async->thdata.hinfo.port = -1;
|
|
ares_query_dnsrec(async->thdata.channel,
|
|
async->hostname, ARES_CLASS_IN,
|
|
ARES_REC_TYPE_HTTPS,
|
|
Curl_dnsrec_done_cb, data, NULL);
|
|
|
|
return CURLE_OK;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* init_resolve_thread() starts a new thread that performs the actual
|
|
* resolve. This function returns before the resolve is done.
|
|
*
|
|
* Returns FALSE in case of failure, otherwise TRUE.
|
|
*/
|
|
static bool init_resolve_thread(struct Curl_easy *data,
|
|
const char *hostname, int port,
|
|
const struct addrinfo *hints)
|
|
{
|
|
struct thread_data *td = &data->state.async.thdata;
|
|
int err = ENOMEM;
|
|
struct Curl_async *async = &data->state.async;
|
|
|
|
async->port = port;
|
|
async->done = FALSE;
|
|
async->dns = NULL;
|
|
td->thread_hnd = curl_thread_t_null;
|
|
td->start = Curl_now();
|
|
|
|
if(!init_thread_sync_data(td, hostname, port, hints)) {
|
|
free(td);
|
|
goto errno_exit;
|
|
}
|
|
|
|
free(async->hostname);
|
|
async->hostname = strdup(hostname);
|
|
if(!async->hostname)
|
|
goto err_exit;
|
|
|
|
/* The thread will set this TRUE when complete. */
|
|
td->tsd.done = FALSE;
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
td->thread_hnd = Curl_thread_create(getaddrinfo_thread, td);
|
|
#else
|
|
td->thread_hnd = Curl_thread_create(gethostbyname_thread, td);
|
|
#endif
|
|
|
|
if(td->thread_hnd == curl_thread_t_null) {
|
|
/* The thread never started, so mark it as done here for proper cleanup. */
|
|
td->tsd.done = TRUE;
|
|
err = errno;
|
|
goto err_exit;
|
|
}
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(resolve_httpsrr(data, async))
|
|
infof(data, "Failed HTTPS RR operation");
|
|
#endif
|
|
return TRUE;
|
|
|
|
err_exit:
|
|
destroy_async_data(data);
|
|
|
|
errno_exit:
|
|
CURL_SETERRNO(err);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* 'entry' may be NULL and then no data is returned
|
|
*/
|
|
static CURLcode thread_wait_resolv(struct Curl_easy *data,
|
|
struct Curl_dns_entry **entry,
|
|
bool report)
|
|
{
|
|
struct thread_data *td;
|
|
CURLcode result = CURLE_OK;
|
|
|
|
DEBUGASSERT(data);
|
|
td = &data->state.async.thdata;
|
|
DEBUGASSERT(td);
|
|
DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
|
|
|
|
/* wait for the thread to resolve the name */
|
|
if(Curl_thread_join(&td->thread_hnd)) {
|
|
if(entry)
|
|
result = getaddrinfo_complete(data);
|
|
}
|
|
else
|
|
DEBUGASSERT(0);
|
|
|
|
data->state.async.done = TRUE;
|
|
|
|
if(entry)
|
|
*entry = data->state.async.dns;
|
|
|
|
if(!data->state.async.dns && report)
|
|
/* a name was not resolved, report error */
|
|
result = Curl_resolver_error(data);
|
|
|
|
destroy_async_data(data);
|
|
|
|
if(!data->state.async.dns && report)
|
|
connclose(data->conn, "asynch resolve failed");
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* Until we gain a way to signal the resolver threads to stop early, we must
|
|
* simply wait for them and ignore their results.
|
|
*/
|
|
void Curl_resolver_kill(struct Curl_easy *data)
|
|
{
|
|
struct thread_data *td = &data->state.async.thdata;
|
|
|
|
/* If we are still resolving, we must wait for the threads to fully clean up,
|
|
unfortunately. Otherwise, we can simply cancel to clean up any resolver
|
|
data. */
|
|
if((td->thread_hnd != curl_thread_t_null) && !data->set.quick_exit)
|
|
(void)thread_wait_resolv(data, NULL, FALSE);
|
|
else
|
|
Curl_resolver_cancel(data);
|
|
}
|
|
|
|
/*
|
|
* 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,
|
|
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
|
|
*
|
|
* This is the version for resolves-in-a-thread.
|
|
*/
|
|
CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
|
struct Curl_dns_entry **entry)
|
|
{
|
|
return thread_wait_resolv(data, entry, TRUE);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
|
struct Curl_dns_entry **entry)
|
|
{
|
|
struct thread_data *td = &data->state.async.thdata;
|
|
bool done = FALSE;
|
|
|
|
DEBUGASSERT(entry);
|
|
*entry = NULL;
|
|
|
|
if(!td) {
|
|
DEBUGASSERT(td);
|
|
return CURLE_COULDNT_RESOLVE_HOST;
|
|
}
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(Curl_ares_perform(data->state.async.thdata.channel, 0) < 0)
|
|
return CURLE_UNRECOVERABLE_POLL;
|
|
#endif
|
|
|
|
Curl_mutex_acquire(&td->tsd.mutx);
|
|
done = td->tsd.done;
|
|
Curl_mutex_release(&td->tsd.mutx);
|
|
|
|
if(done) {
|
|
CURLcode result = td->result;
|
|
getaddrinfo_complete(data);
|
|
|
|
if(!result && !data->state.async.dns)
|
|
result = Curl_resolver_error(data);
|
|
|
|
if(result) {
|
|
destroy_async_data(data);
|
|
return result;
|
|
}
|
|
#ifdef USE_HTTPSRR_ARES
|
|
{
|
|
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&td->hinfo);
|
|
if(!lhrr) {
|
|
destroy_async_data(data);
|
|
return CURLE_OUT_OF_MEMORY;
|
|
}
|
|
data->state.async.dns->hinfo = lhrr;
|
|
}
|
|
#endif
|
|
destroy_async_data(data);
|
|
*entry = data->state.async.dns;
|
|
}
|
|
else {
|
|
/* poll for name lookup done with exponential backoff up to 250ms */
|
|
/* should be fine even if this converts to 32-bit */
|
|
timediff_t elapsed = Curl_timediff(Curl_now(),
|
|
data->progress.t_startsingle);
|
|
if(elapsed < 0)
|
|
elapsed = 0;
|
|
|
|
if(td->poll_interval == 0)
|
|
/* Start at 1ms poll interval */
|
|
td->poll_interval = 1;
|
|
else if(elapsed >= td->interval_end)
|
|
/* Back-off exponentially if last interval expired */
|
|
td->poll_interval *= 2;
|
|
|
|
if(td->poll_interval > 250)
|
|
td->poll_interval = 250;
|
|
|
|
td->interval_end = elapsed + td->poll_interval;
|
|
Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
|
|
}
|
|
|
|
return CURLE_OK;
|
|
}
|
|
|
|
int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
|
|
{
|
|
int ret_val = 0;
|
|
struct thread_data *td = &data->state.async.thdata;
|
|
#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
|
|
int socketi = 0;
|
|
#else
|
|
(void)socks;
|
|
#endif
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(data->state.async.thdata.channel) {
|
|
ret_val = Curl_ares_getsock(data, data->state.async.thdata.channel, socks);
|
|
for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
|
|
if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
|
|
!ARES_GETSOCK_WRITABLE(ret_val, socketi))
|
|
break;
|
|
}
|
|
#endif
|
|
#ifndef CURL_DISABLE_SOCKETPAIR
|
|
/* return read fd to client for polling the DNS resolution status */
|
|
socks[socketi] = td->tsd.sock_pair[0];
|
|
ret_val |= GETSOCK_READSOCK(socketi);
|
|
#else
|
|
{
|
|
timediff_t milli;
|
|
timediff_t ms = Curl_timediff(Curl_now(), td->start);
|
|
if(ms < 3)
|
|
milli = 0;
|
|
else if(ms <= 50)
|
|
milli = ms/3;
|
|
else if(ms <= 250)
|
|
milli = 50;
|
|
else
|
|
milli = 200;
|
|
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
|
|
}
|
|
#endif
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
#ifndef HAVE_GETADDRINFO
|
|
/*
|
|
* Curl_getaddrinfo() - for platforms without getaddrinfo
|
|
*/
|
|
struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
|
const char *hostname,
|
|
int port,
|
|
int *waitp)
|
|
{
|
|
*waitp = 0; /* default to synchronous response */
|
|
|
|
/* fire up a new resolver thread! */
|
|
if(init_resolve_thread(data, hostname, port, NULL)) {
|
|
*waitp = 1; /* expect asynchronous response */
|
|
return NULL;
|
|
}
|
|
|
|
failf(data, "getaddrinfo() thread failed");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#else /* !HAVE_GETADDRINFO */
|
|
|
|
/*
|
|
* Curl_resolver_getaddrinfo() - for getaddrinfo
|
|
*/
|
|
struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
|
const char *hostname,
|
|
int port,
|
|
int *waitp)
|
|
{
|
|
struct addrinfo hints;
|
|
int pf = PF_INET;
|
|
*waitp = 0; /* default to synchronous response */
|
|
|
|
#ifdef CURLRES_IPV6
|
|
if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
|
|
/* The stack seems to be IPv6-enabled */
|
|
if(data->conn->ip_version == CURL_IPRESOLVE_V6)
|
|
pf = PF_INET6;
|
|
else
|
|
pf = PF_UNSPEC;
|
|
}
|
|
#endif /* CURLRES_IPV6 */
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = pf;
|
|
hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
|
|
SOCK_STREAM : SOCK_DGRAM;
|
|
|
|
/* fire up a new resolver thread! */
|
|
if(init_resolve_thread(data, hostname, port, &hints)) {
|
|
*waitp = 1; /* expect asynchronous response */
|
|
return NULL;
|
|
}
|
|
|
|
failf(data, "getaddrinfo() thread failed to start");
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* !HAVE_GETADDRINFO */
|
|
|
|
CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
|
char *servers)
|
|
{
|
|
(void)data;
|
|
(void)servers;
|
|
return CURLE_NOT_BUILT_IN;
|
|
|
|
}
|
|
|
|
CURLcode Curl_set_dns_interface(struct Curl_easy *data,
|
|
const char *interf)
|
|
{
|
|
(void)data;
|
|
(void)interf;
|
|
return CURLE_NOT_BUILT_IN;
|
|
}
|
|
|
|
CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
|
|
const char *local_ip4)
|
|
{
|
|
(void)data;
|
|
(void)local_ip4;
|
|
return CURLE_NOT_BUILT_IN;
|
|
}
|
|
|
|
CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
|
|
const char *local_ip6)
|
|
{
|
|
(void)data;
|
|
(void)local_ip6;
|
|
return CURLE_NOT_BUILT_IN;
|
|
}
|
|
|
|
#endif /* CURLRES_THREADED */
|